From 89defe777abc2a10bbf350986f77769efc989eda Mon Sep 17 00:00:00 2001 From: Will Smith Date: Thu, 20 May 2021 13:54:03 -0700 Subject: [PATCH 01/27] Initial work with Vlad on in-memory documents --- .../FSharp.Compiler.Service.fsproj | 6 + src/fsharp/ParseAndCheckInputs.fs | 32 ++++ src/fsharp/ParseAndCheckInputs.fsi | 7 + src/fsharp/service/FSharpCheckerResults.fsi | 2 +- src/fsharp/service/FSharpDocument.fs | 140 ++++++++++++++++ src/fsharp/service/FSharpDocument.fsi | 30 ++++ src/fsharp/service/IncrementalBuild.fs | 153 ++++++++++-------- src/fsharp/service/IncrementalBuild.fsi | 4 +- src/fsharp/service/service.fs | 19 ++- src/fsharp/service/service.fsi | 2 + .../FSharpAnalysisSaveFileCommandHandler.fs | 76 --------- .../FSharpProjectOptionsManager.fs | 23 +++ .../FSharp.LanguageService/FSharpSource.fs | 2 +- 13 files changed, 353 insertions(+), 143 deletions(-) create mode 100644 src/fsharp/service/FSharpDocument.fs create mode 100644 src/fsharp/service/FSharpDocument.fsi delete mode 100644 vsintegration/src/FSharp.Editor/LanguageService/FSharpAnalysisSaveFileCommandHandler.fs diff --git a/src/fsharp/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj b/src/fsharp/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj index 6f63ba25673..2f95a002431 100644 --- a/src/fsharp/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj +++ b/src/fsharp/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj @@ -825,6 +825,12 @@ Service/SemanticClassificationKey.fs + + Service/FSharpDocument.fsi + + + Service/FSharpDocument.fs + Service/IncrementalBuild.fsi diff --git a/src/fsharp/ParseAndCheckInputs.fs b/src/fsharp/ParseAndCheckInputs.fs index 3fa2ae15aac..6c810357d1b 100644 --- a/src/fsharp/ParseAndCheckInputs.fs +++ b/src/fsharp/ParseAndCheckInputs.fs @@ -402,6 +402,22 @@ let checkInputFile (tcConfig: TcConfig) filename = else error(Error(FSComp.SR.buildInvalidSourceFileExtension(SanitizeFileName filename tcConfig.implicitIncludeDir), rangeStartup)) +let parseInputStreamAux (tcConfig: TcConfig, lexResourceManager, conditionalCompilationDefines, filename, isLastCompiland, errorLogger, retryLocked, stream: Stream) = + use reader = stream.GetReader(tcConfig.inputCodePage, retryLocked) + + // Set up the LexBuffer for the file + let lexbuf = UnicodeLexing.StreamReaderAsLexbuf(tcConfig.langVersion.SupportsFeature, reader) + + // Parse the file drawing tokens from the lexbuf + ParseOneInputLexbuf(tcConfig, lexResourceManager, conditionalCompilationDefines, lexbuf, filename, isLastCompiland, errorLogger) + +let parseInputSourceTextAux (tcConfig: TcConfig, lexResourceManager, conditionalCompilationDefines, filename, isLastCompiland, errorLogger, sourceText: ISourceText) = + // Set up the LexBuffer for the file + let lexbuf = UnicodeLexing.SourceTextAsLexbuf(tcConfig.langVersion.SupportsFeature, sourceText) + + // Parse the file drawing tokens from the lexbuf + ParseOneInputLexbuf(tcConfig, lexResourceManager, conditionalCompilationDefines, lexbuf, filename, isLastCompiland, errorLogger) + let parseInputFileAux (tcConfig: TcConfig, lexResourceManager, conditionalCompilationDefines, filename, isLastCompiland, errorLogger, retryLocked) = // Get a stream reader for the file use fileStream = FileSystem.OpenFileForReadShim(filename).AsStream() @@ -422,6 +438,22 @@ let ParseOneInputFile (tcConfig: TcConfig, lexResourceManager, conditionalCompil errorRecovery e rangeStartup EmptyParsedInput(filename, isLastCompiland) +/// Parse an input from stream +let ParseOneInputStream (tcConfig: TcConfig, lexResourceManager, conditionalCompilationDefines, filename, isLastCompiland, errorLogger, retryLocked, stream: Stream) = + try + parseInputStreamAux(tcConfig, lexResourceManager, conditionalCompilationDefines, filename, isLastCompiland, errorLogger, retryLocked, stream) + with e -> + errorRecovery e rangeStartup + EmptyParsedInput(filename, isLastCompiland) + +/// Parse an input from source text +let ParseOneInputSourceText (tcConfig: TcConfig, lexResourceManager, conditionalCompilationDefines, filename, isLastCompiland, errorLogger, sourceText: ISourceText) = + try + parseInputSourceTextAux(tcConfig, lexResourceManager, conditionalCompilationDefines, filename, isLastCompiland, errorLogger, sourceText) + with e -> + errorRecovery e rangeStartup + EmptyParsedInput(filename, isLastCompiland) + /// Parse multiple input files from disk let ParseInputFiles (tcConfig: TcConfig, lexResourceManager, conditionalCompilationDefines, sourceFiles, errorLogger: ErrorLogger, exiter: Exiter, createErrorLogger: (Exiter -> CapturingErrorLogger), retryLocked) = try diff --git a/src/fsharp/ParseAndCheckInputs.fsi b/src/fsharp/ParseAndCheckInputs.fsi index c7a68dd894a..ab7a0247dfc 100644 --- a/src/fsharp/ParseAndCheckInputs.fsi +++ b/src/fsharp/ParseAndCheckInputs.fsi @@ -3,6 +3,7 @@ /// Contains logic to coordinate the parsing and checking of one or a group of files module internal FSharp.Compiler.ParseAndCheckInputs +open System.IO open Internal.Utilities.Library open FSharp.Compiler.CheckExpressions open FSharp.Compiler.CheckDeclarations @@ -49,6 +50,12 @@ val ApplyNoWarnsToTcConfig: TcConfig * ParsedInput * string -> TcConfig /// Parse one input file val ParseOneInputFile: TcConfig * Lexhelp.LexResourceManager * conditionalCompilationDefines: string list * string * isLastCompiland: (bool * bool) * ErrorLogger * retryLocked: bool -> ParsedInput +/// Parse one input stream +val ParseOneInputStream: TcConfig * Lexhelp.LexResourceManager * conditionalCompilationDefines: string list * string * isLastCompiland: (bool * bool) * ErrorLogger * retryLocked: bool * stream: Stream -> ParsedInput + +/// Parse one input source text +val ParseOneInputSourceText: TcConfig * Lexhelp.LexResourceManager * conditionalCompilationDefines: string list * string * isLastCompiland: (bool * bool) * ErrorLogger * sourceText: ISourceText -> ParsedInput + /// Parse multiple input files from disk val ParseInputFiles: TcConfig * Lexhelp.LexResourceManager * conditionalCompilationDefines: string list * string list * ErrorLogger * Exiter * createErrorLogger: (Exiter -> CapturingErrorLogger) * retryLocked: bool -> (ParsedInput * string) list diff --git a/src/fsharp/service/FSharpCheckerResults.fsi b/src/fsharp/service/FSharpCheckerResults.fsi index 3414d35536a..c74fa0b82de 100644 --- a/src/fsharp/service/FSharpCheckerResults.fsi +++ b/src/fsharp/service/FSharpCheckerResults.fsi @@ -50,7 +50,7 @@ type public FSharpProjectOptions = /// This is the unique identifier for the project, it is case sensitive. If it's None, will key off of ProjectFileName in our caching. ProjectId: string option - /// The files in the project + /// The source files in the project SourceFiles: string[] /// Additional command line argument options for the project. These can include additional files and references. diff --git a/src/fsharp/service/FSharpDocument.fs b/src/fsharp/service/FSharpDocument.fs new file mode 100644 index 00000000000..dff768aa0d7 --- /dev/null +++ b/src/fsharp/service/FSharpDocument.fs @@ -0,0 +1,140 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Compiler.CodeAnalysis + +open System +open System.Collections.Generic +open System.Collections.Immutable +open System.IO +open System.IO.MemoryMappedFiles +open System.Xml +open System.Runtime.InteropServices +open System.Threading +open Internal.Utilities.Library +open Internal.Utilities.Library.Extras +open FSharp.Compiler +open FSharp.Compiler.AbstractIL +open FSharp.Compiler.AbstractIL.IL +open FSharp.Compiler.AbstractIL.ILBinaryReader +open FSharp.Compiler.CheckExpressions +open FSharp.Compiler.CheckDeclarations +open FSharp.Compiler.CompilerConfig +open FSharp.Compiler.CompilerDiagnostics +open FSharp.Compiler.CompilerGlobalState +open FSharp.Compiler.CompilerImports +open FSharp.Compiler.CompilerOptions +open FSharp.Compiler.CreateILModule +open FSharp.Compiler.DependencyManager +open FSharp.Compiler.Diagnostics +open FSharp.Compiler.EditorServices +open FSharp.Compiler.ErrorLogger +open FSharp.Compiler.IO +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.NameResolution +open FSharp.Compiler.ParseAndCheckInputs +open FSharp.Compiler.ScriptClosure +open FSharp.Compiler.Syntax +open FSharp.Compiler.TcGlobals +open FSharp.Compiler.Text +open FSharp.Compiler.Text.Range +open FSharp.Compiler.Xml +open FSharp.Compiler.TypedTree +open FSharp.Compiler.TypedTreeOps + +[] +type FSharpDocumentText = + | Stream of Stream + | SourceText of ISourceText + + interface IDisposable with + + member this.Dispose() = + match this with + | Stream stream -> stream.Dispose() + | _ -> () + +[] +type FSharpDocument internal () = + + abstract FilePath : string + + abstract TimeStamp : DateTime + + abstract GetText : unit -> FSharpDocumentText + +type private FSharpDocumentMemoryMappedFile(filePath: string, timeStamp: DateTime, mmf: MemoryMappedFile) = + inherit FSharpDocument() + + override _.FilePath = filePath + + override _.TimeStamp = timeStamp + + override _.GetText() = + FSharpDocumentText.Stream(mmf.CreateViewStream() :> Stream) + +type private FSharpDocumentByteArray(filePath: string, timeStamp: DateTime, bytes: byte[]) = + inherit FSharpDocument() + + override _.FilePath = filePath + + override _.TimeStamp = timeStamp + + override _.GetText() = + FSharpDocumentText.Stream(new MemoryStream(bytes, 0, bytes.Length, false) :> Stream) + +type private FSharpDocumentFromFile(filePath: string) = + inherit FSharpDocument() + + override _.FilePath = filePath + + override _.TimeStamp = FileSystem.GetLastWriteTimeShim(filePath) + + override _.GetText() = FSharpDocumentText.Stream(File.OpenRead(filePath) :> Stream) + +type private FSharpDocumentCustom(filePath: string, getTimeStamp, getSourceText) = + inherit FSharpDocument() + + override _.FilePath = filePath + + override _.TimeStamp = getTimeStamp() + + override _.GetText() = + FSharpDocumentText.SourceText(getSourceText()) + +type FSharpDocument with + + static member Create(filePath, getTimeStamp, getSourceText) = + FSharpDocumentCustom(filePath, getTimeStamp, getSourceText) :> FSharpDocument + + static member CreateFromFile(filePath: string) = + FSharpDocumentFromFile(filePath) :> FSharpDocument + + static member CreateCopyFromFile(filePath: string) = + let fileMode = FileMode.Open + let fileAccess = FileAccess.Read + let fileShare = FileShare.Delete ||| FileShare.ReadWrite + + let timeStamp = FileSystem.GetLastWriteTimeShim(filePath) + + // We want to use mmaped files only when: + // - Opening large binary files (no need to use for source or resource files really) + // - Running on mono, since its MemoryMappedFile implementation throws when "mapName" is not provided (is null). + // (See: https://github.com/mono/mono/issues/10245) + if runningOnMono then + let bytes = File.ReadAllBytes filePath + FSharpDocumentByteArray(filePath, timeStamp, bytes) :> FSharpDocument + else + let fileStream = new FileStream(filePath, fileMode, fileAccess, fileShare) + let length = fileStream.Length + let mmf = + MemoryMappedFile.CreateNew( + null, + length, + MemoryMappedFileAccess.Read, + MemoryMappedFileOptions.None, + HandleInheritability.None) + use stream = mmf.CreateViewStream(0L, length, MemoryMappedFileAccess.Read) + fileStream.CopyTo(stream) + fileStream.Dispose() + FSharpDocumentMemoryMappedFile(filePath, timeStamp, mmf) :> FSharpDocument + diff --git a/src/fsharp/service/FSharpDocument.fsi b/src/fsharp/service/FSharpDocument.fsi new file mode 100644 index 00000000000..095e2de6674 --- /dev/null +++ b/src/fsharp/service/FSharpDocument.fsi @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Compiler.CodeAnalysis + +open System +open System.IO +open FSharp.Compiler.Text + +[] +type FSharpDocumentText = + internal + | Stream of Stream + | SourceText of ISourceText + + interface IDisposable + +[] +type FSharpDocument = + + abstract FilePath : string + + abstract TimeStamp : DateTime + + abstract GetText : unit -> FSharpDocumentText + + static member CreateFromFile : filePath: string -> FSharpDocument + + static member CreateCopyFromFile : filePath: string -> FSharpDocument + + static member Create : filePath: string * getTimeStamp: (unit -> DateTime) * getSourceText: (unit -> ISourceText) -> FSharpDocument \ No newline at end of file diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 9eff18d0b67..8773bfe24c0 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -101,8 +101,9 @@ module IncrementalBuildSyntaxTree = /// Information needed to lazily parse a file to get a ParsedInput. Internally uses a weak cache. [] - type SyntaxTree (tcConfig: TcConfig, fileParsed: Event, lexResourceManager, sourceRange: range, filename: string, isLastCompiland) = + type SyntaxTree (tcConfig: TcConfig, fileParsed: Event, lexResourceManager, sourceRange: range, doc: FSharpDocument, isLastCompiland) = + let filename = doc.FilePath let mutable weakCache: WeakReference<_> option = None let parse(sigNameOpt: QualifiedNameOfFile option) = @@ -128,7 +129,12 @@ module IncrementalBuildSyntaxTree = ) ) else - ParseOneInputFile(tcConfig, lexResourceManager, [], filename, isLastCompiland, errorLogger, (*retryLocked*)true) + use text = doc.GetText() + match text with + | FSharpDocumentText.Stream(stream) -> + ParseOneInputStream(tcConfig, lexResourceManager, [], filename, isLastCompiland, errorLogger, (*retryLocked*)false, stream) + | FSharpDocumentText.SourceText(sourceText) -> + ParseOneInputSourceText(tcConfig, lexResourceManager, [], filename, isLastCompiland, errorLogger, sourceText) fileParsed.Trigger filename @@ -678,7 +684,7 @@ type IncrementalBuilder(tcGlobals, assemblyName, niceNameGen: NiceNameGenerator, lexResourceManager, - sourceFiles, + docs: FSharpDocument list, loadClosureOpt: LoadClosure option, keepAssemblyContents, keepAllBackgroundResolutions, @@ -698,12 +704,13 @@ type IncrementalBuilder(tcGlobals, let defaultPartialTypeChecking = enablePartialTypeChecking // Check for the existence of loaded sources and prepend them to the sources list if present. - let sourceFiles = tcConfig.GetAvailableLoadedSources() @ (sourceFiles |>List.map (fun s -> rangeStartup, s)) + let sourceFiles = tcConfig.GetAvailableLoadedSources() @ (docs |>List.map (fun s -> rangeStartup, s.FilePath)) // Mark up the source files with an indicator flag indicating if they are the last source file in the project let sourceFiles = let flags, isExe = tcConfig.ComputeCanContainEntryPoint(sourceFiles |> List.map snd) - ((sourceFiles, flags) ||> List.map2 (fun (m, nm) flag -> (m, nm, (flag, isExe)))) + ((docs, flags) ||> List.map2 (fun doc flag -> (rangeStartup, doc, (flag, isExe)))) + |> Array.ofList let defaultTimeStamp = DateTime.UtcNow @@ -720,7 +727,7 @@ type IncrementalBuilder(tcGlobals, let allDependencies = [| yield! basicDependencies for (_, f, _) in sourceFiles do - yield f |] + yield f.FilePath |] // For scripts, the dependency provider is already available. // For projects create a fresh one for the project. @@ -733,12 +740,12 @@ type IncrementalBuilder(tcGlobals, // START OF BUILD TASK FUNCTIONS /// Get the timestamp of the given file name. - let StampFileNameTask (cache: TimeStampCache) (_m: range, filename: string, _isLastCompiland) = - cache.GetFileTimeStamp filename + let StampFileNameTask (_m: range, doc: FSharpDocument, _isLastCompiland) = + doc.TimeStamp /// Parse the given file and return the given input. - let ParseTask (sourceRange: range, filename: string, isLastCompiland) = - SyntaxTree(tcConfig, fileParsed, lexResourceManager, sourceRange, filename, isLastCompiland) + let ParseTask (sourceRange: range, doc: FSharpDocument, isLastCompiland) = + SyntaxTree(tcConfig, fileParsed, lexResourceManager, sourceRange, doc, isLastCompiland) /// Timestamps of referenced assemblies are taken from the file's timestamp. let StampReferencedAssemblyTask (cache: TimeStampCache) (_ref, timeStamper) = @@ -942,50 +949,52 @@ type IncrementalBuilder(tcGlobals, // START OF BUILD DESCRIPTION // Inputs - let fileNames = sourceFiles |> Array.ofList // TODO: This should be an immutable array. + let fileNames = sourceFiles // TODO: This should be an immutable array. let referencedAssemblies = nonFrameworkAssemblyInputs |> Array.ofList // TODO: This should be an immutable array. - let computeStampedFileName (state: IncrementalBuilderState) (cache: TimeStampCache) slot fileInfo = + let invalidateSlot (state: IncrementalBuilderState) newStamp slot = + match state.boundModels.[slot] with + // This prevents an implementation file that has a backing signature file from invalidating the rest of the build. + | Some(boundModel) when state.enablePartialTypeChecking && boundModel.BackingSignature.IsSome -> + boundModel.Invalidate() + { state with + stampedFileNames = state.stampedFileNames.SetItem(slot, newStamp) + } + | _ -> + let stampedFileNames = state.stampedFileNames.ToBuilder() + let logicalStampedFileNames = state.logicalStampedFileNames.ToBuilder() + let boundModels = state.boundModels.ToBuilder() + + // Invalidate the file and all files below it. + for j = 0 to stampedFileNames.Count - slot - 1 do + let stamp = StampFileNameTask fileNames.[slot + j] + stampedFileNames.[slot + j] <- stamp + logicalStampedFileNames.[slot + j] <- stamp + boundModels.[slot + j] <- None + + { state with + // Something changed, the finalized view of the project must be invalidated. + finalizedBoundModel = None + + stampedFileNames = stampedFileNames.ToImmutable() + logicalStampedFileNames = logicalStampedFileNames.ToImmutable() + boundModels = boundModels.ToImmutable() + } + + let computeStampedFileName (state: IncrementalBuilderState) slot fileInfo = let currentStamp = state.stampedFileNames.[slot] - let stamp = StampFileNameTask cache fileInfo + let stamp = StampFileNameTask fileInfo if currentStamp <> stamp then - match state.boundModels.[slot] with - // This prevents an implementation file that has a backing signature file from invalidating the rest of the build. - | Some(boundModel) when state.enablePartialTypeChecking && boundModel.BackingSignature.IsSome -> - boundModel.Invalidate() - { state with - stampedFileNames = state.stampedFileNames.SetItem(slot, StampFileNameTask cache fileInfo) - } - | _ -> - - let stampedFileNames = state.stampedFileNames.ToBuilder() - let logicalStampedFileNames = state.logicalStampedFileNames.ToBuilder() - let boundModels = state.boundModels.ToBuilder() - - // Invalidate the file and all files below it. - for j = 0 to stampedFileNames.Count - slot - 1 do - let stamp = StampFileNameTask cache fileNames.[slot + j] - stampedFileNames.[slot + j] <- stamp - logicalStampedFileNames.[slot + j] <- stamp - boundModels.[slot + j] <- None - - { state with - // Something changed, the finalized view of the project must be invalidated. - finalizedBoundModel = None - - stampedFileNames = stampedFileNames.ToImmutable() - logicalStampedFileNames = logicalStampedFileNames.ToImmutable() - boundModels = boundModels.ToImmutable() - } + invalidateSlot state stamp slot else state - let computeStampedFileNames state (cache: TimeStampCache) = + let computeStampedFileNames state = let mutable i = 0 (state, fileNames) ||> Array.fold (fun state fileInfo -> - let newState = computeStampedFileName state cache i fileInfo + let newState = computeStampedFileName state i fileInfo i <- i + 1 newState ) @@ -1031,13 +1040,13 @@ type IncrementalBuilder(tcGlobals, return state, result } - let computeBoundModel state (cache: TimeStampCache) (ctok: CompilationThreadToken) (slot: int) = + let computeBoundModel state (ctok: CompilationThreadToken) (slot: int) = if IncrementalBuild.injectCancellationFault then Eventually.canceled() else eventually { let fileInfo = fileNames.[slot] - let state = computeStampedFileName state cache slot fileInfo + let state = computeStampedFileName state slot fileInfo if state.boundModels.[slot].IsNone then let! (state, initial) = computeInitialBoundModel state ctok @@ -1064,12 +1073,12 @@ type IncrementalBuilder(tcGlobals, return state } - let computeBoundModels state (cache: TimeStampCache) (ctok: CompilationThreadToken) = - (state, [0..fileNames.Length-1]) ||> Eventually.fold (fun state slot -> computeBoundModel state cache ctok slot) + let computeBoundModels state (ctok: CompilationThreadToken) = + (state, [0..fileNames.Length-1]) ||> Eventually.fold (fun state slot -> computeBoundModel state ctok slot) - let computeFinalizedBoundModel state (cache: TimeStampCache) (ctok: CompilationThreadToken) = + let computeFinalizedBoundModel state (ctok: CompilationThreadToken) = eventually { - let! state = computeBoundModels state cache ctok + let! state = computeBoundModels state ctok match state.finalizedBoundModel with | Some result -> return state, result @@ -1108,7 +1117,7 @@ type IncrementalBuilder(tcGlobals, let! state, result = computeInitialBoundModel state ctok |> Eventually.toCancellable return state, Some(result, DateTime.MinValue) else - let! state = (state, [0..targetSlot]) ||> Eventually.fold (fun state slot -> computeBoundModel state cache ctok slot) |> Eventually.toCancellable + let! state = (state, [0..targetSlot]) ||> Eventually.fold (fun state slot -> computeBoundModel state ctok slot) |> Eventually.toCancellable let result = state.boundModels.[targetSlot] @@ -1123,7 +1132,7 @@ type IncrementalBuilder(tcGlobals, cancellable { let state = computeStampedReferencedAssemblies state cache - let! state, res = computeFinalizedBoundModel state cache ctok |> Eventually.toCancellable + let! state, res = computeFinalizedBoundModel state ctok |> Eventually.toCancellable return state, Some res } @@ -1175,17 +1184,28 @@ type IncrementalBuilder(tcGlobals, member _.AllDependenciesDeprecated = allDependencies + member this.UpdateDocument(ctok, doc: FSharpDocument) = + let state = currentState + let slot = this.GetSlotOfFileName doc.FilePath + let currentStamp = state.stampedFileNames.[slot] + let stamp = doc.TimeStamp + if currentStamp <> stamp then + let (m, _, isLastCompiland) = sourceFiles.[slot] + sourceFiles.[slot] <- (m, doc, isLastCompiland) + invalidateSlot state stamp slot + |> setCurrentState ctok + member _.PopulatePartialCheckingResults (ctok: CompilationThreadToken) = eventually { let cache = TimeStampCache defaultTimeStamp // One per step let state = currentState - let state = computeStampedFileNames state cache + let state = computeStampedFileNames state setCurrentState ctok state do! Eventually.ret () // allow cancellation let state = computeStampedReferencedAssemblies state cache setCurrentState ctok state do! Eventually.ret () // allow cancellation - let! state, _res = computeFinalizedBoundModel state cache ctok + let! state, _res = computeFinalizedBoundModel state ctok setCurrentState ctok state projectChecked.Trigger() } @@ -1201,7 +1221,7 @@ type IncrementalBuilder(tcGlobals, member builder.TryGetCheckResultsBeforeFileInProject (filename) = let cache = TimeStampCache defaultTimeStamp let state = currentState - let state = computeStampedFileNames state cache + let state = computeStampedFileNames state let state = computeStampedReferencedAssemblies state cache let slotOfFile = builder.GetSlotOfFileName filename @@ -1271,7 +1291,7 @@ type IncrementalBuilder(tcGlobals, member _.GetLogicalTimeStampForProject(cache) = let state = currentState - let state = computeStampedFileNames state cache + let state = computeStampedFileNames state let state = computeStampedReferencedAssemblies state cache let t1 = MaxTimeStampInDependencies state.stampedReferencedAssemblies let t2 = MaxTimeStampInDependencies state.stampedFileNames @@ -1279,10 +1299,10 @@ type IncrementalBuilder(tcGlobals, member _.TryGetSlotOfFileName(filename: string) = // Get the slot of the given file and force it to build. - let CompareFileNames (_, f2, _) = + let CompareFileNames (_, f2: FSharpDocument, _) = let result = - String.Compare(filename, f2, StringComparison.CurrentCultureIgnoreCase)=0 - || String.Compare(FileSystem.GetFullPathShim filename, FileSystem.GetFullPathShim f2, StringComparison.CurrentCultureIgnoreCase)=0 + String.Compare(filename, f2.FilePath, StringComparison.CurrentCultureIgnoreCase)=0 + || String.Compare(FileSystem.GetFullPathShim filename, FileSystem.GetFullPathShim f2.FilePath, StringComparison.CurrentCultureIgnoreCase)=0 result match fileNames |> Array.tryFindIndex CompareFileNames with | Some slot -> Some slot @@ -1305,7 +1325,7 @@ type IncrementalBuilder(tcGlobals, let syntaxTree = ParseTask results syntaxTree.Parse None - member _.SourceFiles = sourceFiles |> List.map (fun (_, f, _) -> f) + member _.SourceFiles = sourceFiles |> Array.map (fun (_, f, _) -> f.FilePath) |> List.ofArray /// CreateIncrementalBuilder (for background type checking). Note that fsc.fs also /// creates an incremental builder used by the command line compiler. @@ -1313,7 +1333,7 @@ type IncrementalBuilder(tcGlobals, (ctok, legacyReferenceResolver, defaultFSharpBinariesDir, frameworkTcImportsCache: FrameworkImportsCache, loadClosureOpt: LoadClosure option, - sourceFiles: string list, + docs: FSharpDocument list, commandLineArgs: string list, projectReferences, projectDirectory, useScriptResolutionRules, keepAssemblyContents, @@ -1342,7 +1362,7 @@ type IncrementalBuilder(tcGlobals, let resourceManager = new Lexhelp.LexResourceManager() /// Create a type-check configuration - let tcConfigB, sourceFilesNew = + let tcConfigB, sourceFilesNew, docsNew = let getSwitchValue switchString = match commandLineArgs |> Seq.tryFindIndex(fun s -> s.StartsWithOrdinal switchString) with @@ -1386,9 +1406,16 @@ type IncrementalBuilder(tcGlobals, tcConfigB.useSimpleResolution <- (getSwitchValue useSimpleResolutionSwitch) |> Option.isSome + let sourceFiles = docs |> List.map (fun x -> x.FilePath) // Apply command-line arguments and collect more source files if they are in the arguments let sourceFilesNew = ApplyCommandLineArgs(tcConfigB, sourceFiles, commandLineArgs) + let extraDocs = + sourceFilesNew |> List.skip sourceFiles.Length + |> List.map (fun x -> FSharpDocument.CreateFromFile(x)) + + let docsNew = docs @ extraDocs + // Never open PDB files for the language service, even if --standalone is specified tcConfigB.openDebugInformationForLaterStaticLinking <- false @@ -1403,7 +1430,7 @@ type IncrementalBuilder(tcGlobals, } |> Some - tcConfigB, sourceFilesNew + tcConfigB, sourceFilesNew, docsNew // If this is a builder for a script, re-apply the settings inferred from the // script and its load closure to the configuration. @@ -1473,7 +1500,7 @@ type IncrementalBuilder(tcGlobals, assemblyName, niceNameGen, resourceManager, - sourceFilesNew, + docsNew, loadClosureOpt, keepAssemblyContents, keepAllBackgroundResolutions, diff --git a/src/fsharp/service/IncrementalBuild.fsi b/src/fsharp/service/IncrementalBuild.fsi index bcaa09b9422..b38ccc368f6 100755 --- a/src/fsharp/service/IncrementalBuild.fsi +++ b/src/fsharp/service/IncrementalBuild.fsi @@ -232,6 +232,8 @@ type internal IncrementalBuilder = /// This may be a marginally long-running operation (parses are relatively quick, only one file needs to be parsed) member GetParseResultsForFile: filename:string -> ParsedInput * range * string * (PhasedDiagnostic * FSharpDiagnosticSeverity)[] + member UpdateDocument: CompilationThreadToken * doc: FSharpDocument -> unit + /// Create the incremental builder static member TryCreateIncrementalBuilderForProjectOptions: CompilationThreadToken * @@ -239,7 +241,7 @@ type internal IncrementalBuilder = defaultFSharpBinariesDir: string * FrameworkImportsCache * loadClosureOpt:LoadClosure option * - sourceFiles:string list * + docs:FSharpDocument list * commandLineArgs:string list * projectReferences: IProjectReference list * projectDirectory:string * diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index 2734108809e..0c743efa65f 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -284,8 +284,12 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC let loadClosure = scriptClosureCache.TryGet(AnyCallerThread, options) let! builderOpt, diagnostics = + let docs = + options.SourceFiles + |> Seq.map FSharpDocument.CreateFromFile + |> List.ofSeq IncrementalBuilder.TryCreateIncrementalBuilderForProjectOptions - (ctok, legacyReferenceResolver, FSharpCheckerResultsSettings.defaultFSharpBinariesDir, frameworkTcImportsCache, loadClosure, Array.toList options.SourceFiles, + (ctok, legacyReferenceResolver, FSharpCheckerResultsSettings.defaultFSharpBinariesDir, frameworkTcImportsCache, loadClosure, docs, Array.toList options.OtherOptions, projectReferences, options.ProjectDirectory, options.UseScriptResolutionRules, keepAssemblyContents, keepAllBackgroundResolutions, tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, @@ -567,6 +571,16 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC return! loop() } + member bc.UpdateDocument(options, doc: FSharpDocument, ct: Threading.CancellationToken) = + reactor.EnqueueOp("", "", "", fun ctok -> + match getOrCreateBuilder (ctok, options, "") |> Cancellable.run ct with + | ValueOrCancelled.Value(Some builder, _) -> + parseCacheLock.AcquireLock(fun ltok -> checkFileInProjectCache.RemoveAnySimilar(ltok, (doc.FilePath, 0L, options))) + builder.UpdateDocument(ctok, doc) + | _ -> + () + ) + /// Type-check the result obtained by parsing, but only if the antecedent type checking context is available. member bc.CheckFileInProjectAllowingStaleCachedResults(parseResults: FSharpParseFileResults, filename, fileVersion, sourceText: ISourceText, options, userOpName) = async { @@ -1385,6 +1399,9 @@ type FSharpChecker(legacyReferenceResolver, // This is for unit testing only member _.WaitForBackgroundCompile() = backgroundCompiler.WaitForBackgroundCompile() + member _.UpdateDocument(options: FSharpProjectOptions, doc: FSharpDocument, ct: Threading.CancellationToken) = + backgroundCompiler.UpdateDocument(options, doc, ct) + // Publish the ReactorOps from the background compiler for internal use member ic.ReactorOps = backgroundCompiler.ReactorOps diff --git a/src/fsharp/service/service.fsi b/src/fsharp/service/service.fsi index 0e5d5c9218e..bdb6d9979cc 100644 --- a/src/fsharp/service/service.fsi +++ b/src/fsharp/service/service.fsi @@ -379,6 +379,8 @@ type public FSharpChecker = //[] member WaitForBackgroundCompile: unit -> unit + member UpdateDocument: options: FSharpProjectOptions * doc: FSharpDocument * ct: Threading.CancellationToken -> unit + /// Report a statistic for testability static member ActualParseFileCount: int diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpAnalysisSaveFileCommandHandler.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpAnalysisSaveFileCommandHandler.fs deleted file mode 100644 index ffdc7f3c2af..00000000000 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpAnalysisSaveFileCommandHandler.fs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - -namespace Microsoft.VisualStudio.FSharp.Editor - -open System -open System.ComponentModel.Composition -open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.Text -open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics -open Microsoft.VisualStudio.FSharp.Editor -open Microsoft.VisualStudio.FSharp.Editor.Logging -open Microsoft.VisualStudio.Text.Editor.Commanding.Commands -open Microsoft.VisualStudio.Commanding -open Microsoft.VisualStudio.Utilities - -// This causes re-analysis to happen when a F# document is saved. -// We do this because FCS relies on the file system and existing open documents -// need to be re-analyzed so the changes are propogated. -// We only re-analyze F# documents that are dependent on the document that was just saved. -// We ignore F# script documents here. -// REVIEW: This could be removed when Roslyn workspaces becomes the source of truth for FCS instead of the file system. -[] -[)>] -[] -[] -type internal FSharpAnalysisSaveFileCommandHandler - [] - (analyzerService: IFSharpDiagnosticAnalyzerService) = - - interface IChainedCommandHandler with - - member _.DisplayName = Constants.FSharpAnalysisSaveFileHandler - - member _.ExecuteCommand(args: SaveCommandArgs, nextCommandHandler: Action, _) = - let textContainer = args.SubjectBuffer.AsTextContainer() - match textContainer with - | null -> () - | _ -> - let mutable workspace = Unchecked.defaultof<_> - if Workspace.TryGetWorkspace(textContainer, &workspace) then - let solution = workspace.CurrentSolution - let documentId = workspace.GetDocumentIdInCurrentContext(textContainer) - match box documentId with - | null -> () - | _ -> - let document = solution.GetDocument(documentId) - async { - try - if document.Project.Language = LanguageNames.FSharp && not document.IsFSharpScript then - let openDocIds = workspace.GetOpenDocumentIds() - let depProjIds = document.Project.GetDependentProjectIds().Add(document.Project.Id) - - let docIdsToReanalyze = - openDocIds - |> Seq.filter (fun x -> - depProjIds.Contains(x.ProjectId) && x <> document.Id && - ( - let doc = solution.GetDocument(x) - match box doc with - | null -> false - | _ -> doc.Project.Language = LanguageNames.FSharp - ) - ) - |> Array.ofSeq - - if docIdsToReanalyze.Length > 0 then - analyzerService.Reanalyze(workspace, documentIds=docIdsToReanalyze) - with - | ex -> logException ex - } - |> Async.Start // fire and forget - - nextCommandHandler.Invoke() - - member _.GetCommandState(_, nextCommandHandler: Func) = - nextCommandHandler.Invoke() \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index 5fafb1a1159..5d156faefb8 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -85,6 +85,15 @@ module private FSharpProjectOptionsHelpers = else hasProjectVersionChanged + type Document with + + member this.ToFSharpDocument() = + let dt = DateTime.UtcNow + let getTimeStamp = fun () -> dt + let getSourceText = fun () -> + this.GetTextAsync().Result.ToFSharpSourceText() + FSharpDocument.Create(this.FilePath, getTimeStamp, getSourceText) + [] type private FSharpProjectOptionsMessage = | TryGetOptionsByDocument of Document * AsyncReplyChannel<(FSharpParsingOptions * FSharpProjectOptions) option> * CancellationToken * userOpName: string @@ -331,6 +340,20 @@ type private FSharpProjectOptionsReactor (workspace: Workspace, settings: Editor cache.TryRemove(projectId) |> ignore return! tryComputeOptions project ct else + + + let projectChanges = project.GetChanges(oldProject) + let changedDocs = projectChanges.GetChangedDocuments() |> Array.ofSeq + + if changedDocs.Length > 0 then + changedDocs + |> Array.iter (fun docId -> + let doc = project.GetDocument(docId) + checkerProvider.Checker.UpdateDocument(projectOptions, doc.ToFSharpDocument(), ct) + ) + + cache.[projectId] <- (project, parsingOptions, projectOptions) + return Some(parsingOptions, projectOptions) } diff --git a/vsintegration/src/FSharp.LanguageService/FSharpSource.fs b/vsintegration/src/FSharp.LanguageService/FSharpSource.fs index b311983e6b4..7a60fcaedda 100644 --- a/vsintegration/src/FSharp.LanguageService/FSharpSource.fs +++ b/vsintegration/src/FSharp.LanguageService/FSharpSource.fs @@ -362,7 +362,7 @@ type internal FSharpSource_DEPRECATED(service:LanguageService_DEPRECATED, textLi let co, _ = { ProjectFileName = fileName + ".dummy.fsproj" ProjectId = None - SourceFiles = [| fileName |] + SourceFiles = [|fileName|] OtherOptions = flags ReferencedProjects = [| |] IsIncompleteTypeCheckEnvironment = true From 0d38e9ef47545e52c98b0482bfc27e92882852ba Mon Sep 17 00:00:00 2001 From: Will Smith Date: Thu, 20 May 2021 13:59:43 -0700 Subject: [PATCH 02/27] update baseline --- src/fsharp/service/FSharpDocument.fs | 12 ++++++------ src/fsharp/service/FSharpDocument.fsi | 5 ++--- src/fsharp/service/IncrementalBuild.fs | 4 ++-- .../SurfaceArea.netstandard.fs | 10 ++++++++++ vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj | 1 - 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/fsharp/service/FSharpDocument.fs b/src/fsharp/service/FSharpDocument.fs index dff768aa0d7..d55cf4cac98 100644 --- a/src/fsharp/service/FSharpDocument.fs +++ b/src/fsharp/service/FSharpDocument.fs @@ -42,7 +42,7 @@ open FSharp.Compiler.TypedTree open FSharp.Compiler.TypedTreeOps [] -type FSharpDocumentText = +type DocumentText = | Stream of Stream | SourceText of ISourceText @@ -60,7 +60,7 @@ type FSharpDocument internal () = abstract TimeStamp : DateTime - abstract GetText : unit -> FSharpDocumentText + abstract GetText : unit -> DocumentText type private FSharpDocumentMemoryMappedFile(filePath: string, timeStamp: DateTime, mmf: MemoryMappedFile) = inherit FSharpDocument() @@ -70,7 +70,7 @@ type private FSharpDocumentMemoryMappedFile(filePath: string, timeStamp: DateTim override _.TimeStamp = timeStamp override _.GetText() = - FSharpDocumentText.Stream(mmf.CreateViewStream() :> Stream) + DocumentText.Stream(mmf.CreateViewStream() :> Stream) type private FSharpDocumentByteArray(filePath: string, timeStamp: DateTime, bytes: byte[]) = inherit FSharpDocument() @@ -80,7 +80,7 @@ type private FSharpDocumentByteArray(filePath: string, timeStamp: DateTime, byte override _.TimeStamp = timeStamp override _.GetText() = - FSharpDocumentText.Stream(new MemoryStream(bytes, 0, bytes.Length, false) :> Stream) + DocumentText.Stream(new MemoryStream(bytes, 0, bytes.Length, false) :> Stream) type private FSharpDocumentFromFile(filePath: string) = inherit FSharpDocument() @@ -89,7 +89,7 @@ type private FSharpDocumentFromFile(filePath: string) = override _.TimeStamp = FileSystem.GetLastWriteTimeShim(filePath) - override _.GetText() = FSharpDocumentText.Stream(File.OpenRead(filePath) :> Stream) + override _.GetText() = DocumentText.Stream(File.OpenRead(filePath) :> Stream) type private FSharpDocumentCustom(filePath: string, getTimeStamp, getSourceText) = inherit FSharpDocument() @@ -99,7 +99,7 @@ type private FSharpDocumentCustom(filePath: string, getTimeStamp, getSourceText) override _.TimeStamp = getTimeStamp() override _.GetText() = - FSharpDocumentText.SourceText(getSourceText()) + DocumentText.SourceText(getSourceText()) type FSharpDocument with diff --git a/src/fsharp/service/FSharpDocument.fsi b/src/fsharp/service/FSharpDocument.fsi index 095e2de6674..063f2c800d0 100644 --- a/src/fsharp/service/FSharpDocument.fsi +++ b/src/fsharp/service/FSharpDocument.fsi @@ -7,8 +7,7 @@ open System.IO open FSharp.Compiler.Text [] -type FSharpDocumentText = - internal +type internal DocumentText = | Stream of Stream | SourceText of ISourceText @@ -21,7 +20,7 @@ type FSharpDocument = abstract TimeStamp : DateTime - abstract GetText : unit -> FSharpDocumentText + abstract internal GetText : unit -> DocumentText static member CreateFromFile : filePath: string -> FSharpDocument diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 8773bfe24c0..9bc1d14634c 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -131,9 +131,9 @@ module IncrementalBuildSyntaxTree = else use text = doc.GetText() match text with - | FSharpDocumentText.Stream(stream) -> + | DocumentText.Stream(stream) -> ParseOneInputStream(tcConfig, lexResourceManager, [], filename, isLastCompiland, errorLogger, (*retryLocked*)false, stream) - | FSharpDocumentText.SourceText(sourceText) -> + | DocumentText.SourceText(sourceText) -> ParseOneInputSourceText(tcConfig, lexResourceManager, [], filename, isLastCompiland, errorLogger, sourceText) fileParsed.Trigger filename diff --git a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs index f8fffc54366..7e2143d444f 100644 --- a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs +++ b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs @@ -2029,10 +2029,20 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearLanguageServiceRootCachesA FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateAll() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateConfiguration(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Void StopBackgroundCompile() +FSharp.Compiler.CodeAnalysis.FSharpChecker: Void UpdateDocument(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, FSharp.Compiler.CodeAnalysis.FSharpDocument, System.Threading.CancellationToken) FSharp.Compiler.CodeAnalysis.FSharpChecker: Void WaitForBackgroundCompile() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void set_ImplicitlyStartBackgroundWork(Boolean) FSharp.Compiler.CodeAnalysis.FSharpChecker: Void set_MaxMemory(Int32) FSharp.Compiler.CodeAnalysis.FSharpChecker: Void set_PauseBeforeBackgroundWork(Int32) +FSharp.Compiler.CodeAnalysis.FSharpDocument +FSharp.Compiler.CodeAnalysis.FSharpDocument: FSharp.Compiler.CodeAnalysis.DocumentText GetText() +FSharp.Compiler.CodeAnalysis.FSharpDocument: FSharp.Compiler.CodeAnalysis.FSharpDocument Create(System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime], Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,FSharp.Compiler.Text.ISourceText]) +FSharp.Compiler.CodeAnalysis.FSharpDocument: FSharp.Compiler.CodeAnalysis.FSharpDocument CreateCopyFromFile(System.String) +FSharp.Compiler.CodeAnalysis.FSharpDocument: FSharp.Compiler.CodeAnalysis.FSharpDocument CreateFromFile(System.String) +FSharp.Compiler.CodeAnalysis.FSharpDocument: System.DateTime TimeStamp +FSharp.Compiler.CodeAnalysis.FSharpDocument: System.DateTime get_TimeStamp() +FSharp.Compiler.CodeAnalysis.FSharpDocument: System.String FilePath +FSharp.Compiler.CodeAnalysis.FSharpDocument: System.String get_FilePath() FSharp.Compiler.CodeAnalysis.FSharpParseFileResults FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsBindingALambdaAtPosition(FSharp.Compiler.Text.Position) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsPosContainedInApplication(FSharp.Compiler.Text.Position) diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index 39f449db8bc..29d12baa94c 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -50,7 +50,6 @@ - From adb3f3d959f313eb28cb2fde107f7bd66b1d0854 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Thu, 20 May 2021 15:35:08 -0700 Subject: [PATCH 03/27] Fixing tests --- src/fsharp/service/FSharpDocument.fs | 3 ++- src/fsharp/service/FSharpDocument.fsi | 1 + src/fsharp/service/IncrementalBuild.fs | 20 +++++++++++--------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/fsharp/service/FSharpDocument.fs b/src/fsharp/service/FSharpDocument.fs index d55cf4cac98..a60834ed538 100644 --- a/src/fsharp/service/FSharpDocument.fs +++ b/src/fsharp/service/FSharpDocument.fs @@ -43,6 +43,7 @@ open FSharp.Compiler.TypedTreeOps [] type DocumentText = + | OnDisk | Stream of Stream | SourceText of ISourceText @@ -89,7 +90,7 @@ type private FSharpDocumentFromFile(filePath: string) = override _.TimeStamp = FileSystem.GetLastWriteTimeShim(filePath) - override _.GetText() = DocumentText.Stream(File.OpenRead(filePath) :> Stream) + override _.GetText() = DocumentText.OnDisk type private FSharpDocumentCustom(filePath: string, getTimeStamp, getSourceText) = inherit FSharpDocument() diff --git a/src/fsharp/service/FSharpDocument.fsi b/src/fsharp/service/FSharpDocument.fsi index 063f2c800d0..da4d44d3e71 100644 --- a/src/fsharp/service/FSharpDocument.fsi +++ b/src/fsharp/service/FSharpDocument.fsi @@ -8,6 +8,7 @@ open FSharp.Compiler.Text [] type internal DocumentText = + | OnDisk | Stream of Stream | SourceText of ISourceText diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 9bc1d14634c..5d5cb2d4228 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -135,6 +135,8 @@ module IncrementalBuildSyntaxTree = ParseOneInputStream(tcConfig, lexResourceManager, [], filename, isLastCompiland, errorLogger, (*retryLocked*)false, stream) | DocumentText.SourceText(sourceText) -> ParseOneInputSourceText(tcConfig, lexResourceManager, [], filename, isLastCompiland, errorLogger, sourceText) + | DocumentText.OnDisk -> + ParseOneInputFile(tcConfig, lexResourceManager, [], filename, isLastCompiland, errorLogger, (*retryLocked*)true) fileParsed.Trigger filename @@ -684,7 +686,7 @@ type IncrementalBuilder(tcGlobals, assemblyName, niceNameGen: NiceNameGenerator, lexResourceManager, - docs: FSharpDocument list, + initialDocs: FSharpDocument list, loadClosureOpt: LoadClosure option, keepAssemblyContents, keepAllBackgroundResolutions, @@ -704,12 +706,12 @@ type IncrementalBuilder(tcGlobals, let defaultPartialTypeChecking = enablePartialTypeChecking // Check for the existence of loaded sources and prepend them to the sources list if present. - let sourceFiles = tcConfig.GetAvailableLoadedSources() @ (docs |>List.map (fun s -> rangeStartup, s.FilePath)) + let sourceFiles = tcConfig.GetAvailableLoadedSources() @ (initialDocs |>List.map (fun s -> rangeStartup, s.FilePath)) // Mark up the source files with an indicator flag indicating if they are the last source file in the project - let sourceFiles = + let sourceFileStates = let flags, isExe = tcConfig.ComputeCanContainEntryPoint(sourceFiles |> List.map snd) - ((docs, flags) ||> List.map2 (fun doc flag -> (rangeStartup, doc, (flag, isExe)))) + ((initialDocs, flags) ||> List.map2 (fun doc flag -> (rangeStartup, doc, (flag, isExe)))) |> Array.ofList let defaultTimeStamp = DateTime.UtcNow @@ -726,7 +728,7 @@ type IncrementalBuilder(tcGlobals, let allDependencies = [| yield! basicDependencies - for (_, f, _) in sourceFiles do + for (_, f, _) in sourceFileStates do yield f.FilePath |] // For scripts, the dependency provider is already available. @@ -949,7 +951,7 @@ type IncrementalBuilder(tcGlobals, // START OF BUILD DESCRIPTION // Inputs - let fileNames = sourceFiles // TODO: This should be an immutable array. + let fileNames = sourceFileStates // TODO: This should be an immutable array. let referencedAssemblies = nonFrameworkAssemblyInputs |> Array.ofList // TODO: This should be an immutable array. let invalidateSlot (state: IncrementalBuilderState) newStamp slot = @@ -1190,8 +1192,8 @@ type IncrementalBuilder(tcGlobals, let currentStamp = state.stampedFileNames.[slot] let stamp = doc.TimeStamp if currentStamp <> stamp then - let (m, _, isLastCompiland) = sourceFiles.[slot] - sourceFiles.[slot] <- (m, doc, isLastCompiland) + let (m, _, isLastCompiland) = sourceFileStates.[slot] + sourceFileStates.[slot] <- (m, doc, isLastCompiland) invalidateSlot state stamp slot |> setCurrentState ctok @@ -1325,7 +1327,7 @@ type IncrementalBuilder(tcGlobals, let syntaxTree = ParseTask results syntaxTree.Parse None - member _.SourceFiles = sourceFiles |> Array.map (fun (_, f, _) -> f.FilePath) |> List.ofArray + member _.SourceFiles = sourceFileStates |> Array.map (fun (_, f, _) -> f.FilePath) |> List.ofArray /// CreateIncrementalBuilder (for background type checking). Note that fsc.fs also /// creates an incremental builder used by the command line compiler. From fbe307b0a83e15837b2347e855081c4ab637115e Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Fri, 21 May 2021 13:47:26 +0200 Subject: [PATCH 04/27] FileSystem: return stream by default, have extension method to convert to ByteMemory when needed. Use FIleSystem shim from FSharpDocument, when opening file (both mmaped and bytearray). --- src/fsharp/CompilerConfig.fs | 2 +- src/fsharp/CompilerImports.fs | 4 +- src/fsharp/CompilerOptions.fs | 2 +- src/fsharp/FxResolver.fs | 6 +- src/fsharp/ParseAndCheckInputs.fs | 2 +- src/fsharp/ScriptClosure.fs | 6 +- src/fsharp/XmlDoc.fs | 2 +- src/fsharp/absil/ilread.fs | 12 +-- src/fsharp/absil/ilsupp.fs | 2 +- src/fsharp/absil/ilwrite.fs | 2 +- src/fsharp/absil/ilwritepdb.fs | 6 +- src/fsharp/fsi/fsi.fs | 2 +- src/fsharp/range.fs | 4 +- src/fsharp/service/FSharpDocument.fs | 32 ++---- src/fsharp/utils/FileSystem.fs | 97 ++++++++++-------- src/fsharp/utils/FileSystem.fsi | 4 +- .../SurfaceArea.netstandard.fs | 4 +- tests/FSharp.Test.Utilities/Compiler.fs | 2 +- tests/fsharp/TypeProviderTests.fs | 1 - .../tools/fsharp41/net45/providerDesigner.dll | Bin 75264 -> 75264 bytes .../fsharp41/net461/providerDesigner.dll | Bin 75264 -> 75264 bytes .../netstandard2.0/providerDesigner.dll | Bin 75264 -> 75264 bytes .../fsharp41/net45/providerDesigner.dll | Bin 75264 -> 75264 bytes .../fsharp41/net461/providerDesigner.dll | Bin 75264 -> 75264 bytes .../netstandard2.0/providerDesigner.dll | Bin 75264 -> 75264 bytes tests/service/EditorTests.fs | 2 +- tests/service/FileSystemTests.fs | 3 +- tests/service/MultiProjectAnalysisTests.fs | 4 +- tests/service/ProjectAnalysisTests.fs | 8 +- 29 files changed, 101 insertions(+), 108 deletions(-) diff --git a/src/fsharp/CompilerConfig.fs b/src/fsharp/CompilerConfig.fs index 7617b082bd2..ad617f54392 100644 --- a/src/fsharp/CompilerConfig.fs +++ b/src/fsharp/CompilerConfig.fs @@ -138,7 +138,7 @@ type VersionFlag = if not(FileSystem.FileExistsShim s) then errorR(Error(FSComp.SR.buildInvalidVersionFile s, rangeStartup)); "0.0.0.0" else - use fs = FileSystem.OpenFileForReadShim(s).AsReadOnlyStream() + use fs = FileSystem.OpenFileForReadShim(s) use is = new StreamReader(fs) is.ReadLine() | VersionNone -> "0.0.0.0" diff --git a/src/fsharp/CompilerImports.fs b/src/fsharp/CompilerImports.fs index a989420947a..1c88a57e6af 100644 --- a/src/fsharp/CompilerImports.fs +++ b/src/fsharp/CompilerImports.fs @@ -690,7 +690,7 @@ type RawFSharpAssemblyDataBackedByFileOnDisk (ilModule: ILModuleDef, ilAssemblyR let sigFileName = Path.ChangeExtension(filename, "sigdata") if not (FileSystem.FileExistsShim sigFileName) then error(Error(FSComp.SR.buildExpectedSigdataFile (FileSystem.GetFullPathShim sigFileName), m)) - [ (ilShortAssemName, fun () -> FileSystem.OpenFileForReadShim(sigFileName, shouldShadowCopy=true).AsReadOnly())] + [ (ilShortAssemName, fun () -> FileSystem.OpenFileForReadShim(sigFileName, shouldShadowCopy=true).AsByteMemory().AsReadOnly())] else sigDataReaders sigDataReaders @@ -706,7 +706,7 @@ type RawFSharpAssemblyDataBackedByFileOnDisk (ilModule: ILModuleDef, ilAssemblyR let optDataFile = Path.ChangeExtension(filename, "optdata") if not (FileSystem.FileExistsShim optDataFile) then error(Error(FSComp.SR.buildExpectedFileAlongSideFSharpCore(optDataFile, FileSystem.GetFullPathShim optDataFile), m)) - [ (ilShortAssemName, (fun () -> FileSystem.OpenFileForReadShim(optDataFile, shouldShadowCopy=true).AsReadOnly()))] + [ (ilShortAssemName, (fun () -> FileSystem.OpenFileForReadShim(optDataFile, shouldShadowCopy=true).AsByteMemory().AsReadOnly()))] else optDataReaders optDataReaders diff --git a/src/fsharp/CompilerOptions.fs b/src/fsharp/CompilerOptions.fs index c40cbf50a07..9f061515607 100644 --- a/src/fsharp/CompilerOptions.fs +++ b/src/fsharp/CompilerOptions.fs @@ -183,7 +183,7 @@ module ResponseFile = | s -> Some (ResponseFileLine.CompilerOptionSpec (s.Trim())) try - use stream = FileSystem.OpenFileForReadShim(path).AsReadOnlyStream() + use stream = FileSystem.OpenFileForReadShim(path) use reader = new StreamReader(stream, true) let data = seq { while not reader.EndOfStream do yield reader.ReadLine () } diff --git a/src/fsharp/FxResolver.fs b/src/fsharp/FxResolver.fs index a6e22e39cc3..997fb933a72 100644 --- a/src/fsharp/FxResolver.fs +++ b/src/fsharp/FxResolver.fs @@ -177,7 +177,7 @@ type internal FxResolver(assumeDotNetFramework: bool, projectDir: string, useSdk | Some dir -> try let dotnetConfigFile = Path.Combine(dir, "dotnet.runtimeconfig.json") - let dotnetConfig = FileSystem.OpenFileForReadShim(dotnetConfigFile).AsStream().ReadAllText() + let dotnetConfig = FileSystem.OpenFileForReadShim(dotnetConfigFile).ReadAllText() let pattern = "\"version\": \"" let startPos = dotnetConfig.IndexOf(pattern, StringComparison.OrdinalIgnoreCase) + pattern.Length let endPos = dotnetConfig.IndexOf("\"", startPos) @@ -322,7 +322,7 @@ type internal FxResolver(assumeDotNetFramework: bool, projectDir: string, useSdk | asm -> let depsJsonPath = Path.ChangeExtension(asm.Location, "deps.json") if FileSystem.FileExistsShim(depsJsonPath) then - FileSystem.OpenFileForReadShim(depsJsonPath).AsReadOnlyStream().ReadAllText() + FileSystem.OpenFileForReadShim(depsJsonPath).ReadAllText() else "" with _ -> @@ -783,7 +783,7 @@ type internal FxResolver(assumeDotNetFramework: bool, projectDir: string, useSdk match sdkDir with | Some dir -> let dotnetConfigFile = Path.Combine(dir, "dotnet.runtimeconfig.json") - let dotnetConfig = FileSystem.OpenFileForReadShim(dotnetConfigFile).AsReadOnlyStream().ReadAllText() + let dotnetConfig = FileSystem.OpenFileForReadShim(dotnetConfigFile).ReadAllText() let pattern = "\"tfm\": \"" let startPos = dotnetConfig.IndexOf(pattern, StringComparison.OrdinalIgnoreCase) + pattern.Length let endPos = dotnetConfig.IndexOf("\"", startPos) diff --git a/src/fsharp/ParseAndCheckInputs.fs b/src/fsharp/ParseAndCheckInputs.fs index 6c810357d1b..ed47d81fa7b 100644 --- a/src/fsharp/ParseAndCheckInputs.fs +++ b/src/fsharp/ParseAndCheckInputs.fs @@ -420,7 +420,7 @@ let parseInputSourceTextAux (tcConfig: TcConfig, lexResourceManager, conditional let parseInputFileAux (tcConfig: TcConfig, lexResourceManager, conditionalCompilationDefines, filename, isLastCompiland, errorLogger, retryLocked) = // Get a stream reader for the file - use fileStream = FileSystem.OpenFileForReadShim(filename).AsStream() + use fileStream = FileSystem.OpenFileForReadShim(filename) use reader = fileStream.GetReader(tcConfig.inputCodePage, retryLocked) // Set up the LexBuffer for the file diff --git a/src/fsharp/ScriptClosure.fs b/src/fsharp/ScriptClosure.fs index 03498b92067..c4f90e2008b 100644 --- a/src/fsharp/ScriptClosure.fs +++ b/src/fsharp/ScriptClosure.fs @@ -188,7 +188,7 @@ module ScriptPreprocessClosure = let ClosureSourceOfFilename(filename, m, inputCodePage, parseRequired) = try let filename = FileSystem.GetFullPathShim filename - use stream = FileSystem.OpenFileForReadShim(filename).AsReadOnlyStream() + use stream = FileSystem.OpenFileForReadShim(filename) use reader = match inputCodePage with | None -> new StreamReader(stream, true) @@ -270,7 +270,7 @@ module ScriptPreprocessClosure = for line in result.StdOut do Console.Out.WriteLine(line) for line in result.StdError do Console.Error.WriteLine(line) - packageReferences.[m] <- [ for script in result.SourceFiles do yield! FileSystem.OpenFileForReadShim(script).AsReadOnlyStream().ReadLines() ] + packageReferences.[m] <- [ for script in result.SourceFiles do yield! FileSystem.OpenFileForReadShim(script).ReadLines() ] if not (Seq.isEmpty result.Roots) then let tcConfigB = tcConfig.CloneToBuilder() for folder in result.Roots do @@ -285,7 +285,7 @@ module ScriptPreprocessClosure = tcConfig <- TcConfig.Create(tcConfigB, validate = false) for script in result.SourceFiles do - let scriptText = FileSystem.OpenFileForReadShim(script).AsReadOnlyStream().ReadAllText() + let scriptText = FileSystem.OpenFileForReadShim(script).ReadAllText() loadScripts.Add script |> ignore let iSourceText = SourceText.ofString scriptText yield! loop (ClosureSource(script, m, iSourceText, true)) diff --git a/src/fsharp/XmlDoc.fs b/src/fsharp/XmlDoc.fs index a6d719a31da..3236e7c2f66 100644 --- a/src/fsharp/XmlDoc.fs +++ b/src/fsharp/XmlDoc.fs @@ -265,7 +265,7 @@ type XmlDocumentationInfo private (tryGetXmlDocument: unit -> XmlDocument option | Some doc -> Some doc | _ -> let doc = XmlDocument() - use xmlStream = FileSystem.OpenFileForReadShim(xmlFileName).AsStream() + use xmlStream = FileSystem.OpenFileForReadShim(xmlFileName) doc.Load(xmlStream) cache.Put((), cacheKey, doc) Some doc diff --git a/src/fsharp/absil/ilread.fs b/src/fsharp/absil/ilread.fs index dab6faf14ea..f194be26c00 100644 --- a/src/fsharp/absil/ilread.fs +++ b/src/fsharp/absil/ilread.fs @@ -207,7 +207,7 @@ type WeakByteFile(fileName: string, chunk: (int * int) option) = let bytes = match chunk with | None -> FileSystem.OpenFileForReadShim(fileName).ReadAllBytes() - | Some(start, length) -> FileSystem.OpenFileForReadShim(fileName).ReadBytes(start, length) + | Some(start, length) -> FileSystem.OpenFileForReadShim(fileName).AsByteMemory().ReadBytes(start, length) tg <- bytes @@ -3907,12 +3907,12 @@ let createByteFileChunk opts fileName chunk = let bytes = match chunk with | None -> FileSystem.OpenFileForReadShim(fileName).ReadAllBytes() - | Some(start, length) -> FileSystem.OpenFileForReadShim(fileName).ReadBytes(start, length) + | Some(start, length) -> FileSystem.OpenFileForReadShim(fileName).AsByteMemory().ReadBytes(start, length) ByteFile(fileName, bytes) :> BinaryFile let createMemoryMapFile fileName = - let byteMem = FileSystem.OpenFileForReadShim(fileName, useMemoryMappedFile = true) + let stream = FileSystem.OpenFileForReadShim(fileName, useMemoryMappedFile = true) let safeHolder = { new obj() with @@ -3921,12 +3921,12 @@ let createMemoryMapFile fileName = interface IDisposable with member x.Dispose() = GC.SuppressFinalize x - byteMem.AsStream().Dispose() + stream.Dispose() stats.memoryMapFileClosedCount <- stats.memoryMapFileClosedCount + 1 } stats.memoryMapFileOpenedCount <- stats.memoryMapFileOpenedCount + 1 - safeHolder, RawMemoryFile(fileName, safeHolder, byteMem) :> BinaryFile + safeHolder, RawMemoryFile(fileName, safeHolder, stream.AsByteMemory()) :> BinaryFile let OpenILModuleReaderFromBytes fileName assemblyContents options = let pefile = ByteFile(fileName, assemblyContents) :> BinaryFile @@ -4037,7 +4037,7 @@ let OpenILModuleReader fileName opts = // still use an in-memory ByteFile let pefile = if not runningOnMono && (alwaysMemoryMapFSC || stableFileHeuristicApplies fullPath) then - ByteMemoryFile(fullPath, FileSystem.OpenFileForReadShim(fullPath)) :> BinaryFile + ByteMemoryFile(fullPath, FileSystem.OpenFileForReadShim(fullPath).AsByteMemory()) :> BinaryFile else createByteFileChunk opts fullPath None diff --git a/src/fsharp/absil/ilsupp.fs b/src/fsharp/absil/ilsupp.fs index e8b0d84d103..fa77befa8d3 100644 --- a/src/fsharp/absil/ilsupp.fs +++ b/src/fsharp/absil/ilsupp.fs @@ -931,7 +931,7 @@ let hashSizeOfMD5 = 16 // In this case, catch the failure, and not set a checksum. let internal setCheckSum (url: string, writer: ISymUnmanagedDocumentWriter) = try - use file = FileSystem.OpenFileForReadShim(url).AsReadOnlyStream() + use file = FileSystem.OpenFileForReadShim(url) use md5 = System.Security.Cryptography.MD5.Create() let checkSum = md5.ComputeHash file if (checkSum.Length = hashSizeOfMD5) then diff --git a/src/fsharp/absil/ilwrite.fs b/src/fsharp/absil/ilwrite.fs index ee600b084ea..502a594ade5 100644 --- a/src/fsharp/absil/ilwrite.fs +++ b/src/fsharp/absil/ilwrite.fs @@ -3720,7 +3720,7 @@ and writeBinaryAndReportMappingsAux (stream: Stream, leaveStreamOpen: bool, resources |> List.map (function | ILNativeResource.Out bytes -> bytes | ILNativeResource.In (fileName, linkedResourceBase, start, len) -> - let linkedResource = FileSystem.OpenFileForReadShim(fileName).ReadBytes(start, len) + let linkedResource = FileSystem.OpenFileForReadShim(fileName).AsByteMemory().ReadBytes(start, len) unlinkResource linkedResourceBase linkedResource) begin diff --git a/src/fsharp/absil/ilwritepdb.fs b/src/fsharp/absil/ilwritepdb.fs index f0648d3caf4..78d39ba641c 100644 --- a/src/fsharp/absil/ilwritepdb.fs +++ b/src/fsharp/absil/ilwritepdb.fs @@ -135,7 +135,7 @@ let guidSha2 = Guid("8829d00f-11b8-4213-878b-770e8597ac16") let checkSum (url: string) (checksumAlgorithm: HashAlgorithm) = try - use file = FileSystem.OpenFileForReadShim(url).AsReadOnlyStream() + use file = FileSystem.OpenFileForReadShim(url) let guid, alg = match checksumAlgorithm with | HashAlgorithm.Sha1 -> guidSha1, System.Security.Cryptography.SHA1.Create() :> System.Security.Cryptography.HashAlgorithm @@ -310,7 +310,7 @@ let generatePortablePdb (embedAllSource: bool) (embedSourceList: string list) (s if not embedAllSource && not isInList || not (FileSystem.FileExistsShim file) then None else - use stream = FileSystem.OpenFileForReadShim(file).AsReadOnlyStream() + use stream = FileSystem.OpenFileForReadShim(file) let length64 = stream.Length if length64 > int64 (Int32.MaxValue) then raise (new IOException("File is too long")) @@ -355,7 +355,7 @@ let generatePortablePdb (embedAllSource: bool) (embedSourceList: string list) (s index.Add(doc.File, handle) if not (String.IsNullOrWhiteSpace sourceLink) then - let fs = FileSystem.OpenFileForReadShim(sourceLink).AsReadOnlyStream() + let fs = FileSystem.OpenFileForReadShim(sourceLink) let ms = new MemoryStream() fs.CopyTo ms metadata.AddCustomDebugInformation( diff --git a/src/fsharp/fsi/fsi.fs b/src/fsharp/fsi/fsi.fs index 50b896385f5..17bd5729cb4 100644 --- a/src/fsharp/fsi/fsi.fs +++ b/src/fsharp/fsi/fsi.fs @@ -2472,7 +2472,7 @@ type internal FsiInteractionProcessor // An included script file may contain maybe several interaction blocks. // We repeatedly parse and process these, until an error occurs. - use fileStream = FileSystem.OpenFileForReadShim(sourceFile).AsStream() + use fileStream = FileSystem.OpenFileForReadShim(sourceFile) use reader = fileStream.GetReader(tcConfigB.inputCodePage, false) let tokenizer = fsiStdinLexerProvider.CreateIncludedScriptLexer (sourceFile, reader, errorLogger) diff --git a/src/fsharp/range.fs b/src/fsharp/range.fs index 48f85e53f36..690420ba7e5 100755 --- a/src/fsharp/range.fs +++ b/src/fsharp/range.fs @@ -259,7 +259,7 @@ type Range(code1:int64, code2: int64) = if FileSystem.IsInvalidPathShim r.FileName then "path invalid: " + r.FileName elif not (FileSystem.FileExistsShim r.FileName) then "non existing file: " + r.FileName else - FileSystem.OpenFileForReadShim(r.FileName).AsStream().ReadLines() + FileSystem.OpenFileForReadShim(r.FileName).ReadLines() |> Seq.skip (r.StartLine - 1) |> Seq.take (r.EndLine - r.StartLine + 1) |> String.concat "\n" @@ -391,7 +391,7 @@ module Range = let mkFirstLineOfFile (file: string) = try - let lines = FileSystem.OpenFileForReadShim(file).AsStream().ReadLines() |> Seq.indexed + let lines = FileSystem.OpenFileForReadShim(file).ReadLines() |> Seq.indexed let nonWhiteLine = lines |> Seq.tryFind (fun (_,s) -> not (String.IsNullOrWhiteSpace s)) match nonWhiteLine with | Some (i,s) -> mkRange file (mkPos (i+1) 0) (mkPos (i+1) s.Length) diff --git a/src/fsharp/service/FSharpDocument.fs b/src/fsharp/service/FSharpDocument.fs index a60834ed538..e2046c34fc1 100644 --- a/src/fsharp/service/FSharpDocument.fs +++ b/src/fsharp/service/FSharpDocument.fs @@ -63,7 +63,7 @@ type FSharpDocument internal () = abstract GetText : unit -> DocumentText -type private FSharpDocumentMemoryMappedFile(filePath: string, timeStamp: DateTime, mmf: MemoryMappedFile) = +type private FSharpDocumentMemoryMappedFile(filePath: string, timeStamp: DateTime, openStream: unit -> Stream) = inherit FSharpDocument() override _.FilePath = filePath @@ -71,7 +71,7 @@ type private FSharpDocumentMemoryMappedFile(filePath: string, timeStamp: DateTim override _.TimeStamp = timeStamp override _.GetText() = - DocumentText.Stream(mmf.CreateViewStream() :> Stream) + openStream () |> DocumentText.Stream type private FSharpDocumentByteArray(filePath: string, timeStamp: DateTime, bytes: byte[]) = inherit FSharpDocument() @@ -111,31 +111,15 @@ type FSharpDocument with FSharpDocumentFromFile(filePath) :> FSharpDocument static member CreateCopyFromFile(filePath: string) = - let fileMode = FileMode.Open - let fileAccess = FileAccess.Read - let fileShare = FileShare.Delete ||| FileShare.ReadWrite - let timeStamp = FileSystem.GetLastWriteTimeShim(filePath) - // We want to use mmaped files only when: - // - Opening large binary files (no need to use for source or resource files really) - // - Running on mono, since its MemoryMappedFile implementation throws when "mapName" is not provided (is null). - // (See: https://github.com/mono/mono/issues/10245) + // We want to use mmaped documents only when + // not running on mono, since its MemoryMappedFile implementation throws when "mapName" is not provided (is null), (see: https://github.com/mono/mono/issues/10245) if runningOnMono then - let bytes = File.ReadAllBytes filePath + let bytes = FileSystem.OpenFileForReadShim(filePath, useMemoryMappedFile = false).ReadAllBytes() FSharpDocumentByteArray(filePath, timeStamp, bytes) :> FSharpDocument else - let fileStream = new FileStream(filePath, fileMode, fileAccess, fileShare) - let length = fileStream.Length - let mmf = - MemoryMappedFile.CreateNew( - null, - length, - MemoryMappedFileAccess.Read, - MemoryMappedFileOptions.None, - HandleInheritability.None) - use stream = mmf.CreateViewStream(0L, length, MemoryMappedFileAccess.Read) - fileStream.CopyTo(stream) - fileStream.Dispose() - FSharpDocumentMemoryMappedFile(filePath, timeStamp, mmf) :> FSharpDocument + let openStream = fun () -> + FileSystem.OpenFileForReadShim(filePath, useMemoryMappedFile = true, shouldShadowCopy = true) + FSharpDocumentMemoryMappedFile(filePath, timeStamp, openStream) :> FSharpDocument diff --git a/src/fsharp/utils/FileSystem.fs b/src/fsharp/utils/FileSystem.fs index 81f31d5dc4f..478acff5cd0 100644 --- a/src/fsharp/utils/FileSystem.fs +++ b/src/fsharp/utils/FileSystem.fs @@ -372,7 +372,7 @@ type DefaultAssemblyLoader() = [] type IFileSystem = abstract AssemblyLoader: IAssemblyLoader - abstract OpenFileForReadShim: filePath: string * ?useMemoryMappedFile: bool * ?shouldShadowCopy: bool -> ByteMemory + abstract OpenFileForReadShim: filePath: string * ?useMemoryMappedFile: bool * ?shouldShadowCopy: bool -> Stream abstract OpenFileForWriteShim: filePath: string * ?fileMode: FileMode * ?fileAccess: FileAccess * ?fileShare: FileShare -> Stream abstract GetFullPathShim: fileName: string -> string abstract GetFullFilePathInDirectoryShim: dir: string -> fileName: string -> string @@ -398,7 +398,7 @@ type DefaultFileSystem() as this = interface IFileSystem with member _.AssemblyLoader = DefaultAssemblyLoader() :> IAssemblyLoader - member this.OpenFileForReadShim(filePath: string, ?useMemoryMappedFile: bool, ?shouldShadowCopy: bool) : ByteMemory = + member this.OpenFileForReadShim(filePath: string, ?useMemoryMappedFile: bool, ?shouldShadowCopy: bool) : Stream = let fileMode = FileMode.Open let fileAccess = FileAccess.Read let fileShare = FileShare.Delete ||| FileShare.ReadWrite @@ -413,53 +413,33 @@ type DefaultFileSystem() as this = // - Running on mono, since its MemoryMappedFile implementation throws when "mapName" is not provided (is null). // (See: https://github.com/mono/mono/issues/10245) if runningOnMono || (not useMemoryMappedFile) then - let bytes = File.ReadAllBytes filePath - let byteArrayMemory = if bytes.Length = 0 then ByteArrayMemory([||], 0, 0) else ByteArrayMemory(bytes, 0, bytes.Length) - byteArrayMemory :> ByteMemory + fileStream :> Stream else - let mmf, viewStream, length = - let mmf = - if shouldShadowCopy then - let mmf = - MemoryMappedFile.CreateNew( - null, - length, - MemoryMappedFileAccess.Read, - MemoryMappedFileOptions.None, - HandleInheritability.None) - use stream = mmf.CreateViewStream(0L, length, MemoryMappedFileAccess.Read) - fileStream.CopyTo(stream) - fileStream.Dispose() - mmf - else - MemoryMappedFile.CreateFromFile( - fileStream, + let mmf = + if shouldShadowCopy then + let mmf = + MemoryMappedFile.CreateNew( null, length, MemoryMappedFileAccess.Read, - HandleInheritability.None, - leaveOpen=false) - mmf, mmf.CreateViewStream(0L, length, MemoryMappedFileAccess.Read), length - - if not viewStream.CanRead then + MemoryMappedFileOptions.None, + HandleInheritability.None) + use stream = mmf.CreateViewStream(0L, length, MemoryMappedFileAccess.Read) + fileStream.CopyTo(stream) + fileStream.Dispose() + mmf + else + MemoryMappedFile.CreateFromFile( + fileStream, + null, + length, + MemoryMappedFileAccess.Read, + HandleInheritability.None, + leaveOpen=false) + let stream = mmf.CreateViewStream(0L, length, MemoryMappedFileAccess.Read) + if not stream.CanRead then invalidOp "Cannot read file" - - let safeHolder = - { new obj() with - override x.Finalize() = - (x :?> IDisposable).Dispose() - interface IDisposable with - member x.Dispose() = - GC.SuppressFinalize x - viewStream.Dispose() - mmf.SafeMemoryMappedFileHandle.Close() - mmf.Dispose() - fileStream.Dispose() } - - RawByteMemory( - NativePtr.ofNativeInt (viewStream.SafeMemoryMappedViewHandle.DangerousGetHandle()), - int length, - safeHolder) :> ByteMemory + stream :> Stream member _.OpenFileForWriteShim(filePath: string, ?fileMode: FileMode, ?fileAccess: FileAccess, ?fileShare: FileShare) : Stream = let fileMode = defaultArg fileMode FileMode.OpenOrCreate @@ -560,7 +540,7 @@ module public StreamExtensions = member s.Write (data: 'a) : unit = use sw = s.GetWriter() sw.Write(data) - + member s.GetReader(codePage: int option, ?retryLocked: bool) = let retryLocked = defaultArg retryLocked false let retryDelayMilliseconds = 50 @@ -588,6 +568,11 @@ module public StreamExtensions = reraise() getSource 0 + member s.ReadAllBytes() = + use reader = new BinaryReader(s) + let count = (int s.Length) + reader.ReadBytes(count) + member s.ReadAllText(?encoding: Encoding) = let encoding = defaultArg encoding Encoding.UTF8 use sr = new StreamReader(s, encoding, true) @@ -604,6 +589,28 @@ module public StreamExtensions = let encoding = defaultArg encoding Encoding.UTF8 s.ReadLines(encoding) |> Seq.toArray + member s.AsByteMemory() : ByteMemory = + match s with + | :? MemoryMappedViewStream as ums -> + let safeHolder = + { new obj() with + override x.Finalize() = + (x :?> IDisposable).Dispose() + interface IDisposable with + member x.Dispose() = + GC.SuppressFinalize x + ums.Dispose() } + let length = ums.Length + RawByteMemory( + NativePtr.ofNativeInt (ums.SafeMemoryMappedViewHandle.DangerousGetHandle()), + int length, + safeHolder) :> ByteMemory + + | _ -> + let bytes = s.ReadAllBytes() + let byteArrayMemory = if bytes.Length = 0 then ByteArrayMemory([||], 0, 0) else ByteArrayMemory(bytes, 0, bytes.Length) + byteArrayMemory :> ByteMemory + [] module public FileSystemAutoOpens = /// The global hook into the file system diff --git a/src/fsharp/utils/FileSystem.fsi b/src/fsharp/utils/FileSystem.fsi index 58a426091d8..b79396d5c8a 100644 --- a/src/fsharp/utils/FileSystem.fsi +++ b/src/fsharp/utils/FileSystem.fsi @@ -141,7 +141,7 @@ type DefaultAssemblyLoader = type public IFileSystem = /// Open the file for read, returns ByteMemory, uses either FileStream (for smaller files) or MemoryMappedFile (for potentially big files, such as dlls). - abstract member OpenFileForReadShim: filePath: string * ?useMemoryMappedFile: bool * ?shouldShadowCopy: bool -> ByteMemory + abstract member OpenFileForReadShim: filePath: string * ?useMemoryMappedFile: bool * ?shouldShadowCopy: bool -> Stream /// Open the file for writing. Returns a Stream. abstract member OpenFileForWriteShim: filePath: string * ?fileMode: FileMode * ?fileAccess: FileAccess * ?fileShare: FileShare -> Stream @@ -219,9 +219,11 @@ module public StreamExtensions = member WriteAllLines : contents: string seq * ?encoding: Encoding -> unit member Write : data: ^a -> unit member GetReader : codePage: int option * ?retryLocked: bool -> StreamReader + member ReadAllBytes : unit -> byte[] member ReadAlLText : ?encoding: Encoding -> string member ReadLines : ?encoding: Encoding -> string seq member ReadAllLines : ?encoding: Encoding -> string array + member AsByteMemory : unit -> ByteMemory [] module public FileSystemAutoOpens = diff --git a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs index 7e2143d444f..19749f250cc 100644 --- a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs +++ b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs @@ -4067,7 +4067,6 @@ FSharp.Compiler.IO.IFileSystem: Boolean FileExistsShim(System.String) FSharp.Compiler.IO.IFileSystem: Boolean IsInvalidPathShim(System.String) FSharp.Compiler.IO.IFileSystem: Boolean IsPathRootedShim(System.String) FSharp.Compiler.IO.IFileSystem: Boolean IsStableFileHeuristic(System.String) -FSharp.Compiler.IO.IFileSystem: FSharp.Compiler.IO.ByteMemory OpenFileForReadShim(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) FSharp.Compiler.IO.IFileSystem: FSharp.Compiler.IO.IAssemblyLoader AssemblyLoader FSharp.Compiler.IO.IFileSystem: FSharp.Compiler.IO.IAssemblyLoader get_AssemblyLoader() FSharp.Compiler.IO.IFileSystem: System.Collections.Generic.IEnumerable`1[System.String] EnumerateDirectoriesShim(System.String) @@ -4075,6 +4074,7 @@ FSharp.Compiler.IO.IFileSystem: System.Collections.Generic.IEnumerable`1[System. FSharp.Compiler.IO.IFileSystem: System.DateTime GetCreationTimeShim(System.String) FSharp.Compiler.IO.IFileSystem: System.DateTime GetLastWriteTimeShim(System.String) FSharp.Compiler.IO.IFileSystem: System.IO.DirectoryInfo DirectoryCreateShim(System.String) +FSharp.Compiler.IO.IFileSystem: System.IO.Stream OpenFileForReadShim(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) FSharp.Compiler.IO.IFileSystem: System.IO.Stream OpenFileForWriteShim(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.IO.FileMode], Microsoft.FSharp.Core.FSharpOption`1[System.IO.FileAccess], Microsoft.FSharp.Core.FSharpOption`1[System.IO.FileShare]) FSharp.Compiler.IO.IFileSystem: System.String GetDirectoryNameShim(System.String) FSharp.Compiler.IO.IFileSystem: System.String GetFullFilePathInDirectoryShim(System.String, System.String) @@ -4139,6 +4139,8 @@ FSharp.Compiler.IO.SafeUnmanagedMemoryStream: Void .ctor(Byte*, Int64, Int64, Sy FSharp.Compiler.IO.SafeUnmanagedMemoryStream: Void .ctor(Byte*, Int64, System.Object) FSharp.Compiler.IO.SafeUnmanagedMemoryStream: Void Dispose(Boolean) FSharp.Compiler.IO.StreamExtensions +FSharp.Compiler.IO.StreamExtensions: Byte[] Stream.ReadAllBytes(System.IO.Stream) +FSharp.Compiler.IO.StreamExtensions: FSharp.Compiler.IO.ByteMemory Stream.AsByteMemory(System.IO.Stream) FSharp.Compiler.IO.StreamExtensions: System.Collections.Generic.IEnumerable`1[System.String] Stream.ReadLines(System.IO.Stream, Microsoft.FSharp.Core.FSharpOption`1[System.Text.Encoding]) FSharp.Compiler.IO.StreamExtensions: System.IO.StreamReader Stream.GetReader(System.IO.Stream, Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) FSharp.Compiler.IO.StreamExtensions: System.IO.TextWriter Stream.GetWriter(System.IO.Stream, Microsoft.FSharp.Core.FSharpOption`1[System.Text.Encoding]) diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index 0233c3f3954..4a56eac0263 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -102,7 +102,7 @@ module rec Compiler = let private getSource (src: TestType) : string = match src with | Text t -> t - | Path p -> FileSystem.OpenFileForReadShim(p).AsStream().ReadAllText() + | Path p -> FileSystem.OpenFileForReadShim(p).ReadAllText() let private fsFromString (source: string) (kind: SourceKind) : FSharpCompilationSource = match source with diff --git a/tests/fsharp/TypeProviderTests.fs b/tests/fsharp/TypeProviderTests.fs index df743eea3c5..b129bea904e 100644 --- a/tests/fsharp/TypeProviderTests.fs +++ b/tests/fsharp/TypeProviderTests.fs @@ -244,7 +244,6 @@ let ``negative type provider tests`` (name:string) = let dirp = (dir |> Commands.pathAddBackslash) do FileSystem.OpenFileForReadShim(sprintf "%s%s.%sbslpp" dirp name pref) - .AsStream() .ReadAllText() .Replace("", getfullpath cfg (sprintf "provider_%s.dll" name)) .Replace("",dirp) diff --git a/tests/fsharp/tools/fsharp41/net45/providerDesigner.dll b/tests/fsharp/tools/fsharp41/net45/providerDesigner.dll index cb79536e157facb1e7eb4a0b37657130a7e2937e..3d36c659c05eb02a779fb60e83e9263ad704bed6 100644 GIT binary patch delta 246 zcmZoT!_shuWkLtDP5<(V-Lj1PCaw%;Oq$HdSkHK5awCwOH<^)1ndwr&^o@#)TAKry zCS@~vY<`@(Q->d@Bf;co$-?EX%*{ad=GC*FiZVJ*p12`dI>p&4CbT%Us5qvqDzPXt zCOJPPH72b%BeAGp@`UYXn=Lnbut{g(R=_wgV9j)YMMi1ndG?GG6@|AqC^9B8aT+i(@Brlx*l!1V;~OIYrH)$W delta 215 zcmZoT!_shuWkLt@pIJ;3yJZ>YOd@Bf;co$-?EX%*{ad=GC*FiZaShp12{IKf>86COfq(Gp{HyuQ)$< zGyldqHvTwN!O8p^%{JfLew3Tv8AHN!^XJ1)!?s_MV(eq%&9Y}?fPtRr`HGCv%yssR i6BUKGFHmGmWSag!kx_xufRTX*sAz%xcAz`IF#-U}6io8~ diff --git a/tests/fsharp/tools/fsharp41/net461/providerDesigner.dll b/tests/fsharp/tools/fsharp41/net461/providerDesigner.dll index 1b581c260dc417aad57c74957f547cb50a9ec326..a1196e9ddea2568e00a100233df59dc8ea460e26 100644 GIT binary patch delta 246 zcmZoT!_shuWkLtDX8-bu-Lj1PCaw%;Oq$HdSkHK5awCwOH<^)1ndwr&^o@#)TAKry zCS@~vY<`@(Q->d@Bf;co$-?EX%*{ad=GC*FiZVJ*p12`dI>p&4CbT%Us5qvqDzPXt zCOJPPH72b%BeAGp@`UYXn=Lnbut{g(R=_wgV9j)YMMi1ndG?GG6@|AqC^9B8aT+i(@Brlx*l!1V;~OIYc^O)* delta 215 zcmZoT!_shuWkLt@k6BC;yJZ>YOd@Bf;co$-?EX%*{ad=GC*FiZaShp12{IKf>86COfq(Gp{HyuQ)$< zGyldqHvTwN!O8p^%{JfLew3Tv8AHN!^XJ1)!?s_MV(eq%&9Y}?fPtRr`HGCv%yssR i6BUKGFHmGmWSag!kx_xufRTX*sAz%xcAz`IF#-U>EKKJB diff --git a/tests/fsharp/tools/fsharp41/netstandard2.0/providerDesigner.dll b/tests/fsharp/tools/fsharp41/netstandard2.0/providerDesigner.dll index 1b6a4ded584d05b043fcc4b3892ec7b76d92b6a6..d304e2a81c682d28f9f4afcf65c3f8f71806d12d 100644 GIT binary patch delta 246 zcmZoT!_shuWkLtDTmSNj-Lj1PCaw%;Oq$HdSkHK5awCwOH<^)1ndwr&^o@#)TAKry zCS@~vY<`@(Q->d@Bf;co$-?EX%*{ad=GC*FiZVJ*p12`dI>p&4CbT%Us5qvqDzPXt zCOJPPH72b%BeAGp@`UYXn=Lnbut{g(R=_wgV9j)YMMi1ndG?GG6@|AqC^9B8aT+i(@Brlx*l!1V;~OIYy!2ZH delta 215 zcmZoT!_shuWkLt@-&sr(yJZ>YOd@Bf;co$-?EX%*{ad=GC*FiZaShp12{IKf>86COfq(Gp{HyuQ)$< zGyldqHvTwN!O8p^%{JfLew3Tv8AHN!^XJ1)!?s_MV(eq%&9Y}?fPtRr`HGCv%yssR i6BUKGFHmGmWSag!kx_xufRTX*sAz%xcAz`IF#-V04ovm{ diff --git a/tests/fsharp/typeProviders/fsharp41/net45/providerDesigner.dll b/tests/fsharp/typeProviders/fsharp41/net45/providerDesigner.dll index c033697fd002c06e56faa82597d0104b3d1016a3..76adae056cb1751ecbbb4d2bd1c017a3507fb696 100644 GIT binary patch delta 250 zcmZoT!_shuWkLsYLI3iJ-Lj1LCaw%;teVWoSkL%mawCx3Hkpx0ndw!*^oxp&TAKry zCS@}wY<`@(Q->d@Bf;co$-?EX%*{ad=GC*FiZVt{p12`dI>*^6CbT%Us5qvqDzPXt zCOJPPH72b%BeAGp@~XqOn;kZKvPl;aP;_dK_+=@1@I;<4F{el!@7n{%`dqxHr pI09lTOjHzO+Gan!P>xY#`wT@!PbN+SMg|_Bng{mVfnNE>2mn~}T^s-a delta 218 zcmZoT!_shuWkLrt>ujcp-Lj1PCaw%;jGD~ISkHK5awCwOHkpx0ndwx)^pA>+TAKry zCS@}^Y<`@(Q->d@Bf;co$-?EX%*{ad=GC*FiZYr`p12{IKf&25COfq(Gp{HyuQ)$< z@`mkpn|(KWu<@s%N^IV+otcN<8&wdf+IBPdk*8tX-$*g`u?aQVGcv%y5)fNqqM{hn kH2dijd@Bf;co$-?EX%*{ad=GC*FiZVt{p12`dI>*^6CbT%Us5qvqDzPXt zCOJPPH72b%BeAGp@~XqOn;kZKvPl;aP;_dK_+=@1@I;<4F{el!@7n{%`dqxHr pI09lTOjHzO+Gan!P>xY#`wT@!PbN+SMg|_Bng{mVfnNE>2mmQIT;Koz delta 218 zcmZoT!_shuWkLrt<7}ph-Lj1PCaw%;jGD~ISkHK5awCwOHkpx0ndwx)^pA>+TAKry zCS@}^Y<`@(Q->d@Bf;co$-?EX%*{ad=GC*FiZYr`p12{IKf&25COfq(Gp{HyuQ)$< z@`mkpn|(KWu<@s%N^IV+otcN<8&wdf+IBPdk*8tX-$*g`u?aQVGcv%y5)fNqqM{hn kH2dijd@Bf;co$-?EX%*{ad=GC*FiZVt{p12`dI>*^6CbT%Us5qvqDzPXt zCOJPPH72b%BeAGp@~XqOn;kZKvPl;aP;_dK_+=@1@I;<4F{el!@7n{%`dqxHr pI09lTOjHzO+Gan!P>xY#`wT@!PbN+SMg|_Bng{mVfnNE>2mo$vT{i## delta 218 zcmZoT!_shuWkLrt+ia$Z-Lj1PCaw%;jGD~ISkHK5awCwOHkpx0ndwx)^pA>+TAKry zCS@}^Y<`@(Q->d@Bf;co$-?EX%*{ad=GC*FiZYr`p12{IKf&25COfq(Gp{HyuQ)$< z@`mkpn|(KWu<@s%N^IV+otcN<8&wdf+IBPdk*8tX-$*g`u?aQVGcv%y5)fNqqM{hn kH2dij( (*$*) ) // TEST: no assert on Ctrl-sp """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileLines1 = FileSystem.OpenFileForReadShim(fileName1).AsStream().ReadLines() + let fileLines1 = FileSystem.OpenFileForReadShim(fileName1).ReadLines() let fileNames = [fileName1] let args = Array.append (mkProjectCommandLineArgs (dllName, fileNames)) [| "-r:" + PathRelativeToTestAssembly(@"DummyProviderForLanguageServiceTesting.dll") |] diff --git a/tests/service/FileSystemTests.fs b/tests/service/FileSystemTests.fs index 98396fd2c75..1dce85cd74c 100644 --- a/tests/service/FileSystemTests.fs +++ b/tests/service/FileSystemTests.fs @@ -42,8 +42,7 @@ let B = File1.A + File1.A""" let useMemoryMappedFile = defaultArg useMemoryMappedFile false match files.TryGetValue filePath with | true, text -> - let bytes = Encoding.UTF8.GetBytes(text) - ByteArrayMemory(bytes, 0, bytes.Length) :> ByteMemory + new MemoryStream(Encoding.UTF8.GetBytes(text)) :> Stream | _ -> defaultFileSystem.OpenFileForReadShim(filePath, useMemoryMappedFile, shouldShadowCopy) member _.OpenFileForWriteShim(filePath, ?fileMode: FileMode, ?fileAccess: FileAccess, ?fileShare: FileShare) = diff --git a/tests/service/MultiProjectAnalysisTests.fs b/tests/service/MultiProjectAnalysisTests.fs index 5363ceed00b..d8d41480664 100644 --- a/tests/service/MultiProjectAnalysisTests.fs +++ b/tests/service/MultiProjectAnalysisTests.fs @@ -885,7 +885,7 @@ let ``Type provider project references should not throw exceptions`` () = //printfn "options: %A" options let fileName = __SOURCE_DIRECTORY__ + @"/data/TypeProviderConsole/Program.fs" - let fileSource = FileSystem.OpenFileForReadShim(fileName).AsStream().ReadAllText() + let fileSource = FileSystem.OpenFileForReadShim(fileName).ReadAllText() let fileParseResults, fileCheckAnswer = checker.ParseAndCheckFileInProject(fileName, 0, SourceText.ofString fileSource, options) |> Async.RunSynchronously let fileCheckResults = match fileCheckAnswer with @@ -976,7 +976,7 @@ let ``Projects creating generated types should not utilize cross-project-referen OriginalLoadReferences = [] } //printfn "options: %A" options let fileName = __SOURCE_DIRECTORY__ + @"/data/TypeProvidersBug/TestConsole/Program.fs" - let fileSource = FileSystem.OpenFileForReadShim(fileName).AsStream().ReadAllText() + let fileSource = FileSystem.OpenFileForReadShim(fileName).ReadAllText() let fileParseResults, fileCheckAnswer = checker.ParseAndCheckFileInProject(fileName, 0, SourceText.ofString fileSource, options) |> Async.RunSynchronously let fileCheckResults = diff --git a/tests/service/ProjectAnalysisTests.fs b/tests/service/ProjectAnalysisTests.fs index c441d44207d..4e6503797c6 100644 --- a/tests/service/ProjectAnalysisTests.fs +++ b/tests/service/ProjectAnalysisTests.fs @@ -5168,7 +5168,7 @@ let test2() = test() [] let ``Test project42 to ensure cached checked results are invalidated`` () = - let text2 = SourceText.ofString(FileSystem.OpenFileForReadShim(Project42.fileName2).AsStream().ReadAllText()) + let text2 = SourceText.ofString(FileSystem.OpenFileForReadShim(Project42.fileName2).ReadAllText()) let checkedFile2 = checker.ParseAndCheckFileInProject(Project42.fileName2, text2.GetHashCode(), text2, Project42.options) |> Async.RunSynchronously match checkedFile2 with | _, FSharpCheckFileAnswer.Succeeded(checkedFile2Results) -> @@ -5431,7 +5431,7 @@ type UseTheThings(i:int) = //(snd symbolUses.[42]).Symbol.IsEffectivelySameAs((snd symbolUses.[37]).Symbol) //(snd symbolUses.[42]).Symbol.GetEffectivelySameAsHash() //(snd symbolUses.[37]).Symbol.GetEffectivelySameAsHash() - let lines = FileSystem.OpenFileForReadShim(fileName1).AsStream().ReadAllLines() + let lines = FileSystem.OpenFileForReadShim(fileName1).ReadAllLines() let unusedOpens = UnusedOpens.getUnusedOpens (fileCheckResults, (fun i -> lines.[i-1])) |> Async.RunSynchronously let unusedOpensData = [ for uo in unusedOpens -> tups uo, lines.[uo.StartLine-1] ] let expected = @@ -5504,7 +5504,7 @@ type UseTheThings(i:int) = //(snd symbolUses.[42]).Symbol.IsEffectivelySameAs((snd symbolUses.[37]).Symbol) //(snd symbolUses.[42]).Symbol.GetEffectivelySameAsHash() //(snd symbolUses.[37]).Symbol.GetEffectivelySameAsHash() - let lines = FileSystem.OpenFileForReadShim(fileName1).AsStream().ReadAllLines() + let lines = FileSystem.OpenFileForReadShim(fileName1).ReadAllLines() let unusedOpens = UnusedOpens.getUnusedOpens (fileCheckResults, (fun i -> lines.[i-1])) |> Async.RunSynchronously let unusedOpensData = [ for uo in unusedOpens -> tups uo, lines.[uo.StartLine-1] ] let expected = @@ -5580,7 +5580,7 @@ module M2 = |> function | _, FSharpCheckFileAnswer.Succeeded(res) -> res | _ -> failwithf "Parsing aborted unexpectedly..." - let lines = FileSystem.OpenFileForReadShim(fileName1).AsStream().ReadAllLines() + let lines = FileSystem.OpenFileForReadShim(fileName1).ReadAllLines() let unusedOpens = UnusedOpens.getUnusedOpens (fileCheckResults, (fun i -> lines.[i-1])) |> Async.RunSynchronously let unusedOpensData = [ for uo in unusedOpens -> tups uo, lines.[uo.StartLine-1] ] let expected = From 726ee792096486b1527d8165fb0bd7f9012954ae Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 31 May 2021 11:26:21 +0200 Subject: [PATCH 05/27] Post-merge fixes --- src/fsharp/ParseAndCheckInputs.fs | 4 ++-- src/fsharp/absil/ilread.fs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/fsharp/ParseAndCheckInputs.fs b/src/fsharp/ParseAndCheckInputs.fs index 7830d6b830f..3899f410cca 100644 --- a/src/fsharp/ParseAndCheckInputs.fs +++ b/src/fsharp/ParseAndCheckInputs.fs @@ -406,14 +406,14 @@ let parseInputStreamAux (tcConfig: TcConfig, lexResourceManager, conditionalComp use reader = stream.GetReader(tcConfig.inputCodePage, retryLocked) // Set up the LexBuffer for the file - let lexbuf = UnicodeLexing.StreamReaderAsLexbuf(tcConfig.langVersion.SupportsFeature, reader) + let lexbuf = UnicodeLexing.StreamReaderAsLexbuf(not tcConfig.compilingFslib, tcConfig.langVersion.SupportsFeature, reader) // Parse the file drawing tokens from the lexbuf ParseOneInputLexbuf(tcConfig, lexResourceManager, conditionalCompilationDefines, lexbuf, filename, isLastCompiland, errorLogger) let parseInputSourceTextAux (tcConfig: TcConfig, lexResourceManager, conditionalCompilationDefines, filename, isLastCompiland, errorLogger, sourceText: ISourceText) = // Set up the LexBuffer for the file - let lexbuf = UnicodeLexing.SourceTextAsLexbuf(tcConfig.langVersion.SupportsFeature, sourceText) + let lexbuf = UnicodeLexing.SourceTextAsLexbuf(not tcConfig.compilingFslib, tcConfig.langVersion.SupportsFeature, sourceText) // Parse the file drawing tokens from the lexbuf ParseOneInputLexbuf(tcConfig, lexResourceManager, conditionalCompilationDefines, lexbuf, filename, isLastCompiland, errorLogger) diff --git a/src/fsharp/absil/ilread.fs b/src/fsharp/absil/ilread.fs index 8a10623b2a5..d877e36cdfa 100644 --- a/src/fsharp/absil/ilread.fs +++ b/src/fsharp/absil/ilread.fs @@ -3915,7 +3915,6 @@ let createByteFileChunk opts fileName chunk = let createMemoryMapFile fileName = let stream = FileSystem.OpenFileForReadShim(fileName, useMemoryMappedFile = true) - let byteMem = stream.AsByteMemory() let safeHolder = { new obj() with From dd595cc41d513af928a2590d6b4a675ad21f0140 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Mon, 7 Jun 2021 18:15:00 -0700 Subject: [PATCH 06/27] Added MainState to IncrementalBuilder --- src/fsharp/service/IncrementalBuild.fs | 76 +++++++++++++++++--------- 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index ab241d50db6..80830631810 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -721,23 +721,43 @@ type IncrementalBuilderState = finalizedBoundModel: GraphNode<((ILAssemblyRef * IRawFSharpAssemblyData option * TypedImplFile list option * BoundModel) * DateTime)> } +type IncrementalBuilderMainState = + { + initialBoundModel: BoundModel + tcGlobals: TcGlobals + nonFrameworkAssemblyInputs: (Choice * (TimeStampCache -> DateTime)) list + tcConfig: TcConfig + outfile: string + assemblyName: string + lexResourceManager: Lexhelp.LexResourceManager + sourceFiles: ImmutableArray<(range * FSharpDocument * (bool * bool))> + enablePartialTypeChecking: bool + beforeFileChecked: Event + fileChecked: Event +#if !NO_EXTENSIONTYPING + importsInvalidatedByTypeProvider: Event +#endif + allDependencies: string [] + } + /// Manages an incremental build graph for the build of a single F# project -type IncrementalBuilder( - initialBoundModel: BoundModel, - tcGlobals, - nonFrameworkAssemblyInputs, - tcConfig: TcConfig, - outfile, - assemblyName, - lexResourceManager, - sourceFiles: ImmutableArray<(range * FSharpDocument * (bool * bool))>, - enablePartialTypeChecking, - beforeFileChecked: Event, - fileChecked: Event, +type IncrementalBuilder(mainState: IncrementalBuilderMainState) = + + let initialBoundModel = mainState.initialBoundModel + let tcGlobals = mainState.tcGlobals + let nonFrameworkAssemblyInputs = mainState.nonFrameworkAssemblyInputs + let tcConfig = mainState.tcConfig + let outfile = mainState.outfile + let assemblyName = mainState.assemblyName + let lexResourceManager = mainState.lexResourceManager + let sourceFiles = mainState.sourceFiles + let enablePartialTypeChecking = mainState.enablePartialTypeChecking + let beforeFileChecked = mainState.beforeFileChecked + let fileChecked = mainState.fileChecked #if !NO_EXTENSIONTYPING - importsInvalidatedByTypeProvider: Event, + let importsInvalidatedByTypeProvider = mainState.importsInvalidatedByTypeProvider #endif - allDependencies) = + let allDependencies = mainState.allDependencies let fileParsed = new Event() let projectChecked = new Event() @@ -1552,21 +1572,23 @@ type IncrementalBuilder( let builder = new IncrementalBuilder( - initialBoundModel, - tcGlobals, - nonFrameworkAssemblyInputs, - tcConfig, - outfile, - assemblyName, - resourceManager, - (ImmutableArray.CreateRange(sourceFiles)), - enablePartialTypeChecking, - beforeFileChecked, - fileChecked, + { + initialBoundModel = initialBoundModel + tcGlobals = tcGlobals + nonFrameworkAssemblyInputs = nonFrameworkAssemblyInputs + tcConfig = tcConfig + outfile = outfile + assemblyName = assemblyName + lexResourceManager = resourceManager + sourceFiles = (ImmutableArray.CreateRange(sourceFiles)) + enablePartialTypeChecking = enablePartialTypeChecking + beforeFileChecked = beforeFileChecked + fileChecked = fileChecked #if !NO_EXTENSIONTYPING - importsInvalidatedByTypeProvider, + importsInvalidatedByTypeProvider = importsInvalidatedByTypeProvider #endif - allDependencies) + allDependencies = allDependencies + }) return Some builder with e -> errorRecoveryNoRange e From 30ad1165536866a418fa37cce79807c254323e5e Mon Sep 17 00:00:00 2001 From: Will Smith Date: Mon, 7 Jun 2021 18:40:35 -0700 Subject: [PATCH 07/27] Using MainState --- src/fsharp/service/IncrementalBuild.fs | 159 +++++++++++++------------ 1 file changed, 80 insertions(+), 79 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 80830631810..5ed2cd0662f 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -723,6 +723,7 @@ type IncrementalBuilderState = type IncrementalBuilderMainState = { + mutable isImportsInvalidated: bool initialBoundModel: BoundModel tcGlobals: TcGlobals nonFrameworkAssemblyInputs: (Choice * (TimeStampCache -> DateTime)) list @@ -734,6 +735,7 @@ type IncrementalBuilderMainState = enablePartialTypeChecking: bool beforeFileChecked: Event fileChecked: Event + fileParsed: Event #if !NO_EXTENSIONTYPING importsInvalidatedByTypeProvider: Event #endif @@ -741,17 +743,11 @@ type IncrementalBuilderMainState = } /// Manages an incremental build graph for the build of a single F# project -type IncrementalBuilder(mainState: IncrementalBuilderMainState) = +type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: IncrementalBuilderState) = let initialBoundModel = mainState.initialBoundModel - let tcGlobals = mainState.tcGlobals - let nonFrameworkAssemblyInputs = mainState.nonFrameworkAssemblyInputs let tcConfig = mainState.tcConfig - let outfile = mainState.outfile - let assemblyName = mainState.assemblyName - let lexResourceManager = mainState.lexResourceManager let sourceFiles = mainState.sourceFiles - let enablePartialTypeChecking = mainState.enablePartialTypeChecking let beforeFileChecked = mainState.beforeFileChecked let fileChecked = mainState.fileChecked #if !NO_EXTENSIONTYPING @@ -762,23 +758,21 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = let fileParsed = new Event() let projectChecked = new Event() - let defaultTimeStamp = DateTime.UtcNow - - let mutable isImportsInvalidated = false + static let defaultTimeStamp = DateTime.UtcNow #if !NO_EXTENSIONTYPING - do importsInvalidatedByTypeProvider.Publish.Add(fun () -> isImportsInvalidated <- true) + do importsInvalidatedByTypeProvider.Publish.Add(fun () -> mainState.isImportsInvalidated <- true) #endif //---------------------------------------------------- // START OF BUILD TASK FUNCTIONS /// Get the timestamp of the given file name. - let StampFileNameTask (_cache: TimeStampCache) (_m: range, doc: FSharpDocument, _isLastCompiland) = + static let StampFileNameTask (_cache: TimeStampCache) (_m: range, doc: FSharpDocument, _isLastCompiland) = doc.TimeStamp /// Timestamps of referenced assemblies are taken from the file's timestamp. - let StampReferencedAssemblyTask (cache: TimeStampCache) (_ref, timeStamper) = + static let StampReferencedAssemblyTask (cache: TimeStampCache) (_ref, timeStamper) = timeStamper cache // Link all the assemblies together and produce the input typecheck accumulator @@ -878,7 +872,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = None) } /// Type check all files eagerly. - let TypeCheckTask partialCheck (prevBoundModel: BoundModel) syntaxTree: NodeCode = + static let TypeCheckTask partialCheck (prevBoundModel: BoundModel) syntaxTree: NodeCode = node { let! tcInfo = prevBoundModel.GetTcInfo() let boundModel = prevBoundModel.Next(syntaxTree, tcInfo) @@ -896,15 +890,15 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = } /// Finish up the typechecking to produce outputs for the rest of the compilation process - let FinalizeTypeCheckTask (boundModels: ImmutableArray) = + static let FinalizeTypeCheckTask mainState (boundModels: ImmutableArray) = node { - let errorLogger = CompilationErrorLogger("FinalizeTypeCheckTask", tcConfig.errorSeverityOptions) + let errorLogger = CompilationErrorLogger("FinalizeTypeCheckTask", mainState.tcConfig.errorSeverityOptions) use _ = new CompilationGlobalsScope(errorLogger, BuildPhase.TypeCheck) let! results = boundModels |> Seq.map (fun boundModel -> node { - if enablePartialTypeChecking then + if mainState.enablePartialTypeChecking then let! tcInfo = boundModel.GetTcInfo() return tcInfo, None else @@ -941,22 +935,22 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = let ilAssemRef = let publicKey = try - let signingInfo = ValidateKeySigningAttributes (tcConfig, tcGlobals, topAttrs) + let signingInfo = ValidateKeySigningAttributes (mainState.tcConfig, mainState.tcGlobals, topAttrs) match GetStrongNameSigner signingInfo with | None -> None | Some s -> Some (PublicKey.KeyAsToken(s.PublicKey)) with e -> errorRecoveryNoRange e None - let locale = TryFindFSharpStringAttribute tcGlobals (tcGlobals.FindSysAttrib "System.Reflection.AssemblyCultureAttribute") topAttrs.assemblyAttrs + let locale = TryFindFSharpStringAttribute mainState.tcGlobals (mainState.tcGlobals.FindSysAttrib "System.Reflection.AssemblyCultureAttribute") topAttrs.assemblyAttrs let assemVerFromAttrib = - TryFindFSharpStringAttribute tcGlobals (tcGlobals.FindSysAttrib "System.Reflection.AssemblyVersionAttribute") topAttrs.assemblyAttrs + TryFindFSharpStringAttribute mainState.tcGlobals (mainState.tcGlobals.FindSysAttrib "System.Reflection.AssemblyVersionAttribute") topAttrs.assemblyAttrs |> Option.bind (fun v -> try Some (parseILVersion v) with _ -> None) let ver = match assemVerFromAttrib with - | None -> tcConfig.version.GetVersionInfo(tcConfig.implicitIncludeDir) + | None -> mainState.tcConfig.version.GetVersionInfo(mainState.tcConfig.implicitIncludeDir) | Some v -> v - ILAssemblyRef.Create(assemblyName, None, publicKey, false, Some ver, locale) + ILAssemblyRef.Create(mainState.assemblyName, None, publicKey, false, Some ver, locale) let tcAssemblyDataOpt = try @@ -970,14 +964,14 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = if tcState.CreatesGeneratedProvidedTypes || hasTypeProviderAssemblyAttrib then None else - Some (RawFSharpAssemblyDataBackedByLanguageService (tcConfig, tcGlobals, generatedCcu, outfile, topAttrs, assemblyName, ilAssemRef) :> IRawFSharpAssemblyData) + Some (RawFSharpAssemblyDataBackedByLanguageService (mainState.tcConfig, mainState.tcGlobals, generatedCcu, mainState.outfile, topAttrs, mainState.assemblyName, ilAssemRef) :> IRawFSharpAssemblyData) with e -> errorRecoveryNoRange e None ilAssemRef, tcAssemblyDataOpt, Some tcAssemblyExpr with e -> errorRecoveryNoRange e - mkSimpleAssemblyRef assemblyName, None, None + mkSimpleAssemblyRef mainState.assemblyName, None, None let diagnostics = errorLogger.GetDiagnostics() :: finalInfo.tcErrorsRev let! finalBoundModelWithErrors = finalBoundModel.Finish(diagnostics, Some topAttrs) @@ -990,25 +984,22 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = // --------------------------------------------------------------------------------------------- // START OF BUILD DESCRIPTION - let GetSyntaxTree (sourceRange: range, doc: FSharpDocument, isLastCompiland) = - SyntaxTree(tcConfig, fileParsed, lexResourceManager, sourceRange, doc, isLastCompiland) - - // Inputs - let referencedAssemblies = nonFrameworkAssemblyInputs |> Array.ofList // TODO: This should be an immutable array. + static let GetSyntaxTree (mainState: IncrementalBuilderMainState) (sourceRange: range, doc: FSharpDocument, isLastCompiland) = + SyntaxTree(mainState.tcConfig, mainState.fileParsed, mainState.lexResourceManager, sourceRange, doc, isLastCompiland) - let createBoundModelGraphNode initialBoundModel (boundModels: ImmutableArray>.Builder) i = - let fileInfo = sourceFiles.[i] + static let createBoundModelGraphNode mainState initialBoundModel (boundModels: ImmutableArray>.Builder) i = + let fileInfo = mainState.sourceFiles.[i] let prevBoundModelGraphNode = match i with | 0 (* first file *) -> initialBoundModel | _ -> boundModels.[i - 1] - let syntaxTree = GetSyntaxTree fileInfo + let syntaxTree = GetSyntaxTree mainState fileInfo GraphNode(node { let! prevBoundModel = prevBoundModelGraphNode.GetOrComputeValue() - return! TypeCheckTask enablePartialTypeChecking prevBoundModel syntaxTree + return! TypeCheckTask mainState.enablePartialTypeChecking prevBoundModel syntaxTree }) - let rec createFinalizeBoundModelGraphNode (boundModels: ImmutableArray>.Builder) = + static let rec createFinalizeBoundModelGraphNode mainState (boundModels: ImmutableArray>.Builder) = GraphNode(node { // Compute last bound model then get all the evaluated models. let! _ = boundModels.[boundModels.Count - 1].GetOrComputeValue() @@ -1017,19 +1008,19 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = |> Seq.map (fun x -> x.TryPeekValue().Value) |> ImmutableArray.CreateRange - let! result = FinalizeTypeCheckTask boundModels + let! result = FinalizeTypeCheckTask mainState boundModels let result = (result, DateTime.UtcNow) return result }) - and computeStampedFileName (state: IncrementalBuilderState) (cache: TimeStampCache) slot fileInfo = + and computeStampedFileName mainState (state: IncrementalBuilderState) (cache: TimeStampCache) slot fileInfo = let currentStamp = state.stampedFileNames.[slot] let stamp = StampFileNameTask cache fileInfo if currentStamp <> stamp then match state.boundModels.[slot].TryPeekValue() with // This prevents an implementation file that has a backing signature file from invalidating the rest of the build. - | ValueSome(boundModel) when enablePartialTypeChecking && boundModel.BackingSignature.IsSome -> + | ValueSome(boundModel) when mainState.enablePartialTypeChecking && boundModel.BackingSignature.IsSome -> let newBoundModel = boundModel.ClearTcInfoExtras() { state with boundModels = state.boundModels.RemoveAt(slot).Insert(slot, GraphNode(node { return newBoundModel })) @@ -1043,14 +1034,14 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = // Invalidate the file and all files below it. for j = 0 to stampedFileNames.Count - slot - 1 do - let stamp = StampFileNameTask cache sourceFiles.[slot + j] + let stamp = StampFileNameTask cache mainState.sourceFiles.[slot + j] stampedFileNames.[slot + j] <- stamp logicalStampedFileNames.[slot + j] <- stamp - boundModels.[slot + j] <- createBoundModelGraphNode state.initialBoundModel boundModels (slot + j) + boundModels.[slot + j] <- createBoundModelGraphNode mainState state.initialBoundModel boundModels (slot + j) { state with // Something changed, the finalized view of the project must be invalidated. - finalizedBoundModel = createFinalizeBoundModelGraphNode boundModels + finalizedBoundModel = createFinalizeBoundModelGraphNode mainState boundModels stampedFileNames = stampedFileNames.ToImmutable() logicalStampedFileNames = logicalStampedFileNames.ToImmutable() @@ -1059,21 +1050,21 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = else state - and computeStampedFileNames state (cache: TimeStampCache) = + and computeStampedFileNames mainState state (cache: TimeStampCache) = let mutable i = 0 - (state, sourceFiles) + (state, mainState.sourceFiles) ||> Seq.fold (fun state fileInfo -> - let newState = computeStampedFileName state cache i fileInfo + let newState = computeStampedFileName mainState state cache i fileInfo i <- i + 1 newState ) - and computeStampedReferencedAssemblies state canTriggerInvalidation (cache: TimeStampCache) = + and computeStampedReferencedAssemblies (mainState: IncrementalBuilderMainState) state canTriggerInvalidation (cache: TimeStampCache) = let stampedReferencedAssemblies = state.stampedReferencedAssemblies.ToBuilder() let mutable referencesUpdated = false - referencedAssemblies - |> Array.iteri (fun i asmInfo -> + mainState.nonFrameworkAssemblyInputs + |> List.iteri (fun i asmInfo -> let currentStamp = state.stampedReferencedAssemblies.[i] let stamp = StampReferencedAssemblyTask cache asmInfo @@ -1085,8 +1076,8 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = if referencesUpdated then // Build is invalidated. The build must be rebuilt with the newly updated references. - if not isImportsInvalidated && canTriggerInvalidation then - isImportsInvalidated <- true + if not mainState.isImportsInvalidated && canTriggerInvalidation then + mainState.isImportsInvalidated <- true { state with stampedReferencedAssemblies = stampedReferencedAssemblies.ToImmutable() } @@ -1135,29 +1126,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = *) let gate = obj () - let mutable currentState = - let cache = TimeStampCache(defaultTimeStamp) - let initialBoundModel = GraphNode(node { return initialBoundModel }) - let boundModels = ImmutableArray.CreateBuilder(sourceFiles.Length) - - for slot = 0 to sourceFiles.Length - 1 do - boundModels.Add(createBoundModelGraphNode initialBoundModel boundModels slot) - - let documents = sourceFiles |> Seq.map (fun (_, doc, _) -> doc) |> ImmutableArray.CreateRange - - let state = - { - documents = documents - stampedFileNames = Array.init sourceFiles.Length (fun _ -> DateTime.MinValue) |> ImmutableArray.CreateRange - logicalStampedFileNames = Array.init sourceFiles.Length (fun _ -> DateTime.MinValue) |> ImmutableArray.CreateRange - stampedReferencedAssemblies = Array.init referencedAssemblies.Length (fun _ -> DateTime.MinValue) |> ImmutableArray.CreateRange - initialBoundModel = initialBoundModel - boundModels = boundModels.ToImmutable() - finalizedBoundModel = createFinalizeBoundModelGraphNode boundModels - } - let state = computeStampedReferencedAssemblies state false cache - let state = computeStampedFileNames state cache - state + let mutable currentState = state let computeProjectTimeStamp (state: IncrementalBuilderState) = let t1 = MaxTimeStampInDependencies state.stampedReferencedAssemblies @@ -1167,7 +1136,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = let setCurrentState state cache (ct: CancellationToken) = lock gate (fun () -> ct.ThrowIfCancellationRequested() - currentState <- computeStampedFileNames state cache + currentState <- computeStampedFileNames mainState state cache ) let checkFileTimeStamps (cache: TimeStampCache) = @@ -1194,10 +1163,10 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = member _.IsReferencesInvalidated = // fast path - if isImportsInvalidated then true + if mainState.isImportsInvalidated then true else - computeStampedReferencedAssemblies currentState true (TimeStampCache(defaultTimeStamp)) |> ignore - isImportsInvalidated + computeStampedReferencedAssemblies mainState currentState true (TimeStampCache(defaultTimeStamp)) |> ignore + mainState.isImportsInvalidated member _.AllDependenciesDeprecated = allDependencies @@ -1219,7 +1188,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = member builder.TryGetCheckResultsBeforeFileInProject (filename) = let cache = TimeStampCache defaultTimeStamp - let tmpState = computeStampedFileNames currentState cache + let tmpState = computeStampedFileNames mainState currentState cache let slotOfFile = builder.GetSlotOfFileName filename match tryGetBeforeSlot tmpState slotOfFile with @@ -1292,7 +1261,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = } member _.GetLogicalTimeStampForProject(cache) = - let tmpState = computeStampedFileNames currentState cache + let tmpState = computeStampedFileNames mainState currentState cache computeProjectTimeStamp tmpState member _.TryGetSlotOfFileName(filename: string) = @@ -1320,7 +1289,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = let slotOfFile = builder.GetSlotOfFileName filename let fileInfo = sourceFiles.[slotOfFile] // re-parse on demand instead of retaining - let syntaxTree = GetSyntaxTree fileInfo + let syntaxTree = GetSyntaxTree mainState fileInfo syntaxTree.Parse None member _.SourceFiles = sourceFiles |> Seq.map (fun (_, f, _) -> f.FilePath) |> List.ofSeq @@ -1333,7 +1302,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = (state, docs) ||> Seq.fold (fun state doc -> let slot = this.GetSlotOfFileName doc.FilePath - computeStampedFileName state cache slot sourceFiles.[slot] + computeStampedFileName mainState state cache slot sourceFiles.[slot] ) setCurrentState newState cache CancellationToken.None @@ -1573,6 +1542,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = let builder = new IncrementalBuilder( { + isImportsInvalidated = false initialBoundModel = initialBoundModel tcGlobals = tcGlobals nonFrameworkAssemblyInputs = nonFrameworkAssemblyInputs @@ -1584,6 +1554,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = enablePartialTypeChecking = enablePartialTypeChecking beforeFileChecked = beforeFileChecked fileChecked = fileChecked + fileParsed = Event<_>() #if !NO_EXTENSIONTYPING importsInvalidatedByTypeProvider = importsInvalidatedByTypeProvider #endif @@ -1608,3 +1579,33 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState) = return builderOpt, diagnostics } + + + new(mainState) = + let initialBoundModel = mainState.initialBoundModel + let sourceFiles = mainState.sourceFiles + + let cache = TimeStampCache(defaultTimeStamp) + let initialBoundModel = GraphNode(node { return initialBoundModel }) + let boundModels = ImmutableArray.CreateBuilder(sourceFiles.Length) + + for slot = 0 to sourceFiles.Length - 1 do + boundModels.Add(createBoundModelGraphNode mainState initialBoundModel boundModels slot) + + let documents = sourceFiles |> Seq.map (fun (_, doc, _) -> doc) |> ImmutableArray.CreateRange + + let referencedAssemblies = mainState.nonFrameworkAssemblyInputs |> Array.ofList + + let state = + { + documents = documents + stampedFileNames = Array.init sourceFiles.Length (fun _ -> DateTime.MinValue) |> ImmutableArray.CreateRange + logicalStampedFileNames = Array.init sourceFiles.Length (fun _ -> DateTime.MinValue) |> ImmutableArray.CreateRange + stampedReferencedAssemblies = Array.init referencedAssemblies.Length (fun _ -> DateTime.MinValue) |> ImmutableArray.CreateRange + initialBoundModel = initialBoundModel + boundModels = boundModels.ToImmutable() + finalizedBoundModel = createFinalizeBoundModelGraphNode mainState boundModels + } + let state = computeStampedReferencedAssemblies mainState state false cache + let state = computeStampedFileNames mainState state cache + IncrementalBuilder(mainState, state) From 6acdc4d9246fce682bb64c37222ffebf7fb4027a Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 8 Jun 2021 17:38:10 -0700 Subject: [PATCH 08/27] Fixing surface area --- .../SurfaceArea.netstandard.fs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs index 18b6fd7e05d..a1f1379dacb 100644 --- a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs +++ b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs @@ -2022,6 +2022,14 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearLanguageServiceRootCachesA FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateAll() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateConfiguration(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Void set_MaxMemory(Int32) +FSharp.Compiler.CodeAnalysis.FSharpDocument +FSharp.Compiler.CodeAnalysis.FSharpDocument: FSharp.Compiler.CodeAnalysis.DocumentText GetText() +FSharp.Compiler.CodeAnalysis.FSharpDocument: FSharp.Compiler.CodeAnalysis.FSharpDocument Create(System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime], Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,FSharp.Compiler.Text.ISourceText]) +FSharp.Compiler.CodeAnalysis.FSharpDocument: FSharp.Compiler.CodeAnalysis.FSharpDocument CreateCopyFromFile(System.String) +FSharp.Compiler.CodeAnalysis.FSharpDocument: System.DateTime TimeStamp +FSharp.Compiler.CodeAnalysis.FSharpDocument: System.DateTime get_TimeStamp() +FSharp.Compiler.CodeAnalysis.FSharpDocument: System.String FilePath +FSharp.Compiler.CodeAnalysis.FSharpDocument: System.String get_FilePath() FSharp.Compiler.CodeAnalysis.FSharpParseFileResults FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsBindingALambdaAtPosition(FSharp.Compiler.Text.Position) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsPosContainedInApplication(FSharp.Compiler.Text.Position) @@ -5154,6 +5162,7 @@ FSharp.Compiler.Syntax.DebugPointAtBinding: Int32 Tag FSharp.Compiler.Syntax.DebugPointAtBinding: Int32 get_Tag() FSharp.Compiler.Syntax.DebugPointAtBinding: System.String ToString() FSharp.Compiler.Syntax.DebugPointAtFinally +FSharp.Compiler.Syntax.DebugPointAtFinally+Tags: Int32 Body FSharp.Compiler.Syntax.DebugPointAtFinally+Tags: Int32 No FSharp.Compiler.Syntax.DebugPointAtFinally+Tags: Int32 Yes FSharp.Compiler.Syntax.DebugPointAtFinally+Yes: FSharp.Compiler.Text.Range get_range() @@ -5161,12 +5170,16 @@ FSharp.Compiler.Syntax.DebugPointAtFinally+Yes: FSharp.Compiler.Text.Range range FSharp.Compiler.Syntax.DebugPointAtFinally: Boolean Equals(FSharp.Compiler.Syntax.DebugPointAtFinally) FSharp.Compiler.Syntax.DebugPointAtFinally: Boolean Equals(System.Object) FSharp.Compiler.Syntax.DebugPointAtFinally: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.Syntax.DebugPointAtFinally: Boolean IsBody FSharp.Compiler.Syntax.DebugPointAtFinally: Boolean IsNo FSharp.Compiler.Syntax.DebugPointAtFinally: Boolean IsYes +FSharp.Compiler.Syntax.DebugPointAtFinally: Boolean get_IsBody() FSharp.Compiler.Syntax.DebugPointAtFinally: Boolean get_IsNo() FSharp.Compiler.Syntax.DebugPointAtFinally: Boolean get_IsYes() +FSharp.Compiler.Syntax.DebugPointAtFinally: FSharp.Compiler.Syntax.DebugPointAtFinally Body FSharp.Compiler.Syntax.DebugPointAtFinally: FSharp.Compiler.Syntax.DebugPointAtFinally NewYes(FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.DebugPointAtFinally: FSharp.Compiler.Syntax.DebugPointAtFinally No +FSharp.Compiler.Syntax.DebugPointAtFinally: FSharp.Compiler.Syntax.DebugPointAtFinally get_Body() FSharp.Compiler.Syntax.DebugPointAtFinally: FSharp.Compiler.Syntax.DebugPointAtFinally get_No() FSharp.Compiler.Syntax.DebugPointAtFinally: FSharp.Compiler.Syntax.DebugPointAtFinally+Tags FSharp.Compiler.Syntax.DebugPointAtFinally: FSharp.Compiler.Syntax.DebugPointAtFinally+Yes @@ -5175,11 +5188,6 @@ FSharp.Compiler.Syntax.DebugPointAtFinally: Int32 GetHashCode(System.Collections FSharp.Compiler.Syntax.DebugPointAtFinally: Int32 Tag FSharp.Compiler.Syntax.DebugPointAtFinally: Int32 get_Tag() FSharp.Compiler.Syntax.DebugPointAtFinally: System.String ToString() -FSharp.Compiler.Syntax.DebugPointAtFinally+Tags: Int32 Body -FSharp.Compiler.Syntax.DebugPointAtFinally: Boolean IsBody -FSharp.Compiler.Syntax.DebugPointAtFinally: Boolean get_IsBody() -FSharp.Compiler.Syntax.DebugPointAtFinally: FSharp.Compiler.Syntax.DebugPointAtFinally Body -FSharp.Compiler.Syntax.DebugPointAtFinally: FSharp.Compiler.Syntax.DebugPointAtFinally get_Body() FSharp.Compiler.Syntax.DebugPointAtFor FSharp.Compiler.Syntax.DebugPointAtFor+Tags: Int32 No FSharp.Compiler.Syntax.DebugPointAtFor+Tags: Int32 Yes From a59e5d3fe1b3c68d77f0d7357c40186cd7158492 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 8 Jun 2021 18:45:28 -0700 Subject: [PATCH 09/27] Fixed a few issues with events --- src/fsharp/service/IncrementalBuild.fs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index d329bbda9e7..bb495e8354e 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -760,15 +760,11 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment #endif let allDependencies = mainState.allDependencies - let fileParsed = new Event() + let fileParsed = mainState.fileParsed let projectChecked = new Event() static let defaultTimeStamp = DateTime.UtcNow -#if !NO_EXTENSIONTYPING - do importsInvalidatedByTypeProvider.Publish.Add(fun () -> mainState.isImportsInvalidated <- true) -#endif - //---------------------------------------------------- // START OF BUILD TASK FUNCTIONS @@ -1545,7 +1541,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment ) let builder = - new IncrementalBuilder( + let mainState = { isImportsInvalidated = false initialBoundModel = initialBoundModel @@ -1564,7 +1560,11 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment importsInvalidatedByTypeProvider = importsInvalidatedByTypeProvider #endif allDependencies = allDependencies - }) + } +#if !NO_EXTENSIONTYPING + importsInvalidatedByTypeProvider.Publish.Add(fun () -> mainState.isImportsInvalidated <- true) +#endif + new IncrementalBuilder(mainState) return Some builder with e -> errorRecoveryNoRange e From a5fa3ecb418576b0aa2e798f83b981c275493541 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 9 Jun 2021 09:26:30 -0700 Subject: [PATCH 10/27] Fixing incremental build --- src/fsharp/service/IncrementalBuild.fs | 27 ++++++++++--------------- src/fsharp/service/IncrementalBuild.fsi | 2 +- src/fsharp/service/service.fs | 6 +----- 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index bb495e8354e..d4efc7ec382 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1314,7 +1314,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment (legacyReferenceResolver, defaultFSharpBinariesDir, frameworkTcImportsCache: FrameworkImportsCache, loadClosureOpt: LoadClosure option, - docs: FSharpDocument list, + sourceFiles: string list, commandLineArgs: string list, projectReferences, projectDirectory, useScriptResolutionRules, keepAssemblyContents, @@ -1327,10 +1327,6 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment let useSimpleResolutionSwitch = "--simpleresolution" - let sourceFiles = - docs - |> List.map (fun x -> x.FilePath) - node { // Trap and report warnings and errors from creation. @@ -1482,17 +1478,11 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment #endif // Check for the existence of loaded sources and prepend them to the sources list if present. - let sourceFiles = - ( - tcConfig.GetAvailableLoadedSources() - |> List.map (fun (m, x) -> m, FSharpDocument.CreateFromFile x) - ) - @ - (docs |> List.map (fun doc -> rangeStartup, doc)) + let sourceFiles = tcConfig.GetAvailableLoadedSources() @ (sourceFiles |>List.map (fun s -> rangeStartup, s)) // Mark up the source files with an indicator flag indicating if they are the last source file in the project let sourceFiles = - let flags, isExe = tcConfig.ComputeCanContainEntryPoint(sourceFiles |> List.map (fun (_, x) -> x.FilePath)) + let flags, isExe = tcConfig.ComputeCanContainEntryPoint(sourceFiles |> List.map snd) ((sourceFiles, flags) ||> List.map2 (fun (m, nm) flag -> (m, nm, (flag, isExe)))) let basicDependencies = @@ -1507,8 +1497,8 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment let allDependencies = [| yield! basicDependencies - for (_, doc, _) in sourceFiles do - yield doc.FilePath |] + for (_, f, _) in sourceFiles do + yield f |] // For scripts, the dependency provider is already available. // For projects create a fresh one for the project. @@ -1540,6 +1530,11 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment importsInvalidatedByTypeProvider ) + let sourceFiles = + sourceFiles + |> Seq.map (fun (m, f, isLastCompiland) -> (m, FSharpDocument.CreateFromFile f, isLastCompiland)) + |> List.ofSeq + let builder = let mainState = { @@ -1551,7 +1546,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment outfile = outfile assemblyName = assemblyName lexResourceManager = resourceManager - sourceFiles = (ImmutableArray.CreateRange(sourceFiles)) + sourceFiles = ImmutableArray.CreateRange(sourceFiles) enablePartialTypeChecking = enablePartialTypeChecking beforeFileChecked = beforeFileChecked fileChecked = fileChecked diff --git a/src/fsharp/service/IncrementalBuild.fsi b/src/fsharp/service/IncrementalBuild.fsi index 82f325abb8f..f6952ab7457 100755 --- a/src/fsharp/service/IncrementalBuild.fsi +++ b/src/fsharp/service/IncrementalBuild.fsi @@ -241,7 +241,7 @@ type internal IncrementalBuilder = defaultFSharpBinariesDir: string * FrameworkImportsCache * loadClosureOpt:LoadClosure option * - docs:FSharpDocument list * + sourceFiles:string list * commandLineArgs:string list * projectReferences: IProjectReference list * projectDirectory:string * diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index 17072b6fb1c..7daba78ad19 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -302,12 +302,8 @@ type BackgroundCompiler( let loadClosure = scriptClosureCache.TryGet(AnyCallerThread, options) let! builderOpt, diagnostics = - let docs = - options.SourceFiles - |> Seq.map FSharpDocument.CreateFromFile - |> List.ofSeq IncrementalBuilder.TryCreateIncrementalBuilderForProjectOptions - (legacyReferenceResolver, FSharpCheckerResultsSettings.defaultFSharpBinariesDir, frameworkTcImportsCache, loadClosure, docs, + (legacyReferenceResolver, FSharpCheckerResultsSettings.defaultFSharpBinariesDir, frameworkTcImportsCache, loadClosure, List.ofArray options.SourceFiles, Array.toList options.OtherOptions, projectReferences, options.ProjectDirectory, options.UseScriptResolutionRules, keepAssemblyContents, keepAllBackgroundResolutions, tryGetMetadataSnapshot, suggestNamesForErrors, keepAllBackgroundSymbolUses, From 7905665f0d391685e6e9d0bcac68ea33f02065e1 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 9 Jun 2021 09:36:13 -0700 Subject: [PATCH 11/27] Some cleanup --- src/fsharp/service/IncrementalBuild.fs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index d4efc7ec382..96c44bc09af 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -721,7 +721,7 @@ type IncrementalBuilderState = stampedFileNames: ImmutableArray logicalStampedFileNames: ImmutableArray stampedReferencedAssemblies: ImmutableArray - initialBoundModel: GraphNode + // initialBoundModel: GraphNode boundModels: ImmutableArray> finalizedBoundModel: GraphNode<((ILAssemblyRef * IRawFSharpAssemblyData option * TypedImplFile list option * BoundModel) * DateTime)> } @@ -730,6 +730,7 @@ type IncrementalBuilderMainState = { mutable isImportsInvalidated: bool initialBoundModel: BoundModel + initialBoundModelNode: GraphNode tcGlobals: TcGlobals nonFrameworkAssemblyInputs: (Choice * (TimeStampCache -> DateTime)) list tcConfig: TcConfig @@ -988,11 +989,11 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment static let GetSyntaxTree (mainState: IncrementalBuilderMainState) (sourceRange: range, doc: FSharpDocument, isLastCompiland) = SyntaxTree(mainState.tcConfig, mainState.fileParsed, mainState.lexResourceManager, sourceRange, doc, isLastCompiland) - static let createBoundModelGraphNode mainState initialBoundModel (boundModels: ImmutableArray>.Builder) i = + static let createBoundModelGraphNode mainState (boundModels: ImmutableArray>.Builder) i = let fileInfo = mainState.sourceFiles.[i] let prevBoundModelGraphNode = match i with - | 0 (* first file *) -> initialBoundModel + | 0 (* first file *) -> mainState.initialBoundModelNode | _ -> boundModels.[i - 1] let syntaxTree = GetSyntaxTree mainState fileInfo GraphNode(node { @@ -1038,7 +1039,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment let stamp = StampFileNameTask cache mainState.sourceFiles.[slot + j] stampedFileNames.[slot + j] <- stamp logicalStampedFileNames.[slot + j] <- stamp - boundModels.[slot + j] <- createBoundModelGraphNode mainState state.initialBoundModel boundModels (slot + j) + boundModels.[slot + j] <- createBoundModelGraphNode mainState boundModels (slot + j) { state with // Something changed, the finalized view of the project must be invalidated. @@ -1266,7 +1267,6 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment computeProjectTimeStamp tmpState member _.TryGetSlotOfFileName(filename: string) = - // Get the slot of the given file and force it to build. let CompareFileNames (_, f2: FSharpDocument, _) = let result = String.Compare(filename, f2.FilePath, StringComparison.CurrentCultureIgnoreCase)=0 @@ -1308,8 +1308,6 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment setCurrentState newState cache CancellationToken.None - /// CreateIncrementalBuilder (for background type checking). Note that fsc.fs also - /// creates an incremental builder used by the command line compiler. static member TryCreateIncrementalBuilderForProjectOptions (legacyReferenceResolver, defaultFSharpBinariesDir, frameworkTcImportsCache: FrameworkImportsCache, @@ -1540,6 +1538,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment { isImportsInvalidated = false initialBoundModel = initialBoundModel + initialBoundModelNode = GraphNode(node { return initialBoundModel }) tcGlobals = tcGlobals nonFrameworkAssemblyInputs = nonFrameworkAssemblyInputs tcConfig = tcConfig @@ -1582,15 +1581,13 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment new(mainState) = - let initialBoundModel = mainState.initialBoundModel let sourceFiles = mainState.sourceFiles let cache = TimeStampCache(defaultTimeStamp) - let initialBoundModel = GraphNode(node { return initialBoundModel }) let boundModels = ImmutableArray.CreateBuilder(sourceFiles.Length) for slot = 0 to sourceFiles.Length - 1 do - boundModels.Add(createBoundModelGraphNode mainState initialBoundModel boundModels slot) + boundModels.Add(createBoundModelGraphNode mainState boundModels slot) let documents = sourceFiles |> Seq.map (fun (_, doc, _) -> doc) |> ImmutableArray.CreateRange @@ -1602,7 +1599,6 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment stampedFileNames = Array.init sourceFiles.Length (fun _ -> DateTime.MinValue) |> ImmutableArray.CreateRange logicalStampedFileNames = Array.init sourceFiles.Length (fun _ -> DateTime.MinValue) |> ImmutableArray.CreateRange stampedReferencedAssemblies = Array.init referencedAssemblies.Length (fun _ -> DateTime.MinValue) |> ImmutableArray.CreateRange - initialBoundModel = initialBoundModel boundModels = boundModels.ToImmutable() finalizedBoundModel = createFinalizeBoundModelGraphNode mainState boundModels } From 2f0007b8c2373e06f93d4c98630d5b690e4d03a7 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 9 Jun 2021 10:02:38 -0700 Subject: [PATCH 12/27] More cleanup --- src/fsharp/service/IncrementalBuild.fs | 91 +++++++++----------------- 1 file changed, 31 insertions(+), 60 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 96c44bc09af..34820e37880 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -739,36 +739,21 @@ type IncrementalBuilderMainState = lexResourceManager: Lexhelp.LexResourceManager sourceFiles: ImmutableArray<(range * FSharpDocument * (bool * bool))> enablePartialTypeChecking: bool + allDependencies: string [] beforeFileChecked: Event fileChecked: Event fileParsed: Event + projectChecked: Event #if !NO_EXTENSIONTYPING importsInvalidatedByTypeProvider: Event #endif - allDependencies: string [] } /// Manages an incremental build graph for the build of a single F# project type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: IncrementalBuilderState) = - let initialBoundModel = mainState.initialBoundModel - let tcConfig = mainState.tcConfig - let sourceFiles = mainState.sourceFiles - let beforeFileChecked = mainState.beforeFileChecked - let fileChecked = mainState.fileChecked -#if !NO_EXTENSIONTYPING - let importsInvalidatedByTypeProvider = mainState.importsInvalidatedByTypeProvider -#endif - let allDependencies = mainState.allDependencies - - let fileParsed = mainState.fileParsed - let projectChecked = new Event() - static let defaultTimeStamp = DateTime.UtcNow - //---------------------------------------------------- - // START OF BUILD TASK FUNCTIONS - /// Get the timestamp of the given file name. static let StampFileNameTask (_cache: TimeStampCache) (_m: range, doc: FSharpDocument, _isLastCompiland) = doc.TimeStamp @@ -980,12 +965,6 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment return ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt, finalBoundModelWithErrors } - // END OF BUILD TASK FUNCTIONS - // --------------------------------------------------------------------------------------------- - - // --------------------------------------------------------------------------------------------- - // START OF BUILD DESCRIPTION - static let GetSyntaxTree (mainState: IncrementalBuilderMainState) (sourceRange: range, doc: FSharpDocument, isLastCompiland) = SyntaxTree(mainState.tcConfig, mainState.fileParsed, mainState.lexResourceManager, sourceRange, doc, isLastCompiland) @@ -1086,7 +1065,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment else state - let tryGetSlot (state: IncrementalBuilderState) slot = + static let tryGetSlot (state: IncrementalBuilderState) slot = match state.boundModels.[slot].TryPeekValue() with | ValueSome boundModel -> (boundModel, state.stampedFileNames.[slot]) @@ -1094,47 +1073,38 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment | _ -> None - let tryGetBeforeSlot (state: IncrementalBuilderState) slot = + static let tryGetBeforeSlot mainState (state: IncrementalBuilderState) slot = match slot with | 0 (* first file *) -> - (initialBoundModel, DateTime.MinValue) + (mainState.initialBoundModel, DateTime.MinValue) |> Some | _ -> tryGetSlot state (slot - 1) - let evalUpToTargetSlot (state: IncrementalBuilderState) targetSlot = + static let evalUpToTargetSlot mainState (state: IncrementalBuilderState) targetSlot = node { if targetSlot < 0 then - return Some(initialBoundModel, DateTime.MinValue) + return Some(mainState.initialBoundModel, DateTime.MinValue) else let! boundModel = state.boundModels.[targetSlot].GetOrComputeValue() return Some(boundModel, state.stampedFileNames.[targetSlot]) } - let MaxTimeStampInDependencies stamps = + static let MaxTimeStampInDependencies stamps = if Seq.isEmpty stamps then DateTime.MinValue else stamps |> Seq.max - // END OF BUILD DESCRIPTION - // --------------------------------------------------------------------------------------------- - - (* - The data below represents a dependency graph. - - ReferencedAssembliesStamps => FileStamps => BoundModels => FinalizedBoundModel - *) - - let gate = obj () - let mutable currentState = state - - let computeProjectTimeStamp (state: IncrementalBuilderState) = + static let computeProjectTimeStamp (state: IncrementalBuilderState) = let t1 = MaxTimeStampInDependencies state.stampedReferencedAssemblies let t2 = MaxTimeStampInDependencies state.logicalStampedFileNames max t1 t2 + let gate = obj () + let mutable currentState = state + let setCurrentState state cache (ct: CancellationToken) = lock gate (fun () -> ct.ThrowIfCancellationRequested() @@ -1149,18 +1119,18 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment do IncrementalBuilderEventTesting.MRU.Add(IncrementalBuilderEventTesting.IBECreated) - member _.TcConfig = tcConfig + member _.TcConfig = mainState.tcConfig - member _.FileParsed = fileParsed.Publish + member _.FileParsed = mainState.fileParsed.Publish - member _.BeforeFileChecked = beforeFileChecked.Publish + member _.BeforeFileChecked = mainState.beforeFileChecked.Publish - member _.FileChecked = fileChecked.Publish + member _.FileChecked = mainState.fileChecked.Publish - member _.ProjectChecked = projectChecked.Publish + member _.ProjectChecked = mainState.projectChecked.Publish #if !NO_EXTENSIONTYPING - member _.ImportsInvalidatedByTypeProvider = importsInvalidatedByTypeProvider.Publish + member _.ImportsInvalidatedByTypeProvider = mainState.importsInvalidatedByTypeProvider.Publish #endif member _.IsReferencesInvalidated = @@ -1170,19 +1140,19 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment computeStampedReferencedAssemblies mainState currentState true (TimeStampCache(defaultTimeStamp)) |> ignore mainState.isImportsInvalidated - member _.AllDependenciesDeprecated = allDependencies + member _.AllDependenciesDeprecated = mainState.allDependencies member _.PopulatePartialCheckingResults () = node { let cache = TimeStampCache defaultTimeStamp // One per step do! checkFileTimeStamps cache let! _ = currentState.finalizedBoundModel.GetOrComputeValue() - projectChecked.Trigger() + mainState.projectChecked.Trigger() } member builder.GetCheckResultsBeforeFileInProjectEvenIfStale filename: PartialCheckResults option = let slotOfFile = builder.GetSlotOfFileName filename - let result = tryGetBeforeSlot currentState slotOfFile + let result = tryGetBeforeSlot mainState currentState slotOfFile match result with | Some (boundModel, timestamp) -> Some (PartialCheckResults (boundModel, timestamp)) @@ -1193,7 +1163,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment let tmpState = computeStampedFileNames mainState currentState cache let slotOfFile = builder.GetSlotOfFileName filename - match tryGetBeforeSlot tmpState slotOfFile with + match tryGetBeforeSlot mainState tmpState slotOfFile with | Some(boundModel, timestamp) -> PartialCheckResults(boundModel, timestamp) |> Some | _ -> None @@ -1204,7 +1174,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment node { let cache = TimeStampCache defaultTimeStamp do! checkFileTimeStamps cache - let! result = evalUpToTargetSlot currentState (slotOfFile - 1) + let! result = evalUpToTargetSlot mainState currentState (slotOfFile - 1) match result with | Some (boundModel, timestamp) -> return PartialCheckResults(boundModel, timestamp) | None -> return! failwith "Expected results to be ready. (GetCheckResultsBeforeSlotInProject)." @@ -1214,7 +1184,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment node { let cache = TimeStampCache defaultTimeStamp do! checkFileTimeStamps cache - let! result = evalUpToTargetSlot currentState (slotOfFile - 1) + let! result = evalUpToTargetSlot mainState currentState (slotOfFile - 1) match result with | Some (boundModel, timestamp) -> let! _ = boundModel.GetOrComputeTcInfoExtras() @@ -1272,7 +1242,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment String.Compare(filename, f2.FilePath, StringComparison.CurrentCultureIgnoreCase)=0 || String.Compare(FileSystem.GetFullPathShim filename, FileSystem.GetFullPathShim f2.FilePath, StringComparison.CurrentCultureIgnoreCase)=0 result - match sourceFiles |> Seq.tryFindIndex CompareFileNames with + match mainState.sourceFiles |> Seq.tryFindIndex CompareFileNames with | Some slot -> Some slot | None -> None @@ -1281,19 +1251,19 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment | Some slot -> slot | None -> failwith (sprintf "The file '%s' was not part of the project. Did you call InvalidateConfiguration when the list of files in the project changed?" filename) - member _.GetSlotsCount () = sourceFiles.Length + member _.GetSlotsCount () = mainState.sourceFiles.Length member this.ContainsFile(filename: string) = (this.TryGetSlotOfFileName filename).IsSome member builder.GetParseResultsForFile (filename) = let slotOfFile = builder.GetSlotOfFileName filename - let fileInfo = sourceFiles.[slotOfFile] + let fileInfo = mainState.sourceFiles.[slotOfFile] // re-parse on demand instead of retaining let syntaxTree = GetSyntaxTree mainState fileInfo syntaxTree.Parse None - member _.SourceFiles = sourceFiles |> Seq.map (fun (_, f, _) -> f.FilePath) |> List.ofSeq + member _.SourceFiles = mainState.sourceFiles |> Seq.map (fun (_, f, _) -> f.FilePath) |> List.ofSeq member this.UpdateDocuments(docs: FSharpDocument seq) = let state = currentState @@ -1303,7 +1273,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment (state, docs) ||> Seq.fold (fun state doc -> let slot = this.GetSlotOfFileName doc.FilePath - computeStampedFileName mainState state cache slot sourceFiles.[slot] + computeStampedFileName mainState state cache slot mainState.sourceFiles.[slot] ) setCurrentState newState cache CancellationToken.None @@ -1547,13 +1517,14 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment lexResourceManager = resourceManager sourceFiles = ImmutableArray.CreateRange(sourceFiles) enablePartialTypeChecking = enablePartialTypeChecking + allDependencies = allDependencies beforeFileChecked = beforeFileChecked fileChecked = fileChecked fileParsed = Event<_>() + projectChecked = Event<_>() #if !NO_EXTENSIONTYPING importsInvalidatedByTypeProvider = importsInvalidatedByTypeProvider #endif - allDependencies = allDependencies } #if !NO_EXTENSIONTYPING importsInvalidatedByTypeProvider.Publish.Add(fun () -> mainState.isImportsInvalidated <- true) From 2654229f14cc826ff1dfb959576be19ae3a097be Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 9 Jun 2021 10:43:52 -0700 Subject: [PATCH 13/27] Added UpdateDocuments --- src/fsharp/service/service.fs | 14 ++++++++ src/fsharp/service/service.fsi | 7 ++++ .../SurfaceArea.netstandard.fs | 1 + .../FSharpProjectOptionsManager.fs | 35 +++++++++++++------ 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index 7daba78ad19..38afb83a8f8 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -919,6 +919,16 @@ type BackgroundCompiler( let _ = createBuilderNode (options, userOpName, CancellationToken.None) () + member bc.UpdateDocuments(options: FSharpProjectOptions, docs) = + let builderNode = getOrCreateBuilder(options, "UpdateDocuments") + node { + match! builderNode with + | Some builder, _ -> + builder.UpdateDocuments(docs) + | _ -> + () + } + member bc.ClearCache(options: seq, _userOpName) = lock gate (fun () -> options @@ -1217,6 +1227,10 @@ type FSharpChecker(legacyReferenceResolver, let userOpName = defaultArg userOpName "Unknown" backgroundCompiler.InvalidateConfiguration(options, userOpName) + member _.UpdateDocuments(options: FSharpProjectOptions, docs) = + backgroundCompiler.UpdateDocuments(options, docs) + |> Async.AwaitNodeCode + /// Clear the internal cache of the given projects. member _.ClearCache(options: FSharpProjectOptions seq, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" diff --git a/src/fsharp/service/service.fsi b/src/fsharp/service/service.fsi index 97b6f9f7983..a6d80fde6a6 100644 --- a/src/fsharp/service/service.fsi +++ b/src/fsharp/service/service.fsi @@ -362,6 +362,13 @@ type public FSharpChecker = /// An optional string used for tracing compiler operations associated with this request. member InvalidateConfiguration: options: FSharpProjectOptions * ?userOpName: string -> unit + /// + /// This function is called when the documents of a particular project need to be updated in a batch. + /// + /// The options for the project or script, used to determine active --define conditionals and other options relevant to parsing. + /// FSharp documents + member UpdateDocuments: options: FSharpProjectOptions * docs: FSharpDocument seq -> Async + /// Clear the internal cache of the given projects. /// The given project options. /// An optional string used for tracing compiler operations associated with this request. diff --git a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs index a1f1379dacb..fd2d86c3972 100644 --- a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs +++ b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs @@ -1992,6 +1992,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.CodeAnalysis.FSharpCheckFileAnswer]] CheckFileInProjectAllowingStaleCachedResults(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, 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.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[Microsoft.FSharp.Core.Unit] UpdateDocuments(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, System.Collections.Generic.IEnumerable`1[FSharp.Compiler.CodeAnalysis.FSharpDocument]) 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.FSharpCheckFileResults]] GetBackgroundCheckResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index 7fcb81e15b2..9f1c8dd9812 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -96,8 +96,21 @@ module private FSharpProjectOptionsHelpers = member this.ToFSharpDocument() = let dt = DateTime.UtcNow let getTimeStamp = fun () -> dt + + let mutable weakFSharpText = Unchecked.defaultof<_> let getSourceText = fun () -> - this.GetTextAsync().Result.ToFSharpSourceText() + match weakFSharpText with + | null -> + let fsharpText = this.GetTextAsync().Result.ToFSharpSourceText() + weakFSharpText <- WeakReference<_>(fsharpText) + fsharpText + | _ -> + match weakFSharpText.TryGetTarget() with + | true, fsharpText -> fsharpText + | _ -> + let fsharpText = this.GetTextAsync().Result.ToFSharpSourceText() + weakFSharpText <- WeakReference<_>(fsharpText) + fsharpText FSharpDocument.Create(this.FilePath, getTimeStamp, getSourceText) [] @@ -346,19 +359,19 @@ type private FSharpProjectOptionsReactor (workspace: Workspace, settings: Editor cache.TryRemove(projectId) |> ignore return! tryComputeOptions project ct else + let projectChanges = project.GetChanges(oldProject) + let changedDocs = projectChanges.GetChangedDocuments() |> Array.ofSeq + if changedDocs.Length > 0 then + let fsharpDocs = + changedDocs + |> Array.map (fun docId -> + let doc = project.GetDocument(docId) + doc.ToFSharpDocument()) - //let projectChanges = project.GetChanges(oldProject) - //let changedDocs = projectChanges.GetChangedDocuments() |> Array.ofSeq - - //if changedDocs.Length > 0 then - // changedDocs - // |> Array.iter (fun docId -> - // let doc = project.GetDocument(docId) - // checkerProvider.Checker.UpdateDocument(projectOptions, doc.ToFSharpDocument(), ct) - // ) + do! checkerProvider.Checker.UpdateDocuments(projectOptions, fsharpDocs) - // cache.[projectId] <- (project, parsingOptions, projectOptions) + cache.[projectId] <- (project, parsingOptions, projectOptions) return Some(parsingOptions, projectOptions) } From 3a75d31658f4ac620b19348a4636abf38b9a50fd Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 9 Jun 2021 10:57:02 -0700 Subject: [PATCH 14/27] More cleanup --- src/fsharp/service/IncrementalBuild.fs | 57 ++++++++++++-------------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 34820e37880..4b4b91163ca 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -755,7 +755,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment static let defaultTimeStamp = DateTime.UtcNow /// Get the timestamp of the given file name. - static let StampFileNameTask (_cache: TimeStampCache) (_m: range, doc: FSharpDocument, _isLastCompiland) = + static let StampFileNameTask (_m: range, doc: FSharpDocument, _isLastCompiland) = doc.TimeStamp /// Timestamps of referenced assemblies are taken from the file's timestamp. @@ -994,9 +994,9 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment return result }) - and computeStampedFileName mainState (state: IncrementalBuilderState) (cache: TimeStampCache) slot fileInfo = + and computeStampedFileName mainState (state: IncrementalBuilderState) slot fileInfo = let currentStamp = state.stampedFileNames.[slot] - let stamp = StampFileNameTask cache fileInfo + let stamp = StampFileNameTask fileInfo if currentStamp <> stamp then match state.boundModels.[slot].TryPeekValue() with @@ -1005,7 +1005,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment let newBoundModel = boundModel.ClearTcInfoExtras() { state with boundModels = state.boundModels.RemoveAt(slot).Insert(slot, GraphNode(node { return newBoundModel })) - stampedFileNames = state.stampedFileNames.SetItem(slot, StampFileNameTask cache fileInfo) + stampedFileNames = state.stampedFileNames.SetItem(slot, StampFileNameTask fileInfo) } | _ -> @@ -1015,7 +1015,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment // Invalidate the file and all files below it. for j = 0 to stampedFileNames.Count - slot - 1 do - let stamp = StampFileNameTask cache mainState.sourceFiles.[slot + j] + let stamp = StampFileNameTask mainState.sourceFiles.[slot + j] stampedFileNames.[slot + j] <- stamp logicalStampedFileNames.[slot + j] <- stamp boundModels.[slot + j] <- createBoundModelGraphNode mainState boundModels (slot + j) @@ -1031,16 +1031,17 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment else state - and computeStampedFileNames mainState state (cache: TimeStampCache) = + and computeStampedFileNames mainState state = let mutable i = 0 (state, mainState.sourceFiles) ||> Seq.fold (fun state fileInfo -> - let newState = computeStampedFileName mainState state cache i fileInfo + let newState = computeStampedFileName mainState state i fileInfo i <- i + 1 newState ) - and computeStampedReferencedAssemblies (mainState: IncrementalBuilderMainState) state canTriggerInvalidation (cache: TimeStampCache) = + and computeStampedReferencedAssemblies (mainState: IncrementalBuilderMainState) state canTriggerInvalidation = + let cache = TimeStampCache(defaultTimeStamp) let stampedReferencedAssemblies = state.stampedReferencedAssemblies.ToBuilder() let mutable referencesUpdated = false @@ -1105,16 +1106,16 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment let gate = obj () let mutable currentState = state - let setCurrentState state cache (ct: CancellationToken) = + let setCurrentState state (ct: CancellationToken) = lock gate (fun () -> ct.ThrowIfCancellationRequested() - currentState <- computeStampedFileNames mainState state cache + currentState <- computeStampedFileNames mainState state ) - let checkFileTimeStamps (cache: TimeStampCache) = + let checkFileTimeStamps () = node { let! ct = NodeCode.CancellationToken - setCurrentState currentState cache ct + setCurrentState currentState ct } do IncrementalBuilderEventTesting.MRU.Add(IncrementalBuilderEventTesting.IBECreated) @@ -1137,15 +1138,14 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment // fast path if mainState.isImportsInvalidated then true else - computeStampedReferencedAssemblies mainState currentState true (TimeStampCache(defaultTimeStamp)) |> ignore + computeStampedReferencedAssemblies mainState currentState true |> ignore mainState.isImportsInvalidated member _.AllDependenciesDeprecated = mainState.allDependencies member _.PopulatePartialCheckingResults () = node { - let cache = TimeStampCache defaultTimeStamp // One per step - do! checkFileTimeStamps cache + do! checkFileTimeStamps () let! _ = currentState.finalizedBoundModel.GetOrComputeValue() mainState.projectChecked.Trigger() } @@ -1159,8 +1159,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment | _ -> None member builder.TryGetCheckResultsBeforeFileInProject (filename) = - let cache = TimeStampCache defaultTimeStamp - let tmpState = computeStampedFileNames mainState currentState cache + let tmpState = computeStampedFileNames mainState currentState let slotOfFile = builder.GetSlotOfFileName filename match tryGetBeforeSlot mainState tmpState slotOfFile with @@ -1172,8 +1171,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment member _.GetCheckResultsBeforeSlotInProject (slotOfFile) = node { - let cache = TimeStampCache defaultTimeStamp - do! checkFileTimeStamps cache + do! checkFileTimeStamps() let! result = evalUpToTargetSlot mainState currentState (slotOfFile - 1) match result with | Some (boundModel, timestamp) -> return PartialCheckResults(boundModel, timestamp) @@ -1182,8 +1180,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment member _.GetFullCheckResultsBeforeSlotInProject (slotOfFile) = node { - let cache = TimeStampCache defaultTimeStamp - do! checkFileTimeStamps cache + do! checkFileTimeStamps() let! result = evalUpToTargetSlot mainState currentState (slotOfFile - 1) match result with | Some (boundModel, timestamp) -> @@ -1216,8 +1213,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment member _.GetCheckResultsAndImplementationsForProject() = node { - let cache = TimeStampCache(defaultTimeStamp) - do! checkFileTimeStamps cache + do! checkFileTimeStamps() let! result = currentState.finalizedBoundModel.GetOrComputeValue() match result with | ((ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt, boundModel), timestamp) -> @@ -1232,8 +1228,8 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment return result } - member _.GetLogicalTimeStampForProject(cache) = - let tmpState = computeStampedFileNames mainState currentState cache + member _.GetLogicalTimeStampForProject(_cache: TimeStampCache) = + let tmpState = computeStampedFileNames mainState currentState computeProjectTimeStamp tmpState member _.TryGetSlotOfFileName(filename: string) = @@ -1268,15 +1264,15 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment member this.UpdateDocuments(docs: FSharpDocument seq) = let state = currentState - let cache = TimeStampCache(defaultTimeStamp) let newState = (state, docs) ||> Seq.fold (fun state doc -> let slot = this.GetSlotOfFileName doc.FilePath - computeStampedFileName mainState state cache slot mainState.sourceFiles.[slot] + let m, _, isLastCompiland = mainState.sourceFiles.[slot] + computeStampedFileName mainState state slot (m, doc, isLastCompiland) ) - setCurrentState newState cache CancellationToken.None + setCurrentState newState CancellationToken.None static member TryCreateIncrementalBuilderForProjectOptions (legacyReferenceResolver, defaultFSharpBinariesDir, @@ -1554,7 +1550,6 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment new(mainState) = let sourceFiles = mainState.sourceFiles - let cache = TimeStampCache(defaultTimeStamp) let boundModels = ImmutableArray.CreateBuilder(sourceFiles.Length) for slot = 0 to sourceFiles.Length - 1 do @@ -1573,6 +1568,6 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment boundModels = boundModels.ToImmutable() finalizedBoundModel = createFinalizeBoundModelGraphNode mainState boundModels } - let state = computeStampedReferencedAssemblies mainState state false cache - let state = computeStampedFileNames mainState state cache + let state = computeStampedReferencedAssemblies mainState state false + let state = computeStampedFileNames mainState state IncrementalBuilder(mainState, state) From 64bb3627fa9ffbbeaf0466c1b3a7e2b9ccbda697 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 9 Jun 2021 11:30:15 -0700 Subject: [PATCH 15/27] Fix --- src/fsharp/service/IncrementalBuild.fs | 41 +++++++++++++------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 4b4b91163ca..9dc30b4c443 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -717,11 +717,10 @@ type IncrementalBuilderState = { // stampedFileNames represent the real stamps of the files. // logicalStampedFileNames represent the stamps of the files that are used to calculate the project's logical timestamp. - documents: ImmutableArray + documents: ImmutableArray<(range * FSharpDocument * (bool * bool))> stampedFileNames: ImmutableArray logicalStampedFileNames: ImmutableArray stampedReferencedAssemblies: ImmutableArray - // initialBoundModel: GraphNode boundModels: ImmutableArray> finalizedBoundModel: GraphNode<((ILAssemblyRef * IRawFSharpAssemblyData option * TypedImplFile list option * BoundModel) * DateTime)> } @@ -737,7 +736,7 @@ type IncrementalBuilderMainState = outfile: string assemblyName: string lexResourceManager: Lexhelp.LexResourceManager - sourceFiles: ImmutableArray<(range * FSharpDocument * (bool * bool))> + sourceFiles: ImmutableArray<(range * string * (bool * bool))> enablePartialTypeChecking: bool allDependencies: string [] beforeFileChecked: Event @@ -968,8 +967,8 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment static let GetSyntaxTree (mainState: IncrementalBuilderMainState) (sourceRange: range, doc: FSharpDocument, isLastCompiland) = SyntaxTree(mainState.tcConfig, mainState.fileParsed, mainState.lexResourceManager, sourceRange, doc, isLastCompiland) - static let createBoundModelGraphNode mainState (boundModels: ImmutableArray>.Builder) i = - let fileInfo = mainState.sourceFiles.[i] + static let createBoundModelGraphNode mainState (documents: ImmutableArray<_>) (boundModels: ImmutableArray>.Builder) i = + let fileInfo = documents.[i] let prevBoundModelGraphNode = match i with | 0 (* first file *) -> mainState.initialBoundModelNode @@ -1004,6 +1003,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment | ValueSome(boundModel) when mainState.enablePartialTypeChecking && boundModel.BackingSignature.IsSome -> let newBoundModel = boundModel.ClearTcInfoExtras() { state with + documents = state.documents.RemoveAt(slot).Insert(slot, fileInfo) boundModels = state.boundModels.RemoveAt(slot).Insert(slot, GraphNode(node { return newBoundModel })) stampedFileNames = state.stampedFileNames.SetItem(slot, StampFileNameTask fileInfo) } @@ -1015,15 +1015,16 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment // Invalidate the file and all files below it. for j = 0 to stampedFileNames.Count - slot - 1 do - let stamp = StampFileNameTask mainState.sourceFiles.[slot + j] + let stamp = StampFileNameTask state.documents.[slot + j] stampedFileNames.[slot + j] <- stamp logicalStampedFileNames.[slot + j] <- stamp - boundModels.[slot + j] <- createBoundModelGraphNode mainState boundModels (slot + j) + boundModels.[slot + j] <- createBoundModelGraphNode mainState state.documents boundModels (slot + j) { state with // Something changed, the finalized view of the project must be invalidated. finalizedBoundModel = createFinalizeBoundModelGraphNode mainState boundModels + documents = state.documents.RemoveAt(slot).Insert(slot, fileInfo) stampedFileNames = stampedFileNames.ToImmutable() logicalStampedFileNames = logicalStampedFileNames.ToImmutable() boundModels = boundModels.ToImmutable() @@ -1033,7 +1034,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment and computeStampedFileNames mainState state = let mutable i = 0 - (state, mainState.sourceFiles) + (state, state.documents) ||> Seq.fold (fun state fileInfo -> let newState = computeStampedFileName mainState state i fileInfo i <- i + 1 @@ -1233,10 +1234,10 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment computeProjectTimeStamp tmpState member _.TryGetSlotOfFileName(filename: string) = - let CompareFileNames (_, f2: FSharpDocument, _) = + let CompareFileNames (_, f2: string, _) = let result = - String.Compare(filename, f2.FilePath, StringComparison.CurrentCultureIgnoreCase)=0 - || String.Compare(FileSystem.GetFullPathShim filename, FileSystem.GetFullPathShim f2.FilePath, StringComparison.CurrentCultureIgnoreCase)=0 + String.Compare(filename, f2, StringComparison.CurrentCultureIgnoreCase)=0 + || String.Compare(FileSystem.GetFullPathShim filename, FileSystem.GetFullPathShim f2, StringComparison.CurrentCultureIgnoreCase)=0 result match mainState.sourceFiles |> Seq.tryFindIndex CompareFileNames with | Some slot -> Some slot @@ -1253,13 +1254,14 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment (this.TryGetSlotOfFileName filename).IsSome member builder.GetParseResultsForFile (filename) = + let state = currentState let slotOfFile = builder.GetSlotOfFileName filename - let fileInfo = mainState.sourceFiles.[slotOfFile] + let fileInfo = state.documents.[slotOfFile] // re-parse on demand instead of retaining let syntaxTree = GetSyntaxTree mainState fileInfo syntaxTree.Parse None - member _.SourceFiles = mainState.sourceFiles |> Seq.map (fun (_, f, _) -> f.FilePath) |> List.ofSeq + member _.SourceFiles = mainState.sourceFiles |> Seq.map (fun (_, f, _) -> f) |> List.ofSeq member this.UpdateDocuments(docs: FSharpDocument seq) = let state = currentState @@ -1494,11 +1496,6 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment importsInvalidatedByTypeProvider ) - let sourceFiles = - sourceFiles - |> Seq.map (fun (m, f, isLastCompiland) -> (m, FSharpDocument.CreateFromFile f, isLastCompiland)) - |> List.ofSeq - let builder = let mainState = { @@ -1551,11 +1548,13 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment let sourceFiles = mainState.sourceFiles let boundModels = ImmutableArray.CreateBuilder(sourceFiles.Length) + let documents = + sourceFiles + |> Seq.map (fun (m, f, isLastCompiland) -> (m, FSharpDocument.CreateFromFile(f), isLastCompiland)) + |> ImmutableArray.CreateRange for slot = 0 to sourceFiles.Length - 1 do - boundModels.Add(createBoundModelGraphNode mainState boundModels slot) - - let documents = sourceFiles |> Seq.map (fun (_, doc, _) -> doc) |> ImmutableArray.CreateRange + boundModels.Add(createBoundModelGraphNode mainState documents boundModels slot) let referencedAssemblies = mainState.nonFrameworkAssemblyInputs |> Array.ofList From b6892a16ac3b2acec7266168805e71f01b0a8ea6 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 9 Jun 2021 15:31:48 -0700 Subject: [PATCH 16/27] Sort by slot --- src/fsharp/service/IncrementalBuild.fs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 9dc30b4c443..d8796ef7162 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -1267,9 +1267,13 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment let state = currentState let newState = - (state, docs) - ||> Seq.fold (fun state doc -> - let slot = this.GetSlotOfFileName doc.FilePath + // Sort the documents by slot in order to update in ascending order. + let docsWithSlotSorted = + docs + |> Seq.map (fun doc -> doc, this.GetSlotOfFileName(doc.FilePath)) + |> Seq.sortBy snd + (state, docsWithSlotSorted) + ||> Seq.fold (fun state (doc, slot) -> let m, _, isLastCompiland = mainState.sourceFiles.[slot] computeStampedFileName mainState state slot (m, doc, isLastCompiland) ) From 6e61087d9c0e90229a4326c52da061e3c5dc9af9 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 9 Jun 2021 17:36:34 -0700 Subject: [PATCH 17/27] Getting VS to use background results. No more foreground. --- .../Classification/ClassificationService.fs | 10 +-- .../CodeFix/AddOpenCodeFixProvider.fs | 8 +- ...peAnnotationToObjectOfIndeterminateType.fs | 2 +- .../ChangeRefCellDerefToNotExpression.fs | 2 +- .../ConvertCSharpLambdaToFSharpLambda.fs | 2 +- .../CodeFix/ConvertToAnonymousRecord.fs | 2 +- .../ImplementInterfaceCodeFixProvider.fs | 4 +- .../CodeFix/MakeDeclarationMutable.fs | 2 +- .../CodeFix/MakeOuterBindingRecursive.fs | 2 +- .../CodeFix/RemoveReturnOrYield.fs | 2 +- .../CodeFix/RemoveUnusedBinding.fs | 2 +- .../CodeFix/RenameUnusedValue.fs | 2 +- .../CodeFix/ReplaceWithSuggestion.fs | 2 +- .../CodeFix/UseMutationWhenValueIsMutable.fs | 2 +- .../CodeLens/FSharpCodeLensService.fs | 6 +- .../Commands/HelpContextService.fs | 4 +- .../Commands/XmlDocCommandService.fs | 2 +- .../src/FSharp.Editor/Common/Extensions.fs | 23 +++++ .../Completion/CompletionProvider.fs | 6 +- .../FSharp.Editor/Completion/SignatureHelp.fs | 5 +- .../Debugging/BreakpointResolutionService.fs | 6 +- .../Diagnostics/DocumentDiagnosticAnalyzer.fs | 28 +++--- .../SimplifyNameDiagnosticAnalyzer.fs | 2 +- .../Diagnostics/UnusedDeclarationsAnalyzer.fs | 2 +- .../UnusedOpensDiagnosticAnalyzer.fs | 5 +- .../DocumentHighlightsService.fs | 4 +- .../InlineRename/InlineRenameService.fs | 2 +- .../FSharpCheckerExtensions.fs | 88 +------------------ .../LanguageService/FSharpCheckerProvider.fs | 4 +- .../FSharpProjectOptionsManager.fs | 22 ----- .../LanguageService/SymbolHelpers.fs | 5 +- .../Navigation/FindUsagesService.fs | 2 +- .../Navigation/GoToDefinition.fs | 10 +-- .../Navigation/NavigateToSearchService.fs | 51 +++++------ .../Navigation/NavigationBarItemService.fs | 4 +- .../QuickInfo/QuickInfoProvider.fs | 8 +- .../Refactor/AddExplicitTypeToParameter.fs | 2 +- .../Refactor/ChangeDerefToValueRefactoring.fs | 2 +- .../ChangeTypeofWithNameToNameofExpression.fs | 2 +- .../Structure/BlockStructureService.fs | 2 +- .../UnitTests/GoToDefinitionServiceTests.fs | 2 +- .../SemanticColorizationServiceTests.fs | 4 +- .../UnitTests/SignatureHelpProviderTests.fs | 49 ++++------- 43 files changed, 143 insertions(+), 253 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index a3844ae749e..b6683c4e24a 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -167,7 +167,7 @@ type internal FSharpClassificationService let! sourceText = document.GetTextAsync(cancellationToken) // If we are trying to get semantic classification for a document that is not open, get the results from the background and cache it. - // We do this for find all references when it is populating results. + // We do this for find all references when it is populating results. // We cache it temporarily so we do not have to continously call into the checker and perform a background operation. if not (document.Project.Solution.Workspace.IsDocumentOpen document.Id) then match! semanticClassificationCache.TryGetValueAsync document |> liftAsync with @@ -179,10 +179,10 @@ type internal FSharpClassificationService do! semanticClassificationCache.SetAsync(document, classificationDataLookup) |> liftAsync addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result else - 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 + let! _ = checkerProvider.Checker.CheckDocumentInProject(document, projectOptions) |> liftAsync + let! classificationData = checkerProvider.Checker.GetBackgroundSemanticClassificationForFile(document.FilePath, projectOptions, userOpName=userOpName) |> liftAsync + let classificationDataLookup = toSemanticClassificationLookup classificationData + addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask cancellationToken diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs index 3890eba616f..d341ea30e80 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, userOpName = userOpName) + let! parseResults, checkResults = checker.CheckDocumentInProject(document, projectOptions) |> liftAsync let line = sourceText.Lines.GetLineFromPosition(context.Span.End) let linePos = sourceText.Lines.GetLinePosition(context.Span.End) let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions @@ -110,7 +110,7 @@ type internal FSharpAddOpenCodeFixProvider let endPos = Position.fromZ endLinePos.Line endLinePos.Character Range.mkRange context.Document.FilePath startPos endPos - let isAttribute = ParsedInput.GetEntityKind(unresolvedIdentRange.Start, parsedInput) = Some EntityKind.Attribute + let isAttribute = ParsedInput.GetEntityKind(unresolvedIdentRange.Start, parseResults.ParseTree) = Some EntityKind.Attribute let entities = assemblyContentProvider.GetAllEntitiesInProjectAndReferencedAssemblies checkResults @@ -126,7 +126,7 @@ type internal FSharpAddOpenCodeFixProvider s.CleanedIdents |> Array.replace (s.CleanedIdents.Length - 1) (lastIdent.Substring(0, lastIdent.Length - 9)) ]) - let longIdent = ParsedInput.GetLongIdentAt parsedInput unresolvedIdentRange.End + let longIdent = ParsedInput.GetLongIdentAt parseResults.ParseTree unresolvedIdentRange.End let! maybeUnresolvedIdents = longIdent @@ -141,7 +141,7 @@ type internal FSharpAddOpenCodeFixProvider if document.FSharpOptions.CodeFixes.AlwaysPlaceOpensAtTopLevel then OpenStatementInsertionPoint.TopLevel else OpenStatementInsertionPoint.Nearest - let createEntity = ParsedInput.TryFindInsertionContext unresolvedIdentRange.StartLine parsedInput maybeUnresolvedIdents insertionPoint + let createEntity = ParsedInput.TryFindInsertionContext unresolvedIdentRange.StartLine parseResults.ParseTree maybeUnresolvedIdents insertionPoint return entities |> Seq.map createEntity |> Seq.concat |> Seq.toList |> addSuggestionsAsCodeFixes context } |> Async.Ignore diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs index cbf1b645648..9dbf8d7f16d 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, userOpName=userOpName) + let! _, checkFileResults = checker.CheckDocumentInProject(document, projectOptions) |> liftAsync 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/ChangeRefCellDerefToNotExpression.fs b/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs index b595cc21e55..5a320d6f5d4 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs @@ -27,7 +27,7 @@ type internal FSharpChangeRefCellDerefToNotExpressionCodeFixProvider let document = context.Document let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName=userOpName) + let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions) |> liftAsync let errorRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) let! derefRange = parseResults.TryRangeOfRefCellDereferenceContainingPos errorRange.Start diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs index 169588e1c84..a6627e71df5 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs @@ -24,7 +24,7 @@ type internal FSharpConvertCSharpLambdaToFSharpLambdaCodeFixProvider override _.RegisterCodeFixesAsync context = asyncMaybe { let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(context.Document, context.CancellationToken, userOpName) - let! parseResults = checkerProvider.Checker.ParseDocument(context.Document, parsingOptions, userOpName) + let! parseResults = checkerProvider.Checker.ParseDocument(context.Document, parsingOptions) |> liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let errorRange = RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs index 3710101ed4e..62f9c741421 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs @@ -29,7 +29,7 @@ type internal FSharpConvertToAnonymousRecordCodeFixProvider asyncMaybe { let document = context.Document let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) - let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName=userOpName) + let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions) |> liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let errorRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs index c21a1123380..580bfefe909 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, userOpName = userOpName) + let! parseResults, checkFileResults = checker.CheckDocumentInProject(context.Document, projectOptions) |> liftAsync 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. @@ -172,7 +172,7 @@ type internal FSharpImplementInterfaceCodeFixProvider | '}' -> None | _ -> Some context.Span.End - let! interfaceState = queryInterfaceState appendBracketAt interfacePos tokens parsedInput + let! interfaceState = queryInterfaceState appendBracketAt interfacePos tokens parseResults.ParseTree let! symbol = Tokenizer.getSymbolAtPosition(context.Document.Id, sourceText, fixupPosition, context.Document.FilePath, defines, SymbolLookupKind.Greedy, false, false) let fcsTextLineNumber = textLine.LineNumber + 1 let lineContents = textLine.ToString() diff --git a/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs b/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs index 9aab9475320..27dce9cb0c2 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, userOpName=userOpName) + let! parseFileResults, checkFileResults = checker.CheckDocumentInProject(document, projectOptions) |> liftAsync 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 67b586688e5..8c54567942b 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs @@ -25,7 +25,7 @@ type internal FSharpMakeOuterBindingRecursiveCodeFixProvider override _.RegisterCodeFixesAsync context = asyncMaybe { let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(context.Document, context.CancellationToken, userOpName) - let! parseResults = checkerProvider.Checker.ParseDocument(context.Document, parsingOptions, userOpName) + let! parseResults = checkerProvider.Checker.ParseDocument(context.Document, parsingOptions) |> liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let diagnosticRange = RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs index 59e6ff102c1..95b5377b504 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs @@ -24,7 +24,7 @@ type internal FSharpRemoveReturnOrYieldCodeFixProvider override _.RegisterCodeFixesAsync context = asyncMaybe { let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(context.Document, context.CancellationToken, userOpName) - let! parseResults = checkerProvider.Checker.ParseDocument(context.Document, parsingOptions, userOpName=userOpName) + let! parseResults = checkerProvider.Checker.ParseDocument(context.Document, parsingOptions) |> liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let errorRange = RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs index 35e489e8675..7ac03d8f4a8 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs @@ -32,7 +32,7 @@ type internal FSharpRemoveUnusedBindingCodeFixProvider let! sourceText = document.GetTextAsync(context.CancellationToken) let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) - let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName=userOpName) + let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions) |> liftAsync let diagnostics = context.Diagnostics diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs index 96ecbcd4351..93ee2c3e57e 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, userOpName=userOpName) + let! _, checkResults = checker.CheckDocumentInProject(document, projectOptions) |> liftAsync 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/ReplaceWithSuggestion.fs b/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs index fdbd65bd228..0c9787e70e1 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs @@ -35,7 +35,7 @@ type internal FSharpReplaceWithSuggestionCodeFixProvider let document = context.Document let! _, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) - let! parseFileResults, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, userOpName=userOpName) + let! parseFileResults, checkFileResults = checker.CheckDocumentInProject(document, projectOptions) |> liftAsync // This is all needed to get a declaration list let! sourceText = document.GetTextAsync(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs b/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs index 44a1fa5682d..100383057b1 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, userOpName=userOpName) + let! _, checkFileResults = checker.CheckDocumentInProject(document, projectOptions) |> liftAsync 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/CodeLens/FSharpCodeLensService.fs b/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs index a9b1fc84584..867d93c48d6 100644 --- a/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs +++ b/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs @@ -158,7 +158,7 @@ type internal FSharpCodeLensService #endif let! document = workspace.CurrentSolution.GetDocument(documentId.Value) |> Option.ofObj let! _, options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, bufferChangedCts.Token, userOpName) - let! _, parsedInput, checkFileResults = checker.ParseAndCheckDocument(document, options, "LineLens", allowStaleResults=true) + let! parseResults, checkFileResults = checker.CheckDocumentInProject(document, options) |> liftAsync #if DEBUG logInfof "Getting uses of all symbols!" #endif @@ -236,7 +236,7 @@ type internal FSharpCodeLensService oldResults.Remove funcID |> ignore else let declarationLine, range = - match visit func.DeclarationLocation.Start parsedInput with + match visit func.DeclarationLocation.Start parseResults.ParseTree with | Some range -> range.StartLine - 1, range | _ -> func.DeclarationLocation.StartLine - 1, func.DeclarationLocation // Track the old element for removal @@ -267,7 +267,7 @@ type internal FSharpCodeLensService for unattachedSymbol in unattachedSymbols do let symbolUse, func, funcID, fullTypeSignature = unattachedSymbol let declarationLine, range = - match visit func.DeclarationLocation.Start parsedInput with + match visit func.DeclarationLocation.Start parseResults.ParseTree with | Some range -> range.StartLine - 1, range | _ -> func.DeclarationLocation.StartLine - 1, func.DeclarationLocation diff --git a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs index 1f77bba3428..e13b840644c 100644 --- a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs @@ -25,9 +25,9 @@ type internal FSharpHelpContextService ) = static let userOpName = "ImplementInterfaceCodeFix" - static member GetHelpTerm(checker: FSharpChecker, document: Document, options, span: TextSpan, tokens: List, perfOptions) : Async = + static member GetHelpTerm(checker: FSharpChecker, document: Document, options, span: TextSpan, tokens: List, _perfOptions) : Async = asyncMaybe { - let! _, _, check = checker.ParseAndCheckDocument(document, options, perfOptions, userOpName) + let! _, check = checker.CheckDocumentInProject(document, options) |> liftAsync let! sourceText = document.GetTextAsync() |> liftTaskAsync let textLines = sourceText.Lines let lineInfo = textLines.GetLineFromPosition(span.Start) diff --git a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs index 4dd22f0ffa1..70a402290d6 100644 --- a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs @@ -70,7 +70,7 @@ type internal XmlDocCommandFilter let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, CancellationToken.None, userOpName) let! cancellationToken = Async.CancellationToken |> liftAsync let! sourceText = document.GetTextAsync(cancellationToken) - let! parseResults = checker.ParseDocument(document, parsingOptions, userOpName) + let! parseResults = checker.ParseDocument(document, parsingOptions) |> liftAsync let xmlDocables = XmlDocParser.GetXmlDocables (sourceText.ToFSharpSourceText(), parseResults.ParseTree) let xmlDocablesBelowThisLine = // +1 because looking below current line for e.g. a 'member' diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index 825a010f387..d63471c18d4 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -14,6 +14,7 @@ open Microsoft.CodeAnalysis.Host open FSharp.Compiler.EditorServices open FSharp.Compiler.Syntax open FSharp.Compiler.Text +open FSharp.Compiler.CodeAnalysis open Microsoft.VisualStudio.FSharp.Editor @@ -289,3 +290,25 @@ module Exception = | _ -> root |> flattenInner |> String.concat " ---> " + +type Document with + + member this.ToFSharpDocument() = + let dt = DateTime.UtcNow + let getTimeStamp = fun () -> dt + + let mutable weakFSharpText = Unchecked.defaultof<_> + let getSourceText = fun () -> + match weakFSharpText with + | null -> + let fsharpText = this.GetTextAsync().Result.ToFSharpSourceText() + weakFSharpText <- WeakReference<_>(fsharpText) + fsharpText + | _ -> + match weakFSharpText.TryGetTarget() with + | true, fsharpText -> fsharpText + | _ -> + let fsharpText = this.GetTextAsync().Result.ToFSharpSourceText() + weakFSharpText <- WeakReference<_>(fsharpText) + fsharpText + FSharpDocument.Create(this.FilePath, getTimeStamp, getSourceText) diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index f99d696c62e..fe73a425179 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -108,10 +108,10 @@ type internal FSharpCompletionProvider static member ProvideCompletionsAsyncAux(checker: FSharpChecker, document: Document, caretPosition: int, options: FSharpProjectOptions, - getAllSymbols: FSharpCheckFileResults -> AssemblySymbol list, languageServicePerformanceOptions: LanguageServicePerformanceOptions, intellisenseOptions: IntelliSenseOptions) = + getAllSymbols: FSharpCheckFileResults -> AssemblySymbol list, _languageServicePerformanceOptions: LanguageServicePerformanceOptions, intellisenseOptions: IntelliSenseOptions) = asyncMaybe { - let! parseResults, _, checkFileResults = checker.ParseAndCheckDocument(document, options, languageServicePerformanceOptions, userOpName = userOpName) + let! parseResults, checkFileResults = checker.CheckDocumentInProject(document, options) |> liftAsync let! sourceText = document.GetTextAsync() |> liftTaskAsync let textLines = sourceText.Lines let caretLinePos = textLines.GetLinePosition(caretPosition) @@ -292,7 +292,7 @@ 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! parseResults = checker.ParseDocument(document, parsingOptions, userOpName) + let! parseResults = checker.ParseDocument(document, parsingOptions) |> liftAsync let fullNameIdents = fullName |> Option.map (fun x -> x.Split '.') |> Option.defaultValue [||] let insertionPoint = diff --git a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs index 96ff3b2d7a2..a4fb179ca18 100644 --- a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs +++ b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs @@ -508,15 +508,14 @@ type internal FSharpSignatureHelpProvider possibleCurrentSignatureHelpSessionKind: CurrentSignatureHelpSessionKind option ) = asyncMaybe { + let! parseResults, checkFileResults = checker.CheckDocumentInProject(document, options) |> liftAsync + 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(document, options, perfOptions, userOpName = userOpName) - let adjustedColumnInSource = let rec loop ch pos = if Char.IsWhiteSpace(ch) then diff --git a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs index 77f3081d879..f3b26332c5c 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs @@ -40,10 +40,8 @@ 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.ParseDocument(document, parsingOptions, userOpName) - match parseResults with - | Some parseResults -> return parseResults.ValidateBreakpointLocation(mkPos fcsTextLineNumber textLineColumn) - | _ -> return None + let! parseResults = checker.ParseDocument(document, parsingOptions) + return parseResults.ValidateBreakpointLocation(mkPos fcsTextLineNumber textLineColumn) } interface IFSharpBreakpointResolutionService with diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs index dd26fbf1026..5e4666f4e89 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs @@ -58,31 +58,23 @@ type internal FSharpDocumentDiagnosticAnalyzer static member GetDiagnostics(checker: FSharpChecker, document: Document, parsingOptions: FSharpParsingOptions, options: FSharpProjectOptions, diagnosticType: DiagnosticsType) = async { - 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.CheckDocument(document, parseResults, options, userOpName) - match checkResultsAnswer with - | FSharpCheckFileAnswer.Aborted -> return [||] - | FSharpCheckFileAnswer.Succeeded results -> - // In order to eleminate duplicates, we should not return parse errors here because they are returned by `AnalyzeSyntaxAsync` method. - let allErrors = HashSet(results.Diagnostics, errorInfoEqualityComparer) - allErrors.ExceptWith(parseResults.Diagnostics) - return Seq.toArray allErrors + let! parseResults, checkResults = checker.CheckDocumentInProject(document, options) + // In order to eleminate duplicates, we should not return parse errors here because they are returned by `AnalyzeSyntaxAsync` method. + let allErrors = HashSet(checkResults.Diagnostics, errorInfoEqualityComparer) + allErrors.ExceptWith(parseResults.Diagnostics) + return Seq.toArray allErrors | DiagnosticsType.Syntax -> + let! parseResults = checker.ParseDocument(document, parsingOptions) return parseResults.Diagnostics } + + let! ct = Async.CancellationToken + let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask + let filePath = document.FilePath let results = HashSet(errors, errorInfoEqualityComparer) diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs index 64007b19de8..c335caf30d4 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs @@ -51,7 +51,7 @@ type internal SimplifyNameDiagnosticAnalyzer | _ -> let! sourceText = document.GetTextAsync() let checker = checkerProvider.Checker - let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, userOpName=userOpName) + let! _, checkResults = checker.CheckDocumentInProject(document, projectOptions) |> liftAsync 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 094796c9e36..9448b62b4f0 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs @@ -35,7 +35,7 @@ type internal UnusedDeclarationsAnalyzer | (_parsingOptions, projectOptions) -> let! sourceText = document.GetTextAsync() let checker = checkerProvider.Checker - let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, userOpName = userOpName) + let! _, checkResults = checker.CheckDocumentInProject(document, projectOptions) |> liftAsync 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 c63341ecd0a..d22d1627541 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs @@ -29,8 +29,9 @@ type internal UnusedOpensDiagnosticAnalyzer static member GetUnusedOpenRanges(document: Document, options, checker: FSharpChecker) : Async> = asyncMaybe { do! Option.guard document.FSharpOptions.CodeFixes.UnusedOpens - let! sourceText = document.GetTextAsync() - let! _, _, checkResults = checker.ParseAndCheckDocument(document, options, userOpName = userOpName) + let! _, checkResults = checker.CheckDocumentInProject(document, options) |> liftAsync + let! ct = Async.CancellationToken |> liftAsync + let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask |> liftAsync 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 c2309227658..83618a67728 100644 --- a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs +++ b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs @@ -52,7 +52,7 @@ type internal FSharpDocumentHighlightsService [] (checkerP |> Seq.toArray static member GetDocumentHighlights(checker: FSharpChecker, document: Document, position: int, - defines: string list, options: FSharpProjectOptions, languageServicePerformanceOptions: LanguageServicePerformanceOptions) : Async = + defines: string list, options: FSharpProjectOptions, _languageServicePerformanceOptions: LanguageServicePerformanceOptions) : Async = asyncMaybe { let! sourceText = document.GetTextAsync() |> liftTaskAsync let filePath = document.FilePath @@ -60,7 +60,7 @@ type internal FSharpDocumentHighlightsService [] (checkerP let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, filePath, defines, SymbolLookupKind.Greedy, false, false) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, languageServicePerformanceOptions, userOpName = userOpName) + let! _, checkFileResults = checker.CheckDocumentInProject(document, options) |> liftAsync let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland) let symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) return diff --git a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs index d08d365f0c6..a277b0b9681 100644 --- a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs +++ b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs @@ -156,7 +156,7 @@ type internal InlineRenameService let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, userOpName = userOpName) + let! _, checkFileResults = checker.CheckDocumentInProject(document, options) |> liftAsync let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.Text.ToString(), symbol.FullIsland) let! declLoc = symbolUse.GetDeclarationLocation(document) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs index 44e2b4b9684..94276aa9d5d 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs @@ -1,98 +1,18 @@ [] module internal Microsoft.VisualStudio.FSharp.Editor.FSharpCheckerExtensions -open System - open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.Text - open FSharp.Compiler.CodeAnalysis type FSharpChecker with - 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 - } - - member checker.CheckDocument(document: Document, parseResults: FSharpParseFileResults, options: FSharpProjectOptions, userOpName: string) = + member checker.ParseDocument(document: Document, options: FSharpParsingOptions) = async { let! ct = Async.CancellationToken - let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask - let! textVersion = document.GetTextVersionAsync(ct) |> Async.AwaitTask - - let filePath = document.FilePath - let textVersionHash = textVersion.GetHashCode() - - return! checker.CheckFileInProject(parseResults, filePath, textVersionHash, sourceText.ToFSharpSourceText(), options,userOpName=userOpName) - } - - 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 filePath = document.FilePath - let textVersionHash = textVersion.GetHashCode() - - let parseAndCheckFile = - async { - let! (parseResults, checkFileAnswer) = checker.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText.ToFSharpSourceText(), options, userOpName=userOpName) - return - match checkFileAnswer with - | FSharpCheckFileAnswer.Aborted -> - None - | FSharpCheckFileAnswer.Succeeded(checkFileResults) -> - Some (parseResults, checkFileResults) - } - - let tryGetFreshResultsWithTimeout() = - async { - let! worker = Async.StartChild(async { try return! parseAndCheckFile with | _ -> return None }, millisecondsTimeout=languageServicePerformanceOptions.TimeUntilStaleCompletion) - try - return! worker - with :? TimeoutException -> - return None // worker is cancelled at this point, we cannot return it and wait its completion anymore - } - - let bindParsedInput(results: (FSharpParseFileResults * FSharpCheckFileResults) option) = - match results with - | Some(parseResults, checkResults) -> - Some (parseResults, parseResults.ParseTree, checkResults) - | None -> None - - if languageServicePerformanceOptions.AllowStaleCompletionResults then - let! freshResults = tryGetFreshResultsWithTimeout() - - let! results = - match freshResults with - | Some x -> async.Return (Some x) - | None -> - async { - match checker.TryGetRecentCheckResultsForFile(filePath, options, userOpName=userOpName) with - | Some (parseResults, checkFileResults, _) -> - return Some (parseResults, checkFileResults) - | None -> - return! parseAndCheckFile - } - return bindParsedInput results - else - let! results = parseAndCheckFile - return bindParsedInput results + return! checker.ParseFile(document.FilePath, sourceText.ToFSharpSourceText(), options, cache = true) } - member checker.ParseAndCheckDocument(document: Document, options: FSharpProjectOptions, userOpName: string, ?allowStaleResults: bool) = + member checker.CheckDocumentInProject(document: Document, options: FSharpProjectOptions) = async { - let perfOpts = - match allowStaleResults with - | Some b -> { document.FSharpOptions.LanguageServicePerformance with AllowStaleCompletionResults = b } - | _ -> document.FSharpOptions.LanguageServicePerformance - return! checker.ParseAndCheckDocument(document, options, perfOpts, userOpName=userOpName) + return! checker.GetBackgroundCheckResultsForFileInProject(document.FilePath, options) } diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs index ae2f2ca3bf4..1d69615d68d 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs @@ -57,12 +57,12 @@ type internal FSharpCheckerProvider let checker = FSharpChecker.Create( projectCacheSize = settings.LanguageServicePerformance.ProjectCheckCacheSize, - keepAllBackgroundResolutions = false, + keepAllBackgroundResolutions = true, // Enabling this would mean that if devenv.exe goes above 2.3GB we do a one-off downsize of the F# Compiler Service caches (* , MaxMemory = 2300 *) legacyReferenceResolver=LegacyMSBuildReferenceResolver.getResolver(), tryGetMetadataSnapshot = tryGetMetadataSnapshot, - keepAllBackgroundSymbolUses = false, + keepAllBackgroundSymbolUses = true, enableBackgroundItemKeyStoreAndSemanticClassification = true, enablePartialTypeChecking = true) checker diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index 9f1c8dd9812..972d099f4bd 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -91,28 +91,6 @@ module private FSharpProjectOptionsHelpers = else hasProjectVersionChanged - type Document with - - member this.ToFSharpDocument() = - let dt = DateTime.UtcNow - let getTimeStamp = fun () -> dt - - let mutable weakFSharpText = Unchecked.defaultof<_> - let getSourceText = fun () -> - match weakFSharpText with - | null -> - let fsharpText = this.GetTextAsync().Result.ToFSharpSourceText() - weakFSharpText <- WeakReference<_>(fsharpText) - fsharpText - | _ -> - match weakFSharpText.TryGetTarget() with - | true, fsharpText -> fsharpText - | _ -> - let fsharpText = this.GetTextAsync().Result.ToFSharpSourceText() - weakFSharpText <- WeakReference<_>(fsharpText) - fsharpText - FSharpDocument.Create(this.FilePath, getTimeStamp, getSourceText) - [] type private FSharpProjectOptionsMessage = | TryGetOptionsByDocument of Document * AsyncReplyChannel<(FSharpParsingOptions * FSharpProjectOptions) option> * CancellationToken * userOpName: string diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index 3ed760c5bae..e49dcea1874 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -28,8 +28,7 @@ module internal SymbolHelpers = let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) 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, projectOptions, settings.LanguageServicePerformance, userOpName = userOpName) + let! _, checkFileResults = checker.CheckDocumentInProject(document, projectOptions) |> liftAsync 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) @@ -123,7 +122,7 @@ module internal SymbolHelpers = let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, symbolSpan.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, userOpName = userOpName) + let! _, checkFileResults = checker.CheckDocumentInProject(document, projectOptions) |> liftAsync let textLine = sourceText.Lines.GetLineFromPosition(symbolSpan.Start) let textLinePos = sourceText.Lines.GetLinePosition(symbolSpan.Start) let fcsTextLineNumber = Line.fromZ textLinePos.Line diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index 53a0e68bd25..2548dde0d35 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, userOpName = userOpName) + let! _, checkFileResults = checker.CheckDocumentInProject(document, projectOptions) |> liftAsync 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 240207ad780..d6faf95e8cf 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -186,7 +186,7 @@ type internal GoToDefinition(checkerProvider: FSharpCheckerProvider, projectInfo let fcsTextLineNumber = Line.fromZ textLinePos.Line let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() - let! _, _, checkFileResults = checker.ParseAndCheckDocument (originDocument, projectOptions, userOpName=userOpName) + let! _, checkFileResults = checker.CheckDocumentInProject(originDocument, projectOptions) |> liftAsync let idRange = lexerSymbol.Ident.idRange let! fsSymbolUse = checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) let symbol = fsSymbolUse.Symbol @@ -199,7 +199,7 @@ type internal GoToDefinition(checkerProvider: FSharpCheckerProvider, projectInfo 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, userOpName=userOpName) + let! _, checkFileResults = checker.CheckDocumentInProject(implDoc, projectOptions) |> liftAsync let symbolUses = checkFileResults.GetUsesOfSymbolInFile symbol let! implSymbol = symbolUses |> Array.tryHead let! implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (implSourceText, implSymbol.Range) @@ -218,7 +218,7 @@ type internal GoToDefinition(checkerProvider: FSharpCheckerProvider, projectInfo match targetSymbolUse.Symbol.DeclarationLocation with | Some decl when decl.FileName = filePath -> return decl | _ -> - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, userOpName) + let! _, checkFileResults = checker.CheckDocumentInProject(document, options) |> liftAsync let symbolUses = checkFileResults.GetUsesOfSymbolInFile targetSymbolUse.Symbol let! implSymbol = symbolUses |> Array.tryHead return implSymbol.Range @@ -237,7 +237,7 @@ type internal GoToDefinition(checkerProvider: FSharpCheckerProvider, projectInfo let preferSignature = isSignatureFile originDocument.FilePath - let! _, _, checkFileResults = checker.ParseAndCheckDocument (originDocument, projectOptions, userOpName=userOpName) + let! _, checkFileResults = checker.CheckDocumentInProject(originDocument, projectOptions) |> liftAsync let! lexerSymbol = Tokenizer.getSymbolAtPosition (originDocument.Id, sourceText, position,originDocument.FilePath, defines, SymbolLookupKind.Greedy, false, false) let idRange = lexerSymbol.Ident.idRange @@ -426,7 +426,7 @@ type internal GoToDefinition(checkerProvider: FSharpCheckerProvider, projectInfo let goToAsync = asyncMaybe { let! _, _, projectOptions = projectInfoManager.TryGetOptionsForDocumentOrProject (tmpShownDoc, cancellationToken, userOpName) - let! _, _, checkResults = checker.ParseAndCheckDocument(tmpShownDoc, projectOptions, userOpName) + let! _, checkResults = checker.CheckDocumentInProject(tmpShownDoc, projectOptions) |> liftAsync let! r = let rec areTypesEqual (ty1: FSharpType) (ty2: FSharpType) = let ty1 = ty1.StripAbbreviations() diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs index 821dab6624c..a9f79c843a2 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs @@ -181,34 +181,31 @@ type internal FSharpNavigateToSearchService let GetNavigableItems(document: Document, parsingOptions: FSharpParsingOptions, kinds: IImmutableSet) = async { + let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions) let! cancellationToken = Async.CancellationToken - 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)) - - let items = parseResults.ParseTree |> navItems - let navigableItems = - [| - for item in items do - match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, item.Range) with - | None -> () - | Some sourceSpan -> - let glyph = navigateToItemKindToGlyph item.Kind - let kind = navigateToItemKindToRoslynKind item.Kind - let additionalInfo = containerToString item.Container document - let _name = - if isSignatureFile document.FilePath then - item.Name + " (signature)" - else - item.Name - yield NavigableItem(document, sourceSpan, glyph, item.Name, kind, additionalInfo) - |] - return navigableItems + let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask + let navItems parsedInput = + NavigateTo.GetNavigableItems parsedInput + |> Array.filter (fun i -> kinds.Contains(navigateToItemKindToRoslynKind i.Kind)) + + let items = parseResults.ParseTree |> navItems + let navigableItems = + [| + for item in items do + match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, item.Range) with + | None -> () + | Some sourceSpan -> + let glyph = navigateToItemKindToGlyph item.Kind + let kind = navigateToItemKindToRoslynKind item.Kind + let additionalInfo = containerToString item.Container document + let _name = + if isSignatureFile document.FilePath then + item.Name + " (signature)" + else + item.Name + yield NavigableItem(document, sourceSpan, glyph, item.Name, kind, additionalInfo) + |] + return navigableItems } let getCachedIndexedNavigableItems(document: Document, parsingOptions: FSharpParsingOptions, kinds: IImmutableSet) = diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs index c83fb363a2b..6e946ac29a3 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs @@ -28,9 +28,9 @@ type internal FSharpNavigationBarItemService member _.GetItemsAsync(document, cancellationToken) : Task> = asyncMaybe { let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) - let! sourceText = document.GetTextAsync(cancellationToken) - let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName=userOpName) + let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions) |> liftAsync let navItems = Navigation.getNavigation parseResults.ParseTree + let! sourceText = document.GetTextAsync(cancellationToken) 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 0d383f76136..7e3d882d216 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, userOpName = userOpName) + let! _, extCheckFileResults = checker.CheckDocumentInProject(extDocument, extProjectOptions) |> liftAsync 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, userOpName = userOpName) + let! _, checkFileResults = checker.CheckDocumentInProject(document, projectOptions) |> liftAsync let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() @@ -177,9 +177,9 @@ type internal FSharpAsyncQuickInfoSource ) = // test helper - static member ProvideQuickInfo(checker:FSharpChecker, document: Document, position:int, parsingOptions:FSharpParsingOptions, options:FSharpProjectOptions, languageServicePerformanceOptions: LanguageServicePerformanceOptions) = + static member ProvideQuickInfo(checker:FSharpChecker, document: Document, position:int, parsingOptions:FSharpParsingOptions, options:FSharpProjectOptions, _languageServicePerformanceOptions: LanguageServicePerformanceOptions) = asyncMaybe { - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, languageServicePerformanceOptions, userOpName=FSharpQuickInfo.userOpName) + let! _, checkFileResults = checker.CheckDocumentInProject(document, options) |> liftAsync let! sourceText = document.GetTextAsync() let filePath = document.FilePath let textLine = sourceText.Lines.GetLineFromPosition position diff --git a/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs b/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs index f2c73274dd4..89c659af6aa 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, userOpName=userOpName) + let! parseFileResults, checkFileResults = checker.CheckDocumentInProject(document, projectOptions) |> liftAsync 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/Refactor/ChangeDerefToValueRefactoring.fs b/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs index 2b51b4c3444..c6cdec2c792 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs @@ -32,7 +32,7 @@ type internal FSharpChangeDerefToValueRefactoring let document = context.Document let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName=userOpName) + let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions) |> liftAsync let selectionRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) let! derefRange = parseResults.TryRangeOfRefCellDereferenceContainingPos selectionRange.Start diff --git a/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs b/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs index e1ef1dcf8af..3731c35065c 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs @@ -32,7 +32,7 @@ type internal FSharpChangeTypeofWithNameToNameofExpressionRefactoring let document = context.Document let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName=userOpName) + let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions) |> liftAsync let selectionRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) let! namedTypeOfResults = parseResults.TryRangeOfTypeofWithNameAndTypeExpr(selectionRange.Start) diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index 2a4a7a58da6..eee1fe06598 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -151,7 +151,7 @@ type internal FSharpBlockStructureService [] (checkerProvi asyncMaybe { let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) let! sourceText = document.GetTextAsync(cancellationToken) - let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName) + let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions) |> liftAsync return createBlockSpans document.FSharpOptions.Advanced.IsBlockStructureEnabled sourceText parseResults.ParseTree |> Seq.toImmutableArray } |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) diff --git a/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs b/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs index f77a3e6e9cf..49d48964c48 100644 --- a/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs +++ b/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs @@ -53,7 +53,7 @@ module GoToDefinitionServiceTests = let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line 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 _, checkFileResults = checker.CheckDocumentInProject(document, options) |> Async.RunSynchronously let declarations = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, false) diff --git a/vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs b/vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs index 3846e2b6308..1785aa5ffa0 100644 --- a/vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs +++ b/vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs @@ -29,13 +29,13 @@ type SemanticClassificationServiceTests() = } let checker = FSharpChecker.Create() - let perfOptions = { LanguageServicePerformanceOptions.Default with AllowStaleCompletionResults = false } + let _perfOptions = { LanguageServicePerformanceOptions.Default with AllowStaleCompletionResults = false } let getRanges (source: string) : SemanticClassificationItem list = let projectOptions = { projectOptions with ProjectId = Some(Guid.NewGuid().ToString()) } asyncMaybe { let document, _ = RoslynTestHelpers.CreateDocument(filePath, source) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, perfOptions, "") + let! _, checkFileResults = checker.CheckDocumentInProject(document, projectOptions) |> liftAsync return checkFileResults.GetSemanticClassification(None) } |> Async.RunSynchronously diff --git a/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs b/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs index df7ff193736..961f20d711f 100644 --- a/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs +++ b/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs @@ -53,14 +53,12 @@ let GetSignatureHelp (project:FSharpProject) (fileName:string) (caretPosition:in let textLines = sourceText.Lines let caretLinePos = textLines.GetLinePosition(caretPosition) let caretLineColumn = caretLinePos.Character - let perfOptions = LanguageServicePerformanceOptions.Default + let _perfOptions = LanguageServicePerformanceOptions.Default let document = RoslynTestHelpers.CreateDocument(fileName, sourceText) - let parseResults, _, checkFileResults = - let x = - checker.ParseAndCheckDocument(document, project.Options, perfOptions, "TestSignatureHelpProvider") - |> Async.RunSynchronously - x.Value + let parseResults, checkFileResults = + checker.CheckDocumentInProject(document, project.Options) + |> Async.RunSynchronously let paramInfoLocations = parseResults.FindParameterLocations(Position.fromZ caretLinePos.Line caretLineColumn).Value let triggered = @@ -103,17 +101,12 @@ let assertSignatureHelpForMethodCalls (fileContents: string) (marker: string) (e let textLines = sourceText.Lines let caretLinePos = textLines.GetLinePosition(caretPosition) let caretLineColumn = caretLinePos.Character - let perfOptions = LanguageServicePerformanceOptions.Default + let _perfOptions = LanguageServicePerformanceOptions.Default let document = RoslynTestHelpers.CreateDocument(filePath, sourceText) - let parseResults, _, checkFileResults = - let x = - checker.ParseAndCheckDocument(document, projectOptions, perfOptions, "TestSignatureHelpProvider") - |> Async.RunSynchronously - - if x.IsNone then - Assert.Fail("Could not parse and check document.") - x.Value + let parseResults, checkFileResults = + checker.CheckDocumentInProject(document, projectOptions) + |> Async.RunSynchronously let actual = let paramInfoLocations = parseResults.FindParameterLocations(Position.fromZ caretLinePos.Line caretLineColumn) @@ -142,16 +135,11 @@ let assertSignatureHelpForMethodCalls (fileContents: string) (marker: string) (e let assertSignatureHelpForFunctionApplication (fileContents: string) (marker: string) expectedArgumentCount expectedArgumentIndex = let caretPosition = fileContents.LastIndexOf(marker) + marker.Length let document, sourceText = RoslynTestHelpers.CreateDocument(filePath, fileContents) - let perfOptions = LanguageServicePerformanceOptions.Default + let _perfOptions = LanguageServicePerformanceOptions.Default - let parseResults, _, checkFileResults = - let x = - checker.ParseAndCheckDocument(document, projectOptions, perfOptions, "TestSignatureHelpProvider") - |> Async.RunSynchronously - - if x.IsNone then - Assert.Fail("Could not parse and check document.") - x.Value + let parseResults, checkFileResults = + checker.CheckDocumentInProject(document, projectOptions) + |> Async.RunSynchronously let adjustedColumnInSource = let rec loop ch pos = @@ -429,16 +417,11 @@ M.f let caretPosition = fileContents.IndexOf(marker) + marker.Length let document, sourceText = RoslynTestHelpers.CreateDocument(filePath, fileContents) - let perfOptions = LanguageServicePerformanceOptions.Default + let _perfOptions = LanguageServicePerformanceOptions.Default - let parseResults, _, checkFileResults = - let x = - checker.ParseAndCheckDocument(document, projectOptions, perfOptions, "TestSignatureHelpProvider") - |> Async.RunSynchronously - - if x.IsNone then - Assert.Fail("Could not parse and check document.") - x.Value + let parseResults, checkFileResults = + checker.CheckDocumentInProject(document, projectOptions) + |> Async.RunSynchronously let adjustedColumnInSource = let rec loop ch pos = From db56ab1649c569fe4b92eee4f9e25dd97c6fbdbb Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 9 Jun 2021 18:13:41 -0700 Subject: [PATCH 18/27] Added IsOpen to FSharpDocument --- src/fsharp/service/FSharpDocument.fs | 26 +++++++++---- src/fsharp/service/FSharpDocument.fsi | 10 ++++- src/fsharp/service/IncrementalBuild.fs | 39 ++++++++++++++----- .../src/FSharp.Editor/Common/Extensions.fs | 4 +- .../LanguageService/FSharpCheckerProvider.fs | 4 +- 5 files changed, 60 insertions(+), 23 deletions(-) diff --git a/src/fsharp/service/FSharpDocument.fs b/src/fsharp/service/FSharpDocument.fs index e2046c34fc1..330546324b0 100644 --- a/src/fsharp/service/FSharpDocument.fs +++ b/src/fsharp/service/FSharpDocument.fs @@ -61,25 +61,31 @@ type FSharpDocument internal () = abstract TimeStamp : DateTime + abstract IsOpen : bool + abstract GetText : unit -> DocumentText -type private FSharpDocumentMemoryMappedFile(filePath: string, timeStamp: DateTime, openStream: unit -> Stream) = +type private FSharpDocumentMemoryMappedFile(filePath: string, timeStamp: DateTime, isOpen, openStream: unit -> Stream) = inherit FSharpDocument() override _.FilePath = filePath override _.TimeStamp = timeStamp + override _.IsOpen = isOpen + override _.GetText() = openStream () |> DocumentText.Stream -type private FSharpDocumentByteArray(filePath: string, timeStamp: DateTime, bytes: byte[]) = +type private FSharpDocumentByteArray(filePath: string, timeStamp: DateTime, isOpen, bytes: byte[]) = inherit FSharpDocument() override _.FilePath = filePath override _.TimeStamp = timeStamp + override _.IsOpen = isOpen + override _.GetText() = DocumentText.Stream(new MemoryStream(bytes, 0, bytes.Length, false) :> Stream) @@ -90,36 +96,40 @@ type private FSharpDocumentFromFile(filePath: string) = override _.TimeStamp = FileSystem.GetLastWriteTimeShim(filePath) + override _.IsOpen = false + override _.GetText() = DocumentText.OnDisk -type private FSharpDocumentCustom(filePath: string, getTimeStamp, getSourceText) = +type private FSharpDocumentCustom(filePath: string, isOpen, getTimeStamp, getSourceText) = inherit FSharpDocument() override _.FilePath = filePath override _.TimeStamp = getTimeStamp() + override _.IsOpen = isOpen + override _.GetText() = DocumentText.SourceText(getSourceText()) type FSharpDocument with - static member Create(filePath, getTimeStamp, getSourceText) = - FSharpDocumentCustom(filePath, getTimeStamp, getSourceText) :> FSharpDocument + static member Create(filePath, isOpen, getTimeStamp, getSourceText) = + FSharpDocumentCustom(filePath, isOpen, getTimeStamp, getSourceText) :> FSharpDocument static member CreateFromFile(filePath: string) = FSharpDocumentFromFile(filePath) :> FSharpDocument - static member CreateCopyFromFile(filePath: string) = + static member CreateCopyFromFile(filePath: string, isOpen) = let timeStamp = FileSystem.GetLastWriteTimeShim(filePath) // We want to use mmaped documents only when // not running on mono, since its MemoryMappedFile implementation throws when "mapName" is not provided (is null), (see: https://github.com/mono/mono/issues/10245) if runningOnMono then let bytes = FileSystem.OpenFileForReadShim(filePath, useMemoryMappedFile = false).ReadAllBytes() - FSharpDocumentByteArray(filePath, timeStamp, bytes) :> FSharpDocument + FSharpDocumentByteArray(filePath, timeStamp, isOpen, bytes) :> FSharpDocument else let openStream = fun () -> FileSystem.OpenFileForReadShim(filePath, useMemoryMappedFile = true, shouldShadowCopy = true) - FSharpDocumentMemoryMappedFile(filePath, timeStamp, openStream) :> FSharpDocument + FSharpDocumentMemoryMappedFile(filePath, timeStamp, isOpen, openStream) :> FSharpDocument diff --git a/src/fsharp/service/FSharpDocument.fsi b/src/fsharp/service/FSharpDocument.fsi index f1f3258f5d4..42dfb672556 100644 --- a/src/fsharp/service/FSharpDocument.fsi +++ b/src/fsharp/service/FSharpDocument.fsi @@ -17,14 +17,20 @@ type internal DocumentText = [] type FSharpDocument = + /// The file path of the document. abstract FilePath : string + /// The timestamp of the document. abstract TimeStamp : DateTime + /// Is the document open in an editor? + /// This is used to allow the background build to provide rich information on the document. + abstract IsOpen : bool + abstract internal GetText : unit -> DocumentText static member internal CreateFromFile : filePath: string -> FSharpDocument - static member CreateCopyFromFile : filePath: string -> FSharpDocument + static member CreateCopyFromFile : filePath: string * isOpen: bool -> FSharpDocument - static member Create : filePath: string * getTimeStamp: (unit -> DateTime) * getSourceText: (unit -> ISourceText) -> FSharpDocument \ No newline at end of file + static member Create : filePath: string * isOpen: bool * getTimeStamp: (unit -> DateTime) * getSourceText: (unit -> ISourceText) -> FSharpDocument \ No newline at end of file diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index d8796ef7162..e6143dafb76 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -161,6 +161,8 @@ module IncrementalBuildSyntaxTree = member _.FileName = filename + member _.Document = doc + /// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. [] type TcInfo = @@ -325,14 +327,10 @@ type BoundModel private (tcConfig: TcConfig, | _ -> None - /// If partial type-checking is enabled, - /// this will create a new bound-model that will only have the partial state if the + /// This will create a new bound-model that will only have the partial state if the /// the current bound-model has the full state. member this.ClearTcInfoExtras() = - let hasSig = this.BackingSignature.IsSome - - // If partial checking is enabled and we have a backing sig file, then use the partial state. The partial state contains the sig state. - if tcInfoNode.HasFull && enablePartialTypeChecking && hasSig then + if tcInfoNode.HasFull then // Always invalidate the syntax tree cache. let newSyntaxTreeOpt = syntaxTreeOpt @@ -517,7 +515,7 @@ type BoundModel private (tcConfig: TcConfig, None } - if partialCheck then + if partialCheck && not syntaxTree.Document.IsOpen then return PartialState tcInfo else let! prevTcInfoOptional = prevTcInfoExtras @@ -551,8 +549,8 @@ type BoundModel private (tcConfig: TcConfig, { /// Only keep the typed interface files when doing a "full" build for fsc.exe, otherwise just throw them away latestImplFile = if keepAssemblyContents then implFile else None - tcResolutionsRev = (if keepAllBackgroundResolutions then sink.GetResolutions() else TcResolutions.Empty) :: prevTcInfoOptional.tcResolutionsRev - tcSymbolUsesRev = (if keepAllBackgroundSymbolUses then sink.GetSymbolUses() else TcSymbolUses.Empty) :: prevTcInfoOptional.tcSymbolUsesRev + tcResolutionsRev = (if keepAllBackgroundResolutions || syntaxTree.Document.IsOpen then sink.GetResolutions() else TcResolutions.Empty) :: prevTcInfoOptional.tcResolutionsRev + tcSymbolUsesRev = (if keepAllBackgroundSymbolUses || syntaxTree.Document.IsOpen then sink.GetSymbolUses() else TcSymbolUses.Empty) :: prevTcInfoOptional.tcSymbolUsesRev tcOpenDeclarationsRev = sink.GetOpenDeclarations() :: prevTcInfoOptional.tcOpenDeclarationsRev itemKeyStore = itemKeyStore semanticClassificationKeyStore = semanticClassification @@ -1030,7 +1028,28 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment boundModels = boundModels.ToImmutable() } else - state + let _, doc, _ = fileInfo + let _, currentDoc, _ = state.documents.[slot] + if currentDoc.IsOpen && not doc.IsOpen then + let state = + { state with + documents = state.documents.RemoveAt(slot).Insert(slot, fileInfo) + } + let currentBoundModel = state.boundModels.[slot] + match currentBoundModel.TryPeekValue() with + | ValueSome currentBoundModel -> + let boundModel = currentBoundModel.ClearTcInfoExtras() + { state with + boundModels = state.boundModels.RemoveAt(slot).Insert(slot, GraphNode(node { return boundModel })) + } + | _ -> + state + elif not currentDoc.IsOpen && doc.IsOpen then + { state with + documents = state.documents.RemoveAt(slot).Insert(slot, fileInfo) + } + else + state and computeStampedFileNames mainState state = let mutable i = 0 diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index d63471c18d4..40ac2c767d3 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -311,4 +311,6 @@ type Document with let fsharpText = this.GetTextAsync().Result.ToFSharpSourceText() weakFSharpText <- WeakReference<_>(fsharpText) fsharpText - FSharpDocument.Create(this.FilePath, getTimeStamp, getSourceText) + + let isOpen = this.Project.Solution.Workspace.IsDocumentOpen(this.Id) + FSharpDocument.Create(this.FilePath, isOpen, getTimeStamp, getSourceText) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs index 1d69615d68d..ae2f2ca3bf4 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs @@ -57,12 +57,12 @@ type internal FSharpCheckerProvider let checker = FSharpChecker.Create( projectCacheSize = settings.LanguageServicePerformance.ProjectCheckCacheSize, - keepAllBackgroundResolutions = true, + keepAllBackgroundResolutions = false, // Enabling this would mean that if devenv.exe goes above 2.3GB we do a one-off downsize of the F# Compiler Service caches (* , MaxMemory = 2300 *) legacyReferenceResolver=LegacyMSBuildReferenceResolver.getResolver(), tryGetMetadataSnapshot = tryGetMetadataSnapshot, - keepAllBackgroundSymbolUses = true, + keepAllBackgroundSymbolUses = false, enableBackgroundItemKeyStoreAndSemanticClassification = true, enablePartialTypeChecking = true) checker From 824bf1cc2e5837dda3382383af470d7067b3c3fd Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 15 Jun 2021 17:33:38 -0700 Subject: [PATCH 19/27] Fixed a few issues --- .../service/SemanticClassificationKey.fs | 5 ++-- .../FSharpProjectOptionsManager.fs | 24 ++++++++++++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/fsharp/service/SemanticClassificationKey.fs b/src/fsharp/service/SemanticClassificationKey.fs index 960d3fade56..42962053074 100644 --- a/src/fsharp/service/SemanticClassificationKey.fs +++ b/src/fsharp/service/SemanticClassificationKey.fs @@ -56,8 +56,9 @@ type SemanticClassificationKeyStoreBuilder() = let b = BlobBuilder() member _.WriteAll (semanticClassification: SemanticClassificationItem[]) = - use ptr = fixed semanticClassification - b.WriteBytes(NativePtr.ofNativeInt (NativePtr.toNativeInt ptr), semanticClassification.Length * sizeof) + if semanticClassification.Length > 0 then + use ptr = fixed semanticClassification + b.WriteBytes(NativePtr.ofNativeInt (NativePtr.toNativeInt ptr), semanticClassification.Length * sizeof) member _.TryBuildAndReset() = if b.Count > 0 then diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index 972d099f4bd..70d378b64b3 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -75,12 +75,9 @@ module private FSharpProjectOptionsHelpers = let p2 = newProject.Solution.GetProject(p2.ProjectId) doesProjectIdDiffer || ( - if p1.Language = LanguageNames.FSharp then - p1.Version <> p2.Version - else - let v1 = p1.GetDependentVersionAsync(ct).Result - let v2 = p2.GetDependentVersionAsync(ct).Result - v1 <> v2 + let v1 = p1.GetDependentVersionAsync(ct).Result + let v2 = p2.GetDependentVersionAsync(ct).Result + v1 <> v2 ) ) @@ -326,6 +323,21 @@ type private FSharpProjectOptionsReactor (workspace: Workspace, settings: Editor checkerProvider.Checker.InvalidateConfiguration(projectOptions, userOpName = "tryComputeOptions") + let docsToUpdate = + project.DocumentIds + |> Seq.filter (fun x -> workspace.IsDocumentOpen(x)) + |> Array.ofSeq + + if docsToUpdate.Length > 0 then + let fsharpDocs = + docsToUpdate + |> Array.map (fun docId -> + let doc = project.GetDocument(docId) + doc.ToFSharpDocument() + ) + + do! checkerProvider.Checker.UpdateDocuments(projectOptions, fsharpDocs) + let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions) cache.[projectId] <- (project, parsingOptions, projectOptions) From 1a5fbf021a661cc91806b30fa67bad23f9cbd2ed Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 15 Jun 2021 17:40:58 -0700 Subject: [PATCH 20/27] Renamed UpdateDocuments to UpdateBackgroundDocuments --- src/fsharp/service/service.fs | 2 +- src/fsharp/service/service.fsi | 4 ++-- .../LanguageService/FSharpProjectOptionsManager.fs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index 38afb83a8f8..6a0f7ddfb72 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -1227,7 +1227,7 @@ type FSharpChecker(legacyReferenceResolver, let userOpName = defaultArg userOpName "Unknown" backgroundCompiler.InvalidateConfiguration(options, userOpName) - member _.UpdateDocuments(options: FSharpProjectOptions, docs) = + member _.UpdateBackgroundDocuments(options: FSharpProjectOptions, docs) = backgroundCompiler.UpdateDocuments(options, docs) |> Async.AwaitNodeCode diff --git a/src/fsharp/service/service.fsi b/src/fsharp/service/service.fsi index a6d80fde6a6..8134e7715e7 100644 --- a/src/fsharp/service/service.fsi +++ b/src/fsharp/service/service.fsi @@ -363,11 +363,11 @@ type public FSharpChecker = member InvalidateConfiguration: options: FSharpProjectOptions * ?userOpName: string -> unit /// - /// This function is called when the documents of a particular project need to be updated in a batch. + /// This function is called when the documents of a particular project need to be updated in a batch for the background build. /// /// The options for the project or script, used to determine active --define conditionals and other options relevant to parsing. /// FSharp documents - member UpdateDocuments: options: FSharpProjectOptions * docs: FSharpDocument seq -> Async + member UpdateBackgroundDocuments: options: FSharpProjectOptions * docs: FSharpDocument seq -> Async /// Clear the internal cache of the given projects. /// The given project options. diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index 70d378b64b3..3c8d48a7f3e 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -336,7 +336,7 @@ type private FSharpProjectOptionsReactor (workspace: Workspace, settings: Editor doc.ToFSharpDocument() ) - do! checkerProvider.Checker.UpdateDocuments(projectOptions, fsharpDocs) + do! checkerProvider.Checker.UpdateBackgroundDocuments(projectOptions, fsharpDocs) let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions) @@ -359,7 +359,7 @@ type private FSharpProjectOptionsReactor (workspace: Workspace, settings: Editor let doc = project.GetDocument(docId) doc.ToFSharpDocument()) - do! checkerProvider.Checker.UpdateDocuments(projectOptions, fsharpDocs) + do! checkerProvider.Checker.UpdateBackgroundDocuments(projectOptions, fsharpDocs) cache.[projectId] <- (project, parsingOptions, projectOptions) From 8f08191fc67bee4b14f986e2ba2150ac0bc6cd02 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 15 Jun 2021 17:41:59 -0700 Subject: [PATCH 21/27] Update SurfaceArea.netstandard.fs --- tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs index fd2d86c3972..849060dbca6 100644 --- a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs +++ b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs @@ -1992,7 +1992,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.CodeAnalysis.FSharpCheckFileAnswer]] CheckFileInProjectAllowingStaleCachedResults(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, 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.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[Microsoft.FSharp.Core.Unit] UpdateDocuments(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, System.Collections.Generic.IEnumerable`1[FSharp.Compiler.CodeAnalysis.FSharpDocument]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] UpdateBackgroundDocuments(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, System.Collections.Generic.IEnumerable`1[FSharp.Compiler.CodeAnalysis.FSharpDocument]) 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.FSharpCheckFileResults]] GetBackgroundCheckResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) From 2e33c401fd91604494e1cc09d3e2a11c77f91aa0 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 16 Jun 2021 16:56:24 -0700 Subject: [PATCH 22/27] minor fix --- src/fsharp/service/IncrementalBuild.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 13f625a7550..5681d1c7baa 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -968,7 +968,7 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment let syntaxTree = GetSyntaxTree mainState fileInfo GraphNode(node { let! prevBoundModel = prevBoundModelGraphNode.GetOrComputeValue() - return! TypeCheckTask mainState.enablePartialTypeChecking prevBoundModel syntaxTree + return! TypeCheckTask (mainState.enablePartialTypeChecking && not syntaxTree.Document.IsOpen) prevBoundModel syntaxTree }) static let rec createFinalizeBoundModelGraphNode mainState (boundModels: ImmutableArray>.Builder) = From f719a2b1b1b6da1020490c4c9174b084feda287a Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 16 Jun 2021 17:11:50 -0700 Subject: [PATCH 23/27] another fix --- src/fsharp/service/IncrementalBuild.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 5681d1c7baa..f3681758479 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -452,7 +452,7 @@ type BoundModel private (tcConfig: TcConfig, return res | Some syntaxTree -> let sigNameOpt = - if partialCheck then + if partialCheck && not syntaxTree.Document.IsOpen then this.BackingSignature else None From 4230a178b9d1e48c3cbd1d8f91eabe8f167ddfe7 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 16 Jun 2021 17:25:55 -0700 Subject: [PATCH 24/27] another fix --- src/fsharp/service/IncrementalBuild.fs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index f3681758479..8b96d450773 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -265,6 +265,10 @@ type BoundModel private (tcConfig: TcConfig, prevTcInfo: TcInfo, syntaxTreeOpt: SyntaxTree option, tcInfoStateOpt: TcInfoState option) as this = + let isDocOpen = + match syntaxTreeOpt with + | Some syntaxTree -> syntaxTree.Document.IsOpen + | _ -> false let tcInfoNode = match tcInfoStateOpt with @@ -279,7 +283,7 @@ type BoundModel private (tcConfig: TcConfig, let partialGraphNode = GraphNode(node { - if enablePartialTypeChecking then + if enablePartialTypeChecking && not isDocOpen then // Optimization so we have less of a chance to duplicate work. if fullGraphNode.IsComputing then let! tcInfo, _ = fullGraphNode.GetOrComputeValue() @@ -452,7 +456,7 @@ type BoundModel private (tcConfig: TcConfig, return res | Some syntaxTree -> let sigNameOpt = - if partialCheck && not syntaxTree.Document.IsOpen then + if partialCheck then this.BackingSignature else None @@ -512,7 +516,7 @@ type BoundModel private (tcConfig: TcConfig, None } - if partialCheck && not syntaxTree.Document.IsOpen then + if partialCheck then return PartialState tcInfo else // Build symbol keys From d59782fd4798b5a9f700060f61264ed2194caaca Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 16 Jun 2021 17:45:49 -0700 Subject: [PATCH 25/27] Trying to fix --- src/fsharp/service/IncrementalBuild.fs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 8b96d450773..b2c6a7691c1 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -994,20 +994,26 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment let stamp = StampFileNameTask fileInfo if currentStamp <> stamp then + let boundModels = state.boundModels.ToBuilder() match state.boundModels.[slot].TryPeekValue() with // This prevents an implementation file that has a backing signature file from invalidating the rest of the build. | ValueSome(boundModel) when mainState.enablePartialTypeChecking && boundModel.BackingSignature.IsSome -> - let newBoundModel = boundModel.ClearTcInfoExtras() + let newBoundModelNode = + match fileInfo with + | _, doc, _ when doc.IsOpen -> + createBoundModelGraphNode mainState state.documents boundModels slot + | _ -> + let newBoundModel = boundModel.ClearTcInfoExtras() + GraphNode(node { return newBoundModel }) { state with documents = state.documents.RemoveAt(slot).Insert(slot, fileInfo) - boundModels = state.boundModels.RemoveAt(slot).Insert(slot, GraphNode(node { return newBoundModel })) - stampedFileNames = state.stampedFileNames.SetItem(slot, StampFileNameTask fileInfo) + boundModels = state.boundModels.RemoveAt(slot).Insert(slot, newBoundModelNode) + stampedFileNames = state.stampedFileNames.SetItem(slot, stamp) } | _ -> let stampedFileNames = state.stampedFileNames.ToBuilder() let logicalStampedFileNames = state.logicalStampedFileNames.ToBuilder() - let boundModels = state.boundModels.ToBuilder() // Invalidate the file and all files below it. for j = 0 to stampedFileNames.Count - slot - 1 do From 08fdc6ec9bba342fad5b29a67e79f23dd82c8ccd Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 16 Jun 2021 19:29:57 -0700 Subject: [PATCH 26/27] Fixed thigns --- src/fsharp/service/IncrementalBuild.fs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index b2c6a7691c1..79ee96cb93a 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -998,15 +998,16 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment match state.boundModels.[slot].TryPeekValue() with // This prevents an implementation file that has a backing signature file from invalidating the rest of the build. | ValueSome(boundModel) when mainState.enablePartialTypeChecking && boundModel.BackingSignature.IsSome -> + let newDocuments = state.documents.RemoveAt(slot).Insert(slot, fileInfo) let newBoundModelNode = match fileInfo with | _, doc, _ when doc.IsOpen -> - createBoundModelGraphNode mainState state.documents boundModels slot + createBoundModelGraphNode mainState newDocuments boundModels slot | _ -> let newBoundModel = boundModel.ClearTcInfoExtras() GraphNode(node { return newBoundModel }) { state with - documents = state.documents.RemoveAt(slot).Insert(slot, fileInfo) + documents = newDocuments boundModels = state.boundModels.RemoveAt(slot).Insert(slot, newBoundModelNode) stampedFileNames = state.stampedFileNames.SetItem(slot, stamp) } From 31894a5a40c57ec180a8a773a13d321b7afc38c4 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Thu, 17 Jun 2021 17:55:04 -0700 Subject: [PATCH 27/27] Updating surface area --- src/fsharp/service/IncrementalBuild.fs | 8 ++++---- .../SurfaceArea.netstandard.fs | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/fsharp/service/IncrementalBuild.fs b/src/fsharp/service/IncrementalBuild.fs index 79ee96cb93a..d99c695f06f 100644 --- a/src/fsharp/service/IncrementalBuild.fs +++ b/src/fsharp/service/IncrementalBuild.fs @@ -995,10 +995,10 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment if currentStamp <> stamp then let boundModels = state.boundModels.ToBuilder() + let newDocuments = state.documents.RemoveAt(slot).Insert(slot, fileInfo) match state.boundModels.[slot].TryPeekValue() with // This prevents an implementation file that has a backing signature file from invalidating the rest of the build. | ValueSome(boundModel) when mainState.enablePartialTypeChecking && boundModel.BackingSignature.IsSome -> - let newDocuments = state.documents.RemoveAt(slot).Insert(slot, fileInfo) let newBoundModelNode = match fileInfo with | _, doc, _ when doc.IsOpen -> @@ -1018,16 +1018,16 @@ type IncrementalBuilder(mainState: IncrementalBuilderMainState, state: Increment // Invalidate the file and all files below it. for j = 0 to stampedFileNames.Count - slot - 1 do - let stamp = StampFileNameTask state.documents.[slot + j] + let stamp = StampFileNameTask newDocuments.[slot + j] stampedFileNames.[slot + j] <- stamp logicalStampedFileNames.[slot + j] <- stamp - boundModels.[slot + j] <- createBoundModelGraphNode mainState state.documents boundModels (slot + j) + boundModels.[slot + j] <- createBoundModelGraphNode mainState newDocuments boundModels (slot + j) { state with // Something changed, the finalized view of the project must be invalidated. finalizedBoundModel = createFinalizeBoundModelGraphNode mainState boundModels - documents = state.documents.RemoveAt(slot).Insert(slot, fileInfo) + documents = newDocuments stampedFileNames = stampedFileNames.ToImmutable() logicalStampedFileNames = logicalStampedFileNames.ToImmutable() boundModels = boundModels.ToImmutable() diff --git a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs index 849060dbca6..63af099ba31 100644 --- a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs +++ b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs @@ -2024,9 +2024,11 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateAll() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateConfiguration(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Void set_MaxMemory(Int32) FSharp.Compiler.CodeAnalysis.FSharpDocument +FSharp.Compiler.CodeAnalysis.FSharpDocument: Boolean IsOpen +FSharp.Compiler.CodeAnalysis.FSharpDocument: Boolean get_IsOpen() FSharp.Compiler.CodeAnalysis.FSharpDocument: FSharp.Compiler.CodeAnalysis.DocumentText GetText() -FSharp.Compiler.CodeAnalysis.FSharpDocument: FSharp.Compiler.CodeAnalysis.FSharpDocument Create(System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime], Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,FSharp.Compiler.Text.ISourceText]) -FSharp.Compiler.CodeAnalysis.FSharpDocument: FSharp.Compiler.CodeAnalysis.FSharpDocument CreateCopyFromFile(System.String) +FSharp.Compiler.CodeAnalysis.FSharpDocument: FSharp.Compiler.CodeAnalysis.FSharpDocument Create(System.String, Boolean, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime], Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,FSharp.Compiler.Text.ISourceText]) +FSharp.Compiler.CodeAnalysis.FSharpDocument: FSharp.Compiler.CodeAnalysis.FSharpDocument CreateCopyFromFile(System.String, Boolean) FSharp.Compiler.CodeAnalysis.FSharpDocument: System.DateTime TimeStamp FSharp.Compiler.CodeAnalysis.FSharpDocument: System.DateTime get_TimeStamp() FSharp.Compiler.CodeAnalysis.FSharpDocument: System.String FilePath