-
Notifications
You must be signed in to change notification settings - Fork 805
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Map workspace to iprojectsite #3425
Conversation
override this.Initialize() = | ||
base.Initialize() | ||
|
||
let workspaceChanged (args:WorkspaceChangeEventArgs) = | ||
match args.Kind with | ||
| WorkspaceChangeKind.ProjectAdded -> this.OnProjectAdded(args.ProjectId, this.Workspace, args.NewSolution) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think Roslyn will fire ProjectAdded events for all the projects in a solution when SolutionAdded
is fired, so we probably need to handle the Solution
events too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Pilchie weirdly we do get project added events. I want to dig into these quite a bit more though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work - the approach looks right. Some changes requested
@@ -32,6 +34,12 @@ open Microsoft.VisualStudio.Shell.Interop | |||
open Microsoft.VisualStudio.FSharp.LanguageService | |||
open Microsoft.VisualStudio.ComponentModelHost | |||
|
|||
|
|||
module LogFile = | |||
let log (text:string) = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make this either #if DEBUG
or use System.Diagnostics.Trace
referencePaths |> Seq.iter(fun r -> LogFile.log r) | ||
LogFile.log "=======================================" | ||
|
||
lock lockObject (fun () -> match sourceFiles.TryGetValue path with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Normal formatting would use 4-space indenting for this, like this:
lock lockObject (fun () ->
match sourceFiles.TryGetValue path with
| true, _ ->
sourceFiles.Remove(path) |> ignore
sourceFiles.Add(path, sourcePaths)
...
)
let projectTable = ConcurrentDictionary<ProjectId, Refreshable<ProjectId[] * FSharpProjectOptions>>() | ||
|
||
// A table of information about single-file projects. Currently we only need the load time of each such file, plus | ||
// the original options for editing | ||
let singleFileProjectTable = ConcurrentDictionary<ProjectId, DateTime * FSharpProjectOptions>() | ||
|
||
// Accumulate sorces and references for each project file | ||
let lockObject = new Object() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Normal name for lockObject
is syncObj
referencePaths |> Seq.iter(fun r -> LogFile.log r) | ||
LogFile.log "=======================================" | ||
|
||
lock lockObject (fun () -> match sourceFiles.TryGetValue path with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use sourceFiles.ContainsKey
rather than TryGetValue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I believe you can just do sourceFiles.[path] <- sourcePaths
- you don't need o explicitly Remove nor check if the collection already contains the value - the indexer will do what you need
member __.GetSourceFiles(path:string) = | ||
lock lockObject (fun () -> match sourceFiles.TryGetValue path with | ||
| true, value -> value | ||
| _ -> Array.empty<string>) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Normal F# practice is to use [| |]
for Array.empty
, or to use just Array.empty
without the type annotation (which is inferred)
|
||
projectId | ||
setup (siteProvider.GetProjectSite()) |> ignore | ||
|
||
member private __.ProvideProjectSiteProvider(workspace:Workspace, project:Project) = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be a helper function either outside this class (to prove that it is logically independent of any of the constructs accessible in the class), or as a let
function in the class let
section, to help show what data it depends in the initialization sequence. Perhaps it can even go in ProjectSitesAndFiles.fs where two other ways of creating IProjectSite are implemented (for scripts and standalone-files).
// Accumulate sorces and references for each project file | ||
let lockObject = new Object() | ||
let sourceFiles = new Dictionary<string, string[]>() | ||
let referenceFiles = new Dictionary<string, string[]>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why have two tables for the information? Just use
let projectInfoFromCommandLineUpdates = new ConcurrentDictionary<string, string[] * string[]>()
or even
let projectInfoFromCommandLineUpdates =
new ConcurrentDictionary<string, ImmutableArray<CommandLineSourceFile> * ImmutableArray<CommandLineReference>()
I think I prefer the second - just snapshot the full information we're given since it may be useful in debugging.
Also, in a recent update I renamed ProjectInfoManager
to FSharpProjectOptionsManager
to indicate that it was about maintaining a table mapping projects to up-to-date FSharpCompileOptions
values. It's now been expanded to contain this orthogonal extra information, so perhaps rename back to ProjectInfoManager
or ProjectOptionsManager
let sourcePaths = sources |> Seq.map(fun s -> fullPath s.Path) |> Seq.toArray | ||
let referencePaths = references |> Seq.map(fun r -> fullPath r.Reference) |> Seq.toArray | ||
|
||
LogFile.log "=======================================" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove in favour of System.Diagnostics.Trace.
let hier = visualStudioWorkspace.GetHierarchy(project.Id) | ||
{new IProvideProjectSite with | ||
member this.GetProjectSite() = | ||
let compileItems () = projectInfoManager.GetSourceFiles(project.FilePath) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe there's no need to define all these little functions, nor is it clearer - just put the implementations in the object expression
member __.ProjectProvider = Some this | ||
member __.AssemblyReferences() = assemblyReferences () | ||
} | ||
interface IVsHierarchy with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is an IVsHierarchy
implementation is needed here? I do vaguely remember - is it that crappy code in ProjectSitesAndFiles.fs that searches for IVsHierarchy for project references? However if that's the case, then let's document it, and let's also eventually get rid of this in favour of adding an appropriate method into IProjectSite, or even just using the existing AssemblyReferences
method
hierarchy, | ||
null, | ||
errorReporter) :?> AbstractProject | ||
| _ as p -> p |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line can just be | p -> p
|
||
if isNull (workspace.ProjectTracker.GetProject projectId) then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously we used this line to guard against doing work multiple times for the same project as we traverse the project graph
You've moved the check down a bit, but this means that UpdateProjectInfo
may now be called many times per project, along with actions such as creating ProjectExternalErrorReporter, and, most critically, the recursive calls and AddProjectReference calls here:
for referencedSite in ProjectSitesAndFiles.GetReferencedProjectSites (site, this.SystemServiceProvider) do
let referencedProjectId = setup referencedSite
project.AddProjectReference(ProjectReference referencedProjectId)
I think you will want to somehow want to guard against doing these things many times as we traverse the project graph.
@KevinRansom As part of validating this could you check the before/after times for "devenv.exe CPU usage time at the point we can first perform a file edit" for
Thanks. Also, we should write out a list of things we need to test manually when changes like this are made, e.g.
|
@dsyme .... yes I know ... I have some ideas. |
Why is the branch name |
because halfway through I looked at the keyboard fix, then I cherry picked the fix over. I think because ... reasons .... (none of which are sensible). |
I see. Is this PR in a good enough state to validate experience-wise? |
@cartermp not even close. |
Closing in favour of: #3564 |
Use the Roslyn workspace as a datasource for source files and referenced projects for the F# Editor language service.
Note: as of right now the source files are not in project file order.
Was: #3391