From beb4e32e5f611e6505d2585448d920a003131b87 Mon Sep 17 00:00:00 2001 From: Kevin Ransom Date: Mon, 7 Aug 2017 11:57:09 -0700 Subject: [PATCH 1/3] Add IProvideProjectSite mapping --- .../LanguageService/LanguageService.fs | 182 +++++++++++++----- 1 file changed, 134 insertions(+), 48 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index df729cb4a21..d6f01a5e755 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -7,11 +7,13 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System open System.Collections.Concurrent open System.Collections.Generic +open System.Collections.Immutable open System.ComponentModel.Composition +open System.Diagnostics +open System.IO +open System.Linq open System.Runtime.CompilerServices open System.Runtime.InteropServices -open System.IO -open System.Diagnostics open Microsoft.FSharp.Compiler.CompileOps open Microsoft.FSharp.Compiler.SourceCodeServices @@ -23,6 +25,7 @@ open Microsoft.CodeAnalysis.Options open Microsoft.VisualStudio open Microsoft.VisualStudio.Editor open Microsoft.VisualStudio.TextManager.Interop +open Microsoft.VisualStudio.LanguageServices open Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService open Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem open Microsoft.VisualStudio.LanguageServices.Implementation.TaskList @@ -265,14 +268,12 @@ type override this.CreateWorkspace() = this.ComponentModel.GetService() - override this.CreateLanguageService() = - FSharpLanguageService(this) + override this.CreateLanguageService() = FSharpLanguageService(this) override this.CreateEditorFactories() = Seq.empty override this.RegisterMiscellaneousFilesWorkspaceInformation(_) = () - -and +and [] [, ".fs")>] [, ".fsi")>] @@ -316,12 +317,28 @@ and override this.Initialize() = base.Initialize() + let workspaceChanged (args:WorkspaceChangeEventArgs) = + match args.Kind with + | WorkspaceChangeKind.ProjectAdded + | WorkspaceChangeKind.ProjectChanged + | WorkspaceChangeKind.ProjectRemoved + | WorkspaceChangeKind.DocumentAdded + | WorkspaceChangeKind.DocumentRemoved + | WorkspaceChangeKind.AdditionalDocumentAdded + | WorkspaceChangeKind.AdditionalDocumentRemoved + | WorkspaceChangeKind.DocumentInfoChanged + | WorkspaceChangeKind.SolutionCleared -> + for projectId in this.Workspace.CurrentSolution.ProjectIds do + let project = this.Workspace.CurrentSolution.GetProject(projectId) + let siteProvider = this.ProvideProjectSiteProvider(this.Workspace, project) + this.SetupProjectFile(siteProvider, this.Workspace, "setupProjectsAfterSolutionOpen") + | _ -> () + this.Workspace.Options <- this.Workspace.Options.WithChangedOption(Completion.CompletionOptions.BlockForCompletionItems, FSharpConstants.FSharpLanguageName, false) this.Workspace.Options <- this.Workspace.Options.WithChangedOption(Shared.Options.ServiceFeatureOnOffOptions.ClosedFileDiagnostic, FSharpConstants.FSharpLanguageName, Nullable false) + this.Workspace.DocumentClosed.Add <| fun args -> tryRemoveSingleFileProject args.Document.Project.Id + this.Workspace.WorkspaceChanged.Add(workspaceChanged) - this.Workspace.DocumentClosed.Add <| fun args -> - tryRemoveSingleFileProject args.Document.Project.Id - Events.SolutionEvents.OnAfterCloseSolution.Add <| fun _ -> //checkerProvider.Checker.StopBackgroundCompile() @@ -334,7 +351,7 @@ and singleFileProjects.Keys |> Seq.iter tryRemoveSingleFileProject let ctx = System.Threading.SynchronizationContext.Current - + let rec setupProjectsAfterSolutionOpen() = async { use openedProjects = MailboxProcessor.Start <| fun inbox -> @@ -358,9 +375,9 @@ and let theme = package.ComponentModel.DefaultExportProvider.GetExport().Value theme.SetColors() - + /// Sync the information for the project - member this.SyncProject(project: AbstractProject, projectContext: IWorkspaceProjectContext, site: IProjectSite, workspace, forceUpdate, userOpName) = + member __.SyncProject(project: AbstractProject, projectContext: IWorkspaceProjectContext, site: IProjectSite, workspace, forceUpdate, userOpName) = let wellFormedFilePathSetIgnoreCase (paths: seq) = HashSet(paths |> Seq.filter isPathWellFormed |> Seq.map (fun s -> try System.IO.Path.GetFullPath(s) with _ -> s), StringComparer.OrdinalIgnoreCase) @@ -378,7 +395,7 @@ and if not(updatedFiles.Contains(file)) then projectContext.RemoveSourceFile(file) updated <- true - + let updatedRefs = site.AssemblyReferences() |> wellFormedFilePathSetIgnoreCase let originalRefs = project.GetCurrentMetadataReferences() |> Seq.map (fun ref -> ref.FilePath) |> wellFormedFilePathSetIgnoreCase @@ -417,49 +434,121 @@ and member this.SetupProjectFile(siteProvider: IProvideProjectSite, workspace: VisualStudioWorkspaceImpl, userOpName) = let userOpName = userOpName + ".SetupProjectFile" - let rec setup (site: IProjectSite) = + let rec setup (site: IProjectSite) = let projectGuid = Guid(site.ProjectGuid) let projectFileName = site.ProjectFileName() let projectDisplayName = projectDisplayNameOf projectFileName let projectId = workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectDisplayName) - if isNull (workspace.ProjectTracker.GetProject projectId) then - projectInfoManager.UpdateProjectInfo(tryGetOrCreateProjectId workspace, projectId, site, workspace, userOpName) - let projectContextFactory = package.ComponentModel.GetService(); - let errorReporter = ProjectExternalErrorReporter(projectId, "FS", this.SystemServiceProvider) - - let hierarchy = - site.ProjectProvider - |> Option.map (fun p -> p :?> IVsHierarchy) - |> Option.toObj - - // Roslyn is expecting site to be an IVsHierarchy. - // It just so happens that the object that implements IProvideProjectSite is also - // an IVsHierarchy. This assertion is to ensure that the assumption holds true. - Debug.Assert(hierarchy <> null, "About to CreateProjectContext with a non-hierarchy site") - - let projectContext = - projectContextFactory.CreateProjectContext( - FSharpConstants.FSharpLanguageName, projectDisplayName, projectFileName, projectGuid, hierarchy, null, errorReporter) - - let project = projectContext :?> AbstractProject + projectInfoManager.UpdateProjectInfo(tryGetOrCreateProjectId workspace, projectId, site, workspace, userOpName) + let projectContextFactory = package.ComponentModel.GetService(); + let errorReporter = ProjectExternalErrorReporter(projectId, "FS", this.SystemServiceProvider) + // Roslyn is expecting site to be an IVsHierarchy. + // It just so happens that the object that implements IProvideProjectSite is also + // an IVsHierarchy. This assertion is to ensure that the assumption holds true. + let hierarchy = + site.ProjectProvider + |> Option.map (fun p -> p :?> IVsHierarchy) + |> Option.toObj + Debug.Assert(hierarchy <> null, "About to CreateProjectContext with a non-hierarchy site") + + let isnew, project = + let p = workspace.ProjectTracker.GetProject projectId + match p with + | null -> + true, projectContextFactory.CreateProjectContext( + FSharpConstants.FSharpLanguageName, + projectDisplayName, + projectFileName, + projectGuid, + hierarchy, + null, + errorReporter) :?> AbstractProject + | _ -> false, p + + match project :> obj with + | :? IWorkspaceProjectContext as projectContext -> this.SyncProject(project, projectContext, site, workspace, forceUpdate=false, userOpName=userOpName) - site.AdviseProjectSiteChanges(FSharpConstants.FSharpLanguageServiceCallbackName, - AdviseProjectSiteChanges(fun () -> this.SyncProject(project, projectContext, site, workspace, forceUpdate=true, userOpName="AdviseProjectSiteChanges."+userOpName))) - site.AdviseProjectSiteClosed(FSharpConstants.FSharpLanguageServiceCallbackName, - AdviseProjectSiteChanges(fun () -> + site.AdviseProjectSiteChanges(FSharpConstants.FSharpLanguageServiceCallbackName, + AdviseProjectSiteChanges(fun () -> this.SyncProject(project, projectContext, site, workspace, forceUpdate=true, userOpName="AdviseProjectSiteChanges."+userOpName))) + site.AdviseProjectSiteClosed(FSharpConstants.FSharpLanguageServiceCallbackName, + AdviseProjectSiteChanges(fun () -> projectInfoManager.ClearInfoForProject(project.Id) optionsAssociation.Remove(projectContext) |> ignore project.Disconnect())) + | _ -> () + if isnew then for referencedSite in ProjectSitesAndFiles.GetReferencedProjectSites (site, this.SystemServiceProvider) do - let referencedProjectId = setup referencedSite + let referencedProjectId = setup referencedSite project.AddProjectReference(ProjectReference referencedProjectId) projectId setup (siteProvider.GetProjectSite()) |> ignore + member private __.ProvideProjectSiteProvider(workspace:Workspace, project:Project) = + let visualStudioWorkspace = workspace :?> VisualStudioWorkspace + let hier = visualStudioWorkspace.GetHierarchy(project.Id) + {new IProvideProjectSite with + member this.GetProjectSite() = + let compileItems () = [| for document in project.Documents do yield document.FilePath |] + let compilerFlags () = [| "" |] + let caption () = project.Name + let projFileName () = project.FilePath + let taskProvider = None + let taskReporter = None + let targetFrameworkMoniker = "" + let projectGuid () = project.Id.Id.ToString() + let creationTime = System.DateTime.Now + let assemblyReferences () = + [| + for reference in project.ProjectReferences do + let p = workspace.CurrentSolution.GetProject(reference.ProjectId) + yield (p.OutputFilePath) + for r in project.MetadataReferences do + match r with + | :? PortableExecutableReference as per -> yield per.FilePath + | :? UnresolvedMetadataReference as umr -> yield umr.Reference + | _ -> () |] + { new Microsoft.VisualStudio.FSharp.LanguageService.IProjectSite with + member __.SourceFilesOnDisk() = compileItems () + member __.DescriptionOfProject() = caption () + member __.CompilerFlags() = compilerFlags () + member __.ProjectFileName() = projFileName () + member __.ErrorListTaskProvider() = taskProvider + member __.ErrorListTaskReporter() = taskReporter + member __.AdviseProjectSiteChanges(_,_) = () + member __.AdviseProjectSiteCleaned(_,_) = () + member __.AdviseProjectSiteClosed(_,_) = () + member __.IsIncompleteTypeCheckEnvironment = false + member __.TargetFrameworkMoniker = targetFrameworkMoniker + member __.ProjectGuid = projectGuid () + member __.LoadTime = creationTime + member __.ProjectProvider = Some this + member __.AssemblyReferences() = assemblyReferences () + } + interface IVsHierarchy with + member __.SetSite(psp) = hier.SetSite(psp) + member __.GetSite(psp) = hier.GetSite(ref psp) + member __.QueryClose(pfCanClose) = hier.QueryClose(ref pfCanClose) + member __.Close() = hier.Close() + member __.GetGuidProperty(itemid, propid, pguid) = hier.GetGuidProperty(itemid, propid, ref pguid) + member __.SetGuidProperty(itemid, propid, rguid) = hier.SetGuidProperty(itemid, propid, ref rguid) + member __.GetProperty(itemid, propid, pvar) = hier.GetProperty(itemid, propid, ref pvar) + member __.SetProperty(itemid, propid, var) = hier.SetProperty(itemid, propid, var) + member __.GetNestedHierarchy(itemid, iidHierarchyNested, ppHierarchyNested, pitemidNested) = hier.GetNestedHierarchy(itemid, ref iidHierarchyNested, ref ppHierarchyNested, ref pitemidNested) + member __.GetCanonicalName(itemid, pbstrName) = hier.GetCanonicalName(itemid, ref pbstrName) + member __.ParseCanonicalName(pszName, pitemid) = hier.ParseCanonicalName(pszName, ref pitemid) + member __.Unused0() = hier.Unused0() + member __.AdviseHierarchyEvents(pEventSink, pdwCookie) = hier.AdviseHierarchyEvents(pEventSink, ref pdwCookie) + member __.UnadviseHierarchyEvents(dwCookie) = hier.UnadviseHierarchyEvents(dwCookie) + member __.Unused1() = hier.Unused1() + member __.Unused2() = hier.Unused2() + member __.Unused3() = hier.Unused3() + member __.Unused4() = hier.Unused4() + } + member this.SetupStandAloneFile(fileName: string, fileContents: string, workspace: VisualStudioWorkspaceImpl, hier: IVsHierarchy) = let loadTime = DateTime.Now @@ -476,7 +565,7 @@ and let projectContext = projectContextFactory.CreateProjectContext(FSharpConstants.FSharpLanguageName, projectDisplayName, projectFileName, projectId.Id, hier, null, errorReporter) projectContext.AddSourceFile(fileName) - + let project = projectContext :?> AbstractProject singleFileProjects.[projectId] <- project @@ -493,19 +582,16 @@ and base.SetupNewTextView(textView) let textViewAdapter = package.ComponentModel.GetService() - match textView.GetBuffer() with | (VSConstants.S_OK, textLines) -> let filename = VsTextLines.GetFilename textLines - match VsRunningDocumentTable.FindDocumentWithoutLocking(package.RunningDocumentTable,filename) with + match VsRunningDocumentTable.FindDocumentWithoutLocking(package.RunningDocumentTable, filename) with | Some (hier, _) -> match hier with - | :? IProvideProjectSite as siteProvider when not (IsScript(filename)) -> + | :? IProvideProjectSite as siteProvider when not (IsScript(filename)) -> this.SetupProjectFile(siteProvider, this.Workspace, "SetupNewTextView") - | _ -> - let fileContents = VsTextLines.GetFileContents(textLines, textViewAdapter) - this.SetupStandAloneFile(filename, fileContents, this.Workspace, hier) + | _ -> + let fileContents = VsTextLines.GetFileContents(textLines, textViewAdapter) + this.SetupStandAloneFile(filename, fileContents, this.Workspace, hier) | _ -> () | _ -> () - - From 6cfd52ee996b8d799efa6b93fc9e021db92f5b5f Mon Sep 17 00:00:00 2001 From: Kevin Ransom Date: Tue, 8 Aug 2017 15:24:05 -0700 Subject: [PATCH 2/3] update language service for sdk files --- .../LanguageService/LanguageService.fs | 55 +++++++++---------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index d6f01a5e755..8fc7b1473c7 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -1,5 +1,4 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - namespace Microsoft.VisualStudio.FSharp.Editor #nowarn "40" @@ -314,24 +313,21 @@ and let optionsAssociation = ConditionalWeakTable() + member private this.OnProjectChanged(projectId:ProjectId, workspace:VisualStudioWorkspaceImpl, _newSolution:Solution) = + let project = workspace.CurrentSolution.GetProject(projectId) + let siteProvider = this.ProvideProjectSiteProvider(this.Workspace, project) + this.SetupProjectFile(siteProvider, this.Workspace, "setupProjectsAfterSolutionOpen") + member private this.OnProjectAdded(projectId:ProjectId, workspace:VisualStudioWorkspaceImpl, newSolution:Solution) = this.OnProjectChanged(projectId, workspace, newSolution) + member private this.OnProjectRemoved(_projectId:ProjectId, _workspace:VisualStudioWorkspaceImpl, _newSolution:Solution) = () + override this.Initialize() = base.Initialize() let workspaceChanged (args:WorkspaceChangeEventArgs) = match args.Kind with - | WorkspaceChangeKind.ProjectAdded - | WorkspaceChangeKind.ProjectChanged - | WorkspaceChangeKind.ProjectRemoved - | WorkspaceChangeKind.DocumentAdded - | WorkspaceChangeKind.DocumentRemoved - | WorkspaceChangeKind.AdditionalDocumentAdded - | WorkspaceChangeKind.AdditionalDocumentRemoved - | WorkspaceChangeKind.DocumentInfoChanged - | WorkspaceChangeKind.SolutionCleared -> - for projectId in this.Workspace.CurrentSolution.ProjectIds do - let project = this.Workspace.CurrentSolution.GetProject(projectId) - let siteProvider = this.ProvideProjectSiteProvider(this.Workspace, project) - this.SetupProjectFile(siteProvider, this.Workspace, "setupProjectsAfterSolutionOpen") + | WorkspaceChangeKind.ProjectAdded -> this.OnProjectAdded(args.ProjectId, this.Workspace, args.NewSolution) + | WorkspaceChangeKind.ProjectChanged -> this.OnProjectChanged(args.ProjectId, this.Workspace, args.NewSolution) + | WorkspaceChangeKind.ProjectRemoved -> this.OnProjectRemoved(args.ProjectId, this.Workspace, args.NewSolution) | _ -> () this.Workspace.Options <- this.Workspace.Options.WithChangedOption(Completion.CompletionOptions.BlockForCompletionItems, FSharpConstants.FSharpLanguageName, false) @@ -383,14 +379,14 @@ and let updatedFiles = site.SourceFilesOnDisk() |> wellFormedFilePathSetIgnoreCase let originalFiles = project.GetCurrentDocuments() |> Seq.map (fun file -> file.FilePath) |> wellFormedFilePathSetIgnoreCase - + let mutable updated = forceUpdate for file in updatedFiles do if not(originalFiles.Contains(file)) then projectContext.AddSourceFile(file) updated <- true - + for file in originalFiles do if not(updatedFiles.Contains(file)) then projectContext.RemoveSourceFile(file) @@ -454,11 +450,11 @@ and |> Option.toObj Debug.Assert(hierarchy <> null, "About to CreateProjectContext with a non-hierarchy site") - let isnew, project = - let p = workspace.ProjectTracker.GetProject projectId - match p with + let project = + match workspace.ProjectTracker.GetProject projectId with | null -> - true, projectContextFactory.CreateProjectContext( + /// We make the project object when the project is not already tracked + projectContextFactory.CreateProjectContext( FSharpConstants.FSharpLanguageName, projectDisplayName, projectFileName, @@ -466,7 +462,7 @@ and hierarchy, null, errorReporter) :?> AbstractProject - | _ -> false, p + | _ as p -> p match project :> obj with | :? IWorkspaceProjectContext as projectContext -> @@ -479,10 +475,9 @@ and optionsAssociation.Remove(projectContext) |> ignore project.Disconnect())) | _ -> () - if isnew then - for referencedSite in ProjectSitesAndFiles.GetReferencedProjectSites (site, this.SystemServiceProvider) do - let referencedProjectId = setup referencedSite - project.AddProjectReference(ProjectReference referencedProjectId) + for referencedSite in ProjectSitesAndFiles.GetReferencedProjectSites (site, this.SystemServiceProvider) do + let referencedProjectId = setup referencedSite + project.AddProjectReference(ProjectReference referencedProjectId) projectId setup (siteProvider.GetProjectSite()) |> ignore @@ -503,9 +498,11 @@ and let creationTime = System.DateTime.Now let assemblyReferences () = [| - for reference in project.ProjectReferences do + for reference in project.ProjectReferences do let p = workspace.CurrentSolution.GetProject(reference.ProjectId) - yield (p.OutputFilePath) + if p <> null then + let outputFilePath = p.OutputFilePath + if String.IsNullOrEmpty(outputFilePath) = false then yield outputFilePath for r in project.MetadataReferences do match r with | :? PortableExecutableReference as per -> yield per.FilePath @@ -591,7 +588,7 @@ and | :? IProvideProjectSite as siteProvider when not (IsScript(filename)) -> this.SetupProjectFile(siteProvider, this.Workspace, "SetupNewTextView") | _ -> - let fileContents = VsTextLines.GetFileContents(textLines, textViewAdapter) - this.SetupStandAloneFile(filename, fileContents, this.Workspace, hier) + let fileContents = VsTextLines.GetFileContents(textLines, textViewAdapter) + this.SetupStandAloneFile(filename, fileContents, this.Workspace, hier) | _ -> () | _ -> () From c37d65b895e170af4dcedec7808bb5023377bf45 Mon Sep 17 00:00:00 2001 From: Kevin Ransom Date: Thu, 10 Aug 2017 01:38:52 -0700 Subject: [PATCH 3/3] Update language service to use: direct command line notifications. --- src/fsharp/FSharp.Build/Fsc.fs | 4 +- .../LanguageService/LanguageService.fs | 69 ++++++++++++++----- 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/src/fsharp/FSharp.Build/Fsc.fs b/src/fsharp/FSharp.Build/Fsc.fs index b8d025cfdda..7a791a18f1f 100644 --- a/src/fsharp/FSharp.Build/Fsc.fs +++ b/src/fsharp/FSharp.Build/Fsc.fs @@ -204,13 +204,13 @@ type [ null then for item in defineConstants do - builder.AppendSwitchIfNotNull("--define:", item.ItemSpec) + builder.AppendSwitchIfNotNull("--define:", item.ItemSpec) // DocumentationFile builder.AppendSwitchIfNotNull("--doc:", documentationFile) // GenerateInterfaceFile diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 8fc7b1473c7..d29ce09845b 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -34,6 +34,12 @@ open Microsoft.VisualStudio.Shell.Interop open Microsoft.VisualStudio.FSharp.LanguageService open Microsoft.VisualStudio.ComponentModelHost + +module LogFile = + let log (text:string) = + use logfile = File.AppendText(@"c:\temp\mylogfile.log") + logfile.WriteLine(text) + // Exposes FSharpChecker as MEF export [); Composition.Shared>] type internal FSharpCheckerProvider @@ -90,13 +96,19 @@ type internal ProjectInfoManager checkerProvider: FSharpCheckerProvider, [)>] serviceProvider: System.IServiceProvider ) = - // A table of information about projects, excluding single-file projects. + + // A table of information about projects, excluding single-file projects. let projectTable = ConcurrentDictionary>() // 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() + // Accumulate sorces and references for each project file + let lockObject = new Object() + let sourceFiles = new Dictionary() + let referenceFiles = new Dictionary() + /// Clear a project from the project table member this.ClearInfoForProject(projectId: ProjectId) = projectTable.TryRemove(projectId) |> ignore @@ -198,6 +210,42 @@ type internal ProjectInfoManager | true, (_loadTime, originalOptions) -> Some originalOptions | _ -> this.TryGetOptionsForProject(projectId) + [] + member this.HandleCommandLineChanges(path:string, sources:ImmutableArray, references:ImmutableArray) = + let projectDir = Path.GetDirectoryName(path) + let fullPath p = if Path.IsPathRooted(p) then p else Path.Combine(projectDir, p) + 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 "=======================================" + LogFile.log path + LogFile.log projectDir + LogFile.log "=======================================" + sourcePaths |> Seq.iter(fun s -> LogFile.log s) + LogFile.log "=======================================" + referencePaths |> Seq.iter(fun r -> LogFile.log r) + LogFile.log "=======================================" + + lock lockObject (fun () -> match sourceFiles.TryGetValue path with + | true, _ -> sourceFiles.Remove(path) |> ignore + sourceFiles.Add(path, sourcePaths) + | _ -> sourceFiles.Add(path, sourcePaths) + match referenceFiles.TryGetValue path with + | true, _ -> referenceFiles.Remove(path) |> ignore + referenceFiles.Add(path, referencePaths) + | _ -> referenceFiles.Add(path, referencePaths) ) + () + + member __.GetSourceFiles(path:string) = + lock lockObject (fun () -> match sourceFiles.TryGetValue path with + | true, value -> value + | _ -> Array.empty) + + member __.GetReferenceFiles(path:string) = + lock lockObject (fun () -> match referenceFiles.TryGetValue path with + | true, value -> value + | _ -> Array.empty) + // Used to expose FSharpChecker/ProjectInfo manager to diagnostic providers // Diagnostic providers can be executed in environment that does not use MEF so they can rely only // on services exposed by the workspace @@ -317,6 +365,7 @@ and let project = workspace.CurrentSolution.GetProject(projectId) let siteProvider = this.ProvideProjectSiteProvider(this.Workspace, project) this.SetupProjectFile(siteProvider, this.Workspace, "setupProjectsAfterSolutionOpen") + member private this.OnProjectAdded(projectId:ProjectId, workspace:VisualStudioWorkspaceImpl, newSolution:Solution) = this.OnProjectChanged(projectId, workspace, newSolution) member private this.OnProjectRemoved(_projectId:ProjectId, _workspace:VisualStudioWorkspaceImpl, _newSolution:Solution) = () @@ -434,10 +483,9 @@ and let projectGuid = Guid(site.ProjectGuid) let projectFileName = site.ProjectFileName() let projectDisplayName = projectDisplayNameOf projectFileName - let projectId = workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectDisplayName) - projectInfoManager.UpdateProjectInfo(tryGetOrCreateProjectId workspace, projectId, site, workspace, userOpName) + let projectContextFactory = package.ComponentModel.GetService(); let errorReporter = ProjectExternalErrorReporter(projectId, "FS", this.SystemServiceProvider) @@ -487,7 +535,7 @@ and let hier = visualStudioWorkspace.GetHierarchy(project.Id) {new IProvideProjectSite with member this.GetProjectSite() = - let compileItems () = [| for document in project.Documents do yield document.FilePath |] + let compileItems () = projectInfoManager.GetSourceFiles(project.FilePath) let compilerFlags () = [| "" |] let caption () = project.Name let projFileName () = project.FilePath @@ -496,18 +544,7 @@ and let targetFrameworkMoniker = "" let projectGuid () = project.Id.Id.ToString() let creationTime = System.DateTime.Now - let assemblyReferences () = - [| - for reference in project.ProjectReferences do - let p = workspace.CurrentSolution.GetProject(reference.ProjectId) - if p <> null then - let outputFilePath = p.OutputFilePath - if String.IsNullOrEmpty(outputFilePath) = false then yield outputFilePath - for r in project.MetadataReferences do - match r with - | :? PortableExecutableReference as per -> yield per.FilePath - | :? UnresolvedMetadataReference as umr -> yield umr.Reference - | _ -> () |] + let assemblyReferences () = projectInfoManager.GetReferenceFiles(project.FilePath) { new Microsoft.VisualStudio.FSharp.LanguageService.IProjectSite with member __.SourceFilesOnDisk() = compileItems () member __.DescriptionOfProject() = caption ()