From fdba9745df33c20f86a2fef120b4d37df72d5d12 Mon Sep 17 00:00:00 2001 From: Jakub Majocha <1760221+majocha@users.noreply.github.com> Date: Tue, 14 Mar 2023 10:56:02 +0100 Subject: [PATCH 1/5] Cache parsing results in incremental build (#14852) --- docs/builder-caches.md | 13 + src/Compiler/Service/IncrementalBuild.fs | 265 +++++++++--------- src/Compiler/Service/IncrementalBuild.fsi | 3 +- src/Compiler/Service/service.fs | 23 +- src/Compiler/Service/service.fsi | 5 +- src/Compiler/Utilities/Activity.fs | 11 +- src/Compiler/Utilities/Activity.fsi | 7 + .../FSharpChecker/CommonWorkflows.fs | 76 +++++ ...ervice.SurfaceArea.netstandard20.debug.bsl | 2 +- ...vice.SurfaceArea.netstandard20.release.bsl | 2 +- .../ProjectGeneration.fs | 6 +- .../src/FSharp.Editor/FSharp.Editor.resx | 2 +- .../LanguageService/LanguageService.fs | 6 +- .../FSharp.Editor/Options/EditorOptions.fs | 6 +- .../FSharp.Editor/xlf/FSharp.Editor.cs.xlf | 4 +- .../FSharp.Editor/xlf/FSharp.Editor.de.xlf | 4 +- .../FSharp.Editor/xlf/FSharp.Editor.es.xlf | 4 +- .../FSharp.Editor/xlf/FSharp.Editor.fr.xlf | 4 +- .../FSharp.Editor/xlf/FSharp.Editor.it.xlf | 4 +- .../FSharp.Editor/xlf/FSharp.Editor.ja.xlf | 4 +- .../FSharp.Editor/xlf/FSharp.Editor.ko.xlf | 4 +- .../FSharp.Editor/xlf/FSharp.Editor.pl.xlf | 4 +- .../FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf | 4 +- .../FSharp.Editor/xlf/FSharp.Editor.ru.xlf | 4 +- .../FSharp.Editor/xlf/FSharp.Editor.tr.xlf | 4 +- .../xlf/FSharp.Editor.zh-Hans.xlf | 4 +- .../xlf/FSharp.Editor.zh-Hant.xlf | 4 +- ...nguageServicePerformanceOptionControl.xaml | 3 + .../FSharp.UIResources/Strings.Designer.cs | 9 + .../src/FSharp.UIResources/Strings.resx | 3 + .../src/FSharp.UIResources/xlf/Strings.cs.xlf | 5 + .../src/FSharp.UIResources/xlf/Strings.de.xlf | 5 + .../src/FSharp.UIResources/xlf/Strings.es.xlf | 5 + .../src/FSharp.UIResources/xlf/Strings.fr.xlf | 5 + .../src/FSharp.UIResources/xlf/Strings.it.xlf | 5 + .../src/FSharp.UIResources/xlf/Strings.ja.xlf | 5 + .../src/FSharp.UIResources/xlf/Strings.ko.xlf | 5 + .../src/FSharp.UIResources/xlf/Strings.pl.xlf | 5 + .../FSharp.UIResources/xlf/Strings.pt-BR.xlf | 5 + .../src/FSharp.UIResources/xlf/Strings.ru.xlf | 5 + .../src/FSharp.UIResources/xlf/Strings.tr.xlf | 5 + .../xlf/Strings.zh-Hans.xlf | 5 + .../xlf/Strings.zh-Hant.xlf | 5 + 43 files changed, 382 insertions(+), 177 deletions(-) create mode 100644 docs/builder-caches.md diff --git a/docs/builder-caches.md b/docs/builder-caches.md new file mode 100644 index 00000000000..90d7e2e471d --- /dev/null +++ b/docs/builder-caches.md @@ -0,0 +1,13 @@ +--- +title: IncrementalBuilder caches +category: Language Service Internals +categoryindex: 300 +index: 1300 +--- +# IncrementalBuilder SyntaxTree cache + +Incremental builder keeps in a cache at most one `ParsedInput` for each file it parses. +This behavior can be toggled with `useSyntaxTreeCache` parameter. + +Memory impact of this feature can be in range of tens of MB for larger solutions. This can be inspected in memory profilng tools by searching for `ParsedInput` instances. +When partial checking is enabled, implementation files backed by signature will not be parsed or cached, as expected. diff --git a/src/Compiler/Service/IncrementalBuild.fs b/src/Compiler/Service/IncrementalBuild.fs index 57562813eb9..bfc2d69c759 100644 --- a/src/Compiler/Service/IncrementalBuild.fs +++ b/src/Compiler/Service/IncrementalBuild.fs @@ -7,7 +7,6 @@ open System.Collections.Generic open System.Collections.Immutable open System.Diagnostics open System.IO -open System.IO.Compression open System.Threading open Internal.Utilities.Library open Internal.Utilities.Collections @@ -18,7 +17,6 @@ open FSharp.Compiler.CheckBasics 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 @@ -30,7 +28,6 @@ open FSharp.Compiler.IO open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.NameResolution open FSharp.Compiler.ParseAndCheckInputs -open FSharp.Compiler.Syntax.PrettyNaming open FSharp.Compiler.ScriptClosure open FSharp.Compiler.Syntax open FSharp.Compiler.TcGlobals @@ -41,14 +38,13 @@ open FSharp.Compiler.TypedTree open FSharp.Compiler.TypedTreeOps open FSharp.Compiler.BuildGraph - [] module internal IncrementalBuild = let mutable injectCancellationFault = false let LocallyInjectCancellationFault() = injectCancellationFault <- true - { new IDisposable with member _.Dispose() = injectCancellationFault <- false } + { new IDisposable with member _.Dispose() = injectCancellationFault <- false } // Record the most recent IncrementalBuilder events, so we can more easily unit test/debug the // 'incremental' behavior of the product. @@ -97,9 +93,16 @@ module IncrementalBuilderEventTesting = module Tc = CheckExpressions +type internal FSharpFile = { + Range: range + Source: FSharpSource + Flags: bool * bool + } + // This module is only here to contain the SyntaxTree type as to avoid ambiguity with the module FSharp.Compiler.Syntax. [] module IncrementalBuildSyntaxTree = + open System.Runtime.CompilerServices /// Information needed to lazily parse a file to get a ParsedInput. Internally uses a weak cache. [] @@ -107,78 +110,82 @@ module IncrementalBuildSyntaxTree = tcConfig: TcConfig, fileParsed: Event, lexResourceManager, - sourceRange: range, - source: FSharpSource, - isLastCompiland + file: FSharpFile, + useCache ) = - let fileName = source.FilePath - let mutable weakCache: WeakReference<_> option = None - - let parse(sigNameOpt: QualifiedNameOfFile option) = + static let cache = ConditionalWeakTable() + + let fileName = file.Source.FilePath + let sourceRange = file.Range + let source = file.Source + let isLastCompiland = file.Flags + + let isImplFile = FSharpImplFileSuffixes |> List.exists (FileSystemUtils.checkSuffix fileName) + + let parsedImplFileStub sigName = + ParsedInput.ImplFile( + ParsedImplFileInput( + fileName, + false, + sigName, + [], + [], + [], + isLastCompiland, + { ConditionalDirectives = []; CodeComments = [] }, + Set.empty + ) + ), sourceRange, fileName, [||] + let parse _ = let diagnosticsLogger = CompilationDiagnosticLogger("Parse", tcConfig.diagnosticsOptions) // Return the disposable object that cleans up use _holder = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parse) - - try - IncrementalBuilderEventTesting.MRU.Add(IncrementalBuilderEventTesting.IBEParsed fileName) - let canSkip = sigNameOpt.IsSome && FSharpImplFileSuffixes |> List.exists (FileSystemUtils.checkSuffix fileName) - use act = - Activity.start "IncrementalBuildSyntaxTree.parse" - [| - Activity.Tags.fileName, source.FilePath - "buildPhase", BuildPhase.Parse.ToString() - "canSkip", canSkip.ToString() - |] - let input = - if canSkip then - ParsedInput.ImplFile( - ParsedImplFileInput( - fileName, - false, - sigNameOpt.Value, - [], - [], - [], - isLastCompiland, - { ConditionalDirectives = []; CodeComments = [] }, - Set.empty - ) - ) - else - use text = source.GetTextContainer() - match text with - | TextContainer.Stream(stream) -> - ParseOneInputStream(tcConfig, lexResourceManager, fileName, isLastCompiland, diagnosticsLogger, false, stream) - | TextContainer.SourceText(sourceText) -> - ParseOneInputSourceText(tcConfig, lexResourceManager, fileName, isLastCompiland, diagnosticsLogger, sourceText) - | TextContainer.OnDisk -> - ParseOneInputFile(tcConfig, lexResourceManager, fileName, isLastCompiland, diagnosticsLogger, true) + try + use text = source.GetTextContainer() + let input = + match text with + | TextContainer.Stream(stream) -> + ParseOneInputStream(tcConfig, lexResourceManager, fileName, isLastCompiland, diagnosticsLogger, false, stream) + | TextContainer.SourceText(sourceText) -> + ParseOneInputSourceText(tcConfig, lexResourceManager, fileName, isLastCompiland, diagnosticsLogger, sourceText) + | TextContainer.OnDisk -> + ParseOneInputFile(tcConfig, lexResourceManager, fileName, isLastCompiland, diagnosticsLogger, true) fileParsed.Trigger fileName - let res = input, sourceRange, fileName, diagnosticsLogger.GetDiagnostics() - // If we do not skip parsing the file, then we can cache the real result. - if not canSkip then - weakCache <- Some(WeakReference<_>(res)) - res + input, sourceRange, fileName, diagnosticsLogger.GetDiagnostics() + with exn -> let msg = sprintf "unexpected failure in SyntaxTree.parse\nerror = %s" (exn.ToString()) System.Diagnostics.Debug.Assert(false, msg) failwith msg - /// Parse the given file and return the given input. - member _.Parse sigNameOpt = - match weakCache with - | Some weakCache -> - match weakCache.TryGetTarget() with - | true, res -> res - | _ -> parse sigNameOpt - | _ -> parse sigNameOpt + let parseOrSkip sigNameOpt = + IncrementalBuilderEventTesting.MRU.Add(IncrementalBuilderEventTesting.IBEParsed fileName) + use _ = + Activity.start "IncrementalBuildSyntaxTree.parseOrSkip" + [| + Activity.Tags.fileName, fileName + "buildPhase", BuildPhase.Parse.ToString() + "canSkip", (isImplFile && sigNameOpt |> Option.isSome).ToString() + |] + + match sigNameOpt with + | Some sigName when isImplFile -> parsedImplFileStub sigName + | _ when useCache -> + match cache.TryGetValue file with + | true, result -> + Activity.addEvent Activity.Events.cacheHit + result + | _ -> cache.GetValue(file, parse) + | _ -> parse file - member _.Invalidate() = - SyntaxTree(tcConfig, fileParsed, lexResourceManager, sourceRange, source, isLastCompiland) + /// Parse the given file and return the given input. + member _.Parse(sigNameOpt) = parseOrSkip sigNameOpt + + static member Invalidate(source) = cache.Remove(source) |> ignore member _.FileName = fileName @@ -352,11 +359,6 @@ type BoundModel private (tcConfig: TcConfig, // 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 - // Always invalidate the syntax tree cache. - let newSyntaxTreeOpt = - syntaxTreeOpt - |> Option.map (fun x -> x.Invalidate()) - let newTcInfoStateOpt = match tcInfoNode with | TcInfoNode(_, fullGraphNode) -> @@ -374,7 +376,7 @@ type BoundModel private (tcConfig: TcConfig, beforeFileChecked, fileChecked, prevTcInfo, - newSyntaxTreeOpt, + syntaxTreeOpt, newTcInfoStateOpt) else this @@ -736,11 +738,6 @@ type RawFSharpAssemblyDataBackedByLanguageService (tcConfig, tcGlobals, generate [] module IncrementalBuilderHelpers = - /// Get the timestamp of the given file name. - let StampFileNameTask (cache: TimeStampCache) (_m: range, source: FSharpSource, _isLastCompiland) notifiedTime = - notifiedTime - |> Option.defaultWith (fun () -> cache.GetFileTimeStamp source.FilePath) - /// Timestamps of referenced assemblies are taken from the file's timestamp. let StampReferencedAssemblyTask (cache: TimeStampCache) (_ref, timeStamper) = timeStamper cache @@ -949,8 +946,7 @@ module IncrementalBuilderHelpers = return ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt, finalBoundModelWithErrors } - let GetSyntaxTree tcConfig fileParsed lexResourceManager (sourceRange: range, source, isLastCompiland) = - SyntaxTree(tcConfig, fileParsed, lexResourceManager, sourceRange, source, isLastCompiland) + let GetSyntaxTree tcConfig fileParsed lexResourceManager file useCache = SyntaxTree(tcConfig, fileParsed, lexResourceManager, file, useCache) [] type IncrementalBuilderInitialState = @@ -962,7 +958,7 @@ type IncrementalBuilderInitialState = outfile: string assemblyName: string lexResourceManager: Lexhelp.LexResourceManager - fileNames: ImmutableArray + fileNames: ImmutableArray enablePartialTypeChecking: bool beforeFileChecked: Event fileChecked: Event @@ -975,26 +971,30 @@ type IncrementalBuilderInitialState = defaultTimeStamp: DateTime mutable isImportsInvalidated: bool useChangeNotifications: bool + useSyntaxTreeCache: bool } - static member Create( - initialBoundModel: BoundModel, - tcGlobals, - nonFrameworkAssemblyInputs, - tcConfig: TcConfig, - outfile, - assemblyName, - lexResourceManager, - sourceFiles, - enablePartialTypeChecking, - beforeFileChecked: Event, - fileChecked: Event, + static member Create + ( + initialBoundModel: BoundModel, + tcGlobals, + nonFrameworkAssemblyInputs, + tcConfig: TcConfig, + outfile, + assemblyName, + lexResourceManager, + sourceFiles, + enablePartialTypeChecking, + beforeFileChecked: Event, + fileChecked: Event, #if !NO_TYPEPROVIDERS - importsInvalidatedByTypeProvider: Event, + importsInvalidatedByTypeProvider: Event, #endif - allDependencies, - defaultTimeStamp: DateTime, - useChangeNotifications: bool) = + allDependencies, + defaultTimeStamp: DateTime, + useChangeNotifications: bool, + useSyntaxTreeCache + ) = let initialState = { @@ -1018,6 +1018,7 @@ type IncrementalBuilderInitialState = defaultTimeStamp = defaultTimeStamp isImportsInvalidated = false useChangeNotifications = useChangeNotifications + useSyntaxTreeCache = useSyntaxTreeCache } #if !NO_TYPEPROVIDERS importsInvalidatedByTypeProvider.Publish.Add(fun () -> initialState.isImportsInvalidated <- true) @@ -1043,12 +1044,12 @@ type IncrementalBuilderState = module IncrementalBuilderStateHelpers = let createBoundModelGraphNode (initialState: IncrementalBuilderInitialState) initialBoundModel (boundModels: ImmutableArray>.Builder) i = - let fileInfo = initialState.fileNames[i] + let file = initialState.fileNames[i] let prevBoundModelGraphNode = match i with | 0 (* first file *) -> initialBoundModel | _ -> boundModels[i - 1] - let syntaxTree = GetSyntaxTree initialState.tcConfig initialState.fileParsed initialState.lexResourceManager fileInfo + let syntaxTree = GetSyntaxTree initialState.tcConfig initialState.fileParsed initialState.lexResourceManager file initialState.useSyntaxTreeCache GraphNode(node { let! prevBoundModel = prevBoundModelGraphNode.GetOrComputeValue() return! TypeCheckTask initialState.enablePartialTypeChecking prevBoundModel syntaxTree @@ -1075,19 +1076,30 @@ module IncrementalBuilderStateHelpers = return result }) - and computeStampedFileName (initialState: IncrementalBuilderInitialState) (state: IncrementalBuilderState) (cache: TimeStampCache) slot fileInfo = - let currentStamp = state.stampedFileNames[slot] - let notifiedStamp = if initialState.useChangeNotifications then Some state.notifiedStampedFileNames[slot] else None - let stamp = StampFileNameTask cache fileInfo notifiedStamp + and computeStampedFileNames (initialState: IncrementalBuilderInitialState) (state: IncrementalBuilderState) (cache: TimeStampCache) = + + let getStamp slot = + if initialState.useChangeNotifications then + state.notifiedStampedFileNames[slot] + else + cache.GetFileTimeStamp initialState.fileNames[slot].Source.FilePath + + let modified = + [ for i, file in initialState.fileNames |> Seq.indexed do + let stamp = getStamp i + if state.stampedFileNames[i] <> stamp then + i, stamp, file ] - if currentStamp <> stamp then + for _, _, f in modified do SyntaxTree.Invalidate f + + let computeStampedFileName state (slot, stamp, _) = 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 initialState.enablePartialTypeChecking && boundModel.BackingSignature.IsSome -> 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 notifiedStamp) + boundModels = state.boundModels.SetItem(slot, GraphNode(node.Return newBoundModel)) + stampedFileNames = state.stampedFileNames.SetItem(slot, stamp) } | _ -> @@ -1096,12 +1108,11 @@ module IncrementalBuilderStateHelpers = let boundModels = state.boundModels.ToBuilder() // Invalidate the file and all files below it. - for j = 0 to stampedFileNames.Count - slot - 1 do - let notifiedStamp = if initialState.useChangeNotifications then Some state.notifiedStampedFileNames.[slot + j] else None - let stamp = StampFileNameTask cache initialState.fileNames[slot + j] notifiedStamp - stampedFileNames[slot + j] <- stamp - logicalStampedFileNames[slot + j] <- stamp - boundModels[slot + j] <- createBoundModelGraphNode initialState state.initialBoundModel boundModels (slot + j) + for j = slot to stampedFileNames.Count - 1 do + let stamp = getStamp j + stampedFileNames[j] <- stamp + logicalStampedFileNames[j] <- stamp + boundModels[j] <- createBoundModelGraphNode initialState state.initialBoundModel boundModels j { state with // Something changed, the finalized view of the project must be invalidated. @@ -1111,17 +1122,9 @@ module IncrementalBuilderStateHelpers = logicalStampedFileNames = logicalStampedFileNames.ToImmutable() boundModels = boundModels.ToImmutable() } - else - state - and computeStampedFileNames (initialState: IncrementalBuilderInitialState) state (cache: TimeStampCache) = - let mutable i = 0 - (state, initialState.fileNames) - ||> ImmutableArray.fold (fun state fileInfo -> - let newState = computeStampedFileName initialState state cache i fileInfo - i <- i + 1 - newState - ) + (state, modified) + ||> List.fold computeStampedFileName and computeStampedReferencedAssemblies (initialState: IncrementalBuilderInitialState) state canTriggerInvalidation (cache: TimeStampCache) = let stampedReferencedAssemblies = state.stampedReferencedAssemblies.ToBuilder() @@ -1406,10 +1409,10 @@ type IncrementalBuilder(initialState: IncrementalBuilderInitialState, state: Inc member _.TryGetSlotOfFileName(fileName: string) = // Get the slot of the given file and force it to build. - let CompareFileNames (_, f2: FSharpSource, _) = + let CompareFileNames f = 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, f.Source.FilePath, StringComparison.CurrentCultureIgnoreCase)=0 + || String.Compare(FileSystem.GetFullPathShim fileName, FileSystem.GetFullPathShim f.Source.FilePath, StringComparison.CurrentCultureIgnoreCase)=0 result match fileNames |> ImmutableArray.tryFindIndex CompareFileNames with | Some slot -> Some slot @@ -1427,9 +1430,8 @@ type IncrementalBuilder(initialState: IncrementalBuilderInitialState, state: Inc member builder.GetParseResultsForFile fileName = let slotOfFile = builder.GetSlotOfFileName fileName - let fileInfo = fileNames[slotOfFile] - // re-parse on demand instead of retaining - let syntaxTree = GetSyntaxTree initialState.tcConfig initialState.fileParsed initialState.lexResourceManager fileInfo + let file = initialState.fileNames[slotOfFile] + let syntaxTree = GetSyntaxTree initialState.tcConfig initialState.fileParsed initialState.lexResourceManager file initialState.useSyntaxTreeCache syntaxTree.Parse None member builder.NotifyFileChanged(fileName, timeStamp) = @@ -1441,7 +1443,7 @@ type IncrementalBuilder(initialState: IncrementalBuilderInitialState, state: Inc setCurrentState newState cache ct } - member _.SourceFiles = fileNames |> Seq.map (fun (_, f, _) -> f.FilePath) |> List.ofSeq + member _.SourceFiles = fileNames |> Seq.map (fun f -> f.Source.FilePath) |> List.ofSeq /// CreateIncrementalBuilder (for background type checking). Note that fsc.fs also /// creates an incremental builder used by the command line compiler. @@ -1467,7 +1469,8 @@ type IncrementalBuilder(initialState: IncrementalBuilderInitialState, state: Inc parallelReferenceResolution, captureIdentifiersWhenParsing, getSource, - useChangeNotifications + useChangeNotifications, + useSyntaxTreeCache ) = let useSimpleResolutionSwitch = "--simpleresolution" @@ -1678,16 +1681,16 @@ type IncrementalBuilder(initialState: IncrementalBuilderInitialState, state: Inc let getFSharpSource fileName = getSource |> Option.map(fun getSource -> - let getTimeStamp() = DateTime.UtcNow + let timeStamp = DateTime.UtcNow + let getTimeStamp = fun () -> timeStamp let getSourceText() = getSource fileName FSharpSource.Create(fileName, getTimeStamp, getSourceText)) |> Option.defaultWith(fun () -> FSharpSource.CreateFromFile(fileName)) let sourceFiles = sourceFiles - |> List.map (fun (m, fileName, isLastCompiland) -> - (m, getFSharpSource(fileName), isLastCompiland) - ) + |> List.map (fun (m, fileName, isLastCompiland) -> + { Range = m; Source = getFSharpSource fileName; Flags = isLastCompiland } ) let initialState = IncrementalBuilderInitialState.Create( @@ -1707,7 +1710,9 @@ type IncrementalBuilder(initialState: IncrementalBuilderInitialState, state: Inc #endif allDependencies, defaultTimeStamp, - useChangeNotifications) + useChangeNotifications, + useSyntaxTreeCache + ) let builder = IncrementalBuilder(initialState, IncrementalBuilderState.Create(initialState)) return Some builder diff --git a/src/Compiler/Service/IncrementalBuild.fsi b/src/Compiler/Service/IncrementalBuild.fsi index ee9b06380eb..a1c5fa70624 100644 --- a/src/Compiler/Service/IncrementalBuild.fsi +++ b/src/Compiler/Service/IncrementalBuild.fsi @@ -269,7 +269,8 @@ type internal IncrementalBuilder = parallelReferenceResolution: ParallelReferenceResolution * captureIdentifiersWhenParsing: bool * getSource: (string -> ISourceText option) option * - useChangeNotifications: bool -> + useChangeNotifications: bool * + useSyntaxTreeCache: bool -> NodeCode /// Generalized Incremental Builder. This is exposed only for unit testing purposes. diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index 65537dd67b8..8b68495389b 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -195,7 +195,8 @@ type BackgroundCompiler parallelReferenceResolution, captureIdentifiersWhenParsing, getSource: (string -> ISourceText option) option, - useChangeNotifications + useChangeNotifications, + useSyntaxTreeCache ) as self = let beforeFileChecked = Event() @@ -329,7 +330,8 @@ type BackgroundCompiler parallelReferenceResolution, captureIdentifiersWhenParsing, getSource, - useChangeNotifications + useChangeNotifications, + useSyntaxTreeCache ) match builderOpt with @@ -636,9 +638,6 @@ type BackgroundCompiler ) = node { - if useChangeNotifications then - do! builder.NotifyFileChanged(fileName, DateTime.UtcNow) - match! bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) with | Some (_, results) -> return FSharpCheckFileAnswer.Succeeded results | _ -> @@ -1263,7 +1262,8 @@ type FSharpChecker parallelReferenceResolution, captureIdentifiersWhenParsing, getSource, - useChangeNotifications + useChangeNotifications, + useSyntaxTreeCache ) = let backgroundCompiler = @@ -1280,7 +1280,8 @@ type FSharpChecker parallelReferenceResolution, captureIdentifiersWhenParsing, getSource, - useChangeNotifications + useChangeNotifications, + useSyntaxTreeCache ) static let globalInstance = lazy FSharpChecker.Create() @@ -1324,7 +1325,8 @@ type FSharpChecker ?enablePartialTypeChecking, ?parallelReferenceResolution: bool, ?captureIdentifiersWhenParsing: bool, - ?documentSource: DocumentSource + ?documentSource: DocumentSource, + ?useSyntaxTreeCache: bool ) = use _ = Activity.startNoTags "FSharpChecker.Create" @@ -1352,6 +1354,8 @@ type FSharpChecker | Some (DocumentSource.Custom _) -> true | _ -> false + let useSyntaxTreeCache = defaultArg useSyntaxTreeCache true + if keepAssemblyContents && enablePartialTypeChecking then invalidArg "enablePartialTypeChecking" "'keepAssemblyContents' and 'enablePartialTypeChecking' cannot be both enabled." @@ -1372,7 +1376,8 @@ type FSharpChecker (match documentSource with | Some (DocumentSource.Custom f) -> Some f | _ -> None), - useChangeNotifications + useChangeNotifications, + useSyntaxTreeCache ) member _.ReferenceResolver = legacyReferenceResolver diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index 52a355c9162..5d482b3cbed 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -41,6 +41,7 @@ type public FSharpChecker = /// Indicates whether to resolve references in parallel. /// When set to true we create a set of all identifiers for each parsed file which can be used to speed up finding references. /// Default: FileSystem. You can use Custom source to provide a function that will return the source for a given file path instead of reading it from the file system. Note that with this option the FSharpChecker will also not monitor the file system for file changes. It will expect to be notified of changes via the NotifyFileChanged method. + /// Default: true. Indicates whether to keep parsing results in a cache. static member Create: ?projectCacheSize: int * ?keepAssemblyContents: bool * @@ -53,7 +54,8 @@ type public FSharpChecker = ?enablePartialTypeChecking: bool * ?parallelReferenceResolution: bool * ?captureIdentifiersWhenParsing: bool * - [] ?documentSource: DocumentSource -> + [] ?documentSource: DocumentSource * + [] ?useSyntaxTreeCache: bool -> FSharpChecker /// @@ -383,7 +385,6 @@ type public FSharpChecker = member ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients: unit -> unit /// Notify the checker that given file has changed. This needs to be used when checker is created with documentSource = Custom. - /// Although it is not mandatory when the changed file is the next thing requested to be checked. [] member NotifyFileChanged: fileName: string * options: FSharpProjectOptions * ?userOpName: string -> Async diff --git a/src/Compiler/Utilities/Activity.fs b/src/Compiler/Utilities/Activity.fs index a5f8000cfa0..6305ce77e08 100644 --- a/src/Compiler/Utilities/Activity.fs +++ b/src/Compiler/Utilities/Activity.fs @@ -10,6 +10,8 @@ open System.Text [] module internal Activity = + let FscSourceName = "fsc" + module Tags = let fileName = "fileName" let project = "project" @@ -40,7 +42,10 @@ module internal Activity = outputDllFile |] - let private activitySourceName = "fsc" + module Events = + let cacheHit = "cacheHit" + + let private activitySourceName = FscSourceName let private profiledSourceName = "fsc_with_env_stats" type System.Diagnostics.Activity with @@ -75,6 +80,10 @@ module internal Activity = let startNoTags (name: string) : IDisposable = activitySource.StartActivity(name) + let addEvent name = + if Activity.Current <> null && Activity.Current.Source = activitySource then + Activity.Current.AddEvent(ActivityEvent(name)) |> ignore + module Profiling = module Tags = diff --git a/src/Compiler/Utilities/Activity.fsi b/src/Compiler/Utilities/Activity.fsi index 0f9647a4ecc..96d3844ea4f 100644 --- a/src/Compiler/Utilities/Activity.fsi +++ b/src/Compiler/Utilities/Activity.fsi @@ -9,6 +9,8 @@ open System [] module internal Activity = + val FscSourceName: string + module Tags = val fileName: string val qualifiedNameOfFile: string @@ -17,10 +19,15 @@ module internal Activity = val length: string val cache: string + module Events = + val cacheHit: string + val startNoTags: name: string -> IDisposable val start: name: string -> tags: (string * string) seq -> IDisposable + val addEvent: name: string -> unit + module Profiling = val startAndMeasureEnvironmentStats: name: string -> IDisposable val addConsoleListener: unit -> IDisposable diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs index 6b5775f23e7..ce7820ddf4e 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs @@ -2,6 +2,7 @@ open System open System.IO +open System.Diagnostics open Xunit @@ -10,6 +11,22 @@ open FSharp.Test.ProjectGeneration.Internal open FSharp.Compiler.Text open FSharp.Compiler.CodeAnalysis +module FcsDiagnostics = FSharp.Compiler.Diagnostics.Activity + +let expectCacheHits n = + let events = ResizeArray() + let listener = + new ActivityListener( + ShouldListenTo = (fun s -> s.Name = FcsDiagnostics.FscSourceName), + Sample = (fun _ -> ActivitySamplingResult.AllData), + ActivityStopped = (fun a -> events.AddRange a.Events) + ) + ActivitySource.AddActivityListener listener + { new IDisposable with + member this.Dispose() = + listener.Dispose() + Assert.Equal(n, events |> Seq.filter (fun e -> e.Name = FcsDiagnostics.Events.cacheHit) |> Seq.length) } + let makeTestProject () = SyntheticProject.Create( sourceFile "First" [], @@ -132,3 +149,62 @@ let ``Using getSource and notifications instead of filesystem`` () = checkFile middle expectSignatureChanged checkFile last expectSignatureChanged } + +[] +let ``Using getSource and notifications instead of filesystem with parse caching`` () = + + let size = 20 + + let project = + { SyntheticProject.Create() with + SourceFiles = [ + sourceFile $"File%03d{0}" [] + for i in 1..size do + sourceFile $"File%03d{i}" [$"File%03d{i-1}"] + ] + } + + let first = "File001" + let middle = $"File%03d{size / 2}" + let last = $"File%03d{size}" + + use _ = expectCacheHits 28 + ProjectWorkflowBuilder(project, useGetSource = true, useChangeNotifications = true, useSyntaxTreeCache = true) { + updateFile first updatePublicSurface + checkFile first expectSignatureChanged + checkFile last expectSignatureChanged + updateFile middle updatePublicSurface + checkFile last expectSignatureChanged + addFileAbove middle (sourceFile "addedFile" [first]) + updateFile middle (addDependency "addedFile") + checkFile middle expectSignatureChanged + checkFile last expectSignatureChanged + } + +[] +let ``Edit file, check it, then check dependent file with parse caching`` () = + use _ = expectCacheHits 1 + ProjectWorkflowBuilder(makeTestProject(), useSyntaxTreeCache = true) { + updateFile "First" breakDependentFiles + checkFile "First" expectSignatureChanged + saveFile "First" + checkFile "Second" expectErrors + } + +[] +let ``Edit file, don't check it, check dependent file with parse caching `` () = + use _ = expectCacheHits 1 + ProjectWorkflowBuilder(makeTestProject(), useSyntaxTreeCache = true) { + updateFile "First" breakDependentFiles + saveFile "First" + checkFile "Second" expectErrors + } + +[] +let ``Parse cache not used when not enabled`` () = + use _ = expectCacheHits 0 + ProjectWorkflowBuilder(makeTestProject(), useSyntaxTreeCache = false) { + updateFile "First" breakDependentFiles + saveFile "First" + checkFile "Second" expectErrors + } \ No newline at end of file diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index 615dddacdf9..98973518922 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -2031,7 +2031,7 @@ FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: FSharp.Compiler.Symbols. FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] DependencyFiles FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] get_DependencyFiles() -FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.DocumentSource]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.DocumentSource], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Instance FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker get_Instance() FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions GetProjectOptionsFromCommandLineArgs(System.String, System.String[], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 615dddacdf9..98973518922 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -2031,7 +2031,7 @@ FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: FSharp.Compiler.Symbols. FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] DependencyFiles FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] get_DependencyFiles() -FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.DocumentSource]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.DocumentSource], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Instance FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker get_Instance() FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions GetProjectOptionsFromCommandLineArgs(System.String, System.String[], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index cf5fae95066..7d789d00f4a 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -502,7 +502,8 @@ type ProjectWorkflowBuilder ?initialContext, ?checker: FSharpChecker, ?useGetSource, - ?useChangeNotifications + ?useChangeNotifications, + ?useSyntaxTreeCache ) = let useGetSource = defaultArg useGetSource false @@ -533,7 +534,8 @@ type ProjectWorkflowBuilder enableBackgroundItemKeyStoreAndSemanticClassification = true, enablePartialTypeChecking = true, captureIdentifiersWhenParsing = true, - documentSource = (if useGetSource then DocumentSource.Custom getSource else DocumentSource.FileSystem) + documentSource = (if useGetSource then DocumentSource.Custom getSource else DocumentSource.FileSystem), + useSyntaxTreeCache = defaultArg useSyntaxTreeCache false )) let mapProjectAsync f workflow = diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.resx b/vsintegration/src/FSharp.Editor/FSharp.Editor.resx index af946778b69..da310df50bf 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.resx +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.resx @@ -207,7 +207,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) Advanced diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 821a0ab8aad..fc45d4d7206 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -122,6 +122,9 @@ type internal FSharpWorkspaceServiceFactory let enableLiveBuffers = getOption (fun options -> options.Advanced.IsLiveBuffersEnabled) false + let useSyntaxTreeCache = + getOption (fun options -> options.LanguageServicePerformance.UseSyntaxTreeCache) LanguageServicePerformanceOptions.Default.UseSyntaxTreeCache + let checker = FSharpChecker.Create( projectCacheSize = 5000, // We do not care how big the cache is. VS will actually tell FCS to clear caches, so this is fine. @@ -133,7 +136,8 @@ type internal FSharpWorkspaceServiceFactory enablePartialTypeChecking = true, parallelReferenceResolution = enableParallelReferenceResolution, captureIdentifiersWhenParsing = true, - documentSource = (if enableLiveBuffers then DocumentSource.Custom getSource else DocumentSource.FileSystem)) + documentSource = (if enableLiveBuffers then DocumentSource.Custom getSource else DocumentSource.FileSystem), + useSyntaxTreeCache = useSyntaxTreeCache) if enableLiveBuffers then workspace.WorkspaceChanged.Add(fun args -> diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index 78f751999eb..6edfcc6cc1d 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -64,13 +64,15 @@ type LanguageServicePerformanceOptions = AllowStaleCompletionResults: bool TimeUntilStaleCompletion: int EnableParallelReferenceResolution: bool - EnableFastFindReferences: bool } + EnableFastFindReferences: bool + UseSyntaxTreeCache: bool } static member Default = { EnableInMemoryCrossProjectReferences = true AllowStaleCompletionResults = true TimeUntilStaleCompletion = 2000 // In ms, so this is 2 seconds EnableParallelReferenceResolution = false - EnableFastFindReferences = FSharpExperimentalFeaturesEnabledAutomatically } + EnableFastFindReferences = FSharpExperimentalFeaturesEnabledAutomatically + UseSyntaxTreeCache = FSharpExperimentalFeaturesEnabledAutomatically } [] type AdvancedOptions = diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf index 271152415d1..919b27ecb3e 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf @@ -159,7 +159,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) F# Project and Caching Performance Options; Enable in-memory cross project references; IntelliSense Performance Options; @@ -168,7 +168,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf index a264f044ed3..f43d8d69260 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf @@ -159,7 +159,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) F# Project and Caching Performance Options; Enable in-memory cross project references; IntelliSense Performance Options; @@ -168,7 +168,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf index 5c4e44f80c5..c63cb934417 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf @@ -159,7 +159,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) F# Project and Caching Performance Options; Enable in-memory cross project references; IntelliSense Performance Options; @@ -168,7 +168,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf index ba1cbf9f0bf..eb7ecbe01b5 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf @@ -159,7 +159,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) F# Project and Caching Performance Options; Enable in-memory cross project references; IntelliSense Performance Options; @@ -168,7 +168,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf index 3a287f0f36a..a67aed36350 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf @@ -159,7 +159,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) F# Project and Caching Performance Options; Enable in-memory cross project references; IntelliSense Performance Options; @@ -168,7 +168,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf index 6b5448db356..faf7498c3b5 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf @@ -159,7 +159,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) F# Project and Caching Performance Options; Enable in-memory cross project references; IntelliSense Performance Options; @@ -168,7 +168,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf index 20cac30b405..d4d2cbceedb 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf @@ -159,7 +159,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) F# Project and Caching Performance Options; Enable in-memory cross project references; IntelliSense Performance Options; @@ -168,7 +168,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf index aed9ab9c6f9..ed6fde565ff 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf @@ -159,7 +159,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) F# Project and Caching Performance Options; Enable in-memory cross project references; IntelliSense Performance Options; @@ -168,7 +168,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf index b86e03bc023..77b968844e4 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf @@ -159,7 +159,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) F# Project and Caching Performance Options; Enable in-memory cross project references; IntelliSense Performance Options; @@ -168,7 +168,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf index 4b491e1f465..a6d553a4876 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf @@ -159,7 +159,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) F# Project and Caching Performance Options; Enable in-memory cross project references; IntelliSense Performance Options; @@ -168,7 +168,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf index 75838b42f8c..7e5d5661fc2 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf @@ -159,7 +159,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) F# Project and Caching Performance Options; Enable in-memory cross project references; IntelliSense Performance Options; @@ -168,7 +168,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf index d664fbf723c..fdc3d9f27be 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf @@ -159,7 +159,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) F# Project and Caching Performance Options; Enable in-memory cross project references; IntelliSense Performance Options; @@ -168,7 +168,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf index 0ab59331fa4..5c6f726e41c 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf @@ -159,7 +159,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) F# Project and Caching Performance Options; Enable in-memory cross project references; IntelliSense Performance Options; @@ -168,7 +168,7 @@ Time until stale results are used (in milliseconds); Parallelization (requires restart); Enable parallel type checking with signature files; Enable parallel reference resolution; -Enable fast find references & rename (experimental) +Enable fast find references & rename (experimental);Cache parsing results (experimental) diff --git a/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml b/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml index b61f20c47f2..0a14570aaaf 100644 --- a/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml +++ b/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml @@ -23,6 +23,9 @@ IsChecked="{Binding EnableInMemoryCrossProjectReferences}" Content="{x:Static local:Strings.Enable_in_memory_cross_project_references}" ToolTip="{x:Static local:Strings.Tooltip_in_memory_cross_project_references}"/> + diff --git a/vsintegration/src/FSharp.UIResources/Strings.Designer.cs b/vsintegration/src/FSharp.UIResources/Strings.Designer.cs index 8e3e92323e1..f983f08de3b 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.Designer.cs +++ b/vsintegration/src/FSharp.UIResources/Strings.Designer.cs @@ -446,5 +446,14 @@ public static string Unused_opens_code_fix { return ResourceManager.GetString("Unused_opens_code_fix", resourceCulture); } } + + /// + /// Looks up a localized string similar to Cache parsing results (experimental). + /// + public static string Use_syntax_tree_cache { + get { + return ResourceManager.GetString("Use_syntax_tree_cache", resourceCulture); + } + } } } diff --git a/vsintegration/src/FSharp.UIResources/Strings.resx b/vsintegration/src/FSharp.UIResources/Strings.resx index e97f18d0ecc..06637b5c147 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.resx +++ b/vsintegration/src/FSharp.UIResources/Strings.resx @@ -246,4 +246,7 @@ Live Buffers (experimental) + + Cache parsing results (experimental) + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf index 9289f9407c7..d4e489701e2 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf @@ -217,6 +217,11 @@ Navrhovat názvy pro nerozpoznané identifikátory + + Cache parsing results (experimental) + Cache parsing results (experimental) + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf index d14b590f5b3..5f8ed3d9709 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf @@ -217,6 +217,11 @@ Namen für nicht aufgelöste Bezeichner vorschlagen + + Cache parsing results (experimental) + Cache parsing results (experimental) + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf index 5c807e55d08..648984fccbb 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf @@ -217,6 +217,11 @@ Sugerir nombres para los identificadores no resueltos + + Cache parsing results (experimental) + Cache parsing results (experimental) + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf index 04b999dbe0a..dc13ed2a628 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf @@ -217,6 +217,11 @@ Suggérer des noms pour les identificateurs non résolus + + Cache parsing results (experimental) + Cache parsing results (experimental) + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf index 7351f82dc40..412ccfeb5f9 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf @@ -217,6 +217,11 @@ Suggerisci nomi per gli identificatori non risolti + + Cache parsing results (experimental) + Cache parsing results (experimental) + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf index 597fd8aec1b..7247dbae375 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf @@ -217,6 +217,11 @@ 未解決の識別子の名前を提案します + + Cache parsing results (experimental) + Cache parsing results (experimental) + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf index ad8d15851cb..55ebb99e142 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf @@ -217,6 +217,11 @@ 확인되지 않은 식별자의 이름 제안 + + Cache parsing results (experimental) + Cache parsing results (experimental) + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf index 2fd2a1d7aa7..c9cfedb1971 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf @@ -217,6 +217,11 @@ Sugeruj nazwy w przypadku nierozpoznanych identyfikatorów + + Cache parsing results (experimental) + Cache parsing results (experimental) + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf index c3b72a1253d..038963cf9ba 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf @@ -217,6 +217,11 @@ Sugerir nomes para identificadores não resolvidos + + Cache parsing results (experimental) + Cache parsing results (experimental) + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf index 8bb9fff9020..25a44c54b32 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf @@ -217,6 +217,11 @@ Предлагать имена для неразрешенных идентификаторов + + Cache parsing results (experimental) + Cache parsing results (experimental) + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf index 9134516abd9..0e1f4691b91 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf @@ -217,6 +217,11 @@ Çözümlenmemiş tanımlayıcılar için ad öner + + Cache parsing results (experimental) + Cache parsing results (experimental) + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf index cec0e2d1149..9a5851273c4 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf @@ -217,6 +217,11 @@ 为未解析标识符建议名称 + + Cache parsing results (experimental) + Cache parsing results (experimental) + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf index 268e69e2275..b4dd37ef316 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf @@ -217,6 +217,11 @@ 為未解析的識別碼建議名稱 + + Cache parsing results (experimental) + Cache parsing results (experimental) + + \ No newline at end of file From efe94766afbe7a583ab4e039e6f964f28043dc73 Mon Sep 17 00:00:00 2001 From: Petr Date: Tue, 14 Mar 2023 13:06:58 +0100 Subject: [PATCH 2/5] First take on the F# telemetry (#14889) --- .../src/FSharp.Editor/FSharp.Editor.fsproj | 1 + .../src/FSharp.Editor/Hints/Hints.fs | 7 ++++- .../src/FSharp.Editor/Hints/RoslynAdapter.fs | 4 +++ .../LanguageService/LanguageService.fs | 3 ++ .../Telemetry/TelemetryReporter.fs | 28 +++++++++++++++++++ 5 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index 4e83825b9b8..70a2a009e54 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -28,6 +28,7 @@ + diff --git a/vsintegration/src/FSharp.Editor/Hints/Hints.fs b/vsintegration/src/FSharp.Editor/Hints/Hints.fs index 9e6bab5c297..99bc26409e2 100644 --- a/vsintegration/src/FSharp.Editor/Hints/Hints.fs +++ b/vsintegration/src/FSharp.Editor/Hints/Hints.fs @@ -15,4 +15,9 @@ module Hints = Kind: HintKind Range: range Parts: TaggedText list - } \ No newline at end of file + } + + let serialize kind = + match kind with + | TypeHint -> "type" + | ParameterNameHint -> "parameterName" \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs b/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs index f79e1dba1fa..668cd3cb2fd 100644 --- a/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs +++ b/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs @@ -6,6 +6,7 @@ open System.Collections.Immutable open System.ComponentModel.Composition open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints open Microsoft.VisualStudio.FSharp.Editor +open Microsoft.VisualStudio.FSharp.Editor.Telemetry // So the Roslyn interface is called IFSharpInlineHintsService // but our implementation is called just HintsService. @@ -27,6 +28,9 @@ type internal RoslynAdapter if hintKinds.IsEmpty then return ImmutableArray.Empty else + let hintKindsSerialized = hintKinds |> Set.map Hints.serialize |> String.concat "," + TelemetryReporter.reportEvent "hints" [("hints.kinds", hintKindsSerialized)] + let! sourceText = document.GetTextAsync cancellationToken |> Async.AwaitTask let! nativeHints = HintService.getHintsForDocument diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index fc45d4d7206..74809b4acf4 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -27,6 +27,7 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp open Microsoft.CodeAnalysis.Host open Microsoft.CodeAnalysis.Host.Mef open Microsoft.VisualStudio.FSharp.Editor.WorkspaceExtensions +open Microsoft.VisualStudio.FSharp.Editor.Telemetry open System.Threading.Tasks #nowarn "9" // NativePtr.toNativeInt @@ -104,6 +105,8 @@ type internal FSharpWorkspaceServiceFactory | _ -> let checker = lazy + TelemetryReporter.reportEvent "languageservicestarted" [] + let editorOptions = let editorOptions = workspace.Services.GetService() diff --git a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs new file mode 100644 index 00000000000..f4f91d7f7d0 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Microsoft.VisualStudio.FSharp.Editor.Telemetry + +open Microsoft.VisualStudio.Telemetry + +module TelemetryReporter = + + let eventPrefix = "dotnet/fsharp/" + let propPrefix = "dotnet.fsharp." + + let getFullEventName name = eventPrefix + name + let getFullPropName name = propPrefix + name + + let createEvent name (props: (string * obj) list) = + let event = TelemetryEvent (getFullEventName name) + + props + |> List.map (fun (k, v) -> getFullPropName k, v) + |> List.iter event.Properties.Add + + event + + let reportEvent name props = + let session = TelemetryService.DefaultSession + let event = createEvent name props + session.PostEvent event + From b87ca334c202885de947b34c2ad70c1814f17b69 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 14 Mar 2023 15:33:35 +0100 Subject: [PATCH 3/5] Array.Parallel - search functions (tryFindIndex,tryFind,tryPick) (#14827) * Searching functions for Array.Parallel added * with [] annotation to give us space to change/remove this API in the future if needed --- src/FSharp.Core/array.fs | 40 ++++++++ src/FSharp.Core/array.fsi | 96 +++++++++++++++++++ ...p.Core.SurfaceArea.netstandard20.debug.bsl | 3 + ...Core.SurfaceArea.netstandard20.release.bsl | 3 + ...p.Core.SurfaceArea.netstandard21.debug.bsl | 3 + ...Core.SurfaceArea.netstandard21.release.bsl | 3 + .../ArrayModule.fs | 21 ++-- .../ArrayModule2.fs | 34 ++++--- .../CollectionModulesConsistency.fs | 12 ++- 9 files changed, 192 insertions(+), 23 deletions(-) diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs index 8c2fa8470fa..2e8926ec6c6 100644 --- a/src/FSharp.Core/array.fs +++ b/src/FSharp.Core/array.fs @@ -1934,6 +1934,46 @@ module Array = module Parallel = open System.Threading.Tasks + [] + let tryFindIndex predicate (array: _[]) = + checkNonNull "array" array + + let pResult = + Parallel.For( + 0, + array.Length, + (fun i pState -> + if predicate array[i] then + pState.Break()) + ) + + pResult.LowestBreakIteration |> Option.ofNullable |> Option.map int + + [] + let tryFind predicate (array: _[]) = + array |> tryFindIndex predicate |> Option.map (fun i -> array[i]) + + [] + let tryPick chooser (array: _[]) = + checkNonNull "array" array + let allChosen = new System.Collections.Concurrent.ConcurrentDictionary<_, _>() + + let pResult = + Parallel.For( + 0, + array.Length, + (fun i pState -> + match chooser array[i] with + | None -> () + | chosenElement -> + allChosen[i] <- chosenElement + pState.Break()) + ) + + pResult.LowestBreakIteration + |> Option.ofNullable + |> Option.bind (fun i -> allChosen[int i]) + [] let choose chooser (array: 'T[]) = checkNonNull "array" array diff --git a/src/FSharp.Core/array.fsi b/src/FSharp.Core/array.fsi index 2d08d1116e1..d05ecacc0b3 100644 --- a/src/FSharp.Core/array.fsi +++ b/src/FSharp.Core/array.fsi @@ -3094,6 +3094,102 @@ module Array = /// Provides parallel operations on arrays module Parallel = + /// Returns the first element for which the given function returns True. + /// Returns None if no such element exists. + /// + /// The function to test the input elements. + /// The input array. + /// + /// The first element that satisfies the predicate, or None. + /// + /// Thrown when the input array is null. + /// + /// Try to find the first even number: + /// + /// let inputs = [| 1; 2; 3 |] + /// + /// inputs |> Array.Parallel.tryFind (fun elm -> elm % 2 = 0) + /// + /// Evaluates to Some 2. + /// + /// + /// Try to find the first even number: + /// + /// let inputs = [| 1; 5; 3 |] + /// + /// inputs |> Array.Parallel.tryFind (fun elm -> elm % 2 = 0) + /// + /// Evaluates to None + /// + [] + [] + val tryFind: predicate:('T -> bool) -> array:'T[] -> 'T option + + + /// Returns the index of the first element in the array + /// that satisfies the given predicate. + /// Returns None if no such element exists. + /// The function to test the input elements. + /// The input array. + /// + /// Thrown when the input array is null. + /// + /// The index of the first element that satisfies the predicate, or None. + /// + /// Try to find the index of the first even number: + /// + /// let inputs = [| 1; 2; 3; 4; 5 |] + /// + /// inputs |> Array.Parallel.tryFindIndex (fun elm -> elm % 2 = 0) + /// + /// Evaluates to Some 1 + /// + /// + /// Try to find the index of the first even number: + /// + /// let inputs = [| 1; 3; 5; 7 |] + /// + /// inputs |> Array.Parallel.tryFindIndex (fun elm -> elm % 2 = 0) + /// + /// Evaluates to None + /// + [] + [] + val tryFindIndex : predicate:('T -> bool) -> array:'T[] -> int option + + /// Applies the given function to successive elements, returning the first + /// result where the function returns Some(x) for some x. If the function + /// never returns Some(x) then None is returned. + /// + /// The function to transform the array elements into options. + /// The input array. + /// + /// The first transformed element that is Some(x). + /// + /// Thrown when the input array is null. + /// + /// + /// + /// let input = [| 1; 2; 3 |] + /// + /// input |> Array.Parallel.tryPick (fun n -> if n % 2 = 0 then Some (string n) else None) + /// + /// Evaluates to Some 2. + /// + /// + /// + /// + /// let input = [| 1; 2; 3 |] + /// + /// input |> Array.Parallel.tryPick (fun n -> if n > 3 = 0 then Some (string n) else None) + /// + /// Evaluates to None. + /// + /// + [] + [] + val tryPick: chooser:('T -> 'U option) -> array:'T[] -> 'U option + /// Apply the given function to each element of the array. Return /// the array comprised of the results x for each element where /// the function returns Some(x). diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl index 348c413de1b..783f3bc30aa 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl @@ -40,6 +40,9 @@ Microsoft.FSharp.Collections.Array4DModule: T[,,,] Create[T](Int32, Int32, Int32 Microsoft.FSharp.Collections.Array4DModule: T[,,,] Initialize[T](Int32, Int32, Int32, Int32, Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,T]]]]) Microsoft.FSharp.Collections.Array4DModule: T[,,,] ZeroCreate[T](Int32, Int32, Int32, Int32) Microsoft.FSharp.Collections.Array4DModule: Void Set[T](T[,,,], Int32, Int32, Int32, Int32, T) +Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[System.Int32] TryFindIndex[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[]) +Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[TResult] TryPick[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpOption`1[TResult]], T[]) +Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[T] TryFind[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[]) Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[T[],T[]] Partition[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[]) Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Choose[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpOption`1[TResult]], T[]) Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Collect[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult[]], T[]) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl index 2d5ddb40c7a..bca7a6bb25a 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl @@ -40,6 +40,9 @@ Microsoft.FSharp.Collections.Array4DModule: T[,,,] Create[T](Int32, Int32, Int32 Microsoft.FSharp.Collections.Array4DModule: T[,,,] Initialize[T](Int32, Int32, Int32, Int32, Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,T]]]]) Microsoft.FSharp.Collections.Array4DModule: T[,,,] ZeroCreate[T](Int32, Int32, Int32, Int32) Microsoft.FSharp.Collections.Array4DModule: Void Set[T](T[,,,], Int32, Int32, Int32, Int32, T) +Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[System.Int32] TryFindIndex[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[]) +Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[TResult] TryPick[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpOption`1[TResult]], T[]) +Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[T] TryFind[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[]) Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[T[],T[]] Partition[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[]) Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Choose[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpOption`1[TResult]], T[]) Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Collect[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult[]], T[]) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl index 45805d53ae0..614ec47f2d7 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl @@ -40,6 +40,9 @@ Microsoft.FSharp.Collections.Array4DModule: T[,,,] Create[T](Int32, Int32, Int32 Microsoft.FSharp.Collections.Array4DModule: T[,,,] Initialize[T](Int32, Int32, Int32, Int32, Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,T]]]]) Microsoft.FSharp.Collections.Array4DModule: T[,,,] ZeroCreate[T](Int32, Int32, Int32, Int32) Microsoft.FSharp.Collections.Array4DModule: Void Set[T](T[,,,], Int32, Int32, Int32, Int32, T) +Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[System.Int32] TryFindIndex[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[]) +Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[TResult] TryPick[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpOption`1[TResult]], T[]) +Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[T] TryFind[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[]) Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[T[],T[]] Partition[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[]) Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Choose[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpOption`1[TResult]], T[]) Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Collect[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult[]], T[]) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl index 275087e02e0..702f3302cee 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl @@ -40,6 +40,9 @@ Microsoft.FSharp.Collections.Array4DModule: T[,,,] Create[T](Int32, Int32, Int32 Microsoft.FSharp.Collections.Array4DModule: T[,,,] Initialize[T](Int32, Int32, Int32, Int32, Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,T]]]]) Microsoft.FSharp.Collections.Array4DModule: T[,,,] ZeroCreate[T](Int32, Int32, Int32, Int32) Microsoft.FSharp.Collections.Array4DModule: Void Set[T](T[,,,], Int32, Int32, Int32, Int32, T) +Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[System.Int32] TryFindIndex[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[]) +Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[TResult] TryPick[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpOption`1[TResult]], T[]) +Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[T] TryFind[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[]) Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[T[],T[]] Partition[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[]) Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Choose[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpOption`1[TResult]], T[]) Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Collect[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult[]], T[]) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs index 3d9185468d9..2c68635b3b9 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule.fs @@ -955,17 +955,16 @@ type ArrayModule() = let intArr = [| 1..10 |] let seq = Array.toSeq intArr let sum = Seq.sum seq - Assert.AreEqual(55, sum) - - [] - member this.TryPick() = + Assert.AreEqual(55, sum) + + member private _.TryPickTester tryPickInt tryPickString = // integer array let intArr = [| 1..10 |] let funcInt x = match x with | _ when x % 3 = 0 -> Some (x.ToString()) | _ -> None - let resultInt = Array.tryPick funcInt intArr + let resultInt = tryPickInt funcInt intArr if resultInt <> Some "3" then Assert.Fail() // string array @@ -974,20 +973,26 @@ type ArrayModule() = match x with | "good" -> Some (x.ToString()) | _ -> None - let resultStr = Array.tryPick funcStr strArr + let resultStr = tryPickString funcStr strArr if resultStr <> None then Assert.Fail() // empty array let emptyArr:int[] = [| |] - let resultEpt = Array.tryPick funcInt emptyArr + let resultEpt = tryPickInt funcInt emptyArr if resultEpt <> None then Assert.Fail() // null array let nullArr = null:string[] - CheckThrowsArgumentNullException (fun () -> Array.tryPick funcStr nullArr |> ignore) + CheckThrowsArgumentNullException (fun () -> tryPickString funcStr nullArr |> ignore) () + [] + member this.TryPick() = this.TryPickTester Array.tryPick Array.tryPick + + [] + member this.ParallelTryPick() = this.TryPickTester Array.Parallel.tryPick Array.Parallel.tryPick + [] member this.Fold() = // integer array diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule2.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule2.fs index 23510ddb781..b10ce17fb2e 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule2.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ArrayModule2.fs @@ -972,25 +972,31 @@ type ArrayModule2() = () - [] - member this.TryFind() = + member private _.TryFindTester tryFindInts tryFindStrings = // integer array - let resultInt = [|1..10|] |> Array.tryFind (fun x -> x%7 = 0) + let resultInt = [|1..10|] |> tryFindInts (fun x -> x%7 = 0) if resultInt <> Some 7 then Assert.Fail() // string array - let resultStr = [|"Lists"; "are"; "commonly" ; "list" |] |> Array.tryFind (fun (x:string) -> x.Length > 4) + let resultStr = [|"Lists"; "are"; "commonly" ; "list" |] |> tryFindStrings (fun (x:string) -> x.Length > 4) if resultStr <> Some "Lists" then Assert.Fail() // empty array - let resultEpt =[||] |> Array.tryFind (fun x -> x%7 = 0) + let resultEpt =[||] |> tryFindInts (fun x -> x%7 = 0) if resultEpt <> None then Assert.Fail() // null array let nullArr = null:string[] - CheckThrowsArgumentNullException (fun () -> Array.tryFind (fun (x:string) -> x.Length > 4) nullArr |> ignore) + CheckThrowsArgumentNullException (fun () -> tryFindStrings (fun (x:string) -> x.Length > 4) nullArr |> ignore) () + + [] + member this.TryFind() = this.TryFindTester Array.tryFind Array.tryFind + + [] + member this.ParallelTryFind() = this.TryFindTester Array.Parallel.tryFind Array.Parallel.tryFind + [] member this.TryFindBack() = @@ -1016,26 +1022,30 @@ type ArrayModule2() = () - [] - member this.TryFindIndex() = + member private _.TryFindIndexTester tryFindIdxInt tryFindIdxString = // integer array - let resultInt = [|1..10|] |> Array.tryFindIndex (fun x -> x%7 = 0) + let resultInt = [|1..10|] |> tryFindIdxInt (fun x -> x%7 = 0) if resultInt <> Some 6 then Assert.Fail() // string array - let resultStr = [|"Lists"; "are"; "commonly" ; "list" |] |> Array.tryFindIndex (fun (x:string) -> x.Length > 4) + let resultStr = [|"Lists"; "are"; "commonly" ; "list" |] |> tryFindIdxString (fun (x:string) -> x.Length > 4) if resultStr <> Some 0 then Assert.Fail() // empty array - let resultEpt =[||] |> Array.tryFindIndex (fun x -> x % 7 = 0) + let resultEpt =[||] |> tryFindIdxInt (fun x -> x % 7 = 0) if resultEpt <> None then Assert.Fail() // null array let nullArr = null:string[] - CheckThrowsArgumentNullException (fun () -> Array.tryFindIndex (fun (x:string) -> x.Length > 4) nullArr |> ignore) + CheckThrowsArgumentNullException (fun () -> tryFindIdxString (fun (x:string) -> x.Length > 4) nullArr |> ignore) () + [] + member this.TryFindIndex() = this.TryFindIndexTester Array.tryFindIndex Array.tryFindIndex + + [] + member this.ParallelTryFindIndex() = this.TryFindIndexTester Array.Parallel.tryFindIndex Array.Parallel.tryFindIndex [] member this.TryFindIndexBack() = // integer array diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/CollectionModulesConsistency.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/CollectionModulesConsistency.fs index 1560c16b564..0494f9682d4 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/CollectionModulesConsistency.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/CollectionModulesConsistency.fs @@ -14,6 +14,9 @@ let consistency name sqs ls arr = (sqs = arr) |@ (sprintf "Seq.%s = '%A', Array.%s = '%A'" name sqs name arr) .&. (ls = arr) |@ (sprintf "List.%s = '%A', Array.%s = '%A'" name ls name arr) +let consistencyIncludingParallel name sqs ls arr paraArr = + consistency name sqs ls arr .&. + (paraArr = arr) |@ (sprintf "Parallel.%s = '%A', Array.%s = '%A'" name paraArr name arr) let allPairs<'a when 'a : equality> (xs : list<'a>) (xs2 : list<'a>) = let s = xs |> Seq.allPairs xs2 |> Seq.toArray @@ -1104,7 +1107,8 @@ let tryFind<'a when 'a : equality> (xs : 'a []) predicate = let s = xs |> Seq.tryFind predicate let l = xs |> List.ofArray |> List.tryFind predicate let a = xs |> Array.tryFind predicate - consistency "tryFind" s l a + let pa = xs |> Array.Parallel.tryFind predicate + consistencyIncludingParallel "tryFind" s l a pa [] let ``tryFind is consistent`` () = @@ -1128,7 +1132,8 @@ let tryFindIndex<'a when 'a : equality> (xs : 'a []) predicate = let s = xs |> Seq.tryFindIndex predicate let l = xs |> List.ofArray |> List.tryFindIndex predicate let a = xs |> Array.tryFindIndex predicate - consistency "tryFindIndex" s l a + let pa = xs |> Array.Parallel.tryFindIndex predicate + consistencyIncludingParallel "tryFindIndex" s l a pa [] let ``tryFindIndex is consistent`` () = @@ -1188,7 +1193,8 @@ let tryPick<'a when 'a : comparison> (xs : 'a []) f = let s = xs |> Seq.tryPick f let l = xs |> List.ofArray |> List.tryPick f let a = xs |> Array.tryPick f - consistency "tryPick" s l a + let pa = xs |> Array.Parallel.tryPick f + consistencyIncludingParallel "tryPick" s l a pa [] let ``tryPick is consistent`` () = From fe635dbdaaacef9e0f30be2556dd659079db4ae7 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 14 Mar 2023 16:45:24 +0100 Subject: [PATCH 4/5] Add `GraphNode.FromResult` (#14894) * Add GraphNode.FromResult * fantomas --- src/Compiler/Facilities/BuildGraph.fs | 11 ++++++++--- src/Compiler/Facilities/BuildGraph.fsi | 3 +++ src/Compiler/Service/IncrementalBuild.fs | 6 +++--- src/Compiler/Service/service.fs | 2 +- tests/FSharp.Compiler.UnitTests/BuildGraphTests.fs | 7 +++++++ 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/Compiler/Facilities/BuildGraph.fs b/src/Compiler/Facilities/BuildGraph.fs index b8ee50564c1..5f9f80b7f14 100644 --- a/src/Compiler/Facilities/BuildGraph.fs +++ b/src/Compiler/Facilities/BuildGraph.fs @@ -228,14 +228,14 @@ module GraphNode = | None -> () [] -type GraphNode<'T>(retryCompute: bool, computation: NodeCode<'T>) = +type GraphNode<'T> private (retryCompute: bool, computation: NodeCode<'T>, cachedResult: Task<'T>, cachedResultNode: NodeCode<'T>) = let gate = obj () let mutable computation = computation let mutable requestCount = 0 - let mutable cachedResult: Task<'T> = Unchecked.defaultof<_> - let mutable cachedResultNode: NodeCode<'T> = Unchecked.defaultof<_> + let mutable cachedResult: Task<'T> = cachedResult + let mutable cachedResultNode: NodeCode<'T> = cachedResultNode let isCachedResultNodeNotNull () = not (obj.ReferenceEquals(cachedResultNode, null)) @@ -429,4 +429,9 @@ type GraphNode<'T>(retryCompute: bool, computation: NodeCode<'T>) = member _.IsComputing = requestCount > 0 + static member FromResult(result: 'T) = + let nodeResult = node.Return result + GraphNode(true, nodeResult, Task.FromResult(result), nodeResult) + + new(retryCompute: bool, computation) = GraphNode(retryCompute, computation, Unchecked.defaultof<_>, Unchecked.defaultof<_>) new(computation) = GraphNode(retryCompute = true, computation = computation) diff --git a/src/Compiler/Facilities/BuildGraph.fsi b/src/Compiler/Facilities/BuildGraph.fsi index b94c6e30b26..5afb43e5f08 100644 --- a/src/Compiler/Facilities/BuildGraph.fsi +++ b/src/Compiler/Facilities/BuildGraph.fsi @@ -102,6 +102,9 @@ type internal GraphNode<'T> = /// By default, 'retryCompute' is 'true'. new: computation: NodeCode<'T> -> GraphNode<'T> + /// Creates a GraphNode with given result already cached. + static member FromResult: 'T -> GraphNode<'T> + /// Return NodeCode which, when executed, will get the value of the computation if already computed, or /// await an existing in-progress computation for the node if one exists, or else will synchronously /// start the computation on the current thread. diff --git a/src/Compiler/Service/IncrementalBuild.fs b/src/Compiler/Service/IncrementalBuild.fs index bfc2d69c759..be8c4fae0ce 100644 --- a/src/Compiler/Service/IncrementalBuild.fs +++ b/src/Compiler/Service/IncrementalBuild.fs @@ -275,7 +275,7 @@ type TcInfoNode = static member FromState(state: TcInfoState) = let tcInfo = state.TcInfo let tcInfoExtras = state.TcInfoExtras - TcInfoNode(GraphNode(node.Return tcInfo), GraphNode(node.Return (tcInfo, defaultArg tcInfoExtras emptyTcInfoExtras))) + TcInfoNode(GraphNode.FromResult tcInfo, GraphNode.FromResult (tcInfo, defaultArg tcInfoExtras emptyTcInfoExtras)) /// Bound model of an underlying syntax and typed tree. [] @@ -1098,7 +1098,7 @@ module IncrementalBuilderStateHelpers = | ValueSome(boundModel) when initialState.enablePartialTypeChecking && boundModel.BackingSignature.IsSome -> let newBoundModel = boundModel.ClearTcInfoExtras() { state with - boundModels = state.boundModels.SetItem(slot, GraphNode(node.Return newBoundModel)) + boundModels = state.boundModels.SetItem(slot, GraphNode.FromResult newBoundModel) stampedFileNames = state.stampedFileNames.SetItem(slot, stamp) } | _ -> @@ -1165,7 +1165,7 @@ type IncrementalBuilderState with let referencedAssemblies = initialState.referencedAssemblies let cache = TimeStampCache(defaultTimeStamp) - let initialBoundModel = GraphNode(node.Return initialBoundModel) + let initialBoundModel = GraphNode.FromResult initialBoundModel let boundModels = ImmutableArrayBuilder.create fileNames.Length for slot = 0 to fileNames.Length - 1 do diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index 8b68495389b..cf5487e25df 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -410,7 +410,7 @@ type BackgroundCompiler let createBuilderNode (options, userOpName, ct: CancellationToken) = lock gate (fun () -> if ct.IsCancellationRequested then - GraphNode(node.Return(None, [||])) + GraphNode.FromResult(None, [||]) else let getBuilderNode = GraphNode(CreateOneIncrementalBuilder(options, userOpName)) incrementalBuildersCache.Set(AnyCallerThread, options, getBuilderNode) diff --git a/tests/FSharp.Compiler.UnitTests/BuildGraphTests.fs b/tests/FSharp.Compiler.UnitTests/BuildGraphTests.fs index ae2aeff2071..a33f9020897 100644 --- a/tests/FSharp.Compiler.UnitTests/BuildGraphTests.fs +++ b/tests/FSharp.Compiler.UnitTests/BuildGraphTests.fs @@ -272,3 +272,10 @@ module BuildGraphTests = tasks |> Seq.iter (fun x -> try x.Wait(1000) |> ignore with | :? TimeoutException -> reraise() | _ -> ()) + + [] + let ``GraphNode created from an already computed result will return it in tryPeekValue`` () = + let graphNode = GraphNode.FromResult 1 + + Assert.shouldBeTrue graphNode.HasValue + Assert.shouldBe (ValueSome 1) (graphNode.TryPeekValue()) From ea5a144e71cc63b86f9e96dcb9f395691cdd062a Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 14 Mar 2023 16:47:37 +0100 Subject: [PATCH 5/5] Fix missing reference (#14892) * Fix missing reference * undo whitespace change --- src/Compiler/Service/IncrementalBuild.fs | 16 +++++++++++----- .../src/FSharp.Editor/Common/RoslynHelpers.fs | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Compiler/Service/IncrementalBuild.fs b/src/Compiler/Service/IncrementalBuild.fs index be8c4fae0ce..7977b2d9701 100644 --- a/src/Compiler/Service/IncrementalBuild.fs +++ b/src/Compiler/Service/IncrementalBuild.fs @@ -1058,11 +1058,17 @@ module IncrementalBuilderStateHelpers = let rec createFinalizeBoundModelGraphNode (initialState: IncrementalBuilderInitialState) (boundModels: ImmutableArray>.Builder) = GraphNode(node { use _ = Activity.start "GetCheckResultsAndImplementationsForProject" [|Activity.Tags.project, initialState.outfile|] - // Compute last bound model then get all the evaluated models. + + // Compute last bound model then get all the evaluated models*. let! _ = boundModels[boundModels.Count - 1].GetOrComputeValue() - let boundModels = - boundModels.ToImmutable() - |> ImmutableArray.map (fun x -> x.TryPeekValue().Value) + let! boundModels = + boundModels + |> Seq.map (fun x -> + match x.TryPeekValue() with + | ValueSome v -> node.Return v + // *Evaluating the last bound model doesn't always guarantee that all the other bound models are evaluated. + | _ -> node.ReturnFrom(x.GetOrComputeValue())) + |> NodeCode.Sequential let! result = FinalizeTypeCheckTask @@ -1071,7 +1077,7 @@ module IncrementalBuilderStateHelpers = initialState.enablePartialTypeChecking initialState.assemblyName initialState.outfile - boundModels + (boundModels.ToImmutableArray()) let result = (result, DateTime.UtcNow) return result }) diff --git a/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs b/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs index f2cb16164a9..2df8f930b9e 100644 --- a/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs +++ b/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs @@ -139,7 +139,7 @@ module internal RoslynHelpers = | :? OperationCanceledException -> tcs.TrySetCanceled(cancellationToken) |> ignore | exn -> - System.Diagnostics.Trace.WriteLine("Visual F# Tools: exception swallowed and not passed to Roslyn: {0}", exn.Message) + System.Diagnostics.Trace.TraceError("Visual F# Tools: exception swallowed and not passed to Roslyn: {0}", exn) let res = Unchecked.defaultof<_> tcs.TrySetResult(res) |> ignore ),