diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 05c83bb58ef..03403258adc 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1079,6 +1079,14 @@ type IncrementalBuilder(tcGlobals, return { state with finalizedBoundModel = Some result }, result } + let tryGetSlot (state: IncrementalBuilderState) slot = + match state.boundModels.[slot] with + | Some boundModel -> + (boundModel, state.stampedFileNames.[slot]) + |> Some + | _ -> + None + let tryGetBeforeSlot (state: IncrementalBuilderState) slot = match slot with | 0 (* first file *) -> @@ -1089,12 +1097,7 @@ type IncrementalBuilder(tcGlobals, | _ -> None | _ -> - match state.boundModels.[slot - 1] with - | Some boundModel -> - (boundModel, state.stampedFileNames.[slot - 1]) - |> Some - | _ -> - None + tryGetSlot state (slot - 1) let evalUpToTargetSlot state (cache: TimeStampCache) ctok targetSlot = cancellable { @@ -1192,13 +1195,6 @@ type IncrementalBuilder(tcGlobals, match result with | Some (boundModel, timestamp) -> Some (PartialCheckResults (boundModel, timestamp)) | _ -> None - - - member builder.AreCheckResultsBeforeFileInProjectReady filename = - let slotOfFile = builder.GetSlotOfFileName filename - match tryGetBeforeSlot currentState slotOfFile with - | Some _ -> true - | _ -> false member builder.TryGetCheckResultsBeforeFileInProject (filename) = let cache = TimeStampCache defaultTimeStamp @@ -1211,6 +1207,9 @@ type IncrementalBuilder(tcGlobals, | Some(boundModel, timestamp) -> PartialCheckResults(boundModel, timestamp) |> Some | _ -> None + member builder.AreCheckResultsBeforeFileInProjectReady filename = + (builder.TryGetCheckResultsBeforeFileInProject filename).IsSome + member private _.GetCheckResultsBeforeSlotInProject (ctok: CompilationThreadToken, slotOfFile, enablePartialTypeChecking) = cancellable { let cache = TimeStampCache defaultTimeStamp diff --git a/src/fsharp/service/Reactor.fs b/src/fsharp/service/Reactor.fs index 5f5eebe711a..e8d8de2f403 100755 --- a/src/fsharp/service/Reactor.fs +++ b/src/fsharp/service/Reactor.fs @@ -41,6 +41,7 @@ type Reactor() = // so that when the reactor picks up a thread from the thread pool we can set the culture let mutable culture = CultureInfo(CultureInfo.CurrentUICulture.Name) + let gate = obj() let mutable bgOpCts = new CancellationTokenSource() let sw = new System.Diagnostics.Stopwatch() @@ -86,9 +87,11 @@ type Reactor() = match bgOpOpt with | None -> None | Some (bgUserOpName, bgOpName, bgOpArg, bgOp) -> - let oldBgOpCts = bgOpCts - bgOpCts <- new CancellationTokenSource() - oldBgOpCts.Dispose() + lock gate (fun () -> + let oldBgOpCts = bgOpCts + bgOpCts <- new CancellationTokenSource() + oldBgOpCts.Dispose() + ) Some (bgUserOpName, bgOpName, bgOpArg, bgOp ctok) //Trace.TraceInformation("Reactor: --> set background op, remaining {0}", inbox.CurrentQueueLength) @@ -113,9 +116,11 @@ type Reactor() = | None -> () | Some (bgUserOpName, bgOpName, bgOpArg, bgOp) -> Trace.TraceInformation("Reactor: {0:n3} --> wait for background {1}.{2} ({3}), remaining {4}", DateTime.Now.TimeOfDay.TotalSeconds, bgUserOpName, bgOpName, bgOpArg, inbox.CurrentQueueLength) - let oldBgOpCts = bgOpCts - bgOpCts <- new CancellationTokenSource() - oldBgOpCts.Dispose() + lock gate (fun () -> + let oldBgOpCts = bgOpCts + bgOpCts <- new CancellationTokenSource() + oldBgOpCts.Dispose() + ) try Eventually.force bgOpCts.Token bgOp |> ignore @@ -179,12 +184,12 @@ type Reactor() = // [Foreground Mailbox Accessors] ----------------------------------------------------------- member _.SetBackgroundOp(bgOpOpt) = Trace.TraceInformation("Reactor: {0:n3} enqueue start background, length {1}", DateTime.Now.TimeOfDay.TotalSeconds, builder.CurrentQueueLength) - bgOpCts.Cancel() + lock gate (fun () -> bgOpCts.Cancel()) builder.Post(SetBackgroundOp bgOpOpt) member _.CancelBackgroundOp() = Trace.TraceInformation("FCS: trying to cancel any active background work") - bgOpCts.Cancel() + lock gate (fun () -> bgOpCts.Cancel()) member _.EnqueueOp(userOpName, opName, opArg, op) = Trace.TraceInformation("Reactor: {0:n3} enqueue {1}.{2} ({3}), length {4}", DateTime.Now.TimeOfDay.TotalSeconds, userOpName, opName, opArg, builder.CurrentQueueLength) diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index 30aab3c66bd..4329db1fbac 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -67,14 +67,14 @@ module Helpers = && FSharpProjectOptions.UseSameProject(o1,o2) /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. parsing - let AreSameForParsing((fileName1: string, source1Hash: int, options1), (fileName2, source2Hash, options2)) = + let AreSameForParsing((fileName1: string, source1Hash: int64, options1), (fileName2, source2Hash, options2)) = fileName1 = fileName2 && options1 = options2 && source1Hash = source2Hash let AreSimilarForParsing((fileName1, _, _), (fileName2, _, _)) = fileName1 = fileName2 /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. checking - let AreSameForChecking3((fileName1: string, source1Hash: int, options1: FSharpProjectOptions), (fileName2, source2Hash, options2)) = + let AreSameForChecking3((fileName1: string, source1Hash: int64, options1: FSharpProjectOptions), (fileName2, source2Hash, options2)) = (fileName1 = fileName2) && FSharpProjectOptions.AreSameForChecking(options1,options2) && source1Hash = source2Hash @@ -193,7 +193,8 @@ module CompileHelpers = System.Console.SetError error | None -> () -type SourceTextHash = int +type SourceTextHash = int64 +type CacheStamp = int64 type FileName = string type FilePath = string type ProjectPath = string @@ -364,7 +365,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC let parseCacheLock = Lock() // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.parseFileInProjectCache. Most recently used cache for parsing files. - let parseFileCache = MruCache(parseFileCacheSize, areSimilar = AreSimilarForParsing, areSame = AreSameForParsing) + let parseFileCache = MruCache(parseFileCacheSize, areSimilar = AreSimilarForParsing, areSame = AreSameForParsing) // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.checkFileInProjectCachePossiblyStale // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.checkFileInProjectCache @@ -382,7 +383,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC // Also keyed on source. This can only be out of date if the antecedent is out of date let checkFileInProjectCache = - MruCache + MruCache (keepStrongly=checkFileInProjectCacheSize, areSame=AreSameForChecking3, areSimilar=AreSubsumable3) @@ -398,15 +399,15 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC static let mutable actualCheckFileCount = 0 - member _.RecordCheckFileInProjectResults(filename,options,parsingOptions,parseResults,fileVersion,priorTimeStamp,checkAnswer,sourceText) = + member _.RecordCheckFileInProjectResults(filename,options,parsingOptions,parseResults,fileVersion,priorTimeStamp,checkAnswer,hash,cacheStamp) = match checkAnswer with | None -> () | Some typedResults -> actualCheckFileCount <- actualCheckFileCount + 1 parseCacheLock.AcquireLock (fun ltok -> checkFileInProjectCachePossiblyStale.Set(ltok, (filename,options),(parseResults,typedResults,fileVersion)) - checkFileInProjectCache.Set(ltok, (filename, sourceText, options),(parseResults,typedResults,fileVersion,priorTimeStamp)) - parseFileCache.Set(ltok, (filename, sourceText, parsingOptions), parseResults)) + checkFileInProjectCache.Set(ltok, (filename, cacheStamp, options),(parseResults,typedResults,fileVersion,priorTimeStamp)) + parseFileCache.Set(ltok, (filename, hash, parsingOptions), parseResults)) member bc.ImplicitlyStartCheckProjectInBackground(options, userOpName) = if implicitlyStartBackgroundWork then @@ -415,7 +416,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC member _.ParseFile(filename: string, sourceText: ISourceText, options: FSharpParsingOptions, cache: bool, userOpName: string) = async { if cache then - let hash = sourceText.GetHashCode() + let hash = sourceText.GetHashCode() |> int64 match parseCacheLock.AcquireLock(fun ltok -> parseFileCache.TryGet(ltok, (filename, hash, options))) with | Some res -> return res | None -> @@ -443,20 +444,37 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC return FSharpParseFileResults(diagnostics = diagnostics, input = parseTree, parseHadErrors = false, dependencyFiles = builder.AllDependenciesDeprecated) } - member _.GetCachedCheckFileResult(builder: IncrementalBuilder, filename, sourceText: ISourceText, options) = + member _.GetCachedCheckFileResult(builder: IncrementalBuilder, filename, sourceText: ISourceText, options, cacheStamp: int64 option) = + let stamp = + match cacheStamp with + | None -> sourceText.GetHashCode() |> int64 + | Some cacheStamp -> cacheStamp + // 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 -> checkFileInProjectCache.TryGet(ltok, (filename, sourceText.GetHashCode(), options))) - - match cachedResults with - | Some (parseResults, checkResults,_,priorTimeStamp) - when - (match builder.GetCheckResultsBeforeFileInProjectEvenIfStale filename with - | None -> false - | Some(tcPrior) -> - tcPrior.TimeStamp = priorTimeStamp && - builder.AreCheckResultsBeforeFileInProjectReady(filename)) -> - Some (parseResults,checkResults) - | _ -> None + let cachedResults = parseCacheLock.AcquireLock (fun ltok -> checkFileInProjectCache.TryGet(ltok, (filename, stamp, options))) + + let result = + if cacheStamp.IsSome then + match cachedResults with + | Some (parseResults, checkResults, _, _) -> Some (parseResults, checkResults) + | _ -> None + else + match cachedResults with + | Some (parseResults, checkResults,_,priorTimeStamp) + when + (match builder.GetCheckResultsBeforeFileInProjectEvenIfStale filename with + | None -> false + | Some(tcPrior) -> + tcPrior.TimeStamp = priorTimeStamp && + builder.AreCheckResultsBeforeFileInProjectReady(filename)) -> + Some (parseResults,checkResults) + | _ -> + None + + if result.IsNone then + parseCacheLock.AcquireLock (fun ltok -> checkFileInProjectCache.RemoveAnySimilar(ltok, (filename, stamp, options))) + + result /// 1. Repeatedly try to get cached file check results or get file "lock". /// @@ -480,20 +498,23 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC builder: IncrementalBuilder, tcPrior: PartialCheckResults, tcInfo: TcInfo, - creationDiags: FSharpDiagnostic[]) = - + creationDiags: FSharpDiagnostic[], + cacheStamp: int64 option) = + async { let beingCheckedFileKey = fileName, options, fileVersion let stopwatch = Stopwatch.StartNew() let rec loop() = async { // results may appear while we were waiting for the lock, let's recheck if it's the case - let cachedResults = bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) + let cachedResults = bc.GetCachedCheckFileResult(builder, fileName, sourceText, options, cacheStamp) match cachedResults with | Some (_, checkResults) -> return FSharpCheckFileAnswer.Succeeded checkResults | None -> if beingCheckedFileTable.TryAdd(beingCheckedFileKey, ()) then + let hash: SourceTextHash = sourceText.GetHashCode() |> int64 + let cacheStamp = defaultArg cacheStamp hash try // Get additional script #load closure information if applicable. // For scripts, this will have been recorded by GetProjectOptionsFromScript. @@ -522,7 +543,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC suggestNamesForErrors) |> Cancellable.toAsync let parsingOptions = FSharpParsingOptions.FromTcConfig(tcConfig, Array.ofList builder.SourceFiles, options.UseScriptResolutionRules) reactor.SetPreferredUILang tcConfig.preferredUiLang - bc.RecordCheckFileInProjectResults(fileName, options, parsingOptions, parseResults, fileVersion, tcPrior.TimeStamp, Some checkAnswer, sourceText.GetHashCode()) + bc.RecordCheckFileInProjectResults(fileName, options, parsingOptions, parseResults, fileVersion, tcPrior.TimeStamp, Some checkAnswer, hash, cacheStamp) return FSharpCheckFileAnswer.Succeeded checkAnswer finally let dummy = ref () @@ -550,7 +571,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC match builderOpt with | Some builder -> - match bc.GetCachedCheckFileResult(builder, filename, sourceText, options) with + match bc.GetCachedCheckFileResult(builder, filename, sourceText, options, None) with | Some (_, checkResults) -> return Some (builder, creationDiags, Some (FSharpCheckFileAnswer.Succeeded checkResults)) | _ -> return Some (builder, creationDiags, None) | _ -> return None // the builder wasn't ready @@ -572,7 +593,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC match tcPrior with | Some(tcPrior, tcInfo) -> - let! checkResults = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, fileVersion, builder, tcPrior, tcInfo, creationDiags) + let! checkResults = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, fileVersion, builder, tcPrior, tcInfo, creationDiags, None) return Some checkResults | None -> return None // the incremental builder was not up to date finally @@ -580,7 +601,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC } /// Type-check the result obtained by parsing. Force the evaluation of the antecedent type checking context if needed. - member bc.CheckFileInProject(parseResults: FSharpParseFileResults, filename, fileVersion, sourceText: ISourceText, options, userOpName) = + member bc.CheckFileInProject(parseResults: FSharpParseFileResults, filename, fileVersion, sourceText: ISourceText, options, cacheStamp, userOpName) = let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync(userOpName, "CheckFileInProject", filename, action) async { @@ -592,7 +613,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC | None -> return FSharpCheckFileAnswer.Succeeded (FSharpCheckFileResults.MakeEmpty(filename, creationDiags, keepAssemblyContents)) | Some builder -> // 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 = bc.GetCachedCheckFileResult(builder, filename, sourceText, options) + let cachedResults = bc.GetCachedCheckFileResult(builder, filename, sourceText, options, cacheStamp) match cachedResults with | Some (_, checkResults) -> return FSharpCheckFileAnswer.Succeeded checkResults @@ -610,14 +631,14 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC let! tcInfo = tcPrior.GetTcInfo() |> Eventually.toCancellable return (tcPrior, tcInfo) } - let! checkAnswer = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, fileVersion, builder, tcPrior, tcInfo, creationDiags) + let! checkAnswer = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, fileVersion, builder, tcPrior, tcInfo, creationDiags, cacheStamp) return checkAnswer finally bc.ImplicitlyStartCheckProjectInBackground(options, userOpName) } /// Parses and checks the source file and returns untyped AST and check results. - member bc.ParseAndCheckFileInProject (filename:string, fileVersion, sourceText: ISourceText, options:FSharpProjectOptions, userOpName) = + member bc.ParseAndCheckFileInProject (filename:string, fileVersion, sourceText: ISourceText, options:FSharpProjectOptions, cacheStamp, userOpName) = let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync(userOpName, "ParseAndCheckFileInProject", filename, action) async { @@ -639,7 +660,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC return (parseResults, FSharpCheckFileAnswer.Aborted) | Some builder -> - let cachedResults = bc.GetCachedCheckFileResult(builder, filename, sourceText, options) + let cachedResults = bc.GetCachedCheckFileResult(builder, filename, sourceText, options, cacheStamp) match cachedResults with | Some (parseResults, checkResults) -> @@ -666,7 +687,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC reactor.SetPreferredUILang tcPrior.TcConfig.preferredUiLang let parseDiags, parseTree, anyErrors = ParseAndCheckFile.parseFile (sourceText, filename, parsingOptions, userOpName, suggestNamesForErrors) let parseResults = FSharpParseFileResults(parseDiags, parseTree, anyErrors, builder.AllDependenciesDeprecated) - let! checkResults = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, fileVersion, builder, tcPrior, tcInfo, creationDiags) + let! checkResults = bc.CheckOneFileImpl(parseResults, sourceText, filename, options, fileVersion, builder, tcPrior, tcInfo, creationDiags, cacheStamp) Logger.LogBlockMessageStop (filename + strGuid + "-Successful") LogCompilerFunctionId.Service_ParseAndCheckFileInProject @@ -766,11 +787,12 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC | Some sc -> return Some (sc.GetView ()) }) /// Try to get recent approximate type check results for a file. - member _.TryGetRecentCheckResultsForFile(filename: string, options:FSharpProjectOptions, sourceText: ISourceText option, _userOpName: string) = + member _.TryGetRecentCheckResultsForFile(filename: string, options:FSharpProjectOptions, sourceText: ISourceText option, cacheStamp: CacheStamp option, _userOpName: string) = parseCacheLock.AcquireLock (fun ltok -> match sourceText with | Some sourceText -> - match checkFileInProjectCache.TryGet(ltok,(filename,sourceText.GetHashCode(),options)) with + let cacheStamp = defaultArg cacheStamp (sourceText.GetHashCode() |> int64) + match checkFileInProjectCache.TryGet(ltok,(filename,cacheStamp,options)) with | Some (a,b,c,_) -> Some (a,b,c) | None -> checkFileInProjectCachePossiblyStale.TryGet(ltok,(filename,options)) | None -> checkFileInProjectCachePossiblyStale.TryGet(ltok,(filename,options))) @@ -1071,7 +1093,7 @@ type FSharpChecker(legacyReferenceResolver, member _.MatchBraces(filename, sourceText: ISourceText, options: FSharpParsingOptions, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" - let hash = sourceText.GetHashCode() + let hash = sourceText.GetHashCode() |> int64 async { match braceMatchCache.TryGet(AnyCallerThread, (filename, hash, options)) with | Some res -> return res @@ -1110,9 +1132,9 @@ type FSharpChecker(legacyReferenceResolver, backgroundCompiler.GetBackgroundCheckResultsForFileInProject(filename,options, userOpName) /// Try to get recent approximate type check results for a file. - member _.TryGetRecentCheckResultsForFile(filename: string, options:FSharpProjectOptions, ?sourceText, ?userOpName: string) = + member _.TryGetRecentCheckResultsForFile(filename: string, options:FSharpProjectOptions, ?sourceText, ?userOpName: string, ?cacheStamp: int64) = let userOpName = defaultArg userOpName "Unknown" - backgroundCompiler.TryGetRecentCheckResultsForFile(filename,options,sourceText, userOpName) + backgroundCompiler.TryGetRecentCheckResultsForFile(filename,options,sourceText,cacheStamp,userOpName) member _.Compile(argv: string[], ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" @@ -1252,17 +1274,17 @@ type FSharpChecker(legacyReferenceResolver, /// Typecheck a source code file, returning a handle to the results of the /// parse including the reconstructed types in the file. - member ic.CheckFileInProject(parseResults:FSharpParseFileResults, filename:string, fileVersion:int, sourceText:ISourceText, options:FSharpProjectOptions, ?userOpName: string) = + member ic.CheckFileInProject(parseResults:FSharpParseFileResults, filename:string, fileVersion:int, sourceText:ISourceText, options:FSharpProjectOptions, ?userOpName: string, ?cacheStamp: int64) = let userOpName = defaultArg userOpName "Unknown" ic.CheckMaxMemoryReached() - backgroundCompiler.CheckFileInProject(parseResults,filename,fileVersion,sourceText,options,userOpName) + backgroundCompiler.CheckFileInProject(parseResults,filename,fileVersion,sourceText,options,cacheStamp,userOpName) /// Typecheck a source code file, returning a handle to the results of the /// parse including the reconstructed types in the file. - member ic.ParseAndCheckFileInProject(filename:string, fileVersion:int, sourceText:ISourceText, options:FSharpProjectOptions, ?userOpName: string) = + member ic.ParseAndCheckFileInProject(filename:string, fileVersion:int, sourceText:ISourceText, options:FSharpProjectOptions, ?userOpName: string, ?cacheStamp: int64) = let userOpName = defaultArg userOpName "Unknown" ic.CheckMaxMemoryReached() - backgroundCompiler.ParseAndCheckFileInProject(filename, fileVersion, sourceText, options, userOpName) + backgroundCompiler.ParseAndCheckFileInProject(filename, fileVersion, sourceText, options, cacheStamp, userOpName) member ic.ParseAndCheckProject(options, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" diff --git a/src/fsharp/service/service.fsi b/src/fsharp/service/service.fsi index 0e5d5c9218e..fdb0a8d4766 100644 --- a/src/fsharp/service/service.fsi +++ b/src/fsharp/service/service.fsi @@ -121,7 +121,8 @@ type public FSharpChecker = /// The full source for the file. /// The options for the project or script. /// An optional string used for tracing compiler operations associated with this request. - member CheckFileInProject: parseResults: FSharpParseFileResults * filename: string * fileVersion: int * sourceText: ISourceText * options: FSharpProjectOptions * ?userOpName: string -> Async + /// Used to determine if we should use cached results. If None, it will use ISourceText.GetHashCode. + member CheckFileInProject: parseResults: FSharpParseFileResults * filename: string * fileVersion: int * sourceText: ISourceText * options: FSharpProjectOptions * ?userOpName: string * ?cacheStamp: int64 -> Async /// /// @@ -140,7 +141,8 @@ type public FSharpChecker = /// The source for the file. /// The options for the project or script. /// An optional string used for tracing compiler operations associated with this request. - member ParseAndCheckFileInProject: filename: string * fileVersion: int * sourceText: ISourceText * options: FSharpProjectOptions * ?userOpName: string -> Async + /// Used to determine if we should use cached results. If None, it will use ISourceText.GetHashCode. + member ParseAndCheckFileInProject: filename: string * fileVersion: int * sourceText: ISourceText * options: FSharpProjectOptions * ?userOpName: string * ?cacheStamp: int64 -> Async /// /// Parse and typecheck all files in a project. @@ -349,7 +351,8 @@ type public FSharpChecker = /// The options for the project or script, used to determine active --define conditionals and other options relevant to parsing. /// Optionally, specify source that must match the previous parse precisely. /// An optional string used for tracing compiler operations associated with this request. - member TryGetRecentCheckResultsForFile: filename: string * options:FSharpProjectOptions * ?sourceText: ISourceText * ?userOpName: string -> (FSharpParseFileResults * FSharpCheckFileResults * (*version*)int) option + /// Used to determine if we should use cached results. If None, it will use ISourceText.GetHashCode. + member TryGetRecentCheckResultsForFile: filename: string * options:FSharpProjectOptions * ?sourceText: ISourceText * ?userOpName: string * ?cacheStamp: int64 -> (FSharpParseFileResults * FSharpCheckFileResults * (*version*)int) option /// This function is called when the entire environment is known to have changed for reasons not encoded in the ProjectOptions of any project/compilation. member InvalidateAll: unit -> unit diff --git a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs index 062bd21d080..dd7f196b91b 100644 --- a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs +++ b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs @@ -1437,7 +1437,7 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualParseFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_CurrentQueueLength() FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_MaxMemory() FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_PauseBeforeBackgroundWork() -FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer] CheckFileInProject(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer] CheckFileInProject(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] GetBackgroundParseResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpParsingOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -1446,7 +1446,7 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetBackgroundSemanticClassificationForFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyProjectCleaned(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) -FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults]] GetBackgroundCheckResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Diagnostics.FSharpDiagnostic]]] GetProjectOptionsFromScript(System.String, FSharp.Compiler.Text.ISourceText, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.String[]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.Int64], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.Diagnostics.FSharpDiagnostic[],System.Int32]] Compile(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.ParsedInput], System.String, System.String, Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -1465,7 +1465,7 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.IEvent`2[Mi FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.IEvent`2[Microsoft.FSharp.Control.FSharpHandler`1[System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]],System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]] get_BeforeBackgroundFileCheck() FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.IEvent`2[Microsoft.FSharp.Control.FSharpHandler`1[System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]],System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]] get_FileChecked() FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.IEvent`2[Microsoft.FSharp.Control.FSharpHandler`1[System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]],System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]] get_FileParsed() -FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults,System.Int32]] TryGetRecentCheckResultsForFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.ISourceText], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults,System.Int32]] TryGetRecentCheckResultsForFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.ISourceText], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) FSharp.Compiler.CodeAnalysis.FSharpChecker: System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParsingOptions,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Diagnostics.FSharpDiagnostic]] GetParsingOptionsFromCommandLineArgs(Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) FSharp.Compiler.CodeAnalysis.FSharpChecker: System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParsingOptions,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Diagnostics.FSharpDiagnostic]] GetParsingOptionsFromCommandLineArgs(Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) FSharp.Compiler.CodeAnalysis.FSharpChecker: System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParsingOptions,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Diagnostics.FSharpDiagnostic]] GetParsingOptionsFromProjectOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) @@ -1481,8 +1481,10 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Void set_ImplicitlyStartBackgroundWo FSharp.Compiler.CodeAnalysis.FSharpChecker: Void set_MaxMemory(Int32) FSharp.Compiler.CodeAnalysis.FSharpChecker: Void set_PauseBeforeBackgroundWork(Int32) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults +FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsBindingALambdaAtPosition(FSharp.Compiler.Text.Position) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsPosContainedInApplication(FSharp.Compiler.Text.Position) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsPositionContainedInACurriedParameter(FSharp.Compiler.Text.Position) +FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsTypeAnnotationGivenAtPosition(FSharp.Compiler.Text.Position) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean ParseHadErrors FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean get_ParseHadErrors() FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: FSharp.Compiler.Diagnostics.FSharpDiagnostic[] Diagnostics @@ -1501,8 +1503,6 @@ FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Microsoft.FSharp.Core.FShar FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Text.Range]] GetAllArgumentsForFunctionApplicationAtPostion(FSharp.Compiler.Text.Position) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`2[FSharp.Compiler.Syntax.Ident,System.Int32]] TryIdentOfPipelineContainingPosAndNumArgsApplied(FSharp.Compiler.Text.Position) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[FSharp.Compiler.Text.Range,FSharp.Compiler.Text.Range,FSharp.Compiler.Text.Range]] TryRangeOfParenEnclosingOpEqualsGreaterUsage(FSharp.Compiler.Text.Position) -FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsBindingALambdaAtPosition(FSharp.Compiler.Text.Position) -FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsTypeAnnotationGivenAtPosition(FSharp.Compiler.Text.Position) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: System.String FileName FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: System.String get_FileName() FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: System.String[] DependencyFiles @@ -4153,6 +4153,7 @@ FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsEventAddMethod FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsEventRemoveMethod FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsExplicitInterfaceImplementation FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsExtensionMember +FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsFunction FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsImplicitConstructor FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsInstanceMember FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsInstanceMemberInCompiledCode @@ -4168,7 +4169,6 @@ FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsTypeFunction FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsUnresolved FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsValCompiledAsMethod FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsValue -FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsFunction FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_EventIsStandard() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_HasGetterMethod() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_HasSetterMethod() @@ -4183,6 +4183,7 @@ FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsEventAddMet FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsEventRemoveMethod() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsExplicitInterfaceImplementation() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsExtensionMember() +FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsFunction() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsImplicitConstructor() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsInstanceMember() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsInstanceMemberInCompiledCode() @@ -4198,7 +4199,6 @@ FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsTypeFunctio FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsUnresolved() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsValCompiledAsMethod() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsValue() -FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsFunction() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: FSharp.Compiler.Symbols.FSharpAccessibility Accessibility FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: FSharp.Compiler.Symbols.FSharpAccessibility get_Accessibility() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: FSharp.Compiler.Symbols.FSharpEntity ApparentEnclosingEntity diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index 0abfb224940..a3844ae749e 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -179,7 +179,7 @@ type internal FSharpClassificationService do! semanticClassificationCache.SetAsync(document, classificationDataLookup) |> liftAsync addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result else - let! _, _, checkResults = checkerProvider.Checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, allowStaleResults = false, userOpName=userOpName) + let! _, _, checkResults = checkerProvider.Checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = false, userOpName=userOpName) let targetRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText) let classificationData = checkResults.GetSemanticClassification (Some targetRange) addSemanticClassification sourceText textSpan classificationData result diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs index 7c1bc8e75f0..3890eba616f 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs @@ -90,7 +90,7 @@ type internal FSharpAddOpenCodeFixProvider let document = context.Document let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let! _, parsedInput, checkResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, userOpName = userOpName) + let! _, parsedInput, checkResults = checker.ParseAndCheckDocument(document, projectOptions, userOpName = userOpName) let line = sourceText.Lines.GetLineFromPosition(context.Span.End) let linePos = sourceText.Lines.GetLinePosition(context.Span.End) let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs index 7eb60b25c90..cbf1b645648 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs @@ -47,7 +47,7 @@ type internal FSharpAddTypeAnnotationToObjectOfIndeterminateTypeFixProvider let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! _, _, checkFileResults = checker.ParseAndCheckDocument (document, projectOptions, sourceText=sourceText, userOpName=userOpName) + let! _, _, checkFileResults = checker.ParseAndCheckDocument (document, projectOptions, userOpName=userOpName) let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) let decl = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, false) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs index af96ed0b07c..169588e1c84 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs @@ -23,10 +23,10 @@ type internal FSharpConvertCSharpLambdaToFSharpLambdaCodeFixProvider override _.RegisterCodeFixesAsync context = asyncMaybe { - let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(context.Document, context.CancellationToken, userOpName) - let! parseResults = checkerProvider.Checker.ParseFile(context.Document.FilePath, sourceText.ToFSharpSourceText(), parsingOptions, userOpName=userOpName) |> liftAsync + let! parseResults = checkerProvider.Checker.ParseDocument(context.Document, parsingOptions, userOpName) + let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let errorRange = RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText) let! fullParenRange, lambdaArgRange, lambdaBodyRange = parseResults.TryRangeOfParenEnclosingOpEqualsGreaterUsage errorRange.Start diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs index 2592e8d9aa2..3710101ed4e 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs @@ -29,9 +29,9 @@ type internal FSharpConvertToAnonymousRecordCodeFixProvider asyncMaybe { let document = context.Document let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) - let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let! parseResults = checkerProvider.Checker.ParseFile(document.FilePath, sourceText.ToFSharpSourceText(), parsingOptions, userOpName=userOpName) |> liftAsync + let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName=userOpName) + let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let errorRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) let! recordRange = parseResults.TryRangeOfRecordExpressionContainingPos errorRange.Start let! recordSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, recordRange) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs index f25a57441b8..c21a1123380 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs @@ -145,7 +145,7 @@ type internal FSharpImplementInterfaceCodeFixProvider let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(context.Document, context.CancellationToken, userOpName) let cancellationToken = context.CancellationToken let! sourceText = context.Document.GetTextAsync(cancellationToken) - let! _, parsedInput, checkFileResults = checker.ParseAndCheckDocument(context.Document, projectOptions, sourceText = sourceText, userOpName = userOpName) + let! _, parsedInput, checkFileResults = checker.ParseAndCheckDocument(context.Document, projectOptions, userOpName = userOpName) let textLine = sourceText.Lines.GetLineFromPosition context.Span.Start let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions // Notice that context.Span doesn't return reliable ranges to find tokens at exact positions. diff --git a/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs b/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs index a9cb5ba1e04..9aab9475320 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs @@ -46,7 +46,7 @@ type internal FSharpMakeDeclarationMutableFixProvider let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! parseFileResults, _, checkFileResults = checker.ParseAndCheckDocument (document, projectOptions, sourceText=sourceText, userOpName=userOpName) + let! parseFileResults, _, checkFileResults = checker.ParseAndCheckDocument (document, projectOptions, userOpName=userOpName) let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) let decl = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, false) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs b/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs index 34d4487b3fa..67b586688e5 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs @@ -24,10 +24,10 @@ type internal FSharpMakeOuterBindingRecursiveCodeFixProvider override _.RegisterCodeFixesAsync context = asyncMaybe { - let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(context.Document, context.CancellationToken, userOpName) - let! parseResults = checkerProvider.Checker.ParseFile(context.Document.FilePath, sourceText.ToFSharpSourceText(), parsingOptions, userOpName=userOpName) |> liftAsync + let! parseResults = checkerProvider.Checker.ParseDocument(context.Document, parsingOptions, userOpName) + let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let diagnosticRange = RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText) do! Option.guard (parseResults.IsPosContainedInApplication diagnosticRange.Start) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs index be68ed8240f..59e6ff102c1 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs @@ -23,10 +23,10 @@ type internal FSharpRemoveReturnOrYieldCodeFixProvider override _.RegisterCodeFixesAsync context = asyncMaybe { - let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(context.Document, context.CancellationToken, userOpName) - let! parseResults = checkerProvider.Checker.ParseFile(context.Document.FilePath, sourceText.ToFSharpSourceText(), parsingOptions, userOpName=userOpName) |> liftAsync + let! parseResults = checkerProvider.Checker.ParseDocument(context.Document, parsingOptions, userOpName=userOpName) + let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let errorRange = RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText) let! exprRange = parseResults.TryRangeOfExprInYieldOrReturn errorRange.Start let! exprSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, exprRange) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs index 292509db74f..f7538842ad1 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs @@ -42,7 +42,7 @@ type internal FSharpRenameUnusedValueCodeFixProvider // where backtickes are replaced with parens. if not (PrettyNaming.IsOperatorOrBacktickedName ident) && not (ident.StartsWith "``") then let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) - let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, userOpName=userOpName) + let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, userOpName=userOpName) let m = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, context.Span.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs b/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs index 6f1583d07ae..44a1fa5682d 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs @@ -56,7 +56,7 @@ type internal FSharpUseMutationWhenValueIsMutableFixProvider let textLine = sourceText.Lines.GetLineFromPosition adjustedPosition let textLinePos = sourceText.Lines.GetLinePosition adjustedPosition let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! _, _, checkFileResults = checker.ParseAndCheckDocument (document, projectOptions, sourceText=sourceText, userOpName=userOpName) + let! _, _, checkFileResults = checker.ParseAndCheckDocument (document, projectOptions, userOpName=userOpName) let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, adjustedPosition, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland) diff --git a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs index 51c52182df1..1f77bba3428 100644 --- a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs @@ -13,6 +13,7 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.EditorServices open FSharp.Compiler.Syntax open FSharp.Compiler.Text +open Microsoft.CodeAnalysis [] [, FSharpConstants.FSharpLanguageName)>] @@ -24,9 +25,10 @@ type internal FSharpHelpContextService ) = static let userOpName = "ImplementInterfaceCodeFix" - static member GetHelpTerm(checker: FSharpChecker, sourceText : SourceText, fileName, options, span: TextSpan, tokens: List, textVersion, perfOptions) : Async = + static member GetHelpTerm(checker: FSharpChecker, document: Document, options, span: TextSpan, tokens: List, perfOptions) : Async = asyncMaybe { - let! _, _, check = checker.ParseAndCheckDocument(fileName, textVersion, sourceText, options, perfOptions, userOpName = userOpName) + let! _, _, check = checker.ParseAndCheckDocument(document, options, perfOptions, userOpName) + let! sourceText = document.GetTextAsync() |> liftTaskAsync let textLines = sourceText.Lines let lineInfo = textLines.GetLineFromPosition(span.Start) let line = lineInfo.LineNumber @@ -101,12 +103,11 @@ type internal FSharpHelpContextService asyncMaybe { let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) let! sourceText = document.GetTextAsync(cancellationToken) - let! textVersion = document.GetTextVersionAsync(cancellationToken) let defines = projectInfoManager.GetCompilationDefinesForEditingDocument(document) let textLine = sourceText.Lines.GetLineFromPosition(textSpan.Start) let classifiedSpans = Tokenizer.getClassifiedSpans(document.Id, sourceText, textLine.Span, Some document.Name, defines, cancellationToken) let perfOptions = document.FSharpOptions.LanguageServicePerformance - return! FSharpHelpContextService.GetHelpTerm(checkerProvider.Checker, sourceText, document.FilePath, projectOptions, textSpan, classifiedSpans, textVersion.GetHashCode(), perfOptions) + return! FSharpHelpContextService.GetHelpTerm(checkerProvider.Checker, document, projectOptions, textSpan, classifiedSpans, perfOptions) } |> 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 5e7f33d5aa4..4dd22f0ffa1 100644 --- a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs @@ -70,8 +70,8 @@ type internal XmlDocCommandFilter let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, CancellationToken.None, userOpName) let! cancellationToken = Async.CancellationToken |> liftAsync let! sourceText = document.GetTextAsync(cancellationToken) - let! parsedInput = checker.ParseDocument(document, parsingOptions, sourceText, userOpName) - let xmlDocables = XmlDocParser.GetXmlDocables (sourceText.ToFSharpSourceText(), parsedInput) + let! parseResults = checker.ParseDocument(document, parsingOptions, userOpName) + let xmlDocables = XmlDocParser.GetXmlDocables (sourceText.ToFSharpSourceText(), parseResults.ParseTree) let xmlDocablesBelowThisLine = // +1 because looking below current line for e.g. a 'member' xmlDocables |> List.filter (fun (XmlDocable(line,_indent,_paramNames)) -> line = curLineNum+1) diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index 4ae416cc9c2..f99d696c62e 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -107,11 +107,12 @@ type internal FSharpCompletionProvider (triggerChar = '.' || (intelliSenseOptions.ShowAfterCharIsTyped && CompletionUtils.isStartingNewWord(sourceText, triggerPosition))) - static member ProvideCompletionsAsyncAux(checker: FSharpChecker, sourceText: SourceText, caretPosition: int, options: FSharpProjectOptions, filePath: string, - textVersionHash: int, getAllSymbols: FSharpCheckFileResults -> AssemblySymbol list, languageServicePerformanceOptions: LanguageServicePerformanceOptions, intellisenseOptions: IntelliSenseOptions) = + static member ProvideCompletionsAsyncAux(checker: FSharpChecker, document: Document, caretPosition: int, options: FSharpProjectOptions, + getAllSymbols: FSharpCheckFileResults -> AssemblySymbol list, languageServicePerformanceOptions: LanguageServicePerformanceOptions, intellisenseOptions: IntelliSenseOptions) = asyncMaybe { - let! parseResults, _, checkFileResults = checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText, options, languageServicePerformanceOptions, userOpName = userOpName) + let! parseResults, _, checkFileResults = checker.ParseAndCheckDocument(document, options, languageServicePerformanceOptions, userOpName = userOpName) + let! sourceText = document.GetTextAsync() |> liftTaskAsync let textLines = sourceText.Lines let caretLinePos = textLines.GetLinePosition(caretPosition) let caretLine = textLines.GetLineFromPosition(caretPosition) @@ -223,14 +224,13 @@ type internal FSharpCompletionProvider let defines = projectInfoManager.GetCompilationDefinesForEditingDocument(document) do! Option.guard (CompletionUtils.shouldProvideCompletion(document.Id, document.FilePath, defines, sourceText, context.Position)) let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) - let! textVersion = context.Document.GetTextVersionAsync(context.CancellationToken) let getAllSymbols(fileCheckResults: FSharpCheckFileResults) = if settings.IntelliSense.IncludeSymbolsFromUnopenedNamespacesOrModules then assemblyContentProvider.GetAllEntitiesInProjectAndReferencedAssemblies(fileCheckResults) else [] let! results = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, sourceText, context.Position, projectOptions, document.FilePath, - textVersion.GetHashCode(), getAllSymbols, settings.LanguageServicePerformance, settings.IntelliSense) + FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, context.Document, context.Position, projectOptions, + getAllSymbols, settings.LanguageServicePerformance, settings.IntelliSense) context.AddItems(results) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask context.CancellationToken @@ -292,14 +292,14 @@ type internal FSharpCompletionProvider let textWithItemCommitted = sourceText.WithChanges(TextChange(item.Span, nameInCode)) let line = sourceText.Lines.GetLineFromPosition(item.Span.Start) let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) - let! parsedInput = checker.ParseDocument(document, parsingOptions, sourceText, userOpName) + let! parseResults = checker.ParseDocument(document, parsingOptions, userOpName) let fullNameIdents = fullName |> Option.map (fun x -> x.Split '.') |> Option.defaultValue [||] let insertionPoint = if settings.CodeFixes.AlwaysPlaceOpensAtTopLevel then OpenStatementInsertionPoint.TopLevel else OpenStatementInsertionPoint.Nearest - let ctx = ParsedInput.FindNearestPointToInsertOpenDeclaration line.LineNumber parsedInput fullNameIdents insertionPoint + let ctx = ParsedInput.FindNearestPointToInsertOpenDeclaration line.LineNumber parseResults.ParseTree fullNameIdents insertionPoint let finalSourceText, changedSpanStartPos = OpenDeclarationHelper.insertOpenDeclaration textWithItemCommitted ctx ns let fullChangingSpan = TextSpan.FromBounds(changedSpanStartPos, item.Span.End) let changedSpan = TextSpan.FromBounds(changedSpanStartPos, item.Span.End + (finalSourceText.Length - sourceText.Length)) diff --git a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs index 51224209218..96ff3b2d7a2 100644 --- a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs +++ b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs @@ -502,21 +502,20 @@ type internal FSharpSignatureHelpProvider defines: string list, checker: FSharpChecker, documentationBuilder: IDocumentationBuilder, - sourceText: SourceText, caretPosition: int, options: FSharpProjectOptions, - filePath: string, - textVersionHash: int, triggerTypedChar: char option, possibleCurrentSignatureHelpSessionKind: CurrentSignatureHelpSessionKind option ) = asyncMaybe { + let! sourceText = document.GetTextAsync() |> liftTaskAsync + let textLines = sourceText.Lines let perfOptions = document.FSharpOptions.LanguageServicePerformance let caretLinePos = textLines.GetLinePosition(caretPosition) let caretLineColumn = caretLinePos.Character - let! parseResults, _, checkFileResults = checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText, options, perfOptions, userOpName = userOpName) + let! parseResults, _, checkFileResults = checker.ParseAndCheckDocument(document, options, perfOptions, userOpName = userOpName) let adjustedColumnInSource = let rec loop ch pos = @@ -543,7 +542,7 @@ type internal FSharpSignatureHelpProvider sourceText, caretPosition, adjustedColumnInSource, - filePath) + document.FilePath) | _, Some FunctionApplication when adjustedColumnChar <> ',' && adjustedColumnChar <> '(' && adjustedColumnChar <> '<' -> return! FSharpSignatureHelpProvider.ProvideParametersAsyncAux( @@ -555,7 +554,7 @@ type internal FSharpSignatureHelpProvider sourceText, caretPosition, adjustedColumnInSource, - filePath) + document.FilePath) | _ -> let! paramInfoLocations = parseResults.FindParameterLocations(Position.fromZ caretLinePos.Line caretLineColumn) return! @@ -578,8 +577,6 @@ type internal FSharpSignatureHelpProvider asyncMaybe { let! _, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) let defines = projectInfoManager.GetCompilationDefinesForEditingDocument(document) - let! sourceText = document.GetTextAsync(cancellationToken) - let! textVersion = document.GetTextVersionAsync(cancellationToken) let checker = checkerProvider.Checker let triggerTypedChar = @@ -595,11 +592,8 @@ type internal FSharpSignatureHelpProvider defines, checker, documentationBuilder, - sourceText, position, projectOptions, - document.FilePath, - textVersion.GetHashCode(), triggerTypedChar, possibleCurrentSignatureHelpSessionKind) match signatureHelpDataOpt with diff --git a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs index fc542a396e6..77f3081d879 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs @@ -26,8 +26,12 @@ type internal FSharpBreakpointResolutionService ) = static let userOpName = "BreakpointResolution" - static member GetBreakpointLocation(checker: FSharpChecker, sourceText: SourceText, fileName: string, textSpan: TextSpan, parsingOptions: FSharpParsingOptions) = + static member GetBreakpointLocation(checker: FSharpChecker, document: Document, textSpan: TextSpan, parsingOptions: FSharpParsingOptions) = async { + let! ct = Async.CancellationToken + + let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask + let textLinePos = sourceText.Lines.GetLinePosition(textSpan.Start) let textInLine = sourceText.GetSubText(sourceText.Lines.[textLinePos.Line].Span).ToString() @@ -36,16 +40,18 @@ 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.ParseFile(fileName, sourceText.ToFSharpSourceText(), parsingOptions, userOpName = userOpName) - return parseResults.ValidateBreakpointLocation(mkPos fcsTextLineNumber textLineColumn) + let! parseResults = checker.ParseDocument(document, parsingOptions, userOpName) + match parseResults with + | Some parseResults -> return parseResults.ValidateBreakpointLocation(mkPos fcsTextLineNumber textLineColumn) + | _ -> return None } interface IFSharpBreakpointResolutionService with member this.ResolveBreakpointAsync(document: Document, textSpan: TextSpan, cancellationToken: CancellationToken): Task = asyncMaybe { let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) + let! range = FSharpBreakpointResolutionService.GetBreakpointLocation(checkerProvider.Checker, document, textSpan, parsingOptions) let! sourceText = document.GetTextAsync(cancellationToken) - let! range = FSharpBreakpointResolutionService.GetBreakpointLocation(checkerProvider.Checker, sourceText, document.Name, textSpan, parsingOptions) let! span = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) return FSharpBreakpointResolutionResult.CreateSpanResult(document, span) } diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs index 2ff45d77c38..47d1443c23e 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs @@ -56,15 +56,23 @@ type internal FSharpDocumentDiagnosticAnalyzer hash } - static member GetDiagnostics(checker: FSharpChecker, filePath: string, sourceText: SourceText, textVersionHash: int, parsingOptions: FSharpParsingOptions, options: FSharpProjectOptions, diagnosticType: DiagnosticsType) = + static member GetDiagnostics(checker: FSharpChecker, document: Document, parsingOptions: FSharpParsingOptions, options: FSharpProjectOptions, diagnosticType: DiagnosticsType) = async { - let fsSourceText = sourceText.ToFSharpSourceText() - let! parseResults = checker.ParseFile(filePath, fsSourceText, parsingOptions, userOpName=userOpName) + let! ct = Async.CancellationToken + + let! parseResults = checker.ParseDocument(document, parsingOptions, userOpName) + match parseResults with + | None -> return ImmutableArray.Empty + | Some parseResults -> + + let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask + let filePath = document.FilePath + let! errors = async { match diagnosticType with | DiagnosticsType.Semantic -> - let! checkResultsAnswer = checker.CheckFileInProject(parseResults, filePath, textVersionHash, fsSourceText, options, userOpName=userOpName) + let! checkResultsAnswer = checker.CheckDocument(document, parseResults, options, userOpName) match checkResultsAnswer with | FSharpCheckFileAnswer.Aborted -> return [||] | FSharpCheckFileAnswer.Succeeded results -> @@ -109,10 +117,8 @@ type internal FSharpDocumentDiagnosticAnalyzer member this.AnalyzeSyntaxAsync(document: Document, cancellationToken: CancellationToken): Task> = asyncMaybe { let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) - let! sourceText = document.GetTextAsync(cancellationToken) - let! textVersion = document.GetTextVersionAsync(cancellationToken) return! - FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checkerProvider.Checker, document.FilePath, sourceText, textVersion.GetHashCode(), parsingOptions, projectOptions, DiagnosticsType.Syntax) + FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checkerProvider.Checker, document, parsingOptions, projectOptions, DiagnosticsType.Syntax) |> liftAsync } |> Async.map (Option.defaultValue ImmutableArray.Empty) @@ -121,11 +127,9 @@ type internal FSharpDocumentDiagnosticAnalyzer member this.AnalyzeSemanticsAsync(document: Document, cancellationToken: CancellationToken): Task> = asyncMaybe { let! parsingOptions, _, projectOptions = projectInfoManager.TryGetOptionsForDocumentOrProject(document, cancellationToken, userOpName) - let! sourceText = document.GetTextAsync(cancellationToken) - let! textVersion = document.GetTextVersionAsync(cancellationToken) if document.Project.Name <> FSharpConstants.FSharpMiscellaneousFilesName || isScriptFile document.FilePath then return! - FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checkerProvider.Checker, document.FilePath, sourceText, textVersion.GetHashCode(), parsingOptions, projectOptions, DiagnosticsType.Semantic) + FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checkerProvider.Checker, document, parsingOptions, projectOptions, DiagnosticsType.Semantic) |> liftAsync else return ImmutableArray.Empty diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs index 4680035ecb4..51963b326c8 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs @@ -48,7 +48,7 @@ type internal SimplifyNameDiagnosticAnalyzer | _ -> let! sourceText = document.GetTextAsync() let checker = checkerProvider.Checker - let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, userOpName=userOpName) + let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, userOpName=userOpName) let! result = SimplifyNames.getSimplifiableNames(checkResults, fun lineNumber -> sourceText.Lines.[Line.toZ lineNumber].ToString()) |> liftAsync let mutable diag = ResizeArray() for r in result do diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs index 928c7e3cab5..4fe92e87665 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs @@ -32,7 +32,7 @@ type internal UnusedDeclarationsAnalyzer | (_parsingOptions, projectOptions) -> let! sourceText = document.GetTextAsync() let checker = checkerProvider.Checker - let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, userOpName = userOpName) + let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, userOpName = userOpName) let! unusedRanges = UnusedDeclarations.getUnusedDeclarations( checkResults, (isScriptFile document.FilePath)) |> liftAsync return unusedRanges diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs index a8be1c950a1..b197558ce86 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs @@ -30,7 +30,7 @@ type internal UnusedOpensDiagnosticAnalyzer asyncMaybe { do! Option.guard document.FSharpOptions.CodeFixes.UnusedOpens let! sourceText = document.GetTextAsync() - let! _, _, checkResults = checker.ParseAndCheckDocument(document, options, sourceText = sourceText, userOpName = userOpName) + let! _, _, checkResults = checker.ParseAndCheckDocument(document, options, userOpName = userOpName) let! unusedOpens = UnusedOpens.getUnusedOpens(checkResults, fun lineNumber -> sourceText.Lines.[Line.toZ lineNumber].ToString()) |> liftAsync return unusedOpens } diff --git a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs index 46262e048ed..c2309227658 100644 --- a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs +++ b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs @@ -51,14 +51,16 @@ type internal FSharpDocumentHighlightsService [] (checkerP |> Seq.distinctBy (fun span -> span.TextSpan.Start) |> Seq.toArray - static member GetDocumentHighlights(checker: FSharpChecker, documentKey: DocumentId, sourceText: SourceText, filePath: string, position: int, - defines: string list, options: FSharpProjectOptions, textVersionHash: int, languageServicePerformanceOptions: LanguageServicePerformanceOptions) : Async = + static member GetDocumentHighlights(checker: FSharpChecker, document: Document, position: int, + defines: string list, options: FSharpProjectOptions, languageServicePerformanceOptions: LanguageServicePerformanceOptions) : Async = asyncMaybe { + let! sourceText = document.GetTextAsync() |> liftTaskAsync + let filePath = document.FilePath let textLine = sourceText.Lines.GetLineFromPosition(position) let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! symbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy, false, false) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText, options, languageServicePerformanceOptions, userOpName = userOpName) + let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, filePath, defines, SymbolLookupKind.Greedy, false, false) + let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, languageServicePerformanceOptions, userOpName = userOpName) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland) let symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) return @@ -75,12 +77,9 @@ type internal FSharpDocumentHighlightsService [] (checkerP member _.GetDocumentHighlightsAsync(document, position, _documentsToSearch, cancellationToken) : Task> = asyncMaybe { let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) - let! sourceText = document.GetTextAsync(cancellationToken) - let! textVersion = document.GetTextVersionAsync(cancellationToken) let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions let perfOptions = document.FSharpOptions.LanguageServicePerformance - let! spans = FSharpDocumentHighlightsService.GetDocumentHighlights(checkerProvider.Checker, document.Id, sourceText, document.FilePath, - position, defines, projectOptions, textVersion.GetHashCode(), perfOptions) + let! spans = FSharpDocumentHighlightsService.GetDocumentHighlights(checkerProvider.Checker, document, position, defines, projectOptions, perfOptions) let highlightSpans = spans |> Array.map (fun span -> diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs index e70577b9363..c164214c693 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs @@ -9,17 +9,46 @@ open Microsoft.CodeAnalysis.Text open FSharp.Compiler.CodeAnalysis type FSharpChecker with - member checker.ParseDocument(document: Document, parsingOptions: FSharpParsingOptions, sourceText: SourceText, userOpName: string) = + member checker.ParseDocument(document: Document, parsingOptions: FSharpParsingOptions, userOpName: string) = asyncMaybe { + let! ct = Async.CancellationToken |> liftAsync + + let! sourceText = document.GetTextAsync(ct) |> liftTaskAsync + let! fileParseResults = checker.ParseFile(document.FilePath, sourceText.ToFSharpSourceText(), parsingOptions, userOpName=userOpName) |> liftAsync - return fileParseResults.ParseTree + return fileParseResults + } + + member checker.CheckDocument(document: Document, parseResults: FSharpParseFileResults, options: FSharpProjectOptions, userOpName: string) = + async { + let! ct = Async.CancellationToken + + let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask + let! textVersion = document.GetTextVersionAsync(ct) |> Async.AwaitTask + let! cacheStamp = document.Project.GetDependentVersionAsync(ct) |> Async.AwaitTask + + let filePath = document.FilePath + let textVersionHash = textVersion.GetHashCode() + let cacheStamp = int64(cacheStamp.GetHashCode()) + + return! checker.CheckFileInProject(parseResults, filePath, textVersionHash, sourceText.ToFSharpSourceText(), options,cacheStamp=cacheStamp,userOpName=userOpName) } - member checker.ParseAndCheckDocument(filePath: string, textVersionHash: int, sourceText: SourceText, options: FSharpProjectOptions, languageServicePerformanceOptions: LanguageServicePerformanceOptions, userOpName: string) = + member checker.ParseAndCheckDocument(document: Document, options: FSharpProjectOptions, languageServicePerformanceOptions: LanguageServicePerformanceOptions, userOpName: string) = async { + let! ct = Async.CancellationToken + + let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask + let! textVersion = document.GetTextVersionAsync(ct) |> Async.AwaitTask + let! cacheStamp = document.Project.GetDependentVersionAsync(ct) |> Async.AwaitTask + + let filePath = document.FilePath + let textVersionHash = textVersion.GetHashCode() + let cacheStamp = int64(cacheStamp.GetHashCode()) + let parseAndCheckFile = async { - let! parseResults, checkFileAnswer = checker.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText.ToFSharpSourceText(), options, userOpName=userOpName) + let! parseResults, checkFileAnswer = checker.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText.ToFSharpSourceText(), options, cacheStamp=cacheStamp, userOpName=userOpName) return match checkFileAnswer with | FSharpCheckFileAnswer.Aborted -> @@ -51,7 +80,7 @@ type FSharpChecker with | Some x -> async.Return (Some x) | None -> async { - match checker.TryGetRecentCheckResultsForFile(filePath, options) with + match checker.TryGetRecentCheckResultsForFile(filePath, options, cacheStamp=cacheStamp, userOpName=userOpName) with | Some (parseResults, checkFileResults, _) -> return Some (parseResults, checkFileResults) | None -> @@ -63,26 +92,11 @@ type FSharpChecker with return bindParsedInput results } - - member checker.ParseAndCheckDocument(document: Document, options: FSharpProjectOptions, userOpName: string, ?allowStaleResults: bool, ?sourceText: SourceText) = + member checker.ParseAndCheckDocument(document: Document, options: FSharpProjectOptions, userOpName: string, ?allowStaleResults: bool) = async { - let! cancellationToken = Async.CancellationToken - let! sourceText = - match sourceText with - | Some x -> async.Return x - | None -> document.GetTextAsync(cancellationToken) |> Async.AwaitTask - let! textVersion = document.GetTextVersionAsync(cancellationToken) |> Async.AwaitTask let perfOpts = match allowStaleResults with | Some b -> { document.FSharpOptions.LanguageServicePerformance with AllowStaleCompletionResults = b } | _ -> document.FSharpOptions.LanguageServicePerformance - return! checker.ParseAndCheckDocument(document.FilePath, textVersion.GetHashCode(), sourceText, options, perfOpts, userOpName=userOpName) + return! checker.ParseAndCheckDocument(document, options, perfOpts, userOpName=userOpName) } - - - member checker.TryParseAndCheckFileInProject (projectOptions, fileName, sourceText: SourceText, userOpName) = async { - let! (parseResults, checkAnswer) = checker.ParseAndCheckFileInProject (fileName,0,sourceText.ToFSharpSourceText(),projectOptions, userOpName=userOpName) - match checkAnswer with - | FSharpCheckFileAnswer.Aborted -> return None - | FSharpCheckFileAnswer.Succeeded checkResults -> return Some (parseResults,checkResults) - } diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs index fa2e185cbcc..317448f5cb8 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs @@ -68,20 +68,22 @@ type internal FSharpCheckerProvider // This is one half of the bridge between the F# background builder and the Roslyn analysis engine. // When the F# background builder refreshes the background semantic build context for a file, // we request Roslyn to reanalyze that individual file. - checker.BeforeBackgroundFileCheck.Add(fun (fileName, _extraProjectInfo) -> - async { - try - let solution = workspace.CurrentSolution - let documentIds = solution.GetDocumentIdsWithFilePath(fileName) - if not documentIds.IsEmpty then - let documentIdsFiltered = documentIds |> Seq.filter workspace.IsDocumentOpen |> Seq.toArray - for documentId in documentIdsFiltered do - Trace.TraceInformation("{0:n3} Requesting Roslyn reanalysis of {1}", DateTime.Now.TimeOfDay.TotalSeconds, documentId) - if documentIdsFiltered.Length > 0 then - analyzerService.Reanalyze(workspace,documentIds=documentIdsFiltered) - with ex -> - Assert.Exception(ex) - } |> Async.StartImmediate + checker.BeforeBackgroundFileCheck.Add(fun (fileName, _extraProjectInfo) -> + // Only do this for scripts as misc script Rolsyn projects do not understand the dependencies of the script. e.x "#r "../test.dll"", "#load "test2.fsx"" + if isScriptFile fileName then + async { + try + let solution = workspace.CurrentSolution + let documentIds = solution.GetDocumentIdsWithFilePath(fileName) + if not documentIds.IsEmpty then + let documentIdsFiltered = documentIds |> Seq.filter workspace.IsDocumentOpen |> Seq.toArray + for documentId in documentIdsFiltered do + Trace.TraceInformation("{0:n3} Requesting Roslyn reanalysis of {1}", DateTime.Now.TimeOfDay.TotalSeconds, documentId) + if documentIdsFiltered.Length > 0 then + analyzerService.Reanalyze(workspace,documentIds=documentIdsFiltered) + with ex -> + Assert.Exception(ex) + } |> Async.StartImmediate ) checker diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index 64e11d78e12..08e62c70a2b 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -55,22 +55,23 @@ module private FSharpProjectOptionsHelpers = let hasProjectVersionChanged (oldProject: Project) (newProject: Project) = oldProject.Version <> newProject.Version - let hasDependentVersionChanged (oldProject: Project) (newProject: Project) = + let hasDependentVersionChanged (oldProject: Project) (newProject: Project) (ct: CancellationToken) = let oldProjectRefs = oldProject.ProjectReferences let newProjectRefs = newProject.ProjectReferences oldProjectRefs.Count() <> newProjectRefs.Count() || (oldProjectRefs, newProjectRefs) ||> Seq.exists2 (fun p1 p2 -> + ct.ThrowIfCancellationRequested() let doesProjectIdDiffer = p1.ProjectId <> p2.ProjectId let p1 = oldProject.Solution.GetProject(p1.ProjectId) let p2 = newProject.Solution.GetProject(p2.ProjectId) doesProjectIdDiffer || p1.Version <> p2.Version ) - let isProjectInvalidated (oldProject: Project) (newProject: Project) (settings: EditorOptions) = + let isProjectInvalidated (oldProject: Project) (newProject: Project) (settings: EditorOptions) ct = let hasProjectVersionChanged = hasProjectVersionChanged oldProject newProject if settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences then - hasProjectVersionChanged || hasDependentVersionChanged oldProject newProject + hasProjectVersionChanged || hasDependentVersionChanged oldProject newProject ct else hasProjectVersionChanged @@ -151,7 +152,7 @@ type private FSharpProjectOptionsReactor (workspace: Workspace, settings: Editor | true, site -> Some site | _ -> None - let rec tryComputeOptions (project: Project) = + let rec tryComputeOptions (project: Project) ct = async { let projectId = project.Id match cache.TryGetValue(projectId) with @@ -167,7 +168,7 @@ type private FSharpProjectOptionsReactor (workspace: Workspace, settings: Editor for projectReference in project.ProjectReferences do let referencedProject = project.Solution.GetProject(projectReference.ProjectId) if referencedProject.Language = FSharpConstants.FSharpLanguageName then - match! tryComputeOptions referencedProject with + match! tryComputeOptions referencedProject ct with | None -> canBail <- true | Some(_, projectOptions) -> referencedProjects.Add(referencedProject.OutputFilePath, projectOptions) @@ -239,9 +240,9 @@ type private FSharpProjectOptionsReactor (workspace: Workspace, settings: Editor return Some(parsingOptions, projectOptions) | true, (oldProject, parsingOptions, projectOptions) -> - if isProjectInvalidated oldProject project settings then + if isProjectInvalidated oldProject project settings ct then cache.TryRemove(projectId) |> ignore - return! tryComputeOptions project + return! tryComputeOptions project ct else return Some(parsingOptions, projectOptions) } @@ -266,7 +267,7 @@ type private FSharpProjectOptionsReactor (workspace: Workspace, settings: Editor // We do this to prevent any possible cache thrashing in FCS. let project = document.Project.Solution.Workspace.CurrentSolution.GetProject(document.Project.Id) if not (isNull project) then - let! options = tryComputeOptions project + let! options = tryComputeOptions project ct reply.Reply options else reply.Reply None @@ -286,7 +287,7 @@ type private FSharpProjectOptionsReactor (workspace: Workspace, settings: Editor // We do this to prevent any possible cache thrashing in FCS. let project = project.Solution.Workspace.CurrentSolution.GetProject(project.Id) if not (isNull project) then - let! options = tryComputeOptions project + let! options = tryComputeOptions project ct reply.Reply options else reply.Reply None diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index 9d49d42fb8f..3ed760c5bae 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -22,8 +22,6 @@ module internal SymbolHelpers = asyncMaybe { let! cancellationToken = Async.CancellationToken |> liftAsync let! sourceText = document.GetTextAsync(cancellationToken) - let! textVersion = document.GetTextVersionAsync(cancellationToken) - let textVersionHash = textVersion.GetHashCode() let textLine = sourceText.Lines.GetLineFromPosition(position) let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line @@ -31,7 +29,7 @@ module internal SymbolHelpers = let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) let settings = document.FSharpOptions - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document.FilePath, textVersionHash, sourceText, projectOptions, settings.LanguageServicePerformance, userOpName = userOpName) + let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, settings.LanguageServicePerformance, userOpName = userOpName) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland) let! ct = Async.CancellationToken |> liftAsync let symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol, cancellationToken=ct) diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index 9aa6f22a67e..53a0e68bd25 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs @@ -53,7 +53,7 @@ type internal FSharpFindUsagesService let! sourceText = document.GetTextAsync(context.CancellationToken) |> Async.AwaitTask |> liftAsync let checker = checkerProvider.Checker let! parsingOptions, _, projectOptions = projectInfoManager.TryGetOptionsForDocumentOrProject(document, context.CancellationToken, userOpName) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, userOpName = userOpName) + let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, userOpName = userOpName) let textLine = sourceText.Lines.GetLineFromPosition(position).ToString() let lineNumber = sourceText.Lines.GetLinePosition(position).Line + 1 let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index 5e7a92b33d0..df85f013fc8 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -191,7 +191,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, sourceText=sourceText, userOpName=userOpName) + let! _, _, checkFileResults = checker.ParseAndCheckDocument (originDocument, projectOptions, userOpName=userOpName) let idRange = lexerSymbol.Ident.idRange let! fsSymbolUse = checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) let symbol = fsSymbolUse.Symbol @@ -204,7 +204,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP let! implDoc = originDocument.Project.Solution.TryGetDocumentFromPath fsfilePath let! implSourceText = implDoc.GetTextAsync () let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(implDoc, CancellationToken.None, userOpName) - let! _, _, checkFileResults = checker.ParseAndCheckDocument (implDoc, projectOptions, sourceText=implSourceText, userOpName=userOpName) + let! _, _, checkFileResults = checker.ParseAndCheckDocument (implDoc, projectOptions, userOpName=userOpName) let symbolUses = checkFileResults.GetUsesOfSymbolInFile symbol let! implSymbol = symbolUses |> Array.tryHead let! implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (implSourceText, implSymbol.Range) @@ -217,18 +217,16 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP /// if the symbol is defined in the given file, return its declaration location, otherwise use the targetSymbol to find the first /// instance of its presence in the provided source file. The first case is needed to return proper declaration location for /// recursive type definitions, where the first its usage may not be the declaration. - member _.FindSymbolDeclarationInFile(targetSymbolUse: FSharpSymbolUse, filePath: string, sourceText: SourceText, options: FSharpProjectOptions, fileVersion:int) = + member _.FindSymbolDeclarationInDocument(targetSymbolUse: FSharpSymbolUse, document: Document, options: FSharpProjectOptions) = asyncMaybe { + let filePath = document.FilePath match targetSymbolUse.Symbol.DeclarationLocation with | Some decl when decl.FileName = filePath -> return decl | _ -> - let! _, checkFileAnswer = checker.ParseAndCheckFileInProject (filePath, fileVersion, sourceText.ToFSharpSourceText(), options, userOpName = userOpName) |> liftAsync - match checkFileAnswer with - | FSharpCheckFileAnswer.Aborted -> return! None - | FSharpCheckFileAnswer.Succeeded checkFileResults -> - let symbolUses = checkFileResults.GetUsesOfSymbolInFile targetSymbolUse.Symbol - let! implSymbol = symbolUses |> Array.tryHead - return implSymbol.Range + let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, userOpName) + let symbolUses = checkFileResults.GetUsesOfSymbolInFile targetSymbolUse.Symbol + let! implSymbol = symbolUses |> Array.tryHead + return implSymbol.Range } member private this.FindDefinitionAtPosition(originDocument: Document, position: int) = @@ -244,7 +242,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP let preferSignature = isSignatureFile originDocument.FilePath - let! _, _, checkFileResults = checker.ParseAndCheckDocument (originDocument, projectOptions, sourceText=sourceText, userOpName=userOpName) + let! _, _, checkFileResults = checker.ParseAndCheckDocument (originDocument, projectOptions, userOpName=userOpName) let! lexerSymbol = Tokenizer.getSymbolAtPosition (originDocument.Id, sourceText, position,originDocument.FilePath, defines, SymbolLookupKind.Greedy, false, false) let idRange = lexerSymbol.Ident.idRange @@ -286,11 +284,9 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP let implFilePath = Path.ChangeExtension (originDocument.FilePath,"fs") if not (File.Exists implFilePath) then return! None else let! implDocument = originDocument.Project.Solution.TryGetDocumentFromPath implFilePath - let! implSourceText = implDocument.GetTextAsync () |> liftTaskAsync - let! implVersion = implDocument.GetTextVersionAsync () |> liftTaskAsync - let! targetRange = this.FindSymbolDeclarationInFile(targetSymbolUse, implFilePath, implSourceText, projectOptions, implVersion.GetHashCode()) - + let! targetRange = this.FindSymbolDeclarationInDocument(targetSymbolUse, implDocument, projectOptions) + let! implSourceText = implDocument.GetTextAsync() |> liftTaskAsync let! implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (implSourceText, targetRange) let navItem = FSharpGoToDefinitionNavigableItem (implDocument, implTextSpan) return (FSharpGoToDefinitionResult.NavigableItem(navItem), idRange) @@ -324,12 +320,12 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP else sigDocument.FilePath let! implDocument = originDocument.Project.Solution.TryGetDocumentFromPath implFilePath - let! implVersion = implDocument.GetTextVersionAsync () |> liftTaskAsync - let! implSourceText = implDocument.GetTextAsync () |> liftTaskAsync + let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(implDocument, CancellationToken.None, userOpName) - let! targetRange = this.FindSymbolDeclarationInFile(targetSymbolUse, implFilePath, implSourceText, projectOptions, implVersion.GetHashCode()) + let! targetRange = this.FindSymbolDeclarationInDocument(targetSymbolUse, implDocument, projectOptions) + let! implSourceText = implDocument.GetTextAsync () |> liftTaskAsync let! implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (implSourceText, targetRange) let navItem = FSharpGoToDefinitionNavigableItem (implDocument, implTextSpan) return (FSharpGoToDefinitionResult.NavigableItem(navItem), idRange) diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs index 0260ebd0c59..ee864e3c623 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs @@ -177,9 +177,12 @@ type internal FSharpNavigateToSearchService let GetNavigableItems(document: Document, parsingOptions: FSharpParsingOptions, kinds: IImmutableSet) = async { let! cancellationToken = Async.CancellationToken - let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask - let! parseResults = checkerProvider.Checker.ParseFile(document.FilePath, sourceText.ToFSharpSourceText(), parsingOptions) + let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName) + match parseResults with + | None -> return [||] + | Some parseResults -> + let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask let navItems parsedInput = NavigateTo.GetNavigableItems parsedInput |> Array.filter (fun i -> kinds.Contains(navigateToItemKindToRoslynKind i.Kind)) diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs index b89e7fbafbb..c83fb363a2b 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs @@ -29,8 +29,8 @@ type internal FSharpNavigationBarItemService asyncMaybe { let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) let! sourceText = document.GetTextAsync(cancellationToken) - let! parsedInput = checkerProvider.Checker.ParseDocument(document, parsingOptions, sourceText=sourceText, userOpName=userOpName) - let navItems = Navigation.getNavigation parsedInput + let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName=userOpName) + let navItems = Navigation.getNavigation parseResults.ParseTree let rangeToTextSpan range = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) return navItems.Declarations diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index 4e16b2ee8da..dfe1b15ac3c 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -60,7 +60,7 @@ module internal FSharpQuickInfo = let! extParsingOptions, extProjectOptions = projectInfoManager.TryGetOptionsByProject(extDocument.Project, cancellationToken) let extDefines = CompilerEnvironment.GetCompilationDefinesForEditing extParsingOptions let! extLexerSymbol = Tokenizer.getSymbolAtPosition(extDocId, extSourceText, extSpan.Start, declRange.FileName, extDefines, SymbolLookupKind.Greedy, true, true) - let! _, _, extCheckFileResults = checker.ParseAndCheckDocument(extDocument, extProjectOptions, allowStaleResults=true, sourceText=extSourceText, userOpName = userOpName) + let! _, _, extCheckFileResults = checker.ParseAndCheckDocument(extDocument, extProjectOptions, allowStaleResults=true, userOpName = userOpName) let extQuickInfoText = extCheckFileResults.GetToolTip @@ -97,7 +97,7 @@ module internal FSharpQuickInfo = let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, true, true) let idRange = lexerSymbol.Ident.idRange - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = true, sourceText=sourceText, userOpName = userOpName) + let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = true, userOpName = userOpName) let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() @@ -177,14 +177,16 @@ type internal FSharpAsyncQuickInfoSource ) = // test helper - static member ProvideQuickInfo(checker:FSharpChecker, documentId:DocumentId, sourceText:SourceText, filePath:string, position:int, parsingOptions:FSharpParsingOptions, options:FSharpProjectOptions, textVersionHash:int, languageServicePerformanceOptions: LanguageServicePerformanceOptions) = + static member ProvideQuickInfo(checker:FSharpChecker, document: Document, position:int, parsingOptions:FSharpParsingOptions, options:FSharpProjectOptions, languageServicePerformanceOptions: LanguageServicePerformanceOptions) = asyncMaybe { - let! _, _, checkFileResults = checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText, options, languageServicePerformanceOptions, userOpName=FSharpQuickInfo.userOpName) + let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, languageServicePerformanceOptions, userOpName=FSharpQuickInfo.userOpName) + let! sourceText = document.GetTextAsync() + let filePath = document.FilePath let textLine = sourceText.Lines.GetLineFromPosition position let textLineNumber = textLine.LineNumber + 1 // Roslyn line numbers are zero-based let textLineString = textLine.ToString() let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions - let! symbol = Tokenizer.getSymbolAtPosition (documentId, sourceText, position, filePath, defines, SymbolLookupKind.Precise, true, true) + let! symbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, position, filePath, defines, SymbolLookupKind.Precise, true, true) let res = checkFileResults.GetToolTip (textLineNumber, symbol.Ident.idRange.EndColumn, textLineString, symbol.FullIsland, FSharpTokenTag.IDENT) match res with | ToolTipText [] diff --git a/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs b/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs index 569cc1b57dd..f2c73274dd4 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs @@ -38,7 +38,7 @@ type internal FSharpAddExplicitTypeToParameterRefactoring let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! parseFileResults, _, checkFileResults = checker.ParseAndCheckDocument (document, projectOptions, sourceText=sourceText, userOpName=userOpName) + let! parseFileResults, _, checkFileResults = checker.ParseAndCheckDocument (document, projectOptions, userOpName=userOpName) let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland) diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index e9437fcb1af..2a4a7a58da6 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -151,8 +151,8 @@ type internal FSharpBlockStructureService [] (checkerProvi asyncMaybe { let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) let! sourceText = document.GetTextAsync(cancellationToken) - let! parsedInput = checkerProvider.Checker.ParseDocument(document, parsingOptions, sourceText, userOpName) - return createBlockSpans document.FSharpOptions.Advanced.IsBlockStructureEnabled sourceText parsedInput |> Seq.toImmutableArray + let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName) + return createBlockSpans document.FSharpOptions.Advanced.IsBlockStructureEnabled sourceText parseResults.ParseTree |> Seq.toImmutableArray } |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) |> Async.map FSharpBlockStructure diff --git a/vsintegration/tests/UnitTests/BreakpointResolutionService.fs b/vsintegration/tests/UnitTests/BreakpointResolutionService.fs index f2289f7aa2b..e9437195d8e 100644 --- a/vsintegration/tests/UnitTests/BreakpointResolutionService.fs +++ b/vsintegration/tests/UnitTests/BreakpointResolutionService.fs @@ -72,10 +72,12 @@ let main argv = let searchPosition = code.IndexOf(searchToken) Assert.IsTrue(searchPosition >= 0, "SearchToken '{0}' is not found in code", searchToken) - let sourceText = SourceText.From(code) + let document, sourceText = RoslynTestHelpers.CreateDocument(fileName, code) let searchSpan = TextSpan.FromBounds(searchPosition, searchPosition + searchToken.Length) + + // let document = Microsoft.CodeAnalysis.DocumentInfo.Create(Microsoft.CodeAnalysis.DocumentId() let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions - let actualResolutionOption = FSharpBreakpointResolutionService.GetBreakpointLocation(checker, sourceText, fileName, searchSpan, parsingOptions) |> Async.RunSynchronously + let actualResolutionOption = FSharpBreakpointResolutionService.GetBreakpointLocation(checker, document, 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 b10dde9af37..8e2c70cfb47 100644 --- a/vsintegration/tests/UnitTests/CompletionProviderTests.fs +++ b/vsintegration/tests/UnitTests/CompletionProviderTests.fs @@ -54,8 +54,9 @@ let formatCompletions(completions : string seq) = let VerifyCompletionListWithOptions(fileContents: string, marker: string, expected: string list, unexpected: string list, opts) = let caretPosition = fileContents.IndexOf(marker) + marker.Length + let document, _ = RoslynTestHelpers.CreateDocument(filePath, fileContents) let results = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, SourceText.From(fileContents), caretPosition, projectOptions opts, filePath, 0, (fun _ -> []), LanguageServicePerformanceOptions.Default, IntelliSenseOptions.Default) + FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, document, caretPosition, projectOptions opts, (fun _ -> []), LanguageServicePerformanceOptions.Default, IntelliSenseOptions.Default) |> Async.RunSynchronously |> Option.defaultValue (ResizeArray()) |> Seq.map(fun result -> result.DisplayText) @@ -103,10 +104,11 @@ let VerifyCompletionList(fileContents, marker, expected, unexpected) = let VerifyCompletionListExactly(fileContents: string, marker: string, expected: string list) = + let projectOptions = { projectOptions [| |] with ProjectId = Some(Guid.NewGuid().ToString()) } let caretPosition = fileContents.IndexOf(marker) + marker.Length - + let document, _ = RoslynTestHelpers.CreateDocument(filePath, fileContents) let actual = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, SourceText.From(fileContents), caretPosition, projectOptions [| |], filePath, 0, (fun _ -> []), LanguageServicePerformanceOptions.Default, IntelliSenseOptions.Default) + FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, document, caretPosition, projectOptions, (fun _ -> []), LanguageServicePerformanceOptions.Default, IntelliSenseOptions.Default) |> Async.RunSynchronously |> Option.defaultValue (ResizeArray()) |> Seq.toList diff --git a/vsintegration/tests/UnitTests/DocumentDiagnosticAnalyzerTests.fs b/vsintegration/tests/UnitTests/DocumentDiagnosticAnalyzerTests.fs index 41d071a5607..25e6f9f4060 100644 --- a/vsintegration/tests/UnitTests/DocumentDiagnosticAnalyzerTests.fs +++ b/vsintegration/tests/UnitTests/DocumentDiagnosticAnalyzerTests.fs @@ -40,9 +40,11 @@ type DocumentDiagnosticAnalyzerTests() = let getDiagnostics (fileContents: string) = async { + let projectOptions = { projectOptions with ProjectId = Some(Guid.NewGuid().ToString()) } + let document, _ = RoslynTestHelpers.CreateDocument(filePath, fileContents) 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) + let! syntacticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checker, document, parsingOptions, projectOptions, DiagnosticsType.Syntax) + let! semanticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checker, document, parsingOptions, projectOptions, DiagnosticsType.Semantic) return syntacticDiagnostics.AddRange(semanticDiagnostics) } |> Async.RunSynchronously @@ -53,7 +55,8 @@ type DocumentDiagnosticAnalyzerTests() = | 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") + if not errors.IsEmpty then + Assert.Fail("There should be no errors generated", errors) member private this.VerifyErrorAtMarker(fileContents: string, expectedMarker: string, ?expectedMessage: string) = let errors = getDiagnostics fileContents |> Seq.filter(fun e -> e.Severity = DiagnosticSeverity.Error) |> Seq.toArray diff --git a/vsintegration/tests/UnitTests/DocumentHighlightsServiceTests.fs b/vsintegration/tests/UnitTests/DocumentHighlightsServiceTests.fs index ca5a91dabce..baf54d9ee50 100644 --- a/vsintegration/tests/UnitTests/DocumentHighlightsServiceTests.fs +++ b/vsintegration/tests/UnitTests/DocumentHighlightsServiceTests.fs @@ -52,8 +52,9 @@ let internal projectOptions = { } let private getSpans (sourceText: SourceText) (caretPosition: int) = - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - FSharpDocumentHighlightsService.GetDocumentHighlights(checker, documentId, sourceText, filePath, caretPosition, [], projectOptions, 0, LanguageServicePerformanceOptions.Default) + let projectOptions = { projectOptions with ProjectId = Some(Guid.NewGuid().ToString()) } + let document = RoslynTestHelpers.CreateDocument(filePath, sourceText) + FSharpDocumentHighlightsService.GetDocumentHighlights(checker, document, caretPosition, [], projectOptions, LanguageServicePerformanceOptions.Default) |> Async.RunSynchronously |> Option.defaultValue [||] diff --git a/vsintegration/tests/UnitTests/FsxCompletionProviderTests.fs b/vsintegration/tests/UnitTests/FsxCompletionProviderTests.fs index 5c322868c39..e9c7188d064 100644 --- a/vsintegration/tests/UnitTests/FsxCompletionProviderTests.fs +++ b/vsintegration/tests/UnitTests/FsxCompletionProviderTests.fs @@ -38,7 +38,6 @@ open UnitTests.TestLib.LanguageService // AppDomain helper type Worker () = - inherit MarshalByRefObject() let filePath = "C:\\test.fsx" let projectOptions = { @@ -55,62 +54,13 @@ type Worker () = Stamp = None } - let formatCompletions(completions : string seq) = - "\n\t" + String.Join("\n\t", completions) - - 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, projectOptions, filePath, 0, (fun _ -> []), LanguageServicePerformanceOptions.Default, IntelliSenseOptions.Default) - |> Async.RunSynchronously - |> Option.defaultValue (ResizeArray()) - |> Seq.map(fun result -> result.DisplayText) - - let expectedFound = - expected - |> Seq.filter results.Contains - - let expectedNotFound = - expected - |> Seq.filter (expectedFound.Contains >> not) - - let unexpectedNotFound = - unexpected - |> Seq.filter (results.Contains >> not) - - let unexpectedFound = - unexpected - |> Seq.filter (unexpectedNotFound.Contains >> not) - - // If either of these are true, then the test fails. - let hasExpectedNotFound = not (Seq.isEmpty expectedNotFound) - let hasUnexpectedFound = not (Seq.isEmpty unexpectedFound) - - if hasExpectedNotFound || hasUnexpectedFound then - let expectedNotFoundMsg = - if hasExpectedNotFound then - sprintf "\nExpected completions not found:%s\n" (formatCompletions expectedNotFound) - else - String.Empty - - let unexpectedFoundMsg = - if hasUnexpectedFound then - sprintf "\nUnexpected completions found:%s\n" (formatCompletions unexpectedFound) - else - String.Empty - - let completionsMsg = sprintf "\nin Completions:%s" (formatCompletions results) - - let msg = sprintf "%s%s%s" expectedNotFoundMsg unexpectedFoundMsg completionsMsg - - Assert.Fail(msg) - member _.VerifyCompletionListExactly(fileContents: string, marker: string, expected: List) = - + let projectOptions = { projectOptions with ProjectId = Some(Guid.NewGuid().ToString()) } let caretPosition = fileContents.IndexOf(marker) + marker.Length + let document, _ = RoslynTestHelpers.CreateDocument(filePath, fileContents) let expected = expected |> Seq.toList let actual = - let x = FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, SourceText.From(fileContents), caretPosition, projectOptions, filePath, 0, (fun _ -> []), LanguageServicePerformanceOptions.Default, IntelliSenseOptions.Default) + let x = FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, document, caretPosition, projectOptions, (fun _ -> []), LanguageServicePerformanceOptions.Default, IntelliSenseOptions.Default) |> Async.RunSynchronously x |> Option.defaultValue (ResizeArray()) |> Seq.toList @@ -127,17 +77,7 @@ type Worker () = module FsxCompletionProviderTests = - let pathToThisDll = Assembly.GetExecutingAssembly().CodeBase - - let getWorker () = - - let adSetup = - let setup = new System.AppDomainSetup () - setup.PrivateBinPath <- pathToThisDll - setup - - let ad = AppDomain.CreateDomain((Guid()).ToString(), null, adSetup) - (ad.CreateInstanceFromAndUnwrap(pathToThisDll, typeof.FullName)) :?> Worker + let getWorker () = Worker() [] let fsiShouldTriggerCompletionInFsxFile() = diff --git a/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs b/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs index 0462a1812e9..f77a3e6e9cf 100644 --- a/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs +++ b/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs @@ -42,20 +42,18 @@ module GoToDefinitionServiceTests = let private findDefinition ( checker: FSharpChecker, - documentKey: DocumentId, - sourceText: SourceText, - filePath: string, + document: Document, + sourceText: SourceText, position: int, defines: string list, - options: FSharpProjectOptions, - textVersionHash: int + options: FSharpProjectOptions ) : range option = maybe { let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! lexerSymbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy, false, false) - let! _, _, checkFileResults = checker.ParseAndCheckDocument (filePath, textVersionHash, sourceText, options, LanguageServicePerformanceOptions.Default, userOpName=userOpName) |> Async.RunSynchronously + let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) + let! _, _, checkFileResults = checker.ParseAndCheckDocument (document, options, LanguageServicePerformanceOptions.Default, userOpName=userOpName) |> Async.RunSynchronously let declarations = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, false) @@ -86,9 +84,9 @@ module GoToDefinitionServiceTests = File.WriteAllText(filePath, fileContents) let caretPosition = fileContents.IndexOf(caretMarker) + caretMarker.Length - 1 // inside the marker - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let document, sourceText = RoslynTestHelpers.CreateDocument(filePath, fileContents) let actual = - findDefinition(checker, documentId, SourceText.From(fileContents), filePath, caretPosition, [], options, 0) + findDefinition(checker, document, sourceText, caretPosition, [], options) |> Option.map (fun range -> (range.StartLine, range.EndLine, range.StartColumn, range.EndColumn)) if actual <> expected then diff --git a/vsintegration/tests/UnitTests/QuickInfoTests.fs b/vsintegration/tests/UnitTests/QuickInfoTests.fs index 0441d8fa170..688ca2624ee 100644 --- a/vsintegration/tests/UnitTests/QuickInfoTests.fs +++ b/vsintegration/tests/UnitTests/QuickInfoTests.fs @@ -15,9 +15,8 @@ module QuickInfo = let internal GetQuickInfo (project:FSharpProject) (fileName:string) (caretPosition:int) = async { let code = File.ReadAllText(fileName) - let sourceText = SourceText.From(code) - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) // only used for caching purposes - return! FSharpAsyncQuickInfoSource.ProvideQuickInfo(checker, documentId, sourceText, fileName, caretPosition, FSharpParsingOptions.Default, project.Options, 0, LanguageServicePerformanceOptions.Default) + let document, _ = RoslynTestHelpers.CreateDocument(fileName, code) + return! FSharpAsyncQuickInfoSource.ProvideQuickInfo(checker, document, caretPosition, FSharpParsingOptions.Default, project.Options, LanguageServicePerformanceOptions.Default) } |> Async.RunSynchronously let GetQuickInfoText (project:FSharpProject) (fileName:string) (caretPosition:int) = diff --git a/vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs b/vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs index 967719024e4..3846e2b6308 100644 --- a/vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs +++ b/vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs @@ -32,9 +32,10 @@ type SemanticClassificationServiceTests() = let perfOptions = { LanguageServicePerformanceOptions.Default with AllowStaleCompletionResults = false } let getRanges (source: string) : SemanticClassificationItem list = + let projectOptions = { projectOptions with ProjectId = Some(Guid.NewGuid().ToString()) } asyncMaybe { - - let! _, _, checkFileResults = checker.ParseAndCheckDocument(filePath, 0, SourceText.From(source), projectOptions, perfOptions, "") + let document, _ = RoslynTestHelpers.CreateDocument(filePath, source) + let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, perfOptions, "") return checkFileResults.GetSemanticClassification(None) } |> Async.RunSynchronously diff --git a/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs b/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs index 96fe558cc79..df7ff193736 100644 --- a/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs +++ b/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs @@ -54,11 +54,11 @@ let GetSignatureHelp (project:FSharpProject) (fileName:string) (caretPosition:in let caretLinePos = textLines.GetLinePosition(caretPosition) let caretLineColumn = caretLinePos.Character let perfOptions = LanguageServicePerformanceOptions.Default - let textVersionHash = 1 + let document = RoslynTestHelpers.CreateDocument(fileName, sourceText) let parseResults, _, checkFileResults = let x = - checker.ParseAndCheckDocument(fileName, textVersionHash, sourceText, project.Options, perfOptions, "TestSignatureHelpProvider") + checker.ParseAndCheckDocument(document, project.Options, perfOptions, "TestSignatureHelpProvider") |> Async.RunSynchronously x.Value @@ -104,11 +104,11 @@ let assertSignatureHelpForMethodCalls (fileContents: string) (marker: string) (e let caretLinePos = textLines.GetLinePosition(caretPosition) let caretLineColumn = caretLinePos.Character let perfOptions = LanguageServicePerformanceOptions.Default - let textVersionHash = 0 + let document = RoslynTestHelpers.CreateDocument(filePath, sourceText) let parseResults, _, checkFileResults = let x = - checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText, projectOptions, perfOptions, "TestSignatureHelpProvider") + checker.ParseAndCheckDocument(document, projectOptions, perfOptions, "TestSignatureHelpProvider") |> Async.RunSynchronously if x.IsNone then @@ -141,14 +141,12 @@ let assertSignatureHelpForMethodCalls (fileContents: string) (marker: string) (e let assertSignatureHelpForFunctionApplication (fileContents: string) (marker: string) expectedArgumentCount expectedArgumentIndex = let caretPosition = fileContents.LastIndexOf(marker) + marker.Length - let sourceText = SourceText.From(fileContents) + let document, sourceText = RoslynTestHelpers.CreateDocument(filePath, fileContents) let perfOptions = LanguageServicePerformanceOptions.Default - let textVersionHash = 0 - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) let parseResults, _, checkFileResults = let x = - checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText, projectOptions, perfOptions, "TestSignatureHelpProvider") + checker.ParseAndCheckDocument(document, projectOptions, perfOptions, "TestSignatureHelpProvider") |> Async.RunSynchronously if x.IsNone then @@ -167,7 +165,7 @@ let assertSignatureHelpForFunctionApplication (fileContents: string) (marker: st FSharpSignatureHelpProvider.ProvideParametersAsyncAux( parseResults, checkFileResults, - documentId, + document.Id, [], DefaultDocumentationProvider, sourceText, @@ -429,14 +427,13 @@ M.f """ let marker = "id " let caretPosition = fileContents.IndexOf(marker) + marker.Length - let sourceText = SourceText.From(fileContents) + + let document, sourceText = RoslynTestHelpers.CreateDocument(filePath, fileContents) let perfOptions = LanguageServicePerformanceOptions.Default - let textVersionHash = 0 - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) let parseResults, _, checkFileResults = let x = - checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText, projectOptions, perfOptions, "TestSignatureHelpProvider") + checker.ParseAndCheckDocument(document, projectOptions, perfOptions, "TestSignatureHelpProvider") |> Async.RunSynchronously if x.IsNone then @@ -455,7 +452,7 @@ M.f FSharpSignatureHelpProvider.ProvideParametersAsyncAux( parseResults, checkFileResults, - documentId, + document.Id, [], DefaultDocumentationProvider, sourceText, diff --git a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs new file mode 100644 index 00000000000..6ccdbacc715 --- /dev/null +++ b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs @@ -0,0 +1,39 @@ +namespace Microsoft.VisualStudio.FSharp.Editor.Tests.Roslyn + +open System +open System.IO +open Microsoft.CodeAnalysis +open Microsoft.CodeAnalysis.Text + +[] +type RoslynTestHelpers private () = + + static member CreateDocument (filePath, text: SourceText) = + let isScript = String.Equals(Path.GetExtension(filePath), ".fsx", StringComparison.OrdinalIgnoreCase) + let workspace = new AdhocWorkspace() + let projInfo = + let projId = ProjectId.CreateNewId() + ProjectInfo.Create( + projId, + VersionStamp.Create(DateTime.UtcNow), + "test.fsproj", + "test.dll", + LanguageNames.CSharp + ) + let proj = workspace.AddProject(projInfo) + + let docInfo = + let docId = DocumentId.CreateNewId(proj.Id) + DocumentInfo.Create( + docId, + filePath, + loader=TextLoader.From(text.Container, VersionStamp.Create(DateTime.UtcNow)), + filePath=filePath, + sourceCodeKind= if isScript then SourceCodeKind.Script else SourceCodeKind.Regular) + + workspace.AddDocument(docInfo) + + static member CreateDocument (filePath, code: string) = + let text = SourceText.From(code) + RoslynTestHelpers.CreateDocument(filePath, text), text + diff --git a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj index d2161ed8a4b..ab0064b7dd8 100644 --- a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj +++ b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj @@ -38,6 +38,7 @@ +