@@ -125,10 +125,9 @@ type internal ProjectInfoManager
125125
126126/// Get the options for a project
127127member this.TryGetOptionsForProject ( projectId : ProjectId ) =
128- if projectTable.ContainsKey( projectId) then
129- Some( projectTable.[ projectId])
130- else
131- None
128+ match projectTable.TryGetValue( projectId) with
129+ | true , options-> Some options
130+ | _ -> None
132131
133132/// Get the exact options for a document or project
134133member this.TryGetOptionsForDocumentOrProject ( document : Document ) = async {
@@ -137,9 +136,9 @@ type internal ProjectInfoManager
137136// The options for a single-file script project are re-requested each time the file is analyzed. This is because the
138137// single-file project may contain #load and #r references which are changing as the user edits, and we may need to re-analyze
139138// to determine the latest settings. FCS keeps a cache to help ensure these are up-to-date.
140- if singleFileProjectTable.ContainsKey( projectId) then
139+ match singleFileProjectTable.TryGetValue( projectId) with
140+ | true , ( loadTime,_) ->
141141try
142- let loadTime , _ = singleFileProjectTable.[ projectId]
143142let fileName = document.FilePath
144143let! cancellationToken = Async.CancellationToken
145144let! sourceText = document.GetTextAsync( cancellationToken)
@@ -149,18 +148,16 @@ type internal ProjectInfoManager
149148with ex->
150149 Assert.Exception( ex)
151150return None
152- else return this.TryGetOptionsForProject( projectId)
151+ | _ -> return this.TryGetOptionsForProject( projectId)
153152}
154153
155154/// Get the options for a document or project relevant for syntax processing.
156155/// Quicker then TryGetOptionsForDocumentOrProject as it doesn't need to recompute the exact project options for a script.
157156member this.TryGetOptionsForEditingDocumentOrProject ( document : Document ) =
158157let projectId = document.Project.Id
159- if singleFileProjectTable.ContainsKey( projectId) then
160- let _loadTime , originalOptions = singleFileProjectTable.[ projectId]
161- Some originalOptions
162- else
163- this.TryGetOptionsForProject( projectId)
158+ match singleFileProjectTable.TryGetValue( projectId) with
159+ | true , (_ loadTime, originalOptions) -> Some originalOptions
160+ | _ -> this.TryGetOptionsForProject( projectId)
164161
165162// Used to expose FSharpChecker/ProjectInfo manager to diagnostic providers
166163// Diagnostic providers can be executed in environment that does not use MEF so they can rely only
@@ -245,32 +242,59 @@ and
245242internal FSharpLanguageService ( package : FSharpPackage ) =
246243inherit AbstractLanguageService< FSharpPackage, FSharpLanguageService>( package)
247244
248- let checkerProvider = package.ComponentModel.DefaultExportProvider.GetExport< FSharpCheckerProvider>() .Value
249245let projectInfoManager = package.ComponentModel.DefaultExportProvider.GetExport< ProjectInfoManager>() .Value
250246
251247let projectDisplayNameOf projectFileName =
252248if String.IsNullOrWhiteSpace projectFileNamethen projectFileName
253249else Path.GetFileNameWithoutExtension projectFileName
254250
255- let openedProjects = Queue< IProvideProjectSite>()
251+ let singleFileProjects = ConcurrentDictionary<_, AbstractProject>()
252+
253+ let tryRemoveSingleFileProject projectId =
254+ match singleFileProjects.TryRemove( projectId) with
255+ | true , project->
256+ projectInfoManager.RemoveSingleFileProject( projectId)
257+ project.Disconnect()
258+ | _ -> ()
259+
260+ override this.Initialize () =
261+ base .Initialize()
256262
257- do
258- Events.SolutionEvents.OnAfterOpenProject.Add( fun ( args : Events.OpenProjectEventArgs ) ->
259- match args.Hierarchywith
260- | :? IProvideProjectSiteas siteProvider->
261- openedProjects.Enqueue( siteProvider)
262- | _ -> ())
263+ this.Workspace.Options<- this.Workspace.Options.WithChangedOption( Completion.CompletionOptions.BlockForCompletionItems, FSharpCommonConstants.FSharpLanguageName, false )
264+ this.Workspace.Options<- this.Workspace.Options.WithChangedOption( Shared.Options.ServiceFeatureOnOffOptions.ClosedFileDiagnostic, FSharpCommonConstants.FSharpLanguageName, Nullablefalse )
265+
266+ this.Workspace.DocumentClosed.Add<| fun args ->
267+ tryRemoveSingleFileProject args.Document.Project.Id
268+
269+ Events.SolutionEvents.OnAfterCloseSolution.Add<| fun _ ->
270+ singleFileProjects.Keys|> Seq.iter tryRemoveSingleFileProject
271+
272+ let ctx = System.Threading.SynchronizationContext.Current
263273
264- member this.BatchSetupProjects () =
265- if openedProjects.Count> 0 then
266- let workspace = package.ComponentModel.GetService< VisualStudioWorkspaceImpl>()
267- for siteProviderin openedProjectsdo
268- this.SetupProjectFile( siteProvider, workspace)
269- openedProjects.Clear()
274+ let rec setupProjectsAfterSolutionOpen () =
275+ async {
276+ use openedProjects= MailboxProcessor.Start<| fun inbox ->
277+ async {
278+ // waits for AfterOpenSolution and then starts projects setup
279+ do ! Async.AwaitEvent Events.SolutionEvents.OnAfterOpenSolution|> Async.Ignore
280+ while true do
281+ let! siteProvider = inbox.Receive()
282+ do ! Async.SwitchToContext ctx
283+ this.SetupProjectFile( siteProvider, this.Workspace) }
284+
285+ use _= Events.SolutionEvents.OnAfterOpenProject|> Observable.subscribe( fun args ->
286+ match args.Hierarchywith
287+ | :? IProvideProjectSiteas siteProvider-> openedProjects.Post( siteProvider)
288+ | _ -> () )
289+
290+ do ! Async.AwaitEvent Events.SolutionEvents.OnAfterCloseSolution|> Async.Ignore
291+ do ! setupProjectsAfterSolutionOpen()
292+ }
293+ setupProjectsAfterSolutionOpen() |> Async.StartImmediate
270294
271295/// Sync the information for the project
272296member this.SyncProject ( project : AbstractProject , projectContext : IWorkspaceProjectContext , site : IProjectSite , forceUpdate ) =
273-
297+ async {
274298let hashSetIgnoreCase x = new HashSet< string>( x, StringComparer.OrdinalIgnoreCase)
275299let updatedFiles = site.SourceFilesOnDisk() |> hashSetIgnoreCase
276300let workspaceFiles = project.GetCurrentDocuments() |> Seq.map( fun file -> file.FilePath) |> hashSetIgnoreCase
290314// update the cached options
291315if updatedthen
292316 projectInfoManager.UpdateProjectInfo( project.Id, site, project.Workspace)
317+ } |> Async.Start
293318
294319member this.SetupProjectFile ( siteProvider : IProvideProjectSite , workspace : VisualStudioWorkspaceImpl ) =
295320let rec setup ( site : IProjectSite ) =
341366 projectContext.AddSourceFile( fileName)
342367
343368let project = projectContext:?> AbstractProject
344- let documentId = project.GetCurrentDocumentFromPath( fileName) .Id
345-
346- let rec onDocumentClosed = EventHandler< DocumentEventArgs> ( fun _ args ->
347- if args.Document.Id= documentIdthen
348- projectInfoManager.RemoveSingleFileProject( projectId)
349- project.Disconnect()
350- workspace.DocumentClosed.RemoveHandler( onDocumentClosed)
351- )
352- workspace.DocumentClosed.AddHandler( onDocumentClosed)
369+ singleFileProjects.[ projectId] <- project
353370
354371override this.ContentTypeName = FSharpCommonConstants.FSharpContentTypeName
355372override this.LanguageName = FSharpCommonConstants.FSharpLanguageName
362379
363380override this.SetupNewTextView ( textView ) =
364381base .SetupNewTextView( textView)
365- let workspace = package.ComponentModel.GetService < VisualStudioWorkspaceImpl >()
382+
366383let textViewAdapter = package.ComponentModel.GetService< IVsEditorAdaptersFactoryService>()
367-
368- workspace.Options<- workspace.Options.WithChangedOption( Completion.CompletionOptions.BlockForCompletionItems, FSharpCommonConstants.FSharpLanguageName, false )
369- workspace.Options<- workspace.Options.WithChangedOption( Shared.Options.ServiceFeatureOnOffOptions.ClosedFileDiagnostic, FSharpCommonConstants.FSharpLanguageName, Nullablefalse )
370384
371385match textView.GetBuffer() with
372386| ( VSConstants.S_ OK, textLines) ->
@@ -375,38 +389,11 @@ and
375389| Some( hier, _) ->
376390match hierwith
377391| :? IProvideProjectSiteas siteProviderwhen not ( IsScript( filename)) ->
378- this.SetupProjectFile( siteProvider, workspace )
392+ this.SetupProjectFile( siteProvider, this.Workspace )
379393| _ ->
380394let fileContents = VsTextLines.GetFileContents( textLines, textViewAdapter)
381- this.SetupStandAloneFile( filename, fileContents, workspace , hier)
395+ this.SetupStandAloneFile( filename, fileContents, this.Workspace , hier)
382396| _ -> ()
383397| _ -> ()
384398
385- // This is the second half of the bridge between the F# IncrementalBuild analysis engine and the Roslyn analysis
386- // engine. When a document gets the focus, we call FSharpLanguageService.Checker.StartBackgroundCompile for the
387- // project containing that file. This ensures the F# IncrementalBuild engine starts analyzing the project.
388- let wpfTextView = textViewAdapter.GetWpfTextView( textView)
389- match wpfTextView.TextBuffer.Properties.TryGetProperty< ITextDocument>( typeof< ITextDocument>) with
390- | true , textDocument->
391- let filePath = textDocument.FilePath
392- let onGotFocus = new EventHandler( fun _ _ ->
393- for documentIdin workspace.CurrentSolution.GetDocumentIdsWithFilePath( filePath) do
394- let document = workspace.CurrentSolution.GetDocument( documentId)
395- match projectInfoManager.TryGetOptionsForEditingDocumentOrProject( document) with
396- | Some options-> checkerProvider.Checker.StartBackgroundCompile( options)
397- | None-> ()
398- )
399- let rec onViewClosed = new EventHandler( fun _ _ ->
400- wpfTextView.GotAggregateFocus.RemoveHandler( onGotFocus)
401- wpfTextView.Closed.RemoveHandler( onViewClosed)
402- )
403- wpfTextView.GotAggregateFocus.AddHandler( onGotFocus)
404- wpfTextView.Closed.AddHandler( onViewClosed)
405- | _ -> ()
406-
407- // When the text view is opened, setup all remaining projects on the background.
408- async {
409- do this.BatchSetupProjects()
410- }
411- |> Async.StartImmediate
412-
399+