diff --git a/fcs/README.md b/fcs/README.md index 9754b8e6286..0b4b45830bd 100644 --- a/fcs/README.md +++ b/fcs/README.md @@ -60,9 +60,9 @@ which does things like: Yu can push the packages if you have permissions, either automatically using ``build Release`` or manually set APIKEY=... - .nuget\nuget.exe push Release\FSharp.Compiler.Service.15.0.1.nupkg %APIKEY% -Source https://nuget.org - .nuget\nuget.exe push Release\FSharp.Compiler.Service.MSBuild.v12.15.0.1.nupkg %APIKEY% -Source https://nuget.org - .nuget\nuget.exe push Release\FSharp.Compiler.Service.ProjectCracker.15.0.1.nupkg %APIKEY% -Source https://nuget.org + .nuget\nuget.exe push Release\FSharp.Compiler.Service.16.0.1.nupkg %APIKEY% -Source https://nuget.org + .nuget\nuget.exe push Release\FSharp.Compiler.Service.MSBuild.v12.16.0.1.nupkg %APIKEY% -Source https://nuget.org + .nuget\nuget.exe push Release\FSharp.Compiler.Service.ProjectCracker.16.0.1.nupkg %APIKEY% -Source https://nuget.org ### Use of Paket and FAKE diff --git a/fcs/RELEASE_NOTES.md b/fcs/RELEASE_NOTES.md index 01a32e4538f..cca2fc17f80 100644 --- a/fcs/RELEASE_NOTES.md +++ b/fcs/RELEASE_NOTES.md @@ -1,3 +1,7 @@ +#### 16.0.1 + * FSharpChecker provides non-reactor ParseFile instead of ParseFileInProject + * Add FSharpParsingOptions, GetParsingOptionsFromProjectOptions, GetParsingOptionsFromCommandLine + #### 15.0.1 * Integrate latest changes from visualfsharp * Add implementation file contents to CheckFileResults diff --git a/fcs/docsrc/content/caches.fsx b/fcs/docsrc/content/caches.fsx index cfed3248ec5..e63857c5dac 100644 --- a/fcs/docsrc/content/caches.fsx +++ b/fcs/docsrc/content/caches.fsx @@ -21,19 +21,14 @@ Each FSharpChecker object maintains a set of caches. These are * ``braceMatchCache`` - an MRU cache of size ``braceMatchCacheSize`` (default = 5) keeping the results of calls to MatchBraces, keyed by filename, source and project options. -* ``parseFileInProjectCache`` - an MRU cache of size ``parseFileInProjectCacheSize`` (default = 2) keeping the results of ParseFileInProject, +* ``parseFileCache`` - an MRU cache of size ``parseFileCacheSize`` (default = 2) keeping the results of ParseFile, keyed by filename, source and project options. -* ``parseAndCheckFileInProjectCache`` - an MRU cache of size ``incrementalTypeCheckCacheSize`` (default = 5) keeping the results of +* ``checkFileInProjectCache`` - an MRU cache of size ``incrementalTypeCheckCacheSize`` (default = 5) keeping the results of ParseAndCheckFileInProject, CheckFileInProject and/or CheckFileInProjectIfReady. This is keyed by filename, file source and project options. The results held in this cache are only returned if they would reflect an accurate parse and check of the file. -* ``parseAndCheckFileInProjectCachePossiblyStale`` - a somewhat peculiar MRU cache of size ``incrementalTypeCheckCacheSize`` (default = 5) - keeping the results of ParseAndCheckFileInProject, CheckFileInProject and CheckFileInProjectIfReady, - keyed by filename and project options. This cache is accessed by TryGetRecentTypeCheckResultsForFile. Because the results - are accessed regardless of the content of the file, the checking results returned may be "stale". - * ``getToolTipTextCache`` - an aged lookup cache of strong size ``getToolTipTextSize`` (default = 5) computing the results of GetToolTipText. * ``ilModuleReaderCache`` - an aged lookup of weak references to "readers" for references .NET binaries. Because these diff --git a/fcs/docsrc/content/editor.fsx b/fcs/docsrc/content/editor.fsx index ccd61e922d2..d6b19fd0e6f 100644 --- a/fcs/docsrc/content/editor.fsx +++ b/fcs/docsrc/content/editor.fsx @@ -56,16 +56,19 @@ let projOptions = checker.GetProjectOptionsFromScript(file, input) |> Async.RunSynchronously +let parsingOptions, _errors = checker.GetParsingOptionsFromProjectOptions(projOptions) + (** To perform type checking, we first need to parse the input using -`ParseFileInProject`, which gives us access to the [untyped AST](untypedtree.html). However, +`ParseFile`, which gives us access to the [untyped AST](untypedtree.html). However, then we need to call `CheckFileInProject` to perform the full type checking. This function also requires the result of `ParseFileInProject`, so the two functions are often called together. *) // Perform parsing + let parseFileResults = - checker.ParseFileInProject(file, input, projOptions) + checker.ParseFile(file, input, parsingOptions) |> Async.RunSynchronously (** Before we look at the interesting operations provided by `TypeCheckResults`, we diff --git a/fcs/docsrc/content/ja/editor.fsx b/fcs/docsrc/content/ja/editor.fsx index 9e4ac05b3ca..014be2e86c5 100644 --- a/fcs/docsrc/content/ja/editor.fsx +++ b/fcs/docsrc/content/ja/editor.fsx @@ -58,20 +58,22 @@ let file = "/home/user/Test.fsx" let projOptions = checker.GetProjectOptionsFromScript(file, input) |> Async.RunSynchronously +let parsingOptions, _errors = checker.GetParsingOptionsFromProjectOptions(projOptions) + (** -型チェックを実行するには、まず `ParseFileInProject` を使って +型チェックを実行するには、まず `ParseFile` を使って 入力値をパースする必要があります。 このメソッドを使うと [型無しAST](untypedtree.html) にアクセスできるようになります。 しかし今回は完全な型チェックを実行するため、続けて `CheckFileInProject` を呼び出す必要があります。 -このメソッドは `ParseFileInProject` の結果も必要とするため、 +このメソッドは `ParseFile` の結果も必要とするため、 たいていの場合にはこれら2つのメソッドをセットで呼び出すことになります。 *) // パースを実行 let parseFileResults = - checker.ParseFileInProject(file, input, projOptions) + checker.ParseFile(file, input, parsingOptions) |> Async.RunSynchronously (** `TypeCheckResults` に備えられた興味深い機能の紹介に入る前に、 diff --git a/fcs/docsrc/content/ja/untypedtree.fsx b/fcs/docsrc/content/ja/untypedtree.fsx index f34f9c6b435..df6b6f4deb8 100644 --- a/fcs/docsrc/content/ja/untypedtree.fsx +++ b/fcs/docsrc/content/ja/untypedtree.fsx @@ -73,9 +73,11 @@ let getUntypedTree (file, input) = checker.GetProjectOptionsFromScript(file, input) |> Async.RunSynchronously + let parsingOptions, _errors = checker.GetParsingOptionsFromProjectOptions(projOptions) + // コンパイラの第1フェーズを実行する let untypedRes = - checker.ParseFileInProject(file, input, projectOptions) + checker.ParseFile(file, input, parsingOptions) |> Async.RunSynchronously match untypedRes.ParseTree with diff --git a/fcs/docsrc/content/untypedtree.fsx b/fcs/docsrc/content/untypedtree.fsx index 600e87fb04e..d67a1ef71bf 100644 --- a/fcs/docsrc/content/untypedtree.fsx +++ b/fcs/docsrc/content/untypedtree.fsx @@ -49,7 +49,7 @@ To get the AST, we define a function that takes file name and the source code (the file is only used for location information and does not have to exist). We first need to get "interactive checker options" which represents the context. For simple tasks, you can use `GetProjectOptionsFromScriptRoot` which infers -the context for a script file. Then we use the `ParseFileInProject` method and +the context for a script file. Then we use the `ParseFile` method and return the `ParseTree` property: *) @@ -60,9 +60,11 @@ let getUntypedTree (file, input) = checker.GetProjectOptionsFromScript(file, input) |> Async.RunSynchronously + let parsingOptions, _errors = checker.GetParsingOptionsFromProjectOptions(projOptions) + // Run the first phase (untyped parsing) of the compiler let parseFileResults = - checker.ParseFileInProject(file, input, projOptions) + checker.ParseFile(file, input, parsingOptions) |> Async.RunSynchronously match parseFileResults.ParseTree with diff --git a/fcs/fcs.props b/fcs/fcs.props index 68be7ef9f1b..7a6e7c40585 100644 --- a/fcs/fcs.props +++ b/fcs/fcs.props @@ -3,7 +3,7 @@ - 15.0.1 + 16.0.1 $(FSharpSourcesRoot)\..\packages\FSharp.Compiler.Tools.4.1.23\tools diff --git a/fcs/nuget/FSharp.Compiler.Service.MSBuild.v12.nuspec b/fcs/nuget/FSharp.Compiler.Service.MSBuild.v12.nuspec index 5443abd0768..ad905b35c0f 100644 --- a/fcs/nuget/FSharp.Compiler.Service.MSBuild.v12.nuspec +++ b/fcs/nuget/FSharp.Compiler.Service.MSBuild.v12.nuspec @@ -8,7 +8,7 @@ en-US false - 15.0.1 + 16.0.1 Microsoft Corporation and F# community contributors https://github.com/fsharp/FSharp.Compiler.Service/blob/master/LICENSE https://github.com/fsharp/FSharp.Compiler.Service diff --git a/fcs/nuget/FSharp.Compiler.Service.ProjectCracker.nuspec b/fcs/nuget/FSharp.Compiler.Service.ProjectCracker.nuspec index 33681ee90ff..d8501e86006 100644 --- a/fcs/nuget/FSharp.Compiler.Service.ProjectCracker.nuspec +++ b/fcs/nuget/FSharp.Compiler.Service.ProjectCracker.nuspec @@ -10,7 +10,7 @@ en-US false - 15.0.1 + 16.0.1 Microsoft Corporation and F# community contributors https://github.com/fsharp/FSharp.Compiler.Service/blob/master/LICENSE https://github.com/fsharp/FSharp.Compiler.Service diff --git a/fcs/nuget/FSharp.Compiler.Service.nuspec b/fcs/nuget/FSharp.Compiler.Service.nuspec index 405ce4b5701..0df07c0240a 100644 --- a/fcs/nuget/FSharp.Compiler.Service.nuspec +++ b/fcs/nuget/FSharp.Compiler.Service.nuspec @@ -10,7 +10,7 @@ en-US false - 15.0.1 + 16.0.1 Microsoft Corporation and F# community contributors https://github.com/fsharp/FSharp.Compiler.Service/blob/master/LICENSE https://github.com/fsharp/FSharp.Compiler.Service diff --git a/fcs/samples/EditorService/Program.fs b/fcs/samples/EditorService/Program.fs index 20bf20cda7a..40052ddce7e 100644 --- a/fcs/samples/EditorService/Program.fs +++ b/fcs/samples/EditorService/Program.fs @@ -8,7 +8,8 @@ let checker = FSharpChecker.Create() let parseWithTypeInfo (file, input) = let checkOptions, _errors = checker.GetProjectOptionsFromScript(file, input) |> Async.RunSynchronously - let untypedRes = checker.ParseFileInProject(file, input, checkOptions) |> Async.RunSynchronously + let parsingOptions, _errors = checker.GetParsingOptionsFromProjectOptions(checkOptions) + let untypedRes = checker.ParseFile(file, input, parsingOptions) |> Async.RunSynchronously match checker.CheckFileInProject(untypedRes, file, 0, input, checkOptions) |> Async.RunSynchronously with | FSharpCheckFileAnswer.Succeeded(res) -> untypedRes, res diff --git a/src/fsharp/vs/service.fs b/src/fsharp/vs/service.fs index 28514624e00..5c2fc8f5445 100644 --- a/src/fsharp/vs/service.fs +++ b/src/fsharp/vs/service.fs @@ -54,11 +54,11 @@ type internal Layout = StructuredFormat.Layout [] module EnvMisc = - let getToolTipTextSize = GetEnvInteger "FCS_RecentForegroundTypeCheckCacheSize" 5 + let getToolTipTextSize = GetEnvInteger "FCS_GetToolTipTextCacheSize" 5 let maxTypeCheckErrorsOutOfProjectContext = GetEnvInteger "FCS_MaxErrorsOutOfProjectContext" 3 let braceMatchCacheSize = GetEnvInteger "FCS_BraceMatchCacheSize" 5 - let parseFileInProjectCacheSize = GetEnvInteger "FCS_ParseFileInProjectCacheSize" 2 - let incrementalTypeCheckCacheSize = GetEnvInteger "FCS_IncrementalTypeCheckCacheSize" 5 + let parseFileCacheSize = GetEnvInteger "FCS_ParseFileCacheSize" 2 + let checkFileInProjectCacheSize = GetEnvInteger "FCS_CheckFileInProjectCacheSize" 5 let projectCacheSizeDefault = GetEnvInteger "FCS_ProjectCacheSizeDefault" 3 let frameworkTcImportsCacheStrongSize = GetEnvInteger "FCS_frameworkTcImportsCacheStrongSizeDefault" 8 @@ -2307,26 +2307,26 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.parseFileInProjectCache. Most recently used cache for parsing files. - let parseFileCache = MruCache(parseFileInProjectCacheSize, areSimilar = AreSimilarForParsing, areSame = AreSameForParsing) + let parseFileCache = MruCache(parseFileCacheSize, areSimilar = AreSimilarForParsing, areSame = AreSameForParsing) - // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.parseAndCheckFileInProjectCachePossiblyStale - // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.parseAndCheckFileInProjectCache + // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.checkFileInProjectCachePossiblyStale + // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.checkFileInProjectCache // /// Cache which holds recently seen type-checks. /// This cache may hold out-of-date entries, in two senses /// - there may be a more recent antecedent state available because the background build has made it available /// - the source for the file may have changed - let parseAndCheckFileInProjectCachePossiblyStale = + let checkFileInProjectCachePossiblyStale = MruCache - (keepStrongly=incrementalTypeCheckCacheSize, + (keepStrongly=checkFileInProjectCacheSize, areSame=AreSameForChecking2, areSimilar=AreSubsumable2) // Also keyed on source. This can only be out of date if the antecedent is out of date - let parseAndCheckFileInProjectCache = + let checkFileInProjectCache = MruCache - (keepStrongly=incrementalTypeCheckCacheSize, + (keepStrongly=checkFileInProjectCacheSize, areSame=AreSameForChecking3, areSimilar=AreSubsumable3) @@ -2366,8 +2366,8 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC | Some (FSharpCheckFileAnswer.Succeeded typedResults) -> foregroundTypeCheckCount <- foregroundTypeCheckCount + 1 parseCacheLock.AcquireLock (fun ltok -> - parseAndCheckFileInProjectCachePossiblyStale.Set(ltok, (filename,options),(parseResults,typedResults,fileVersion)) - parseAndCheckFileInProjectCache.Set(ltok, (filename,source,options),(parseResults,typedResults,fileVersion,priorTimeStamp)) + checkFileInProjectCachePossiblyStale.Set(ltok, (filename,options),(parseResults,typedResults,fileVersion)) + checkFileInProjectCache.Set(ltok, (filename,source,options),(parseResults,typedResults,fileVersion,priorTimeStamp)) parseFileCache.Set(ltok, (filename, source, parsingOptions), parseResults)) member bc.ImplicitlyStartCheckProjectInBackground(options, userOpName) = @@ -2403,7 +2403,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC member bc.GetCachedCheckFileResult(builder: IncrementalBuilder,filename,source,options) = // Check the cache. We can only use cached results when there is no work to do to bring the background builder up-to-date - let cachedResults = parseCacheLock.AcquireLock (fun ltok -> parseAndCheckFileInProjectCache.TryGet(ltok, (filename,source,options))) + let cachedResults = parseCacheLock.AcquireLock (fun ltok -> checkFileInProjectCache.TryGet(ltok, (filename,source,options))) match cachedResults with // | Some (parseResults, checkResults, _, _) when builder.AreCheckResultsBeforeFileInProjectReady(filename) -> @@ -2620,10 +2620,10 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC match source with | Some sourceText -> parseCacheLock.AcquireLock (fun ltok -> - match parseAndCheckFileInProjectCache.TryGet(ltok,(filename,sourceText,options)) with + match checkFileInProjectCache.TryGet(ltok,(filename,sourceText,options)) with | Some (a,b,c,_) -> Some (a,b,c) | None -> None) - | None -> parseCacheLock.AcquireLock (fun ltok -> parseAndCheckFileInProjectCachePossiblyStale.TryGet(ltok,(filename,options))) + | None -> parseCacheLock.AcquireLock (fun ltok -> checkFileInProjectCachePossiblyStale.TryGet(ltok,(filename,options))) /// Parse and typecheck the whole project (the implementation, called recursively as project graph is evaluated) member private bc.ParseAndCheckProjectImpl(options, ctok, userOpName) : Cancellable = @@ -2774,8 +2774,8 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC member bc.ClearCachesAsync (userOpName) = reactor.EnqueueAndAwaitOpAsync (userOpName, "ClearCachesAsync", "", fun ctok -> parseCacheLock.AcquireLock (fun ltok -> - parseAndCheckFileInProjectCachePossiblyStale.Clear ltok - parseAndCheckFileInProjectCache.Clear ltok + checkFileInProjectCachePossiblyStale.Clear ltok + checkFileInProjectCache.Clear ltok parseFileCache.Clear(ltok)) incrementalBuildersCache.Clear ctok frameworkTcImportsCache.Clear ctok @@ -2785,8 +2785,8 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC member bc.DownsizeCaches(userOpName) = reactor.EnqueueAndAwaitOpAsync (userOpName, "DownsizeCaches", "", fun ctok -> parseCacheLock.AcquireLock (fun ltok -> - parseAndCheckFileInProjectCachePossiblyStale.Resize(ltok, keepStrongly=1) - parseAndCheckFileInProjectCache.Resize(ltok, keepStrongly=1) + checkFileInProjectCachePossiblyStale.Resize(ltok, keepStrongly=1) + checkFileInProjectCache.Resize(ltok, keepStrongly=1) parseFileCache.Resize(ltok, keepStrongly=1)) incrementalBuildersCache.Resize(ctok, keepStrongly=1, keepMax=1) frameworkTcImportsCache.Downsize(ctok) @@ -2849,15 +2849,15 @@ type FSharpChecker(legacyReferenceResolver, projectCacheSize, keepAssemblyConten return res } - member ic.GetParsingOptionsFromProjectOptions(options): FSharpParsingOptions = + member ic.GetParsingOptionsFromProjectOptions(options): FSharpParsingOptions * _ = let sourceFiles = List.ofArray options.SourceFiles let argv = List.ofArray options.OtherOptions - let parsingOptions, _ = ic.GetParsingOptionsFromCommandLineArgs(sourceFiles, argv) - parsingOptions + ic.GetParsingOptionsFromCommandLineArgs(sourceFiles, argv) member ic.MatchBraces(filename, source, options: FSharpProjectOptions, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" - ic.MatchBraces(filename, source, ic.GetParsingOptionsFromProjectOptions(options), userOpName) + let parsingOptions, _ = ic.GetParsingOptionsFromProjectOptions(options) + ic.MatchBraces(filename, source, parsingOptions, userOpName) member ic.ParseFile(filename, source, options, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" @@ -2867,7 +2867,8 @@ type FSharpChecker(legacyReferenceResolver, projectCacheSize, keepAssemblyConten member ic.ParseFileInProject(filename, source, options, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" - ic.ParseFile(filename, source, ic.GetParsingOptionsFromProjectOptions(options), userOpName) + let parsingOptions, _ = ic.GetParsingOptionsFromProjectOptions(options) + ic.ParseFile(filename, source, parsingOptions, userOpName) member ic.GetBackgroundParseResultsForFileInProject (filename,options, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" @@ -3177,21 +3178,9 @@ module CompilerEnvironment = let DefaultReferencesForOrphanSources(assumeDotNetFramework) = DefaultReferencesForScriptsAndOutOfProjectSources(assumeDotNetFramework) /// Publish compiler-flags parsing logic. Must be fast because its used by the colorizer. - let GetCompilationDefinesForEditing(filename:string, compilerFlags : string list) = - let defines = ref(SourceFileImpl.AdditionalDefinesForUseInEditor(filename)) - let MatchAndExtract(flag:string,prefix:string) = - if flag.StartsWith(prefix) then - let sub = flag.Substring(prefix.Length) - let trimmed = sub.Trim() - defines := trimmed :: !defines - let rec QuickParseDefines = function - | hd :: tail -> - MatchAndExtract(hd,"-d:") - MatchAndExtract(hd,"--define:") - QuickParseDefines tail - | _ -> () - QuickParseDefines compilerFlags - !defines + let GetCompilationDefinesForEditing(filename:string, parsingOptions: FSharpParsingOptions) = + SourceFileImpl.AdditionalDefinesForUseInEditor(filename) @ + parsingOptions.ConditionalCompilationDefines /// Return true if this is a subcategory of error or warning message that the language service can emit let IsCheckerSupportedSubcategory(subcategory:string) = diff --git a/src/fsharp/vs/service.fsi b/src/fsharp/vs/service.fsi index e53ad229162..986b9a0e4c8 100755 --- a/src/fsharp/vs/service.fsi +++ b/src/fsharp/vs/service.fsi @@ -417,6 +417,7 @@ type internal FSharpChecker = /// The full source for the file. /// Parsing options for the project or script. /// An optional string used for tracing compiler operations associated with this request. + [] member MatchBraces: filename: string * source: string * options: FSharpProjectOptions * ?userOpName: string -> Async<(range * range)[]> /// @@ -440,6 +441,7 @@ type internal FSharpChecker = /// The full source for the file. /// The options for the project or script, used to determine active --define conditionals and other options relevant to parsing. /// An optional string used for tracing compiler operations associated with this request. + [] member ParseFileInProject: filename: string * source: string * options: FSharpProjectOptions * ?userOpName: string -> Async /// @@ -453,7 +455,7 @@ type internal FSharpChecker = /// /// /// - /// The results of ParseFileInProject for this file. + /// The results of ParseFile for this file. /// The name of the file in the project whose source is being checked. /// An integer that can be used to indicate the version of the file. This will be returned by TryGetRecentCheckResultsForFile when looking up the file. /// The full source for the file. @@ -479,7 +481,7 @@ type internal FSharpChecker = /// /// /// - /// The results of ParseFileInProject for this file. + /// The results of ParseFile for this file. /// The name of the file in the project whose source is being checked. /// An integer that can be used to indicate the version of the file. This will be returned by TryGetRecentCheckResultsForFile when looking up the file. /// The full source for the file. @@ -574,7 +576,14 @@ type internal FSharpChecker = member GetParsingOptionsFromCommandLineArgs: argv: string list -> FSharpParsingOptions * FSharpErrorInfo list /// - /// Like ParseFileInProject, but uses results from the background builder. + /// Get the FSharpParsingOptions implied by a FSharpProjectOptions. + /// + /// + /// The command line arguments for the project build. + member GetParsingOptionsFromProjectOptions: FSharpProjectOptions -> FSharpParsingOptions * FSharpErrorInfo list + + /// + /// Like ParseFile, but uses results from the background builder. /// All files are read from the FileSystem API, including the file being checked. /// /// @@ -584,7 +593,7 @@ type internal FSharpChecker = member GetBackgroundParseResultsForFileInProject : filename : string * options : FSharpProjectOptions * ?userOpName: string -> Async /// - /// Like ParseFileInProject, but uses the existing results from the background builder. + /// Like CheckFileInProject, but uses the existing results from the background builder. /// All files are read from the FileSystem API, including the file being checked. /// /// @@ -760,7 +769,7 @@ module internal CompilerEnvironment = /// are not associated with a project. val DefaultReferencesForOrphanSources : assumeDotNetFramework: bool -> string list /// Return the compilation defines that should be used when editing the given file. - val GetCompilationDefinesForEditing : filename : string * compilerFlags : string list -> string list + val GetCompilationDefinesForEditing : filename : string * parsingOptions : FSharpParsingOptions -> string list /// Return true if this is a subcategory of error or warning message that the language service can emit val IsCheckerSupportedSubcategory : string -> bool diff --git a/vsintegration/Utils/LanguageServiceProfiling/Program.fs b/vsintegration/Utils/LanguageServiceProfiling/Program.fs index a64218f3e4a..bc785b787cd 100644 --- a/vsintegration/Utils/LanguageServiceProfiling/Program.fs +++ b/vsintegration/Utils/LanguageServiceProfiling/Program.fs @@ -156,7 +156,8 @@ let main argv = let! fileResults = checkFile fileVersion match fileResults with | Some fileResults -> - let! parseResult = checker.ParseFileInProject(options.FileToCheck, getFileText(), options.Options) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions(options.Options) + let! parseResult = checker.ParseFile(options.FileToCheck, getFileText(), parsingOptions) for completion in options.CompletionPositions do eprintfn "querying %A %s" completion.QualifyingNames completion.PartialName let! listInfo = diff --git a/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs b/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs index b40aaa4d0a2..b60ee3b9bcc 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs @@ -40,9 +40,9 @@ type internal FSharpColorizationService asyncMaybe { do Trace.TraceInformation("{0:n3} (start) SemanticColorization", DateTime.Now.TimeOfDay.TotalSeconds) do! Async.Sleep DefaultTuning.SemanticColorizationInitialDelay |> liftAsync // be less intrusive, give other work priority most of the time - let! options = projectInfoManager.TryGetOptionsForDocumentOrProject(document) + let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) - let! _, _, checkResults = checkerProvider.Checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = false, userOpName=userOpName) + let! _, _, checkResults = checkerProvider.Checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, allowStaleResults = false, userOpName=userOpName) // it's crucial to not return duplicated or overlapping `ClassifiedSpan`s because Find Usages service crashes. let targetRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText) let colorizationData = checkResults.GetSemanticClassification (Some targetRange) |> Array.distinctBy fst diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs index 92622f4fe7e..76f88464e7a 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs @@ -96,12 +96,12 @@ type internal FSharpAddOpenCodeFixProvider override __.RegisterCodeFixesAsync context : Task = asyncMaybe { let document = context.Document - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let! _, parsedInput, checkResults = checker.ParseAndCheckDocument(document, options, allowStaleResults = true, sourceText = sourceText, userOpName = userOpName) + let! _, parsedInput, checkResults = checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = true, sourceText = sourceText, userOpName = userOpName) let line = sourceText.Lines.GetLineFromPosition(context.Span.End) let linePos = sourceText.Lines.GetLinePosition(context.Span.End) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, options.OtherOptions |> Seq.toList) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, parsingOptions) let! symbol = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs index a37be23a58e..5cf5fcba215 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs @@ -139,12 +139,12 @@ type internal FSharpImplementInterfaceCodeFixProvider override __.RegisterCodeFixesAsync context : Task = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject context.Document + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject context.Document let cancellationToken = context.CancellationToken let! sourceText = context.Document.GetTextAsync(cancellationToken) - let! _, parsedInput, checkFileResults = checker.ParseAndCheckDocument(context.Document, options, sourceText = sourceText, allowStaleResults = true, userOpName = userOpName) + let! _, parsedInput, checkFileResults = checker.ParseAndCheckDocument(context.Document, projectOptions, sourceText = sourceText, allowStaleResults = true, userOpName = userOpName) let textLine = sourceText.Lines.GetLineFromPosition context.Span.Start - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(context.Document.FilePath, options.OtherOptions |> Seq.toList) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(context.Document.FilePath, parsingOptions) // Notice that context.Span doesn't return reliable ranges to find tokens at exact positions. // That's why we tokenize the line and try to find the last successive identifier token let tokens = Tokenizer.tokenizeLine(context.Document.Id, sourceText, context.Span.Start, context.Document.FilePath, defines) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs index fc6c71fb3a9..5e97220d08c 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs @@ -32,8 +32,8 @@ type internal FSharpRemoveUnusedOpensCodeFixProvider let document = context.Document let! sourceText = document.GetTextAsync() let checker = checkerProvider.Checker - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) - let! unusedOpens = UnusedOpensDiagnosticAnalyzer.GetUnusedOpenRanges(document, options, checker) + let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! unusedOpens = UnusedOpensDiagnosticAnalyzer.GetUnusedOpenRanges(document, projectOptions, checker) let changes = unusedOpens |> List.map (fun m -> diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs index 7b544ca866a..710bd2244c8 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs @@ -54,10 +54,10 @@ type internal FSharpRenameUnusedValueCodeFixProvider // We have to use the additional check for backtickes because `IsOperatorOrBacktickedName` operates on display names // where backtickes are replaced with parens. if not (PrettyNaming.IsOperatorOrBacktickedName ident) && not (ident.StartsWith "``") then - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document - let! _, _, checkResults = checker.ParseAndCheckDocument(document, options, allowStaleResults = true, sourceText = sourceText, userOpName=userOpName) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document + let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = true, sourceText = sourceText, userOpName=userOpName) let m = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing (document.FilePath, options.OtherOptions |> Seq.toList) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing (document.FilePath, parsingOptions) let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, context.Span.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false) let lineText = (sourceText.Lines.GetLineFromPosition context.Span.Start).ToString() let! symbolUse = checkResults.GetSymbolUseAtLocation(m.StartLine, m.EndColumn, lineText, lexerSymbol.FullIsland, userOpName=userOpName) diff --git a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs index 2c7ece30131..99bcd091c91 100644 --- a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs @@ -99,13 +99,13 @@ type internal FSharpHelpContextService member this.GetHelpTermAsync(document, textSpan, cancellationToken) = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) let! textVersion = document.GetTextVersionAsync(cancellationToken) let defines = projectInfoManager.GetCompilationDefinesForEditingDocument(document) let textLine = sourceText.Lines.GetLineFromPosition(textSpan.Start) let tokens = Tokenizer.getColorizationData(document.Id, sourceText, textLine.Span, Some document.Name, defines, cancellationToken) - return! FSharpHelpContextService.GetHelpTerm(checkerProvider.Checker, sourceText, document.FilePath, options, textSpan, tokens, textVersion.GetHashCode()) + return! FSharpHelpContextService.GetHelpTerm(checkerProvider.Checker, sourceText, document.FilePath, projectOptions, textSpan, tokens, textVersion.GetHashCode()) } |> Async.map (Option.defaultValue "") |> RoslynHelpers.StartAsyncAsTask cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs index 2af89506561..9d5c2990aec 100644 --- a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs @@ -67,9 +67,9 @@ type internal XmlDocCommandFilter // XmlDocable line #1 are 1-based, editor is 0-based let curLineNum = wpfTextView.Caret.Position.BufferPosition.GetContainingLine().LineNumber + 1 let! document = document.Value - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let sourceText = wpfTextView.TextBuffer.CurrentSnapshot.GetText() - let! parsedInput = checker.ParseDocument(document, options, sourceText, userOpName) + let! parsedInput = checker.ParseDocument(document, parsingOptions, sourceText, userOpName) let xmlDocables = XmlDocParser.getXmlDocables (sourceText, Some parsedInput) let xmlDocablesBelowThisLine = // +1 because looking below current line for e.g. a 'member' diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index f77988f2702..e30c0322b47 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -210,15 +210,15 @@ type internal FSharpCompletionProvider let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let defines = projectInfoManager.GetCompilationDefinesForEditingDocument(document) do! Option.guard (CompletionUtils.shouldProvideCompletion(document.Id, document.FilePath, defines, sourceText, context.Position)) - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! textVersion = context.Document.GetTextVersionAsync(context.CancellationToken) - let! _, _, fileCheckResults = checker.ParseAndCheckDocument(document, options, true, userOpName=userOpName) + let! _, _, fileCheckResults = checker.ParseAndCheckDocument(document, projectOptions, true, userOpName=userOpName) let getAllSymbols() = if Settings.IntelliSense.ShowAllSymbols then assemblyContentProvider.GetAllEntitiesInProjectAndReferencedAssemblies(fileCheckResults) else [] let! results = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, sourceText, context.Position, options, + FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, sourceText, context.Position, projectOptions, document.FilePath, textVersion.GetHashCode(), getAllSymbols) context.AddItems(results) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask context.CancellationToken @@ -268,8 +268,8 @@ type internal FSharpCompletionProvider let! sourceText = document.GetTextAsync(cancellationToken) let textWithItemCommitted = sourceText.WithChanges(TextChange(item.Span, nameInCode)) let line = sourceText.Lines.GetLineFromPosition(item.Span.Start) - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) - let! parsedInput = checker.ParseDocument(document, options, sourceText, userOpName) + let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! parsedInput = checker.ParseDocument(document, parsingOptions, sourceText, userOpName) let fullNameIdents = fullName |> Option.map (fun x -> x.Split '.') |> Option.defaultValue [||] let insertionPoint = diff --git a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs index 22310a9bf43..852fa7bfd56 100644 --- a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs +++ b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs @@ -196,7 +196,7 @@ type internal FSharpSignatureHelpProvider member this.GetItemsAsync(document, position, triggerInfo, cancellationToken) = asyncMaybe { try - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) let! textVersion = document.GetTextVersionAsync(cancellationToken) @@ -206,7 +206,7 @@ type internal FSharpSignatureHelpProvider else None let! (results,applicableSpan,argumentIndex,argumentCount,argumentName) = - FSharpSignatureHelpProvider.ProvideMethodsAsyncAux(checkerProvider.Checker, documentationBuilder, sourceText, position, options, triggerTypedChar, document.FilePath, textVersion.GetHashCode()) + FSharpSignatureHelpProvider.ProvideMethodsAsyncAux(checkerProvider.Checker, documentationBuilder, sourceText, position, projectOptions, triggerTypedChar, document.FilePath, textVersion.GetHashCode()) let items = results |> Array.map (fun (hasParamArrayArg, doc, prefixParts, separatorParts, suffixParts, parameters, descriptionParts) -> diff --git a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs index 864a77e44aa..97bf920d70a 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs @@ -27,13 +27,8 @@ type internal FSharpBreakpointResolutionService ) = static let userOpName = "BreakpointResolution" - static member GetBreakpointLocation(checker: FSharpChecker, sourceText: SourceText, fileName: string, textSpan: TextSpan, options: FSharpProjectOptions) = + static member GetBreakpointLocation(checker: FSharpChecker, sourceText: SourceText, fileName: string, textSpan: TextSpan, parsingOptions: FSharpParsingOptions) = async { - // REVIEW: ParseFileInProject can cause FSharp.Compiler.Service to become unavailable (i.e. not responding to requests) for - // an arbitrarily long time while it parses all files prior to this one in the project (plus dependent projects if we enable - // cross-project checking in multi-project solutions). FCS will not respond to other - // requests unless this task is cancelled. We need to check that this task is cancelled in a timely way by the - // Roslyn UI machinery. let textLinePos = sourceText.Lines.GetLinePosition(textSpan.Start) let textInLine = sourceText.GetSubText(sourceText.Lines.[textLinePos.Line].Span).ToString() @@ -42,16 +37,16 @@ type internal FSharpBreakpointResolutionService else let textLineColumn = textLinePos.Character let fcsTextLineNumber = Line.fromZ textLinePos.Line // Roslyn line numbers are zero-based, FSharp.Compiler.Service line numbers are 1-based - let! parseResults = checker.ParseFileInProject(fileName, sourceText.ToString(), options, userOpName = userOpName) + let! parseResults = checker.ParseFile(fileName, sourceText.ToString(), parsingOptions, userOpName = userOpName) return parseResults.ValidateBreakpointLocation(mkPos fcsTextLineNumber textLineColumn) } interface IBreakpointResolutionService with member this.ResolveBreakpointAsync(document: Document, textSpan: TextSpan, cancellationToken: CancellationToken): Task = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) - let! range = FSharpBreakpointResolutionService.GetBreakpointLocation(checkerProvider.Checker, sourceText, document.Name, textSpan, options) + let! range = FSharpBreakpointResolutionService.GetBreakpointLocation(checkerProvider.Checker, sourceText, document.Name, textSpan, parsingOptions) let! span = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) return BreakpointResolutionResult.CreateSpanResult(document, span) } diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs index 412496efa35..ffcf5c6ef83 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs @@ -57,9 +57,9 @@ type internal FSharpDocumentDiagnosticAnalyzer() = hash } - static member GetDiagnostics(checker: FSharpChecker, filePath: string, sourceText: SourceText, textVersionHash: int, options: FSharpProjectOptions, diagnosticType: DiagnosticsType) = + static member GetDiagnostics(checker: FSharpChecker, filePath: string, sourceText: SourceText, textVersionHash: int, parsingOptions: FSharpParsingOptions, options: FSharpProjectOptions, diagnosticType: DiagnosticsType) = async { - let! parseResults = checker.ParseFileInProject(filePath, sourceText.ToString(), options, userOpName=userOpName) + let! parseResults = checker.ParseFile(filePath, sourceText.ToString(), parsingOptions, userOpName=userOpName) let! errors = async { match diagnosticType with @@ -109,11 +109,11 @@ type internal FSharpDocumentDiagnosticAnalyzer() = override this.AnalyzeSyntaxAsync(document: Document, cancellationToken: CancellationToken): Task> = let projectInfoManager = getProjectInfoManager document asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) let! textVersion = document.GetTextVersionAsync(cancellationToken) return! - FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(getChecker document, document.FilePath, sourceText, textVersion.GetHashCode(), options, DiagnosticsType.Syntax) + FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(getChecker document, document.FilePath, sourceText, textVersion.GetHashCode(), parsingOptions, projectOptions, DiagnosticsType.Syntax) |> liftAsync } |> Async.map (Option.defaultValue ImmutableArray.Empty) @@ -122,11 +122,11 @@ type internal FSharpDocumentDiagnosticAnalyzer() = override this.AnalyzeSemanticsAsync(document: Document, cancellationToken: CancellationToken): Task> = let projectInfoManager = getProjectInfoManager document asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForDocumentOrProject(document) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) let! textVersion = document.GetTextVersionAsync(cancellationToken) return! - FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(getChecker document, document.FilePath, sourceText, textVersion.GetHashCode(), options, DiagnosticsType.Semantic) + FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(getChecker document, document.FilePath, sourceText, textVersion.GetHashCode(), parsingOptions, projectOptions, DiagnosticsType.Semantic) |> liftAsync } |> Async.map (Option.defaultValue ImmutableArray.Empty) diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs index 225a32d29e7..b0b25ca6ead 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs @@ -51,7 +51,7 @@ type internal SimplifyNameDiagnosticAnalyzer() = do! Option.guard Settings.CodeFixes.SimplifyName do Trace.TraceInformation("{0:n3} (start) SimplifyName", DateTime.Now.TimeOfDay.TotalSeconds) do! Async.Sleep DefaultTuning.SimplifyNameInitialDelay |> liftAsync - let! options = getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document) + let! _parsingOptions, projectOptions = getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document) let! textVersion = document.GetTextVersionAsync(cancellationToken) let textVersionHash = textVersion.GetHashCode() let! _ = guard.WaitAsync(cancellationToken) |> Async.AwaitTask |> liftAsync @@ -61,7 +61,7 @@ type internal SimplifyNameDiagnosticAnalyzer() = | _ -> let! sourceText = document.GetTextAsync() let checker = getChecker document - let! _, _, checkResults = checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = true, userOpName=userOpName) + let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, allowStaleResults = true, userOpName=userOpName) let! symbolUses = checkResults.GetAllUsesOfAllSymbolsInFile() |> liftAsync let mutable result = ResizeArray() let symbolUses = diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs index a0466dd4ff4..cb541cac322 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs @@ -103,10 +103,10 @@ 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 options -> + | Some (_parsingOptions, projectOptions) -> let! sourceText = document.GetTextAsync() let checker = getChecker document - let! _, _, checkResults = checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = true, userOpName = userOpName) + let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, allowStaleResults = true, userOpName = userOpName) let! allSymbolUsesInFile = checkResults.GetAllUsesOfAllSymbolsInFile() |> liftAsync let unusedRanges = getUnusedDeclarationRanges allSymbolUsesInFile (isScriptFile document.FilePath) return diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs index 58226fb8f1e..0dcb5c9da5d 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs @@ -173,10 +173,10 @@ type internal UnusedOpensDiagnosticAnalyzer() = asyncMaybe { do Trace.TraceInformation("{0:n3} (start) UnusedOpensAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) do! Async.Sleep DefaultTuning.UnusedOpensAnalyzerInitialDelay |> liftAsync // be less intrusive, give other work priority most of the time - let! options = getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document) + let! _parsingOptions, projectOptions = getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync() let checker = getChecker document - let! unusedOpens = UnusedOpensDiagnosticAnalyzer.GetUnusedOpenRanges(document, options, checker) + let! unusedOpens = UnusedOpensDiagnosticAnalyzer.GetUnusedOpenRanges(document, projectOptions, checker) return unusedOpens diff --git a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs index e4829fd4764..354c41759dd 100644 --- a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs +++ b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs @@ -76,12 +76,12 @@ type internal FSharpDocumentHighlightsService [] (checkerP interface IDocumentHighlightsService with member __.GetDocumentHighlightsAsync(document, position, _documentsToSearch, cancellationToken) : Task> = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) let! textVersion = document.GetTextVersionAsync(cancellationToken) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, options.OtherOptions |> Seq.toList) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, parsingOptions) let! spans = FSharpDocumentHighlightsService.GetDocumentHighlights(checkerProvider.Checker, document.Id, sourceText, document.FilePath, - position, defines, options, textVersion.GetHashCode()) + position, defines, projectOptions, textVersion.GetHashCode()) let highlightSpans = spans |> Array.map (fun span -> diff --git a/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs b/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs index d71a0826a14..7b795f0cc82 100644 --- a/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs +++ b/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs @@ -18,9 +18,9 @@ type internal FSharpBraceMatchingService static let defaultUserOpName = "BraceMatching" - static member GetBraceMatchingResult(checker: FSharpChecker, sourceText, fileName, options: FSharpProjectOptions, position: int, userOpName: string) = + static member GetBraceMatchingResult(checker: FSharpChecker, sourceText, fileName, parsingOptions: FSharpParsingOptions, position: int, userOpName: string) = async { - let! matchedBraces = checker.MatchBraces(fileName, sourceText.ToString(), options, userOpName) + let! matchedBraces = checker.MatchBraces(fileName, sourceText.ToString(), parsingOptions, userOpName) let isPositionInRange range = match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) with | None -> false @@ -33,9 +33,9 @@ type internal FSharpBraceMatchingService interface IBraceMatcher with member this.FindBracesAsync(document, position, cancellationToken) = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) - let! (left, right) = FSharpBraceMatchingService.GetBraceMatchingResult(checkerProvider.Checker, sourceText, document.Name, options, position, defaultUserOpName) + let! (left, right) = FSharpBraceMatchingService.GetBraceMatchingResult(checkerProvider.Checker, sourceText, document.Name, parsingOptions, position, defaultUserOpName) let! leftSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, left) let! rightSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, right) return BraceMatchingResult(leftSpan, rightSpan) diff --git a/vsintegration/src/FSharp.Editor/Formatting/EditorFormattingService.fs b/vsintegration/src/FSharp.Editor/Formatting/EditorFormattingService.fs index 0abbd0abddf..d4b1b24ebf6 100644 --- a/vsintegration/src/FSharp.Editor/Formatting/EditorFormattingService.fs +++ b/vsintegration/src/FSharp.Editor/Formatting/EditorFormattingService.fs @@ -23,7 +23,7 @@ type internal FSharpEditorFormattingService projectInfoManager: FSharpProjectOptionsManager ) = - static member GetFormattingChanges(documentId: DocumentId, sourceText: SourceText, filePath: string, checker: FSharpChecker, indentStyle: FormattingOptions.IndentStyle, projectOptions: FSharpProjectOptions option, position: int) = + static member GetFormattingChanges(documentId: DocumentId, sourceText: SourceText, filePath: string, checker: FSharpChecker, indentStyle: FormattingOptions.IndentStyle, options: (FSharpParsingOptions * FSharpProjectOptions) option, position: int) = // Logic for determining formatting changes: // If first token on the current line is a closing brace, // match the indent with the indent on the line that opened it @@ -34,11 +34,11 @@ type internal FSharpEditorFormattingService // (this is what C# does) do! Option.guard (indentStyle = FormattingOptions.IndentStyle.Smart) - let! projectOptions = projectOptions + let! parsingOptions, _projectOptions = options let line = sourceText.Lines.[sourceText.Lines.IndexOf position] - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(filePath, projectOptions.OtherOptions |> List.ofArray) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(filePath, parsingOptions) let tokens = Tokenizer.tokenizeLine(documentId, sourceText, line.Start, filePath, defines) @@ -50,7 +50,7 @@ type internal FSharpEditorFormattingService x.Tag <> FSharpTokenTag.LINE_COMMENT) let! (left, right) = - FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, filePath, projectOptions, position, "FormattingService") + FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, filePath, parsingOptions, position, "FormattingService") if right.StartColumn = firstMeaningfulToken.LeftColumn then // Replace the indentation on this line with the indentation of the left bracket diff --git a/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs b/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs index 4fa71c024fb..23afe5131ce 100644 --- a/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs +++ b/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs @@ -23,7 +23,7 @@ type internal FSharpIndentationService static member IsSmartIndentEnabled (options: Microsoft.CodeAnalysis.Options.OptionSet) = options.GetOption(FormattingOptions.SmartIndent, FSharpConstants.FSharpLanguageName) = FormattingOptions.IndentStyle.Smart - static member GetDesiredIndentation(documentId: DocumentId, sourceText: SourceText, filePath: string, lineNumber: int, tabSize: int, indentStyle: FormattingOptions.IndentStyle, projectOptions: FSharpProjectOptions option): Option = + static member GetDesiredIndentation(documentId: DocumentId, sourceText: SourceText, filePath: string, lineNumber: int, tabSize: int, indentStyle: FormattingOptions.IndentStyle, options: (FSharpParsingOptions * FSharpProjectOptions) option): Option = // Match indentation with previous line let rec tryFindPreviousNonEmptyLine l = @@ -36,8 +36,8 @@ type internal FSharpIndentationService tryFindPreviousNonEmptyLine (l - 1) let rec tryFindLastNonWhitespaceOrCommentToken (line: TextLine) = maybe { - let! projectOptions = projectOptions - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(filePath, projectOptions.OtherOptions |> Seq.toList) + let! parsingOptions, _projectOptions = options + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(filePath, parsingOptions) let tokens = Tokenizer.tokenizeLine(documentId, sourceText, line.Start, filePath, defines) return! diff --git a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs index d2f804ebe55..89e9d6a8f2d 100644 --- a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs +++ b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs @@ -167,10 +167,10 @@ type internal InlineRenameService interface IEditorInlineRenameService with member __.GetRenameInfoAsync(document: Document, position: int, cancellationToken: CancellationToken) : Task = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, options.OtherOptions |> Seq.toList) - return! InlineRenameService.GetInlineRenameInfo(checkerProvider.Checker, projectInfoManager, document, sourceText, position, defines, options) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, parsingOptions) + return! InlineRenameService.GetInlineRenameInfo(checkerProvider.Checker, projectInfoManager, document, sourceText, position, defines, projectOptions) } |> Async.map (Option.defaultValue FailureInlineRenameInfo.Instance) |> RoslynHelpers.StartAsyncAsTask(cancellationToken) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs index 7eb2616f283..2d6b877fee9 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs @@ -15,14 +15,14 @@ type CheckResults = | StillRunning of Async<(FSharpParseFileResults * FSharpCheckFileResults) option> type FSharpChecker with - member checker.ParseDocument(document: Document, options: FSharpProjectOptions, sourceText: string, userOpName: string) = + member checker.ParseDocument(document: Document, parsingOptions: FSharpParsingOptions, sourceText: string, userOpName: string) = asyncMaybe { - let! fileParseResults = checker.ParseFileInProject(document.FilePath, sourceText, options, userOpName=userOpName) |> liftAsync + let! fileParseResults = checker.ParseFile(document.FilePath, sourceText, parsingOptions, userOpName=userOpName) |> liftAsync return! fileParseResults.ParseTree } - member checker.ParseDocument(document: Document, options: FSharpProjectOptions, sourceText: SourceText, userOpName: string) = - checker.ParseDocument(document, options, sourceText=sourceText.ToString(), userOpName=userOpName) + member checker.ParseDocument(document: Document, parsingOptions: FSharpParsingOptions, sourceText: SourceText, userOpName: string) = + checker.ParseDocument(document, parsingOptions, sourceText=sourceText.ToString(), userOpName=userOpName) member checker.ParseAndCheckDocument(filePath: string, textVersionHash: int, sourceText: string, options: FSharpProjectOptions, allowStaleResults: bool, userOpName: string) = let parseAndCheckFile = diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 29658d69a56..311e7ba928c 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -101,11 +101,11 @@ type internal FSharpProjectOptionsManager ) = // A table of information about projects, excluding single-file projects. - let projectTable = ConcurrentDictionary>() + let projectTable = ConcurrentDictionary>() // 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 singleFileProjectTable = ConcurrentDictionary() // Accumulate sources and references for each project file let projectInfo = new ConcurrentDictionary() @@ -128,7 +128,7 @@ type internal FSharpProjectOptionsManager member this.RefreshInfoForProjectsThatReferenceThisProject(projectId: ProjectId) = // Search the projectTable for things to refresh - for KeyValue(otherProjectId, ((referencedProjectIds, _options), refresh)) in projectTable.ToArray() do + for KeyValue(otherProjectId, ((referencedProjectIds, _parsingOptions, _options), refresh)) in projectTable.ToArray() do for referencedProjectId in referencedProjectIds do if referencedProjectId = projectId then projectTable.[otherProjectId] <- (refresh true, refresh) @@ -143,7 +143,7 @@ type internal FSharpProjectOptionsManager /// Get the exact options for a single-file script member this.ComputeSingleFileOptions (tryGetOrCreateProjectId, fileName, loadTime, fileContents, workspace: Workspace) = async { let extraProjectInfo = Some(box workspace) - let tryGetOptionsForReferencedProject f = f |> tryGetOrCreateProjectId |> Option.bind this.TryGetOptionsForProject + let tryGetOptionsForReferencedProject f = f |> tryGetOrCreateProjectId |> Option.bind this.TryGetOptionsForProject |> Option.map snd 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. @@ -153,37 +153,42 @@ type internal FSharpProjectOptionsManager // compiled and #r will refer to files on disk let referencedProjectFileNames = [| |] let site = ProjectSitesAndFiles.CreateProjectSiteForScript(fileName, referencedProjectFileNames, options) - return ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject,site,fileName,options.ExtraProjectInfo,serviceProvider, true) + let deps, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject,site,fileName,options.ExtraProjectInfo,serviceProvider, true) + let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions) + return (deps, parsingOptions, projectOptions) else let site = ProjectSitesAndFiles.ProjectSiteOfSingleFile(fileName) - return ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject,site,fileName,extraProjectInfo,serviceProvider, true) + let deps, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject,site,fileName,extraProjectInfo,serviceProvider, true) + 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: ProjectId, site: IProjectSite, userOpName) = this.AddOrUpdateProject(projectId, (fun isRefresh -> let extraProjectInfo = Some(box workspace) - let tryGetOptionsForReferencedProject f = f |> tryGetOrCreateProjectId |> Option.bind this.TryGetOptionsForProject - let referencedProjects, options = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, site, site.ProjectFileName(), extraProjectInfo, serviceProvider, true) + let tryGetOptionsForReferencedProject f = f |> tryGetOrCreateProjectId |> Option.bind this.TryGetOptionsForProject |> Option.map snd + let referencedProjects, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, site, site.ProjectFileName(), extraProjectInfo, serviceProvider, true) let referencedProjectIds = referencedProjects |> Array.choose tryGetOrCreateProjectId - checkerProvider.Checker.InvalidateConfiguration(options, startBackgroundCompileIfAlreadySeen = not isRefresh, userOpName= userOpName + ".UpdateProjectInfo") - referencedProjectIds, options)) + checkerProvider.Checker.InvalidateConfiguration(projectOptions, startBackgroundCompileIfAlreadySeen = not isRefresh, userOpName= userOpName + ".UpdateProjectInfo") + let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions) + referencedProjectIds, parsingOptions, projectOptions)) /// Get compilation defines relevant for syntax processing. /// Quicker then TryGetOptionsForDocumentOrProject as it doesn't need to recompute the exact project /// options for a script. member this.GetCompilationDefinesForEditingDocument(document: Document) = let projectOptionsOpt = this.TryGetOptionsForProject(document.Project.Id) - let otherOptions = + let parsingOptions = match projectOptionsOpt with - | None -> [] - | Some options -> options.OtherOptions |> Array.toList - CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, otherOptions) + | None -> FSharpParsingOptions.Default + | Some (parsingOptions, _projectOptions) -> parsingOptions + CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, parsingOptions) /// Get the options for a project member this.TryGetOptionsForProject(projectId: ProjectId) = match projectTable.TryGetValue(projectId) with - | true, ((_referencedProjects, options), _) -> Some options + | true, ((_referencedProjects, parsingOptions, projectOptions), _) -> Some (parsingOptions, projectOptions) | _ -> None /// Get the exact options for a document or project @@ -194,7 +199,7 @@ type internal FSharpProjectOptionsManager // 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, _) -> + | true, (loadTime, _, _) -> try let fileName = document.FilePath let! cancellationToken = Async.CancellationToken @@ -202,9 +207,9 @@ type internal FSharpProjectOptionsManager // 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, options = this.ComputeSingleFileOptions (tryGetOrCreateProjectId, fileName, loadTime, sourceText.ToString(), document.Project.Solution.Workspace) - this.AddOrUpdateSingleFileProject(projectId, (loadTime, options)) - return Some options + let! _referencedProjectFileNames, parsingOptions, projectOptions = this.ComputeSingleFileOptions (tryGetOrCreateProjectId, fileName, loadTime, sourceText.ToString(), document.Project.Solution.Workspace) + this.AddOrUpdateSingleFileProject(projectId, (loadTime, parsingOptions, projectOptions)) + return Some (parsingOptions, projectOptions) with ex -> Assert.Exception(ex) return None @@ -216,7 +221,7 @@ type internal FSharpProjectOptionsManager member this.TryGetOptionsForEditingDocumentOrProject(document: Document) = let projectId = document.Project.Id match singleFileProjectTable.TryGetValue(projectId) with - | true, (_loadTime, originalOptions) -> Some originalOptions + | true, (_loadTime, parsingOptions, originalOptions) -> Some (parsingOptions, originalOptions) | _ -> this.TryGetOptionsForProject(projectId) member this.ProvideProjectSiteProvider(project:Project) = @@ -301,6 +306,8 @@ type internal FSharpProjectOptionsManager | true, value -> value | _ -> [||], [||], [||] + member __.Checker = checkerProvider.Checker + // Used to expose FSharpChecker/ProjectInfo manager to diagnostic providers // Diagnostic providers can be executed in environment that does not use MEF so they can rely only // on services exposed by the workspace @@ -574,8 +581,8 @@ type let projectDisplayName = projectDisplayNameOf projectFileName let projectId = workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectDisplayName) - let _referencedProjectFileNames, options = projectInfoManager.ComputeSingleFileOptions (tryGetOrCreateProjectId workspace, fileName, loadTime, fileContents, workspace) |> Async.RunSynchronously - projectInfoManager.AddOrUpdateSingleFileProject(projectId, (loadTime, options)) + let _referencedProjectFileNames, parsingOptions, projectOptions = projectInfoManager.ComputeSingleFileOptions (tryGetOrCreateProjectId workspace, fileName, loadTime, fileContents, workspace) |> Async.RunSynchronously + projectInfoManager.AddOrUpdateSingleFileProject(projectId, (loadTime, parsingOptions, projectOptions)) if isNull (workspace.ProjectTracker.GetProject projectId) then let projectContextFactory = package.ComponentModel.GetService(); diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index dfe76098b80..15995d015f0 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -30,10 +30,10 @@ module internal SymbolHelpers = let textLine = sourceText.Lines.GetLineFromPosition(position) let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, options.OtherOptions |> Seq.toList) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, parsingOptions) let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document.FilePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true, userOpName = userOpName) + let! _, _, checkFileResults = checker.ParseAndCheckDocument(document.FilePath, textVersionHash, sourceText.ToString(), projectOptions, allowStaleResults = true, userOpName = userOpName) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland, userOpName=userOpName) let! symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) |> liftAsync return symbolUses @@ -59,8 +59,8 @@ module internal SymbolHelpers = |> Seq.map (fun project -> async { match projectInfoManager.TryGetOptionsForProject(project.Id) with - | Some options -> - let! projectCheckResults = checker.ParseAndCheckProject(options, userOpName = userOpName) + | Some (_parsingOptions, projectOptions) -> + let! projectCheckResults = checker.ParseAndCheckProject(projectOptions, userOpName = userOpName) return! projectCheckResults.GetUsesOfSymbol(symbol) | None -> return [||] }) @@ -103,10 +103,10 @@ module internal SymbolHelpers = let! sourceText = document.GetTextAsync(cancellationToken) let originalText = sourceText.ToString(symbolSpan) do! Option.guard (originalText.Length > 0) - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, options.OtherOptions |> Seq.toList) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, parsingOptions) let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, symbolSpan.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, allowStaleResults = true, userOpName = userOpName) + let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = true, userOpName = userOpName) let textLine = sourceText.Lines.GetLineFromPosition(symbolSpan.Start) let textLinePos = sourceText.Lines.GetLinePosition(symbolSpan.Start) let fcsTextLineNumber = Line.fromZ textLinePos.Line diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index b964f1d2eff..e3479737db0 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs @@ -51,11 +51,11 @@ type internal FSharpFindUsagesService asyncMaybe { let! sourceText = document.GetTextAsync(context.CancellationToken) |> Async.AwaitTask |> liftAsync let checker = checkerProvider.Checker - let! options = projectInfoManager.TryGetOptionsForDocumentOrProject(document) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = true, userOpName = userOpName) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForDocumentOrProject(document) + let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, allowStaleResults = true, userOpName = userOpName) let textLine = sourceText.Lines.GetLineFromPosition(position).ToString() let lineNumber = sourceText.Lines.GetLinePosition(position).Line + 1 - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.FilePath, options.OtherOptions |> Seq.toList) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.FilePath, parsingOptions) let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland, userOpName=userOpName) @@ -112,8 +112,8 @@ type internal FSharpFindUsagesService projectsToCheck |> Seq.map (fun project -> asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForProject(project.Id) - let! projectCheckResults = checker.ParseAndCheckProject(options, userOpName = userOpName) |> liftAsync + let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForProject(project.Id) + let! projectCheckResults = checker.ParseAndCheckProject(projectOptions, userOpName = userOpName) |> liftAsync return! projectCheckResults.GetUsesOfSymbol(symbolUse.Symbol) |> liftAsync } |> Async.map (Option.defaultValue [||])) |> Async.Parallel diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs index 9925058fcfd..7707b8a2554 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs @@ -57,8 +57,8 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP /// Helper function that is used to determine the navigation strategy to apply, can be tuned towards signatures or implementation files. let findSymbolHelper (originDocument: Document, originRange: range, sourceText: SourceText, preferSignature: bool) : Async = asyncMaybe { - let! projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject originDocument - let defines = CompilerEnvironment.GetCompilationDefinesForEditing (originDocument.FilePath, projectOptions.OtherOptions |> Seq.toList) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject originDocument + let defines = CompilerEnvironment.GetCompilationDefinesForEditing (originDocument.FilePath, parsingOptions) let! originTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (sourceText, originRange) let position = originTextSpan.Start let! lexerSymbol = Tokenizer.getSymbolAtPosition (originDocument.Id, sourceText, position, originDocument.FilePath, defines, SymbolLookupKind.Greedy, false) @@ -67,7 +67,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP let fcsTextLineNumber = Line.fromZ textLinePos.Line let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() - let! _, _, checkFileResults = checker.ParseAndCheckDocument (originDocument,projectOptions,allowStaleResults=true,sourceText=sourceText, userOpName = userOpName) + let! _, _, checkFileResults = checker.ParseAndCheckDocument (originDocument, projectOptions, allowStaleResults=true,sourceText=sourceText, userOpName = userOpName) let idRange = lexerSymbol.Ident.idRange let! fsSymbolUse = checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland, userOpName=userOpName) let symbol = fsSymbolUse.Symbol @@ -79,7 +79,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP if not (File.Exists fsfilePath) then return! None else let! implDoc = originDocument.Project.Solution.TryGetDocumentFromPath fsfilePath let! implSourceText = implDoc.GetTextAsync () - let! projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject implDoc + let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject implDoc let! _, _, checkFileResults = checker.ParseAndCheckDocument (implDoc, projectOptions, allowStaleResults=true, sourceText=implSourceText, userOpName = userOpName) let! symbolUses = checkFileResults.GetUsesOfSymbolInFile symbol |> liftAsync let! implSymbol = symbolUses |> Array.tryHead @@ -295,9 +295,9 @@ type internal FSharpGoToDefinitionService /// at the provided position in the document. member __.FindDefinitionsTask(originDocument: Document, position: int, cancellationToken: CancellationToken) = asyncMaybe { - let! projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject originDocument + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject originDocument let! sourceText = originDocument.GetTextAsync () |> liftTaskAsync - let defines = CompilerEnvironment.GetCompilationDefinesForEditing (originDocument.FilePath, projectOptions.OtherOptions |> Seq.toList) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing (originDocument.FilePath, parsingOptions) let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line @@ -382,7 +382,7 @@ type internal FSharpGoToDefinitionService let! implDocument = originDocument.Project.Solution.TryGetDocumentFromPath implFilePath let! implVersion = implDocument.GetTextVersionAsync () |> liftTaskAsync let! implSourceText = implDocument.GetTextAsync () |> liftTaskAsync - let! projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject implDocument + let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject implDocument let! targetRange = gotoDefinition.FindSymbolDeclarationInFile(targetSymbolUse, implFilePath, implSourceText.ToString(), projectOptions, implVersion.GetHashCode()) diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs index d84202eac73..a1cee3bff49 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs @@ -187,11 +187,11 @@ type internal FSharpNavigateToSearchService let itemsByDocumentId = ConditionalWeakTable() - let getNavigableItems(document: Document, options: FSharpProjectOptions) = + let getNavigableItems(document: Document, parsingOptions: FSharpParsingOptions) = async { let! cancellationToken = Async.CancellationToken let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask - let! parseResults = checkerProvider.Checker.ParseFileInProject(document.FilePath, sourceText.ToString(), options) + let! parseResults = checkerProvider.Checker.ParseFile(document.FilePath, sourceText.ToString(), parsingOptions) return match parseResults.ParseTree |> Option.map NavigateTo.getNavigableItems with | Some items -> @@ -206,7 +206,7 @@ type internal FSharpNavigateToSearchService | None -> [||] } - let getCachedIndexedNavigableItems(document: Document, options: FSharpProjectOptions) = + let getCachedIndexedNavigableItems(document: Document, parsingOptions: FSharpParsingOptions) = async { let! cancellationToken = Async.CancellationToken let! textVersion = document.GetTextVersionAsync(cancellationToken) |> Async.AwaitTask @@ -215,7 +215,7 @@ type internal FSharpNavigateToSearchService | true, (oldTextVersionHash, items) when oldTextVersionHash = textVersionHash -> return items | _ -> - let! items = getNavigableItems(document, options) + let! items = getNavigableItems(document, parsingOptions) let indexedItems = Index.build items itemsByDocumentId.Remove(document.Id) |> ignore itemsByDocumentId.Add(document.Id, (textVersionHash, indexedItems)) @@ -233,10 +233,10 @@ type internal FSharpNavigateToSearchService interface INavigateToSearchService with member __.SearchProjectAsync(project, searchPattern, cancellationToken) : Task> = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForProject(project.Id) + let! parsingOptions, _options = projectInfoManager.TryGetOptionsForProject(project.Id) let! items = project.Documents - |> Seq.map (fun document -> getCachedIndexedNavigableItems(document, options)) + |> Seq.map (fun document -> getCachedIndexedNavigableItems(document, parsingOptions)) |> Async.Parallel |> liftAsync @@ -265,8 +265,8 @@ type internal FSharpNavigateToSearchService member __.SearchDocumentAsync(document, searchPattern, cancellationToken) : Task> = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForDocumentOrProject(document) - let! items = getCachedIndexedNavigableItems(document, options) |> liftAsync + let! parsingOptions, _options = projectInfoManager.TryGetOptionsForDocumentOrProject(document) + let! items = getCachedIndexedNavigableItems(document, parsingOptions) |> liftAsync return items.Find(searchPattern) } |> Async.map (Option.defaultValue [||]) diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs index 7e0fdac0def..34fad074bac 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs @@ -32,9 +32,9 @@ type internal FSharpNavigationBarItemService interface INavigationBarItemService with member __.GetItemsAsync(document, cancellationToken) : Task> = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) - let! parsedInput = checkerProvider.Checker.ParseDocument(document, options, sourceText=sourceText, userOpName=userOpName) + let! parsedInput = checkerProvider.Checker.ParseDocument(document, parsingOptions, sourceText=sourceText, userOpName=userOpName) let navItems = NavigationImpl.getNavigation parsedInput let rangeToTextSpan range = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) return diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index 1324602ffb2..8e4a6fa82a0 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -55,8 +55,8 @@ module private FSharpQuickInfo = let extLineText = (extSourceText.Lines.GetLineFromPosition extSpan.Start).ToString() // project options need to be retrieved because the signature file could be in another project - let! extProjectOptions = projectInfoManager.TryGetOptionsForProject extDocId.ProjectId - let extDefines = CompilerEnvironment.GetCompilationDefinesForEditing (extDocument.FilePath, List.ofSeq extProjectOptions.OtherOptions) + let! extParsingOptions, extProjectOptions = projectInfoManager.TryGetOptionsForProject extDocId.ProjectId + let extDefines = CompilerEnvironment.GetCompilationDefinesForEditing (extDocument.FilePath, extParsingOptions) let! extLexerSymbol = Tokenizer.getSymbolAtPosition(extDocId, extSourceText, extSpan.Start, declRange.FileName, extDefines, SymbolLookupKind.Greedy, true) let! _, _, extCheckFileResults = checker.ParseAndCheckDocument(extDocument, extProjectOptions, allowStaleResults=true, sourceText=extSourceText, userOpName = userOpName) @@ -91,8 +91,8 @@ module private FSharpQuickInfo = asyncMaybe { let! sourceText = document.GetTextAsync cancellationToken - let! projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.FilePath, projectOptions.OtherOptions |> Seq.toList) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.FilePath, parsingOptions) let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, true) let idRange = lexerSymbol.Ident.idRange let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = true, sourceText=sourceText, userOpName = userOpName) @@ -170,12 +170,12 @@ type internal FSharpQuickInfoProvider let xmlMemberIndexService = serviceProvider.GetService(typeof) :?> IVsXMLMemberIndexService let documentationBuilder = XmlDocumentation.CreateDocumentationBuilder(xmlMemberIndexService, serviceProvider.DTE) - static member ProvideQuickInfo(checker: FSharpChecker, documentId: DocumentId, sourceText: SourceText, filePath: string, position: int, options: FSharpProjectOptions, textVersionHash: int) = + static member ProvideQuickInfo(checker: FSharpChecker, documentId: DocumentId, sourceText: SourceText, filePath: string, position: int, parsingOptions: FSharpParsingOptions, options: FSharpProjectOptions, textVersionHash: int) = asyncMaybe { let! _, _, checkFileResults = checker.ParseAndCheckDocument (filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true, userOpName = FSharpQuickInfo.userOpName) let textLine = sourceText.Lines.GetLineFromPosition position let textLineNumber = textLine.LineNumber + 1 // Roslyn line numbers are zero-based - let defines = CompilerEnvironment.GetCompilationDefinesForEditing (filePath, options.OtherOptions |> Seq.toList) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing (filePath, parsingOptions) let! symbol = Tokenizer.getSymbolAtPosition (documentId, sourceText, position, filePath, defines, SymbolLookupKind.Precise, true) let! res = checkFileResults.GetStructuredToolTipText (textLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland, FSharpTokenTag.IDENT, userOpName=FSharpQuickInfo.userOpName) |> liftAsync match res with diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index 68a41f19e6d..865165665a3 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -148,9 +148,9 @@ type internal FSharpBlockStructureService(checker: FSharpChecker, projectInfoMan override __.GetBlockStructureAsync(document, cancellationToken) : Task = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) - let! parsedInput = checker.ParseDocument(document, options, sourceText, userOpName) + let! parsedInput = checker.ParseDocument(document, parsingOptions, sourceText, userOpName) return createBlockSpans sourceText parsedInput |> Seq.toImmutableArray } |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) diff --git a/vsintegration/src/FSharp.LanguageService/FSharpSource.fs b/vsintegration/src/FSharp.LanguageService/FSharpSource.fs index 24962179193..f270d3ba94a 100644 --- a/vsintegration/src/FSharp.LanguageService/FSharpSource.fs +++ b/vsintegration/src/FSharp.LanguageService/FSharpSource.fs @@ -356,7 +356,7 @@ type internal FSharpSource_DEPRECATED(service:LanguageService_DEPRECATED, textLi |] // get a sync parse of the file - let co = + let co, _ = { ProjectFileName = fileName + ".dummy.fsproj" SourceFiles = [| fileName |] OtherOptions = flags @@ -368,8 +368,9 @@ type internal FSharpSource_DEPRECATED(service:LanguageService_DEPRECATED, textLi OriginalLoadReferences = [] ExtraProjectInfo=None Stamp = None } + |> ic.GetParsingOptionsFromProjectOptions - ic.ParseFileInProject(fileName, source.GetText(), co) |> Async.RunSynchronously + ic.ParseFile(fileName, source.GetText(), co) |> Async.RunSynchronously override source.GetCommentFormat() = let mutable info = new CommentInfo() diff --git a/vsintegration/src/FSharp.LanguageService/ProjectSitesAndFiles.fs b/vsintegration/src/FSharp.LanguageService/ProjectSitesAndFiles.fs index a7367999ab4..8f4ddaa8772 100644 --- a/vsintegration/src/FSharp.LanguageService/ProjectSitesAndFiles.fs +++ b/vsintegration/src/FSharp.LanguageService/ProjectSitesAndFiles.fs @@ -242,10 +242,10 @@ type internal ProjectSitesAndFiles() = | None -> None - member art.GetDefinesForFile_DEPRECATED(rdt:IVsRunningDocumentTable, filename : string) = + member art.GetDefinesForFile_DEPRECATED(rdt:IVsRunningDocumentTable, filename : string, checker:FSharpChecker) = // The only caller of this function calls it each time it needs to colorize a line, so this call must execute very fast. if SourceFile.MustBeSingleFileProject(filename) then - CompilerEnvironment.GetCompilationDefinesForEditing(filename,[]) + CompilerEnvironment.GetCompilationDefinesForEditing(filename,FSharpParsingOptions.Default) else let siteOpt = match VsRunningDocumentTable.FindDocumentWithoutLocking(rdt,filename) with @@ -257,7 +257,8 @@ type internal ProjectSitesAndFiles() = | Some site -> site | None -> ProjectSitesAndFiles.ProjectSiteOfSingleFile(filename) - CompilerEnvironment.GetCompilationDefinesForEditing(filename,site.CompilerFlags() |> Array.toList) + let parsingOptions,_ = checker.GetParsingOptionsFromCommandLineArgs(site.CompilerFlags() |> Array.toList) + CompilerEnvironment.GetCompilationDefinesForEditing(filename,parsingOptions) member art.TryFindOwningProject_DEPRECATED(rdt:IVsRunningDocumentTable, filename) = diff --git a/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs b/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs index 23b24f741d9..4e95bf58303 100644 --- a/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs +++ b/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs @@ -210,7 +210,7 @@ type internal FSharpLanguageServiceTestable() as this = // So this is not ideal from a perf perspective, but it is easy to reason about the correctness. let filename = VsTextLines.GetFilename buffer let rdt = this.ServiceProvider.RunningDocumentTable - let defines = this.ProjectSitesAndFiles.GetDefinesForFile_DEPRECATED(rdt, filename) + let defines = this.ProjectSitesAndFiles.GetDefinesForFile_DEPRECATED(rdt, filename, this.FSharpChecker) let sourceTokenizer = FSharpSourceTokenizer(defines,Some(filename)) sourceTokenizer.CreateLineTokenizer(source)) diff --git a/vsintegration/tests/unittests/BraceMatchingServiceTests.fs b/vsintegration/tests/unittests/BraceMatchingServiceTests.fs index 277ad012f43..8e3d4ee7c9a 100644 --- a/vsintegration/tests/unittests/BraceMatchingServiceTests.fs +++ b/vsintegration/tests/unittests/BraceMatchingServiceTests.fs @@ -17,7 +17,7 @@ open UnitTests.TestLib.LanguageService [][] type BraceMatchingServiceTests() = let fileName = "C:\\test.fs" - let options: FSharpProjectOptions = { + let projectOptions: FSharpProjectOptions = { ProjectFileName = "C:\\test.fsproj" SourceFiles = [| fileName |] ReferencedProjects = [| |] @@ -36,7 +36,8 @@ type BraceMatchingServiceTests() = let position = fileContents.IndexOf(marker) Assert.IsTrue(position >= 0, "Cannot find marker '{0}' in file contents", marker) - match FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, fileName, options, position, "UnitTest") |> Async.RunSynchronously with + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions + match FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, fileName, parsingOptions, position, "UnitTest") |> Async.RunSynchronously with | None -> () | Some(left, right) -> Assert.Fail("Found match for brace '{0}'", marker) @@ -48,7 +49,8 @@ type BraceMatchingServiceTests() = Assert.IsTrue(startMarkerPosition >= 0, "Cannot find start marker '{0}' in file contents", startMarkerPosition) Assert.IsTrue(endMarkerPosition >= 0, "Cannot find end marker '{0}' in file contents", endMarkerPosition) - match FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, fileName, options, startMarkerPosition, "UnitTest") |> Async.RunSynchronously with + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions + match FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, fileName, parsingOptions, startMarkerPosition, "UnitTest") |> Async.RunSynchronously with | None -> Assert.Fail("Didn't find a match for start brace at position '{0}", startMarkerPosition) | Some(left, right) -> let endPositionInRange(range) = @@ -169,9 +171,10 @@ let main argv = // https://github.com/Microsoft/visualfsharp/issues/2092 let sourceText = SourceText.From(fileContents) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions matchingPositions |> Array.iter (fun position -> - match FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, fileName, options, position, "UnitTest") |> Async.RunSynchronously with + match FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, fileName, parsingOptions, position, "UnitTest") |> Async.RunSynchronously with | Some _ -> () | None -> match position with diff --git a/vsintegration/tests/unittests/BreakpointResolutionService.fs b/vsintegration/tests/unittests/BreakpointResolutionService.fs index 3461d3009b1..c84c1b76f76 100644 --- a/vsintegration/tests/unittests/BreakpointResolutionService.fs +++ b/vsintegration/tests/unittests/BreakpointResolutionService.fs @@ -22,7 +22,7 @@ open UnitTests.TestLib.LanguageService type BreakpointResolutionServiceTests() = let fileName = "C:\\test.fs" - let options: FSharpProjectOptions = { + let projectOptions: FSharpProjectOptions = { ProjectFileName = "C:\\test.fsproj" SourceFiles = [| fileName |] ReferencedProjects = [| |] @@ -74,7 +74,8 @@ let main argv = let sourceText = SourceText.From(code) let searchSpan = TextSpan.FromBounds(searchPosition, searchPosition + searchToken.Length) - let actualResolutionOption = FSharpBreakpointResolutionService.GetBreakpointLocation(checker, sourceText, fileName, searchSpan, options) |> Async.RunSynchronously + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions + let actualResolutionOption = FSharpBreakpointResolutionService.GetBreakpointLocation(checker, sourceText, fileName, searchSpan, parsingOptions) |> Async.RunSynchronously match actualResolutionOption with | None -> Assert.IsTrue(expectedResolution.IsNone, "BreakpointResolutionService failed to resolve breakpoint position") diff --git a/vsintegration/tests/unittests/CompletionProviderTests.fs b/vsintegration/tests/unittests/CompletionProviderTests.fs index a0de868fc75..e34ed22f0ea 100644 --- a/vsintegration/tests/unittests/CompletionProviderTests.fs +++ b/vsintegration/tests/unittests/CompletionProviderTests.fs @@ -35,7 +35,7 @@ open Microsoft.FSharp.Compiler.SourceCodeServices open UnitTests.TestLib.LanguageService let filePath = "C:\\test.fs" -let internal options = { +let internal projectOptions = { ProjectFileName = "C:\\test.fsproj" SourceFiles = [| filePath |] ReferencedProjects = [| |] @@ -52,7 +52,7 @@ let internal options = { let VerifyCompletionList(fileContents: string, marker: string, expected: string list, unexpected: string list) = let caretPosition = fileContents.IndexOf(marker) + marker.Length let results = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, SourceText.From(fileContents), caretPosition, options, filePath, 0, fun _ -> []) + FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, SourceText.From(fileContents), caretPosition, projectOptions, filePath, 0, fun _ -> []) |> Async.RunSynchronously |> Option.defaultValue (ResizeArray()) |> Seq.map(fun result -> result.DisplayText) @@ -67,7 +67,7 @@ let VerifyCompletionListExactly(fileContents: string, marker: string, expected: let caretPosition = fileContents.IndexOf(marker) + marker.Length let actual = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, SourceText.From(fileContents), caretPosition, options, filePath, 0, fun _ -> []) + FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, SourceText.From(fileContents), caretPosition, projectOptions, filePath, 0, fun _ -> []) |> Async.RunSynchronously |> Option.defaultValue (ResizeArray()) |> Seq.toList diff --git a/vsintegration/tests/unittests/DocumentDiagnosticAnalyzerTests.fs b/vsintegration/tests/unittests/DocumentDiagnosticAnalyzerTests.fs index 912738d5380..d7302afc296 100644 --- a/vsintegration/tests/unittests/DocumentDiagnosticAnalyzerTests.fs +++ b/vsintegration/tests/unittests/DocumentDiagnosticAnalyzerTests.fs @@ -24,7 +24,7 @@ type DocumentDiagnosticAnalyzerTests() = let filePath = "C:\\test.fs" let startMarker = "(*start*)" let endMarker = "(*end*)" - let options: FSharpProjectOptions = { + let projectOptions: FSharpProjectOptions = { ProjectFileName = "C:\\test.fsproj" SourceFiles = [| filePath |] ReferencedProjects = [| |] @@ -40,15 +40,17 @@ type DocumentDiagnosticAnalyzerTests() = let getDiagnostics (fileContents: string) = async { - let! syntacticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checker, filePath, SourceText.From(fileContents), 0, options, DiagnosticsType.Syntax) - let! semanticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checker, filePath, SourceText.From(fileContents), 0, options, DiagnosticsType.Semantic) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions + let! syntacticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checker, filePath, SourceText.From(fileContents), 0, parsingOptions, projectOptions, DiagnosticsType.Syntax) + let! semanticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checker, filePath, SourceText.From(fileContents), 0, parsingOptions, projectOptions, DiagnosticsType.Semantic) return syntacticDiagnostics.AddRange(semanticDiagnostics) } |> Async.RunSynchronously member private this.VerifyNoErrors(fileContents: string, ?additionalFlags: string[]) = + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions let additionalOptions = match additionalFlags with - | None -> options - | Some(flags) -> {options with OtherOptions = Array.append options.OtherOptions flags} + | None -> projectOptions + | Some(flags) -> {projectOptions with OtherOptions = Array.append projectOptions.OtherOptions flags} let errors = getDiagnostics fileContents Assert.AreEqual(0, errors.Length, "There should be no errors generated") diff --git a/vsintegration/tests/unittests/DocumentHighlightsServiceTests.fs b/vsintegration/tests/unittests/DocumentHighlightsServiceTests.fs index 8b884c5f610..53ac885768a 100644 --- a/vsintegration/tests/unittests/DocumentHighlightsServiceTests.fs +++ b/vsintegration/tests/unittests/DocumentHighlightsServiceTests.fs @@ -37,7 +37,7 @@ open UnitTests.TestLib.LanguageService let filePath = "C:\\test.fs" -let internal options = { +let internal projectOptions = { ProjectFileName = "C:\\test.fsproj" SourceFiles = [| filePath |] ReferencedProjects = [| |] @@ -53,7 +53,7 @@ let internal options = { let private getSpans (sourceText: SourceText) (caretPosition: int) = let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - FSharpDocumentHighlightsService.GetDocumentHighlights(checker, documentId, sourceText, filePath, caretPosition, [], options, 0) + FSharpDocumentHighlightsService.GetDocumentHighlights(checker, documentId, sourceText, filePath, caretPosition, [], projectOptions, 0) |> Async.RunSynchronously |> Option.defaultValue [||] diff --git a/vsintegration/tests/unittests/EditorFormattingServiceTests.fs b/vsintegration/tests/unittests/EditorFormattingServiceTests.fs index 83cd4c381fd..7fa10f056c9 100644 --- a/vsintegration/tests/unittests/EditorFormattingServiceTests.fs +++ b/vsintegration/tests/unittests/EditorFormattingServiceTests.fs @@ -17,8 +17,8 @@ open Microsoft.CodeAnalysis.Formatting [] [] type EditorFormattingServiceTests() = - static let filePath = "C:\\test.fs" - static let options: FSharpProjectOptions = { + let filePath = "C:\\test.fs" + let projectOptions : FSharpProjectOptions = { ProjectFileName = "C:\\test.fsproj" SourceFiles = [| filePath |] ReferencedProjects = [| |] @@ -31,11 +31,12 @@ type EditorFormattingServiceTests() = ExtraProjectInfo = None Stamp = None } + //let parsingOptions: FSharpParsingOptions = - static let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - static let indentStyle = FormattingOptions.IndentStyle.Smart + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let indentStyle = FormattingOptions.IndentStyle.Smart - static let template = """ + let template = """ let foo = [ 15 ]marker1 @@ -66,8 +67,9 @@ let def = let sourceText = SourceText.From(template) let lineNumber = sourceText.Lines |> Seq.findIndex (fun line -> line.Span.Contains position) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions - let changesOpt = FSharpEditorFormattingService.GetFormattingChanges(documentId, sourceText, filePath, checker, indentStyle, Some options, position) |> Async.RunSynchronously + let changesOpt = FSharpEditorFormattingService.GetFormattingChanges(documentId, sourceText, filePath, checker, indentStyle, Some (parsingOptions, projectOptions), position) |> Async.RunSynchronously match changesOpt with | None -> Assert.Fail("Expected a text change, but got None") | Some change -> diff --git a/vsintegration/tests/unittests/IndentationServiceTests.fs b/vsintegration/tests/unittests/IndentationServiceTests.fs index 9534fa99113..66f92ea534f 100644 --- a/vsintegration/tests/unittests/IndentationServiceTests.fs +++ b/vsintegration/tests/unittests/IndentationServiceTests.fs @@ -14,10 +14,12 @@ open Microsoft.VisualStudio.FSharp.Editor open Microsoft.FSharp.Compiler.SourceCodeServices open Microsoft.CodeAnalysis.Formatting +open UnitTests.TestLib.LanguageService + [][] type IndentationServiceTests() = - static let filePath = "C:\\test.fs" - static let options: FSharpProjectOptions = { + let filePath = "C:\\test.fs" + let projectOptions: FSharpProjectOptions = { ProjectFileName = "C:\\test.fsproj" SourceFiles = [| filePath |] ReferencedProjects = [| |] @@ -31,13 +33,13 @@ type IndentationServiceTests() = Stamp = None } - static let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - static let tabSize = 4 - static let indentStyle = FormattingOptions.IndentStyle.Smart + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let tabSize = 4 + let indentStyle = FormattingOptions.IndentStyle.Smart - static let indentComment = System.Text.RegularExpressions.Regex(@"\$\s*Indent:\s*(\d+)\s*\$") + let indentComment = System.Text.RegularExpressions.Regex(@"\$\s*Indent:\s*(\d+)\s*\$") - static let consoleProjectTemplate = " + let consoleProjectTemplate = " // Learn more about F# at http://fsharp.org // See the 'F# Tutorial' project for more help. @@ -46,20 +48,20 @@ let main argv = printfn \"%A\" argv 0 // return an integer exit code" - static let libraryProjectTemplate = " + let libraryProjectTemplate = " namespace ProjectNamespace type Class1() = member this.X = \"F#\"" - static let nestedTypesTemplate = " + let nestedTypesTemplate = " namespace testspace type testtype static member testmember = 1 " - static let autoIndentTemplate = " + let autoIndentTemplate = " let plus x y = x + y // $Indent: 4$ @@ -130,33 +132,33 @@ while true do // The follwing line should inherit that indentation too $Indent: 4$ " - static member private testCases: Object[][] = [| - [| None; 0; consoleProjectTemplate |] - [| None; 1; consoleProjectTemplate |] - [| Some(0); 2; consoleProjectTemplate |] - [| Some(0); 3; consoleProjectTemplate |] - [| Some(0); 4; consoleProjectTemplate |] - [| Some(0); 5; consoleProjectTemplate |] - [| Some(4); 6; consoleProjectTemplate |] - [| Some(4); 7; consoleProjectTemplate |] - [| Some(4); 8; consoleProjectTemplate |] + let testCases = [| + ( None, 0, consoleProjectTemplate ) + ( None, 1, consoleProjectTemplate ) + ( Some(0), 2, consoleProjectTemplate ) + ( Some(0), 3, consoleProjectTemplate ) + ( Some(0), 4, consoleProjectTemplate ) + ( Some(0), 5, consoleProjectTemplate ) + ( Some(4), 6, consoleProjectTemplate ) + ( Some(4), 7, consoleProjectTemplate ) + ( Some(4), 8, consoleProjectTemplate ) - [| None; 0; libraryProjectTemplate |] - [| None; 1; libraryProjectTemplate |] - [| Some(0); 2; libraryProjectTemplate |] - [| Some(0); 3; libraryProjectTemplate |] - [| Some(4); 4; libraryProjectTemplate |] - [| Some(4); 5; libraryProjectTemplate |] + ( None, 0, libraryProjectTemplate ) + ( None, 1, libraryProjectTemplate ) + ( Some(0), 2, libraryProjectTemplate ) + ( Some(0), 3, libraryProjectTemplate ) + ( Some(4), 4, libraryProjectTemplate ) + ( Some(4), 5, libraryProjectTemplate ) - [| None; 0; nestedTypesTemplate |] - [| None; 1; nestedTypesTemplate |] - [| Some(0); 2; nestedTypesTemplate |] - [| Some(4); 3; nestedTypesTemplate |] - [| Some(8); 4; nestedTypesTemplate |] - [| Some(8); 5; nestedTypesTemplate |] + ( None, 0, nestedTypesTemplate ) + ( None, 1, nestedTypesTemplate ) + ( Some(0), 2, nestedTypesTemplate ) + ( Some(4), 3, nestedTypesTemplate ) + ( Some(8), 4, nestedTypesTemplate ) + ( Some(8), 5, nestedTypesTemplate ) |] - static member private autoIndentTestCases = + let autoIndentTestCases = autoIndentTemplate.Split [|'\n'|] |> Array.map (fun s -> s.Trim()) |> Array.indexed @@ -165,22 +167,26 @@ while true do if m.Success then Some (line, System.Convert.ToInt32 m.Groups.[1].Value) else None ) |> Array.map (fun (lineNumber, expectedIndentation) -> - [| Some(expectedIndentation); lineNumber; autoIndentTemplate |]: Object[] ) + ( Some(expectedIndentation), lineNumber, autoIndentTemplate )) - [] - member this.TestIndentation(expectedIndentation: Option, lineNumber: int, template: string) = - let sourceText = SourceText.From(template) + member this.TestIndentation() = + for (expectedIndentation, lineNumber, template) in testCases do + let sourceText = SourceText.From(template) - let actualIndentation = FSharpIndentationService.GetDesiredIndentation(documentId, sourceText, filePath, lineNumber, tabSize, indentStyle, Some options) - match expectedIndentation with - | None -> Assert.IsTrue(actualIndentation.IsNone, "No indentation was expected at line {0}", lineNumber) - | Some indentation -> Assert.AreEqual(expectedIndentation.Value, actualIndentation.Value, "Indentation on line {0} doesn't match", lineNumber) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions + let actualIndentation = FSharpIndentationService.GetDesiredIndentation(documentId, sourceText, filePath, lineNumber, tabSize, indentStyle, Some (parsingOptions, projectOptions)) + match expectedIndentation with + | None -> Assert.IsTrue(actualIndentation.IsNone, "No indentation was expected at line {0}", lineNumber) + | Some indentation -> Assert.AreEqual(expectedIndentation.Value, actualIndentation.Value, "Indentation on line {0} doesn't match", lineNumber) - [] - member this.TestAutoIndentation(expectedIndentation: Option, lineNumber: int, template: string) = - let sourceText = SourceText.From(template) + member this.TestAutoIndentation() = + for (expectedIndentation, lineNumber, template) in autoIndentTestCases do + + + let sourceText = SourceText.From(template) - let actualIndentation = FSharpIndentationService.GetDesiredIndentation(documentId, sourceText, filePath, lineNumber, tabSize, indentStyle, Some options) - match expectedIndentation with - | None -> Assert.IsTrue(actualIndentation.IsNone, "No indentation was expected at line {0}", lineNumber) - | Some indentation -> Assert.AreEqual(expectedIndentation.Value, actualIndentation.Value, "Indentation on line {0} doesn't match", lineNumber) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions + let actualIndentation = FSharpIndentationService.GetDesiredIndentation(documentId, sourceText, filePath, lineNumber, tabSize, indentStyle, Some (parsingOptions, projectOptions)) + match expectedIndentation with + | None -> Assert.IsTrue(actualIndentation.IsNone, "No indentation was expected at line {0}", lineNumber) + | Some indentation -> Assert.AreEqual(expectedIndentation.Value, actualIndentation.Value, "Indentation on line {0} doesn't match", lineNumber) diff --git a/vsintegration/tests/unittests/QuickInfoProviderTests.fs b/vsintegration/tests/unittests/QuickInfoProviderTests.fs index 13500ea5c89..45d39459564 100644 --- a/vsintegration/tests/unittests/QuickInfoProviderTests.fs +++ b/vsintegration/tests/unittests/QuickInfoProviderTests.fs @@ -34,7 +34,7 @@ open UnitTests.TestLib.LanguageService let filePath = "C:\\test.fs" -let internal options = { +let internal projectOptions = { ProjectFileName = "C:\\test.fsproj" SourceFiles = [| filePath |] ReferencedProjects = [| |] @@ -97,8 +97,9 @@ Full name: System.Console" let caretPosition = fileContents.IndexOf(symbol) let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions let quickInfo = - FSharpQuickInfoProvider.ProvideQuickInfo(checker, documentId, SourceText.From(fileContents), filePath, caretPosition, options, 0) + FSharpQuickInfoProvider.ProvideQuickInfo(checker, documentId, SourceText.From(fileContents), filePath, caretPosition, parsingOptions, projectOptions, 0) |> Async.RunSynchronously let actual = quickInfo |> Option.map (fun (text, _, _, _) -> getQuickInfoText text) @@ -227,8 +228,9 @@ let res8 = abs 5.0 let caretPosition = fileContents.IndexOf(symbol) + symbol.Length - 1 let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions let quickInfo = - FSharpQuickInfoProvider.ProvideQuickInfo(checker, documentId, SourceText.From(fileContents), filePath, caretPosition, options, 0) + FSharpQuickInfoProvider.ProvideQuickInfo(checker, documentId, SourceText.From(fileContents), filePath, caretPosition, parsingOptions, projectOptions, 0) |> Async.RunSynchronously let actual = quickInfo |> Option.map (fun (text, _, _, _) -> getQuickInfoText text) diff --git a/vsintegration/tests/unittests/SignatureHelpProviderTests.fs b/vsintegration/tests/unittests/SignatureHelpProviderTests.fs index 1d1da4e85af..2280116b4fe 100644 --- a/vsintegration/tests/unittests/SignatureHelpProviderTests.fs +++ b/vsintegration/tests/unittests/SignatureHelpProviderTests.fs @@ -34,7 +34,7 @@ let filePath = "C:\\test.fs" let PathRelativeToTestAssembly p = Path.Combine(Path.GetDirectoryName(Uri( System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath), p) -let internal options = { +let internal projectOptions = { ProjectFileName = "C:\\test.fsproj" SourceFiles = [| filePath |] ReferencedProjects = [| |] @@ -145,7 +145,7 @@ type foo5 = N1.T } let triggerChar = if marker = "," then Some ',' elif marker = "(" then Some '(' elif marker = "<" then Some '<' else None - let triggered = FSharpSignatureHelpProvider.ProvideMethodsAsyncAux(checker, documentationProvider, SourceText.From(fileContents), caretPosition, options, triggerChar, filePath, 0) |> Async.RunSynchronously + let triggered = FSharpSignatureHelpProvider.ProvideMethodsAsyncAux(checker, documentationProvider, SourceText.From(fileContents), caretPosition, projectOptions, triggerChar, filePath, 0) |> Async.RunSynchronously checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() let actual = match triggered with