diff --git a/vsintegration/packages.config b/vsintegration/packages.config index 620fe9c39da..8b1616e54a7 100644 --- a/vsintegration/packages.config +++ b/vsintegration/packages.config @@ -62,6 +62,9 @@ + + + diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index a2b541fa40e..8899468d419 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -147,6 +147,15 @@ $(FSharpSourcesRoot)\..\packages\EnvDTE80.8.0.1\lib\net10\EnvDTE80.dll True + + $(FSharpSourcesRoot)\..\packages\Microsoft.VisualStudio.Composition.15.3.38\lib\net45\Microsoft.VisualStudio.Composition.dll + + + $(FSharpSourcesRoot)\..\packages\Microsoft.VisualStudio.ProjectSystem.Managed.2.0.6142705\lib\net46\Microsoft.VisualStudio.ProjectSystem.Managed.dll + + + $(FSharpSourcesRoot)\..\packages\Microsoft.VisualStudio.ProjectSystem.15.0.751\lib\net46\Microsoft.VisualStudio.ProjectSystem.dll + $(FSharpSourcesRoot)\..\packages\Microsoft.VisualStudio.Threading.$(MicrosoftVisualStudioThreadingVersion)\lib\net45\Microsoft.VisualStudio.Threading.dll diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 19f8b1cf606..e1ec1ae9857 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,27 @@ 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.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 +350,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 +374,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 +394,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,42 +433,49 @@ 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())) - let referencedProjectSites = ProjectSitesAndFiles.GetReferencedProjectSites (site, this.SystemServiceProvider) for referencedSite in referencedProjectSites do @@ -469,6 +492,68 @@ and 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 @@ -485,7 +570,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 @@ -502,19 +587,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) | _ -> () | _ -> () - -