@@ -15,17 +15,16 @@ open System.Linq
1515open System.Runtime .CompilerServices
1616open System.Runtime .InteropServices
1717open System.Threading
18-
19- open Microsoft.FSharp .Compiler .CompileOps
20- open Microsoft.FSharp .Compiler .SourceCodeServices
21- open Microsoft.VisualStudio .FSharp .LanguageService .SiteProvider
22-
2318open Microsoft.CodeAnalysis
2419open Microsoft.CodeAnalysis .Diagnostics
2520open Microsoft.CodeAnalysis .Completion
2621open Microsoft.CodeAnalysis .Options
22+ open Microsoft.FSharp .Compiler .CompileOps
23+ open Microsoft.FSharp .Compiler .SourceCodeServices
2724open Microsoft.VisualStudio
2825open Microsoft.VisualStudio .Editor
26+ open Microsoft.VisualStudio .FSharp .LanguageService
27+ open Microsoft.VisualStudio .FSharp .LanguageService .SiteProvider
2928open Microsoft.VisualStudio .TextManager .Interop
3029open Microsoft.VisualStudio .LanguageServices
3130open Microsoft.VisualStudio .LanguageServices .Implementation .LanguageService
@@ -34,7 +33,6 @@ open Microsoft.VisualStudio.LanguageServices.Implementation.TaskList
3433open Microsoft.VisualStudio .LanguageServices .ProjectSystem
3534open Microsoft.VisualStudio .Shell
3635open Microsoft.VisualStudio .Shell .Interop
37- open Microsoft.VisualStudio .FSharp .LanguageService
3836open Microsoft.VisualStudio .ComponentModelHost
3937
4038// Exposes FSharpChecker as MEF export
@@ -89,7 +87,7 @@ type internal FSharpCheckerProvider
8987// It also allows a 'cheaper' route to get the project options relevant to parsing (e.g. the #define values).
9088// The main entrypoints are TryGetOptionsForDocumentOrProject and TryGetOptionsForEditingDocumentOrProject.
9189[<Export( typeof< FSharpProjectOptionsManager>); Composition.Shared>]
92- type internal FSharpProjectOptionsManager
90+ type internal FSharpProjectOptionsManager
9391[<ImportingConstructor>]
9492(
9593 checkerProvider: FSharpCheckerProvider,
@@ -114,33 +112,36 @@ type internal FSharpProjectOptionsManager
114112/// Clear a project from the project table
115113member this.ClearInfoForProject ( projectId : ProjectId ) = projectOptionsTable.ClearInfoForProject( projectId)
116114
115+ /// Clear a project from the single file project table
117116member this.ClearInfoForSingleFileProject ( projectId ) =
118117 singleFileProjectTable.TryRemove( projectId) |> ignore
119118
119+ /// Update a project in the single file project table
120120member this.AddOrUpdateSingleFileProject ( projectId , data ) = singleFileProjectTable.[ projectId] <- data
121121
122122/// Get the exact options for a single-file script
123- member this.ComputeSingleFileOptions ( tryGetOrCreateProjectId , fileName , loadTime , fileContents , workspace : Workspace ) = async {
124- let extraProjectInfo = Some( box workspace)
125- let tryGetOptionsForReferencedProject f = f|> tryGetOrCreateProjectId|> Option.bind this.TryGetOptionsForProject|> Option.map( fun ( _ , _ , projectOptions ) -> projectOptions)
126- if SourceFile.MustBeSingleFileProject( fileName) then
127- // NOTE: we don't use a unique stamp for single files, instead comparing options structurally.
128- // This is because we repeatedly recompute the options.
129- let optionsStamp = None
130- let! options , _diagnostics = checkerProvider.Checker.GetProjectOptionsFromScript( fileName, fileContents, loadTime, [| |], ?extraProjectInfo= extraProjectInfo, ?optionsStamp= optionsStamp)
131- // NOTE: we don't use FCS cross-project references from scripts to projects. THe projects must have been
132- // compiled and #r will refer to files on disk
133- let referencedProjectFileNames = [| |]
134- let site = ProjectSitesAndFiles.CreateProjectSiteForScript( fileName, referencedProjectFileNames, options)
135- let deps , projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite( Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, site, serviceProvider, ( tryGetOrCreateProjectId fileName), fileName, options.ExtraProjectInfo, Some projectOptionsTable, true )
136- let parsingOptions , _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions( projectOptions)
137- return ( deps, parsingOptions, projectOptions)
138- else
139- let site = ProjectSitesAndFiles.ProjectSiteOfSingleFile( fileName)
140- let deps , projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite( Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, site, serviceProvider, ( tryGetOrCreateProjectId fileName), fileName, extraProjectInfo, Some projectOptionsTable, true )
141- let parsingOptions , _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions( projectOptions)
142- return ( deps, parsingOptions, projectOptions)
143- }
123+ member this.ComputeSingleFileOptions ( tryGetOrCreateProjectId , fileName , loadTime , fileContents ) =
124+ async {
125+ let extraProjectInfo = Some( box workspace)
126+ let tryGetOptionsForReferencedProject f = f|> tryGetOrCreateProjectId|> Option.bind this.TryGetOptionsForProject|> Option.map( fun ( _ , _ , projectOptions ) -> projectOptions)
127+ if SourceFile.MustBeSingleFileProject( fileName) then
128+ // NOTE: we don't use a unique stamp for single files, instead comparing options structurally.
129+ // This is because we repeatedly recompute the options.
130+ let optionsStamp = None
131+ let! options , _diagnostics = checkerProvider.Checker.GetProjectOptionsFromScript( fileName, fileContents, loadTime, [| |], ?extraProjectInfo= extraProjectInfo, ?optionsStamp= optionsStamp)
132+ // NOTE: we don't use FCS cross-project references from scripts to projects. THe projects must have been
133+ // compiled and #r will refer to files on disk
134+ let referencedProjectFileNames = [| |]
135+ let site = ProjectSitesAndFiles.CreateProjectSiteForScript( fileName, referencedProjectFileNames, options)
136+ let deps , projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite( Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, site, serviceProvider, ( tryGetOrCreateProjectId fileName), fileName, options.ExtraProjectInfo, Some projectOptionsTable, true )
137+ let parsingOptions , _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions( projectOptions)
138+ return ( deps, parsingOptions, projectOptions)
139+ else
140+ let site = ProjectSitesAndFiles.ProjectSiteOfSingleFile( fileName)
141+ let deps , projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite( Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, site, serviceProvider, ( tryGetOrCreateProjectId fileName), fileName, extraProjectInfo, Some projectOptionsTable, true )
142+ let parsingOptions , _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions( projectOptions)
143+ return ( deps, parsingOptions, projectOptions)
144+ }
144145
145146/// Update the info for a project in the project table
146147member this.UpdateProjectInfo ( tryGetOrCreateProjectId , projectId , site , userOpName ) =
@@ -164,32 +165,34 @@ type internal FSharpProjectOptionsManager
164165| _ -> FSharpParsingOptions.Default
165166 CompilerEnvironment.GetCompilationDefinesForEditing( document.Name, parsingOptions)
166167
168+ /// Try and get the Options for a project
167169member this.TryGetOptionsForProject ( projectId : ProjectId ) = projectOptionsTable.TryGetOptionsForProject( projectId)
168170
169171/// Get the exact options for a document or project
170- member this.TryGetOptionsForDocumentOrProject ( document : Document ) = async {
171- let projectId = document.Project.Id
172-
173- // The options for a single-file script project are re-requested each time the file is analyzed. This is because the
174- // single-file project may contain #load and #r references which are changing as the user edits, and we may need to re-analyze
175- // to determine the latest settings. FCS keeps a cache to help ensure these are up-to-date.
176- match singleFileProjectTable.TryGetValue( projectId) with
177- | true , ( loadTime, _, _) ->
178- try
179- let fileName = document.FilePath
180- let! cancellationToken = Async.CancellationToken
181- let! sourceText = document.GetTextAsync( cancellationToken) |> Async.AwaitTask
182- // NOTE: we don't use FCS cross-project references from scripts to projects. The projects must have been
183- // compiled and #r will refer to files on disk.
184- let tryGetOrCreateProjectId _ = None
185- let! _referencedProjectFileNames , parsingOptions , projectOptions = this.ComputeSingleFileOptions( tryGetOrCreateProjectId, fileName, loadTime, sourceText.ToString(), document.Project.Solution.Workspace)
186- this.AddOrUpdateSingleFileProject( projectId, ( loadTime, parsingOptions, projectOptions))
187- return Some( parsingOptions, None, projectOptions)
188- with ex->
189- Assert.Exception( ex)
190- return None
191- | _ -> return this.TryGetOptionsForProject( projectId)
192- }
172+ member this.TryGetOptionsForDocumentOrProject ( document : Document ) =
173+ async {
174+ let projectId = document.Project.Id
175+
176+ // The options for a single-file script project are re-requested each time the file is analyzed. This is because the
177+ // single-file project may contain #load and #r references which are changing as the user edits, and we may need to re-analyze
178+ // to determine the latest settings. FCS keeps a cache to help ensure these are up-to-date.
179+ match singleFileProjectTable.TryGetValue( projectId) with
180+ | true , ( loadTime, _, _) ->
181+ try
182+ let fileName = document.FilePath
183+ let! cancellationToken = Async.CancellationToken
184+ let! sourceText = document.GetTextAsync( cancellationToken) |> Async.AwaitTask
185+ // NOTE: we don't use FCS cross-project references from scripts to projects. The projects must have been
186+ // compiled and #r will refer to files on disk.
187+ let tryGetOrCreateProjectId _ = None
188+ let! _referencedProjectFileNames , parsingOptions , projectOptions = this.ComputeSingleFileOptions( tryGetOrCreateProjectId, fileName, loadTime, sourceText.ToString())
189+ this.AddOrUpdateSingleFileProject( projectId, ( loadTime, parsingOptions, projectOptions))
190+ return Some( parsingOptions, None, projectOptions)
191+ with ex->
192+ Assert.Exception( ex)
193+ return None
194+ | _ -> return this.TryGetOptionsForProject( projectId)
195+ }
193196
194197/// Get the options for a document or project relevant for syntax processing.
195198/// Quicker then TryGetOptionsForDocumentOrProject as it doesn't need to recompute the exact project options for a script.
540543let projectDisplayName = projectDisplayNameOf projectFileName
541544
542545let projectId = workspace.ProjectTracker.GetOrCreateProjectIdForPath( projectFileName, projectDisplayName)
543- let _referencedProjectFileNames , parsingOptions , projectOptions = projectInfoManager.ComputeSingleFileOptions( tryGetOrCreateProjectId workspace, fileName, loadTime, fileContents, workspace ) |> Async.RunSynchronously
546+ let _referencedProjectFileNames , parsingOptions , projectOptions = projectInfoManager.ComputeSingleFileOptions( tryGetOrCreateProjectId workspace, fileName, loadTime, fileContents) |> Async.RunSynchronously
544547 projectInfoManager.AddOrUpdateSingleFileProject( projectId, ( loadTime, parsingOptions, projectOptions))
545548
546549if isNull( workspace.ProjectTracker.GetProject projectId) then