From 0b69878ad505c2bc15f23263a56ffb5fc7aec5cb Mon Sep 17 00:00:00 2001 From: TIHan Date: Thu, 4 Oct 2018 13:01:32 -0700 Subject: [PATCH 1/4] Refactoring single file workspace handling out of language service --- .../src/FSharp.Editor/FSharp.Editor.fsproj | 1 + .../LanguageService/LanguageService.fs | 51 +++----------- .../LanguageService/SingleFileWorkspaceMap.fs | 70 +++++++++++++++++++ 3 files changed, 79 insertions(+), 43 deletions(-) create mode 100644 vsintegration/src/FSharp.Editor/LanguageService/SingleFileWorkspaceMap.fs diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index 3e011cb5d08..02d40fb289c 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -53,6 +53,7 @@ + diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index a91cd2634b5..2177c13c760 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -175,24 +175,8 @@ type internal FSharpLanguageService(package : FSharpPackage, solution: IVsSoluti let projectInfoManager = package.ComponentModel.DefaultExportProvider.GetExport().Value - let projectDisplayNameOf projectFileName = - if String.IsNullOrWhiteSpace projectFileName then projectFileName - else Path.GetFileNameWithoutExtension projectFileName - - let singleFileProjects = ConcurrentDictionary<_, IWorkspaceProjectContext>() - - let tryRemoveSingleFileProject projectId = - match singleFileProjects.TryRemove(projectId) with - | true, project -> - projectInfoManager.ClearInfoForSingleFileProject(projectId) - project.Dispose() - | _ -> () - - let tryGetOrCreateProjectId (workspace: VisualStudioWorkspaceImpl) (projectFileName: string) = - let projectDisplayName = projectDisplayNameOf projectFileName - Some (workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectDisplayName)) - let mutable legacyProjectWorkspaceMap = Unchecked.defaultof + let mutable singleFileWorkspaceMap = Unchecked.defaultof override this.Initialize() = base.Initialize() @@ -200,35 +184,16 @@ type internal FSharpLanguageService(package : FSharpPackage, solution: IVsSoluti 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 - - legacyProjectWorkspaceMap <- LegacyProjectWorkspaceMap(this.Workspace, solution, projectInfoManager, package.ComponentModel.GetService(), this.SystemServiceProvider) + let projectContextFactory = package.ComponentModel.GetService() + legacyProjectWorkspaceMap <- LegacyProjectWorkspaceMap(this.Workspace, solution, projectInfoManager, projectContextFactory, this.SystemServiceProvider) legacyProjectWorkspaceMap.Initialize() + singleFileWorkspaceMap <- SingleFileWorkspaceMap(this.Workspace, projectInfoManager, projectContextFactory) + singleFileWorkspaceMap.Initialize() + let theme = package.ComponentModel.DefaultExportProvider.GetExport().Value theme.SetColors() - member this.SetupStandAloneFile(fileName: string, fileContents: string, workspace: VisualStudioWorkspaceImpl, hier: IVsHierarchy) = - let loadTime = DateTime.Now - let projectFileName = fileName - let projectDisplayName = projectDisplayNameOf projectFileName - - let mutable projectId = workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectDisplayName) - - if isNull (workspace.ProjectTracker.GetProject projectId) then - let projectContextFactory = package.ComponentModel.GetService(); - - let projectContext = projectContextFactory.CreateProjectContext(FSharpConstants.FSharpLanguageName, projectDisplayName, projectFileName, projectId.Id, hier, null) - - projectId <- workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectDisplayName) - - projectContext.AddSourceFile(fileName) - - singleFileProjects.[projectId] <- projectContext - - let _referencedProjectFileNames, parsingOptions, projectOptions = projectInfoManager.ComputeSingleFileOptions (tryGetOrCreateProjectId workspace, fileName, loadTime, fileContents) |> Async.RunSynchronously - projectInfoManager.AddOrUpdateSingleFileProject(projectId, (loadTime, parsingOptions, projectOptions)) - override this.ContentTypeName = FSharpConstants.FSharpContentTypeName override this.LanguageName = FSharpConstants.FSharpLanguageName override this.RoslynLanguageName = FSharpConstants.FSharpLanguageName @@ -279,14 +244,14 @@ type internal FSharpLanguageService(package : FSharpPackage, solution: IVsSoluti // This is the path when opening out-of-project .fs/.fsi files in CPS projects let fileContents = VsTextLines.GetFileContents(textLines, textViewAdapter) - this.SetupStandAloneFile(filename, fileContents, this.Workspace, hier) + singleFileWorkspaceMap.AddFile(filename, fileContents, hier) | _ -> () | _ -> // This is the path for both in-project and out-of-project .fsx files let fileContents = VsTextLines.GetFileContents(textLines, textViewAdapter) - this.SetupStandAloneFile(filename, fileContents, this.Workspace, hier) + singleFileWorkspaceMap.AddFile(filename, fileContents, hier) | _ -> () | _ -> () diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SingleFileWorkspaceMap.fs b/vsintegration/src/FSharp.Editor/LanguageService/SingleFileWorkspaceMap.fs new file mode 100644 index 00000000000..144c3cd269e --- /dev/null +++ b/vsintegration/src/FSharp.Editor/LanguageService/SingleFileWorkspaceMap.fs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Microsoft.VisualStudio.FSharp.Editor + +#nowarn "40" + +open System +open System.Collections.Concurrent +open System.Collections.Generic +open System.Diagnostics +open System.IO +open System.Linq +open System.Runtime.CompilerServices +open Microsoft.CodeAnalysis +open Microsoft.VisualStudio +open Microsoft.VisualStudio.FSharp.Editor +open Microsoft.VisualStudio.FSharp.Editor.SiteProvider +open Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem +open Microsoft.VisualStudio.LanguageServices.ProjectSystem +open Microsoft.VisualStudio.Shell.Interop + +[] +type internal SingleFileWorkspaceMap(workspace: VisualStudioWorkspaceImpl, + projectInfoManager: FSharpProjectOptionsManager, + projectContextFactory: IWorkspaceProjectContextFactory) = + + let mutable isInitialized = false + let singleFileProjects = ConcurrentDictionary<_, IWorkspaceProjectContext>() + + let tryRemoveSingleFileProject projectId = + match singleFileProjects.TryRemove(projectId) with + | true, project -> + projectInfoManager.ClearInfoForSingleFileProject(projectId) + project.Dispose() + | _ -> () + + let projectDisplayNameOf projectFileName = + if String.IsNullOrWhiteSpace projectFileName then projectFileName + else Path.GetFileNameWithoutExtension projectFileName + + let tryGetOrCreateProjectId (workspace: VisualStudioWorkspaceImpl) (projectFileName: string) = + let projectDisplayName = projectDisplayNameOf projectFileName + Some (workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectDisplayName)) + + member this.Initialize() = + if not isInitialized then + workspace.DocumentClosed.Add <| fun args -> tryRemoveSingleFileProject args.Document.Project.Id + isInitialized <- true + + member this.AddFile(fileName: string, fileContents: string, hier: IVsHierarchy) = + if not isInitialized then + failwith "SingleFileWorkspaceMap is not initialized." + + let loadTime = DateTime.Now + let projectFileName = fileName + let projectDisplayName = projectDisplayNameOf projectFileName + + let mutable projectId = workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectDisplayName) + + if isNull (workspace.ProjectTracker.GetProject projectId) then + let projectContext = projectContextFactory.CreateProjectContext(FSharpConstants.FSharpLanguageName, projectDisplayName, projectFileName, projectId.Id, hier, null) + + projectId <- workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectDisplayName) + + projectContext.AddSourceFile(fileName) + + singleFileProjects.[projectId] <- projectContext + + let _referencedProjectFileNames, parsingOptions, projectOptions = projectInfoManager.ComputeSingleFileOptions (tryGetOrCreateProjectId workspace, fileName, loadTime, fileContents) |> Async.RunSynchronously + projectInfoManager.AddOrUpdateSingleFileProject(projectId, (loadTime, parsingOptions, projectOptions)) \ No newline at end of file From e85467f0cf7c2b9182488dc31f646fbb68b0447a Mon Sep 17 00:00:00 2001 From: TIHan Date: Mon, 8 Oct 2018 11:50:52 -0700 Subject: [PATCH 2/4] Using MiscellaneousFilesWorkspace --- .../src/FSharp.Editor/FSharp.Editor.fsproj | 1 - .../FSharpProjectOptionsManager.fs | 45 ++++-------- .../LanguageService/LanguageService.fs | 49 +------------ .../LanguageService/SingleFileWorkspaceMap.fs | 70 ------------------- 4 files changed, 14 insertions(+), 151 deletions(-) delete mode 100644 vsintegration/src/FSharp.Editor/LanguageService/SingleFileWorkspaceMap.fs diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index 02d40fb289c..3e011cb5d08 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -53,7 +53,6 @@ - diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index 007ca3fbe18..6074f32d391 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -36,10 +36,6 @@ type internal FSharpProjectOptionsManager // A table of information about projects, excluding single-file projects. let projectOptionsTable = FSharpProjectOptionsTable() - // 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() - let tryGetOrCreateProjectId (projectFileName:string) = let projectDisplayName = projectDisplayNameOf projectFileName Some (workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectDisplayName)) @@ -50,13 +46,6 @@ type internal FSharpProjectOptionsManager /// Clear a project from the project table member this.ClearInfoForProject(projectId:ProjectId) = projectOptionsTable.ClearInfoForProject(projectId) - /// Clear a project from the single file project table - member this.ClearInfoForSingleFileProject(projectId) = - singleFileProjectTable.TryRemove(projectId) |> ignore - - /// Update a project in the single file project table - member this.AddOrUpdateSingleFileProject(projectId, data) = singleFileProjectTable.[projectId] <- data - /// Get the exact options for a single-file script member this.ComputeSingleFileOptions (tryGetOrCreateProjectId, fileName, loadTime, fileContents) = async { @@ -110,34 +99,24 @@ type internal FSharpProjectOptionsManager async { let projectId = document.Project.Id - // The options for a single-file script project are re-requested each time the file is analyzed. This is because the - // single-file project may contain #load and #r references which are changing as the user edits, and we may need to re-analyze - // to determine the latest settings. FCS keeps a cache to help ensure these are up-to-date. - match singleFileProjectTable.TryGetValue(projectId) with - | true, (loadTime, _, _) -> - try - let fileName = document.FilePath - let! cancellationToken = Async.CancellationToken - let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask - // NOTE: we don't use FCS cross-project references from scripts to projects. The projects must have been - // compiled and #r will refer to files on disk. - let tryGetOrCreateProjectId _ = None - let! _referencedProjectFileNames, parsingOptions, projectOptions = this.ComputeSingleFileOptions (tryGetOrCreateProjectId, fileName, loadTime, sourceText.ToString()) - this.AddOrUpdateSingleFileProject(projectId, (loadTime, parsingOptions, projectOptions)) - return Some (parsingOptions, None, projectOptions) - with ex -> - Assert.Exception(ex) - return None - | _ -> return this.TryGetOptionsForProject(projectId) + match this.TryGetOptionsForProject(projectId) with + | Some(x) -> return Some(x) + | _ -> + let! cancellationToken = Async.CancellationToken + let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask + // NOTE: we don't use FCS cross-project references from scripts to projects. The projects must have been + // compiled and #r will refer to files on disk. + let tryGetOrCreateProjectId _ = None + let! _referencedProjectFileNames, parsingOptions, projectOptions = this.ComputeSingleFileOptions (tryGetOrCreateProjectId, document.FilePath, DateTime.Now, sourceText.ToString()) + projectOptionsTable.AddOrUpdateProject(projectId, (fun _ -> [||], parsingOptions, None, projectOptions)) + return Some (parsingOptions, None, projectOptions) } /// Get the options for a document or project relevant for syntax processing. /// Quicker then TryGetOptionsForDocumentOrProject as it doesn't need to recompute the exact project options for a script. member this.TryGetOptionsForEditingDocumentOrProject(document:Document) = let projectId = document.Project.Id - match singleFileProjectTable.TryGetValue(projectId) with - | true, (_loadTime, parsingOptions, originalOptions) -> Some (parsingOptions, originalOptions) - | _ -> this.TryGetOptionsForProject(projectId) |> Option.map(fun (parsingOptions, _, projectOptions) -> parsingOptions, projectOptions) + this.TryGetOptionsForProject(projectId) |> Option.map(fun (parsingOptions, _, projectOptions) -> parsingOptions, projectOptions) /// get a siteprovider member this.ProvideProjectSiteProvider(project:Project) = provideProjectSiteProvider(workspace, project, serviceProvider, Some projectOptionsTable) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 2177c13c760..32880bc5272 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -161,7 +161,8 @@ type internal FSharpPackage() as this = override this.CreateWorkspace() = this.ComponentModel.GetService() override this.CreateLanguageService() = FSharpLanguageService(this, this.GetService(typeof) :?> IVsSolution) override this.CreateEditorFactories() = seq { yield FSharpEditorFactory(this) :> IVsEditorFactory } - override this.RegisterMiscellaneousFilesWorkspaceInformation(_) = () + override this.RegisterMiscellaneousFilesWorkspaceInformation(miscFilesWorkspace) = + miscFilesWorkspace.RegisterLanguage(Guid(FSharpConstants.languageServiceGuidString),FSharpConstants.FSharpLanguageName,".fsx") interface Microsoft.VisualStudio.FSharp.Interactive.ITestVFSI with member this.SendTextInteraction(s:string) = @@ -176,7 +177,6 @@ type internal FSharpLanguageService(package : FSharpPackage, solution: IVsSoluti let projectInfoManager = package.ComponentModel.DefaultExportProvider.GetExport().Value let mutable legacyProjectWorkspaceMap = Unchecked.defaultof - let mutable singleFileWorkspaceMap = Unchecked.defaultof override this.Initialize() = base.Initialize() @@ -188,9 +188,6 @@ type internal FSharpLanguageService(package : FSharpPackage, solution: IVsSoluti legacyProjectWorkspaceMap <- LegacyProjectWorkspaceMap(this.Workspace, solution, projectInfoManager, projectContextFactory, this.SystemServiceProvider) legacyProjectWorkspaceMap.Initialize() - singleFileWorkspaceMap <- SingleFileWorkspaceMap(this.Workspace, projectInfoManager, projectContextFactory) - singleFileWorkspaceMap.Initialize() - let theme = package.ComponentModel.DefaultExportProvider.GetExport().Value theme.SetColors() @@ -206,8 +203,6 @@ type internal FSharpLanguageService(package : FSharpPackage, solution: IVsSoluti override this.SetupNewTextView(textView) = base.SetupNewTextView(textView) - let textViewAdapter = package.ComponentModel.GetService() - // Toggles outlining (or code folding) based on settings let outliningManagerService = this.Package.ComponentModel.GetService() let wpfTextView = this.EditorAdaptersFactoryService.GetWpfTextView(textView) @@ -215,43 +210,3 @@ type internal FSharpLanguageService(package : FSharpPackage, solution: IVsSoluti if not (isNull outliningManager) then let settings = this.Workspace.Services.GetService() outliningManager.Enabled <- settings.Advanced.IsOutliningEnabled - - match textView.GetBuffer() with - | (VSConstants.S_OK, textLines) -> - let filename = VsTextLines.GetFilename textLines - - match VsRunningDocumentTable.FindDocumentWithoutLocking(package.RunningDocumentTable,filename) with - | Some (hier, _) -> - - - // Check if the file is in a CPS project or not. - // CPS projects don't implement IProvideProjectSite and IVSProjectHierarchy - // Simple explanation: - // Legacy projects have IVSHierarchy and IProjectSite - // CPS Projects, out-of-project file and script files don't - - match hier with - | :? IProvideProjectSite as _siteProvider when not (IsScript(filename)) -> - - // This is the path for .fs/.fsi files in legacy projects - () - | h when not (isNull h) && not (IsScript(filename)) -> - let docId = this.Workspace.CurrentSolution.GetDocumentIdsWithFilePath(filename).FirstOrDefault() - match docId with - | null -> - if not (h.IsCapabilityMatch("CPS")) then - - // This is the path when opening out-of-project .fs/.fsi files in CPS projects - - let fileContents = VsTextLines.GetFileContents(textLines, textViewAdapter) - singleFileWorkspaceMap.AddFile(filename, fileContents, hier) - | _ -> () - | _ -> - - // This is the path for both in-project and out-of-project .fsx files - - let fileContents = VsTextLines.GetFileContents(textLines, textViewAdapter) - singleFileWorkspaceMap.AddFile(filename, fileContents, hier) - - | _ -> () - | _ -> () diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SingleFileWorkspaceMap.fs b/vsintegration/src/FSharp.Editor/LanguageService/SingleFileWorkspaceMap.fs deleted file mode 100644 index 144c3cd269e..00000000000 --- a/vsintegration/src/FSharp.Editor/LanguageService/SingleFileWorkspaceMap.fs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - -namespace Microsoft.VisualStudio.FSharp.Editor - -#nowarn "40" - -open System -open System.Collections.Concurrent -open System.Collections.Generic -open System.Diagnostics -open System.IO -open System.Linq -open System.Runtime.CompilerServices -open Microsoft.CodeAnalysis -open Microsoft.VisualStudio -open Microsoft.VisualStudio.FSharp.Editor -open Microsoft.VisualStudio.FSharp.Editor.SiteProvider -open Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem -open Microsoft.VisualStudio.LanguageServices.ProjectSystem -open Microsoft.VisualStudio.Shell.Interop - -[] -type internal SingleFileWorkspaceMap(workspace: VisualStudioWorkspaceImpl, - projectInfoManager: FSharpProjectOptionsManager, - projectContextFactory: IWorkspaceProjectContextFactory) = - - let mutable isInitialized = false - let singleFileProjects = ConcurrentDictionary<_, IWorkspaceProjectContext>() - - let tryRemoveSingleFileProject projectId = - match singleFileProjects.TryRemove(projectId) with - | true, project -> - projectInfoManager.ClearInfoForSingleFileProject(projectId) - project.Dispose() - | _ -> () - - let projectDisplayNameOf projectFileName = - if String.IsNullOrWhiteSpace projectFileName then projectFileName - else Path.GetFileNameWithoutExtension projectFileName - - let tryGetOrCreateProjectId (workspace: VisualStudioWorkspaceImpl) (projectFileName: string) = - let projectDisplayName = projectDisplayNameOf projectFileName - Some (workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectDisplayName)) - - member this.Initialize() = - if not isInitialized then - workspace.DocumentClosed.Add <| fun args -> tryRemoveSingleFileProject args.Document.Project.Id - isInitialized <- true - - member this.AddFile(fileName: string, fileContents: string, hier: IVsHierarchy) = - if not isInitialized then - failwith "SingleFileWorkspaceMap is not initialized." - - let loadTime = DateTime.Now - let projectFileName = fileName - let projectDisplayName = projectDisplayNameOf projectFileName - - let mutable projectId = workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectDisplayName) - - if isNull (workspace.ProjectTracker.GetProject projectId) then - let projectContext = projectContextFactory.CreateProjectContext(FSharpConstants.FSharpLanguageName, projectDisplayName, projectFileName, projectId.Id, hier, null) - - projectId <- workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectDisplayName) - - projectContext.AddSourceFile(fileName) - - singleFileProjects.[projectId] <- projectContext - - let _referencedProjectFileNames, parsingOptions, projectOptions = projectInfoManager.ComputeSingleFileOptions (tryGetOrCreateProjectId workspace, fileName, loadTime, fileContents) |> Async.RunSynchronously - projectInfoManager.AddOrUpdateSingleFileProject(projectId, (loadTime, parsingOptions, projectOptions)) \ No newline at end of file From e6f40a7dc6772222cc78ea9bf8765dd095393f0f Mon Sep 17 00:00:00 2001 From: TIHan Date: Mon, 8 Oct 2018 16:36:46 -0700 Subject: [PATCH 3/4] Trying to finish up misc files workspace --- .../Diagnostics/UnusedDeclarationsAnalyzer.fs | 22 ++++---- .../Formatting/EditorFormattingService.fs | 2 +- .../Formatting/IndentationService.fs | 2 +- .../FSharpProjectOptionsManager.fs | 55 +++++++++---------- .../LanguageService/LanguageService.fs | 3 + 5 files changed, 40 insertions(+), 44 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs index 355cb0606e6..48e38e6c1d3 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs @@ -106,18 +106,16 @@ type internal UnusedDeclarationsAnalyzer() = do Trace.TraceInformation("{0:n3} (start) UnusedDeclarationsAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) do! Async.Sleep DefaultTuning.UnusedDeclarationsAnalyzerInitialDelay |> liftAsync // be less intrusive, give other work priority most of the time - match getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document) with - | Some (_parsingOptions, projectOptions) -> - let! sourceText = document.GetTextAsync() - let checker = getChecker document - let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, userOpName = userOpName) - let! allSymbolUsesInFile = checkResults.GetAllUsesOfAllSymbolsInFile() |> liftAsync - let unusedRanges = getUnusedDeclarationRanges allSymbolUsesInFile (isScriptFile document.FilePath) - return - unusedRanges - |> Seq.map (fun m -> Diagnostic.Create(Descriptor, RoslynHelpers.RangeToLocation(m, sourceText, document.FilePath))) - |> Seq.toImmutableArray - | None -> return ImmutableArray.Empty + let! _parsingOptions, projectOptions = getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document) + let! sourceText = document.GetTextAsync() + let checker = getChecker document + let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, userOpName = userOpName) + let! allSymbolUsesInFile = checkResults.GetAllUsesOfAllSymbolsInFile() |> liftAsync + let unusedRanges = getUnusedDeclarationRanges allSymbolUsesInFile (isScriptFile document.FilePath) + return + unusedRanges + |> Seq.map (fun m -> Diagnostic.Create(Descriptor, RoslynHelpers.RangeToLocation(m, sourceText, document.FilePath))) + |> Seq.toImmutableArray } |> Async.map (Option.defaultValue ImmutableArray.Empty) |> RoslynHelpers.StartAsyncAsTask cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Formatting/EditorFormattingService.fs b/vsintegration/src/FSharp.Editor/Formatting/EditorFormattingService.fs index 03061fa350f..fff52669f1f 100644 --- a/vsintegration/src/FSharp.Editor/Formatting/EditorFormattingService.fs +++ b/vsintegration/src/FSharp.Editor/Formatting/EditorFormattingService.fs @@ -74,7 +74,7 @@ type internal FSharpEditorFormattingService let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask let! options = document.GetOptionsAsync(cancellationToken) |> Async.AwaitTask let indentStyle = options.GetOption(FormattingOptions.SmartIndent, FSharpConstants.FSharpLanguageName) - let projectOptionsOpt = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document + let! projectOptionsOpt = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document let! textChange = FSharpEditorFormattingService.GetFormattingChanges(document.Id, sourceText, document.FilePath, checkerProvider.Checker, indentStyle, projectOptionsOpt, position) return diff --git a/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs b/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs index c4c293624bd..b7419be8bd4 100644 --- a/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs +++ b/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs @@ -103,7 +103,7 @@ type internal FSharpIndentationService let! options = document.GetOptionsAsync(cancellationToken) |> Async.AwaitTask let tabSize = options.GetOption(FormattingOptions.TabSize, FSharpConstants.FSharpLanguageName) let indentStyle = options.GetOption(FormattingOptions.SmartIndent, FSharpConstants.FSharpLanguageName) - let projectOptionsOpt = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document + let! projectOptionsOpt = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document let indent = FSharpIndentationService.GetDesiredIndentation(document.Id, sourceText, document.FilePath, lineNumber, tabSize, indentStyle, projectOptionsOpt) return match indent with diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index 6074f32d391..db710e22cec 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -47,26 +47,20 @@ type internal FSharpProjectOptionsManager member this.ClearInfoForProject(projectId:ProjectId) = projectOptionsTable.ClearInfoForProject(projectId) /// Get the exact options for a single-file script - member this.ComputeSingleFileOptions (tryGetOrCreateProjectId, fileName, loadTime, fileContents) = + member this.ComputeSingleFileOptions (fileName, loadTime, fileContents) = async { - let extraProjectInfo = Some(box workspace) - if SourceFile.MustBeSingleFileProject(fileName) then - // NOTE: we don't use a unique stamp for single files, instead comparing options structurally. - // This is because we repeatedly recompute the options. - let optionsStamp = None - let! options, _diagnostics = checkerProvider.Checker.GetProjectOptionsFromScript(fileName, fileContents, loadTime, [| |], ?extraProjectInfo=extraProjectInfo, ?optionsStamp=optionsStamp) - // NOTE: we don't use FCS cross-project references from scripts to projects. THe projects must have been - // compiled and #r will refer to files on disk - let referencedProjectFileNames = [| |] - let site = ProjectSitesAndFiles.CreateProjectSiteForScript(fileName, referencedProjectFileNames, options) - let deps, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, site, serviceProvider, (tryGetOrCreateProjectId fileName), fileName, options.ExtraProjectInfo, Some projectOptionsTable) - let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions) - return (deps, parsingOptions, projectOptions) - else - let site = ProjectSitesAndFiles.ProjectSiteOfSingleFile(fileName) - let deps, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, site, serviceProvider, (tryGetOrCreateProjectId fileName), fileName, extraProjectInfo, Some projectOptionsTable) - let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions) - return (deps, parsingOptions, projectOptions) + // NOTE: we don't use a unique stamp for single files, instead comparing options structurally. + // This is because we repeatedly recompute the options. + let extraProjectInfo = None + let optionsStamp = None + let! options, _diagnostics = checkerProvider.Checker.GetProjectOptionsFromScript(fileName, fileContents, loadTime, [| |], ?extraProjectInfo=extraProjectInfo, ?optionsStamp=optionsStamp) + // NOTE: we don't use FCS cross-project references from scripts to projects. THe projects must have been + // compiled and #r will refer to files on disk + let referencedProjectFileNames = [| |] + let site = ProjectSitesAndFiles.CreateProjectSiteForScript(fileName, referencedProjectFileNames, options) + let deps, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, site, serviceProvider, None, fileName, options.ExtraProjectInfo, Some projectOptionsTable) + let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions) + return (deps, parsingOptions, projectOptions) } /// Update the info for a project in the project table @@ -102,21 +96,22 @@ type internal FSharpProjectOptionsManager match this.TryGetOptionsForProject(projectId) with | Some(x) -> return Some(x) | _ -> - let! cancellationToken = Async.CancellationToken - let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask - // NOTE: we don't use FCS cross-project references from scripts to projects. The projects must have been - // compiled and #r will refer to files on disk. - let tryGetOrCreateProjectId _ = None - let! _referencedProjectFileNames, parsingOptions, projectOptions = this.ComputeSingleFileOptions (tryGetOrCreateProjectId, document.FilePath, DateTime.Now, sourceText.ToString()) - projectOptionsTable.AddOrUpdateProject(projectId, (fun _ -> [||], parsingOptions, None, projectOptions)) - return Some (parsingOptions, None, projectOptions) + // This is to handle misc single files + let! textVersion = document.GetTextVersionAsync() |> Async.AwaitTask + let! sourceText = document.GetTextAsync() |> Async.AwaitTask + let! _referencedProjectFileNames, parsingOptions, projectOptions = this.ComputeSingleFileOptions(document.FilePath, DateTime(int64(textVersion.GetHashCode())), sourceText.ToString()) + let result = (parsingOptions, None, projectOptions) + return Some(result) } /// Get the options for a document or project relevant for syntax processing. /// Quicker then TryGetOptionsForDocumentOrProject as it doesn't need to recompute the exact project options for a script. - member this.TryGetOptionsForEditingDocumentOrProject(document:Document) = - let projectId = document.Project.Id - this.TryGetOptionsForProject(projectId) |> Option.map(fun (parsingOptions, _, projectOptions) -> parsingOptions, projectOptions) + member this.TryGetOptionsForEditingDocumentOrProject(document:Document) = + async { + match! this.TryGetOptionsForDocumentOrProject(document) with + | Some(parsingOptions, _, projectOptions) -> return Some(parsingOptions, projectOptions) + | _ -> return None + } /// get a siteprovider member this.ProvideProjectSiteProvider(project:Project) = provideProjectSiteProvider(workspace, project, serviceProvider, Some projectOptionsTable) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 32880bc5272..1db919b24e9 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -175,6 +175,7 @@ type internal FSharpLanguageService(package : FSharpPackage, solution: IVsSoluti inherit AbstractLanguageService(package) let projectInfoManager = package.ComponentModel.DefaultExportProvider.GetExport().Value + let miscFilesWorkspace = package.ComponentModel.GetService() let mutable legacyProjectWorkspaceMap = Unchecked.defaultof @@ -183,6 +184,8 @@ type internal FSharpLanguageService(package : FSharpPackage, solution: IVsSoluti 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) + miscFilesWorkspace.Options <- miscFilesWorkspace.Options.WithChangedOption(Completion.CompletionOptions.BlockForCompletionItems, FSharpConstants.FSharpLanguageName, false) + miscFilesWorkspace.Options <- miscFilesWorkspace.Options.WithChangedOption(Shared.Options.ServiceFeatureOnOffOptions.ClosedFileDiagnostic, FSharpConstants.FSharpLanguageName, Nullable false) let projectContextFactory = package.ComponentModel.GetService() legacyProjectWorkspaceMap <- LegacyProjectWorkspaceMap(this.Workspace, solution, projectInfoManager, projectContextFactory, this.SystemServiceProvider) From e2b71c4fb8f1e41c0ea8277af2a96ec942358840 Mon Sep 17 00:00:00 2001 From: TIHan Date: Fri, 19 Oct 2018 14:14:46 -0700 Subject: [PATCH 4/4] Added caching mechanism for single files --- .../src/FSharp.Editor/FSharp.Editor.fsproj | 1 + .../FSharpProjectOptionsCache.fs | 110 ++++++++++++++++++ .../FSharpProjectOptionsManager.fs | 44 +++---- .../LanguageService/LanguageService.fs | 25 ++-- .../Navigation/NavigateToSearchService.fs | 2 +- .../Structure/BlockStructureService.fs | 11 +- 6 files changed, 147 insertions(+), 46 deletions(-) create mode 100644 vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsCache.fs diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index 3e011cb5d08..1683b26a46f 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -51,6 +51,7 @@ + diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsCache.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsCache.fs new file mode 100644 index 00000000000..7a5a4467c4f --- /dev/null +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsCache.fs @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Microsoft.VisualStudio.FSharp.Editor + +open System +open System.Collections.Concurrent +open System.Threading.Tasks +open System.Collections.Immutable +open System.ComponentModel.Composition +open System.IO +open System.Linq +open Microsoft.CodeAnalysis +open Microsoft.FSharp.Compiler.CompileOps +open Microsoft.FSharp.Compiler.SourceCodeServices +open Microsoft.VisualStudio +open Microsoft.VisualStudio.FSharp.Editor +open Microsoft.VisualStudio.FSharp.Editor.SiteProvider +open Microsoft.VisualStudio.LanguageServices +open Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem +open Microsoft.VisualStudio.Shell +open System.Threading +open System.Collections + +module private FSharpProjectOptions = + + /// Get the exact options for a single-file script + let computeSingleFileOptions fileName loadTime fileContents (checkerProvider: FSharpCheckerProvider) (settings: EditorOptions) projectOptionsTable serviceProvider = + async { + // NOTE: we don't use a unique stamp for single files, instead comparing options structurally. + // This is because we repeatedly recompute the options. + let extraProjectInfo = None + let optionsStamp = None + let! options, _diagnostics = checkerProvider.Checker.GetProjectOptionsFromScript(fileName, fileContents, loadTime, [| |], ?extraProjectInfo=extraProjectInfo, ?optionsStamp=optionsStamp) + // NOTE: we don't use FCS cross-project references from scripts to projects. THe projects must have been + // compiled and #r will refer to files on disk + let referencedProjectFileNames = [| |] + let site = ProjectSitesAndFiles.CreateProjectSiteForScript(fileName, referencedProjectFileNames, options) + let deps, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, site, serviceProvider, None, fileName, options.ExtraProjectInfo, Some projectOptionsTable) + let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions) + return (deps, parsingOptions, projectOptions) + } + +[] +type private FSharpProjectOptionsMessage = + | GetSingleFileOptions of Document * AsyncReplyChannel + +[] +type internal FSharpProjectOptionsCache (checkerProvider, settings, projectOptionsTable, serviceProvider) = + + let cancellationTokenSource = new CancellationTokenSource() + + let singleFileOptionsCache = ConcurrentDictionary() + + let cacheSingleFileOptions (document: Document) = + async { + let! textVersion = document.GetTextVersionAsync() |> Async.AwaitTask + let! sourceText = document.GetTextAsync() |> Async.AwaitTask + let timeStamp = DateTime.UtcNow + let! _referencedProjectFileNames, parsingOptions, projectOptions = + FSharpProjectOptions.computeSingleFileOptions document.FilePath timeStamp (sourceText.ToString()) checkerProvider settings projectOptionsTable serviceProvider + singleFileOptionsCache.[document.Id] <- (textVersion, parsingOptions, projectOptions) + return (parsingOptions, projectOptions) + } + + let tryGetSingleFileOptions (document: Document) f = + async { + let! textVersion = document.GetTextVersionAsync() |> Async.AwaitTask + + match singleFileOptionsCache.TryGetValue(document.Id) with + | true, (lastTextVersion, _, _) when textVersion <> lastTextVersion -> + return! f + | false, _ -> + return! f + | true, (_, parsingOptions, projectOptions) -> + return (parsingOptions, projectOptions) + } + + let loop (agent: MailboxProcessor) = + async { + while true do + try + match! agent.Receive() with + | FSharpProjectOptionsMessage.GetSingleFileOptions(document, reply) -> + let! (parsingOptions, projectOptions) = tryGetSingleFileOptions document (cacheSingleFileOptions document) + reply.Reply(parsingOptions, projectOptions) + with + | _ -> () + } + + let agent = MailboxProcessor.Start((fun agent -> loop agent), cancellationToken = cancellationTokenSource.Token) + + member this.TryGetProjectOptionsAsync(document: Document) = + match projectOptionsTable.TryGetOptionsForProject(document.Project.Id) with + | Some(result) when not (isScriptFile document.FilePath) -> async { return Some(result) } + | _ -> + async { + let! (parsingOptions, projectOptions) = + tryGetSingleFileOptions document (agent.PostAndAsyncReply(fun reply -> FSharpProjectOptionsMessage.GetSingleFileOptions(document, reply))) + return Some(parsingOptions, None, projectOptions) + } + + member __.ClearSingleDocumentCacheById(documentId: DocumentId) = + singleFileOptionsCache.TryRemove(documentId) |> ignore + + interface IDisposable with + + member __.Dispose() = + (agent :> IDisposable).Dispose() + cancellationTokenSource.Cancel() + cancellationTokenSource.Dispose() \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index db710e22cec..164f182e402 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -3,7 +3,6 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System -open System.Collections.Concurrent open System.Collections.Immutable open System.ComponentModel.Composition open System.IO @@ -29,6 +28,7 @@ type internal FSharpProjectOptionsManager ( checkerProvider: FSharpCheckerProvider, [)>] workspace: VisualStudioWorkspaceImpl, + [)>] miscWorkspace: MiscellaneousFilesWorkspace, [)>] serviceProvider: System.IServiceProvider, settings: EditorOptions ) = @@ -36,33 +36,27 @@ type internal FSharpProjectOptionsManager // A table of information about projects, excluding single-file projects. let projectOptionsTable = FSharpProjectOptionsTable() + let cache = new FSharpProjectOptionsCache(checkerProvider, settings, projectOptionsTable, serviceProvider) + let tryGetOrCreateProjectId (projectFileName:string) = let projectDisplayName = projectDisplayNameOf projectFileName Some (workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectDisplayName)) + do + workspace.DocumentClosed.Add(fun args -> + cache.ClearSingleDocumentCacheById(args.Document.Id) + ) + + miscWorkspace.DocumentClosed.Add(fun args -> + cache.ClearSingleDocumentCacheById(args.Document.Id) + ) + /// Retrieve the projectOptionsTable member __.FSharpOptions = projectOptionsTable /// Clear a project from the project table member this.ClearInfoForProject(projectId:ProjectId) = projectOptionsTable.ClearInfoForProject(projectId) - /// Get the exact options for a single-file script - member this.ComputeSingleFileOptions (fileName, loadTime, fileContents) = - async { - // NOTE: we don't use a unique stamp for single files, instead comparing options structurally. - // This is because we repeatedly recompute the options. - let extraProjectInfo = None - let optionsStamp = None - let! options, _diagnostics = checkerProvider.Checker.GetProjectOptionsFromScript(fileName, fileContents, loadTime, [| |], ?extraProjectInfo=extraProjectInfo, ?optionsStamp=optionsStamp) - // NOTE: we don't use FCS cross-project references from scripts to projects. THe projects must have been - // compiled and #r will refer to files on disk - let referencedProjectFileNames = [| |] - let site = ProjectSitesAndFiles.CreateProjectSiteForScript(fileName, referencedProjectFileNames, options) - let deps, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, site, serviceProvider, None, fileName, options.ExtraProjectInfo, Some projectOptionsTable) - let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions) - return (deps, parsingOptions, projectOptions) - } - /// Update the info for a project in the project table member this.UpdateProjectInfo(tryGetOrCreateProjectId, projectId, site, userOpName, invalidateConfig) = Logger.Log LogEditorFunctionId.LanguageService_UpdateProjectInfo @@ -90,19 +84,7 @@ type internal FSharpProjectOptionsManager /// Get the exact options for a document or project member this.TryGetOptionsForDocumentOrProject(document: Document) = - async { - let projectId = document.Project.Id - - match this.TryGetOptionsForProject(projectId) with - | Some(x) -> return Some(x) - | _ -> - // This is to handle misc single files - let! textVersion = document.GetTextVersionAsync() |> Async.AwaitTask - let! sourceText = document.GetTextAsync() |> Async.AwaitTask - let! _referencedProjectFileNames, parsingOptions, projectOptions = this.ComputeSingleFileOptions(document.FilePath, DateTime(int64(textVersion.GetHashCode())), sourceText.ToString()) - let result = (parsingOptions, None, projectOptions) - return Some(result) - } + cache.TryGetProjectOptionsAsync(document) /// Get the options for a document or project relevant for syntax processing. /// Quicker then TryGetOptionsForDocumentOrProject as it doesn't need to recompute the exact project options for a script. diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 1db919b24e9..858bd174453 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -143,19 +143,24 @@ type internal FSharpPackage() as this = vfsiToolWindow <- this.FindToolWindow(typeof, 0, true) :?> Microsoft.VisualStudio.FSharp.Interactive.FsiToolWindow vfsiToolWindow :> Microsoft.VisualStudio.FSharp.Interactive.ITestVFSI + let getCommandService () = + this.GetService() :?> OleMenuCommandService // FSI-LINKAGE-POINT + // FSI-LINKAGE-POINT: unsited init do Microsoft.VisualStudio.FSharp.Interactive.Hooks.fsiConsoleWindowPackageCtorUnsited (this :> Package) - override this.Initialize() = - base.Initialize() - - // FSI-LINKAGE-POINT: sited init - let commandService = this.GetService(typeof) :?> OleMenuCommandService // FSI-LINKAGE-POINT - Microsoft.VisualStudio.FSharp.Interactive.Hooks.fsiConsoleWindowPackageInitalizeSited (this :> Package) commandService - // FSI-LINKAGE-POINT: private method GetDialogPage forces fsi options to be loaded - let _fsiPropertyPage = this.GetDialogPage(typeof) - - () + override this.InitializeAsync(cancellationToken, progress) = + let baseTask = base.InitializeAsync(cancellationToken, progress) + async { + do! Async.AwaitTask baseTask + + // FSI-LINKAGE-POINT: sited init + let commandService = getCommandService () + Microsoft.VisualStudio.FSharp.Interactive.Hooks.fsiConsoleWindowPackageInitalizeSited (this :> Package) commandService + // FSI-LINKAGE-POINT: private method GetDialogPage forces fsi options to be loaded + let _fsiPropertyPage = this.GetDialogPage(typeof) + () + } |> RoslynHelpers.StartAsyncAsTask cancellationToken :> System.Threading.Tasks.Task override this.RoslynLanguageName = FSharpConstants.FSharpLanguageName override this.CreateWorkspace() = this.ComponentModel.GetService() diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs index ad3f894b730..a1622f10e4f 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs @@ -245,7 +245,7 @@ type internal FSharpNavigateToSearchService | _ -> NavigateToMatchKind.Regular interface INavigateToSearchService_RemoveInterfaceAboveAndRenameThisAfterInternalsVisibleToUsersUpdate with - member __.SearchProjectAsync(project, searchPattern, kinds, cancellationToken) : Task> = + member __.SearchProjectAsync(project, _, searchPattern, kinds, cancellationToken) : Task> = asyncMaybe { let! parsingOptions, _site, _options = projectInfoManager.TryGetOptionsForProject(project.Id) let! items = diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index af3341351a6..d3e3461f9d7 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -150,10 +150,13 @@ type internal FSharpBlockStructureService(checker: FSharpChecker, projectInfoMan override __.GetBlockStructureAsync(document, cancellationToken) : Task = asyncMaybe { - let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) - let! sourceText = document.GetTextAsync(cancellationToken) - let! parsedInput = checker.ParseDocument(document, parsingOptions, sourceText, userOpName) - return createBlockSpans document.FSharpOptions.Advanced.IsBlockStructureEnabled sourceText parsedInput |> Seq.toImmutableArray + try + let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! sourceText = document.GetTextAsync(cancellationToken) + let! parsedInput = checker.ParseDocument(document, parsingOptions, sourceText, userOpName) + return createBlockSpans document.FSharpOptions.Advanced.IsBlockStructureEnabled sourceText parsedInput |> Seq.toImmutableArray + with + | _ex -> return ImmutableArray<_>.Empty } |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) |> Async.map BlockStructure