@@ -49,190 +49,168 @@ type internal RoamingProfileStorageLocation(keyName: string) =
4949
5050[<Composition.Shared>]
5151[<ExportWorkspaceServiceFactory( typeof< IFSharpWorkspaceService>, ServiceLayer.Default) >]
52- type internal FSharpWorkspaceServiceFactory [<Composition.ImportingConstructor>] ( metadataAsSourceService : FSharpMetadataAsSourceService ) =
52+ type internal FSharpWorkspaceServiceFactory
53+ [<Composition.ImportingConstructor>]
54+ ( editorOptions: EditorOptions, metadataAsSourceService: FSharpMetadataAsSourceService) =
55+
56+ let tryGetMetadataSnapshot ( workspace : VisualStudioWorkspace ) ( path , timeStamp ) =
57+ try
58+ let md =
59+ LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetMetadata( workspace, path, timeStamp)
60+
61+ let amd = ( md :?> AssemblyMetadata)
62+ let mmd = amd.GetModules().[ 0 ]
63+ let mmr = mmd.GetMetadataReader()
64+
65+ // "lifetime is timed to Metadata you got from the GetMetadata(...). As long as you hold it strongly, raw
66+ // memory we got from metadata reader will be alive. Once you are done, just let everything go and
67+ // let finalizer handle resource rather than calling Dispose from Metadata directly. It is shared metadata.
68+ // You shouldn't dispose it directly."
69+
70+ let objToHold = box md
71+
72+ // We don't expect any ilread WeakByteFile to be created when working in Visual Studio
73+ // Debug.Assert((FSharp.Compiler.AbstractIL.ILBinaryReader.GetStatistics().weakByteFileCount = 0), "Expected weakByteFileCount to be zero when using F# in Visual Studio. Was there a problem reading a .NET binary?")
74+
75+ Some( objToHold, NativePtr.toNativeInt mmr.MetadataPointer, mmr.MetadataLength)
76+ with ex ->
77+ // We catch all and let the backup routines in the F# compiler find the error
78+ Assert.Exception( ex)
79+ None
80+
81+ let getSource ( workspace : Workspace ) filename =
82+ async {
83+ let! ct = Async.CancellationToken
84+
85+ match workspace.CurrentSolution.TryGetDocumentFromPath filename with
86+ | ValueSome document ->
87+ let! text = document.GetTextAsync( ct) |> Async.AwaitTask
88+ return Some( text.ToFSharpSourceText())
89+ | ValueNone -> return None
90+ }
91+
92+ let enableParallelReferenceResolution =
93+ editorOptions.LanguageServicePerformance.EnableParallelReferenceResolution
94+
95+ let enableLiveBuffers = editorOptions.Advanced.IsUseLiveBuffersEnabled
96+
97+ let enableInMemoryCrossProjectReferences =
98+ editorOptions.LanguageServicePerformance.EnableInMemoryCrossProjectReferences
99+
100+ let enableFastFindReferences =
101+ editorOptions.LanguageServicePerformance.EnableFastFindReferencesAndRename
102+
103+ let isInlineParameterNameHintsEnabled =
104+ editorOptions.Advanced.IsInlineParameterNameHintsEnabled
105+
106+ let isInlineTypeHintsEnabled = editorOptions.Advanced.IsInlineTypeHintsEnabled
107+
108+ let isInlineReturnTypeHintsEnabled =
109+ editorOptions.Advanced.IsInlineReturnTypeHintsEnabled
110+
111+ let enablePartialTypeChecking =
112+ editorOptions.LanguageServicePerformance.EnablePartialTypeChecking
113+
114+ // Default should be false
115+ let keepAllBackgroundResolutions =
116+ editorOptions.LanguageServicePerformance.KeepAllBackgroundResolutions
117+
118+ // Default should be false
119+ let keepAllBackgroundSymbolUses =
120+ editorOptions.LanguageServicePerformance.KeepAllBackgroundSymbolUses
121+
122+ // Default should be true
123+ let enableBackgroundItemKeyStoreAndSemanticClassification =
124+ editorOptions.LanguageServicePerformance.EnableBackgroundItemKeyStoreAndSemanticClassification
125+
126+ let useTransparentCompiler = editorOptions.Advanced.UseTransparentCompiler
127+
128+ // Default is false here
129+ let solutionCrawler = editorOptions.Advanced.SolutionBackgroundAnalysis
130+
131+ let create getSource tryGetMetadataSnapshot =
132+
133+ use _eventDuration =
134+ TelemetryReporter.ReportSingleEventWithDuration(
135+ TelemetryEvents.LanguageServiceStarted,
136+ [|
137+ nameof enableLiveBuffers, enableLiveBuffers
138+ nameof enableParallelReferenceResolution, enableParallelReferenceResolution
139+ nameof enableInMemoryCrossProjectReferences, enableInMemoryCrossProjectReferences
140+ nameof enableFastFindReferences, enableFastFindReferences
141+ nameof isInlineParameterNameHintsEnabled, isInlineParameterNameHintsEnabled
142+ nameof isInlineTypeHintsEnabled, isInlineTypeHintsEnabled
143+ nameof isInlineReturnTypeHintsEnabled, isInlineReturnTypeHintsEnabled
144+ nameof enablePartialTypeChecking, enablePartialTypeChecking
145+ nameof keepAllBackgroundResolutions, keepAllBackgroundResolutions
146+ nameof keepAllBackgroundSymbolUses, keepAllBackgroundSymbolUses
147+ nameof enableBackgroundItemKeyStoreAndSemanticClassification, enableBackgroundItemKeyStoreAndSemanticClassification
148+ " captureIdentifiersWhenParsing" , enableFastFindReferences
149+ nameof useTransparentCompiler, useTransparentCompiler
150+ nameof solutionCrawler, solutionCrawler
151+ |],
152+ TelemetryThrottlingStrategy.NoThrottling
153+ )
53154
54- // We have a lock just in case if multi-threads try to create a new IFSharpWorkspaceService -
55- // but we only want to have a single instance of the FSharpChecker regardless if there are multiple instances of IFSharpWorkspaceService.
56- // In VS, we only ever have a single IFSharpWorkspaceService, but for testing we may have multiple; we still only want a
57- // single FSharpChecker instance shared across them.
58- static let gate = obj ()
155+ let checker =
156+ FSharpChecker.Create(
157+ projectCacheSize = 5000 , // We do not care how big the cache is. VS will actually tell FCS to clear caches, so this is fine.
158+ keepAllBackgroundResolutions = keepAllBackgroundResolutions,
159+ legacyReferenceResolver = LegacyMSBuildReferenceResolver.getResolver (),
160+ tryGetMetadataSnapshot = tryGetMetadataSnapshot,
161+ keepAllBackgroundSymbolUses = keepAllBackgroundSymbolUses,
162+ enableBackgroundItemKeyStoreAndSemanticClassification = enableBackgroundItemKeyStoreAndSemanticClassification,
163+ enablePartialTypeChecking = enablePartialTypeChecking,
164+ parallelReferenceResolution = enableParallelReferenceResolution,
165+ captureIdentifiersWhenParsing = enableFastFindReferences,
166+ documentSource =
167+ ( if enableLiveBuffers then
168+ ( DocumentSource.Custom( fun filename ->
169+ async {
170+ match ! getSource filename with
171+ | Some source -> return Some( source :> ISourceText)
172+ | None -> return None
173+ }))
174+ else
175+ DocumentSource.FileSystem),
176+ useTransparentCompiler = useTransparentCompiler
177+ )
59178
60- // We only ever want to have a single FSharpChecker.
61- static let mutable checkerSingleton = None
179+ checker
62180
63181 interface IWorkspaceServiceFactory with
64- member _.CreateService ( workspaceServices ) =
65182
183+ member _.CreateService ( workspaceServices ) =
66184 let workspace = workspaceServices.Workspace
185+ let getSource = getSource workspace
67186
68- let tryGetMetadataSnapshot ( path , timeStamp ) =
187+ let tryGetMetadataSnapshot =
69188 match workspace with
70- | :? VisualStudioWorkspace as workspace ->
71- try
72- let md =
73- LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetMetadata( workspace, path, timeStamp)
74-
75- let amd = ( md :?> AssemblyMetadata)
76- let mmd = amd.GetModules().[ 0 ]
77- let mmr = mmd.GetMetadataReader()
78-
79- // "lifetime is timed to Metadata you got from the GetMetadata(...). As long as you hold it strongly, raw
80- // memory we got from metadata reader will be alive. Once you are done, just let everything go and
81- // let finalizer handle resource rather than calling Dispose from Metadata directly. It is shared metadata.
82- // You shouldn't dispose it directly."
83-
84- let objToHold = box md
85-
86- // We don't expect any ilread WeakByteFile to be created when working in Visual Studio
87- // Debug.Assert((FSharp.Compiler.AbstractIL.ILBinaryReader.GetStatistics().weakByteFileCount = 0), "Expected weakByteFileCount to be zero when using F# in Visual Studio. Was there a problem reading a .NET binary?")
88-
89- Some( objToHold, NativePtr.toNativeInt mmr.MetadataPointer, mmr.MetadataLength)
90- with ex ->
91- // We catch all and let the backup routines in the F# compiler find the error
92- Assert.Exception( ex)
93- None
94- | _ -> None
95-
96- let getSource filename =
97- async {
98- let! ct = Async.CancellationToken
99-
100- match workspace.CurrentSolution.TryGetDocumentFromPath filename with
101- | ValueSome document ->
102- let! text = document.GetTextAsync( ct) |> Async.AwaitTask
103- return Some( text.ToFSharpSourceText())
104- | ValueNone -> return None
105- }
189+ | :? VisualStudioWorkspace as workspace -> tryGetMetadataSnapshot workspace
190+ | _ -> fun _ -> None
106191
107- lock gate ( fun () ->
108- match checkerSingleton with
109- | Some _ -> ()
110- | _ ->
111- let checker =
112- lazy
113- let editorOptions = workspace.Services.GetService< EditorOptions>()
114-
115- let enableParallelReferenceResolution =
116- editorOptions.LanguageServicePerformance.EnableParallelReferenceResolution
117-
118- let enableLiveBuffers = editorOptions.Advanced.IsUseLiveBuffersEnabled
119-
120- let enableInMemoryCrossProjectReferences =
121- editorOptions.LanguageServicePerformance.EnableInMemoryCrossProjectReferences
122-
123- let enableFastFindReferences =
124- editorOptions.LanguageServicePerformance.EnableFastFindReferencesAndRename
125-
126- let isInlineParameterNameHintsEnabled =
127- editorOptions.Advanced.IsInlineParameterNameHintsEnabled
128-
129- let isInlineTypeHintsEnabled = editorOptions.Advanced.IsInlineTypeHintsEnabled
130-
131- let isInlineReturnTypeHintsEnabled =
132- editorOptions.Advanced.IsInlineReturnTypeHintsEnabled
133-
134- let enablePartialTypeChecking =
135- editorOptions.LanguageServicePerformance.EnablePartialTypeChecking
136-
137- // Default should be false
138- let keepAllBackgroundResolutions =
139- editorOptions.LanguageServicePerformance.KeepAllBackgroundResolutions
140-
141- // Default should be false
142- let keepAllBackgroundSymbolUses =
143- editorOptions.LanguageServicePerformance.KeepAllBackgroundSymbolUses
144-
145- // Default should be true
146- let enableBackgroundItemKeyStoreAndSemanticClassification =
147- editorOptions.LanguageServicePerformance.EnableBackgroundItemKeyStoreAndSemanticClassification
148-
149- let useTransparentCompiler = editorOptions.Advanced.UseTransparentCompiler
150-
151- // Default is false here
152- let solutionCrawler = editorOptions.Advanced.SolutionBackgroundAnalysis
153-
154- use _ eventDuration =
155- TelemetryReporter.ReportSingleEventWithDuration(
156- TelemetryEvents.LanguageServiceStarted,
157- [|
158- nameof enableLiveBuffers, enableLiveBuffers
159- nameof enableParallelReferenceResolution, enableParallelReferenceResolution
160- nameof enableInMemoryCrossProjectReferences, enableInMemoryCrossProjectReferences
161- nameof enableFastFindReferences, enableFastFindReferences
162- nameof isInlineParameterNameHintsEnabled, isInlineParameterNameHintsEnabled
163- nameof isInlineTypeHintsEnabled, isInlineTypeHintsEnabled
164- nameof isInlineReturnTypeHintsEnabled, isInlineReturnTypeHintsEnabled
165- nameof enablePartialTypeChecking, enablePartialTypeChecking
166- nameof keepAllBackgroundResolutions, keepAllBackgroundResolutions
167- nameof keepAllBackgroundSymbolUses, keepAllBackgroundSymbolUses
168- nameof enableBackgroundItemKeyStoreAndSemanticClassification,
169- enableBackgroundItemKeyStoreAndSemanticClassification
170- " captureIdentifiersWhenParsing" , enableFastFindReferences
171- nameof useTransparentCompiler, useTransparentCompiler
172- nameof solutionCrawler, solutionCrawler
173- |],
174- TelemetryThrottlingStrategy.NoThrottling
175- )
176-
177- let checker =
178- FSharpChecker.Create(
179- projectCacheSize = 5000 , // We do not care how big the cache is. VS will actually tell FCS to clear caches, so this is fine.
180- keepAllBackgroundResolutions = keepAllBackgroundResolutions,
181- legacyReferenceResolver = LegacyMSBuildReferenceResolver.getResolver (),
182- tryGetMetadataSnapshot = tryGetMetadataSnapshot,
183- keepAllBackgroundSymbolUses = keepAllBackgroundSymbolUses,
184- enableBackgroundItemKeyStoreAndSemanticClassification =
185- enableBackgroundItemKeyStoreAndSemanticClassification,
186- enablePartialTypeChecking = enablePartialTypeChecking,
187- parallelReferenceResolution = enableParallelReferenceResolution,
188- captureIdentifiersWhenParsing = enableFastFindReferences,
189- documentSource =
190- ( if enableLiveBuffers then
191- ( DocumentSource.Custom( fun filename ->
192- async {
193- match ! getSource filename with
194- | Some source -> return Some( source :> ISourceText)
195- | None -> return None
196- }))
197- else
198- DocumentSource.FileSystem),
199- useTransparentCompiler = useTransparentCompiler
200- )
201-
202- if enableLiveBuffers && not useTransparentCompiler then
203- workspace.WorkspaceChanged.Add( fun args ->
204- if args.DocumentId <> null then
205- cancellableTask {
206- let document = args.NewSolution.GetDocument( args.DocumentId)
207-
208- let! _ , _ , _ , options =
209- document.GetFSharpCompilationOptionsAsync( nameof ( workspace.WorkspaceChanged))
210-
211- do ! checker.NotifyFileChanged( document.FilePath, options)
212- }
213- |> CancellableTask.startAsTask CancellationToken.None
214- |> ignore)
215-
216- checker
217-
218- checkerSingleton <- Some checker)
219-
220- let optionsManager =
221- lazy
222- match checkerSingleton with
223- | Some checker -> FSharpProjectOptionsManager( checker.Value, workspaceServices.Workspace)
224- | _ -> failwith " Checker not set."
192+ let checker = create getSource tryGetMetadataSnapshot
225193
226- { new IFSharpWorkspaceService with
227- member _.Checker =
228- match checkerSingleton with
229- | Some checker -> checker.Value
230- | _ -> failwith " Checker not set."
194+ if enableLiveBuffers && not useTransparentCompiler then
195+ workspace.WorkspaceChanged.Add( fun args ->
196+ if args.DocumentId <> null then
197+ cancellableTask {
198+ let document = args.NewSolution.GetDocument( args.DocumentId)
199+
200+ let! _ , _ , _ , options = document.GetFSharpCompilationOptionsAsync( nameof ( workspace.WorkspaceChanged))
201+
202+ do ! checker.NotifyFileChanged( document.FilePath, options)
203+ }
204+ |> CancellableTask.startAsTask CancellationToken.None
205+ |> ignore)
231206
232- member _.FSharpProjectOptionsManager = optionsManager.Value
207+ let optionsManager = FSharpProjectOptionsManager( checker, workspace)
208+
209+ { new IFSharpWorkspaceService with
210+ member _.Checker = checker
211+ member _.FSharpProjectOptionsManager = optionsManager
233212 member _.MetadataAsSource = metadataAsSourceService
234213 }
235- :> _
236214
237215[<Sealed>]
238216type private FSharpSolutionEvents ( projectManager : FSharpProjectOptionsManager , metadataAsSource : FSharpMetadataAsSourceService ) =
0 commit comments