From fa416b3cd3fa97d8b033eca65b136f76e75ff811 Mon Sep 17 00:00:00 2001 From: Jimmy Byrd Date: Thu, 23 Mar 2023 23:55:20 -0400 Subject: [PATCH 01/10] Using AsyncAdaptive --- paket.dependencies | 3 +- paket.lock | 13 +- src/FsAutoComplete.Core/AdaptiveExtensions.fs | 421 +++++++++++++++++ src/FsAutoComplete.Core/CodeGeneration.fs | 21 +- src/FsAutoComplete.Core/Commands.fs | 81 ++-- src/FsAutoComplete.Core/SymbolLocation.fs | 51 +- src/FsAutoComplete.Core/paket.references | 3 +- src/FsAutoComplete/CodeFixes.fs | 3 +- .../LspServers/AdaptiveFSharpLspServer.fs | 446 +++++++++++------- src/FsAutoComplete/LspServers/Common.fs | 3 + .../LspServers/FsAutoComplete.Lsp.fs | 4 +- 11 files changed, 812 insertions(+), 237 deletions(-) diff --git a/paket.dependencies b/paket.dependencies index 0fcc7d399..a8003c397 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -34,7 +34,8 @@ nuget Serilog.Sinks.Async nuget Destructurama.FSharp nuget FSharp.UMX nuget FSharp.Formatting -nuget FsToolkit.ErrorHandling +nuget FsToolkit.ErrorHandling.TaskResult +nuget IcedTasks nuget FSharpx.Async nuget CliWrap nuget System.CommandLine prerelease diff --git a/paket.lock b/paket.lock index c828ae149..f316260b2 100644 --- a/paket.lock +++ b/paket.lock @@ -73,8 +73,12 @@ NUGET FSharpx.Async (1.14.1) FSharp.Control.AsyncSeq (>= 2.0.21) FSharp.Core (>= 4.6.2) - FsToolkit.ErrorHandling (2.13) - FSharp.Core (>= 4.7.2) + FsToolkit.ErrorHandling (4.4) + FSharp.Core (>= 4.7.2) - restriction: || (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) + FSharp.Core (>= 7.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netstandard2.1)) + FsToolkit.ErrorHandling.TaskResult (4.4) + FsToolkit.ErrorHandling (>= 4.4) + Ply (>= 0.3.1) - restriction: || (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) GitHubActionsTestLogger (2.0.1) Microsoft.TestPlatform.ObjectModel (>= 17.2) Google.Protobuf (3.22) @@ -92,6 +96,8 @@ NUGET Microsoft.Extensions.Logging.Abstractions (>= 3.0.3) Grpc.Net.Common (2.51) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netstandard2.1)) Grpc.Core.Api (>= 2.51) + IcedTasks (0.5.3) + FSharp.Core (>= 7.0) ICSharpCode.Decompiler (7.2.1.6856) Microsoft.Win32.Registry (>= 5.0) System.Collections.Immutable (>= 5.0) @@ -297,6 +303,9 @@ NUGET Grpc (>= 2.44 < 3.0) - restriction: || (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net462)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) Grpc.Net.Client (>= 2.43 < 3.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netstandard2.1)) OpenTelemetry (>= 1.3.2) + Ply (0.3.1) - restriction: || (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) + FSharp.Core (>= 4.6.2) + System.Threading.Tasks.Extensions (>= 4.5.4) SemanticVersioning (2.0.2) Serilog (2.11) Serilog.Sinks.Async (1.5) diff --git a/src/FsAutoComplete.Core/AdaptiveExtensions.fs b/src/FsAutoComplete.Core/AdaptiveExtensions.fs index 2758a5d95..2dc809771 100644 --- a/src/FsAutoComplete.Core/AdaptiveExtensions.fs +++ b/src/FsAutoComplete.Core/AdaptiveExtensions.fs @@ -3,6 +3,9 @@ namespace FsAutoComplete.Adaptive open System open FSharp.Data.Adaptive open FSharp.Data.Traceable +open System.Threading.Tasks +open IcedTasks +open System.Threading [] module AdaptiveExtensions = @@ -288,3 +291,421 @@ module AMap = >> HashMap.map (fun _ v -> AVal.constant v |> AVal.mapWithAdditionalDependenies (id)) batchRecalcDirty mapping map + + +type internal RefCountingTaskCreator<'a>(create: CancellationToken -> Task<'a>) = + + let mutable refCount = 0 + let mutable cache: option> = None + let mutable cancel: CancellationTokenSource = null + + member private x.RemoveRef() = + lock x (fun () -> + if refCount = 1 then + refCount <- 0 + cancel.Cancel() + cancel.Dispose() + cancel <- null + cache <- None + else + refCount <- refCount - 1) + + member x.New() = + lock x (fun () -> + match cache with + | Some cache -> + refCount <- refCount + 1 + AdaptiveCancellableTask(x.RemoveRef, cache) + | None -> + cancel <- new CancellationTokenSource() + let task = create cancel.Token + cache <- Some task + refCount <- refCount + 1 + AdaptiveCancellableTask(x.RemoveRef, task)) + + +and AdaptiveCancellableTask<'a>(cancel: unit -> unit, real: Task<'a>) = + let cts = new CancellationTokenSource() + + let output = + if real.IsCompleted then + real + else + let tcs = new TaskCompletionSource<'a>() + + let s = + cts.Token.Register(fun () -> tcs.TrySetCanceled() |> ignore + + ) + + real.ContinueWith(fun (t: Task<'a>) -> + s.Dispose() + + if t.IsFaulted then tcs.TrySetException(t.Exception) + elif t.IsCanceled then tcs.TrySetCanceled() + else tcs.TrySetResult(t.Result)) + |> ignore + + tcs.Task + + member x.Cancel() = + cancel () + cts.Cancel() + + member x.Task = output + +type asyncaval<'a> = + inherit IAdaptiveObject + abstract GetValue: AdaptiveToken -> AdaptiveCancellableTask<'a> + +module CancellableTask = + let inline ofAdaptiveCancellableTask (ct: AdaptiveCancellableTask<_>) = + fun (ctok: CancellationToken) -> + task { + use _ = ctok.Register(fun () -> ct.Cancel()) + return! ct.Task + } + +module Async = + let inline ofAdaptiveCancellableTask (ct: AdaptiveCancellableTask<_>) = + async { + let! ctok = Async.CancellationToken + use _ = ctok.Register(fun () -> ct.Cancel()) + return! ct.Task |> Async.AwaitTask + } + +[] +module Extensions = + + type IcedTasks.CancellableTasks.CancellableTaskBuilderBase with + + member inline x.Source(ct: AdaptiveCancellableTask<_>) = + fun ctok -> (CancellableTask.ofAdaptiveCancellableTask ct ctok).GetAwaiter() + + +module AsyncAVal = + + let force (value: asyncaval<_>) = value.GetValue(AdaptiveToken.Top) + + let forceAsync (value: asyncaval<_>) = + async { + let ct = value.GetValue(AdaptiveToken.Top) + return! Async.ofAdaptiveCancellableTask ct + } + + + let forceCancellableTask (value: asyncaval<_>) = + cancellableTask { + let ct = value.GetValue(AdaptiveToken.Top) + return! ct + } + + type ConstantVal<'a>(value: AdaptiveCancellableTask<'a>) = + inherit ConstantObject() + + new(value: Task<'a>) = ConstantVal<'a>(AdaptiveCancellableTask(id, value)) + + interface asyncaval<'a> with + member x.GetValue _ = value + + [] + type AbstractVal<'a>() = + inherit AdaptiveObject() + abstract Compute: AdaptiveToken -> AdaptiveCancellableTask<'a> + + member x.GetValue token = x.EvaluateAlways token x.Compute + + interface asyncaval<'a> with + member x.GetValue t = x.GetValue t + + let constant (value: 'a) = + ConstantVal(Task.FromResult value) :> asyncaval<_> + + let ofTask (value: Task<'a>) = ConstantVal(value) :> asyncaval<_> + + let ofCancelableTask (value: AdaptiveCancellableTask<'a>) = ConstantVal(value) :> asyncaval<_> + + // let ofCancellableTask (value : CancellableTask<'a>) = + // ConstantVal ( + // let cts = new CancellationTokenSource () + // let cancel () = + // cts.Cancel() + // cts.Dispose() + // let real = task { + // try + // return! value cts.Token + // finally + // cts.Dispose() + // } + // CancelableTask(cancel, real) + // ) + // :> asyncaval<_> + + let ofAVal (value: aval<'a>) = + if value.IsConstant then + ConstantVal(Task.FromResult(AVal.force value)) :> asyncaval<_> + else + { new AbstractVal<'a>() with + member x.Compute t = + let real = Task.FromResult(value.GetValue t) + AdaptiveCancellableTask(id, real) } + :> asyncaval<_> + + let map (mapping: 'a -> CancellationToken -> Task<'b>) (input: asyncaval<'a>) = + let mutable cache: option> = None + + { new AbstractVal<'b>() with + member x.Compute t = + if x.OutOfDate || Option.isNone cache then + let ref = + RefCountingTaskCreator( + cancellableTask { + // let! ct = CancellableTask.getCancellationToken () + let it = input.GetValue t + // let s = ct.Register(fun () -> it.Cancel()) + // try + let! i = it + return! mapping i + // finally + // s.Dispose() + } + ) + + cache <- Some ref + ref.New() + else + cache.Value.New() } + :> asyncaval<_> + + + let mapSync (mapping: 'a -> CancellationToken -> 'b) (input: asyncaval<'a>) = + let mutable cache: option> = None + + { new AbstractVal<'b>() with + member x.Compute t = + if x.OutOfDate || Option.isNone cache then + let ref = + RefCountingTaskCreator( + cancellableTask { + let! ct = CancellableTask.getCancellationToken () + let it = input.GetValue t + let s = ct.Register(fun () -> it.Cancel()) + + try + let! i = it + return mapping i ct + finally + s.Dispose() + } + ) + + cache <- Some ref + ref.New() + else + cache.Value.New() } + :> asyncaval<_> + + let map2 (mapping: 'a -> 'b -> CancellationToken -> Task<'c>) (ca: asyncaval<'a>) (cb: asyncaval<'b>) = + let mutable cache: option> = None + + { new AbstractVal<'c>() with + member x.Compute t = + if x.OutOfDate || Option.isNone cache then + let ref = + RefCountingTaskCreator( + cancellableTask { + let ta = ca.GetValue t + let tb = cb.GetValue t + + let! ct = CancellableTask.getCancellationToken () + + let s = + ct.Register(fun () -> + ta.Cancel() + tb.Cancel()) + + try + let! va = ta + let! vb = tb + return! mapping va vb ct + finally + s.Dispose() + } + ) + + cache <- Some ref + ref.New() + else + cache.Value.New() } + :> asyncaval<_> + + let bind (mapping: 'a -> CancellationToken -> asyncaval<'b>) (value: asyncaval<'a>) = + let mutable cache: option<_> = None + let mutable innerCache: option<_> = None + let mutable inputChanged = 0 + let inners: ref>> = ref HashSet.empty + + { new AbstractVal<'b>() with + + override x.InputChangedObject(_, o) = + if System.Object.ReferenceEquals(o, value) then + inputChanged <- 1 + + lock inners (fun () -> + for i in inners.Value do + i.Outputs.Remove x |> ignore + + inners.Value <- HashSet.empty) + + member x.Compute t = + if x.OutOfDate then + if Interlocked.Exchange(&inputChanged, 0) = 1 || Option.isNone cache then + let outerTask = + RefCountingTaskCreator( + cancellableTask { + let it = value.GetValue t + let! ct = CancellableTask.getCancellationToken () + let s = ct.Register(fun () -> it.Cancel()) + + try + let! i = it + let inner = mapping i ct + return inner + finally + s.Dispose() + } + ) + + cache <- Some outerTask + + let outerTask = cache.Value + + let ref = + RefCountingTaskCreator( + cancellableTask { + let innerCellTask = outerTask.New() + + let! ct = CancellableTask.getCancellationToken () + let s = ct.Register(fun () -> innerCellTask.Cancel()) + + try + let! inner = innerCellTask + let innerTask = inner.GetValue t + lock inners (fun () -> inners.Value <- HashSet.add inner inners.Value) + + let s2 = + ct.Register(fun () -> + innerTask.Cancel() + lock inners (fun () -> inners.Value <- HashSet.remove inner inners.Value) + inner.Outputs.Remove x |> ignore) + + try + let! innerValue = innerTask + return innerValue + finally + s2.Dispose() + finally + s.Dispose() + } + ) + + innerCache <- Some ref + + ref.New() + else + innerCache.Value.New() + + } + :> asyncaval<_> + + let mapOption f = + mapSync (fun data _ -> data |> Option.map f) + +type AsyncAValBuilder() = + + member inline x.MergeSources(v1: asyncaval<'T1>, v2: asyncaval<'T2>) = + AsyncAVal.map2 (fun a b _ -> Task.FromResult(a, b)) v1 v2 + + // member inline x.MergeSources3(v1 : aval<'T1>, v2 : aval<'T2>, v3 : aval<'T3>) = + // AVal.map3 (fun a b c -> a,b,c) v1 v2 v3 + + member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 -> CancellationToken -> Task<'T2>) = + AsyncAVal.map mapping value + + member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 -> Task<'T2>) = + AsyncAVal.map (fun data _ -> mapping data) value + + // member inline x.Bind2Return(v1 : aval<'T1>, v2 : aval<'T2>, mapping: 'T1 * 'T2 -> 'T3) = + // AVal.map2 (fun a b -> mapping(a,b)) v1 v2 + + // member inline x.Bind3Return(v1 : aval<'T1>, v2: aval<'T2>, v3: aval<'T3>, mapping: 'T1 * 'T2 * 'T3 -> 'T4) = + // AVal.map3 (fun a b c -> mapping(a, b, c)) v1 v2 v3 + + member inline x.Bind(value: asyncaval<'T1>, [] mapping: 'T1 -> CancellationToken -> asyncaval<'T2>) = + AsyncAVal.bind (mapping) value + + member inline x.Bind(value: asyncaval<'T1>, [] mapping: 'T1 -> asyncaval<'T2>) = + AsyncAVal.bind (fun data _ -> mapping data) value + + // member inline x.Bind2(v1: aval<'T1>, v2: aval<'T2>, mapping: 'T1 * 'T2 -> aval<'T3>) = + // AVal.bind2 (fun a b -> mapping(a,b)) v1 v2 + + // member inline x.Bind3(v1: aval<'T1>, v2: aval<'T2>, v3: aval<'T3>, mapping: 'T1 * 'T2 * 'T3 -> aval<'T4>) = + // AVal.bind3 (fun a b c -> mapping(a, b, c)) v1 v2 v3 + + member inline x.Return(value: 'T) = AsyncAVal.constant value + + member inline x.ReturnFrom(value: asyncaval<'T>) = value + + member inline x.Source(value: asyncaval<'T>) = value + +[] +module AsyncAValBuilderExtensions = + let asyncAVal = AsyncAValBuilder() + + type AsyncAValBuilder with + + member inline x.Source(value: aval<'T>) = AsyncAVal.ofAVal value + member inline x.Source(value: Task<'T>) = AsyncAVal.ofTask value + member inline x.Source(value: AdaptiveCancellableTask<'T>) = AsyncAVal.ofCancelableTask value + // member inline x.Source(value : CancellableTask<'T>) = AsyncAVal.ofCancellableTask value + + member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 -> 'T2) = + AsyncAVal.map (fun data _ -> mapping data |> Task.FromResult) value + + + + +module AMapAsync = + + let mapAsyncAVal + (mapper: 'Key -> 'InValue -> CancellationToken -> asyncaval<'OutValue>) + (map: #amap<'Key, #aval<'InValue>>) + : amap<'Key, asyncaval<'OutValue>> = + map |> AMap.map (fun k v -> v |> AsyncAVal.ofAVal |> AsyncAVal.bind (mapper k)) + + + let mapAsyncAVal2 + (mapper: 'Key -> 'InValue -> CancellationToken -> asyncaval<'OutValue>) + (map: #amap<'Key, #asyncaval<'InValue>>) + : amap<'Key, asyncaval<'OutValue>> = + map |> AMap.map (fun k v -> v |> AsyncAVal.bind (mapper k)) + + /// Adaptively looks up the given key in the map and binds the value to be easily worked with. Note that this operation should not be used extensively since its resulting aval will be re-evaluated upon every change of the map. + let tryFindAsyncAval (key: 'Key) (map: amap<'Key, #asyncaval<'Value>>) = + asyncAVal { + match! AMap.tryFind key map with + | Some v -> + let! v2 = v + return Some v2 + | None -> return None + } + + + /// Adaptively looks up the given key in the map and flattens the value to be easily worked with. Note that this operation should not be used extensively since its resulting aval will be re-evaluated upon every change of the map. + let tryFindAsyncAndFlatten (key: 'Key) (map: amap<'Key, asyncaval>>) = + asyncAVal { + match! AMap.tryFind key map with + | Some x -> return! x + | None -> return None + } diff --git a/src/FsAutoComplete.Core/CodeGeneration.fs b/src/FsAutoComplete.Core/CodeGeneration.fs index 8769a8d14..0549553a5 100644 --- a/src/FsAutoComplete.Core/CodeGeneration.fs +++ b/src/FsAutoComplete.Core/CodeGeneration.fs @@ -25,7 +25,7 @@ type ICodeGenerationService = abstract GetSymbolAndUseAtPositionOfKind: string * Position * SymbolKind -> Async>> - abstract ParseFileInProject: string -> option + abstract ParseFileInProject: string -> Async> type CodeGenerationService(checker: FSharpCompilerServiceChecker, state: State) = interface ICodeGenerationService with @@ -65,14 +65,17 @@ type CodeGenerationService(checker: FSharpCompilerServiceChecker, state: State) } override x.ParseFileInProject(fileName) = - match state.TryGetFileCheckerOptionsWithLines fileName with - | ResultOrString.Error _ -> None - | ResultOrString.Ok(opts, text) -> - try - checker.TryGetRecentCheckResultsForFile(fileName, opts, text) - |> Option.map (fun n -> n.GetParseResults) - with _ -> - None + async { + match state.TryGetFileCheckerOptionsWithLines fileName with + | ResultOrString.Error _ -> return None + | ResultOrString.Ok(opts, text) -> + try + return + checker.TryGetRecentCheckResultsForFile(fileName, opts, text) + |> Option.map (fun n -> n.GetParseResults) + with _ -> + return None + } module CodeGenerationUtils = open FSharp.Compiler.Syntax.PrettyNaming diff --git a/src/FsAutoComplete.Core/Commands.fs b/src/FsAutoComplete.Core/Commands.fs index e894cfee4..de49cc019 100644 --- a/src/FsAutoComplete.Core/Commands.fs +++ b/src/FsAutoComplete.Core/Commands.fs @@ -531,11 +531,12 @@ module Commands = if fsym.IsPrivateToFile then return CoreResponse.Res(LocationResponse.Use(sym, filterSymbols usages)) else if fsym.IsInternalToProject then - let opts = getProjectOptions tyRes.FileName + let! opts = getProjectOptions tyRes.FileName let! symbols = getUsesOfSymbol (tyRes.FileName, [ UMX.untag tyRes.FileName, opts ], sym.Symbol) return CoreResponse.Res(LocationResponse.Use(sym, filterSymbols symbols)) else - let! symbols = getUsesOfSymbol (tyRes.FileName, getAllProjects (), sym.Symbol) + let! projs = getAllProjects () + let! symbols = getUsesOfSymbol (tyRes.FileName, projs, sym.Symbol) let symbols = filterSymbols symbols return CoreResponse.Res(LocationResponse.Use(sym, filterSymbols symbols)) | Error e -> return CoreResponse.ErrorRes e @@ -677,7 +678,7 @@ module Commands = return CoreResponse.Res hints } - |> Result.fold id (fun _ -> CoreResponse.InfoRes "Couldn't find file content") + |> Result.bimap id (fun _ -> CoreResponse.InfoRes "Couldn't find file content") let calculateNamespaceInsert currentAst @@ -760,11 +761,11 @@ module Commands = /// * When exact ranges are required /// -> for "Rename" let symbolUseWorkspace - (getDeclarationLocation: FSharpSymbolUse * NamedText -> SymbolDeclarationLocation option) + (getDeclarationLocation: FSharpSymbolUse * NamedText -> Async) (findReferencesForSymbolInFile: (string * FSharpProjectOptions * FSharpSymbol) -> Async) (tryGetFileSource: string -> ResultOrString) - (tryGetProjectOptionsForFsproj: string -> FSharpProjectOptions option) - (getAllProjectOptions: unit -> FSharpProjectOptions seq) + (tryGetProjectOptionsForFsproj: string -> Async) + (getAllProjectOptions: unit -> Async) (includeDeclarations: bool) (includeBackticks: bool) (errorOnFailureToFixRange: bool) @@ -797,7 +798,7 @@ module Commands = |> Seq.toArray |> Ok - let declLoc = getDeclarationLocation (symbolUse, text) + let! declLoc = getDeclarationLocation (symbolUse, text) match declLoc with // local symbol -> all uses are inside `text` @@ -819,23 +820,35 @@ module Commands = return (symbol, ranges) | scope -> - let projectsToCheck = - match scope with - | Some(SymbolDeclarationLocation.Projects(projects (*isLocalForProject=*) , true)) -> projects - | Some(SymbolDeclarationLocation.Projects(projects (*isLocalForProject=*) , false)) -> - [ for project in projects do - yield project - - yield! - project.ReferencedProjects - |> Array.choose (fun p -> UMX.tag p.OutputFile |> tryGetProjectOptionsForFsproj) ] - |> List.distinctBy (fun x -> x.ProjectFileName) - | _ (*None*) -> - // symbol is declared external -> look through all F# projects - // (each script (including untitled) has its own project -> scripts get checked too. But only once they are loaded (-> inside `state`)) - getAllProjectOptions () - |> Seq.distinctBy (fun x -> x.ProjectFileName) - |> Seq.toList + let projectsToCheck: Async = + async { + match scope with + | Some(SymbolDeclarationLocation.Projects(projects (*isLocalForProject=*) , true)) -> return projects + | Some(SymbolDeclarationLocation.Projects(projects (*isLocalForProject=*) , false)) -> + let output = ResizeArray<_>() + + for project in projects do + output.Add(project) + + for r in project.ReferencedProjects do + match! tryGetProjectOptionsForFsproj (UMX.tag r.OutputFile) with + | Some v -> output.Add(v) + | None -> () + + return output |> Seq.toList |> List.distinctBy (fun x -> x.ProjectFileName) + // [ for project in projects do + // yield project + + // yield! + // project.ReferencedProjects + // |> Array.choose (fun p -> UMX.tag p.OutputFile |> tryGetProjectOptionsForFsproj) ] + // |> List.distinctBy (fun x -> x.ProjectFileName) + | _ (*None*) -> + // symbol is declared external -> look through all F# projects + // (each script (including untitled) has its own project -> scripts get checked too. But only once they are loaded (-> inside `state`)) + let! allOptions = getAllProjectOptions () + return allOptions |> Seq.distinctBy (fun x -> x.ProjectFileName) |> Seq.toList + } let tryAdjustRanges (file: string, ranges: Range[]) = match tryGetFileSource file with @@ -922,11 +935,13 @@ module Commands = | None -> Ok() | Some err -> Error err) - do! iterProjects projectsToCheck + let! projects = projectsToCheck + do! iterProjects projects return (symbol, dict) } + /// Puts `newName` into backticks if necessary. /// /// @@ -972,7 +987,7 @@ module Commands = /// Rename for Active Pattern Cases is disabled: /// `SymbolUseWorkspace` returns ranges for ALL Cases of that Active Pattern instead of just the single case let renameSymbolRange - (getDeclarationLocation: FSharpSymbolUse * NamedText -> SymbolDeclarationLocation option) + (getDeclarationLocation: FSharpSymbolUse * NamedText -> Async) (includeBackticks: bool) pos lineStr @@ -987,7 +1002,7 @@ module Commands = let! _ = // None: external symbol -> not under our control -> cannot rename getDeclarationLocation (symbolUse, text) - |> Result.ofOption (fun _ -> "Must be declared inside current workspace, but is external.") + |> AsyncResult.ofOption (fun _ -> "Must be declared inside current workspace, but is external.") do! match symbolUse with @@ -2014,8 +2029,8 @@ type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers: SymbolLocation.getDeclarationLocation ( symbolUse, text, - state.GetProjectOptions, - state.ProjectController.ProjectsThatContainFile, + state.GetProjectOptions >> Async.singleton, + state.ProjectController.ProjectsThatContainFile >> Async.singleton, state.ProjectController.GetDependentProjectsOfProjects ) @@ -2052,9 +2067,10 @@ type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers: let tryGetProjectOptionsForFsproj (fsprojPath: string) = state.ProjectController.GetProjectOptionsForFsproj(UMX.untag fsprojPath) + |> Async.singleton let getAllProjectOptions () = - state.ProjectController.ProjectOptions |> Seq.map snd + state.ProjectController.ProjectOptions |> Seq.map snd |> Async.singleton return! Commands.symbolUseWorkspace @@ -2086,13 +2102,14 @@ type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers: } member x.SymbolImplementationProject (tyRes: ParseAndCheckResults) (pos: Position) lineStr = - let getProjectOptions filePath = state.GetProjectOptions' filePath + let getProjectOptions filePath = + state.GetProjectOptions' filePath |> Async.singleton let getUsesOfSymbol (filePath, opts, sym: FSharpSymbol) = checker.GetUsesOfSymbol(filePath, opts, sym) let getAllProjects () = - state.FSharpProjectOptions |> Seq.toList + state.FSharpProjectOptions |> Seq.toList |> Async.singleton Commands.symbolImplementationProject getProjectOptions getUsesOfSymbol getAllProjects tyRes pos lineStr |> x.AsCancellable tyRes.FileName diff --git a/src/FsAutoComplete.Core/SymbolLocation.fs b/src/FsAutoComplete.Core/SymbolLocation.fs index 9ad9e76b8..b6f9701bc 100644 --- a/src/FsAutoComplete.Core/SymbolLocation.fs +++ b/src/FsAutoComplete.Core/SymbolLocation.fs @@ -4,6 +4,7 @@ open FSharp.Compiler.EditorServices open FSharp.Compiler.CodeAnalysis open System.IO open FSharp.UMX +open FsToolkit.ErrorHandling [] type SymbolDeclarationLocation = @@ -15,27 +16,27 @@ let getDeclarationLocation symbolUse: FSharpSymbolUse, currentDocument: NamedText, getProjectOptions, - projectsThatContainFile, + projectsThatContainFile: string -> Async, getDependentProjectsOfProjects // state: State - ) : SymbolDeclarationLocation option = + ) : Async> = + asyncOption { - // `symbolUse.IsPrivateToFile` throws exception when no `DeclarationLocation` - if - symbolUse.Symbol.DeclarationLocation |> Option.isSome - && symbolUse.IsPrivateToFile - then - Some SymbolDeclarationLocation.CurrentDocument - else - let isSymbolLocalForProject = symbolUse.Symbol.IsInternalToProject + // `symbolUse.IsPrivateToFile` throws exception when no `DeclarationLocation` + if + symbolUse.Symbol.DeclarationLocation |> Option.isSome + && symbolUse.IsPrivateToFile + then + return SymbolDeclarationLocation.CurrentDocument + else + let isSymbolLocalForProject = symbolUse.Symbol.IsInternalToProject - let declarationLocation = - match symbolUse.Symbol.ImplementationLocation with - | Some x -> Some x - | None -> symbolUse.Symbol.DeclarationLocation + let declarationLocation = + match symbolUse.Symbol.ImplementationLocation with + | Some x -> Some x + | None -> symbolUse.Symbol.DeclarationLocation - match declarationLocation with - | Some loc -> + let! loc = declarationLocation let isScript = isAScript loc.FileName // sometimes the source file locations start with a capital, despite all of our efforts. let normalizedPath = @@ -48,22 +49,24 @@ let getDeclarationLocation let taggedFilePath = UMX.tag normalizedPath if isScript && taggedFilePath = currentDocument.FileName then - Some SymbolDeclarationLocation.CurrentDocument + return SymbolDeclarationLocation.CurrentDocument elif isScript then // The standalone script might include other files via '#load' // These files appear in project options and the standalone file // should be treated as an individual project - getProjectOptions (taggedFilePath) - |> Option.map (fun p -> SymbolDeclarationLocation.Projects([ p ], isSymbolLocalForProject)) + return! + getProjectOptions (taggedFilePath) + |> AsyncOption.map (fun p -> SymbolDeclarationLocation.Projects([ p ], isSymbolLocalForProject)) else - match projectsThatContainFile (taggedFilePath) with - | [] -> None + match! projectsThatContainFile (taggedFilePath) with + | [] -> return! None | projectsThatContainFile -> let projectsThatDependOnContainingProjects = getDependentProjectsOfProjects projectsThatContainFile match projectsThatDependOnContainingProjects with - | [] -> Some(SymbolDeclarationLocation.Projects(projectsThatContainFile, isSymbolLocalForProject)) + | [] -> return (SymbolDeclarationLocation.Projects(projectsThatContainFile, isSymbolLocalForProject)) | projects -> - Some(SymbolDeclarationLocation.Projects(projectsThatContainFile @ projects, isSymbolLocalForProject)) - | None -> None + return (SymbolDeclarationLocation.Projects(projectsThatContainFile @ projects, isSymbolLocalForProject)) + + } diff --git a/src/FsAutoComplete.Core/paket.references b/src/FsAutoComplete.Core/paket.references index 6512c0971..f05099119 100644 --- a/src/FsAutoComplete.Core/paket.references +++ b/src/FsAutoComplete.Core/paket.references @@ -5,9 +5,10 @@ ICSharpCode.Decompiler Microsoft.SourceLink.GitHub System.Configuration.ConfigurationManager FSharp.UMX -FsToolkit.ErrorHandling +FsToolkit.ErrorHandling.TaskResult Fantomas.Client FSharp.Data.Adaptive +IcedTasks Ionide.ProjInfo.ProjectSystem System.Reflection.Metadata diff --git a/src/FsAutoComplete/CodeFixes.fs b/src/FsAutoComplete/CodeFixes.fs index 378b0e289..ef0c3f741 100644 --- a/src/FsAutoComplete/CodeFixes.fs +++ b/src/FsAutoComplete/CodeFixes.fs @@ -28,7 +28,8 @@ module Types = -> FSharp.Compiler.Text.Position -> Async> - type GetProjectOptionsForFile = string -> ResultOrString + type GetProjectOptionsForFile = + string -> Async> [] type FixKind = diff --git a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs index 6d70414f2..a60a763a7 100644 --- a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs +++ b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs @@ -972,26 +972,26 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let openFilesToChangesAndProjectOptions = openFilesWithChanges - |> AMap.mapAVal (fun filePath file -> - aval { + |> AMapAsync.mapAsyncAVal (fun filePath file ctok -> + asyncAVal { if Utils.isAScript (UMX.untag filePath) then let! checker = checker and! tfmConfig = tfmConfig - let projs = - option { + let! projs = + taskOption { let! cts = tryGetOpenFileToken filePath let! opts = checker.GetProjectOptionsFromScript(filePath, file.Lines, tfmConfig) - |> Async.RunSynchronouslyWithCTSafe(fun () -> cts.Token) + |> Async.withCancellation cts.Token + |> Async.startImmediateAsTask ctok opts |> scriptFileProjectOptions.Trigger return opts } - |> Option.toList - return file, projs + return file, Option.toList projs else let! projs = sourceFileToProjectOptions @@ -1003,18 +1003,29 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let allProjectOptions = let wins = - openFilesToChangesAndProjectOptions |> AMap.map (fun k v -> v |> AVal.map snd) + openFilesToChangesAndProjectOptions + |> AMap.map (fun k v -> v |> AsyncAVal.mapSync (fun d _ -> snd d)) - let loses = sourceFileToProjectOptions |> AMap.map (fun k v -> AVal.constant v) + let loses = sourceFileToProjectOptions |> AMap.map (fun k v -> AsyncAVal.constant v) AMap.union loses wins - let allProjectOptions' = - allProjectOptions |> AMap.toASetValues |> ASet.collect (ASet.ofAVal) + let getAllProjectOptions () = + async { + let! set = + allProjectOptions + |> AMap.toASetValues + |> ASet.force + |> HashSet.toArray + |> Array.map (AsyncAVal.forceAsync) + |> Async.Parallel + + return set |> Array.collect (List.toArray) + } let getProjectOptionsForFile (filePath: string) = - aval { - match! allProjectOptions |> AMap.tryFindA filePath with + asyncAVal { + match! allProjectOptions |> AMapAsync.tryFindAsyncAval filePath with | Some projs -> return projs | None -> return [] } @@ -1208,13 +1219,13 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let openFilesToParsedResults = openFilesToChangesAndProjectOptions - |> AMap.mapAVal (fun _ (info, projectOptions) -> - aval { + |> AMapAsync.mapAsyncAVal2 (fun _ (info, projectOptions) ctok -> + asyncAVal { let file = info.Lines.FileName let! checker = checker return - option { + taskOption { let! opts = selectProject projectOptions and! cts = tryGetOpenFileToken file @@ -1222,7 +1233,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! result = checker.ParseFile(file, info.Lines, parseOpts) - |> Async.RunSynchronouslyWithCTSafe(fun () -> cts.Token) + |> Async.withCancellation cts.Token + |> fun work -> Async.StartImmediateAsTask(work, ctok) fileParsed.Trigger(result, opts, cts.Token) return result @@ -1233,8 +1245,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let openFilesToRecentCheckedFilesResults = openFilesToChangesAndProjectOptions - |> AMap.mapAVal (fun _ (info, projectOptions) -> - aval { + |> AMapAsync.mapAsyncAVal2 (fun _ (info, projectOptions) _ -> + asyncAVal { let file = info.Lines.FileName let! checker = checker @@ -1247,53 +1259,56 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let openFilesToCheckedFilesResults = openFilesToChangesAndProjectOptions - |> AMap.mapAVal (fun _ (info, projectOptions) -> - aval { + |> AMapAsync.mapAsyncAVal2 (fun _ (info, projectOptions) ctok -> + asyncAVal { let file = info.Lines.FileName let! checker = checker and! config = config return - option { + taskOption { let! opts = selectProject projectOptions and! cts = tryGetOpenFileToken file return! - Debug.measure $"parseAndCheckFile - {file}" - <| fun () -> - parseAndCheckFile checker info opts config true - |> Async.RunSynchronouslyWithCTSafe(fun () -> cts.Token) + parseAndCheckFile checker info opts config true + |> Async.withCancellation cts.Token + |> fun work -> Async.StartImmediateAsTask(work, ctok) } }) let getParseResults filePath = - openFilesToParsedResults |> AMap.tryFindAndFlatten filePath + openFilesToParsedResults |> AMapAsync.tryFindAsyncAndFlatten filePath let getTypeCheckResults filePath = - openFilesToCheckedFilesResults |> AMap.tryFindAndFlatten (filePath) + openFilesToCheckedFilesResults |> AMapAsync.tryFindAsyncAndFlatten (filePath) let getRecentTypeCheckResults filePath = - openFilesToRecentCheckedFilesResults |> AMap.tryFindAndFlatten (filePath) + openFilesToRecentCheckedFilesResults + |> AMapAsync.tryFindAsyncAndFlatten (filePath) let tryGetLineStr pos (text: NamedText) = text.GetLine(pos) |> Result.ofOption (fun () -> $"No line in {text.FileName} at position {pos}") let forceGetParseResults filePath = - getParseResults filePath - |> AVal.force - |> Result.ofOption (fun () -> $"No parse results for {filePath}") + async { + let! results = getParseResults filePath |> AsyncAVal.forceAsync + return results |> Result.ofOption (fun () -> $"No parse results for {filePath}") + } let forceGetRecentTypeCheckResults filePath = - getRecentTypeCheckResults filePath - |> AVal.force - |> Result.ofOption (fun () -> $"No typecheck results for {filePath}") + async { + let! results = getRecentTypeCheckResults filePath |> AsyncAVal.forceAsync + return results |> Result.ofOption (fun () -> $"No typecheck results for {filePath}") + } let forceGetTypeCheckResults (filePath: string) = - getTypeCheckResults (filePath) - |> AVal.force - |> Result.ofOption (fun () -> $"No typecheck results for {filePath}") + async { + let! results = getTypeCheckResults (filePath) |> AsyncAVal.forceAsync + return results |> Result.ofOption (fun () -> $"No typecheck results for {filePath}") + } /// /// This will attempt to get typecheck results in this order @@ -1307,44 +1322,67 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar /// The name of the file in the project whose source to find a typecheck. /// A Result of ParseAndCheckResults let forceGetTypeCheckResultsStale (filePath: string) = - aval { + asyncAVal { let! checker = checker let inline tryGetLastCheckResultForFile filePath = checker.TryGetLastCheckResultForFile(filePath) |> Result.ofOption (fun () -> $"No typecheck results for {filePath}") + |> async.Return - return + return! tryGetLastCheckResultForFile filePath - |> Result.orElseWith (fun _ -> forceGetRecentTypeCheckResults filePath) - |> Result.orElseWith (fun _ -> forceGetTypeCheckResults filePath) - |> Result.tee (fun _ -> Async.Start(async { forceGetTypeCheckResults filePath |> ignore })) + |> AsyncResult.orElseWith (fun _ -> forceGetRecentTypeCheckResults filePath) + |> AsyncResult.orElseWith (fun _ -> forceGetTypeCheckResults filePath) + |> Async.StartImmediateAsTask + // |> Result.tee (fun _ -> Async.Start(async { forceGetTypeCheckResults filePath |> ignore })) } - |> AVal.force + |> AsyncAVal.forceAsync let openFilesToCheckedDeclarations = openFilesToCheckedFilesResults |> AMap.map (fun k v -> v - |> AVal.mapOption (fun c -> c.GetParseResults.GetNavigationItems().Declarations)) + |> AsyncAVal.mapOption (fun c -> c.GetParseResults.GetNavigationItems().Declarations)) let getAllDeclarations () = - openFilesToCheckedDeclarations |> AMap.chooseA (fun k v -> v) |> AMap.force + async { + let! results = + openFilesToCheckedDeclarations + |> AMap.force + |> HashMap.toArray + |> Array.map (fun (k, v) -> + async { + let! decls = AsyncAVal.forceAsync v + return Option.map (fun v -> k, v) decls + }) + |> Async.Parallel + + return results |> Array.Parallel.choose id + + } let getDeclarations filename = - openFilesToCheckedDeclarations |> AMap.tryFindAndFlatten filename + openFilesToCheckedDeclarations |> AMapAsync.tryFindAsyncAndFlatten filename let getFilePathAndPosition (p: ITextDocumentPositionParams) = let filePath = p.GetFilePath() |> Utils.normalizePath let pos = p.GetFcsPos() filePath, pos + let forceGetProjectOptions filePath = - getProjectOptionsForFile filePath - |> AVal.force - |> selectProject - |> Result.ofOption (fun () -> $"Could not find project containing {filePath}") + asyncAVal { + let! projects = getProjectOptionsForFile filePath + let project = selectProject projects + + return + project + |> Result.ofOption (fun () -> $"Could not find project containing {filePath}") + + } + |> AsyncAVal.forceAsync let codeGenServer = { new ICodeGenerationService with @@ -1376,7 +1414,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar if symbol.Kind = kind then let! (text) = forceFindOpenFileOrRead fileName |> Option.ofResult let! line = tryGetLineStr pos text.Lines |> Option.ofResult - let! tyRes = forceGetTypeCheckResults fileName |> Option.ofResult + let! tyRes = forceGetTypeCheckResults fileName |> Async.map (Option.ofResult) let symbolUse = tyRes.TryGetSymbolUse pos line return! Some(symbol, symbolUse) else @@ -1384,7 +1422,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar } member x.ParseFileInProject(file) = - forceGetParseResults file |> Option.ofResult } + forceGetParseResults file |> Async.map (Option.ofResult) } let getDependentProjectsOfProjects ps = let projectSnapshot = forceLoadProjects () @@ -1420,10 +1458,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let getDeclarationLocation (symbolUse, text) = let getProjectOptions file = - getProjectOptionsForFile file |> AVal.force |> selectProject + getProjectOptionsForFile file |> AsyncAVal.forceAsync |> Async.map selectProject let projectsThatContainFile file = - getProjectOptionsForFile file |> AVal.force + getProjectOptionsForFile file |> AsyncAVal.forceAsync SymbolLocation.getDeclarationLocation ( symbolUse, @@ -1452,7 +1490,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! checker.FindReferencesForSymbolInFile(UMX.untag file, project, symbol) else // untitled script files - match forceGetRecentTypeCheckResults file with + match! forceGetRecentTypeCheckResults file with | Error _ -> return [||] | Ok tyRes -> let! ct = Async.CancellationToken @@ -1461,17 +1499,28 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar } let tryGetProjectOptionsForFsproj (file: string) = - forceGetProjectOptions file |> Option.ofResult + forceGetProjectOptions file |> Async.map Option.ofResult + + let getAllProjectOptions () = + async { + let! set = + allProjectOptions + |> AMap.toASetValues + |> ASet.force + |> HashSet.toArray + |> Array.map (AsyncAVal.forceAsync) + |> Async.Parallel + + return set |> Array.collect (List.toArray) + } - let getAllProjectOptions () : _ seq = - allProjectOptions'.Content |> AVal.force :> _ Commands.symbolUseWorkspace getDeclarationLocation findReferencesForSymbolInFile forceFindSourceText tryGetProjectOptionsForFsproj - getAllProjectOptions + (getAllProjectOptions >> Async.map Array.toSeq) includeDeclarations includeBackticks errorOnFailureToFixRange @@ -1606,98 +1655,107 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar RenameParamToMatchSignature.fix tryGetParseResultsForFile |]) let forgetDocument (uri: DocumentUri) = - let filePath = uri |> Path.FileUriToLocalPath |> Utils.normalizePath + async { + let filePath = uri |> Path.FileUriToLocalPath |> Utils.normalizePath - let doesNotExist (file: string) = not (File.Exists(UMX.untag file)) + let doesNotExist (file: string) = not (File.Exists(UMX.untag file)) - let isOutsideWorkspace (file: string) = - aval { - let! rootPath = rootPath - - match rootPath with - | None -> return true // no root workspace specified - | Some rootPath -> - let rec isInside (rootDir: DirectoryInfo, dirToCheck: DirectoryInfo) = - if String.Equals(rootDir.FullName, dirToCheck.FullName, StringComparison.InvariantCultureIgnoreCase) then - true - else - match dirToCheck.Parent with - | null -> false - | parent -> isInside (rootDir, parent) + let isOutsideWorkspace (file: string) = + asyncAVal { + let! rootPath = rootPath - let rootDir = DirectoryInfo(rootPath) - let fileDir = FileInfo(UMX.untag file).Directory + match rootPath with + | None -> return true // no root workspace specified + | Some rootPath -> + let rec isInside (rootDir: DirectoryInfo, dirToCheck: DirectoryInfo) = + if String.Equals(rootDir.FullName, dirToCheck.FullName, StringComparison.InvariantCultureIgnoreCase) then + true + else + match dirToCheck.Parent with + | null -> false + | parent -> isInside (rootDir, parent) - if isInside (rootDir, fileDir) then - return false - else - let! projectOptions = getProjectOptionsForFile file + let rootDir = DirectoryInfo(rootPath) + let fileDir = FileInfo(UMX.untag file).Directory - match projectOptions |> selectProject with - | None -> return true - | Some projectOptions -> - if doesNotExist (UMX.tag projectOptions.ProjectFileName) then - return true // script file - else - // issue: fs-file does never get removed from project options (-> requires reload of FSAC to register) - // -> don't know if file still part of project (file might have been removed from project) - // -> keep cache for file - return false - } + if isInside (rootDir, fileDir) then + return false + else + let! projectOptions = getProjectOptionsForFile file + + match projectOptions |> selectProject with + | None -> return true + | Some projectOptions -> + if doesNotExist (UMX.tag projectOptions.ProjectFileName) then + return true // script file + else + // issue: fs-file does never get removed from project options (-> requires reload of FSAC to register) + // -> don't know if file still part of project (file might have been removed from project) + // -> keep cache for file + return false + } - |> AVal.force + |> AsyncAVal.forceAsync - transact (fun () -> - openFiles.Remove filePath |> ignore + transact (fun () -> + openFiles.Remove filePath |> ignore - match openFilesTokens.TryRemove(filePath) with - | (true, cts) -> cancelToken filePath cts - | _ -> () + match openFilesTokens.TryRemove(filePath) with + | (true, cts) -> cancelToken filePath cts + | _ -> () - textChanges.Remove filePath |> ignore) + textChanges.Remove filePath |> ignore) - if doesNotExist filePath || isOutsideWorkspace filePath then - logger.info ( - Log.setMessage "Removing cached data for {file}." - >> Log.addContext "file" filePath - ) + let! isOutsideWorkspace = isOutsideWorkspace filePath + + if doesNotExist filePath || isOutsideWorkspace then + logger.info ( + Log.setMessage "Removing cached data for {file}." + >> Log.addContext "file" filePath + ) + + diagnosticCollections.ClearFor(uri) + else + logger.info ( + Log.setMessage "File {file} exists inside workspace so diagnostics will not be cleared" + >> Log.addContext "file" filePath + ) + } - diagnosticCollections.ClearFor(uri) - else - logger.info ( - Log.setMessage "File {file} exists inside workspace so diagnostics will not be cleared" - >> Log.addContext "file" filePath - ) let getDependentFilesForFile file = - let projects = getProjectOptionsForFile file |> AVal.force + async { + let! projects = getProjectOptionsForFile file |> AsyncAVal.forceAsync - projects - |> List.toArray - |> Array.collect (fun proj -> - logger.info ( - Log.setMessage "Source Files: {sourceFiles}" - >> Log.addContextDestructured "sourceFiles" proj.SourceFiles - ) + return + projects + |> List.toArray + |> Array.collect (fun proj -> + logger.info ( + Log.setMessage "Source Files: {sourceFiles}" + >> Log.addContextDestructured "sourceFiles" proj.SourceFiles + ) - let idx = proj.SourceFiles |> Array.findIndex (fun x -> x = UMX.untag file) + let idx = proj.SourceFiles |> Array.findIndex (fun x -> x = UMX.untag file) - proj.SourceFiles - |> Array.splitAt idx - |> snd - |> Array.map (fun sourceFile -> proj, sourceFile)) - |> Array.distinct + proj.SourceFiles + |> Array.splitAt idx + |> snd + |> Array.map (fun sourceFile -> proj, sourceFile)) + |> Array.distinct + } let bypassAdaptiveAndCheckDepenenciesForFile (filePath: string) = async { let tags = [ SemanticConventions.fsac_sourceCodePath, box (UMX.untag filePath) ] use _ = fsacActivitySource.StartActivityForType(thisType, tags = tags) - let dependentFiles = getDependentFilesForFile filePath + let! dependentFiles = getDependentFilesForFile filePath + + let! projs = getProjectOptionsForFile filePath |> AsyncAVal.forceAsync let dependentProjects = - getProjectOptionsForFile filePath - |> AVal.force + projs |> getDependentProjectsOfProjects |> List.toArray |> Array.collect (fun proj -> proj.SourceFiles |> Array.map (fun sourceFile -> proj, sourceFile)) @@ -1761,13 +1819,72 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar } + let getDeclarationLocation (symbolUse, text) = + let getProjectOptions file = + getProjectOptionsForFile file |> AsyncAVal.forceAsync |> Async.map selectProject + + let projectsThatContainFile file = + getProjectOptionsForFile file |> AsyncAVal.forceAsync + + SymbolLocation.getDeclarationLocation ( + symbolUse, + text, + getProjectOptions, + projectsThatContainFile, + getDependentProjectsOfProjects + ) + + let symbolUseWorkspace + (includeDeclarations: bool) + (includeBackticks: bool) + (errorOnFailureToFixRange: bool) + pos + lineStr + text + tyRes + = + + let findReferencesForSymbolInFile (file: string, project, symbol) = + async { + let checker = checker |> AVal.force + + if File.Exists(UMX.untag file) then + // `FSharpChecker.FindBackgroundReferencesInFile` only works with existing files + return! checker.FindReferencesForSymbolInFile(UMX.untag file, project, symbol) + else + // untitled script files + match! forceGetRecentTypeCheckResults file with + | Error _ -> return [||] + | Ok tyRes -> + let! ct = Async.CancellationToken + let usages = tyRes.GetCheckResults.GetUsesOfSymbolInFile(symbol, ct) + return usages |> Seq.map (fun u -> u.Range) + } + let tryGetProjectOptionsForFsproj (file: string) = + forceGetProjectOptions file |> Async.map Option.ofResult + let getAllProjectOptions () = + getAllProjectOptions () |> Async.map Array.toSeq - member private x.handleSemanticTokens (filePath: string) range : LspResult = - result { + Commands.symbolUseWorkspace + getDeclarationLocation + findReferencesForSymbolInFile + forceFindSourceText + tryGetProjectOptionsForFsproj + getAllProjectOptions + includeDeclarations + includeBackticks + errorOnFailureToFixRange + pos + lineStr + text + tyRes + + member private x.handleSemanticTokens (filePath: string) range : AsyncLspResult = + asyncResult { - let! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr + let! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr let r = tyRes.GetCheckResults.GetSemanticClassification(range) let filteredRanges = Commands.scrubRanges r @@ -2106,7 +2223,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let doc = p.TextDocument - forgetDocument doc.Uri + do! forgetDocument doc.Uri return () with e -> @@ -2419,7 +2536,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar | Some insertText -> helpText insertText |> Result.ofCoreResponse - |> Result.fold + |> Result.bimap (function | None -> ci | Some text -> mapHelpText ci text) @@ -2452,7 +2569,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let (filePath, pos) = getFilePathAndPosition p let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr - and! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr + and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr @@ -2517,7 +2634,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let (filePath, pos) = getFilePathAndPosition p let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr - and! tyRes = forceGetTypeCheckResultsStale filePath |> Result.ofStringErr + and! tyRes = forceGetTypeCheckResultsStale filePath |> AsyncResult.ofStringErr match tyRes.TryGetToolTipEnhanced pos lineStr with | Ok(Some tooltipResult) -> @@ -2610,7 +2727,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let (filePath, pos) = getFilePathAndPosition p let! namedText = forceFindOpenFileOrRead filePath |> Result.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr - let! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr + let! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr let! (_, _, range) = Commands.renameSymbolRange getDeclarationLocation false pos lineStr namedText.Lines tyRes @@ -2633,7 +2750,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let (filePath, pos) = getFilePathAndPosition p let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr - and! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr + and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr // validate name and surround with backticks if necessary let! newName = @@ -2701,7 +2818,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr - and! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr + and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr let! decl = tyRes.TryFindDeclaration pos lineStr |> AsyncResult.ofStringErr return decl |> findDeclToLspLocation |> GotoResult.Single |> Some with e -> @@ -2731,7 +2848,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr - and! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr + and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr let! decl = tyRes.TryFindTypeDeclaration pos lineStr |> AsyncResult.ofStringErr return decl |> findDeclToLspLocation |> GotoResult.Single |> Some with e -> @@ -2760,7 +2877,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let (filePath, pos) = getFilePathAndPosition p let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr let! lineStr = tryGetLineStr pos namedText.Lines |> Result.ofStringErr - and! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr + and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr let! (_, usages) = symbolUseWorkspace true true false pos lineStr namedText.Lines tyRes @@ -2796,7 +2913,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let (filePath, pos) = getFilePathAndPosition p let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr let! lineStr = tryGetLineStr pos namedText.Lines |> Result.ofStringErr - and! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr + and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr match tyRes.TryGetSymbolUseAndUsages pos lineStr @@ -2838,7 +2955,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let (filePath, pos) = getFilePathAndPosition p let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr let! lineStr = tryGetLineStr pos namedText.Lines |> Result.ofStringErr - and! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr + and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr logger.info ( Log.setMessage "TextDocumentImplementation Request: {parms}" @@ -2846,7 +2963,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let getProjectOptions file = - getProjectOptionsForFile file |> AVal.force |> List.head + getProjectOptionsForFile file |> AsyncAVal.forceAsync |> Async.map List.head let checker = checker |> AVal.force @@ -2857,11 +2974,13 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar allProjectOptions |> AMap.force |> Seq.toList - |> List.choose (fun (path, opt) -> - option { - let! opt = AVal.force opt |> selectProject - return UMX.untag path, opt + |> Seq.map (fun (k, v) -> + async { + let! proj = AsyncAVal.forceAsync v + return Option.map (fun proj -> UMX.untag k, proj) (selectProject proj) }) + |> Async.Parallel + |> Async.map (Array.choose id >> List.ofArray) let! res = Commands.symbolImplementationProject getProjectOptions getUsesOfSymbol getAllProjects tyRes pos lineStr @@ -2905,7 +3024,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let fn = p.TextDocument.GetFilePath() |> Utils.normalizePath - match getDeclarations fn |> AVal.force with + match! getDeclarations fn |> AsyncAVal.forceAsync with | Some decls -> return glyphToSymbolKind @@ -2943,7 +3062,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let glyphToSymbolKind = glyphToSymbolKind |> AVal.force - let decls = getAllDeclarations () |> Seq.toArray + let! decls = getAllDeclarations () let res = decls @@ -3141,7 +3260,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let fn = p.TextDocument.GetFilePath() |> Utils.normalizePath - match getDeclarations (fn) |> AVal.force with + match! getDeclarations (fn) |> AsyncAVal.forceAsync with | None -> return None | Some decls -> let config = AVal.force config @@ -3186,7 +3305,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let filePath = Path.FileUriToLocalPath data.[0] |> Utils.normalizePath try - let! tyRes = forceGetTypeCheckResultsStale filePath |> Result.ofStringErr + let! tyRes = forceGetTypeCheckResultsStale filePath |> AsyncResult.ofStringErr logger.info ( @@ -3329,12 +3448,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar >> Log.addContextDestructured "parms" p ) - p.Changes - |> Array.iter (fun c -> + for c in p.Changes do if c.Type = FileChangeType.Deleted then - forgetDocument c.Uri - - ()) + do! forgetDocument c.Uri with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -3522,7 +3638,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let filePath = p.TextDocument.GetFilePath() |> Utils.normalizePath let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr - and! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr + and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr let fcsRange = protocolRangeToRange (UMX.untag filePath) p.Range let config = config |> AVal.force @@ -3628,7 +3744,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr - let! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr + let! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr let fcsRange = protocolRangeToRange (UMX.untag filePath) p.Range @@ -3832,7 +3948,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr - and! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr + and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr let! tip = Commands.typesig tyRes pos lineStr |> Result.ofCoreResponse return @@ -3868,7 +3984,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr - and! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr + and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr let! (typ, parms, generics) = tyRes.TryGetSignatureData pos lineStr |> Result.ofStringErr return @@ -3903,7 +4019,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr - and! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr + and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr match! Commands.GenerateXmlDocumentation(tyRes, pos, lineStr) @@ -3952,7 +4068,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let fn = p.Project.GetFilePath() |> Utils.normalizePath - match getDeclarations fn |> AVal.force with + match! getDeclarations fn |> AsyncAVal.forceAsync with | None -> return! LspResult.internalError $"No declerations found for {fn}" | Some decls -> let decls = decls |> Array.map (fun d -> d, fn) @@ -4250,7 +4366,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let (filePath, pos) = getFilePathAndPosition p let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr - and! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr + and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr match! Commands.Help tyRes pos lineStr |> Result.ofCoreResponse with | Some t -> return Some { Content = CommandResponse.help FsAutoComplete.JsonSerializer.writeJson t } @@ -4281,7 +4397,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let (filePath, pos) = getFilePathAndPosition p let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr - and! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr + and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr lastFSharpDocumentationTypeCheck <- Some tyRes match! Commands.FormattedDocumentation tyRes pos lineStr |> Result.ofCoreResponse with @@ -4391,7 +4507,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let filePath = p.TextDocument.GetFilePath() |> Utils.normalizePath - let! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr + let! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr match! Commands.pipelineHints forceFindSourceText tyRes |> Result.ofCoreResponse with | None -> return None diff --git a/src/FsAutoComplete/LspServers/Common.fs b/src/FsAutoComplete/LspServers/Common.fs index a6e9d8cf9..9e29f0510 100644 --- a/src/FsAutoComplete/LspServers/Common.fs +++ b/src/FsAutoComplete/LspServers/Common.fs @@ -194,6 +194,9 @@ module Async = let StartWithCT ct work = Async.Start(work, ct) + let startImmediateAsTask ct work = + Async.StartImmediateAsTask(work, ct) + let RunSynchronouslyWithCT ct work = Async.RunSynchronously(work, cancellationToken = ct) diff --git a/src/FsAutoComplete/LspServers/FsAutoComplete.Lsp.fs b/src/FsAutoComplete/LspServers/FsAutoComplete.Lsp.fs index 8ad54220b..f6b58b019 100644 --- a/src/FsAutoComplete/LspServers/FsAutoComplete.Lsp.fs +++ b/src/FsAutoComplete/LspServers/FsAutoComplete.Lsp.fs @@ -1117,7 +1117,7 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let getProjectOptsAndLines = commands.TryGetFileCheckerOptionsWithLinesAndLineStr let tryGetProjectOptions = - commands.TryGetFileCheckerOptionsWithLines >> Result.map fst + commands.TryGetFileCheckerOptionsWithLines >> Result.map fst >> Async.singleton let implementInterfaceConfig () : ImplementInterface.Config = { ObjectIdentifier = config.InterfaceStubGenerationObjectIdentifier @@ -1326,7 +1326,7 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let initialText = state.TryGetFileSource(filePath) - |> Result.fold id (fun _ -> NamedText(filePath, "")) + |> Result.bimap id (fun _ -> NamedText(filePath, "")) let evolvedFileContent = (initialText, p.ContentChanges) From 7e4986c9a698f7f0ce59a10cc2137b27cac69ed2 Mon Sep 17 00:00:00 2001 From: Jimmy Byrd Date: Sat, 25 Mar 2023 22:29:33 -0400 Subject: [PATCH 02/10] More async in more places --- .../AbstractClassStubGenerator.fs | 109 ++++---- src/FsAutoComplete.Core/AdaptiveExtensions.fs | 33 ++- src/FsAutoComplete.Core/CodeGeneration.fs | 108 +++---- src/FsAutoComplete.Core/Commands.fs | 74 ++--- .../CompilerServiceInterface.fs | 40 ++- .../RecordStubGenerator.fs | 11 +- .../UnionPatternMatchCaseGenerator.fs | 171 ++++++------ src/FsAutoComplete.Core/Utils.fs | 9 + src/FsAutoComplete/CodeFixes.fs | 25 +- .../CodeFixes/ChangeDowncastToUpcast.fs | 58 ++-- .../LspServers/AdaptiveFSharpLspServer.fs | 263 +++++++++--------- src/FsAutoComplete/LspServers/Common.fs | 24 +- .../LspServers/FsAutoComplete.Lsp.fs | 44 +-- 13 files changed, 518 insertions(+), 451 deletions(-) diff --git a/src/FsAutoComplete.Core/AbstractClassStubGenerator.fs b/src/FsAutoComplete.Core/AbstractClassStubGenerator.fs index 1d6efbf4d..6b6fc134a 100644 --- a/src/FsAutoComplete.Core/AbstractClassStubGenerator.fs +++ b/src/FsAutoComplete.Core/AbstractClassStubGenerator.fs @@ -6,6 +6,7 @@ open FSharp.Compiler.Syntax open FSharp.Compiler.Symbols open FSharp.Compiler.Tokenization open FsAutoComplete.Logging +open FsToolkit.ErrorHandling type AbstractClassData = @@ -122,26 +123,29 @@ let inferStartColumn (abstractClassData: AbstractClassData) (indentSize: int) = - match getMemberNameAndRanges abstractClassData with - | (_, range) :: _ -> getLineIdent (lines.GetLineString(range.StartLine - 1)) - | [] -> - match abstractClassData with - | AbstractClassData.ExplicitImpl _ -> - // 'interface ISomething with' is often in a new line, we use the indentation of that line - getLineIdent lineStr + indentSize - | AbstractClassData.ObjExpr(_, _, newExprRange) -> - match codeGenServer.TokenizeLine(doc.FullName, pos.Line) with - | Some tokens -> - tokens - |> List.tryPick (fun (t: FSharpTokenInfo) -> - if t.CharClass = FSharpTokenCharKind.Keyword && t.TokenName = "NEW" then - // We round to nearest so the generated code will align on the indentation guides - findGreaterMultiple (t.LeftColumn + indentSize) indentSize |> Some - else - None) - // There is no reference point, we indent the content at the start column of the interface - |> Option.defaultValue newExprRange.StartColumn - | None -> newExprRange.StartColumn + async { + match getMemberNameAndRanges abstractClassData with + | (_, range) :: _ -> return getLineIdent (lines.GetLineString(range.StartLine - 1)) + | [] -> + match abstractClassData with + | AbstractClassData.ExplicitImpl _ -> + // 'interface ISomething with' is often in a new line, we use the indentation of that line + return getLineIdent lineStr + indentSize + | AbstractClassData.ObjExpr(_, _, newExprRange) -> + match! codeGenServer.TokenizeLine(doc.FullName, pos.Line) with + | Some tokens -> + return + tokens + |> List.tryPick (fun (t: FSharpTokenInfo) -> + if t.CharClass = FSharpTokenCharKind.Keyword && t.TokenName = "NEW" then + // We round to nearest so the generated code will align on the indentation guides + findGreaterMultiple (t.LeftColumn + indentSize) indentSize |> Some + else + None) + // There is no reference point, we indent the content at the start column of the interface + |> Option.defaultValue newExprRange.StartColumn + | None -> return newExprRange.StartColumn + } /// Try to write any missing members of the given abstract type at the given location. /// If the destination type isn't an abstract class, or if there are no missing members to implement, @@ -154,7 +158,7 @@ let writeAbstractClassStub (lineStr: string) (abstractClassData: AbstractClassData) = - asyncMaybe { + asyncOption { let pos = Position.mkPos abstractClassData.AbstractTypeIdentRange.Start.Line @@ -164,7 +168,7 @@ let writeAbstractClassStub let! usage = usages let! (displayContext, entity) = - asyncMaybe { + asyncOption { // need the enclosing entity because we're always looking at a ctor, which isn't an Entity, but a MemberOrFunctionOrValue match usage.Symbol with | :? FSharpMemberOrFunctionOrValue as v -> @@ -177,19 +181,22 @@ let writeAbstractClassStub } let getMemberByLocation (name, range: Range) = - asyncMaybe { + asyncOption { let pos = Position.fromZ (range.StartLine - 1) (range.StartColumn + 1) return! checkResultForFile.GetCheckResults.GetSymbolUseAtLocation(pos.Line, pos.Column, lineStr, []) } let insertInfo = - match codeGenServer.TokenizeLine(doc.FullName, pos.Line) with - | Some tokens -> - match abstractClassData with - | AbstractClassData.ObjExpr _ -> - findLastPositionOfWithKeyword tokens entity pos (getAbstractClassIdentifier abstractClassData) - | AbstractClassData.ExplicitImpl(_, _, safeInsertPosition) -> Some(false, safeInsertPosition) - | None -> None + asyncOption { + let! tokens = codeGenServer.TokenizeLine(doc.FullName, pos.Line) + + return + match abstractClassData with + | AbstractClassData.ObjExpr _ -> + findLastPositionOfWithKeyword tokens entity pos (getAbstractClassIdentifier abstractClassData) + | AbstractClassData.ExplicitImpl(_, _, safeInsertPosition) -> Some(false, safeInsertPosition) + + } let desiredMemberNamesWithRanges = getMemberNameAndRanges abstractClassData @@ -197,31 +204,35 @@ let writeAbstractClassStub getImplementedMemberSignatures getMemberByLocation displayContext desiredMemberNamesWithRanges |> Async.map Some - let generatedString = - let formattedString = - formatMembersAt - (inferStartColumn codeGenServer pos doc lines lineStr abstractClassData 4) // 4 here correspond to the indent size - 4 // Should we make it a setting from the IDE ? - abstractClassData.TypeParameters - "$objectIdent" - "$methodBody" - displayContext - implementedSignatures - entity - getAbstractNonVirtualMembers - true // Always generate the verbose version of the code - - // If we are in a object expression, we remove the last new line, so the `}` stay on the same line - match abstractClassData with - | AbstractClassData.ExplicitImpl _ -> formattedString - | AbstractClassData.ObjExpr _ -> formattedString.TrimEnd('\n') + let! generatedString = + async { + let! start = (inferStartColumn codeGenServer pos doc lines lineStr abstractClassData 4) // 4 here correspond to the indent size + + let formattedString = + formatMembersAt + start + 4 // Should we make it a setting from the IDE ? + abstractClassData.TypeParameters + "$objectIdent" + "$methodBody" + displayContext + implementedSignatures + entity + getAbstractNonVirtualMembers + true // Always generate the verbose version of the code + + // If we are in a object expression, we remove the last new line, so the `}` stay on the same line + match abstractClassData with + | AbstractClassData.ExplicitImpl _ -> return formattedString + | AbstractClassData.ObjExpr _ -> return formattedString.TrimEnd('\n') + } // If generatedString is empty it means nothing is missing to the abstract class // So we return None, in order to not show a "Falsy Hint" if System.String.IsNullOrEmpty generatedString then return! None else - match insertInfo with + match! insertInfo with | Some(shouldAppendWith, insertPosition) -> if shouldAppendWith then return! Some(insertPosition, " with" + generatedString) diff --git a/src/FsAutoComplete.Core/AdaptiveExtensions.fs b/src/FsAutoComplete.Core/AdaptiveExtensions.fs index 2dc809771..9295c1e0b 100644 --- a/src/FsAutoComplete.Core/AdaptiveExtensions.fs +++ b/src/FsAutoComplete.Core/AdaptiveExtensions.fs @@ -437,7 +437,7 @@ module AsyncAVal = // finally // cts.Dispose() // } - // CancelableTask(cancel, real) + // AdaptiveCancellableTask(cancel, real) // ) // :> asyncaval<_> @@ -460,14 +460,15 @@ module AsyncAVal = let ref = RefCountingTaskCreator( cancellableTask { - // let! ct = CancellableTask.getCancellationToken () + let! ct = CancellableTask.getCancellationToken () let it = input.GetValue t - // let s = ct.Register(fun () -> it.Cancel()) - // try - let! i = it - return! mapping i - // finally - // s.Dispose() + let s = ct.Register(fun () -> it.Cancel()) + + try + let! i = it + return! mapping i + finally + s.Dispose() } ) @@ -670,29 +671,35 @@ module AsyncAValBuilderExtensions = member inline x.Source(value: AdaptiveCancellableTask<'T>) = AsyncAVal.ofCancelableTask value // member inline x.Source(value : CancellableTask<'T>) = AsyncAVal.ofCancellableTask value + member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 -> CancellationToken -> 'T2) = + AsyncAVal.map (fun data ctok -> mapping data ctok |> Task.FromResult) value + member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 -> 'T2) = - AsyncAVal.map (fun data _ -> mapping data |> Task.FromResult) value + AsyncAVal.map (fun data ctok -> mapping data |> Task.FromResult) value + +// member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 -> (CancellationToken * 'T2)) = +// AsyncAVal.map (fun data ct -> mapping data ct |> Task.FromResult) value module AMapAsync = - let mapAsyncAVal + let mapAVal (mapper: 'Key -> 'InValue -> CancellationToken -> asyncaval<'OutValue>) (map: #amap<'Key, #aval<'InValue>>) : amap<'Key, asyncaval<'OutValue>> = map |> AMap.map (fun k v -> v |> AsyncAVal.ofAVal |> AsyncAVal.bind (mapper k)) - let mapAsyncAVal2 + let mapAsyncAVal (mapper: 'Key -> 'InValue -> CancellationToken -> asyncaval<'OutValue>) (map: #amap<'Key, #asyncaval<'InValue>>) : amap<'Key, asyncaval<'OutValue>> = map |> AMap.map (fun k v -> v |> AsyncAVal.bind (mapper k)) /// Adaptively looks up the given key in the map and binds the value to be easily worked with. Note that this operation should not be used extensively since its resulting aval will be re-evaluated upon every change of the map. - let tryFindAsyncAval (key: 'Key) (map: amap<'Key, #asyncaval<'Value>>) = + let tryFindA (key: 'Key) (map: amap<'Key, #asyncaval<'Value>>) = asyncAVal { match! AMap.tryFind key map with | Some v -> @@ -703,7 +710,7 @@ module AMapAsync = /// Adaptively looks up the given key in the map and flattens the value to be easily worked with. Note that this operation should not be used extensively since its resulting aval will be re-evaluated upon every change of the map. - let tryFindAsyncAndFlatten (key: 'Key) (map: amap<'Key, asyncaval>>) = + let tryFindAndFlatten (key: 'Key) (map: amap<'Key, asyncaval>>) = asyncAVal { match! AMap.tryFind key map with | Some x -> return! x diff --git a/src/FsAutoComplete.Core/CodeGeneration.fs b/src/FsAutoComplete.Core/CodeGeneration.fs index 0549553a5..2963343f7 100644 --- a/src/FsAutoComplete.Core/CodeGeneration.fs +++ b/src/FsAutoComplete.Core/CodeGeneration.fs @@ -19,8 +19,8 @@ type Line0 type Line1 type ICodeGenerationService = - abstract TokenizeLine: string * int -> option> - abstract GetSymbolAtPosition: string * Position -> option + abstract TokenizeLine: string * int -> Async>> + abstract GetSymbolAtPosition: string * Position -> Async> abstract GetSymbolAndUseAtPositionOfKind: string * Position * SymbolKind -> Async>> @@ -30,7 +30,7 @@ type ICodeGenerationService = type CodeGenerationService(checker: FSharpCompilerServiceChecker, state: State) = interface ICodeGenerationService with override x.TokenizeLine(fileName, i) = - option { + asyncOption { let! text = state.TryGetFileSource fileName |> Option.ofResult try @@ -41,13 +41,16 @@ type CodeGenerationService(checker: FSharpCompilerServiceChecker, state: State) } override x.GetSymbolAtPosition(fileName, pos: Position) = - match state.TryGetFileCheckerOptionsWithLinesAndLineStr(fileName, pos) with - | ResultOrString.Error _ -> None - | ResultOrString.Ok(opts, lines, line) -> - try - Lexer.getSymbol pos.Line pos.Column line SymbolLookupKind.Fuzzy [||] - with _ -> - None + asyncOption { + return! + match state.TryGetFileCheckerOptionsWithLinesAndLineStr(fileName, pos) with + | ResultOrString.Error _ -> None + | ResultOrString.Ok(opts, lines, line) -> + try + Lexer.getSymbol pos.Line pos.Column line SymbolLookupKind.Fuzzy [||] + with _ -> + None + } override x.GetSymbolAndUseAtPositionOfKind(fileName, pos: Position, kind) = asyncMaybe { @@ -124,49 +127,54 @@ module CodeGenerationUtils = (document: Document) (predicate: FSharpTokenInfo -> bool) = - // Normalize range - // NOTE: FCS compiler sometimes returns an invalid range. In particular, the - // range end limit can exceed the end limit of the document - let range = - if range.EndLine > document.LineCount then - let newEndLine = document.LineCount - let newEndColumn = document.GetLineText1(document.LineCount).Length - let newEndPos = Position.mkPos newEndLine newEndColumn - - Range.mkRange range.FileName range.Start newEndPos - else - range + async { + // Normalize range + // NOTE: FCS compiler sometimes returns an invalid range. In particular, the + // range end limit can exceed the end limit of the document + let range = + if range.EndLine > document.LineCount then + let newEndLine = document.LineCount + let newEndColumn = document.GetLineText1(document.LineCount).Length + let newEndPos = Position.mkPos newEndLine newEndColumn + + Range.mkRange range.FileName range.Start newEndPos + else + range - let lineIdxAndTokenSeq = - seq { - let lines = + let! lineIdxAndTokenSeq = + seq { + let lines = + if range.StartLine = range.EndLine then + [ range.StartLine ] + else + [ range.StartLine .. range.EndLine ] + + for lineIdx in lines do + yield + codeGenService.TokenizeLine(document.FullName, lineIdx) + |> AsyncOption.map (List.map (fun tokenInfo -> lineIdx * 1, tokenInfo)) + + } + |> Async.parallel75 + |> Async.map (Array.Parallel.choose id) + + let lineIdxAndTokenSeq = lineIdxAndTokenSeq |> Array.collect (List.toArray) + + return + lineIdxAndTokenSeq + |> Seq.tryFind (fun (line1, tokenInfo) -> if range.StartLine = range.EndLine then - [ range.StartLine ] + tokenInfo.LeftColumn >= range.StartColumn + && tokenInfo.RightColumn < range.EndColumn + && predicate tokenInfo + elif range.StartLine = int line1 then + tokenInfo.LeftColumn >= range.StartColumn && predicate tokenInfo + elif int line1 = range.EndLine then + tokenInfo.RightColumn < range.EndColumn && predicate tokenInfo else - [ range.StartLine .. range.EndLine ] - - for lineIdx in lines do - match - codeGenService.TokenizeLine(document.FullName, lineIdx) - |> Option.map (List.map (fun tokenInfo -> lineIdx * 1, tokenInfo)) - with - | Some xs -> yield! xs - | None -> () - } - - lineIdxAndTokenSeq - |> Seq.tryFind (fun (line1, tokenInfo) -> - if range.StartLine = range.EndLine then - tokenInfo.LeftColumn >= range.StartColumn - && tokenInfo.RightColumn < range.EndColumn - && predicate tokenInfo - elif range.StartLine = int line1 then - tokenInfo.LeftColumn >= range.StartColumn && predicate tokenInfo - elif int line1 = range.EndLine then - tokenInfo.RightColumn < range.EndColumn && predicate tokenInfo - else - predicate tokenInfo) - |> Option.map (fun (line1, tokenInfo) -> tokenInfo, (Position.fromZ (int line1 - 1) tokenInfo.LeftColumn)) + predicate tokenInfo) + |> Option.map (fun (line1, tokenInfo) -> tokenInfo, (Position.fromZ (int line1 - 1) tokenInfo.LeftColumn)) + } /// Represent environment where a captured identifier should be renamed type NamesWithIndices = Map> diff --git a/src/FsAutoComplete.Core/Commands.fs b/src/FsAutoComplete.Core/Commands.fs index de49cc019..e41c9911f 100644 --- a/src/FsAutoComplete.Core/Commands.fs +++ b/src/FsAutoComplete.Core/Commands.fs @@ -397,7 +397,7 @@ module Commands = } let formatSelection - (tryGetFileCheckerOptionsWithLines: _ -> Result) + (tryGetFileCheckerOptionsWithLines: _ -> Async>) (formatSelectionAsync: _ -> System.Threading.Tasks.Task) (file: string) (rangeToFormat: FormatSelectionRange) @@ -454,7 +454,7 @@ module Commands = } let formatDocument - (tryGetFileCheckerOptionsWithLines: _ -> Result) + (tryGetFileCheckerOptionsWithLines: _ -> Async>) (formatDocumentAsync: _ -> System.Threading.Tasks.Task) (file: string) : Async> = @@ -618,8 +618,8 @@ module Commands = |> AsyncResult.foldResult id (fun _ -> [||]) - let pipelineHints (tryGetFileSource: _ -> Result) (tyRes: ParseAndCheckResults) = - result { + let pipelineHints (tryGetFileSource: _ -> Async>) (tyRes: ParseAndCheckResults) = + asyncResult { // Debug.waitForDebuggerAttached "AdaptiveServer" let! contents = tryGetFileSource tyRes.FileName @@ -678,7 +678,7 @@ module Commands = return CoreResponse.Res hints } - |> Result.bimap id (fun _ -> CoreResponse.InfoRes "Couldn't find file content") + |> AsyncResult.bimap id (fun _ -> CoreResponse.InfoRes "Couldn't find file content") let calculateNamespaceInsert currentAst @@ -763,7 +763,7 @@ module Commands = let symbolUseWorkspace (getDeclarationLocation: FSharpSymbolUse * NamedText -> Async) (findReferencesForSymbolInFile: (string * FSharpProjectOptions * FSharpSymbol) -> Async) - (tryGetFileSource: string -> ResultOrString) + (tryGetFileSource: string -> Async>) (tryGetProjectOptionsForFsproj: string -> Async) (getAllProjectOptions: unit -> Async) (includeDeclarations: bool) @@ -827,22 +827,21 @@ module Commands = | Some(SymbolDeclarationLocation.Projects(projects (*isLocalForProject=*) , false)) -> let output = ResizeArray<_>() - for project in projects do - output.Add(project) + let! resolvedProjects = + [ for project in projects do + yield Async.singleton (Some project) - for r in project.ReferencedProjects do - match! tryGetProjectOptionsForFsproj (UMX.tag r.OutputFile) with - | Some v -> output.Add(v) - | None -> () + yield! + project.ReferencedProjects + |> Array.map (fun p -> UMX.tag p.OutputFile |> tryGetProjectOptionsForFsproj) ] + |> Async.parallel75 - return output |> Seq.toList |> List.distinctBy (fun x -> x.ProjectFileName) - // [ for project in projects do - // yield project - // yield! - // project.ReferencedProjects - // |> Array.choose (fun p -> UMX.tag p.OutputFile |> tryGetProjectOptionsForFsproj) ] - // |> List.distinctBy (fun x -> x.ProjectFileName) + return + resolvedProjects + |> Array.choose id + |> Array.toList + |> List.distinctBy (fun x -> x.ProjectFileName) | _ (*None*) -> // symbol is declared external -> look through all F# projects // (each script (including untitled) has its own project -> scripts get checked too. But only once they are loaded (-> inside `state`)) @@ -851,13 +850,16 @@ module Commands = } let tryAdjustRanges (file: string, ranges: Range[]) = - match tryGetFileSource file with - | Error _ when errorOnFailureToFixRange -> Error $"Cannot get source of '{file}'" - | Error _ -> Ok ranges - | Ok text -> - tryAdjustRanges (text, ranges) - // Note: `Error` only possible when `errorOnFailureToFixRange` - |> Result.mapError (fun _ -> $"Cannot adjust ranges in file '{file}'") + async { + match! tryGetFileSource file with + | Error _ when errorOnFailureToFixRange -> return Error $"Cannot get source of '{file}'" + | Error _ -> return Ok ranges + | Ok text -> + return + tryAdjustRanges (text, ranges) + // Note: `Error` only possible when `errorOnFailureToFixRange` + |> Result.mapError (fun _ -> $"Cannot adjust ranges in file '{file}'") + } let isDeclLocation = if includeDeclarations then @@ -892,7 +894,7 @@ module Commands = if references |> Array.isEmpty then return Ok() else - let ranges = tryAdjustRanges (file, references) + let! ranges = tryAdjustRanges (file, references) match ranges with | Error msg when errorOnFailureToFixRange -> return Error msg @@ -930,10 +932,11 @@ module Commands = | Error err -> return Some err } ] - |> Async.Choice - |> Async.map (function - | None -> Ok() - | Some err -> Error err) + |> Async.parallel75 + |> Async.Ignore<_> + // |> Async.map (function + // | None -> Ok() + // | Some err -> Error err) let! projects = projectsToCheck do! iterProjects projects @@ -2063,7 +2066,8 @@ type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers: return usages |> Seq.map (fun u -> u.Range) } - let tryGetFileSource symbolFile = state.TryGetFileSource symbolFile + let tryGetFileSource symbolFile = + state.TryGetFileSource symbolFile |> Async.singleton let tryGetProjectOptionsForFsproj (fsprojPath: string) = state.ProjectController.GetProjectOptionsForFsproj(UMX.untag fsprojPath) @@ -2336,7 +2340,7 @@ type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers: member x.FormatDocument(file: string) : Async> = let tryGetFileCheckerOptionsWithLines file = - x.TryGetFileCheckerOptionsWithLines file |> Result.map snd + x.TryGetFileCheckerOptionsWithLines file |> Result.map snd |> Async.singleton let formatDocumentAsync x = fantomasService.FormatDocumentAsync x Commands.formatDocument tryGetFileCheckerOptionsWithLines formatDocumentAsync file @@ -2347,7 +2351,7 @@ type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers: rangeToFormat: FormatSelectionRange ) : Async> = let tryGetFileCheckerOptionsWithLines file = - x.TryGetFileCheckerOptionsWithLines file |> Result.map snd + x.TryGetFileCheckerOptionsWithLines file |> Result.map snd |> Async.singleton let formatSelectionAsync x = fantomasService.FormatSelectionAsync x Commands.formatSelection tryGetFileCheckerOptionsWithLines formatSelectionAsync file rangeToFormat @@ -2398,7 +2402,7 @@ type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers: static member InlineValues(contents: NamedText, tyRes: ParseAndCheckResults) = Commands.inlineValues contents tyRes member __.PipelineHints(tyRes: ParseAndCheckResults) = - Commands.pipelineHints state.TryGetFileSource tyRes + Commands.pipelineHints (state.TryGetFileSource >> Async.singleton) tyRes interface IDisposable with member x.Dispose() = diff --git a/src/FsAutoComplete.Core/CompilerServiceInterface.fs b/src/FsAutoComplete.Core/CompilerServiceInterface.fs index 0a918b424..b0446ba3a 100644 --- a/src/FsAutoComplete.Core/CompilerServiceInterface.fs +++ b/src/FsAutoComplete.Core/CompilerServiceInterface.fs @@ -165,6 +165,8 @@ type FSharpCompilerServiceChecker(hasAnalyzers, typecheckCacheSize) = let allFlags = Array.append [| "--targetprofile:mscorlib" |] fsiAdditionalArguments + do! Async.SwitchToNewThread() + let! (opts, errors) = checker.GetProjectOptionsFromScript( UMX.untag file, @@ -190,6 +192,8 @@ type FSharpCompilerServiceChecker(hasAnalyzers, typecheckCacheSize) = let allFlags = Array.append [| "--targetprofile:netstandard" |] fsiAdditionalArguments + do! Async.SwitchToNewThread() + let! (opts, errors) = checker.GetProjectOptionsFromScript( UMX.untag file, @@ -257,6 +261,7 @@ type FSharpCompilerServiceChecker(hasAnalyzers, typecheckCacheSize) = checkerLogger.info (Log.setMessage "ParseFile - {file}" >> Log.addContextDestructured "file" fn) let path = UMX.untag fn + do! Async.SwitchToNewThread() return! checker.ParseFile(path, source, fpo) } @@ -286,6 +291,7 @@ type FSharpCompilerServiceChecker(hasAnalyzers, typecheckCacheSize) = let path = UMX.untag filePath try + do! Async.SwitchToNewThread() let! (p, c) = checker.ParseAndCheckFileInProject(path, version, source, options, userOpName = opName) let parseErrors = p.Diagnostics |> Array.map (fun p -> p.Message) @@ -380,6 +386,8 @@ type FSharpCompilerServiceChecker(hasAnalyzers, typecheckCacheSize) = >> Log.addContextDestructured "file" file ) + do! Async.SwitchToNewThread() + match FSharpCompilerServiceChecker.GetDependingProjects file options with | None -> return [||] | Some(opts, []) -> @@ -391,28 +399,35 @@ type FSharpCompilerServiceChecker(hasAnalyzers, typecheckCacheSize) = opts :: dependentProjects |> List.map (fun (opts) -> async { + do! Async.SwitchToNewThread() let opts = clearProjectReferences opts let! res = checker.ParseAndCheckProject opts return res.GetUsesOfSymbol symbol }) - |> Async.Parallel + |> Async.parallel75 return res |> Array.concat } member _.FindReferencesForSymbolInFile(file, project, symbol) = - checkerLogger.info ( - Log.setMessage "FindReferencesForSymbolInFile - {file}" - >> Log.addContextDestructured "file" file - ) + async { + checkerLogger.info ( + Log.setMessage "FindReferencesForSymbolInFile - {file}" + >> Log.addContextDestructured "file" file + ) - checker.FindBackgroundReferencesInFile( - file, - project, - symbol, - canInvalidateProject = false, - userOpName = "find references" - ) + do! Async.SwitchToNewThread() + + return! + checker.FindBackgroundReferencesInFile( + file, + project, + symbol, + canInvalidateProject = false, + fastCheck = true, + userOpName = "find references" + ) + } member __.GetDeclarations(fileName: string, source, options, version) = async { @@ -421,6 +436,7 @@ type FSharpCompilerServiceChecker(hasAnalyzers, typecheckCacheSize) = >> Log.addContextDestructured "file" fileName ) + do! Async.SwitchToNewThread() let! parseResult = checker.ParseFile(UMX.untag fileName, source, options) return parseResult.GetNavigationItems().Declarations } diff --git a/src/FsAutoComplete.Core/RecordStubGenerator.fs b/src/FsAutoComplete.Core/RecordStubGenerator.fs index ee88d0435..258204472 100644 --- a/src/FsAutoComplete.Core/RecordStubGenerator.fs +++ b/src/FsAutoComplete.Core/RecordStubGenerator.fs @@ -7,6 +7,7 @@ open FSharp.Compiler.Text open System.Diagnostics open FsAutoComplete.CodeGenerationUtils open FSharp.Compiler.Symbols +open FsToolkit.ErrorHandling // Algorithm // [x] Make sure '}' is the last token of the expression @@ -220,7 +221,7 @@ let tryFindRecordExprInBufferAtPos (codeGenService: ICodeGenerationService) (pos let checkThatRecordExprEndsWithRBrace (codeGenService: ICodeGenerationService) (document: Document) (expr: RecordExpr) = - maybe { + asyncOption { let! rangeWhereToLookForEnclosingRBrace = match expr.FieldExprList with | [] -> Some expr.Expr.Range @@ -247,13 +248,15 @@ let checkThatRecordExprEndsWithRBrace (codeGenService: ICodeGenerationService) ( tryFindTokenLPosInRange codeGenService rangeWhereToLookForEnclosingRBrace document (fun tokenInfo -> tokenInfo.TokenName = "RBRACE") } - |> Option.isSome + |> Async.map Option.isSome let tryFindStubInsertionParamsAtPos (codeGenService: ICodeGenerationService) (pos: Position) (document: Document) = - asyncMaybe { + asyncOption { let! recordExpression = tryFindRecordExprInBufferAtPos codeGenService pos document - if checkThatRecordExprEndsWithRBrace codeGenService document recordExpression then + let! endsWithBrace = checkThatRecordExprEndsWithRBrace codeGenService document recordExpression + + if endsWithBrace then let! insertionPos = RecordStubsInsertionParams.TryCreateFromRecordExpression recordExpression return recordExpression, insertionPos else diff --git a/src/FsAutoComplete.Core/UnionPatternMatchCaseGenerator.fs b/src/FsAutoComplete.Core/UnionPatternMatchCaseGenerator.fs index 33611cc07..4680c7df2 100644 --- a/src/FsAutoComplete.Core/UnionPatternMatchCaseGenerator.fs +++ b/src/FsAutoComplete.Core/UnionPatternMatchCaseGenerator.fs @@ -385,90 +385,93 @@ let tryFindBarTokenLPosInRange (codeGenService: ICodeGenerationService) (range: tryFindTokenLPosInRange codeGenService range document (fun tokenInfo -> tokenInfo.TokenName = "BAR") let tryFindInsertionParams (codeGenService: ICodeGenerationService) document (patMatchExpr: PatternMatchExpr) = - match List.rev patMatchExpr.Clauses with - | [] -> - // Not possible normally - None - - | last :: _ -> - // Interesting cases: - // - // (1) - // match x with - // | Case1 -> () | Case2 -> () - // - // - // - // (2) - // match x with - // Case1 -> () | Case2 -> () - // - // - // (3) - // match x with - // | Case1 -> () - // | Case2 -> () - // - // - // (4) - // match x with - // | Case1 -> () - // | - // Case2 -> () - // - // - // (5) - // match x with | Case1 -> () | Case2 -> () - // - - // To know the indentation column, - // We want to find the first clause of the clauses that are on the same last line - // All clause f(i) start at line l(i) - // We want to 'f(k)' such that k = min { i >= k such that l(i) = l(k) } - // And l(k) = max { l(i) } - - // TODO: report this bug: - // when renaming it like this: ``list of (clause, line index)`` - // FSI interactive bugs: - // error FS0192: internal error: binding null type in envBindTypeRef: list of (clause, line index) - let clauseAndLineIdxList = - [ for clause in patMatchExpr.Clauses do - yield clause, clause.Range.StartLine ] - - // Get first of the clauses that are on the same last line - let lastLineIdx = - clauseAndLineIdxList |> List.map (fun (_, lineIdx) -> lineIdx) |> Seq.last - - let firstClauseOnLastLine = - clauseAndLineIdxList - |> List.find (fun (_, lineIdx) -> lineIdx = lastLineIdx) - |> fst - - // Find if this clause has a pipe before it on the same line as itself - let possibleBarLocationRange = - // Special case (5): - // 'match-with'/'function' is on the same line as the first clause - // on the last line - if patMatchExpr.MatchWithOrFunctionRange.EndLine = firstClauseOnLastLine.Range.StartLine then - Range.unionRanges patMatchExpr.MatchWithOrFunctionRange.EndRange firstClauseOnLastLine.Range.StartRange - else - let clause = firstClauseOnLastLine - let start = Position.mkPos clause.Range.StartLine 0 - Range.mkRange clause.Range.FileName start clause.Range.Start - - let barTokenOpt = - tryFindBarTokenLPosInRange codeGenService possibleBarLocationRange document - - match barTokenOpt with - | Some(_, barTokenLPos) -> - { IndentColumn = barTokenLPos.Column - InsertionPos = last.Range.End } - |> Some - - | None -> - { IndentColumn = firstClauseOnLastLine.Range.StartColumn - InsertionPos = last.Range.End } - |> Some + async { + match List.rev patMatchExpr.Clauses with + | [] -> + // Not possible normally + return None + + | last :: _ -> + // Interesting cases: + // + // (1) + // match x with + // | Case1 -> () | Case2 -> () + // + // + // + // (2) + // match x with + // Case1 -> () | Case2 -> () + // + // + // (3) + // match x with + // | Case1 -> () + // | Case2 -> () + // + // + // (4) + // match x with + // | Case1 -> () + // | + // Case2 -> () + // + // + // (5) + // match x with | Case1 -> () | Case2 -> () + // + + // To know the indentation column, + // We want to find the first clause of the clauses that are on the same last line + // All clause f(i) start at line l(i) + // We want to 'f(k)' such that k = min { i >= k such that l(i) = l(k) } + // And l(k) = max { l(i) } + + // TODO: report this bug: + // when renaming it like this: ``list of (clause, line index)`` + // FSI interactive bugs: + // error FS0192: internal error: binding null type in envBindTypeRef: list of (clause, line index) + let clauseAndLineIdxList = + [ for clause in patMatchExpr.Clauses do + yield clause, clause.Range.StartLine ] + + // Get first of the clauses that are on the same last line + let lastLineIdx = + clauseAndLineIdxList |> List.map (fun (_, lineIdx) -> lineIdx) |> Seq.last + + let firstClauseOnLastLine = + clauseAndLineIdxList + |> List.find (fun (_, lineIdx) -> lineIdx = lastLineIdx) + |> fst + + // Find if this clause has a pipe before it on the same line as itself + let possibleBarLocationRange = + // Special case (5): + // 'match-with'/'function' is on the same line as the first clause + // on the last line + if patMatchExpr.MatchWithOrFunctionRange.EndLine = firstClauseOnLastLine.Range.StartLine then + Range.unionRanges patMatchExpr.MatchWithOrFunctionRange.EndRange firstClauseOnLastLine.Range.StartRange + else + let clause = firstClauseOnLastLine + let start = Position.mkPos clause.Range.StartLine 0 + Range.mkRange clause.Range.FileName start clause.Range.Start + + let! barTokenOpt = tryFindBarTokenLPosInRange codeGenService possibleBarLocationRange document + + match barTokenOpt with + | Some(_, barTokenLPos) -> + return + { IndentColumn = barTokenLPos.Column + InsertionPos = last.Range.End } + |> Some + + | None -> + return + { IndentColumn = firstClauseOnLastLine.Range.StartColumn + InsertionPos = last.Range.End } + |> Some + } let checkThatPatternMatchExprEndsWithCompleteClause (expr: PatternMatchExpr) = diff --git a/src/FsAutoComplete.Core/Utils.fs b/src/FsAutoComplete.Core/Utils.fs index b333a9b51..1e977066d 100644 --- a/src/FsAutoComplete.Core/Utils.fs +++ b/src/FsAutoComplete.Core/Utils.fs @@ -222,6 +222,15 @@ module Async = // Start the workflow using a provided cancellation token Async.StartWithContinuations(work, cont, econt, ccont, cancellationToken = cancellationToken)) + /// Creates an asynchronous computation that executes all the given asynchronous computations, using 75% of the Environment.ProcessorCount + /// A sequence of distinct computations to be parallelized. + let parallel75 computations = + let maxConcurrency = + Math.Max(1.0, Math.Floor((float System.Environment.ProcessorCount) * 0.75)) + + Async.Parallel(computations, int maxConcurrency) + // Async.Parallel(computations) + [] module Array = /// Async implementation of Array.map. diff --git a/src/FsAutoComplete/CodeFixes.fs b/src/FsAutoComplete/CodeFixes.fs index ef0c3f741..05026d058 100644 --- a/src/FsAutoComplete/CodeFixes.fs +++ b/src/FsAutoComplete/CodeFixes.fs @@ -17,11 +17,12 @@ type FcsPos = FSharp.Compiler.Text.Position module LspTypes = Ionide.LanguageServerProtocol.Types module Types = + type IsEnabled = unit -> bool - type GetRangeText = string -> LspTypes.Range -> ResultOrString - type GetFileLines = string -> ResultOrString - type GetLineText = NamedText -> LspTypes.Range -> Result + type GetRangeText = string -> LspTypes.Range -> Async> + type GetFileLines = string -> Async> + type GetLineText = NamedText -> LspTypes.Range -> Async> type GetParseResultsForFile = string @@ -49,10 +50,20 @@ module Types = type CodeAction with static member OfFix getFileVersion clientCapabilities (fix: Fix) = - let filePath = fix.File.GetFilePath() |> Utils.normalizePath - let fileVersion = getFileVersion filePath - - CodeAction.OfDiagnostic fix.File fileVersion fix.Title fix.SourceDiagnostic fix.Edits fix.Kind clientCapabilities + async { + let filePath = fix.File.GetFilePath() |> Utils.normalizePath + let! fileVersion = getFileVersion filePath + + return + CodeAction.OfDiagnostic + fix.File + fileVersion + fix.Title + fix.SourceDiagnostic + fix.Edits + fix.Kind + clientCapabilities + } static member OfDiagnostic (fileUri) diff --git a/src/FsAutoComplete/CodeFixes/ChangeDowncastToUpcast.fs b/src/FsAutoComplete/CodeFixes/ChangeDowncastToUpcast.fs index d5ae2e7b9..88442fec5 100644 --- a/src/FsAutoComplete/CodeFixes/ChangeDowncastToUpcast.fs +++ b/src/FsAutoComplete/CodeFixes/ChangeDowncastToUpcast.fs @@ -12,31 +12,35 @@ let titleUpcastFunction = "Use 'upcast' function" /// a codefix that replaces unsafe casts with safe casts let fix (getRangeText: GetRangeText) : CodeFix = Run.ifDiagnosticByCode (Set.ofList [ "3198" ]) (fun diagnostic codeActionParams -> - match getRangeText (codeActionParams.TextDocument.GetFilePath() |> Utils.normalizePath) diagnostic.Range with - | Ok expressionText -> - let isDowncastOperator = expressionText.Contains(":?>") - let isDowncastKeyword = expressionText.Contains("downcast") + async { + match! getRangeText (codeActionParams.TextDocument.GetFilePath() |> Utils.normalizePath) diagnostic.Range with + | Ok expressionText -> + let isDowncastOperator = expressionText.Contains(":?>") + let isDowncastKeyword = expressionText.Contains("downcast") - match isDowncastOperator, isDowncastKeyword with - // must be either/or here, cannot be both - | true, true -> AsyncResult.retn [] - | false, false -> AsyncResult.retn [] - | true, false -> - AsyncResult.retn - [ { File = codeActionParams.TextDocument - SourceDiagnostic = Some diagnostic - Title = titleUpcastOperator - Edits = - [| { Range = diagnostic.Range - NewText = expressionText.Replace(":?>", ":>") } |] - Kind = FixKind.Refactor } ] - | false, true -> - AsyncResult.retn - [ { File = codeActionParams.TextDocument - SourceDiagnostic = Some diagnostic - Title = titleUpcastFunction - Edits = - [| { Range = diagnostic.Range - NewText = expressionText.Replace("downcast", "upcast") } |] - Kind = FixKind.Refactor } ] - | Error _ -> AsyncResult.retn []) + match isDowncastOperator, isDowncastKeyword with + // must be either/or here, cannot be both + | true, true -> return! AsyncResult.retn [] + | false, false -> return! AsyncResult.retn [] + | true, false -> + return! + AsyncResult.retn + [ { File = codeActionParams.TextDocument + SourceDiagnostic = Some diagnostic + Title = titleUpcastOperator + Edits = + [| { Range = diagnostic.Range + NewText = expressionText.Replace(":?>", ":>") } |] + Kind = FixKind.Refactor } ] + | false, true -> + return! + AsyncResult.retn + [ { File = codeActionParams.TextDocument + SourceDiagnostic = Some diagnostic + Title = titleUpcastFunction + Edits = + [| { Range = diagnostic.Range + NewText = expressionText.Replace("downcast", "upcast") } |] + Kind = FixKind.Refactor } ] + | Error _ -> return! AsyncResult.retn [] + }) diff --git a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs index a60a763a7..f5e549f89 100644 --- a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs +++ b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs @@ -877,102 +877,98 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let isFileOpen file = openFiles |> AMap.tryFindA file |> AVal.map (Option.isSome) - let findFileInOpenFiles' file = + let findFileInOpenFiles file = openFilesWithChanges |> AMap.tryFindA file - let findFileInOpenFiles file = findFileInOpenFiles' file - let forceFindOpenFile filePath = findFileInOpenFiles filePath |> AVal.force let forceFindOpenFileOrRead file = - findFileInOpenFiles file - |> AVal.force - |> Option.orElseWith (fun () -> - // TODO: Log how many times this kind area gets hit and possibly if this should be rethought - try - logger.info ( - Log.setMessage "forceFindOpenFileOrRead else - {file}" - >> Log.addContextDestructured "file" file - ) + asyncOption { - let untagged = UMX.untag file + match findFileInOpenFiles file |> AVal.force with + | Some s -> return s + | None -> + // TODO: Log how many times this kind area gets hit and possibly if this should be rethought + try + logger.debug ( + Log.setMessage "forceFindOpenFileOrRead else - {file}" + >> Log.addContextDestructured "file" file + ) - if File.Exists untagged && isFileWithFSharp untagged then - let change = File.ReadAllText untagged + let untagged = UMX.untag file - let lastWriteTime = File.GetLastWriteTimeUtc untagged + if File.Exists untagged && isFileWithFSharp untagged then + let! change = File.ReadAllTextAsync untagged |> Async.AwaitTask + let lastWriteTime = File.GetLastWriteTimeUtc untagged - let file = - { Touched = lastWriteTime - Lines = NamedText(file, change) - Version = None } - - Some file - else - None - with e -> - logger.warn ( - Log.setMessage "Could not read file {file}" - >> Log.addContextDestructured "file" file - >> Log.addExn e - ) - - None) - |> Result.ofOption (fun () -> $"Could not read file: {file}") + let file = + { Touched = lastWriteTime + Lines = NamedText(file, change) + Version = None } + return file + else + return! None + with e -> + logger.warn ( + Log.setMessage "Could not read file {file}" + >> Log.addContextDestructured "file" file + >> Log.addExn e + ) + return! None + } + |> Async.map (Result.ofOption (fun () -> $"Could not read file: {file}")) do + let fileshimChanges = openFilesWithChanges |> AMap.mapA (fun _ v -> v) + let filesystemShim file = // GetLastWriteTimeShim gets called _alot_ and when we do checks on save we use Async.Parallel for type checking. // Adaptive uses lots of locks under the covers, so many threads can get blocked waiting for data. - // We know we don't store anything other than Fsharp type files in open files so this so we shouldn't hit any locks - // when F# compiler asks for DLL timestamps - if Utils.isFileWithFSharp %file then - forceFindOpenFile file - else - None + // flattening openFilesWithChanges makes this check a lot quicker as it's not needing to recalculate each value. + fileshimChanges |> AMap.force |> HashMap.tryFind file FSharp.Compiler.IO.FileSystemAutoOpens.FileSystem <- FileSystem(FSharp.Compiler.IO.FileSystemAutoOpens.FileSystem, filesystemShim) /// Parses all files in the workspace. This is mostly used to trigger finding tests. let parseAllFiles () = - aval { + asyncAVal { let! projects = loadedProjectOptions and! checker = checker return - projects - |> Array.ofList - |> Array.Parallel.collect (fun p -> - let parseOpts = Utils.projectOptionsToParseOptions p - p.SourceFiles |> Array.map (fun s -> p, parseOpts, s)) - |> Array.Parallel.choose (fun (opts, parseOpts, fileName) -> - let fileName = UMX.tag fileName - let file = forceFindOpenFileOrRead fileName - - file - |> Result.toOption - |> Option.map (fun file -> - async { + fun (ct: CancellationToken) -> + projects + |> Array.ofList + |> Array.Parallel.collect (fun p -> + let parseOpts = Utils.projectOptionsToParseOptions p + p.SourceFiles |> Array.map (fun s -> p, parseOpts, s)) + |> Array.Parallel.map (fun (opts, parseOpts, fileName) -> + let fileName = UMX.tag fileName + + asyncResult { + let! file = forceFindOpenFileOrRead fileName let! parseResult = checker.ParseFile(fileName, file.Lines, parseOpts) let! ct = Async.CancellationToken fileParsed.Trigger(parseResult, opts, ct) return parseResult - })) - |> Async.parallel75 - + } + |> Async.map Result.toOption) + |> Async.parallel75 + |> Async.map (Array.Parallel.choose id) + |> Async.startImmediateAsTask ct } let forceFindSourceText filePath = - forceFindOpenFileOrRead filePath |> Result.map (fun f -> f.Lines) + forceFindOpenFileOrRead filePath |> AsyncResult.map (fun f -> f.Lines) let openFilesToChangesAndProjectOptions = openFilesWithChanges - |> AMapAsync.mapAsyncAVal (fun filePath file ctok -> + |> AMapAsync.mapAVal (fun filePath file ctok -> asyncAVal { if Utils.isAScript (UMX.untag filePath) then let! checker = checker @@ -1018,14 +1014,14 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> ASet.force |> HashSet.toArray |> Array.map (AsyncAVal.forceAsync) - |> Async.Parallel + |> Async.parallel75 return set |> Array.collect (List.toArray) } let getProjectOptionsForFile (filePath: string) = asyncAVal { - match! allProjectOptions |> AMapAsync.tryFindAsyncAval filePath with + match! allProjectOptions |> AMapAsync.tryFindA filePath with | Some projs -> return projs | None -> return [] } @@ -1101,7 +1097,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar checkSimplifiedNames ] async { - do! analyzers |> Async.Parallel |> Async.Ignore + do! analyzers |> Async.parallel75 |> Async.Ignore do! lspClient.NotifyDocumentAnalyzed @@ -1204,9 +1200,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let checker = checker |> AVal.force let config = config |> AVal.force - match forceFindOpenFileOrRead filePath with + match! forceFindOpenFileOrRead filePath with // Don't cache for autocompletions as we really only want to cache "Opened" files. - | Ok(fileInfo) -> return! parseAndCheckFile checker fileInfo opts config false |> Async.Ignore + | Ok(fileInfo) -> + do! Async.SwitchToNewThread() + return! parseAndCheckFile checker fileInfo opts config false |> Async.Ignore | _ -> () with e -> @@ -1219,12 +1217,12 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let openFilesToParsedResults = openFilesToChangesAndProjectOptions - |> AMapAsync.mapAsyncAVal2 (fun _ (info, projectOptions) ctok -> + |> AMapAsync.mapAsyncAVal (fun _ (info, projectOptions) ctok -> asyncAVal { let file = info.Lines.FileName let! checker = checker - return + return! taskOption { let! opts = selectProject projectOptions and! cts = tryGetOpenFileToken file @@ -1245,7 +1243,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let openFilesToRecentCheckedFilesResults = openFilesToChangesAndProjectOptions - |> AMapAsync.mapAsyncAVal2 (fun _ (info, projectOptions) _ -> + |> AMapAsync.mapAsyncAVal (fun _ (info, projectOptions) _ -> asyncAVal { let file = info.Lines.FileName let! checker = checker @@ -1259,13 +1257,13 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let openFilesToCheckedFilesResults = openFilesToChangesAndProjectOptions - |> AMapAsync.mapAsyncAVal2 (fun _ (info, projectOptions) ctok -> + |> AMapAsync.mapAsyncAVal (fun _ (info, projectOptions) ctok -> asyncAVal { let file = info.Lines.FileName let! checker = checker and! config = config - return + return! taskOption { let! opts = selectProject projectOptions and! cts = tryGetOpenFileToken file @@ -1279,14 +1277,13 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar }) let getParseResults filePath = - openFilesToParsedResults |> AMapAsync.tryFindAsyncAndFlatten filePath + openFilesToParsedResults |> AMapAsync.tryFindAndFlatten filePath let getTypeCheckResults filePath = - openFilesToCheckedFilesResults |> AMapAsync.tryFindAsyncAndFlatten (filePath) + openFilesToCheckedFilesResults |> AMapAsync.tryFindAndFlatten (filePath) let getRecentTypeCheckResults filePath = - openFilesToRecentCheckedFilesResults - |> AMapAsync.tryFindAsyncAndFlatten (filePath) + openFilesToRecentCheckedFilesResults |> AMapAsync.tryFindAndFlatten (filePath) let tryGetLineStr pos (text: NamedText) = text.GetLine(pos) @@ -1357,14 +1354,14 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! decls = AsyncAVal.forceAsync v return Option.map (fun v -> k, v) decls }) - |> Async.Parallel + |> Async.parallel75 return results |> Array.Parallel.choose id } let getDeclarations filename = - openFilesToCheckedDeclarations |> AMapAsync.tryFindAsyncAndFlatten filename + openFilesToCheckedDeclarations |> AMapAsync.tryFindAndFlatten filename let getFilePathAndPosition (p: ITextDocumentPositionParams) = let filePath = p.GetFilePath() |> Utils.normalizePath @@ -1387,8 +1384,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let codeGenServer = { new ICodeGenerationService with member x.TokenizeLine(file, i) = - option { - let! (text) = forceFindOpenFileOrRead file |> Option.ofResult + asyncOption { + let! (text) = forceFindOpenFileOrRead file |> Async.map Option.ofResult try let! line = text.Lines.GetLine(Position.mkPos i 0) @@ -1398,9 +1395,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar } member x.GetSymbolAtPosition(file, pos) = - option { + asyncOption { try - let! (text) = forceFindOpenFileOrRead file |> Option.ofResult + let! (text) = forceFindOpenFileOrRead file |> Async.map Option.ofResult let! line = tryGetLineStr pos text.Lines |> Option.ofResult return! Lexer.getSymbol pos.Line pos.Column line SymbolLookupKind.Fuzzy [||] with _ -> @@ -1412,7 +1409,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! symbol = x.GetSymbolAtPosition(fileName, pos) if symbol.Kind = kind then - let! (text) = forceFindOpenFileOrRead fileName |> Option.ofResult + let! (text) = forceFindOpenFileOrRead fileName |> Async.map Option.ofResult let! line = tryGetLineStr pos text.Lines |> Option.ofResult let! tyRes = forceGetTypeCheckResults fileName |> Async.map (Option.ofResult) let symbolUse = tyRes.TryGetSymbolUse pos line @@ -1542,8 +1539,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar } let getRangeText fileName (range: Ionide.LanguageServerProtocol.Types.Range) = - getFileLines fileName - |> Result.bind (fun lines -> lines.GetText(protocolRangeToRange (UMX.untag fileName) range)) + asyncResult { + let! lines = getFileLines fileName + return! lines.GetText(protocolRangeToRange (UMX.untag fileName) range) + } let tryFindUnionDefinitionFromPos = tryFindUnionDefinitionFromPos codeGenServer @@ -1570,6 +1569,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let getLineText (lines: NamedText) (range: Ionide.LanguageServerProtocol.Types.Range) = lines.GetText(protocolRangeToRange (UMX.untag lines.FileName) range) + |> Async.singleton let abstractClassStubReplacements config () = Map.ofList @@ -2160,7 +2160,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar try logger.info (Log.setMessage "Initialized request {p}" >> Log.addContextDestructured "p" p) - let! _ = parseAllFiles () |> AVal.force + let! _ = parseAllFiles () |> AsyncAVal.forceAsync return () with e -> @@ -2334,7 +2334,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let (filePath, pos) = getFilePathAndPosition p - let! (namedText2) = forceFindOpenFileOrRead filePath |> Result.ofStringErr + let! (namedText2) = forceFindOpenFileOrRead filePath |> AsyncResult.ofStringErr let! lineStr2 = namedText2.Lines |> tryGetLineStr pos |> Result.ofStringErr @@ -2568,7 +2568,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let (filePath, pos) = getFilePathAndPosition p - let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr + let! (namedText) = forceFindOpenFileOrRead filePath |> AsyncResult.ofStringErr and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr @@ -2632,7 +2632,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let (filePath, pos) = getFilePathAndPosition p - let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr + let! (namedText) = forceFindOpenFileOrRead filePath |> AsyncResult.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr and! tyRes = forceGetTypeCheckResultsStale filePath |> AsyncResult.ofStringErr @@ -2725,7 +2725,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let (filePath, pos) = getFilePathAndPosition p - let! namedText = forceFindOpenFileOrRead filePath |> Result.ofStringErr + let! namedText = forceFindOpenFileOrRead filePath |> AsyncResult.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr let! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr @@ -2748,7 +2748,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let (filePath, pos) = getFilePathAndPosition p - let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr + let! (namedText) = forceFindOpenFileOrRead filePath |> AsyncResult.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr @@ -2766,27 +2766,31 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar symbolUseWorkspace true true true pos lineStr namedText.Lines tyRes |> AsyncResult.mapError (fun msg -> JsonRpc.Error.Create(JsonRpc.ErrorCodes.invalidParams, msg)) - let documentChanges = + let! documentChanges = ranges |> Seq.map (fun kvp -> - let edits = - kvp.Value - |> Array.map (fun range -> - let range = fcsRangeToLsp range - { Range = range; NewText = newName }) + async { + let edits = + kvp.Value + |> Array.map (fun range -> + let range = fcsRangeToLsp range + { Range = range; NewText = newName }) - let file: string = kvp.Key + let file: string = kvp.Key - let version = - forceFindOpenFileOrRead file - |> Option.ofResult - |> Option.bind (fun (f) -> f.Version) + let! version = + async { + let! file = forceFindOpenFileOrRead file + return file |> Option.ofResult |> Option.bind (fun (f) -> f.Version) + } - { TextDocument = - { Uri = Path.FilePathToUri(UMX.untag file) - Version = version } - Edits = edits }) - |> Array.ofSeq + return + { TextDocument = + { Uri = Path.FilePathToUri(UMX.untag file) + Version = version } + Edits = edits } + }) + |> Async.parallel75 let clientCapabilities = clientCapabilities |> AVal.force |> Option.get return WorkspaceEdit.Create(documentChanges, clientCapabilities) |> Some @@ -2815,7 +2819,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let (filePath, pos) = getFilePathAndPosition p - let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr + let! (namedText) = forceFindOpenFileOrRead filePath |> AsyncResult.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr @@ -2846,7 +2850,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let (filePath, pos) = getFilePathAndPosition p - let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr + let! (namedText) = forceFindOpenFileOrRead filePath |> AsyncResult.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr let! decl = tyRes.TryFindTypeDeclaration pos lineStr |> AsyncResult.ofStringErr @@ -2875,7 +2879,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let (filePath, pos) = getFilePathAndPosition p - let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr + let! (namedText) = forceFindOpenFileOrRead filePath |> AsyncResult.ofStringErr let! lineStr = tryGetLineStr pos namedText.Lines |> Result.ofStringErr and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr @@ -2911,7 +2915,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let (filePath, pos) = getFilePathAndPosition p - let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr + let! (namedText) = forceFindOpenFileOrRead filePath |> AsyncResult.ofStringErr let! lineStr = tryGetLineStr pos namedText.Lines |> Result.ofStringErr and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr @@ -2953,7 +2957,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let (filePath, pos) = getFilePathAndPosition p - let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr + let! (namedText) = forceFindOpenFileOrRead filePath |> AsyncResult.ofStringErr let! lineStr = tryGetLineStr pos namedText.Lines |> Result.ofStringErr and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr @@ -2979,7 +2983,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! proj = AsyncAVal.forceAsync v return Option.map (fun proj -> UMX.untag k, proj) (selectProject proj) }) - |> Async.Parallel + |> Async.parallel75 |> Async.map (Array.choose id >> List.ofArray) let! res = @@ -3204,7 +3208,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar with e -> return Ok [] }) - |> Async.Parallel + |> Async.parallel75 let! fixes = fixes let (actions: Fix list[], errors: string[]) = Array.partitionResults fixes @@ -3221,20 +3225,22 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let tryGetFileVersion filePath = - forceFindOpenFileOrRead filePath - |> Option.ofResult - |> Option.bind (fun (f) -> f.Version) + async { + let! foo = forceFindOpenFileOrRead filePath + return foo |> Option.ofResult |> Option.bind (fun (f) -> f.Version) + } let clientCapabilities = clientCapabilities |> AVal.force match actions with | [] -> return None | actions -> - return + let! fixes = actions - |> List.map (CodeAction.OfFix tryGetFileVersion clientCapabilities.Value >> U2.Second) - |> List.toArray - |> Some + |> List.map (CodeAction.OfFix tryGetFileVersion clientCapabilities.Value) + |> Async.parallel75 + + return Some(fixes |> Array.map U2.Second) with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -3313,7 +3319,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar >> Log.addContextDestructured "file" filePath ) - let! (namedText: NamedText) = forceFindSourceText filePath |> Result.ofStringErr + let! (namedText: NamedText) = forceFindSourceText filePath |> AsyncResult.ofStringErr let! lineStr = namedText |> tryGetLineStr pos |> Result.ofStringErr let typ = data.[1] @@ -3636,7 +3642,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let filePath = p.TextDocument.GetFilePath() |> Utils.normalizePath - let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr + let! (namedText) = forceFindOpenFileOrRead filePath |> AsyncResult.ofStringErr and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr @@ -3741,7 +3747,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let filePath = p.TextDocument.GetFilePath() |> Utils.normalizePath - let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr + let! (namedText) = forceFindOpenFileOrRead filePath |> AsyncResult.ofStringErr let! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr @@ -3945,7 +3951,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let (filePath, pos) = getFilePathAndPosition p - let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr + let! (namedText) = forceFindOpenFileOrRead filePath |> AsyncResult.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr @@ -3981,7 +3987,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar FSharp.Compiler.Text.Position.mkPos (p.Position.Line) (p.Position.Character + 2) let filePath = p.TextDocument.GetFilePath() |> Utils.normalizePath - let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr + let! (namedText) = forceFindOpenFileOrRead filePath |> AsyncResult.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr @@ -4016,7 +4022,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let (filePath, pos) = getFilePathAndPosition p - let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr + let! (namedText) = forceFindOpenFileOrRead filePath |> AsyncResult.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr @@ -4104,7 +4110,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> HashSet.ofArray transact (fun () -> workspacePaths.Value <- (WorkspaceChosen.Projs projs)) - let! _ = parseAllFiles () |> AVal.force + let! _ = parseAllFiles () |> AsyncAVal.forceAsync return { Content = CommandResponse.workspaceLoad FsAutoComplete.JsonSerializer.writeJson true } @@ -4364,7 +4370,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let (filePath, pos) = getFilePathAndPosition p - let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr + let! (namedText) = forceFindOpenFileOrRead filePath |> AsyncResult.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr @@ -4395,7 +4401,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let (filePath, pos) = getFilePathAndPosition p - let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr + let! (namedText) = forceFindOpenFileOrRead filePath |> AsyncResult.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr and! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr lastFSharpDocumentationTypeCheck <- Some tyRes @@ -4509,7 +4515,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let filePath = p.TextDocument.GetFilePath() |> Utils.normalizePath let! tyRes = forceGetTypeCheckResults filePath |> AsyncResult.ofStringErr - match! Commands.pipelineHints forceFindSourceText tyRes |> Result.ofCoreResponse with + match! Commands.pipelineHints forceFindSourceText tyRes |> AsyncResult.ofCoreResponse with | None -> return None | Some res -> return Some { Content = CommandResponse.pipelineHint FsAutoComplete.JsonSerializer.writeJson res } @@ -4882,6 +4888,9 @@ module AdaptiveFSharpLspServer = |> Map.add "fsproj/removeFile" (serverRequestHandling (fun s p -> s.FsProjRemoveFile(p))) let adaptiveServer lspClient = + // match System.Threading.ThreadPool.GetMinThreads() with + // | _, completionPortThreads -> + // ThreadPool.SetMinThreads(System.Environment.ProcessorCount * 4, completionPortThreads) |> ignore let loader = workspaceLoaderFactory toolsPath new AdaptiveFSharpLspServer(loader, lspClient) :> IFSharpLspServer diff --git a/src/FsAutoComplete/LspServers/Common.fs b/src/FsAutoComplete/LspServers/Common.fs index 9e29f0510..00fb67e7a 100644 --- a/src/FsAutoComplete/LspServers/Common.fs +++ b/src/FsAutoComplete/LspServers/Common.fs @@ -150,13 +150,6 @@ module Async = let inline logCancelled e = logger.trace (Log.setMessage "Operation Cancelled" >> Log.addExn e) - /// Creates an asynchronous computation that executes all the given asynchronous computations, using 75% of the Environment.ProcessorCount - /// A sequence of distinct computations to be parallelized. - let parallel75 computations = - let maxConcurrency = - Math.Max(1.0, Math.Floor((float System.Environment.ProcessorCount) * 0.75)) - - Async.Parallel(computations, int maxConcurrency) let withCancellation (ct: CancellationToken) (a: Async<'a>) : Async<'a> = async { @@ -194,22 +187,7 @@ module Async = let StartWithCT ct work = Async.Start(work, ct) - let startImmediateAsTask ct work = - Async.StartImmediateAsTask(work, ct) - - let RunSynchronouslyWithCT ct work = - Async.RunSynchronously(work, cancellationToken = ct) - - let RunSynchronouslyWithCTSafe ct work = - try - work |> RunSynchronouslyWithCT(ct ()) |> Some - with - | :? OperationCanceledException as e -> - logCancelled e - None - | :? ObjectDisposedException as e when e.Message.Contains("CancellationTokenSource has been disposed") -> - logCancelled e - None + let startImmediateAsTask ct work = Async.StartImmediateAsTask(work, ct) [] module ObservableExtensions = diff --git a/src/FsAutoComplete/LspServers/FsAutoComplete.Lsp.fs b/src/FsAutoComplete/LspServers/FsAutoComplete.Lsp.fs index f6b58b019..61f039e50 100644 --- a/src/FsAutoComplete/LspServers/FsAutoComplete.Lsp.fs +++ b/src/FsAutoComplete/LspServers/FsAutoComplete.Lsp.fs @@ -68,7 +68,7 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = commands.CheckSimplifiedNames filePath ] async { - do! analyzers |> Async.Parallel |> Async.Ignore + do! analyzers |> Async.parallel75 |> Async.Ignore do! lspClient.NotifyDocumentAnalyzed @@ -1105,14 +1105,18 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = | Some false -> None | None -> None - let getFileLines = commands.TryGetFileCheckerOptionsWithLines >> Result.map snd + let getFileLines = + commands.TryGetFileCheckerOptionsWithLines >> Result.map snd >> Async.singleton let getLineText (lines: NamedText) (range: Ionide.LanguageServerProtocol.Types.Range) = lines.GetText(protocolRangeToRange (UMX.untag lines.FileName) range) + |> Async.singleton let getRangeText fileName (range: Ionide.LanguageServerProtocol.Types.Range) = - getFileLines fileName - |> Result.bind (fun lines -> lines.GetText(protocolRangeToRange (UMX.untag fileName) range)) + asyncResult { + let! lines = getFileLines fileName + return! lines.GetText(protocolRangeToRange (UMX.untag fileName) range) + } let getProjectOptsAndLines = commands.TryGetFileCheckerOptionsWithLinesAndLineStr @@ -1917,7 +1921,7 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = with e -> return Ok [] }) - |> Async.Parallel + |> Async.parallel75 let! fixes = fixes let (actions: Fix list[], errors: string[]) = Array.partitionResults fixes @@ -1936,14 +1940,12 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = match actions with | [] -> return None | actions -> - return + let! fixes = actions - |> List.map ( - CodeAction.OfFix commands.TryGetFileVersion clientCapabilities.Value - >> U2.Second - ) - |> List.toArray - |> Some + |> List.map (CodeAction.OfFix (commands.TryGetFileVersion >> Async.singleton) clientCapabilities.Value) + |> Async.parallel75 + + return Some(fixes |> Array.map U2.Second) } override __.TextDocumentCodeLens(p: CodeLensParams) = @@ -2873,14 +2875,16 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = p.TextDocument |> x.fileHandler (fun fn tyRes lines -> - match commands.PipelineHints tyRes with - | CoreResponse.InfoRes msg -> async.Return(success None) - | CoreResponse.ErrorRes msg -> AsyncLspResult.internalError msg - | CoreResponse.Res(res) -> - { Content = CommandResponse.pipelineHint FsAutoComplete.JsonSerializer.writeJson res } - |> Some - |> success - |> async.Return) + async { + match! commands.PipelineHints tyRes with + | CoreResponse.InfoRes msg -> return! async.Return(success None) + | CoreResponse.ErrorRes msg -> return! AsyncLspResult.internalError msg + | CoreResponse.Res(res) -> + return + { Content = CommandResponse.pipelineHint FsAutoComplete.JsonSerializer.writeJson res } + |> Some + |> success + }) override x.TextDocumentInlineValue(p: InlineValueParams) : AsyncLspResult = logger.info ( From 6933fc9c9e7c1aea1910f0b503ad537aa636d525 Mon Sep 17 00:00:00 2001 From: Jimmy Byrd Date: Sun, 26 Mar 2023 10:14:18 -0400 Subject: [PATCH 03/10] fix stalling tests --- src/FsAutoComplete.Core/AdaptiveExtensions.fs | 64 +++--- .../CompilerServiceInterface.fs | 2 +- src/FsAutoComplete.Core/Utils.fs | 3 + .../LspServers/AdaptiveFSharpLspServer.fs | 184 +++++++++--------- 4 files changed, 133 insertions(+), 120 deletions(-) diff --git a/src/FsAutoComplete.Core/AdaptiveExtensions.fs b/src/FsAutoComplete.Core/AdaptiveExtensions.fs index 9295c1e0b..5d877f3be 100644 --- a/src/FsAutoComplete.Core/AdaptiveExtensions.fs +++ b/src/FsAutoComplete.Core/AdaptiveExtensions.fs @@ -441,6 +441,24 @@ module AsyncAVal = // ) // :> asyncaval<_> + + // let ofAsync (value : Async<'a>) = + // ConstantVal ( + // let cts = new CancellationTokenSource () + // let cancel () = + // cts.Cancel() + // cts.Dispose() + // let real = task { + // try + // return! Async.StartImmediateAsTask(value, cts.Token) + // finally + // cts.Dispose() + // } + // AdaptiveCancellableTask(cancel, real) + // ) + // :> asyncaval<_> + + let ofAVal (value: aval<'a>) = if value.IsConstant then ConstantVal(Task.FromResult(AVal.force value)) :> asyncaval<_> @@ -480,31 +498,7 @@ module AsyncAVal = let mapSync (mapping: 'a -> CancellationToken -> 'b) (input: asyncaval<'a>) = - let mutable cache: option> = None - - { new AbstractVal<'b>() with - member x.Compute t = - if x.OutOfDate || Option.isNone cache then - let ref = - RefCountingTaskCreator( - cancellableTask { - let! ct = CancellableTask.getCancellationToken () - let it = input.GetValue t - let s = ct.Register(fun () -> it.Cancel()) - - try - let! i = it - return mapping i ct - finally - s.Dispose() - } - ) - - cache <- Some ref - ref.New() - else - cache.Value.New() } - :> asyncaval<_> + map (fun a ct -> Task.FromResult(mapping a ct)) input let map2 (mapping: 'a -> 'b -> CancellationToken -> Task<'c>) (ca: asyncaval<'a>) (cb: asyncaval<'b>) = let mutable cache: option> = None @@ -528,7 +522,7 @@ module AsyncAVal = try let! va = ta let! vb = tb - return! mapping va vb ct + return! mapping va vb finally s.Dispose() } @@ -633,6 +627,10 @@ type AsyncAValBuilder() = member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 -> CancellationToken -> Task<'T2>) = AsyncAVal.map mapping value + + // member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 * CancellationToken -> Task<'T2>) = + // AsyncAVal.map (fun data ctok -> mapping(data,ctok)) value + member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 -> Task<'T2>) = AsyncAVal.map (fun data _ -> mapping data) value @@ -645,6 +643,12 @@ type AsyncAValBuilder() = member inline x.Bind(value: asyncaval<'T1>, [] mapping: 'T1 -> CancellationToken -> asyncaval<'T2>) = AsyncAVal.bind (mapping) value + // member inline x.Bind(value: asyncaval<'T1>, [] mapping: 'T1 * CancellationToken -> asyncaval<'T2>) = + // AsyncAVal.bind (fun data ctok -> mapping(data,ctok)) value + + + + member inline x.Bind(value: asyncaval<'T1>, [] mapping: 'T1 -> asyncaval<'T2>) = AsyncAVal.bind (fun data _ -> mapping data) value @@ -668,15 +672,17 @@ module AsyncAValBuilderExtensions = member inline x.Source(value: aval<'T>) = AsyncAVal.ofAVal value member inline x.Source(value: Task<'T>) = AsyncAVal.ofTask value - member inline x.Source(value: AdaptiveCancellableTask<'T>) = AsyncAVal.ofCancelableTask value + // member inline x.Source(value: AdaptiveCancellableTask<'T>) = AsyncAVal.ofCancelableTask value // member inline x.Source(value : CancellableTask<'T>) = AsyncAVal.ofCancellableTask value member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 -> CancellationToken -> 'T2) = - AsyncAVal.map (fun data ctok -> mapping data ctok |> Task.FromResult) value + AsyncAVal.mapSync (fun data ctok -> mapping data ctok) value member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 -> 'T2) = - AsyncAVal.map (fun data ctok -> mapping data |> Task.FromResult) value + AsyncAVal.mapSync (fun data ctok -> mapping data) value +// member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 * CancellationToken -> 'T2) = +// AsyncAVal.mapSync (fun data ctok -> mapping(data, ctok)) value // member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 -> (CancellationToken * 'T2)) = // AsyncAVal.map (fun data ct -> mapping data ct |> Task.FromResult) value diff --git a/src/FsAutoComplete.Core/CompilerServiceInterface.fs b/src/FsAutoComplete.Core/CompilerServiceInterface.fs index b0446ba3a..8e4046728 100644 --- a/src/FsAutoComplete.Core/CompilerServiceInterface.fs +++ b/src/FsAutoComplete.Core/CompilerServiceInterface.fs @@ -424,7 +424,7 @@ type FSharpCompilerServiceChecker(hasAnalyzers, typecheckCacheSize) = project, symbol, canInvalidateProject = false, - fastCheck = true, + // fastCheck = true, userOpName = "find references" ) } diff --git a/src/FsAutoComplete.Core/Utils.fs b/src/FsAutoComplete.Core/Utils.fs index 1e977066d..45425d4d3 100644 --- a/src/FsAutoComplete.Core/Utils.fs +++ b/src/FsAutoComplete.Core/Utils.fs @@ -912,6 +912,9 @@ module Tracing = let fsacActivitySource = new ActivitySource(serviceName, Version.info().Version) + let recordException (e: exn) (trace: Activity) = + trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + /// /// StreamJsonRpcTracingStrategy participates in and propagates trace context in vs-streamjsonrpc /// diff --git a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs index f5e549f89..3e72d1400 100644 --- a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs +++ b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs @@ -39,6 +39,7 @@ open FSharp.Compiler.CodeAnalysis open FsAutoComplete.LspHelpers open FsAutoComplete.UnionPatternMatchCaseGenerator open System.Collections.Concurrent +open System.Diagnostics [] type WorkspaceChosen = @@ -719,7 +720,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar AVal.Observable.onWeakMarking loadedProjectOptions |> Observable.throttleOn Concurrency.NewThreadScheduler.Default (TimeSpan.FromMilliseconds(200.)) |> Observable.observeOn Concurrency.NewThreadScheduler.Default - |> Observable.subscribe (forceLoadProjects >> ignore) + |> Observable.subscribe (forceLoadProjects >> ignore>) |> disposables.Add @@ -856,7 +857,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar cancelToken filePath value new CancellationTokenSource() - openFilesTokens.AddOrUpdate(filePath, adder, updater) |> ignore + openFilesTokens.AddOrUpdate(filePath, adder, updater) + |> ignore let updateOpenFiles (file: VolatileFile) = @@ -869,7 +871,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let updateTextchanges filePath p = let adder _ = cset<_> [ p ] - let updater _ (v: cset<_>) = v.Add p |> ignore + let updater _ (v: cset<_>) = v.Add p |> ignore resetCancellationToken filePath transact (fun () -> textChanges.AddOrElse(filePath, adder, updater)) @@ -937,7 +939,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let parseAllFiles () = asyncAVal { let! projects = loadedProjectOptions - and! checker = checker + and! (checker: FSharpCompilerServiceChecker) = checker + return fun (ct: CancellationToken) -> @@ -960,6 +963,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> Async.parallel75 |> Async.map (Array.Parallel.choose id) |> Async.startImmediateAsTask ct + } let forceFindSourceText filePath = @@ -971,7 +975,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> AMapAsync.mapAVal (fun filePath file ctok -> asyncAVal { if Utils.isAScript (UMX.untag filePath) then - let! checker = checker + let! (checker: FSharpCompilerServiceChecker) = checker and! tfmConfig = tfmConfig let! projs = @@ -986,7 +990,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar opts |> scriptFileProjectOptions.Trigger return opts } - + // return Unchecked.defaultof<_> return file, Option.toList projs else let! projs = @@ -1320,7 +1324,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar /// A Result of ParseAndCheckResults let forceGetTypeCheckResultsStale (filePath: string) = asyncAVal { - let! checker = checker + let! (checker: FSharpCompilerServiceChecker) = checker let inline tryGetLastCheckResultForFile filePath = checker.TryGetLastCheckResultForFile(filePath) @@ -1331,8 +1335,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar tryGetLastCheckResultForFile filePath |> AsyncResult.orElseWith (fun _ -> forceGetRecentTypeCheckResults filePath) |> AsyncResult.orElseWith (fun _ -> forceGetTypeCheckResults filePath) + |> Async.map (fun r -> + Async.Start(forceGetTypeCheckResults filePath |> Async.Ignore) + r) |> Async.StartImmediateAsTask - // |> Result.tee (fun _ -> Async.Start(async { forceGetTypeCheckResults filePath |> ignore })) } |> AsyncAVal.forceAsync @@ -1698,13 +1704,13 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> AsyncAVal.forceAsync transact (fun () -> - openFiles.Remove filePath |> ignore + openFiles.Remove filePath |> ignore match openFilesTokens.TryRemove(filePath) with | (true, cts) -> cancelToken filePath cts | _ -> () - textChanges.Remove filePath |> ignore) + textChanges.Remove filePath |> ignore) let! isOutsideWorkspace = isOutsideWorkspace filePath @@ -1795,7 +1801,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> Async.Ignore |> Async.bind (fun _ -> async { - Interlocked.Increment(&checksCompleted) |> ignore + let checksCompleted = Interlocked.Increment(&checksCompleted) do! progressReporter.Report( @@ -2042,7 +2048,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! LspResult.internalError "Fantomas install not found." | (FormatDocumentResponse.Error ex) -> return! LspResult.internalError ex with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error (Log.setMessage "HandleFormatting Request Errored {p}" >> Log.addExn e) return! LspResult.internalError (string e) } @@ -2142,7 +2148,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar Capabilities = defaultSettings } with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "Initialize Request Errored {p}" @@ -2164,7 +2170,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return () with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "Initialized Request Errored {p}" @@ -2177,12 +2183,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar override __.TextDocumentDidOpen(p: DidOpenTextDocumentParams) = async { - let tags = [ "InitializedParams", box p ] + let tags = [ "DidOpenTextDocumentParams", box p ] use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) try - trace.SetTagSafe("DidOpenTextDocumentParams", p) |> ignore - logger.info ( Log.setMessage "TextDocumentDidOpen Request: {parms}" >> Log.addContextDestructured "parms" p @@ -2197,10 +2201,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar // We want to try to use the file system's datetime if available let file = VolatileFile.Create(filePath, doc.Text, (Some doc.Version)) updateOpenFiles file - forceGetTypeCheckResults filePath |> ignore + let! _ = forceGetTypeCheckResults filePath return () with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentDidOpen Request Errored {p}" @@ -2227,7 +2231,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return () with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentDidClose Request Errored {p}" @@ -2254,12 +2258,12 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar updateTextchanges filePath (p, DateTime.UtcNow) - forceGetTypeCheckResults filePath |> ignore + let! _ = forceGetTypeCheckResults filePath return () with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentDidChange Request Errored {p}" @@ -2297,9 +2301,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar transact (fun () -> updateOpenFiles file - textChanges.Remove filePath |> ignore) + textChanges.Remove filePath |> ignore) - forceGetTypeCheckResults filePath |> ignore + let! _ = forceGetTypeCheckResults filePath do! bypassAdaptiveAndCheckDepenenciesForFile filePath do! lspClient.CodeLensRefresh() @@ -2310,7 +2314,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return () with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentDidSave Request Errored {p}" @@ -2405,7 +2409,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar d.NameInList, (d, pos, filePath, namedText.Lines.GetLine, typeCheckResults.GetAST) ] ) |> autoCompleteItems.UpdateTo) - |> ignore + |> ignore let includeKeywords = config.KeywordsAutocomplete && shouldKeywords @@ -2444,7 +2448,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar success (Some completionList) with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentCompletion Request Errored {p}" @@ -2544,7 +2548,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> success with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "CompletionItemResolve Request Errored {p}" @@ -2609,7 +2613,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! success (Some res) with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentSignatureHelp Request: {parms}" @@ -2702,11 +2706,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! LspResult.internalError $"No TryGetToolTipEnhanced results for {filePath}" | Error e -> - trace.RecordError(e, "TextDocumentHover.Error") |> ignore + trace.RecordError(e, "TextDocumentHover.Error") |> ignore logger.error (Log.setMessage "Failed with {error}" >> Log.addContext "error" e) return! LspResult.internalError e with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentHover Request Errored {p}" @@ -2796,7 +2800,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return WorkspaceEdit.Create(documentChanges, clientCapabilities) |> Some with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentRename Request Errored {p}" @@ -2826,7 +2830,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! decl = tyRes.TryFindDeclaration pos lineStr |> AsyncResult.ofStringErr return decl |> findDeclToLspLocation |> GotoResult.Single |> Some with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentDefinition Request Errored {p}" @@ -2856,7 +2860,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! decl = tyRes.TryFindTypeDeclaration pos lineStr |> AsyncResult.ofStringErr return decl |> findDeclToLspLocation |> GotoResult.Single |> Some with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentTypeDefinition Request Errored {p}" @@ -2892,7 +2896,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return Some references with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentReferences Request Errored {p}" @@ -2933,7 +2937,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar Kind = None }) |> Some with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentDocumentHighlight Request Errored {p}" @@ -3004,7 +3008,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar | [| single |] -> return Some(GotoResult.Single single) | multiple -> return Some(GotoResult.Multiple multiple) with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentImplementation Request Errored {p}" @@ -3041,7 +3045,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> Some | None -> return! LspResult.internalError $"No declarations for {fn}" with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentDocumentSymbol Request Errored {p}" @@ -3080,7 +3084,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return res with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "WorkspaceSymbol Request Errored {p}" @@ -3122,7 +3126,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! x.HandleFormatting(fileName, action, handlerFormattedDoc, (fun (_, _, _) -> [||])) with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentFormatting Request Errored {p}" @@ -3175,7 +3179,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! x.HandleFormatting(fileName, action, (fun (_, _) -> [||]), handlerFormattedRangeDoc) with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentRangeFormatting Request Errored {p}" @@ -3242,7 +3246,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return Some(fixes |> Array.map U2.Second) with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentCodeAction Request Errored {p}" @@ -3280,7 +3284,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return Some res with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentCodeLens Request Errored {p}" @@ -3349,7 +3353,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return codeLens with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "CodeLensResolve - Operation failed on {file}" @@ -3458,7 +3462,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar if c.Type = FileChangeType.Deleted then do! forgetDocument c.Uri with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "WorkspaceDidChangeWatchedFiles Request Errored {p}" @@ -3487,7 +3491,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar updateConfig c) with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "WorkspaceDidChangeConfiguration Request Errored {p}" @@ -3519,7 +3523,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! scopes = Commands.scopesForFile getParseResultsForFile file |> AsyncResult.ofStringErr return scopes |> Seq.map Structure.toFoldingRange |> Set.ofSeq |> List.ofSeq |> Some with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentFoldingRange Request Errored {p}" @@ -3565,7 +3569,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return ranges |> List.choose mkSelectionRanges |> Some with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentSelectionRange Request Errored {p}" @@ -3591,7 +3595,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! x.handleSemanticTokens fn None with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentSemanticTokensFull Request Errored {p}" @@ -3619,7 +3623,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let fcsRange = protocolRangeToRange (UMX.untag fn) p.Range return! x.handleSemanticTokens fn (Some fcsRange) with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentSemanticTokensRange Request Errored {p}" @@ -3724,7 +3728,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return (Some hints) with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentInlayHint Request Errored {p}" @@ -3766,7 +3770,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return hints with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "TextDocumentInlineValue Request Errored {p}" @@ -3961,7 +3965,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar tip |> Option.map (fun tip -> { Content = CommandResponse.typeSig FsAutoComplete.JsonSerializer.writeJson tip }) with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FSharpSignature Request Errored {p}" @@ -3998,7 +4002,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar { Content = CommandResponse.signatureData FsAutoComplete.JsonSerializer.writeJson (typ, parms, generics) } with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FSharpSignatureData Request Errored {p}" @@ -4050,7 +4054,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return () with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FSharpDocumentationGenerator Request Errored {p}" @@ -4082,7 +4086,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return Some { Content = CommandResponse.declarations FsAutoComplete.JsonSerializer.writeJson decls } with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FSharpLineLense Request Errored {p}" @@ -4115,7 +4119,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return { Content = CommandResponse.workspaceLoad FsAutoComplete.JsonSerializer.writeJson true } with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FSharpWorkspaceLoad Request Errored {p}" @@ -4153,7 +4157,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! res with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FSharpWorkspacePeek Request Errored {p}" @@ -4195,7 +4199,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! Helpers.notImplemented with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FSharpWorkspacePeek Request Errored {p}" @@ -4231,7 +4235,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return Some { Content = CommandResponse.dotnetnewlist FsAutoComplete.JsonSerializer.writeJson funcs } | None -> return None with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FSharpDotnetNewList Request Errored {p}" @@ -4256,11 +4260,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! Commands.DotnetNewRun p.Template p.Name p.Output [] |> AsyncResult.ofCoreResponse - |> AsyncResult.map ignore // mapping unit option to unit + |> AsyncResult.ignore // mapping unit option to unit return None with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FSharpDotnetNewRun Request Errored {p}" @@ -4285,11 +4289,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! Commands.DotnetAddProject p.Target p.Reference |> AsyncResult.ofCoreResponse - |> AsyncResult.map ignore // mapping unit option to unit + |> AsyncResult.ignore // mapping unit option to unit return None with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FSharpDotnetAddProject Request Errored {p}" @@ -4314,11 +4318,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! Commands.DotnetRemoveProject p.Target p.Reference |> AsyncResult.ofCoreResponse - |> AsyncResult.map ignore + |> AsyncResult.ignore return None with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FSharpDotnetRemoveProject Request Errored {p}" @@ -4343,11 +4347,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! Commands.DotnetSlnAdd p.Target p.Reference |> AsyncResult.ofCoreResponse - |> AsyncResult.map ignore + |> AsyncResult.ignore return None with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FSharpDotnetSlnAdd Request Errored {p}" @@ -4378,7 +4382,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar | Some t -> return Some { Content = CommandResponse.help FsAutoComplete.JsonSerializer.writeJson t } | None -> return None with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FSharpHelp Request Errored {p}" @@ -4420,7 +4424,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar XmlKey = xmlKey |} } | None -> return None with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FSharpDocumentation Request Errored {p}" @@ -4466,7 +4470,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar XmlKey = xmlKey |} } |> Some with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FSharpDocumentationSymbol Request Errored {p}" @@ -4496,7 +4500,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return LspResult.success () with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e Loggers.analyzers.error (Log.setMessage "Loading failed" >> Log.addExn e) return LspResult.success () } @@ -4520,7 +4524,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar | Some res -> return Some { Content = CommandResponse.pipelineHint FsAutoComplete.JsonSerializer.writeJson res } with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FSharpPipelineHints Request Errored {p}" @@ -4545,11 +4549,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! Commands.FsProjMoveFileUp p.FsProj p.FileVirtualPath |> AsyncResult.ofCoreResponse - |> AsyncResult.map ignore + |> AsyncResult.ignore return None with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FsProjMoveFileUp Request Errored {p}" @@ -4575,11 +4579,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! Commands.FsProjMoveFileDown p.FsProj p.FileVirtualPath |> AsyncResult.ofCoreResponse - |> AsyncResult.map ignore + |> AsyncResult.ignore return None with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FsProjMoveFileDown Request Errored {p}" @@ -4605,11 +4609,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! Commands.addFileAbove p.FsProj p.FileVirtualPath p.NewFile |> AsyncResult.ofCoreResponse - |> AsyncResult.map ignore + |> AsyncResult.ignore return None with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FsProjAddFileAbove Request Errored {p}" @@ -4634,11 +4638,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! Commands.addFileBelow p.FsProj p.FileVirtualPath p.NewFile |> AsyncResult.ofCoreResponse - |> AsyncResult.map ignore + |> AsyncResult.ignore return None with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FsProjAddFileBelow Request Errored {p}" @@ -4663,11 +4667,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! Commands.renameFile p.FsProj p.OldFileVirtualPath p.NewFileName |> AsyncResult.ofCoreResponse - |> AsyncResult.map ignore + |> AsyncResult.ignore return None with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FsProjRenameFile Request Errored {p}" @@ -4693,11 +4697,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! Commands.addFile p.FsProj p.FileVirtualPath |> AsyncResult.ofCoreResponse - |> AsyncResult.map ignore + |> AsyncResult.ignore return None with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FsProjAddFile Request Errored {p}" @@ -4724,14 +4728,14 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! Commands.removeFile p.FsProj p.FileVirtualPath |> AsyncResult.ofCoreResponse - |> AsyncResult.map ignore + |> AsyncResult.ignore let fileUri = Path.FilePathToUri fullPath diagnosticCollections.ClearFor fileUri return None with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FsProjRemoveFile Request Errored {p}" @@ -4756,11 +4760,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! Commands.addExistingFile p.FsProj p.FileVirtualPath |> AsyncResult.ofCoreResponse - |> AsyncResult.map ignore + |> AsyncResult.ignore return None with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "FsProjAddExistingFile Request Errored {p}" @@ -4786,7 +4790,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore + trace |> Tracing.recordException e logger.error ( Log.setMessage "WorkDoneProgessCancel Request Errored {p}" From cbe4c8edda5359f02cee13ba9a492f5210915fd9 Mon Sep 17 00:00:00 2001 From: Jimmy Byrd Date: Sun, 26 Mar 2023 19:19:38 -0400 Subject: [PATCH 04/10] AsyncAval comments --- src/FsAutoComplete.Core/AdaptiveExtensions.fs | 105 ++++++++++++++++-- .../LspServers/AdaptiveFSharpLspServer.fs | 2 +- 2 files changed, 95 insertions(+), 12 deletions(-) diff --git a/src/FsAutoComplete.Core/AdaptiveExtensions.fs b/src/FsAutoComplete.Core/AdaptiveExtensions.fs index 5d877f3be..15b2ba122 100644 --- a/src/FsAutoComplete.Core/AdaptiveExtensions.fs +++ b/src/FsAutoComplete.Core/AdaptiveExtensions.fs @@ -292,13 +292,19 @@ module AMap = batchRecalcDirty mapping map - +/// +/// A task creator that caches the task and cancels it when no longer needed. +/// +/// +/// Since the task can be references multiple times in the dependency graph, it is important to cancel it only after there are no more references to it. +/// type internal RefCountingTaskCreator<'a>(create: CancellationToken -> Task<'a>) = let mutable refCount = 0 let mutable cache: option> = None let mutable cancel: CancellationTokenSource = null + /// Decrements the reference count and cancels the CancellationTokenSource if there are no more references. member private x.RemoveRef() = lock x (fun () -> if refCount = 1 then @@ -310,6 +316,7 @@ type internal RefCountingTaskCreator<'a>(create: CancellationToken -> Task<'a>) else refCount <- refCount - 1) + /// Creates a new task based on the creation function from the constructor. If a task has already been created, returns a cached version of the inflight task. member x.New() = lock x (fun () -> match cache with @@ -323,7 +330,12 @@ type internal RefCountingTaskCreator<'a>(create: CancellationToken -> Task<'a>) refCount <- refCount + 1 AdaptiveCancellableTask(x.RemoveRef, task)) - +/// +/// Represents a task that can be cancelled. +/// +/// +/// Upon cancellation, it will run the cancel function passed in and set cancellation for the task completion source. +/// and AdaptiveCancellableTask<'a>(cancel: unit -> unit, real: Task<'a>) = let cts = new CancellationTokenSource() @@ -333,10 +345,7 @@ and AdaptiveCancellableTask<'a>(cancel: unit -> unit, real: Task<'a>) = else let tcs = new TaskCompletionSource<'a>() - let s = - cts.Token.Register(fun () -> tcs.TrySetCanceled() |> ignore - - ) + let s = cts.Token.Register(fun () -> tcs.TrySetCanceled() |> ignore) real.ContinueWith(fun (t: Task<'a>) -> s.Dispose() @@ -348,10 +357,13 @@ and AdaptiveCancellableTask<'a>(cancel: unit -> unit, real: Task<'a>) = tcs.Task + /// Will run the cancel function passed into the constructor and set the output Task to cancelled state. member x.Cancel() = cancel () cts.Cancel() + /// The output of the passed in task to the constructor. + /// member x.Task = output type asyncaval<'a> = @@ -359,6 +371,7 @@ type asyncaval<'a> = abstract GetValue: AdaptiveToken -> AdaptiveCancellableTask<'a> module CancellableTask = + /// Converts AdaptiveCancellableTask to a CancellableTask. let inline ofAdaptiveCancellableTask (ct: AdaptiveCancellableTask<_>) = fun (ctok: CancellationToken) -> task { @@ -367,6 +380,7 @@ module CancellableTask = } module Async = + /// Converts AdaptiveCancellableTask to an Async. let inline ofAdaptiveCancellableTask (ct: AdaptiveCancellableTask<_>) = async { let! ctok = Async.CancellationToken @@ -379,27 +393,52 @@ module Extensions = type IcedTasks.CancellableTasks.CancellableTaskBuilderBase with + /// Allows implicit conversion of a AdaptiveCancellableTask to a CancellableTask in a cancellableTask CE. member inline x.Source(ct: AdaptiveCancellableTask<_>) = fun ctok -> (CancellableTask.ofAdaptiveCancellableTask ct ctok).GetAwaiter() module AsyncAVal = + /// + /// Evaluates the given adaptive value and returns a Task containing the value. + /// This should not be used inside the adaptive evaluation + /// of other AdaptiveObjects since it does not track dependencies. + /// + /// + /// This follows Task semantics and is already running. + /// let force (value: asyncaval<_>) = value.GetValue(AdaptiveToken.Top) + /// + /// Evaluates the given adaptive value and returns an Async containing the value. + /// This should not be used inside the adaptive evaluation + /// of other AdaptiveObjects since it does not track dependencies. + /// + /// + /// This follows Async semantics and is not already running. + /// let forceAsync (value: asyncaval<_>) = async { let ct = value.GetValue(AdaptiveToken.Top) return! Async.ofAdaptiveCancellableTask ct } - + /// + /// Evaluates the given adaptive value and returns a CancellableTask containing the value. + /// This should not be used inside the adaptive evaluation + /// of other AdaptiveObjects since it does not track dependencies. + /// + /// + /// This follows CancellableTask semantics and is not already running. + /// let forceCancellableTask (value: asyncaval<_>) = cancellableTask { let ct = value.GetValue(AdaptiveToken.Top) return! ct } + /// A constant value that results in a Task. type ConstantVal<'a>(value: AdaptiveCancellableTask<'a>) = inherit ConstantObject() @@ -408,6 +447,9 @@ module AsyncAVal = interface asyncaval<'a> with member x.GetValue _ = value + /// + /// Base class for standard Async Adaptive Values. + /// [] type AbstractVal<'a>() = inherit AdaptiveObject() @@ -418,12 +460,18 @@ module AsyncAVal = interface asyncaval<'a> with member x.GetValue t = x.GetValue t + /// + /// Creates a constant async adaptive value always holding the given value. + /// let constant (value: 'a) = ConstantVal(Task.FromResult value) :> asyncaval<_> + /// + /// Creates a constant async adaptive value always holding the task. + /// let ofTask (value: Task<'a>) = ConstantVal(value) :> asyncaval<_> - let ofCancelableTask (value: AdaptiveCancellableTask<'a>) = ConstantVal(value) :> asyncaval<_> + // let ofCancelableTask (value: AdaptiveCancellableTask<'a>) = ConstantVal(value) :> asyncaval<_> // let ofCancellableTask (value : CancellableTask<'a>) = // ConstantVal ( @@ -459,6 +507,9 @@ module AsyncAVal = // :> asyncaval<_> + /// + /// Creates an async adaptive value evaluation the given value. + /// let ofAVal (value: aval<'a>) = if value.IsConstant then ConstantVal(Task.FromResult(AVal.force value)) :> asyncaval<_> @@ -469,6 +520,11 @@ module AsyncAVal = AdaptiveCancellableTask(id, real) } :> asyncaval<_> + + /// + /// Returns a new async adaptive value that adaptively applies the mapping fun tion to the given + /// adaptive inputs. + /// let map (mapping: 'a -> CancellationToken -> Task<'b>) (input: asyncaval<'a>) = let mutable cache: option> = None @@ -497,9 +553,18 @@ module AsyncAVal = :> asyncaval<_> + + /// + /// Returns a new async adaptive value that adaptively applies the mapping fun tion to the given + /// adaptive inputs. + /// let mapSync (mapping: 'a -> CancellationToken -> 'b) (input: asyncaval<'a>) = map (fun a ct -> Task.FromResult(mapping a ct)) input + /// + /// Returns a new async adaptive value that adaptively applies the mapping function to the given + /// adaptive inputs. + /// let map2 (mapping: 'a -> 'b -> CancellationToken -> Task<'c>) (ca: asyncaval<'a>) (cb: asyncaval<'b>) = let mutable cache: option> = None @@ -534,6 +599,9 @@ module AsyncAVal = cache.Value.New() } :> asyncaval<_> + /// Returns a new async adaptive value that adaptively applies the mapping function to the given + /// input and adaptively depends on the resulting adaptive value. + /// The resulting adaptive value will hold the latest value of the asyncaval<_> returned by mapping. let bind (mapping: 'a -> CancellationToken -> asyncaval<'b>) (value: asyncaval<'a>) = let mutable cache: option<_> = None let mutable innerCache: option<_> = None @@ -613,13 +681,22 @@ module AsyncAVal = } :> asyncaval<_> - let mapOption f = - mapSync (fun data _ -> data |> Option.map f) + + /// Returns a new async adaptive value that adaptively applies the mapping function to the given + /// optional adaptive inputs. + let mapOption f (value: asyncaval<'a option>) : asyncaval<'b option> = + mapSync (fun data ctok -> data |> Option.map (fun d -> f d ctok)) value type AsyncAValBuilder() = member inline x.MergeSources(v1: asyncaval<'T1>, v2: asyncaval<'T2>) = - AsyncAVal.map2 (fun a b _ -> Task.FromResult(a, b)) v1 v2 + (v1, v2) + ||> AsyncAVal.map2 (fun a b ctok -> + if ctok.IsCancellationRequested then + Task.FromCanceled<_>(ctok) + else + Task.FromResult(a, b)) + // member inline x.MergeSources3(v1 : aval<'T1>, v2 : aval<'T2>, v3 : aval<'T3>) = // AVal.map3 (fun a b c -> a,b,c) v1 v2 v3 @@ -691,6 +768,9 @@ module AsyncAValBuilderExtensions = module AMapAsync = + /// + /// Adaptively maps over the given map lifting the value in the map to be an asyncaval. + /// let mapAVal (mapper: 'Key -> 'InValue -> CancellationToken -> asyncaval<'OutValue>) (map: #amap<'Key, #aval<'InValue>>) @@ -698,6 +778,9 @@ module AMapAsync = map |> AMap.map (fun k v -> v |> AsyncAVal.ofAVal |> AsyncAVal.bind (mapper k)) + /// + /// Adaptively maps over the given map. + /// let mapAsyncAVal (mapper: 'Key -> 'InValue -> CancellationToken -> asyncaval<'OutValue>) (map: #amap<'Key, #asyncaval<'InValue>>) diff --git a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs index 3e72d1400..5508ac783 100644 --- a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs +++ b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs @@ -1347,7 +1347,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar openFilesToCheckedFilesResults |> AMap.map (fun k v -> v - |> AsyncAVal.mapOption (fun c -> c.GetParseResults.GetNavigationItems().Declarations)) + |> AsyncAVal.mapOption (fun c _ -> c.GetParseResults.GetNavigationItems().Declarations)) let getAllDeclarations () = async { From 2441b955b42b8f9d12f6c223f12e1f6b6bdb14d7 Mon Sep 17 00:00:00 2001 From: Jimmy Byrd Date: Mon, 27 Mar 2023 21:37:05 -0400 Subject: [PATCH 05/10] Cleanups --- src/FsAutoComplete.Core/AdaptiveExtensions.fs | 139 +++++++++--------- .../CompilerServiceInterface.fs | 16 +- src/FsAutoComplete.Core/Utils.fs | 1 - src/FsAutoComplete/LspHelpers.fs | 2 +- .../LspServers/AdaptiveFSharpLspServer.fs | 86 +++++------ 5 files changed, 129 insertions(+), 115 deletions(-) diff --git a/src/FsAutoComplete.Core/AdaptiveExtensions.fs b/src/FsAutoComplete.Core/AdaptiveExtensions.fs index 15b2ba122..7c8db2913 100644 --- a/src/FsAutoComplete.Core/AdaptiveExtensions.fs +++ b/src/FsAutoComplete.Core/AdaptiveExtensions.fs @@ -471,40 +471,46 @@ module AsyncAVal = /// let ofTask (value: Task<'a>) = ConstantVal(value) :> asyncaval<_> - // let ofCancelableTask (value: AdaptiveCancellableTask<'a>) = ConstantVal(value) :> asyncaval<_> - - // let ofCancellableTask (value : CancellableTask<'a>) = - // ConstantVal ( - // let cts = new CancellationTokenSource () - // let cancel () = - // cts.Cancel() - // cts.Dispose() - // let real = task { - // try - // return! value cts.Token - // finally - // cts.Dispose() - // } - // AdaptiveCancellableTask(cancel, real) - // ) - // :> asyncaval<_> - - - // let ofAsync (value : Async<'a>) = - // ConstantVal ( - // let cts = new CancellationTokenSource () - // let cancel () = - // cts.Cancel() - // cts.Dispose() - // let real = task { - // try - // return! Async.StartImmediateAsTask(value, cts.Token) - // finally - // cts.Dispose() - // } - // AdaptiveCancellableTask(cancel, real) - // ) - // :> asyncaval<_> + let ofCancellableTask (value: CancellableTask<'a>) = + ConstantVal( + let cts = new CancellationTokenSource() + + let cancel () = + cts.Cancel() + cts.Dispose() + + let real = + task { + try + return! value cts.Token + finally + cts.Dispose() + } + + AdaptiveCancellableTask(cancel, real) + ) + :> asyncaval<_> + + + let ofAsync (value: Async<'a>) = + ConstantVal( + let cts = new CancellationTokenSource() + + let cancel () = + cts.Cancel() + cts.Dispose() + + let real = + task { + try + return! Async.StartImmediateAsTask(value, cts.Token) + finally + cts.Dispose() + } + + AdaptiveCancellableTask(cancel, real) + ) + :> asyncaval<_> /// @@ -553,6 +559,37 @@ module AsyncAVal = :> asyncaval<_> + /// + /// Returns a new async adaptive value that adaptively applies the mapping fun tion to the given + /// adaptive inputs. + /// + let mapAsync (mapping: 'a -> Async<'b>) (input: asyncaval<'a>) = + let mutable cache: option> = None + + { new AbstractVal<'b>() with + member x.Compute t = + if x.OutOfDate || Option.isNone cache then + let ref = + RefCountingTaskCreator( + cancellableTask { + let! ct = CancellableTask.getCancellationToken () + let it = input.GetValue t + let s = ct.Register(fun () -> it.Cancel()) + + try + let! i = it + return! mapping i + finally + s.Dispose() + } + ) + + cache <- Some ref + ref.New() + else + cache.Value.New() } + :> asyncaval<_> + /// /// Returns a new async adaptive value that adaptively applies the mapping fun tion to the given @@ -704,37 +741,18 @@ type AsyncAValBuilder() = member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 -> CancellationToken -> Task<'T2>) = AsyncAVal.map mapping value - - // member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 * CancellationToken -> Task<'T2>) = - // AsyncAVal.map (fun data ctok -> mapping(data,ctok)) value + member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 -> Async<'T2>) = + AsyncAVal.mapAsync mapping value member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 -> Task<'T2>) = AsyncAVal.map (fun data _ -> mapping data) value - // member inline x.Bind2Return(v1 : aval<'T1>, v2 : aval<'T2>, mapping: 'T1 * 'T2 -> 'T3) = - // AVal.map2 (fun a b -> mapping(a,b)) v1 v2 - - // member inline x.Bind3Return(v1 : aval<'T1>, v2: aval<'T2>, v3: aval<'T3>, mapping: 'T1 * 'T2 * 'T3 -> 'T4) = - // AVal.map3 (fun a b c -> mapping(a, b, c)) v1 v2 v3 - member inline x.Bind(value: asyncaval<'T1>, [] mapping: 'T1 -> CancellationToken -> asyncaval<'T2>) = AsyncAVal.bind (mapping) value - // member inline x.Bind(value: asyncaval<'T1>, [] mapping: 'T1 * CancellationToken -> asyncaval<'T2>) = - // AsyncAVal.bind (fun data ctok -> mapping(data,ctok)) value - - - - member inline x.Bind(value: asyncaval<'T1>, [] mapping: 'T1 -> asyncaval<'T2>) = AsyncAVal.bind (fun data _ -> mapping data) value - // member inline x.Bind2(v1: aval<'T1>, v2: aval<'T2>, mapping: 'T1 * 'T2 -> aval<'T3>) = - // AVal.bind2 (fun a b -> mapping(a,b)) v1 v2 - - // member inline x.Bind3(v1: aval<'T1>, v2: aval<'T2>, v3: aval<'T3>, mapping: 'T1 * 'T2 * 'T3 -> aval<'T4>) = - // AVal.bind3 (fun a b c -> mapping(a, b, c)) v1 v2 v3 - member inline x.Return(value: 'T) = AsyncAVal.constant value member inline x.ReturnFrom(value: asyncaval<'T>) = value @@ -749,8 +767,6 @@ module AsyncAValBuilderExtensions = member inline x.Source(value: aval<'T>) = AsyncAVal.ofAVal value member inline x.Source(value: Task<'T>) = AsyncAVal.ofTask value - // member inline x.Source(value: AdaptiveCancellableTask<'T>) = AsyncAVal.ofCancelableTask value - // member inline x.Source(value : CancellableTask<'T>) = AsyncAVal.ofCancellableTask value member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 -> CancellationToken -> 'T2) = AsyncAVal.mapSync (fun data ctok -> mapping data ctok) value @@ -758,14 +774,6 @@ module AsyncAValBuilderExtensions = member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 -> 'T2) = AsyncAVal.mapSync (fun data ctok -> mapping data) value -// member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 * CancellationToken -> 'T2) = -// AsyncAVal.mapSync (fun data ctok -> mapping(data, ctok)) value -// member inline x.BindReturn(value: asyncaval<'T1>, [] mapping: 'T1 -> (CancellationToken * 'T2)) = -// AsyncAVal.map (fun data ct -> mapping data ct |> Task.FromResult) value - - - - module AMapAsync = /// @@ -777,7 +785,6 @@ module AMapAsync = : amap<'Key, asyncaval<'OutValue>> = map |> AMap.map (fun k v -> v |> AsyncAVal.ofAVal |> AsyncAVal.bind (mapper k)) - /// /// Adaptively maps over the given map. /// diff --git a/src/FsAutoComplete.Core/CompilerServiceInterface.fs b/src/FsAutoComplete.Core/CompilerServiceInterface.fs index 8e4046728..7f4bc9299 100644 --- a/src/FsAutoComplete.Core/CompilerServiceInterface.fs +++ b/src/FsAutoComplete.Core/CompilerServiceInterface.fs @@ -256,13 +256,21 @@ type FSharpCompilerServiceChecker(hasAnalyzers, typecheckCacheSize) = checker.InvalidateAll() checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() - member __.ParseFile(fn: string, source, fpo) = + /// Parses a source code for a file and caches the results. Returns an AST that can be traversed for various features. + /// The path for the file. The file name is used as a module name for implicit top level modules (e.g. in scripts). + /// The source to be parsed. + /// Parsing options for the project or script. + /// + member __.ParseFile(filePath: string, source: ISourceText, options: FSharpParsingOptions) = async { - checkerLogger.info (Log.setMessage "ParseFile - {file}" >> Log.addContextDestructured "file" fn) + checkerLogger.info ( + Log.setMessage "ParseFile - {file}" + >> Log.addContextDestructured "file" filePath + ) - let path = UMX.untag fn + let path = UMX.untag filePath do! Async.SwitchToNewThread() - return! checker.ParseFile(path, source, fpo) + return! checker.ParseFile(path, source, options) } /// Parse and check a source code file, returning a handle to the results diff --git a/src/FsAutoComplete.Core/Utils.fs b/src/FsAutoComplete.Core/Utils.fs index 45425d4d3..c055d8772 100644 --- a/src/FsAutoComplete.Core/Utils.fs +++ b/src/FsAutoComplete.Core/Utils.fs @@ -229,7 +229,6 @@ module Async = Math.Max(1.0, Math.Floor((float System.Environment.ProcessorCount) * 0.75)) Async.Parallel(computations, int maxConcurrency) - // Async.Parallel(computations) [] module Array = diff --git a/src/FsAutoComplete/LspHelpers.fs b/src/FsAutoComplete/LspHelpers.fs index 824dae84e..dd9c024a0 100644 --- a/src/FsAutoComplete/LspHelpers.fs +++ b/src/FsAutoComplete/LspHelpers.fs @@ -172,7 +172,7 @@ module Conversions = not keep topLevel.Nested - |> Array.choose (fun n -> if shouldKeep n then Some(map n) else None) + |> Array.Parallel.choose (fun n -> if shouldKeep n then Some(map n) else None) let getLine (lines: string[]) (pos: Lsp.Position) = lines.[pos.Line] diff --git a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs index 5508ac783..99caedd4f 100644 --- a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs +++ b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs @@ -289,6 +289,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let handleCommandEvents (n: NotificationEvent, ct: CancellationToken) = try async { + try match n with | NotificationEvent.FileParsed fn -> @@ -935,34 +936,44 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar FSharp.Compiler.IO.FileSystemAutoOpens.FileSystem <- FileSystem(FSharp.Compiler.IO.FileSystemAutoOpens.FileSystem, filesystemShim) + /// Parses a source code for a file and caches the results. Returns an AST that can be traversed for various features. + /// The FSharpCompilerServiceChecker. + /// The source to be parsed. + /// Parsing options for the project or script + /// The options for the project or script. + /// + let parseFile (checker: FSharpCompilerServiceChecker) (source: VolatileFile) parseOpts options = + async { + let! result = checker.ParseFile(source.FileName, source.Lines, parseOpts) + + let! ct = Async.CancellationToken + fileParsed.Trigger(result, options, ct) + return result + } + + /// Parses all files in the workspace. This is mostly used to trigger finding tests. let parseAllFiles () = asyncAVal { let! projects = loadedProjectOptions and! (checker: FSharpCompilerServiceChecker) = checker - return - fun (ct: CancellationToken) -> - projects - |> Array.ofList - |> Array.Parallel.collect (fun p -> - let parseOpts = Utils.projectOptionsToParseOptions p - p.SourceFiles |> Array.map (fun s -> p, parseOpts, s)) - |> Array.Parallel.map (fun (opts, parseOpts, fileName) -> - let fileName = UMX.tag fileName - - asyncResult { - let! file = forceFindOpenFileOrRead fileName - let! parseResult = checker.ParseFile(fileName, file.Lines, parseOpts) - let! ct = Async.CancellationToken - fileParsed.Trigger(parseResult, opts, ct) - return parseResult - } - |> Async.map Result.toOption) - |> Async.parallel75 - |> Async.map (Array.Parallel.choose id) - |> Async.startImmediateAsTask ct + projects + |> Array.ofList + |> Array.Parallel.collect (fun p -> + let parseOpts = Utils.projectOptionsToParseOptions p + p.SourceFiles |> Array.map (fun s -> p, parseOpts, s)) + |> Array.Parallel.map (fun (opts, parseOpts, fileName) -> + let fileName = UMX.tag fileName + + asyncResult { + let! file = forceFindOpenFileOrRead fileName + return! parseFile checker file parseOpts opts + } + |> Async.map Result.toOption) + |> Async.parallel75 + |> Async.map (Array.Parallel.choose id) } @@ -990,7 +1001,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar opts |> scriptFileProjectOptions.Trigger return opts } - // return Unchecked.defaultof<_> + return file, Option.toList projs else let! projs = @@ -1049,7 +1060,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let getAutoCompleteNamespacesByDeclName name = autoCompleteNamespaces |> AMap.tryFind name - let analyzeFile config (filePath: string, version, source, tyRes: ParseAndCheckResults) = + let analyzeFile config (filePath: string, version, source: NamedText, tyRes: ParseAndCheckResults) = let checkUnusedOpens = async { try @@ -1079,7 +1090,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let checkSimplifiedNames = async { try - let getSourceLine lineNo = source.GetLineString(lineNo - 1) + let getSourceLine lineNo = + (source: ISourceText).GetLineString(lineNo - 1) let! ct = Async.CancellationToken let! simplified = SimplifyNames.getSimplifiableNames (tyRes.GetCheckResults, getSourceLine) @@ -1110,6 +1122,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar Version = version } } } + /// Gets Parse and Check results of a given file while also handling other concerns like Progress, Logging, Eventing. /// The FSharpCompilerServiceChecker. /// The name of the file in the project whose source to find a typecheck. @@ -1206,9 +1219,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar match! forceFindOpenFileOrRead filePath with // Don't cache for autocompletions as we really only want to cache "Opened" files. - | Ok(fileInfo) -> - do! Async.SwitchToNewThread() - return! parseAndCheckFile checker fileInfo opts config false |> Async.Ignore + | Ok(fileInfo) -> return! parseAndCheckFile checker fileInfo opts config false |> Async.Ignore | _ -> () with e -> @@ -1233,15 +1244,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let parseOpts = Utils.projectOptionsToParseOptions opts - let! result = - checker.ParseFile(file, info.Lines, parseOpts) + return! + parseFile checker info parseOpts opts |> Async.withCancellation cts.Token - |> fun work -> Async.StartImmediateAsTask(work, ctok) - - fileParsed.Trigger(result, opts, cts.Token) - return result + |> Async.startImmediateAsTask ctok } - }) @@ -1331,14 +1338,13 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> Result.ofOption (fun () -> $"No typecheck results for {filePath}") |> async.Return - return! + return tryGetLastCheckResultForFile filePath |> AsyncResult.orElseWith (fun _ -> forceGetRecentTypeCheckResults filePath) |> AsyncResult.orElseWith (fun _ -> forceGetTypeCheckResults filePath) |> Async.map (fun r -> Async.Start(forceGetTypeCheckResults filePath |> Async.Ignore) r) - |> Async.StartImmediateAsTask } |> AsyncAVal.forceAsync @@ -1515,7 +1521,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> Async.Parallel return set |> Array.collect (List.toArray) - } + } Commands.symbolUseWorkspace @@ -1818,9 +1824,6 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar percentage = percentage 0 checksToPerform.Length ) - let maxConcurrency = - Math.Max(1.0, Math.Floor((float System.Environment.ProcessorCount) * 0.75)) - do! checksToPerform |> Async.parallel75 |> Async.Ignore } @@ -4892,9 +4895,6 @@ module AdaptiveFSharpLspServer = |> Map.add "fsproj/removeFile" (serverRequestHandling (fun s p -> s.FsProjRemoveFile(p))) let adaptiveServer lspClient = - // match System.Threading.ThreadPool.GetMinThreads() with - // | _, completionPortThreads -> - // ThreadPool.SetMinThreads(System.Environment.ProcessorCount * 4, completionPortThreads) |> ignore let loader = workspaceLoaderFactory toolsPath new AdaptiveFSharpLspServer(loader, lspClient) :> IFSharpLspServer From 21f118ccee7cf8fd6a69a1bcc7fec4b8902dfb23 Mon Sep 17 00:00:00 2001 From: Jimmy Byrd Date: Mon, 3 Apr 2023 23:40:43 -0400 Subject: [PATCH 06/10] Refactorings --- .../LspServers/AdaptiveFSharpLspServer.fs | 160 +++++++++--------- 1 file changed, 79 insertions(+), 81 deletions(-) diff --git a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs index 99caedd4f..19f0104ab 100644 --- a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs +++ b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs @@ -65,7 +65,6 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let config = cval FSharpConfig.Default - let analyzersEnabled = config |> AVal.map (fun c -> c.EnableAnalyzers) let checker = @@ -236,10 +235,80 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar >> Log.addException e )) + let builtInCompilerAnalyzers config (file: VolatileFile, tyRes: ParseAndCheckResults) = + let filePath = file.FileName + let source = file.Lines + let version = file.Version + + let checkUnusedOpens = + async { + try + let! ct = Async.CancellationToken + + let! unused = + UnusedOpens.getUnusedOpens (tyRes.GetCheckResults, (fun i -> (source: ISourceText).GetLineString(i - 1))) + + notifications.Trigger(NotificationEvent.UnusedOpens(filePath, (unused |> List.toArray)), ct) + with e -> + logger.error (Log.setMessage "checkUnusedOpens failed" >> Log.addExn e) + } + + let checkUnusedDeclarations = + async { + try + let! ct = Async.CancellationToken + let isScript = Utils.isAScript (UMX.untag filePath) + let! unused = UnusedDeclarations.getUnusedDeclarations (tyRes.GetCheckResults, isScript) + let unused = unused |> Seq.toArray + + notifications.Trigger(NotificationEvent.UnusedDeclarations(filePath, unused), ct) + with e -> + logger.error (Log.setMessage "checkUnusedDeclarations failed" >> Log.addExn e) + } + + let checkSimplifiedNames = + async { + try + let getSourceLine lineNo = + (source: ISourceText).GetLineString(lineNo - 1) + + let! ct = Async.CancellationToken + let! simplified = SimplifyNames.getSimplifiableNames (tyRes.GetCheckResults, getSourceLine) + let simplified = Array.ofSeq simplified + notifications.Trigger(NotificationEvent.SimplifyNames(filePath, simplified), ct) + with e -> + logger.error (Log.setMessage "checkSimplifiedNames failed" >> Log.addExn e) + } + + let analyzers = + [ + // if config.Linter then + // commands.Lint filePath |> Async .Ignore + if config.UnusedOpensAnalyzer then + checkUnusedOpens + if config.UnusedDeclarationsAnalyzer then + checkUnusedDeclarations + if config.SimplifyNameAnalyzer then + checkSimplifiedNames ] + + async { + do! analyzers |> Async.parallel75 |> Async.Ignore + + do! + lspClient.NotifyDocumentAnalyzed + { TextDocument = + { Uri = filePath |> Path.LocalPathToUri + Version = version } } + } + + do disposables.Add <| fileChecked.Publish.Subscribe(fun (parseAndCheck, volatileFile, ct) -> async { + let config = config |> AVal.force + do! builtInCompilerAnalyzers config (volatileFile, parseAndCheck) + if analyzersEnabled |> AVal.force then let file = volatileFile.FileName @@ -513,14 +582,12 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let loader = cval workspaceLoader - - let binlogConfig = aval { - let! config = config + let! generateBinLog = config |> AVal.map (fun c -> c.GenerateBinlog) and! rootPath = rootPath - match config.GenerateBinlog, rootPath with + match generateBinLog, rootPath with | _, None | false, _ -> return Ionide.ProjInfo.BinaryLogGeneration.Off | true, Some rootPath -> @@ -767,10 +834,6 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar >> Log.addContextDestructured "version" v.Version ) - let tee f x = - f x - x - let openFilesWithChanges: amap<_, aval> = openFilesReadOnly |> AMap.map (fun filePath file -> @@ -829,7 +892,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar text) - return (file) |> tee logTextChange + logTextChange file + return file }) @@ -1060,77 +1124,14 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let getAutoCompleteNamespacesByDeclName name = autoCompleteNamespaces |> AMap.tryFind name - let analyzeFile config (filePath: string, version, source: NamedText, tyRes: ParseAndCheckResults) = - let checkUnusedOpens = - async { - try - let! ct = Async.CancellationToken - - let! unused = - UnusedOpens.getUnusedOpens (tyRes.GetCheckResults, (fun i -> (source: ISourceText).GetLineString(i - 1))) - - notifications.Trigger(NotificationEvent.UnusedOpens(filePath, (unused |> List.toArray)), ct) - with e -> - logger.error (Log.setMessage "checkUnusedOpens failed" >> Log.addExn e) - } - - let checkUnusedDeclarations = - async { - try - let! ct = Async.CancellationToken - let isScript = Utils.isAScript (UMX.untag filePath) - let! unused = UnusedDeclarations.getUnusedDeclarations (tyRes.GetCheckResults, isScript) - let unused = unused |> Seq.toArray - - notifications.Trigger(NotificationEvent.UnusedDeclarations(filePath, unused), ct) - with e -> - logger.error (Log.setMessage "checkUnusedDeclarations failed" >> Log.addExn e) - } - - let checkSimplifiedNames = - async { - try - let getSourceLine lineNo = - (source: ISourceText).GetLineString(lineNo - 1) - - let! ct = Async.CancellationToken - let! simplified = SimplifyNames.getSimplifiableNames (tyRes.GetCheckResults, getSourceLine) - let simplified = Array.ofSeq simplified - notifications.Trigger(NotificationEvent.SimplifyNames(filePath, simplified), ct) - with e -> - logger.error (Log.setMessage "checkSimplifiedNames failed" >> Log.addExn e) - } - - let analyzers = - [ - // if config.Linter then - // commands.Lint filePath |> Async .Ignore - if config.UnusedOpensAnalyzer then - checkUnusedOpens - if config.UnusedDeclarationsAnalyzer then - checkUnusedDeclarations - if config.SimplifyNameAnalyzer then - checkSimplifiedNames ] - - async { - do! analyzers |> Async.parallel75 |> Async.Ignore - - do! - lspClient.NotifyDocumentAnalyzed - { TextDocument = - { Uri = filePath |> Path.LocalPathToUri - Version = version } } - } - /// Gets Parse and Check results of a given file while also handling other concerns like Progress, Logging, Eventing. /// The FSharpCompilerServiceChecker. /// The name of the file in the project whose source to find a typecheck. /// The options for the project or script. - /// The FSharpConfig. /// Determines if the typecheck should be cached for autocompletions. /// - let parseAndCheckFile (checker: FSharpCompilerServiceChecker) (file: VolatileFile) options config shouldCache = + let parseAndCheckFile (checker: FSharpCompilerServiceChecker) (file: VolatileFile) options shouldCache = async { let tags = [ SemanticConventions.fsac_sourceCodePath, box (UMX.untag file.Lines.FileName) @@ -1200,7 +1201,6 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ct ) - Async.Start(analyzeFile config (file.Lines.FileName, file.Version, file.Lines, parseAndCheck), ct) return parseAndCheck } @@ -1215,11 +1215,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let checker = checker |> AVal.force - let config = config |> AVal.force match! forceFindOpenFileOrRead filePath with // Don't cache for autocompletions as we really only want to cache "Opened" files. - | Ok(fileInfo) -> return! parseAndCheckFile checker fileInfo opts config false |> Async.Ignore + | Ok(fileInfo) -> return! parseAndCheckFile checker fileInfo opts false |> Async.Ignore | _ -> () with e -> @@ -1272,7 +1271,6 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar asyncAVal { let file = info.Lines.FileName let! checker = checker - and! config = config return! taskOption { @@ -1280,7 +1278,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar and! cts = tryGetOpenFileToken file return! - parseAndCheckFile checker info opts config true + parseAndCheckFile checker info opts true |> Async.withCancellation cts.Token |> fun work -> Async.StartImmediateAsTask(work, ctok) } @@ -1774,8 +1772,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let mutable checksCompleted = 0 - let progressToken = ProgressToken.Second(Guid.NewGuid().ToString()) - do! lspClient.WorkDoneProgressCreate progressToken |> Async.Ignore + // let progressToken = ProgressToken.Second(Guid.NewGuid().ToString()) + // do! lspClient.WorkDoneProgressCreate progressToken |> Async.Ignore> use progressReporter = new ServerProgressReport(lspClient) let percentage numerator denominator = From e810de0de92815b6bf3e8aedb64ee58d4b7179b3 Mon Sep 17 00:00:00 2001 From: Jimmy Byrd Date: Wed, 5 Apr 2023 21:26:18 -0400 Subject: [PATCH 07/10] Refactorings --- src/FsAutoComplete.Core/AdaptiveExtensions.fs | 11 +- .../LspServers/AdaptiveFSharpLspServer.fs | 261 +++++++----------- .../LspServers/FSharpLspClient.fs | 2 +- 3 files changed, 114 insertions(+), 160 deletions(-) diff --git a/src/FsAutoComplete.Core/AdaptiveExtensions.fs b/src/FsAutoComplete.Core/AdaptiveExtensions.fs index 7c8db2913..06cfc0dbf 100644 --- a/src/FsAutoComplete.Core/AdaptiveExtensions.fs +++ b/src/FsAutoComplete.Core/AdaptiveExtensions.fs @@ -110,9 +110,14 @@ module AVal = /// Creates an observable with the given object and will be executed whenever the object gets marked out-of-date. Note that it does not trigger when the object is currently out-of-date. /// /// The aval to get out-of-date information from. - /// An observable - let onWeakMarking (aval: #aval<_>) = - Observable.Create(fun (obs: IObserver) -> aval.AddWeakMarkingCallback(obs.OnNext)) + let onOutOfDateWeak (aval: #aval<_>) = + Observable.Create(fun (obs: IObserver<_>) -> aval.AddWeakMarkingCallback(fun _ -> obs.OnNext aval)) + + + /// Creates an observable on the aval that will be executed whenever the avals value changed. + /// The aval to get out-of-date information from. + let onValueChangedWeak (aval: #aval<_>) = + Observable.Create(fun (obs: IObserver<_>) -> aval.AddCallback(obs.OnNext)) module ASet = /// Creates an amap with the keys from the set and the values given by mapping and diff --git a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs index 19f0104ab..da5256285 100644 --- a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs +++ b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs @@ -57,6 +57,8 @@ type AdaptiveWorkspaceChosen = type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FSharpLspClient) = + let logger = LogProvider.getLoggerFor () + let thisType = typeof let disposables = new Disposables.CompositeDisposable() @@ -65,8 +67,6 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let config = cval FSharpConfig.Default - let analyzersEnabled = config |> AVal.map (fun c -> c.EnableAnalyzers) - let checker = config |> AVal.map (fun c -> c.EnableAnalyzers, c.Fsac.CachedTypeCheckCount) @@ -79,7 +79,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let mutable traceNotifications: ProgressListener option = None - let replaceTraceNotification shouldTrace traceNamespaces = + /// Toggles trace notifications on or off. + /// Determines if tracing should occur + /// The namespaces to start tracing + /// + let toggleTraceNotification shouldTrace traceNamespaces = traceNotifications |> Option.iter dispose if shouldTrace then @@ -87,86 +91,107 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar else traceNotifications <- None - let mutableConfigChanges = + /// Sets tje FSI arguments on the FSharpCompilerServiceChecker + /// + /// Compiler tool locations + /// Any extra parameters to pass to FSI + let setFSIArgs + (checker: FSharpCompilerServiceChecker) + (fsiCompilerToolLocations: string array) + (fsiExtraParameters: seq) + = let toCompilerToolArgument (path: string) = sprintf "--compilertool:%s" path - aval { - let! config = config - and! checker = checker - and! rootPath = rootPath + checker.SetFSIAdditionalArguments + [| yield! fsiCompilerToolLocations |> Array.map toCompilerToolArgument + yield! fsiExtraParameters |] - replaceTraceNotification config.Notifications.Trace config.Notifications.TraceNamespaces + /// Loads F# Analyzers from the configured directories + /// The FSharpConfig + /// The RootPath + /// + let loadAnalyzers (config: FSharpConfig) (rootPath: string option) = + if config.EnableAnalyzers then + Loggers.analyzers.info (Log.setMessageI $"Using analyzer roots of {config.AnalyzersPath:roots}") + + config.AnalyzersPath + |> Array.iter (fun analyzerPath -> + match rootPath with + | None -> () + | Some workspacePath -> + let dir = + if + System.IO.Path.IsPathRooted analyzerPath + // if analyzer is using absolute path, use it as is + then + analyzerPath + // otherwise, it is a relative path and should be combined with the workspace path + else + System.IO.Path.Combine(workspacePath, analyzerPath) - checker.SetFSIAdditionalArguments - [| yield! config.FSICompilerToolLocations |> Array.map toCompilerToolArgument - yield! config.FSIExtraParameters |] + Loggers.analyzers.info (Log.setMessageI $"Loading analyzers from {dir:dir}") - if config.EnableAnalyzers then - Loggers.analyzers.info ( - Log.setMessage "Using analyzer roots of {roots}" - >> Log.addContextDestructured "roots" config.AnalyzersPath - ) + let (dllCount, analyzerCount) = dir |> FSharp.Analyzers.SDK.Client.loadAnalyzers - config.AnalyzersPath - |> Array.iter (fun analyzerPath -> - match rootPath with - | None -> () - | Some workspacePath -> - let dir = - if - System.IO.Path.IsPathRooted analyzerPath - // if analyzer is using absolute path, use it as is - then - analyzerPath - // otherwise, it is a relative path and should be combined with the workspace path - else - System.IO.Path.Combine(workspacePath, analyzerPath) + Loggers.analyzers.info ( + Log.setMessageI + $"From {analyzerPath:name}: {dllCount:dllNo} dlls including {analyzerCount:analyzersNo} analyzers" + )) - Loggers.analyzers.info ( - Log.setMessage "Loading analyzers from {dir}" - >> Log.addContextDestructured "dir" dir - ) + else + Loggers.analyzers.info (Log.setMessage "Analyzers disabled") - let (n, m) = dir |> FSharp.Analyzers.SDK.Client.loadAnalyzers + /// + /// the FSharpCompilerServiceChecker + /// The path to dotnet + /// The root path + /// + let setDotnetRoot (checker: FSharpCompilerServiceChecker) (dotnetRoot: string) (rootPath: string option) = + let di = DirectoryInfo dotnetRoot + + if di.Exists then + let dotnetBinary = + if + System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(Runtime.InteropServices.OSPlatform.Windows) + then + FileInfo(Path.Combine(di.FullName, "dotnet.exe")) + else + FileInfo(Path.Combine(di.FullName, "dotnet")) - Loggers.analyzers.info ( - Log.setMessage "From {name}: {dllNo} dlls including {analyzersNo} analyzers" - >> Log.addContextDestructured "name" analyzerPath - >> Log.addContextDestructured "dllNo" n - >> Log.addContextDestructured "analyzersNo" m - )) + if dotnetBinary.Exists then + checker.SetDotnetRoot(dotnetBinary, defaultArg rootPath System.Environment.CurrentDirectory |> DirectoryInfo) - else - Loggers.analyzers.info (Log.setMessage "Analyzers disabled") + else + // if we were mistakenly given the path to a dotnet binary + // then use the parent directory as the dotnet root instead + let fi = FileInfo(di.FullName) - let di = DirectoryInfo config.DotNetRoot + if fi.Exists && (fi.Name = "dotnet" || fi.Name = "dotnet.exe") then + checker.SetDotnetRoot(fi, defaultArg rootPath System.Environment.CurrentDirectory |> DirectoryInfo) - if di.Exists then - let dotnetBinary = - if - System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(Runtime.InteropServices.OSPlatform.Windows) - then - FileInfo(Path.Combine(di.FullName, "dotnet.exe")) - else - FileInfo(Path.Combine(di.FullName, "dotnet")) + let configChanges = + aval { + let! config = config + and! checker = checker + and! rootPath = rootPath - if dotnetBinary.Exists then - checker.SetDotnetRoot(dotnetBinary, defaultArg rootPath System.Environment.CurrentDirectory |> DirectoryInfo) + return config, checker, rootPath + } - else - // if we were mistakenly given the path to a dotnet binary - // then use the parent directory as the dotnet root instead - let fi = FileInfo(di.FullName) + // Syncs config changes to the mutable world + do + AVal.Observable.onValueChangedWeak configChanges + |> Observable.subscribe (fun (config, checker, rootPath) -> + toggleTraceNotification config.Notifications.Trace config.Notifications.TraceNamespaces - if fi.Exists && (fi.Name = "dotnet" || fi.Name = "dotnet.exe") then - checker.SetDotnetRoot(fi, defaultArg rootPath System.Environment.CurrentDirectory |> DirectoryInfo) + setFSIArgs checker config.FSICompilerToolLocations config.FSIExtraParameters - return () - } + loadAnalyzers config rootPath - let updateConfig c = - transact (fun () -> config.Value <- c) - mutableConfigChanges |> AVal.force + setDotnetRoot checker config.DotNetRoot rootPath) + |> disposables.Add + + let updateConfig c = transact (fun () -> config.Value <- c) let tfmConfig = config @@ -176,14 +201,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar else FSIRefs.TFM.NetFx) - let logger = LogProvider.getLoggerFor () let sendDiagnostics (uri: DocumentUri) (diags: Diagnostic[]) = - logger.info ( - Log.setMessage "SendDiag for {file}: {diags} entries" - >> Log.addContextDestructured "file" uri - >> Log.addContextDestructured "diags" diags.Length - ) + logger.info (Log.setMessageI $"SendDiag for {uri:file}: {diags.Length:diags} entries") { Uri = uri; Diagnostics = diags } |> lspClient.TextDocumentPublishDiagnostics @@ -309,7 +329,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let config = config |> AVal.force do! builtInCompilerAnalyzers config (volatileFile, parseAndCheck) - if analyzersEnabled |> AVal.force then + if config.EnableAnalyzers then let file = volatileFile.FileName try @@ -785,10 +805,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do // Reload Projects with some debouncing if `loadedProjectOptions` is out of date. - AVal.Observable.onWeakMarking loadedProjectOptions + AVal.Observable.onOutOfDateWeak loadedProjectOptions |> Observable.throttleOn Concurrency.NewThreadScheduler.Default (TimeSpan.FromMilliseconds(200.)) |> Observable.observeOn Concurrency.NewThreadScheduler.Default - |> Observable.subscribe (forceLoadProjects >> ignore>) + |> Observable.subscribe (fun _ -> forceLoadProjects () |> ignore>) |> disposables.Add @@ -1027,7 +1047,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> Array.ofList |> Array.Parallel.collect (fun p -> let parseOpts = Utils.projectOptionsToParseOptions p - p.SourceFiles |> Array.map (fun s -> p, parseOpts, s)) + p.SourceFiles |> Array.Parallel.map (fun s -> p, parseOpts, s)) |> Array.Parallel.map (fun (opts, parseOpts, fileName) -> let fileName = UMX.tag fileName @@ -1037,8 +1057,6 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar } |> Async.map Result.toOption) |> Async.parallel75 - |> Async.map (Array.Parallel.choose id) - } let forceFindSourceText filePath = @@ -1341,7 +1359,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> AsyncResult.orElseWith (fun _ -> forceGetRecentTypeCheckResults filePath) |> AsyncResult.orElseWith (fun _ -> forceGetTypeCheckResults filePath) |> Async.map (fun r -> - Async.Start(forceGetTypeCheckResults filePath |> Async.Ignore) + Async.Start( + forceGetTypeCheckResults filePath + |> Async.Ignore> + ) + r) } |> AsyncAVal.forceAsync @@ -1497,7 +1519,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! checker.FindReferencesForSymbolInFile(UMX.untag file, project, symbol) else // untitled script files - match! forceGetRecentTypeCheckResults file with + match! forceGetTypeCheckResultsStale file with | Error _ -> return [||] | Ok tyRes -> let! ct = Async.CancellationToken @@ -1508,20 +1530,6 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let tryGetProjectOptionsForFsproj (file: string) = forceGetProjectOptions file |> Async.map Option.ofResult - let getAllProjectOptions () = - async { - let! set = - allProjectOptions - |> AMap.toASetValues - |> ASet.force - |> HashSet.toArray - |> Array.map (AsyncAVal.forceAsync) - |> Async.Parallel - - return set |> Array.collect (List.toArray) - } - - Commands.symbolUseWorkspace getDeclarationLocation findReferencesForSymbolInFile @@ -1826,67 +1834,6 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar } - let getDeclarationLocation (symbolUse, text) = - let getProjectOptions file = - getProjectOptionsForFile file |> AsyncAVal.forceAsync |> Async.map selectProject - - let projectsThatContainFile file = - getProjectOptionsForFile file |> AsyncAVal.forceAsync - - SymbolLocation.getDeclarationLocation ( - symbolUse, - text, - getProjectOptions, - projectsThatContainFile, - getDependentProjectsOfProjects - ) - - let symbolUseWorkspace - (includeDeclarations: bool) - (includeBackticks: bool) - (errorOnFailureToFixRange: bool) - pos - lineStr - text - tyRes - = - - let findReferencesForSymbolInFile (file: string, project, symbol) = - async { - let checker = checker |> AVal.force - - if File.Exists(UMX.untag file) then - // `FSharpChecker.FindBackgroundReferencesInFile` only works with existing files - return! checker.FindReferencesForSymbolInFile(UMX.untag file, project, symbol) - else - // untitled script files - match! forceGetRecentTypeCheckResults file with - | Error _ -> return [||] - | Ok tyRes -> - let! ct = Async.CancellationToken - let usages = tyRes.GetCheckResults.GetUsesOfSymbolInFile(symbol, ct) - return usages |> Seq.map (fun u -> u.Range) - } - - let tryGetProjectOptionsForFsproj (file: string) = - forceGetProjectOptions file |> Async.map Option.ofResult - - let getAllProjectOptions () = - getAllProjectOptions () |> Async.map Array.toSeq - - Commands.symbolUseWorkspace - getDeclarationLocation - findReferencesForSymbolInFile - forceFindSourceText - tryGetProjectOptionsForFsproj - getAllProjectOptions - includeDeclarations - includeBackticks - errorOnFailureToFixRange - pos - lineStr - text - tyRes member private x.handleSemanticTokens (filePath: string) range : AsyncLspResult = asyncResult { @@ -4776,7 +4723,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! LspResult.internalError (string e) } - override x.Dispose() = disposables.Dispose() + override x.Dispose() = + traceNotifications |> Option.iter (dispose) + disposables.Dispose() member this.WorkDoneProgessCancel(token: ProgressToken) : Async = async { diff --git a/src/FsAutoComplete/LspServers/FSharpLspClient.fs b/src/FsAutoComplete/LspServers/FSharpLspClient.fs index c5aeb03a4..dc3b9954b 100644 --- a/src/FsAutoComplete/LspServers/FSharpLspClient.fs +++ b/src/FsAutoComplete/LspServers/FSharpLspClient.fs @@ -148,7 +148,7 @@ open System.Diagnostics open Ionide.ProjInfo.Logging -/// listener for the the events generated from the fsc ActivitySource +/// listener for the the events generated from the fsc ActivitySource type ProgressListener(lspClient: FSharpLspClient, traceNamespace: string array) = let isOneOf list string = From f93c45e011e76de93e1d68ae1568d1a95b46b95d Mon Sep 17 00:00:00 2001 From: Jimmy Byrd Date: Wed, 5 Apr 2023 21:48:40 -0400 Subject: [PATCH 08/10] Update paket, pin dependencies --- .config/dotnet-tools.json | 4 +- paket.dependencies | 35 ++--- paket.lock | 262 +++++++++++++++++++------------------- 3 files changed, 152 insertions(+), 149 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 46f03f417..4a9d31988 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -9,7 +9,7 @@ ] }, "paket": { - "version": "7.2.0-alpha001", + "version": "7.2.1", "commands": [ "paket" ] @@ -33,4 +33,4 @@ ] } } -} \ No newline at end of file +} diff --git a/paket.dependencies b/paket.dependencies index a8003c397..cda9daa8b 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -1,24 +1,26 @@ -version 7.2.0-alpha001 +version 7.2.1 -framework: netstandard2.0, net6.0, net7.0 +framework: netstandard2.0, netstandard2.1, net6.0, net7.0 source https://api.nuget.org/v3/index.json # this is the FCS nightly feed, re-enable at your own risk! #source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json #source: ./libs storage: none +strategy: min +lowest_matching: true -nuget Fantomas.Client -nuget FSharp.Compiler.Service -nuget Ionide.ProjInfo -nuget Ionide.ProjInfo.FCS -nuget Ionide.ProjInfo.ProjectSystem -nuget Ionide.ProjInfo.Sln -nuget Microsoft.Build copy_local:false -nuget Microsoft.Build.Framework copy_local:false -nuget Microsoft.Build.Utilities.Core copy_local:false -nuget Microsoft.Build.Tasks.Core copy_local: false +nuget Fantomas.Client >= 0.9 +nuget FSharp.Compiler.Service >= 43.7.200 +nuget Ionide.ProjInfo >= 0.61.3 +nuget Ionide.ProjInfo.FCS >= 0.61.3 +nuget Ionide.ProjInfo.ProjectSystem >= 0.61.3 +nuget Ionide.ProjInfo.Sln >= 0.61.3 +nuget Microsoft.Build >= 17.2 copy_local:false +nuget Microsoft.Build.Framework >= 17.4 copy_local:false +nuget Microsoft.Build.Utilities.Core >= 17.4 copy_local:false +nuget Microsoft.Build.Tasks.Core >= 17.4 copy_local: false nuget Nuget.Frameworks copy_local: false nuget FSharp.Analyzers.SDK nuget ICSharpCode.Decompiler @@ -34,7 +36,7 @@ nuget Serilog.Sinks.Async nuget Destructurama.FSharp nuget FSharp.UMX nuget FSharp.Formatting -nuget FsToolkit.ErrorHandling.TaskResult +nuget FsToolkit.ErrorHandling.TaskResult framework: netstandard2.1 ,net6.0, net7.0 nuget IcedTasks nuget FSharpx.Async nuget CliWrap @@ -50,9 +52,12 @@ nuget Expecto.Diff nuget YoloDev.Expecto.TestSdk nuget AltCover nuget GitHubActionsTestLogger -nuget Ionide.LanguageServerProtocol +nuget Ionide.LanguageServerProtocol >= 0.4.12 nuget Microsoft.Extensions.Caching.Memory -nuget OpenTelemetry.Exporter.OpenTelemetryProtocol +nuget OpenTelemetry.Exporter.OpenTelemetryProtocol >= 1.3.2 + + + group Build source https://api.nuget.org/v3/index.json diff --git a/paket.lock b/paket.lock index f316260b2..b10707200 100644 --- a/paket.lock +++ b/paket.lock @@ -1,13 +1,15 @@ STORAGE: NONE -RESTRICTION: || (== net6.0) (== net7.0) (== netstandard2.0) +STRATEGY: MIN +LOWEST_MATCHING: TRUE +RESTRICTION: || (== net6.0) (== net7.0) (== netstandard2.0) (== netstandard2.1) NUGET remote: https://api.nuget.org/v3/index.json altcover (8.3.838) CliWrap (3.4.4) - Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) - System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) - System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) + Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net461)) + System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net461)) + System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net461)) + System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net461)) Destructurama.FSharp (1.2) FSharp.Core (>= 4.3.4) Serilog (>= 2.0 < 3.0) @@ -28,12 +30,12 @@ NUGET FSharp.Core (>= 5.0.1) SemanticVersioning (>= 2.0.2) StreamJsonRpc (>= 2.8.28) - FParsec (1.1.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) + FParsec (1.1.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) FSharp.Core (>= 4.3.4) FSharp.Analyzers.SDK (0.11) - FSharp.Compiler.Service (>= 41.0.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) - FSharp.Core (>= 6.0.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) - McMaster.NETCore.Plugins (>= 1.4) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) + FSharp.Compiler.Service (>= 41.0.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) + FSharp.Core (>= 6.0.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) + McMaster.NETCore.Plugins (>= 1.4) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) FSharp.Compiler.Service (43.7.200) FSharp.Core (7.0.200) System.Buffers (>= 4.5.1) @@ -46,7 +48,7 @@ NUGET FSharp.Control.AsyncSeq (3.2.1) FSharp.Core (>= 4.7.2) Microsoft.Bcl.AsyncInterfaces (>= 5.0) - FSharp.Control.Reactive (5.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) + FSharp.Control.Reactive (5.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) FSharp.Core (>= 4.7.2) System.Reactive (>= 5.0 < 6.0) FSharp.Core (7.0.200) - content: none @@ -54,47 +56,46 @@ NUGET FSharp.Core (>= 4.7) System.Reflection.Emit.Lightweight (>= 4.6) FSharp.Formatting (14.0.1) - FSharp.Compiler.Service (>= 40.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netstandard2.1)) + FSharp.Compiler.Service (>= 40.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netstandard2.1)) (== netstandard2.1) FSharp.UMX (1.1) FSharp.Core (>= 4.3.4) FSharpLint.Core (0.21.2) - FParsec (>= 1.1.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) - FSharp.Compiler.Service (>= 40.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) - FSharp.Core (>= 5.0.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) - Ionide.ProjInfo (>= 0.53.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) - Ionide.ProjInfo.FCS (>= 0.53.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) - Ionide.ProjInfo.ProjectSystem (>= 0.53.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) - Microsoft.Build (>= 16.10) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) - Microsoft.Build.Framework (>= 16.10) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) - Microsoft.Build.Locator (>= 1.4.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) - Microsoft.Build.Tasks.Core (>= 16.10) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) - Microsoft.Build.Utilities.Core (>= 16.10) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) - Newtonsoft.Json (>= 13.0.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) + FParsec (>= 1.1.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) + FSharp.Compiler.Service (>= 40.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) + FSharp.Core (>= 5.0.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) + Ionide.ProjInfo (>= 0.53.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) + Ionide.ProjInfo.FCS (>= 0.53.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) + Ionide.ProjInfo.ProjectSystem (>= 0.53.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) + Microsoft.Build (>= 16.10) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) + Microsoft.Build.Framework (>= 16.10) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) + Microsoft.Build.Locator (>= 1.4.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) + Microsoft.Build.Tasks.Core (>= 16.10) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) + Microsoft.Build.Utilities.Core (>= 16.10) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) + Newtonsoft.Json (>= 13.0.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) FSharpx.Async (1.14.1) FSharp.Control.AsyncSeq (>= 2.0.21) FSharp.Core (>= 4.6.2) - FsToolkit.ErrorHandling (4.4) - FSharp.Core (>= 4.7.2) - restriction: || (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) - FSharp.Core (>= 7.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netstandard2.1)) - FsToolkit.ErrorHandling.TaskResult (4.4) + FsToolkit.ErrorHandling (4.4) - restriction: || (== net6.0) (== net7.0) (== netstandard2.1) + FSharp.Core (>= 7.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netstandard2.1)) (== netstandard2.1) + FsToolkit.ErrorHandling.TaskResult (4.4) - restriction: || (== net6.0) (== net7.0) (== netstandard2.1) FsToolkit.ErrorHandling (>= 4.4) - Ply (>= 0.3.1) - restriction: || (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) GitHubActionsTestLogger (2.0.1) Microsoft.TestPlatform.ObjectModel (>= 17.2) Google.Protobuf (3.22) - System.Memory (>= 4.5.3) - restriction: || (&& (== net6.0) (>= net45)) (&& (== net6.0) (< net5.0)) (&& (== net6.0) (< netstandard2.0)) (&& (== net7.0) (>= net45)) (&& (== net7.0) (< net5.0)) (&& (== net7.0) (< netstandard2.0)) (== netstandard2.0) - System.Runtime.CompilerServices.Unsafe (>= 4.5.2) - restriction: || (&& (== net6.0) (< net5.0)) (&& (== net7.0) (< net5.0)) (== netstandard2.0) - Grpc (2.46.6) - restriction: || (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net462)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) + System.Memory (>= 4.5.3) - restriction: || (&& (== net6.0) (>= net45)) (&& (== net6.0) (< net5.0)) (&& (== net6.0) (< netstandard2.0)) (&& (== net7.0) (>= net45)) (&& (== net7.0) (< net5.0)) (&& (== net7.0) (< netstandard2.0)) (== netstandard2.0) (== netstandard2.1) + System.Runtime.CompilerServices.Unsafe (>= 4.5.2) - restriction: || (&& (== net6.0) (< net5.0)) (&& (== net7.0) (< net5.0)) (== netstandard2.0) (== netstandard2.1) + Grpc (2.46.6) - restriction: || (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net462)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net462)) Grpc.Core (>= 2.46.6) - Grpc.Core (2.46.6) - restriction: || (&& (== net6.0) (>= net45)) (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net45)) (&& (== net7.0) (>= net462)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) + Grpc.Core (2.46.6) - restriction: || (&& (== net6.0) (>= net45)) (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net45)) (&& (== net7.0) (>= net462)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net462)) Grpc.Core.Api (>= 2.46.6) System.Memory (>= 4.5.3) Grpc.Core.Api (2.51) System.Memory (>= 4.5.3) - Grpc.Net.Client (2.51) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netstandard2.1)) + Grpc.Net.Client (2.51) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netstandard2.1)) (== netstandard2.1) Grpc.Net.Common (>= 2.51) Microsoft.Extensions.Logging.Abstractions (>= 3.0.3) - Grpc.Net.Common (2.51) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netstandard2.1)) + System.Diagnostics.DiagnosticSource (>= 4.5.1) - restriction: || (&& (== net6.0) (< net5.0)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (< net5.0)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) (== netstandard2.1) + Grpc.Net.Common (2.51) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netstandard2.1)) (== netstandard2.1) Grpc.Core.Api (>= 2.51) IcedTasks (0.5.3) FSharp.Core (>= 7.0) @@ -108,58 +109,58 @@ NUGET Newtonsoft.Json (>= 13.0.1) StreamJsonRpc (>= 2.10.44) Ionide.ProjInfo (0.61.3) - FSharp.Core (>= 6.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - Ionide.ProjInfo.Sln (>= 0.61.3) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - Microsoft.Build (>= 17.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - Microsoft.Build.Framework (>= 17.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - SemanticVersioning (>= 2.0.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) + FSharp.Core (>= 6.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) + Ionide.ProjInfo.Sln (>= 0.61.3) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) + Microsoft.Build (>= 17.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) + Microsoft.Build.Framework (>= 17.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) + SemanticVersioning (>= 2.0.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) Ionide.ProjInfo.FCS (0.61.3) - FSharp.Compiler.Service (>= 41.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - FSharp.Core (>= 6.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - Ionide.ProjInfo (>= 0.61.3) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) + FSharp.Compiler.Service (>= 41.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) + FSharp.Core (>= 6.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) + Ionide.ProjInfo (>= 0.61.3) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) Ionide.ProjInfo.ProjectSystem (0.61.3) - FSharp.Compiler.Service (>= 41.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - FSharp.Control.Reactive (>= 5.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - FSharp.Core (>= 6.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - Ionide.ProjInfo (>= 0.61.3) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - Ionide.ProjInfo.FCS (>= 0.61.3) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - Ionide.ProjInfo.Sln (>= 0.61.3) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - Newtonsoft.Json (>= 13.0.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) + FSharp.Compiler.Service (>= 41.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) + FSharp.Control.Reactive (>= 5.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) + FSharp.Core (>= 6.0.5) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) + Ionide.ProjInfo (>= 0.61.3) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) + Ionide.ProjInfo.FCS (>= 0.61.3) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) + Ionide.ProjInfo.Sln (>= 0.61.3) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) + Newtonsoft.Json (>= 13.0.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) Ionide.ProjInfo.Sln (0.61.3) - McMaster.NETCore.Plugins (1.4) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) - Microsoft.DotNet.PlatformAbstractions (>= 3.1.6) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) - Microsoft.Extensions.DependencyModel (>= 5.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) + McMaster.NETCore.Plugins (1.4) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) + Microsoft.DotNet.PlatformAbstractions (>= 3.1.6) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) (&& (== netstandard2.1) (>= netcoreapp2.1)) + Microsoft.Extensions.DependencyModel (>= 5.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp2.1)) (&& (== netstandard2.1) (>= netcoreapp2.1)) MessagePack (2.4.35) MessagePack.Annotations (>= 2.4.35) - Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) + Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) (== netstandard2.1) Microsoft.NET.StringTools (>= 1.0) - System.Collections.Immutable (>= 1.7.1) - restriction: || (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) - System.Reflection.Emit (>= 4.7) - restriction: || (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) - System.Reflection.Emit.Lightweight (>= 4.7) - restriction: || (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) - System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) + System.Collections.Immutable (>= 1.7.1) - restriction: || (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) (== netstandard2.1) + System.Reflection.Emit (>= 4.7) - restriction: || (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) (== netstandard2.1) + System.Reflection.Emit.Lightweight (>= 4.7) - restriction: || (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) (== netstandard2.1) + System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) (== netstandard2.1) MessagePack.Annotations (2.4.35) Microsoft.Bcl.AsyncInterfaces (6.0) - System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) + System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net461)) Microsoft.Build (17.2) - copy_local: false - Microsoft.Build.Framework (>= 17.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0)) - Microsoft.NET.StringTools (>= 1.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0)) - Microsoft.Win32.Registry (>= 4.3) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - System.Collections.Immutable (>= 5.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0)) - System.Configuration.ConfigurationManager (>= 4.7) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0)) - System.Reflection.Metadata (>= 1.6) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - System.Security.Principal.Windows (>= 4.7) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - System.Text.Encoding.CodePages (>= 4.0.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) - System.Text.Json (>= 6.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0)) - System.Threading.Tasks.Dataflow (>= 6.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0)) + Microsoft.Build.Framework (>= 17.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net472)) (&& (== netstandard2.1) (>= net6.0)) + Microsoft.NET.StringTools (>= 1.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net472)) (&& (== netstandard2.1) (>= net6.0)) + Microsoft.Win32.Registry (>= 4.3) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) + System.Collections.Immutable (>= 5.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net472)) (&& (== netstandard2.1) (>= net6.0)) + System.Configuration.ConfigurationManager (>= 4.7) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net472)) (&& (== netstandard2.1) (>= net6.0)) + System.Reflection.Metadata (>= 1.6) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) + System.Security.Principal.Windows (>= 4.7) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) + System.Text.Encoding.CodePages (>= 4.0.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) + System.Text.Json (>= 6.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net472)) (&& (== netstandard2.1) (>= net6.0)) + System.Threading.Tasks.Dataflow (>= 6.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net472)) (&& (== netstandard2.1) (>= net6.0)) Microsoft.Build.Framework (17.4) - copy_local: false - Microsoft.Win32.Registry (>= 5.0) - restriction: || (== net6.0) (== netstandard2.0) + Microsoft.Win32.Registry (>= 5.0) - restriction: || (== net6.0) (== netstandard2.0) (== netstandard2.1) System.Security.Permissions (>= 6.0) - Microsoft.Build.Locator (1.5.3) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) + Microsoft.Build.Locator (1.5.3) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) Microsoft.Build.Tasks.Core (17.4) - copy_local: false Microsoft.Build.Framework (>= 17.4) Microsoft.Build.Utilities.Core (>= 17.4) Microsoft.NET.StringTools (>= 17.4) - Microsoft.Win32.Registry (>= 5.0) - restriction: || (== net6.0) (== netstandard2.0) + Microsoft.Win32.Registry (>= 5.0) - restriction: || (== net6.0) (== netstandard2.0) (== netstandard2.1) System.CodeDom (>= 6.0) System.Collections.Immutable (>= 6.0) System.Reflection.Metadata (>= 6.0) @@ -172,13 +173,13 @@ NUGET Microsoft.Build.Utilities.Core (17.4) - copy_local: false Microsoft.Build.Framework (>= 17.4) Microsoft.NET.StringTools (>= 17.4) - Microsoft.Win32.Registry (>= 5.0) - restriction: || (== net6.0) (== netstandard2.0) + Microsoft.Win32.Registry (>= 5.0) - restriction: || (== net6.0) (== netstandard2.0) (== netstandard2.1) System.Collections.Immutable (>= 6.0) System.Configuration.ConfigurationManager (>= 6.0) - System.Security.Permissions (>= 6.0) - restriction: || (== net6.0) (== netstandard2.0) - System.Text.Encoding.CodePages (>= 6.0) - restriction: || (== net6.0) (== netstandard2.0) - Microsoft.CodeCoverage (17.4.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net462)) (&& (== netstandard2.0) (>= netcoreapp3.1)) - Microsoft.DotNet.PlatformAbstractions (3.1.6) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) + System.Security.Permissions (>= 6.0) - restriction: || (== net6.0) (== netstandard2.0) (== netstandard2.1) + System.Text.Encoding.CodePages (>= 6.0) - restriction: || (== net6.0) (== netstandard2.0) (== netstandard2.1) + Microsoft.CodeCoverage (17.4.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net462)) (&& (== netstandard2.0) (>= netcoreapp3.1)) (&& (== netstandard2.1) (>= net462)) (&& (== netstandard2.1) (>= netcoreapp3.1)) + Microsoft.DotNet.PlatformAbstractions (3.1.6) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) Microsoft.Extensions.Caching.Abstractions (6.0) Microsoft.Extensions.Primitives (>= 6.0) Microsoft.Extensions.Caching.Memory (6.0.1) @@ -195,29 +196,29 @@ NUGET Microsoft.Extensions.Configuration.Binder (6.0) Microsoft.Extensions.Configuration.Abstractions (>= 6.0) Microsoft.Extensions.DependencyInjection (6.0.1) - Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) + Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net461)) Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0) System.Runtime.CompilerServices.Unsafe (>= 6.0) - System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) + System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net461)) Microsoft.Extensions.DependencyInjection.Abstractions (6.0) - Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) - System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) - Microsoft.Extensions.DependencyModel (6.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) + Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net461)) + System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net461)) + Microsoft.Extensions.DependencyModel (6.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net5.0)) (&& (== netstandard2.1) (>= net5.0)) System.Buffers (>= 4.5.1) System.Memory (>= 4.5.4) System.Runtime.CompilerServices.Unsafe (>= 6.0) System.Text.Encodings.Web (>= 6.0) System.Text.Json (>= 6.0) Microsoft.Extensions.Logging (6.0) - Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) + Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net461)) Microsoft.Extensions.DependencyInjection (>= 6.0) Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0) Microsoft.Extensions.Logging.Abstractions (>= 6.0) Microsoft.Extensions.Options (>= 6.0) System.Diagnostics.DiagnosticSource (>= 6.0) Microsoft.Extensions.Logging.Abstractions (6.0.2) - System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net6.0)) (== netstandard2.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net6.0)) (== netstandard2.0) + System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net6.0)) (== netstandard2.0) (== netstandard2.1) + System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net6.0)) (== netstandard2.0) (== netstandard2.1) Microsoft.Extensions.Logging.Configuration (6.0) Microsoft.Extensions.Configuration (>= 6.0) Microsoft.Extensions.Configuration.Abstractions (>= 6.0) @@ -238,14 +239,14 @@ NUGET Microsoft.Extensions.Options (>= 6.0) Microsoft.Extensions.Primitives (>= 6.0) Microsoft.Extensions.Primitives (6.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) + System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) (== netstandard2.1) System.Runtime.CompilerServices.Unsafe (>= 6.0) Microsoft.NET.StringTools (17.4) - copy_local: false System.Memory (>= 4.5.5) System.Runtime.CompilerServices.Unsafe (>= 6.0) Microsoft.NET.Test.Sdk (17.4.1) - Microsoft.CodeCoverage (>= 17.4.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net462)) (&& (== netstandard2.0) (>= netcoreapp3.1)) - Microsoft.TestPlatform.TestHost (>= 17.4.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) + Microsoft.CodeCoverage (>= 17.4.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net462)) (&& (== netstandard2.0) (>= netcoreapp3.1)) (&& (== netstandard2.1) (>= net462)) (&& (== netstandard2.1) (>= netcoreapp3.1)) + Microsoft.TestPlatform.TestHost (>= 17.4.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) (&& (== netstandard2.1) (>= netcoreapp3.1)) Microsoft.NETFramework.ReferenceAssemblies (1.0.3) Microsoft.SourceLink.AzureRepos.Git (1.1.1) - copy_local: true Microsoft.Build.Tasks.Git (>= 1.1.1) @@ -263,9 +264,9 @@ NUGET Microsoft.TestPlatform.ObjectModel (17.4.1) NuGet.Frameworks (>= 5.11) System.Reflection.Metadata (>= 1.6) - Microsoft.TestPlatform.TestHost (17.4.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) - Microsoft.TestPlatform.ObjectModel (>= 17.4.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) - Newtonsoft.Json (>= 13.0.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) + Microsoft.TestPlatform.TestHost (17.4.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) (&& (== netstandard2.1) (>= netcoreapp3.1)) + Microsoft.TestPlatform.ObjectModel (>= 17.4.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) (&& (== netstandard2.1) (>= netcoreapp3.1)) + Newtonsoft.Json (>= 13.0.1) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) (&& (== netstandard2.1) (>= netcoreapp3.1)) Microsoft.VisualStudio.Threading (17.3.44) Microsoft.Bcl.AsyncInterfaces (>= 6.0) Microsoft.VisualStudio.Threading.Analyzers (>= 17.3.44) @@ -275,11 +276,11 @@ NUGET Microsoft.VisualStudio.Threading.Analyzers (17.3.44) Microsoft.VisualStudio.Validation (17.0.64) Microsoft.Win32.Registry (5.0) - System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= monoandroid) (< netstandard1.3)) (&& (== net6.0) (>= monotouch)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (>= xamarinios)) (&& (== net6.0) (>= xamarinmac)) (&& (== net6.0) (>= xamarintvos)) (&& (== net6.0) (>= xamarinwatchos)) (&& (== net7.0) (>= monoandroid) (< netstandard1.3)) (&& (== net7.0) (>= monotouch)) (&& (== net7.0) (< netcoreapp2.0)) (&& (== net7.0) (>= xamarinios)) (&& (== net7.0) (>= xamarinmac)) (&& (== net7.0) (>= xamarintvos)) (&& (== net7.0) (>= xamarinwatchos)) (== netstandard2.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (>= uap10.1)) (&& (== net7.0) (< netcoreapp2.0)) (&& (== net7.0) (< netcoreapp2.1)) (&& (== net7.0) (>= uap10.1)) (== netstandard2.0) + System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= monoandroid) (< netstandard1.3)) (&& (== net6.0) (>= monotouch)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (>= xamarinios)) (&& (== net6.0) (>= xamarinmac)) (&& (== net6.0) (>= xamarintvos)) (&& (== net6.0) (>= xamarinwatchos)) (&& (== net7.0) (>= monoandroid) (< netstandard1.3)) (&& (== net7.0) (>= monotouch)) (&& (== net7.0) (< netcoreapp2.0)) (&& (== net7.0) (>= xamarinios)) (&& (== net7.0) (>= xamarinmac)) (&& (== net7.0) (>= xamarintvos)) (&& (== net7.0) (>= xamarinwatchos)) (== netstandard2.0) (== netstandard2.1) + System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (>= uap10.1)) (&& (== net7.0) (< netcoreapp2.0)) (&& (== net7.0) (< netcoreapp2.1)) (&& (== net7.0) (>= uap10.1)) (== netstandard2.0) (== netstandard2.1) System.Security.AccessControl (>= 5.0) System.Security.Principal.Windows (>= 5.0) - Microsoft.Win32.SystemEvents (6.0.1) - copy_local: false, restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) + Microsoft.Win32.SystemEvents (6.0.1) - copy_local: false, restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) (&& (== netstandard2.1) (>= netcoreapp3.1)) Mono.Cecil (0.11.4) Nerdbank.Streams (2.8.61) Microsoft.Bcl.AsyncInterfaces (>= 5.0) @@ -300,12 +301,9 @@ NUGET System.Reflection.Emit.Lightweight (>= 4.7) OpenTelemetry.Exporter.OpenTelemetryProtocol (1.3.2) Google.Protobuf (>= 3.19.4 < 4.0) - Grpc (>= 2.44 < 3.0) - restriction: || (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net462)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) - Grpc.Net.Client (>= 2.43 < 3.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netstandard2.1)) + Grpc (>= 2.44 < 3.0) - restriction: || (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net462)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net462)) + Grpc.Net.Client (>= 2.43 < 3.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netstandard2.1)) (== netstandard2.1) OpenTelemetry (>= 1.3.2) - Ply (0.3.1) - restriction: || (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) - FSharp.Core (>= 4.6.2) - System.Threading.Tasks.Extensions (>= 4.5.4) SemanticVersioning (2.0.2) Serilog (2.11) Serilog.Sinks.Async (1.5) @@ -327,77 +325,77 @@ NUGET System.Buffers (4.5.1) System.CodeDom (6.0) - copy_local: false System.Collections.Immutable (6.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net6.0)) (== netstandard2.0) + System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net6.0)) (== netstandard2.0) (== netstandard2.1) System.Runtime.CompilerServices.Unsafe (>= 6.0) System.CommandLine (2.0.0-beta4.22272.1) - System.Memory (>= 4.5.4) - restriction: || (&& (== net7.0) (< net6.0)) (== netstandard2.0) + System.Memory (>= 4.5.4) - restriction: || (&& (== net7.0) (< net6.0)) (== netstandard2.0) (== netstandard2.1) System.ComponentModel.Annotations (5.0) - restriction: || (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) System.Configuration.ConfigurationManager (6.0) System.Security.Cryptography.ProtectedData (>= 6.0) System.Security.Permissions (>= 6.0) System.Diagnostics.DiagnosticSource (6.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< net5.0)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net5.0)) (== netstandard2.0) + System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< net5.0)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net5.0)) (== netstandard2.0) (== netstandard2.1) System.Runtime.CompilerServices.Unsafe (>= 6.0) - System.Drawing.Common (6.0) - copy_local: false, restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) - Microsoft.Win32.SystemEvents (>= 6.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) + System.Drawing.Common (6.0) - copy_local: false, restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) (&& (== netstandard2.1) (>= netcoreapp3.1)) + Microsoft.Win32.SystemEvents (>= 6.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) (&& (== netstandard2.1) (>= netcoreapp3.1)) System.Formats.Asn1 (6.0) - copy_local: false - System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net6.0)) (== netstandard2.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net6.0)) (== netstandard2.0) + System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net6.0)) (== netstandard2.0) (== netstandard2.1) + System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net6.0)) (== netstandard2.0) (== netstandard2.1) System.IO.Pipelines (6.0.3) - System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) - System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) + System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) (== netstandard2.1) + System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) (== netstandard2.1) + System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) (== netstandard2.1) System.Memory (4.5.5) - System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= monotouch)) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= xamarinios)) (&& (== net6.0) (>= xamarinmac)) (&& (== net6.0) (>= xamarintvos)) (&& (== net6.0) (>= xamarinwatchos)) (&& (== net7.0) (>= monotouch)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netcoreapp2.0)) (&& (== net7.0) (< netstandard1.1)) (&& (== net7.0) (< netstandard2.0)) (&& (== net7.0) (>= xamarinios)) (&& (== net7.0) (>= xamarinmac)) (&& (== net7.0) (>= xamarintvos)) (&& (== net7.0) (>= xamarinwatchos)) (== netstandard2.0) - System.Numerics.Vectors (>= 4.4) - restriction: || (&& (== net6.0) (< netcoreapp2.0)) (&& (== net7.0) (< netcoreapp2.0)) (== netstandard2.0) - System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (== net6.0) (>= monotouch)) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= uap10.1)) (&& (== net6.0) (>= xamarinios)) (&& (== net6.0) (>= xamarinmac)) (&& (== net6.0) (>= xamarintvos)) (&& (== net6.0) (>= xamarinwatchos)) (&& (== net7.0) (>= monotouch)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netcoreapp2.0)) (&& (== net7.0) (< netcoreapp2.1)) (&& (== net7.0) (< netstandard1.1)) (&& (== net7.0) (< netstandard2.0)) (&& (== net7.0) (>= uap10.1)) (&& (== net7.0) (>= xamarinios)) (&& (== net7.0) (>= xamarinmac)) (&& (== net7.0) (>= xamarintvos)) (&& (== net7.0) (>= xamarinwatchos)) (== netstandard2.0) - System.Numerics.Vectors (4.5) - restriction: || (&& (== net6.0) (< netcoreapp2.0)) (&& (== net7.0) (< netcoreapp2.0)) (== netstandard2.0) - System.Reactive (5.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) + System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= monotouch)) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= xamarinios)) (&& (== net6.0) (>= xamarinmac)) (&& (== net6.0) (>= xamarintvos)) (&& (== net6.0) (>= xamarinwatchos)) (&& (== net7.0) (>= monotouch)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netcoreapp2.0)) (&& (== net7.0) (< netstandard1.1)) (&& (== net7.0) (< netstandard2.0)) (&& (== net7.0) (>= xamarinios)) (&& (== net7.0) (>= xamarinmac)) (&& (== net7.0) (>= xamarintvos)) (&& (== net7.0) (>= xamarinwatchos)) (== netstandard2.0) (== netstandard2.1) + System.Numerics.Vectors (>= 4.4) - restriction: || (&& (== net6.0) (< netcoreapp2.0)) (&& (== net7.0) (< netcoreapp2.0)) (== netstandard2.0) (== netstandard2.1) + System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (== net6.0) (>= monotouch)) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= uap10.1)) (&& (== net6.0) (>= xamarinios)) (&& (== net6.0) (>= xamarinmac)) (&& (== net6.0) (>= xamarintvos)) (&& (== net6.0) (>= xamarinwatchos)) (&& (== net7.0) (>= monotouch)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netcoreapp2.0)) (&& (== net7.0) (< netcoreapp2.1)) (&& (== net7.0) (< netstandard1.1)) (&& (== net7.0) (< netstandard2.0)) (&& (== net7.0) (>= uap10.1)) (&& (== net7.0) (>= xamarinios)) (&& (== net7.0) (>= xamarinmac)) (&& (== net7.0) (>= xamarintvos)) (&& (== net7.0) (>= xamarinwatchos)) (== netstandard2.0) (== netstandard2.1) + System.Numerics.Vectors (4.5) - restriction: || (&& (== net6.0) (< netcoreapp2.0)) (&& (== net7.0) (< netcoreapp2.0)) (== netstandard2.0) (== netstandard2.1) + System.Reactive (5.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0)) System.Reflection.Emit (4.7) - System.Reflection.Emit.ILGeneration (>= 4.7) - restriction: || (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= uap10.1)) (&& (== net7.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net7.0) (< netstandard1.1)) (&& (== net7.0) (< netstandard2.0)) (&& (== net7.0) (>= uap10.1)) (== netstandard2.0) - System.Reflection.Emit.ILGeneration (4.7) - restriction: || (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= uap10.1)) (&& (== net7.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net7.0) (< netstandard1.1)) (&& (== net7.0) (< netstandard2.0)) (&& (== net7.0) (>= uap10.1)) (== netstandard2.0) + System.Reflection.Emit.ILGeneration (>= 4.7) - restriction: || (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= uap10.1)) (&& (== net7.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net7.0) (< netstandard1.1)) (&& (== net7.0) (< netstandard2.0)) (&& (== net7.0) (>= uap10.1)) (== netstandard2.0) (&& (== netstandard2.1) (< netstandard1.1)) (&& (== netstandard2.1) (< netstandard2.0)) (&& (== netstandard2.1) (>= uap10.1)) + System.Reflection.Emit.ILGeneration (4.7) - restriction: || (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= uap10.1)) (&& (== net7.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net7.0) (< netstandard1.1)) (&& (== net7.0) (< netstandard2.0)) (&& (== net7.0) (>= uap10.1)) (== netstandard2.0) (&& (== netstandard2.1) (< netstandard1.1)) (&& (== netstandard2.1) (< netstandard2.0)) (&& (== netstandard2.1) (>= uap10.1)) System.Reflection.Emit.Lightweight (4.7) - System.Reflection.Emit.ILGeneration (>= 4.7) - restriction: || (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (< portable-net45+wp8)) (&& (== net6.0) (>= uap10.1)) (&& (== net7.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net7.0) (< netstandard2.0)) (&& (== net7.0) (< portable-net45+wp8)) (&& (== net7.0) (>= uap10.1)) (== netstandard2.0) + System.Reflection.Emit.ILGeneration (>= 4.7) - restriction: || (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (< portable-net45+wp8)) (&& (== net6.0) (>= uap10.1)) (&& (== net7.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net7.0) (< netstandard2.0)) (&& (== net7.0) (< portable-net45+wp8)) (&& (== net7.0) (>= uap10.1)) (== netstandard2.0) (&& (== netstandard2.1) (< netstandard2.0)) (&& (== netstandard2.1) (< portable-net45+wp8)) (&& (== netstandard2.1) (>= uap10.1)) System.Reflection.Metadata (6.0.1) System.Collections.Immutable (>= 6.0) System.Resources.Extensions (6.0) - copy_local: false - System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net6.0)) (== netstandard2.0) + System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net6.0)) (== netstandard2.0) (== netstandard2.1) System.Runtime.CompilerServices.Unsafe (6.0) System.Security.AccessControl (6.0) - copy_local: false - System.Security.Principal.Windows (>= 5.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net6.0)) (== netstandard2.0) - System.Security.Cryptography.Cng (5.0) - copy_local: false, restriction: || (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (< net6.0)) (&& (== net7.0) (< netcoreapp3.1)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) + System.Security.Principal.Windows (>= 5.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< net6.0)) (== netstandard2.0) (== netstandard2.1) + System.Security.Cryptography.Cng (5.0) - copy_local: false, restriction: || (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (< net6.0)) (&& (== net7.0) (< netcoreapp3.1)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) (== netstandard2.1) System.Security.Cryptography.Pkcs (6.0.1) - copy_local: false System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) System.Formats.Asn1 (>= 6.0) System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) - System.Security.Cryptography.Cng (>= 5.0) - restriction: || (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (< net6.0)) (&& (== net7.0) (< netcoreapp3.1)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) + System.Security.Cryptography.Cng (>= 5.0) - restriction: || (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (< net6.0)) (&& (== net7.0) (< netcoreapp3.1)) (&& (== net7.0) (< netstandard2.1)) (== netstandard2.0) (== netstandard2.1) System.Security.Cryptography.ProtectedData (6.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net7.0) (< net6.0)) (== netstandard2.0) + System.Memory (>= 4.5.4) - restriction: || (&& (== net7.0) (< net6.0)) (== netstandard2.0) (== netstandard2.1) System.Security.Cryptography.Xml (6.0.1) - copy_local: false - System.Memory (>= 4.5.4) - restriction: || (&& (== net7.0) (< net6.0)) (== netstandard2.0) + System.Memory (>= 4.5.4) - restriction: || (&& (== net7.0) (< net6.0)) (== netstandard2.0) (== netstandard2.1) System.Security.AccessControl (>= 6.0) System.Security.Cryptography.Pkcs (>= 6.0.1) System.Security.Permissions (6.0) - copy_local: false System.Security.AccessControl (>= 6.0) - System.Windows.Extensions (>= 6.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) + System.Windows.Extensions (>= 6.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) (&& (== netstandard2.1) (>= netcoreapp3.1)) System.Security.Principal.Windows (5.0) - copy_local: false System.Text.Encoding.CodePages (6.0) - copy_local: false - System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) + System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netcoreapp3.1)) (== netstandard2.0) (== netstandard2.1) System.Runtime.CompilerServices.Unsafe (>= 6.0) - System.Text.Encodings.Web (6.0) - copy_local: false, restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0)) + System.Text.Encodings.Web (6.0) - copy_local: false, restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net472)) (&& (== netstandard2.1) (>= net6.0)) System.Runtime.CompilerServices.Unsafe (>= 6.0) - System.Text.Json (6.0.5) - copy_local: false, restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0)) + System.Text.Json (6.0.5) - copy_local: false, restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= net472)) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net472)) (&& (== netstandard2.1) (>= net6.0)) System.Runtime.CompilerServices.Unsafe (>= 6.0) System.Text.Encodings.Web (>= 6.0) System.Threading.Tasks.Dataflow (6.0) - copy_local: false System.Threading.Tasks.Extensions (4.5.4) - System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (< netstandard1.0)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= wp8)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netcoreapp2.1)) (&& (== net7.0) (< netstandard1.0)) (&& (== net7.0) (< netstandard2.0)) (&& (== net7.0) (>= wp8)) (== netstandard2.0) - System.Windows.Extensions (6.0) - copy_local: false, restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) - System.Drawing.Common (>= 6.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) + System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (< netstandard1.0)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= wp8)) (&& (== net7.0) (>= net461)) (&& (== net7.0) (< netcoreapp2.1)) (&& (== net7.0) (< netstandard1.0)) (&& (== net7.0) (< netstandard2.0)) (&& (== net7.0) (>= wp8)) (== netstandard2.0) (== netstandard2.1) + System.Windows.Extensions (6.0) - copy_local: false, restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) (&& (== netstandard2.1) (>= netcoreapp3.1)) + System.Drawing.Common (>= 6.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) (&& (== netstandard2.1) (>= netcoreapp3.1)) YoloDev.Expecto.TestSdk (0.13.3) - Expecto (>= 9.0 < 10.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) - FSharp.Core (>= 4.6.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) - System.Collections.Immutable (>= 6.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) + Expecto (>= 9.0 < 10.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) (&& (== netstandard2.1) (>= netcoreapp3.1)) + FSharp.Core (>= 4.6.2) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) (&& (== netstandard2.1) (>= netcoreapp3.1)) + System.Collections.Immutable (>= 6.0) - restriction: || (== net6.0) (== net7.0) (&& (== netstandard2.0) (>= netcoreapp3.1)) (&& (== netstandard2.1) (>= netcoreapp3.1)) GROUP Build STORAGE: NONE From 95f1a5337f4922f43568f3f420ce12ceea221f08 Mon Sep 17 00:00:00 2001 From: Jimmy Byrd Date: Wed, 5 Apr 2023 23:21:03 -0400 Subject: [PATCH 09/10] sequence test detection tests --- test/FsAutoComplete.Tests.Lsp/DetectUnitTests.fs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/FsAutoComplete.Tests.Lsp/DetectUnitTests.fs b/test/FsAutoComplete.Tests.Lsp/DetectUnitTests.fs index 9ad1c1626..1b57357df 100644 --- a/test/FsAutoComplete.Tests.Lsp/DetectUnitTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/DetectUnitTests.fs @@ -10,7 +10,7 @@ open FsToolkit.ErrorHandling open Helpers.Expecto.ShadowedTimeouts let tests state = - let geTestNotification projectFolder fileName = + let getTestNotification projectFolder fileName = async { let path = Path.Combine(__SOURCE_DIRECTORY__, "TestCases", projectFolder) let! server, events = serverInitialize path defaultConfigDto state @@ -24,12 +24,13 @@ let tests state = } |> Async.Cache - testList + testSequenced + <| testList "Find unit tests" [ testCaseAsync "Find nunit test" (async { - let! testNotification = geTestNotification "NUnitTests" "UnitTest1.fs" + let! testNotification = getTestNotification "NUnitTests" "UnitTest1.fs" Expect.hasLength testNotification.Tests 1 "Expected to have found 1 nunit test" Expect.equal testNotification.Tests[0].Childs[1].Childs[0].Name "Inner" "Expect nested module to be named Inner" @@ -41,7 +42,7 @@ let tests state = testCaseAsync "Find xunit test" (async { - let! testNotification = geTestNotification "XUnitTests" "Tests.fs" + let! testNotification = getTestNotification "XUnitTests" "Tests.fs" Expect.hasLength testNotification.Tests 1 "Expected to have found 1 xunit test list" Expect.equal testNotification.Tests[0].ModuleType "Module" "Expected top list to be module" @@ -52,7 +53,7 @@ let tests state = testCaseAsync "Find expecto tests" (async { - let! testNotification = geTestNotification "ExpectoTests" "Sample.fs" + let! testNotification = getTestNotification "ExpectoTests" "Sample.fs" Expect.hasLength testNotification.Tests 1 "Expected to have found 1 expecto test list" Expect.hasLength testNotification.Tests.[0].Childs 8 "Expected to have found 8 expecto tests" }) ] From f6cc022e1c13cdd87615ff114f783dbf1064d1a4 Mon Sep 17 00:00:00 2001 From: Jimmy Byrd Date: Mon, 10 Apr 2023 20:06:28 -0400 Subject: [PATCH 10/10] fixing cancellation crash --- .../LspServers/AdaptiveFSharpLspServer.fs | 152 +++++++++--------- 1 file changed, 74 insertions(+), 78 deletions(-) diff --git a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs index da5256285..34bc06534 100644 --- a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs +++ b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs @@ -220,42 +220,36 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let fileChecked = Event() - do - disposables.Add - <| fileParsed.Publish.Subscribe(fun (parseResults, proj, ct) -> - try - logger.info ( - Log.setMessage "Test Detection of {file} started" - >> Log.addContextDestructured "file" parseResults.FileName - ) + let detectTests (parseResults: FSharpParseFileResults) (proj: FSharpProjectOptions) ct = + try + logger.info (Log.setMessageI $"Test Detection of {parseResults.FileName:file} started") - let fn = UMX.tag parseResults.FileName + let fn = UMX.tag parseResults.FileName - let res = - if proj.OtherOptions |> Seq.exists (fun o -> o.Contains "Expecto.dll") then - TestAdapter.getExpectoTests parseResults.ParseTree - elif proj.OtherOptions |> Seq.exists (fun o -> o.Contains "nunit.framework.dll") then - TestAdapter.getNUnitTest parseResults.ParseTree - elif proj.OtherOptions |> Seq.exists (fun o -> o.Contains "xunit.assert.dll") then - TestAdapter.getXUnitTest parseResults.ParseTree - else - [] + let res = + if proj.OtherOptions |> Seq.exists (fun o -> o.Contains "Expecto.dll") then + TestAdapter.getExpectoTests parseResults.ParseTree + elif proj.OtherOptions |> Seq.exists (fun o -> o.Contains "nunit.framework.dll") then + TestAdapter.getNUnitTest parseResults.ParseTree + elif proj.OtherOptions |> Seq.exists (fun o -> o.Contains "xunit.assert.dll") then + TestAdapter.getXUnitTest parseResults.ParseTree + else + [] - logger.info ( - Log.setMessage "Test Detection of {file} - {res}" - >> Log.addContextDestructured "file" parseResults.FileName - >> Log.addContextDestructured "res" res - ) + logger.info (Log.setMessageI $"Test Detection of {parseResults.FileName:file} - {res:res}") - notifications.Trigger(NotificationEvent.TestDetected(fn, res |> List.toArray), ct) - with e -> - logger.info ( - Log.setMessage "Test Detection of {file} failed - {res}" - >> Log.addContextDestructured "file" parseResults.FileName - >> Log.addException e - )) + notifications.Trigger(NotificationEvent.TestDetected(fn, res |> List.toArray), ct) + with e -> + logger.info ( + Log.setMessageI $"Test Detection of {parseResults.FileName:file} failed" + >> Log.addExn e + ) + + do + disposables.Add + <| fileParsed.Publish.Subscribe(fun (parseResults, proj, ct) -> detectTests parseResults proj ct) - let builtInCompilerAnalyzers config (file: VolatileFile, tyRes: ParseAndCheckResults) = + let builtInCompilerAnalyzers config (file: VolatileFile) (tyRes: ParseAndCheckResults) = let filePath = file.FileName let source = file.Lines let version = file.Version @@ -322,55 +316,52 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar } - do - disposables.Add - <| fileChecked.Publish.Subscribe(fun (parseAndCheck, volatileFile, ct) -> - async { - let config = config |> AVal.force - do! builtInCompilerAnalyzers config (volatileFile, parseAndCheck) + let runAnalyzers (config: FSharpConfig) (parseAndCheck: ParseAndCheckResults) (volatileFile: VolatileFile) = + async { + if config.EnableAnalyzers then + let file = volatileFile.FileName + + try + Loggers.analyzers.info ( + Log.setMessage "begin analysis of {file}" + >> Log.addContextDestructured "file" file + ) - if config.EnableAnalyzers then - let file = volatileFile.FileName + match parseAndCheck.GetCheckResults.ImplementationFile with + | Some tast -> + do! Async.SwitchToNewThread() - try - Loggers.analyzers.info ( - Log.setMessage "begin analysis of {file}" - >> Log.addContextDestructured "file" file - ) + let res = + Commands.analyzerHandler ( + file, + volatileFile.Lines.ToString().Split("\n"), + parseAndCheck.GetParseResults.ParseTree, + tast, + parseAndCheck.GetCheckResults.PartialAssemblySignature.Entities |> Seq.toList, + parseAndCheck.GetAllEntities + ) - match parseAndCheck.GetCheckResults.ImplementationFile with - | Some tast -> - - let res = - Commands.analyzerHandler ( - file, - volatileFile.Lines.ToString().Split("\n"), - parseAndCheck.GetParseResults.ParseTree, - tast, - parseAndCheck.GetCheckResults.PartialAssemblySignature.Entities |> Seq.toList, - parseAndCheck.GetAllEntities - ) + let! ct = Async.CancellationToken + notifications.Trigger(NotificationEvent.AnalyzerMessage(res, file), ct) - notifications.Trigger(NotificationEvent.AnalyzerMessage(res, file), ct) + Loggers.analyzers.info (Log.setMessageI $"end analysis of {file:file}") - Loggers.analyzers.info ( - Log.setMessage "end analysis of {file}" - >> Log.addContextDestructured "file" file - ) + | _ -> + Loggers.analyzers.info (Log.setMessageI $"missing components of {file:file} to run analyzers, skipped them") - | _ -> - Loggers.analyzers.info ( - Log.setMessage "missing components of {file} to run analyzers, skipped them" - >> Log.addContextDestructured "file" file - ) + () + with ex -> + Loggers.analyzers.error (Log.setMessageI $"Run failed for {file:file}" >> Log.addExn ex) + } + + do + disposables.Add + <| fileChecked.Publish.Subscribe(fun (parseAndCheck, volatileFile, ct) -> + async { + let config = config |> AVal.force + do! builtInCompilerAnalyzers config volatileFile parseAndCheck + do! runAnalyzers config parseAndCheck volatileFile - () - with ex -> - Loggers.analyzers.error ( - Log.setMessage "Run failed for {file}" - >> Log.addContextDestructured "file" file - >> Log.addExn ex - ) } |> Async.StartWithCT ct) @@ -589,7 +580,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> AVal.map (fun writeTime -> filePath, writeTime) let addAValLogging cb (aval: aval<_>) = - let cb = aval.AddMarkingCallback(cb) + let cb = aval.AddWeakMarkingCallback(cb) aval |> AVal.mapDisposableTuple (fun x -> x, cb) let projectFileChanges project (filePath: string) = @@ -1360,8 +1351,15 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> AsyncResult.orElseWith (fun _ -> forceGetTypeCheckResults filePath) |> Async.map (fun r -> Async.Start( - forceGetTypeCheckResults filePath - |> Async.Ignore> + async { + // This needs to be in a try catch as it can throw on cancellation which causes the server to crash + try + do! + forceGetTypeCheckResults filePath + |> Async.Ignore> + with e -> + () + } ) r) @@ -1780,8 +1778,6 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let mutable checksCompleted = 0 - // let progressToken = ProgressToken.Second(Guid.NewGuid().ToString()) - // do! lspClient.WorkDoneProgressCreate progressToken |> Async.Ignore> use progressReporter = new ServerProgressReport(lspClient) let percentage numerator denominator =