diff --git a/src/FsAutoComplete.Core/AdaptiveExtensions.fs b/src/FsAutoComplete.Core/AdaptiveExtensions.fs index b87a0a97a..1b5c4b7eb 100644 --- a/src/FsAutoComplete.Core/AdaptiveExtensions.fs +++ b/src/FsAutoComplete.Core/AdaptiveExtensions.fs @@ -408,7 +408,7 @@ and AdaptiveCancellableTask<'a>(cancel: unit -> unit, real: Task<'a>) = if real.IsCompleted then real else - cachedTcs <- new TaskCompletionSource<'a>() + cachedTcs <- new TaskCompletionSource<'a>(TaskCreationOptions.RunContinuationsAsynchronously) cachedTcs.TrySetFromTaskFinished real diff --git a/src/FsAutoComplete.Core/CompilerServiceInterface.fs b/src/FsAutoComplete.Core/CompilerServiceInterface.fs index 50322a88e..1db80509c 100644 --- a/src/FsAutoComplete.Core/CompilerServiceInterface.fs +++ b/src/FsAutoComplete.Core/CompilerServiceInterface.fs @@ -78,9 +78,22 @@ type CompilerProjectOption = | BackgroundCompiler(options) -> options.OtherOptions |> Array.toList | TransparentCompiler(snapshot) -> snapshot.OtherOptions -type FSharpCompilerServiceChecker(hasAnalyzers, typecheckCacheSize, parallelReferenceResolution, useTransparentCompiler) - = +type FSharpCompilerServiceChecker + ( + hasAnalyzers, + typecheckCacheSize, + parallelReferenceResolution, + useTransparentCompiler, + ?transparentCompilerCacheSizes: int + ) = let checker = + let cacheSize = + if useTransparentCompiler then + TransparentCompiler.CacheSizes.Create(defaultArg transparentCompilerCacheSizes 10) + |> Some + else + None + FSharpChecker.Create( projectCacheSize = 200, keepAssemblyContents = hasAnalyzers, @@ -91,7 +104,8 @@ type FSharpCompilerServiceChecker(hasAnalyzers, typecheckCacheSize, parallelRefe enablePartialTypeChecking = not hasAnalyzers, parallelReferenceResolution = parallelReferenceResolution, captureIdentifiersWhenParsing = true, - useTransparentCompiler = useTransparentCompiler + useTransparentCompiler = useTransparentCompiler, + ?transparentCompilerCacheSizes = cacheSize ) let entityCache = EntityCache() @@ -502,7 +516,10 @@ type FSharpCompilerServiceChecker(hasAnalyzers, typecheckCacheSize, parallelRefe let ops = MemoryCacheEntryOptions().SetSize(1).SetSlidingExpiration(TimeSpan.FromMinutes(5.)) - return lastCheckResults.Set(filePath, r, ops) + lastCheckResults.Set(filePath, WeakReference(r), ops) + |> ignore> + + return r else return r with ex -> @@ -553,7 +570,10 @@ type FSharpCompilerServiceChecker(hasAnalyzers, typecheckCacheSize, parallelRefe let ops = MemoryCacheEntryOptions().SetSize(1).SetSlidingExpiration(TimeSpan.FromMinutes(5.)) - return lastCheckResults.Set(filePath, r, ops) + lastCheckResults.Set(filePath, WeakReference(r), ops) + |> ignore> + + return r else return r with ex -> @@ -578,8 +598,11 @@ type FSharpCompilerServiceChecker(hasAnalyzers, typecheckCacheSize, parallelRefe checkerLogger.info (Log.setMessage "{opName}" >> Log.addContextDestructured "opName" opName) - match lastCheckResults.TryGetValue(file) with - | (true, v) -> Some v + match lastCheckResults.TryGetValue>(file) with + | (true, v) -> + match v.TryGetTarget() with + | (true, v) -> Some v + | _ -> None | _ -> None member _.TryGetRecentCheckResultsForFile(file: string, snapshot: FSharpProjectSnapshot) = diff --git a/src/FsAutoComplete.Core/CompilerServiceInterface.fsi b/src/FsAutoComplete.Core/CompilerServiceInterface.fsi index bb953853c..dce6fbf5a 100644 --- a/src/FsAutoComplete.Core/CompilerServiceInterface.fsi +++ b/src/FsAutoComplete.Core/CompilerServiceInterface.fsi @@ -29,7 +29,11 @@ type CompilerProjectOption = type FSharpCompilerServiceChecker = new: - hasAnalyzers: bool * typecheckCacheSize: int64 * parallelReferenceResolution: bool * useTransparentCompiler: bool -> + hasAnalyzers: bool * + typecheckCacheSize: int64 * + parallelReferenceResolution: bool * + useTransparentCompiler: bool * + ?transparentCompilerCacheSizes: int -> FSharpCompilerServiceChecker member DisableInMemoryProjectReferences: bool with get, set diff --git a/src/FsAutoComplete.Core/Utils.fs b/src/FsAutoComplete.Core/Utils.fs index 31c58f3a9..342a67641 100644 --- a/src/FsAutoComplete.Core/Utils.fs +++ b/src/FsAutoComplete.Core/Utils.fs @@ -70,7 +70,9 @@ module ProcessHelper = let WaitForExitAsync (p: Process) = asyncEx { - let tcs = TaskCompletionSource() + let tcs = + TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously) + p.EnableRaisingEvents <- true p.Exited.Add(fun _args -> tcs.TrySetResult(null) |> ignore) diff --git a/src/FsAutoComplete/LspHelpers.fs b/src/FsAutoComplete/LspHelpers.fs index 6db940947..e333cef85 100644 --- a/src/FsAutoComplete/LspHelpers.fs +++ b/src/FsAutoComplete/LspHelpers.fs @@ -86,11 +86,7 @@ module Conversions = | Some(U2.C2 code) -> code |> Some | None -> None - type TextDocumentIdentifier with - - member doc.GetFilePath() = Path.FileUriToLocalPath doc.Uri - - type VersionedTextDocumentIdentifier with + type ITextDocumentIdentifier with member doc.GetFilePath() = Path.FileUriToLocalPath doc.Uri diff --git a/src/FsAutoComplete/LspHelpers.fsi b/src/FsAutoComplete/LspHelpers.fsi index 857c93fb7..8848a41db 100644 --- a/src/FsAutoComplete/LspHelpers.fsi +++ b/src/FsAutoComplete/LspHelpers.fsi @@ -46,11 +46,7 @@ module Conversions = member CodeAsString: string option - type TextDocumentIdentifier with - - member GetFilePath: unit -> string - - type VersionedTextDocumentIdentifier with + type ITextDocumentIdentifier with member GetFilePath: unit -> string diff --git a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs index 092ee01ab..4a4c5eb9a 100644 --- a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs +++ b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs @@ -939,7 +939,7 @@ type AdaptiveFSharpLspServer let (filePath, pos) = getFilePathAndPosition p let! volatileFile = state.GetOpenFileOrRead filePath |> AsyncResult.ofStringErr let! lineStr = volatileFile.Source |> tryGetLineStr pos |> Result.lineLookupErr - and! tyRes = state.GetOpenFileTypeCheckResultsCached filePath |> AsyncResult.ofStringErr + and! tyRes = state.GetOpenFileTypeCheckResults filePath |> AsyncResult.ofStringErr match tyRes.TryGetToolTipEnhanced pos lineStr with | Some tooltipResult -> @@ -1580,7 +1580,7 @@ type AdaptiveFSharpLspServer let filePath = Path.FileUriToLocalPath data.[0] |> Utils.normalizePath try - let! tyRes = state.GetOpenFileTypeCheckResultsCached filePath |> AsyncResult.ofStringErr + let! tyRes = state.GetOpenFileTypeCheckResults filePath |> AsyncResult.ofStringErr logger.info ( diff --git a/src/FsAutoComplete/LspServers/AdaptiveServerState.fs b/src/FsAutoComplete/LspServers/AdaptiveServerState.fs index 31f231250..d4aa44cac 100644 --- a/src/FsAutoComplete/LspServers/AdaptiveServerState.fs +++ b/src/FsAutoComplete/LspServers/AdaptiveServerState.fs @@ -377,6 +377,14 @@ type AdaptiveState disposables.Add <| fileParsed.Publish.Subscribe(fun (parseResults, proj, ct) -> detectTests parseResults proj ct) + let analyzersLocker = new SemaphoreSlim(1, 1) + + let typecheckLocker = + let maxConcurrency = + Math.Max(1.0, Math.Floor(float System.Environment.ProcessorCount * 0.75)) |> int + + new SemaphoreSlim(maxConcurrency, maxConcurrency) + let builtInCompilerAnalyzers config (file: VolatileFile) (tyRes: ParseAndCheckResults) = let filePath = file.FileName let filePathUntag = UMX.untag filePath @@ -390,6 +398,8 @@ type AdaptiveState let checkUnusedOpens = asyncEx { try + let! ct = Async.CancellationToken + use! _l = analyzersLocker.LockAsync(ct) use progress = progressLookup.CreateProgressReport(lspClient, cancellable = true) do! progress.Begin($"Checking unused opens {fileName}...", message = filePathUntag) @@ -397,7 +407,6 @@ type AdaptiveState UnusedOpens.getUnusedOpens (tyRes.GetCheckResults, getSourceLine) |> Async.withCancellation progress.CancellationToken - let! ct = Async.CancellationToken notifications.Trigger(NotificationEvent.UnusedOpens(filePath, (unused |> List.toArray), file.Version), ct) with e -> logger.error (Log.setMessage "checkUnusedOpens failed" >> Log.addExn e) @@ -406,6 +415,8 @@ type AdaptiveState let checkUnusedDeclarations = asyncEx { try + let! ct = Async.CancellationToken + use! _l = analyzersLocker.LockAsync(ct) use progress = progressLookup.CreateProgressReport(lspClient, cancellable = true) do! progress.Begin($"Checking unused declarations {fileName}...", message = filePathUntag) @@ -417,7 +428,6 @@ type AdaptiveState let unused = unused |> Seq.toArray - let! ct = Async.CancellationToken notifications.Trigger(NotificationEvent.UnusedDeclarations(filePath, unused, file.Version), ct) with e -> logger.error (Log.setMessage "checkUnusedDeclarations failed" >> Log.addExn e) @@ -426,6 +436,8 @@ type AdaptiveState let checkSimplifiedNames = asyncEx { try + let! ct = Async.CancellationToken + use! _l = analyzersLocker.LockAsync(ct) use progress = progressLookup.CreateProgressReport(lspClient, cancellable = true) do! progress.Begin($"Checking simplifying of names {fileName}...", message = filePathUntag) @@ -434,7 +446,6 @@ type AdaptiveState |> Async.withCancellation progress.CancellationToken let simplified = Array.ofSeq simplified - let! ct = Async.CancellationToken notifications.Trigger(NotificationEvent.SimplifyNames(filePath, simplified, file.Version), ct) with e -> logger.error (Log.setMessage "checkSimplifiedNames failed" >> Log.addExn e) @@ -443,6 +454,8 @@ type AdaptiveState let checkUnnecessaryParentheses = asyncEx { try + let! ct = Async.CancellationToken + use! _l = analyzersLocker.LockAsync(ct) use progress = progressLookup.CreateProgressReport(lspClient) do! progress.Begin($"Checking for unnecessary parentheses {fileName}...", message = filePathUntag) @@ -464,8 +477,6 @@ type AdaptiveState | _ -> ranges) - let! ct = Async.CancellationToken - notifications.Trigger( NotificationEvent.UnnecessaryParentheses(filePath, Array.ofSeq unnecessaryParentheses, file.Version), ct @@ -1599,6 +1610,8 @@ type AdaptiveState ] + let! ct = Async.CancellationToken + use! _l = typecheckLocker.LockAsync ct use _ = fsacActivitySource.StartActivityForType(thisType, tags = tags) diff --git a/src/FsAutoComplete/LspServers/Common.fs b/src/FsAutoComplete/LspServers/Common.fs index 89e3e3fca..63a998ccf 100644 --- a/src/FsAutoComplete/LspServers/Common.fs +++ b/src/FsAutoComplete/LspServers/Common.fs @@ -158,7 +158,10 @@ module Async = asyncEx { let! ct2 = Async.CancellationToken use cts = CancellationTokenSource.CreateLinkedTokenSource(ct, ct2) - let tcs = new TaskCompletionSource<'a>() + + let tcs = + new TaskCompletionSource<'a>(TaskCreationOptions.RunContinuationsAsynchronously) + use _reg = cts.Token.Register(fun () -> tcs.TrySetCanceled(cts.Token) |> ignore) let a =