From 7bbfbeaa6bc1de442724dd9a9b47a1cd786a9f9a Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Thu, 4 May 2023 15:12:15 +0200 Subject: [PATCH 01/20] wip --- src/Compiler/FSharp.Compiler.Service.fsproj | 2 + src/Compiler/Utilities/CancellableTask.fs | 937 ++++++++++++++++++ .../ClassificationDefinitions.fs | 1 - .../Classification/ClassificationService.fs | 139 ++- .../Debugging/LanguageDebugInfoService.fs | 15 +- .../Diagnostics/DocumentDiagnosticAnalyzer.fs | 44 +- .../src/FSharp.Editor/Hints/HintService.fs | 14 +- .../src/FSharp.Editor/Hints/Hints.fs | 4 +- .../Hints/InlineParameterNameHints.fs | 7 +- .../Hints/InlineReturnTypeHints.fs | 5 +- .../FSharp.Editor/Hints/InlineTypeHints.fs | 5 +- .../Hints/NativeToRoslynHintConverter.fs | 26 +- .../src/FSharp.Editor/Hints/RoslynAdapter.fs | 16 +- .../LanguageService/LanguageService.fs | 150 ++- .../LanguageService/Tokenizer.fs | 52 +- .../Navigation/NavigateToSearchService.fs | 2 +- .../DocumentDiagnosticAnalyzerTests.fs | 23 +- .../FSharp.Editor.Tests.fsproj | 1 + .../Hints/HintTestFramework.fs | 33 +- 19 files changed, 1196 insertions(+), 280 deletions(-) create mode 100644 src/Compiler/Utilities/CancellableTask.fs diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index d95ad840eff..31b2529ce92 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -76,6 +76,7 @@ + @@ -91,6 +92,7 @@ + diff --git a/src/Compiler/Utilities/CancellableTask.fs b/src/Compiler/Utilities/CancellableTask.fs new file mode 100644 index 00000000000..7d505b20df7 --- /dev/null +++ b/src/Compiler/Utilities/CancellableTask.fs @@ -0,0 +1,937 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Internal.Utilities + +// Don't warn about the resumable code invocation +#nowarn "3513" + +module CancellableTasks = + + open System + open System.Runtime.CompilerServices + open System.Threading + open System.Threading.Tasks + open Microsoft.FSharp.Core + open Microsoft.FSharp.Core.CompilerServices + open Microsoft.FSharp.Core.CompilerServices.StateMachineHelpers + open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators + open Microsoft.FSharp.Collections + + /// A type that looks like an Awaiter + type Awaiter<'Awaiter, 'TResult + when 'Awaiter :> ICriticalNotifyCompletion + and 'Awaiter: (member IsCompleted: bool) + and 'Awaiter: (member GetResult: unit -> 'TResult)> = 'Awaiter + + /// A type that looks like an Awaitable + type Awaitable<'Awaitable, 'Awaiter, 'TResult + when 'Awaitable: (member GetAwaiter: unit -> Awaiter<'Awaiter, 'TResult>)> = 'Awaitable + + /// Functions for Awaiters + module Awaiter = + /// Gets a value that indicates whether the asynchronous task has completed + let inline isCompleted<'Awaiter, 'TResult when Awaiter<'Awaiter, 'TResult>> (x: 'Awaiter) = + x.IsCompleted + + /// Ends the wait for the completion of the asynchronous task. + let inline getResult<'Awaiter, 'TResult when Awaiter<'Awaiter, 'TResult>> (x: 'Awaiter) = + x.GetResult() + + /// Functions for Awaitables + module Awaitable = + /// Creates an awaiter for this value. + let inline getAwaiter<'Awaitable, 'Awaiter, 'TResult + when Awaitable<'Awaitable, 'Awaiter, 'TResult>> + (x: 'Awaitable) + = + x.GetAwaiter() + + /// CancellationToken -> Task<'T> + type CancellableTask<'T> = CancellationToken -> Task<'T> + + /// CancellationToken -> Task + type CancellableTask = CancellationToken -> Task + + /// The extra data stored in ResumableStateMachine for tasks + [] + type CancellableTaskStateMachineData<'T> = + [] + val mutable CancellationToken: CancellationToken + + [] + val mutable Result: 'T + + [] + val mutable MethodBuilder: AsyncTaskMethodBuilder<'T> + + member inline this.ThrowIfCancellationRequested() = + this.CancellationToken.ThrowIfCancellationRequested() + + /// This is used by the compiler as a template for creating state machine structs + and CancellableTaskStateMachine<'TOverall> = + ResumableStateMachine> + + /// Represents the runtime continuation of a CancellableTask state machine created dynamically + and CancellableTaskResumptionFunc<'TOverall> = + ResumptionFunc> + + /// Represents the runtime continuation of a CancellableTask state machine created dynamically + and CancellableTaskResumptionDynamicInfo<'TOverall> = + ResumptionDynamicInfo> + + /// A special compiler-recognised delegate type for specifying blocks of CancellableTask code with access to the state machine + and CancellableTaskCode<'TOverall, 'T> = + ResumableCode, 'T> + + + /// Contains methods to build CancellableTasks using the F# computation expression syntax + [] + type CancellableTaskBuilderBase() = + + /// Creates a CancellableTask that runs generator + /// The function to run + /// A cancellableTask that runs generator + member inline _.Delay + ([] generator: unit -> CancellableTaskCode<'TOverall, 'T>) + : CancellableTaskCode<'TOverall, 'T> = + ResumableCode.Delay(fun () -> + CancellableTaskCode(fun sm -> + sm.Data.ThrowIfCancellationRequested() + (generator ()).Invoke(&sm) + ) + ) + + + /// Creates an CancellableTask that just returns (). + /// + /// The existence of this method permits the use of empty else branches in the + /// cancellableTask { ... } computation expression syntax. + /// + /// An CancellableTask that returns (). + [] + member inline _.Zero() : CancellableTaskCode<'TOverall, unit> = ResumableCode.Zero() + + /// Creates an computation that returns the result v. + /// + /// A cancellation check is performed when the computation is executed. + /// + /// The existence of this method permits the use of return in the + /// cancellableTask { ... } computation expression syntax. + /// + /// The value to return from the computation. + /// + /// An CancellableTask that returns value when executed. + member inline _.Return(value: 'T) : CancellableTaskCode<'T, 'T> = + CancellableTaskCode<'T, _>(fun sm -> + sm.Data.ThrowIfCancellationRequested() + sm.Data.Result <- value + true + ) + + /// Creates an CancellableTask that first runs task1 + /// and then runs computation2, returning the result of computation2. + /// + /// + /// + /// The existence of this method permits the use of expression sequencing in the + /// cancellableTask { ... } computation expression syntax. + /// + /// The first part of the sequenced computation. + /// The second part of the sequenced computation. + /// + /// An CancellableTask that runs both of the computations sequentially. + member inline _.Combine + ( + task1: CancellableTaskCode<'TOverall, unit>, + task2: CancellableTaskCode<'TOverall, 'T> + ) : CancellableTaskCode<'TOverall, 'T> = + ResumableCode.Combine( + CancellableTaskCode(fun sm -> + sm.Data.ThrowIfCancellationRequested() + task1.Invoke(&sm) + ), + + CancellableTaskCode(fun sm -> + sm.Data.ThrowIfCancellationRequested() + task2.Invoke(&sm) + ) + ) + + /// Creates an CancellableTask that runs computation repeatedly + /// until guard() becomes false. + /// + /// + /// + /// The existence of this method permits the use of while in the + /// cancellableTask { ... } computation expression syntax. + /// + /// The function to determine when to stop executing computation. + /// The function to be executed. Equivalent to the body + /// of a while expression. + /// + /// An CancellableTask that behaves similarly to a while loop when run. + member inline _.While + ( + [] guard: unit -> bool, + computation: CancellableTaskCode<'TOverall, unit> + ) : CancellableTaskCode<'TOverall, unit> = + ResumableCode.While( + guard, + CancellableTaskCode(fun sm -> + sm.Data.ThrowIfCancellationRequested() + computation.Invoke(&sm) + ) + ) + + /// Creates an CancellableTask that runs computation and returns its result. + /// If an exception happens then catchHandler(exn) is called and the resulting computation executed instead. + /// + /// + /// + /// The existence of this method permits the use of try/with in the + /// cancellableTask { ... } computation expression syntax. + /// + /// The input computation. + /// The function to run when computation throws an exception. + /// + /// An CancellableTask that executes computation and calls catchHandler if an + /// exception is thrown. + member inline _.TryWith + ( + computation: CancellableTaskCode<'TOverall, 'T>, + [] catchHandler: exn -> CancellableTaskCode<'TOverall, 'T> + ) : CancellableTaskCode<'TOverall, 'T> = + ResumableCode.TryWith( + CancellableTaskCode(fun sm -> + sm.Data.ThrowIfCancellationRequested() + computation.Invoke(&sm) + ), + catchHandler + ) + + /// Creates an CancellableTask that runs computation. The action compensation is executed + /// after computation completes, whether computation exits normally or by an exception. If compensation raises an exception itself + /// the original exception is discarded and the new exception becomes the overall result of the computation. + /// + /// + /// + /// The existence of this method permits the use of try/finally in the + /// cancellableTask { ... } computation expression syntax. + /// + /// The input computation. + /// The action to be run after computation completes or raises an + /// exception (including cancellation). + /// + /// An CancellableTask that executes computation and compensation afterwards or + /// when an exception is raised. + member inline _.TryFinally + ( + computation: CancellableTaskCode<'TOverall, 'T>, + [] compensation: unit -> unit + ) : CancellableTaskCode<'TOverall, 'T> = + ResumableCode.TryFinally( + + CancellableTaskCode(fun sm -> + sm.Data.ThrowIfCancellationRequested() + computation.Invoke(&sm) + ), + ResumableCode<_, _>(fun _ -> + compensation () + true + ) + ) + + /// Creates an CancellableTask that enumerates the sequence seq + /// on demand and runs body for each element. + /// + /// A cancellation check is performed on each iteration of the loop. + /// + /// The existence of this method permits the use of for in the + /// cancellableTask { ... } computation expression syntax. + /// + /// The sequence to enumerate. + /// A function to take an item from the sequence and create + /// an CancellableTask. Can be seen as the body of the for expression. + /// + /// An CancellableTask that will enumerate the sequence and run body + /// for each element. + member inline _.For + ( + sequence: seq<'T>, + [] body: 'T -> CancellableTaskCode<'TOverall, unit> + ) : CancellableTaskCode<'TOverall, unit> = + ResumableCode.For( + sequence, + fun item -> + CancellableTaskCode(fun sm -> + sm.Data.ThrowIfCancellationRequested() + (body item).Invoke(&sm) + ) + ) + + /// Contains methods to build CancellableTasks using the F# computation expression syntax + [] + type CancellableTaskBuilder(runOnBackground: bool) = + + inherit CancellableTaskBuilderBase() + + member val IsBackground = runOnBackground + + // This is the dynamic implementation - this is not used + // for statically compiled tasks. An executor (resumptionFuncExecutor) is + // registered with the state machine, plus the initial resumption. + // The executor stays constant throughout the execution, it wraps each step + // of the execution in a try/with. The resumption is changed at each step + // to represent the continuation of the computation. + /// + /// The entry point for the dynamic implementation of the corresponding operation. Do not use directly, only used when executing quotations that involve tasks or other reflective execution of F# code. + /// + static member inline RunDynamicAux(code: CancellableTaskCode<'T, 'T>) : CancellableTask<'T> = + + let mutable sm = CancellableTaskStateMachine<'T>() + + let initialResumptionFunc = + CancellableTaskResumptionFunc<'T>(fun sm -> code.Invoke(&sm)) + + let resumptionInfo = + { new CancellableTaskResumptionDynamicInfo<'T>(initialResumptionFunc) with + member info.MoveNext(sm) = + let mutable savedExn = null + + try + sm.ResumptionDynamicInfo.ResumptionData <- null + let step = info.ResumptionFunc.Invoke(&sm) + + if step then + sm.Data.MethodBuilder.SetResult(sm.Data.Result) + else + let mutable awaiter = + sm.ResumptionDynamicInfo.ResumptionData + :?> ICriticalNotifyCompletion + + assert not (isNull awaiter) + sm.Data.MethodBuilder.AwaitUnsafeOnCompleted(&awaiter, &sm) + + with exn -> + savedExn <- exn + // Run SetException outside the stack unwind, see https://github.com/dotnet/roslyn/issues/26567 + match savedExn with + | null -> () + | exn -> sm.Data.MethodBuilder.SetException exn + + member _.SetStateMachine(sm, state) = + sm.Data.MethodBuilder.SetStateMachine(state) + } + + fun (ct) -> + if ct.IsCancellationRequested then + Task.FromCanceled<_>(ct) + else + sm.Data.CancellationToken <- ct + sm.ResumptionDynamicInfo <- resumptionInfo + sm.Data.MethodBuilder <- AsyncTaskMethodBuilder<'T>.Create() + sm.Data.MethodBuilder.Start(&sm) + sm.Data.MethodBuilder.Task + + /// + /// The entry point for the dynamic implementation of the corresponding operation. Do not use directly, only used when executing quotations that involve tasks or other reflective execution of F# code. + /// + static member inline RunDynamic(code: CancellableTaskCode<'T, 'T>, runOnBackground: bool) : CancellableTask<'T> = + // When runOnBackground is true, task escapes to a background thread where necessary + // See spec of ConfigureAwait(false) at https://devblogs.microsoft.com/dotnet/configureawait-faq/ + + if runOnBackground + && not (isNull SynchronizationContext.Current + && obj.ReferenceEquals(TaskScheduler.Current, TaskScheduler.Default)) + then + fun (ct) -> + // Warning: this will always try to yield even if on thread pool already. + Task.Run<'T>((fun () -> CancellableTaskBuilder.RunDynamicAux (code) (ct)), ct) + else + CancellableTaskBuilder.RunDynamicAux(code) + + + /// Hosts the task code in a state machine and starts the task. + member inline this.Run(code: CancellableTaskCode<'T, 'T>) : CancellableTask<'T> = + if __useResumableCode then + __stateMachine, CancellableTask<'T>> + (MoveNextMethodImpl<_>(fun sm -> + //-- RESUMABLE CODE START + __resumeAt sm.ResumptionPoint + let mutable __stack_exn: Exception = null + + try + let __stack_code_fin = code.Invoke(&sm) + + if __stack_code_fin then + sm.Data.MethodBuilder.SetResult(sm.Data.Result) + with exn -> + __stack_exn <- exn + // Run SetException outside the stack unwind, see https://github.com/dotnet/roslyn/issues/26567 + match __stack_exn with + | null -> () + | exn -> sm.Data.MethodBuilder.SetException exn + //-- RESUMABLE CODE END + )) + (SetStateMachineMethodImpl<_>(fun sm state -> + sm.Data.MethodBuilder.SetStateMachine(state) + )) + (AfterCode<_, _>(fun sm -> + if this.IsBackground + && not (isNull SynchronizationContext.Current + && obj.ReferenceEquals(TaskScheduler.Current, TaskScheduler.Default)) + then + + let sm = sm // copy contents of state machine so we can capture it + + fun (ct) -> + if ct.IsCancellationRequested then + Task.FromCanceled<_>(ct) + else + // Warning: this will always try to yield even if on thread pool already. + Task.Run<'T>( + (fun () -> + let mutable sm = sm // host local mutable copy of contents of state machine on this thread pool thread + sm.Data.CancellationToken <- ct + + sm.Data.MethodBuilder <- + AsyncTaskMethodBuilder<'T>.Create() + + sm.Data.MethodBuilder.Start(&sm) + sm.Data.MethodBuilder.Task + ), + ct + ) + else + let mutable sm = sm + + fun (ct) -> + if ct.IsCancellationRequested then + Task.FromCanceled<_>(ct) + else + sm.Data.CancellationToken <- ct + sm.Data.MethodBuilder <- AsyncTaskMethodBuilder<'T>.Create() + sm.Data.MethodBuilder.Start(&sm) + sm.Data.MethodBuilder.Task + )) + else + CancellableTaskBuilder.RunDynamic(code, this.IsBackground) + + /// Contains the cancellableTask computation expression builder. + [] + module CancellableTaskBuilder = + + /// + /// Builds a cancellableTask using computation expression syntax. + /// Default behaviour when binding (v)options is to return a cacnelled task. + /// + let foregroundCancellableTask = CancellableTaskBuilder(false) + + /// + /// Builds a cancellableTask using computation expression syntax which switches to execute on a background thread if not already doing so. + /// Default behaviour when binding (v)options is to return a cacnelled task. + /// + let cancellableTask = CancellableTaskBuilder(true) + + /// + [] + module LowPriority = + // Low priority extensions + type CancellableTaskBuilderBase with + + /// + /// The entry point for the dynamic implementation of the corresponding operation. Do not use directly, only used when executing quotations that involve tasks or other reflective execution of F# code. + /// + [] + static member inline BindDynamic<'TResult1, 'TResult2, 'Awaiter, 'TOverall + when Awaiter<'Awaiter, 'TResult1>> + ( + sm: byref>>, + [] getAwaiter: CancellationToken -> 'Awaiter, + [] continuation: ('TResult1 -> CancellableTaskCode<'TOverall, 'TResult2>) + ) : bool = + sm.Data.ThrowIfCancellationRequested() + + let mutable awaiter = getAwaiter sm.Data.CancellationToken + + let cont: CancellableTaskResumptionFunc<'TOverall> = + (CancellableTaskResumptionFunc<'TOverall>(fun (sm: byref>>) -> + let result: 'TResult1 = Awaiter.getResult awaiter + (continuation result).Invoke(&sm) + )) + + // shortcut to continue immediately + if Awaiter.isCompleted awaiter then + cont.Invoke(&sm) + else + sm.ResumptionDynamicInfo.ResumptionData <- + (awaiter :> ICriticalNotifyCompletion) + + sm.ResumptionDynamicInfo.ResumptionFunc <- cont + false + + /// Creates an CancellableTask that runs computation, and when + /// computation generates a result T, runs binder res. + /// + /// A cancellation check is performed when the computation is executed. + /// + /// The existence of this method permits the use of let! in the + /// cancellableTask { ... } computation expression syntax. + /// + /// The computation to provide an unbound result. + /// The function to bind the result of computation. + /// + /// An CancellableTask that performs a monadic bind on the result + /// of computation. + [] + member inline _.Bind<'TResult1, 'TResult2, 'Awaiter, 'TOverall + when Awaiter<'Awaiter, 'TResult1>> + ( + [] getAwaiter: CancellationToken -> 'Awaiter, + [] continuation: ('TResult1 -> CancellableTaskCode<'TOverall, 'TResult2>) + ) : CancellableTaskCode<'TOverall, 'TResult2> = + + CancellableTaskCode<'TOverall, _>(fun sm -> + if __useResumableCode then + //-- RESUMABLE CODE START + sm.Data.ThrowIfCancellationRequested() + // Get an awaiter from the Awaiter + let mutable awaiter = getAwaiter sm.Data.CancellationToken + + let mutable __stack_fin = true + + if not (Awaiter.isCompleted awaiter) then + // This will yield with __stack_yield_fin = false + // This will resume with __stack_yield_fin = true + let __stack_yield_fin = ResumableCode.Yield().Invoke(&sm) + __stack_fin <- __stack_yield_fin + + if __stack_fin then + let result = + awaiter + |> Awaiter.getResult + + (continuation result).Invoke(&sm) + else + sm.Data.MethodBuilder.AwaitUnsafeOnCompleted(&awaiter, &sm) + false + else + CancellableTaskBuilderBase.BindDynamic<'TResult1, 'TResult2, 'Awaiter, 'TOverall>( + &sm, + getAwaiter, + continuation + ) + //-- RESUMABLE CODE END + ) + + + /// Delegates to the input computation. + /// + /// The existence of this method permits the use of return! in the + /// cancellableTask { ... } computation expression syntax. + /// + /// The input computation. + /// + /// The input computation. + [] + member inline this.ReturnFrom<'TResult1, 'TResult2, 'Awaiter, 'TOverall + when Awaiter<'Awaiter, 'TResult1>> + ([] getAwaiter: CancellationToken -> 'Awaiter) + : CancellableTaskCode<_, _> = + this.Bind(getAwaiter, this.Return) + + + [] + member inline this.BindReturn<'TResult1, 'TResult2, 'Awaiter, 'TOverall + when Awaiter<'Awaiter, 'TResult1>> + ( + [] getAwaiter: CancellationToken -> 'Awaiter, + f + ) : CancellableTaskCode<'TResult2, 'TResult2> = + this.Bind(getAwaiter, (fun v -> this.Return(f v))) + + /// Allows the computation expression to turn other types into CancellationToken -> 'Awaiter + /// + /// This is the identify function. + /// + /// CancellationToken -> 'Awaiter + [] + member inline _.Source<'TResult1, 'TResult2, 'Awaiter, 'TOverall + when Awaiter<'Awaiter, 'TResult1>> + ([] getAwaiter: CancellationToken -> 'Awaiter) + : CancellationToken -> 'Awaiter = + getAwaiter + + + /// Allows the computation expression to turn other types into CancellationToken -> 'Awaiter + /// + /// This is the identify function. + /// + /// CancellationToken -> 'Awaiter + [] + member inline _.Source<'TResult1, 'TResult2, 'Awaiter, 'TOverall + when Awaiter<'Awaiter, 'TResult1>> + (getAwaiter: 'Awaiter) + : CancellationToken -> 'Awaiter = + (fun _ct -> getAwaiter) + + + /// Allows the computation expression to turn other types into CancellationToken -> 'Awaiter + /// + /// This turns a 'Awaitable into a CancellationToken -> 'Awaiter. + /// + /// CancellationToken -> 'Awaiter + [] + member inline _.Source<'Awaitable, 'TResult1, 'TResult2, 'Awaiter, 'TOverall + when Awaitable<'Awaitable, 'Awaiter, 'TResult1>> + (task: 'Awaitable) + : CancellationToken -> 'Awaiter = + (fun (_ct: CancellationToken) -> + task + |> Awaitable.getAwaiter + ) + + + /// Allows the computation expression to turn other types into CancellationToken -> 'Awaiter + /// + /// This turns a CancellationToken -> 'Awaitable into a CancellationToken -> 'Awaiter. + /// + /// CancellationToken -> 'Awaiter + [] + member inline _.Source<'Awaitable, 'TResult1, 'TResult2, 'Awaiter, 'TOverall + when Awaitable<'Awaitable, 'Awaiter, 'TResult1>> + ([] task: CancellationToken -> 'Awaitable) + : CancellationToken -> 'Awaiter = + (fun ct -> + task ct + |> Awaitable.getAwaiter + ) + + + /// Allows the computation expression to turn other types into CancellationToken -> 'Awaiter + /// + /// This turns a unit -> 'Awaitable into a CancellationToken -> 'Awaiter. + /// + /// CancellationToken -> 'Awaiter + [] + member inline _.Source<'Awaitable, 'TResult1, 'TResult2, 'Awaiter, 'TOverall + when Awaitable<'Awaitable, 'Awaiter, 'TResult1>> + ([] task: unit -> 'Awaitable) + : CancellationToken -> 'Awaiter = + (fun _ct -> + task () + |> Awaitable.getAwaiter + ) + + + /// Creates an CancellableTask that runs binder(resource). + /// The action resource.Dispose() is executed as this computation yields its result + /// or if the CancellableTask exits by an exception or by cancellation. + /// + /// + /// + /// The existence of this method permits the use of use and use! in the + /// cancellableTask { ... } computation expression syntax. + /// + /// The resource to be used and disposed. + /// The function that takes the resource and returns an asynchronous + /// computation. + /// + /// An CancellableTask that binds and eventually disposes resource. + /// + member inline _.Using<'Resource, 'TOverall, 'T when 'Resource :> IDisposable> + ( + resource: 'Resource, + [] binder: 'Resource -> CancellableTaskCode<'TOverall, 'T> + ) = + ResumableCode.Using( + resource, + fun resource -> + CancellableTaskCode<'TOverall, 'T>(fun sm -> + sm.Data.ThrowIfCancellationRequested() + (binder resource).Invoke(&sm) + ) + ) + + /// + [] + module HighPriority = + + type Control.Async with + + /// Return an asynchronous computation that will wait for the given task to complete and return + /// its result. + static member inline AwaitCancellableTask(t: CancellableTask<'T>) = + async { + let! ct = Async.CancellationToken + + return! + t ct + |> Async.AwaitTask + } + + /// Return an asynchronous computation that will wait for the given task to complete and return + /// its result. + static member inline AwaitCancellableTask(t: CancellableTask) = + async { + let! ct = Async.CancellationToken + + return! + t ct + |> Async.AwaitTask + } + + /// Runs an asynchronous computation, starting on the current operating system thread. + static member inline AsCancellableTask(computation: Async<'T>) : CancellableTask<_> = + fun ct -> Async.StartImmediateAsTask(computation, cancellationToken = ct) + + // High priority extensions + type CancellableTaskBuilderBase with + + /// + /// Turn option into "awaitable", will return cancelled task if None + /// + /// Option instance to bind on + member inline _.Source(s: 'T option) = + (fun (_ct: CancellationToken) -> + match s with + | Some x -> Task.FromResult<'T>(x).GetAwaiter() + | None -> Task.FromCanceled<'T>(CancellationToken(true)).GetAwaiter() + ) + + /// + /// Turn a value option into "awaitable", will return cancelled task if None + /// + /// Option instance to bind on + member inline _.Source(s: 'T voption) = + (fun (_ct: CancellationToken) -> + match s with + | ValueSome x -> Task.FromResult<'T>(x).GetAwaiter() + | ValueNone -> Task.FromCanceled<'T>(CancellationToken(true)).GetAwaiter() + ) + + /// Allows the computation expression to turn other types into other types + /// + /// This is the identify function for For binds. + /// + /// IEnumerable + member inline _.Source(s: #seq<_>) : #seq<_> = s + + /// Allows the computation expression to turn other types into CancellationToken -> 'Awaiter + /// + /// This turns a Task<'T> into a CancellationToken -> 'Awaiter. + /// + /// CancellationToken -> 'Awaiter + member inline _.Source(task: Task<'T>) = + (fun (_ct: CancellationToken) -> task.GetAwaiter()) + + /// Allows the computation expression to turn other types into CancellationToken -> 'Awaiter + /// + /// This turns a ColdTask<'T> into a CancellationToken -> 'Awaiter. + /// + /// CancellationToken -> 'Awaiter + member inline _.Source([] task: unit -> Task<'TResult1>) = + (fun (_ct: CancellationToken) -> (task ()).GetAwaiter()) + + /// Allows the computation expression to turn other types into CancellationToken -> 'Awaiter + /// + /// This turns a CancellableTask<'T> into a CancellationToken -> 'Awaiter. + /// + /// CancellationToken -> 'Awaiter + member inline _.Source([] task: CancellableTask<'TResult1>) = + (fun ct -> (task ct).GetAwaiter()) + + /// Allows the computation expression to turn other types into CancellationToken -> 'Awaiter + /// + /// This turns a Async<'T> into a CancellationToken -> 'Awaiter. + /// + /// CancellationToken -> 'Awaiter + member inline this.Source(computation: Async<'TResult1>) = + this.Source(Async.AsCancellableTask(computation)) + + /// Allows the computation expression to turn other types into CancellationToken -> 'Awaiter + /// + /// This turns a CancellableTask<'T> into a CancellationToken -> 'Awaiter. + /// + /// CancellationToken -> 'Awaiter + member inline _.Source(awaiter: TaskAwaiter<'TResult1>) = (fun _ct -> awaiter) + + /// + /// A set of extension methods making it possible to bind against in async computations. + /// + [] + module AsyncExtenions = + type Control.AsyncBuilder with + + member inline this.Bind(t: CancellableTask<'T>, [] binder: ('T -> Async<'U>)) : Async<'U> = + this.Bind(Async.AwaitCancellableTask t, binder) + + member inline this.ReturnFrom([] t: CancellableTask<'T>) : Async<'T> = + this.ReturnFrom(Async.AwaitCancellableTask t) + + member inline this.Bind([] t: CancellableTask, binder: (unit -> Async<'U>)) : Async<'U> = + this.Bind(Async.AwaitCancellableTask t, binder) + + member inline this.ReturnFrom([] t: CancellableTask) : Async = + this.ReturnFrom(Async.AwaitCancellableTask t) + + /// Contains a set of standard functional helper function + [] + module CancellableTask = + + /// Gets the default cancellation token for executing computations. + /// + /// The default CancellationToken. + /// + /// Cancellation and Exceptions + /// + /// + /// + /// use tokenSource = new CancellationTokenSource() + /// let primes = [ 2; 3; 5; 7; 11 ] + /// for i in primes do + /// let computation = + /// cancellableTask { + /// let! cancellationToken = CancellableTask.getCurrentCancellationToken() + /// do! Task.Delay(i * 1000, cancellationToken) + /// printfn $"{i}" + /// } + /// computation tokenSource.Token |> ignore + /// Thread.Sleep(6000) + /// tokenSource.Cancel() + /// printfn "Tasks Finished" + /// + /// This will print "2" 2 seconds from start, "3" 3 seconds from start, "5" 5 seconds from start, cease computation and then + /// followed by "Tasks Finished". + /// + let getCurrentCancellationToken () = + cancellableTask.Run( + CancellableTaskCode<_, _>(fun sm -> + sm.Data.Result <- sm.Data.CancellationToken + true + ) + ) + + /// Lifts an item to a CancellableTask. + /// The item to be the result of the CancellableTask. + /// A CancellableTask with the item as the result. + let inline singleton (item: 'item) : CancellableTask<'item> = fun _ -> Task.FromResult(item) + + + /// Allows chaining of CancellableTasks. + /// The continuation. + /// The value. + /// The result of the binder. + let inline bind + ([] binder: 'input -> CancellableTask<'output>) + ([] cTask: CancellableTask<'input>) + = + cancellableTask { + let! cResult = cTask + return! binder cResult + } + + /// Allows chaining of CancellableTasks. + /// The continuation. + /// The value. + /// The result of the mapper wrapped in a CancellableTasks. + let inline map + ([] mapper: 'input -> 'output) + ([] cTask: CancellableTask<'input>) + = + cancellableTask { + let! cResult = cTask + return mapper cResult + } + + /// Allows chaining of CancellableTasks. + /// A function wrapped in a CancellableTasks + /// The value. + /// The result of the applicable. + let inline apply + ([] applicable: CancellableTask<'input -> 'output>) + ([] cTask: CancellableTask<'input>) + = + cancellableTask { + let! applier = applicable + let! cResult = cTask + return applier cResult + } + + /// Takes two CancellableTasks, starts them serially in order of left to right, and returns a tuple of the pair. + /// The left value. + /// The right value. + /// A tuple of the parameters passed in + let inline zip + ([] left: CancellableTask<'left>) + ([] right: CancellableTask<'right>) + = + cancellableTask { + let! r1 = left + let! r2 = right + return r1, r2 + } + + /// Takes two CancellableTask, starts them concurrently, and returns a tuple of the pair. + /// The left value. + /// The right value. + /// A tuple of the parameters passed in. + let inline parallelZip + ([] left: CancellableTask<'left>) + ([] right: CancellableTask<'right>) + = + cancellableTask { + let! ct = getCurrentCancellationToken () + let r1 = left ct + let r2 = right ct + let! r1 = r1 + let! r2 = r2 + return r1, r2 + } + + + /// Coverts a CancellableTask to a CancellableTask\<unit\>. + /// The CancellableTask to convert. + /// a CancellableTask\<unit\>. + let inline ofUnit ([] unitCancellabletTask: CancellableTask) = + cancellableTask { + return! unitCancellabletTask + } + + /// Coverts a CancellableTask\<_\> to a CancellableTask. + /// The CancellableTask to convert. + /// A cancellation token. + /// a CancellableTask. + let inline toUnit ([] ctask: CancellableTask<_>) : CancellableTask = + fun ct -> ctask ct + + let inline getAwaiter ([] ctask: CancellableTask<_>) = + fun ct -> (ctask ct).GetAwaiter() + + let inline start ct ([] ctask: CancellableTask<_>) = ctask ct + + let inline startAsTask ct ([] ctask: CancellableTask<_>) = (ctask ct) :> Task + + /// + [] + module MergeSourcesExtensions = + + type CancellableTaskBuilderBase with + + [] + member inline _.MergeSources<'TResult1, 'TResult2, 'Awaiter1, 'Awaiter2 + when Awaiter<'Awaiter1, 'TResult1> and Awaiter<'Awaiter2, 'TResult2>> + ( + [] left: CancellationToken -> 'Awaiter1, + [] right: CancellationToken -> 'Awaiter2 + ) : CancellationToken -> TaskAwaiter<'TResult1 * 'TResult2> = + + cancellableTask { + let! ct = CancellableTask.getCurrentCancellationToken () + let leftStarted = left ct + let rightStarted = right ct + let! leftResult = leftStarted + let! rightResult = rightStarted + return leftResult, rightResult + } + |> CancellableTask.getAwaiter \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationDefinitions.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationDefinitions.fs index 79405d93b48..442e13b44cd 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationDefinitions.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationDefinitions.fs @@ -17,7 +17,6 @@ open Microsoft.VisualStudio.Text.Classification open Microsoft.VisualStudio.Utilities open Microsoft.CodeAnalysis.Classification -open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.EditorServices [] diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index 43673355abc..b3fa677fc1c 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -6,20 +6,18 @@ open System open System.Composition open System.Collections.Generic open System.Collections.Immutable -open System.Diagnostics open System.Threading open System.Runtime.Caching open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Classification -open Microsoft.CodeAnalysis.Editor -open Microsoft.CodeAnalysis.Host.Mef open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Classification -open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.EditorServices open FSharp.Compiler.Tokenization +open Internal.Utilities.CancellableTasks.CancellableTaskBuilder +open Internal.Utilities.CancellableTasks // IEditorClassificationService is marked as Obsolete, but is still supported. The replacement (IClassificationService) // is internal to Microsoft.CodeAnalysis.Workspaces which we don't have internals visible to. Rather than add yet another @@ -45,9 +43,9 @@ type DocumentCache<'Value when 'Value: not struct>() = CacheItemPolicy(SlidingExpiration = TimeSpan.FromSeconds slidingExpirationSeconds) member _.TryGetValueAsync(doc: Document) = - async { - let! ct = Async.CancellationToken - let! currentVersion = doc.GetTextVersionAsync ct |> Async.AwaitTask + cancellableTask { + let! ct = CancellableTask.getCurrentCancellationToken () + let! currentVersion = doc.GetTextVersionAsync ct match cache.Get(doc.Id.ToString()) with | null -> return ValueNone @@ -60,9 +58,9 @@ type DocumentCache<'Value when 'Value: not struct>() = } member _.SetAsync(doc: Document, value: 'Value) = - async { - let! ct = Async.CancellationToken - let! currentVersion = doc.GetTextVersionAsync ct |> Async.AwaitTask + cancellableTask { + let! ct = CancellableTask.getCurrentCancellationToken () + let! currentVersion = doc.GetTextVersionAsync ct cache.Set(doc.Id.ToString(), (currentVersion, value), policy) } @@ -73,43 +71,47 @@ type DocumentCache<'Value when 'Value: not struct>() = [)>] type internal FSharpClassificationService [] () = - static let getLexicalClassifications (filePath: string, defines, text: SourceText, textSpan: TextSpan, ct) = - let text = text.GetSubText(textSpan) - let result = ImmutableArray.CreateBuilder() - - let tokenCallback = - fun (tok: FSharpToken) -> - let spanKind = - if tok.IsKeyword then - ClassificationTypeNames.Keyword - elif tok.IsNumericLiteral then - ClassificationTypeNames.NumericLiteral - elif tok.IsCommentTrivia then - ClassificationTypeNames.Comment - elif tok.IsStringLiteral then - ClassificationTypeNames.StringLiteral - else - ClassificationTypeNames.Text - - match RoslynHelpers.TryFSharpRangeToTextSpan(text, tok.Range) with - | Some span -> result.Add(ClassifiedSpan(TextSpan(textSpan.Start + span.Start, span.Length), spanKind)) - | _ -> () - - let flags = - FSharpLexerFlags.Default - &&& ~~~FSharpLexerFlags.Compiling - &&& ~~~FSharpLexerFlags.UseLexFilter - - FSharpLexer.Tokenize( - text.ToFSharpSourceText(), - tokenCallback, - filePath = filePath, - conditionalDefines = defines, - flags = flags, - ct = ct - ) - - result.ToImmutable() + static let getLexicalClassifications (filePath: string, defines, text: SourceText, textSpan: TextSpan) = + cancellableTask { + let! ct = CancellableTask.getCurrentCancellationToken () + + let text = text.GetSubText(textSpan) + let result = ImmutableArray.CreateBuilder() + + let tokenCallback = + fun (tok: FSharpToken) -> + let spanKind = + if tok.IsKeyword then + ClassificationTypeNames.Keyword + elif tok.IsNumericLiteral then + ClassificationTypeNames.NumericLiteral + elif tok.IsCommentTrivia then + ClassificationTypeNames.Comment + elif tok.IsStringLiteral then + ClassificationTypeNames.StringLiteral + else + ClassificationTypeNames.Text + + match RoslynHelpers.TryFSharpRangeToTextSpan(text, tok.Range) with + | Some span -> result.Add(ClassifiedSpan(TextSpan(textSpan.Start + span.Start, span.Length), spanKind)) + | _ -> () + + let flags = + FSharpLexerFlags.Default + &&& ~~~FSharpLexerFlags.Compiling + &&& ~~~FSharpLexerFlags.UseLexFilter + + FSharpLexer.Tokenize( + text.ToFSharpSourceText(), + tokenCallback, + filePath = filePath, + conditionalDefines = defines, + flags = flags, + ct = ct + ) + + return result.ToImmutable() + } static let addSemanticClassification sourceText @@ -144,7 +146,7 @@ type internal FSharpClassificationService [] () = static let toSemanticClassificationLookup (d: SemanticClassificationData) = let lookup = - System.Collections.Generic.Dictionary>() + Dictionary>() let f (dataItem: SemanticClassificationItem) = let items = @@ -159,7 +161,7 @@ type internal FSharpClassificationService [] () = d.ForEach(f) - System.Collections.ObjectModel.ReadOnlyDictionary lookup :> IReadOnlyDictionary<_, _> + Collections.ObjectModel.ReadOnlyDictionary lookup :> IReadOnlyDictionary<_, _> let semanticClassificationCache = new DocumentCache() @@ -174,43 +176,36 @@ type internal FSharpClassificationService [] () = result: List, cancellationToken: CancellationToken ) = - async { + cancellableTask { use _logBlock = Logger.LogBlock(LogEditorFunctionId.Classification_Syntactic) - let defines, langVersion = document.GetFSharpQuickDefinesAndLangVersion() - let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask + let! cancellationToken = CancellableTask.getCurrentCancellationToken () + + let defines, _= document.GetFSharpQuickDefinesAndLangVersion() + let! sourceText = document.GetTextAsync(cancellationToken) // For closed documents, only get classification for the text within the span. // This may be inaccurate for multi-line tokens such as string literals, but this is ok for now // as it's better than having to tokenize a big part of a file which in return will allocate a lot and hurt find all references performance. if not (document.Project.Solution.Workspace.IsDocumentOpen document.Id) then - result.AddRange(getLexicalClassifications (document.FilePath, defines, sourceText, textSpan, cancellationToken)) + let! classifiedSpans = getLexicalClassifications (document.FilePath, defines, sourceText, textSpan) + result.AddRange(classifiedSpans) else - result.AddRange( - Tokenizer.getClassifiedSpans ( - document.Id, - sourceText, - textSpan, - Some(document.FilePath), - defines, - Some langVersion, - cancellationToken - ) - ) - } - |> RoslynHelpers.StartAsyncUnitAsTask cancellationToken - - member this.AddSemanticClassificationsAsync + let! classifiedSpans = getLexicalClassifications (document.FilePath, defines, sourceText, textSpan) + result.AddRange(classifiedSpans) + } |> CancellableTask.startAsTask cancellationToken + + member _.AddSemanticClassificationsAsync ( document: Document, textSpan: TextSpan, result: List, cancellationToken: CancellationToken ) = - async { + cancellableTask { use _logBlock = Logger.LogBlock(LogEditorFunctionId.Classification_Semantic) - let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask + let! sourceText = document.GetTextAsync(cancellationToken) // If we are trying to get semantic classification for a document that is not open, get the results from the background and cache it. // We do this for find all references when it is populating results. @@ -232,9 +227,7 @@ type internal FSharpClassificationService [] () = let classificationData = checkResults.GetSemanticClassification(Some targetRange) addSemanticClassification sourceText textSpan classificationData result - } - |> Async.Ignore - |> RoslynHelpers.StartAsyncUnitAsTask cancellationToken + } |> CancellableTask.startAsTask cancellationToken // Do not perform classification if we don't have project options (#defines matter) member _.AdjustStaleClassification(_: SourceText, classifiedSpan: ClassifiedSpan) : ClassifiedSpan = classifiedSpan diff --git a/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs b/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs index 30a3d8a148d..52d6284aabe 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs @@ -13,6 +13,8 @@ open Microsoft.CodeAnalysis.Classification open FSharp.Compiler.EditorServices open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor.Implementation.Debugging +open Internal.Utilities.CancellableTasks.CancellableTaskBuilder +open Internal.Utilities.CancellableTasks [)>] type internal FSharpLanguageDebugInfoService [] () = @@ -45,19 +47,19 @@ type internal FSharpLanguageDebugInfoService [] () = interface IFSharpLanguageDebugInfoService with // FSROSLYNTODO: This is used to get function names in breakpoint window. It should return fully qualified function name and line offset from the start of the function. - member this.GetLocationInfoAsync(_, _, _) : Task = + member _.GetLocationInfoAsync(_, _, _) : Task = Task.FromResult(Unchecked.defaultof) - member this.GetDataTipInfoAsync + member _.GetDataTipInfoAsync ( document: Document, position: int, cancellationToken: CancellationToken ) : Task = - async { + cancellableTask { let defines, langVersion = document.GetFSharpQuickDefinesAndLangVersion() - let! cancellationToken = Async.CancellationToken - let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask + let! cancellationToken = CancellableTask.getCurrentCancellationToken () + let! sourceText = document.GetTextAsync(cancellationToken) let textSpan = TextSpan.FromBounds(0, sourceText.Length) let classifiedSpans = @@ -77,5 +79,4 @@ type internal FSharpLanguageDebugInfoService [] () = | Some textSpan -> FSharpDebugDataTipInfo(textSpan, sourceText.GetSubText(textSpan).ToString()) return result - } - |> RoslynHelpers.StartAsyncAsTask(cancellationToken) + } |> CancellableTask.start cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs index 9607840abf4..a38f03b9d38 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs @@ -2,7 +2,6 @@ namespace Microsoft.VisualStudio.FSharp.Editor -open System open System.Composition open System.Collections.Immutable open System.Collections.Generic @@ -13,8 +12,9 @@ open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics -open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Diagnostics +open Internal.Utilities.CancellableTasks.CancellableTaskBuilder +open Internal.Utilities.CancellableTasks [] type internal DiagnosticsType = @@ -52,16 +52,16 @@ type internal FSharpDocumentDiagnosticAnalyzer [] () = } static member GetDiagnostics(document: Document, diagnosticType: DiagnosticsType) = - async { - let! ct = Async.CancellationToken + cancellableTask { + let! ct = CancellableTask.getCurrentCancellationToken () let! parseResults = document.GetFSharpParseResultsAsync("GetDiagnostics") - let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask + let! sourceText = document.GetTextAsync(ct) let filePath = document.FilePath let! errors = - async { + cancellableTask { match diagnosticType with | DiagnosticsType.Semantic -> let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync("GetDiagnostics") @@ -106,20 +106,18 @@ type internal FSharpDocumentDiagnosticAnalyzer [] () = interface IFSharpDocumentDiagnosticAnalyzer with - member this.AnalyzeSyntaxAsync(document: Document, cancellationToken: CancellationToken) : Task> = - if document.Project.IsFSharpMetadata then - Task.FromResult(ImmutableArray.Empty) - else - FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Syntax) - |> liftAsync - |> Async.map (Option.defaultValue ImmutableArray.Empty) - |> RoslynHelpers.StartAsyncAsTask cancellationToken - - member this.AnalyzeSemanticsAsync(document: Document, cancellationToken: CancellationToken) : Task> = - if document.Project.IsFSharpMiscellaneousOrMetadata && not document.IsFSharpScript then - Task.FromResult(ImmutableArray.Empty) - else - FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Semantic) - |> liftAsync - |> Async.map (Option.defaultValue ImmutableArray.Empty) - |> RoslynHelpers.StartAsyncAsTask cancellationToken + member _.AnalyzeSyntaxAsync(document: Document, cancellationToken: CancellationToken) : Task> = + cancellableTask { + if document.Project.IsFSharpMetadata then + return ImmutableArray.Empty + else + return! FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Syntax) + } |> CancellableTask.start cancellationToken + + member _.AnalyzeSemanticsAsync(document: Document, cancellationToken: CancellationToken) : Task> = + cancellableTask { + if document.Project.IsFSharpMiscellaneousOrMetadata && not document.IsFSharpScript then + return ImmutableArray.Empty + else + return! FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Semantic) + } |> CancellableTask.start cancellationToken \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Hints/HintService.fs b/vsintegration/src/FSharp.Editor/Hints/HintService.fs index d4c49ca9e07..cfc2dfbf66c 100644 --- a/vsintegration/src/FSharp.Editor/Hints/HintService.fs +++ b/vsintegration/src/FSharp.Editor/Hints/HintService.fs @@ -7,6 +7,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.VisualStudio.FSharp.Editor open FSharp.Compiler.Symbols open Hints +open Internal.Utilities.CancellableTasks module HintService = @@ -15,15 +16,15 @@ module HintService = let getHintsPerKind hintKind = match hintKind, symbol with | HintKind.TypeHint, (:? FSharpMemberOrFunctionOrValue as symbol) -> - symbolUses |> Seq.collect (InlineTypeHints(parseResults, symbol)).getHints + symbolUses |> Seq.collect (InlineTypeHints(parseResults, symbol)).GetHints | HintKind.ReturnTypeHint, (:? FSharpMemberOrFunctionOrValue as symbol) -> - symbolUses |> Seq.collect (InlineReturnTypeHints(parseResults, symbol).getHints) + symbolUses |> Seq.collect (InlineReturnTypeHints(parseResults, symbol).GetHints) | HintKind.ParameterNameHint, (:? FSharpMemberOrFunctionOrValue as symbol) -> symbolUses - |> Seq.collect (InlineParameterNameHints(parseResults).getHintsForMemberOrFunctionOrValue sourceText symbol) + |> Seq.collect (InlineParameterNameHints(parseResults).GetHintsForMemberOrFunctionOrValue sourceText symbol) | HintKind.ParameterNameHint, (:? FSharpUnionCase as symbol) -> symbolUses - |> Seq.collect (InlineParameterNameHints(parseResults).getHintsForUnionCase symbol) + |> Seq.collect (InlineParameterNameHints(parseResults).GetHintsForUnionCase symbol) | _ -> [] let rec getHints hintKinds acc = @@ -37,11 +38,12 @@ module HintService = let hints = getHints sourceText parseResults hintKinds symbolUses symbol Seq.concat hints - let getHintsForDocument sourceText (document: Document) hintKinds userOpName cancellationToken = - async { + let getHintsForDocument sourceText (document: Document) hintKinds userOpName = + cancellableTask { if isSignatureFile document.FilePath then return [] else + let! cancellationToken = CancellableTask.getCurrentCancellationToken () let! parseResults, checkResults = document.GetFSharpParseAndCheckResultsAsync userOpName return diff --git a/vsintegration/src/FSharp.Editor/Hints/Hints.fs b/vsintegration/src/FSharp.Editor/Hints/Hints.fs index 9b356db9855..08792fb967d 100644 --- a/vsintegration/src/FSharp.Editor/Hints/Hints.fs +++ b/vsintegration/src/FSharp.Editor/Hints/Hints.fs @@ -2,9 +2,9 @@ namespace Microsoft.VisualStudio.FSharp.Editor.Hints -open System.Threading open Microsoft.CodeAnalysis open FSharp.Compiler.Text +open Internal.Utilities.CancellableTasks module Hints = @@ -19,7 +19,7 @@ module Hints = Kind: HintKind Range: range Parts: TaggedText list - GetTooltip: Document -> Async + GetTooltip: Document -> CancellableTask } let serialize kind = diff --git a/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs b/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs index 22affc41a77..4486e569b8d 100644 --- a/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs +++ b/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs @@ -9,11 +9,12 @@ open FSharp.Compiler.EditorServices open FSharp.Compiler.Symbols open FSharp.Compiler.Text open Hints +open Internal.Utilities.CancellableTasks type InlineParameterNameHints(parseResults: FSharpParseFileResults) = let getTooltip (symbol: FSharpSymbol) _ = - async { + cancellableTask { // This brings little value as of now. Basically just discerns fields from parameters // and fills the tooltip bubble which otherwise looks like a visual glitch. // @@ -109,7 +110,7 @@ type InlineParameterNameHints(parseResults: FSharpParseFileResults) = // If a case does not use field names, don't even bother getting applied argument ranges && symbol.Fields |> Seq.exists fieldNameExists - member _.getHintsForMemberOrFunctionOrValue + member _.GetHintsForMemberOrFunctionOrValue (sourceText: SourceText) (symbol: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) @@ -146,7 +147,7 @@ type InlineParameterNameHints(parseResults: FSharpParseFileResults) = else [] - member _.getHintsForUnionCase (symbol: FSharpUnionCase) (symbolUse: FSharpSymbolUse) = + member _.GetHintsForUnionCase (symbol: FSharpUnionCase) (symbolUse: FSharpSymbolUse) = if isUnionCaseValidForHint symbol symbolUse then let fields = Seq.toList symbol.Fields diff --git a/vsintegration/src/FSharp.Editor/Hints/InlineReturnTypeHints.fs b/vsintegration/src/FSharp.Editor/Hints/InlineReturnTypeHints.fs index a5796b6d196..a05c2bb74ca 100644 --- a/vsintegration/src/FSharp.Editor/Hints/InlineReturnTypeHints.fs +++ b/vsintegration/src/FSharp.Editor/Hints/InlineReturnTypeHints.fs @@ -7,6 +7,7 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Symbols open FSharp.Compiler.Text open Hints +open Internal.Utilities.CancellableTasks type InlineReturnTypeHints(parseFileResults: FSharpParseFileResults, symbol: FSharpMemberOrFunctionOrValue) = @@ -20,7 +21,7 @@ type InlineReturnTypeHints(parseFileResults: FSharpParseFileResults, symbol: FSh ]) let getTooltip _ = - async { + cancellableTask { let typeAsString = symbol.ReturnParameter.Type.TypeDefinition.ToString() let text = $"type {typeAsString}" return [ TaggedText(TextTag.Text, text) ] @@ -38,7 +39,7 @@ type InlineReturnTypeHints(parseFileResults: FSharpParseFileResults, symbol: FSh let isValidForHint (symbol: FSharpMemberOrFunctionOrValue) = symbol.IsFunction - member _.getHints(symbolUse: FSharpSymbolUse) = + member _.GetHints(symbolUse: FSharpSymbolUse) = [ if isValidForHint symbol then yield! diff --git a/vsintegration/src/FSharp.Editor/Hints/InlineTypeHints.fs b/vsintegration/src/FSharp.Editor/Hints/InlineTypeHints.fs index a29b2302bb7..5a902067cf3 100644 --- a/vsintegration/src/FSharp.Editor/Hints/InlineTypeHints.fs +++ b/vsintegration/src/FSharp.Editor/Hints/InlineTypeHints.fs @@ -8,6 +8,7 @@ open FSharp.Compiler.Symbols open FSharp.Compiler.Text open FSharp.Compiler.Text.Position open Hints +open Internal.Utilities.CancellableTasks type InlineTypeHints(parseResults: FSharpParseFileResults, symbol: FSharpMemberOrFunctionOrValue) = @@ -22,7 +23,7 @@ type InlineTypeHints(parseResults: FSharpParseFileResults, symbol: FSharpMemberO | None -> [] let getTooltip _ = - async { + cancellableTask { // Done this way because I am not sure if we want to show full-blown types everywhere, // e.g. Microsoft.FSharp.Core.string instead of string. // On the other hand, for user types this could be useful. @@ -83,7 +84,7 @@ type InlineTypeHints(parseResults: FSharpParseFileResults, symbol: FSharpMemberO && isNotAfterDot && isNotTypeAlias - member _.getHints symbolUse = + member _.GetHints symbolUse = [ if isValidForHint symbolUse then getHint symbol symbolUse diff --git a/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs b/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs index 1eb05429ea6..8fa842b49d7 100644 --- a/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs +++ b/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs @@ -2,16 +2,15 @@ namespace Microsoft.VisualStudio.FSharp.Editor.Hints -open System open System.Collections.Immutable open System.Threading -open System.Threading.Tasks open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints open Microsoft.VisualStudio.FSharp.Editor open Microsoft.CodeAnalysis open FSharp.Compiler.Text open Hints +open Internal.Utilities.CancellableTasks module NativeToRoslynHintConverter = @@ -25,14 +24,17 @@ module NativeToRoslynHintConverter = let text = taggedText.Text RoslynTaggedText(tag, text) - let nativeToRoslynFunc nativeFunc = - Func>>(fun doc ct -> - nativeFunc doc - |> Async.map (List.map nativeToRoslynText >> ImmutableArray.CreateRange) - |> fun comp -> Async.StartAsTask(comp, cancellationToken = ct)) - let convert sourceText hint = - let span = rangeToSpan hint.Range sourceText - let displayParts = hint.Parts |> Seq.map nativeToRoslynText - let getDescription = hint.GetTooltip |> nativeToRoslynFunc - FSharpInlineHint(span, displayParts.ToImmutableArray(), getDescription) + + let getDescriptionAsync (doc: Document) (ct: CancellationToken) = + cancellableTask { + let! taggedText = hint.GetTooltip doc + return taggedText |> List.map nativeToRoslynText |> ImmutableArray.CreateRange + } |> CancellableTask.start ct + + cancellableTask { + let span = rangeToSpan hint.Range sourceText + let displayParts = hint.Parts |> Seq.map nativeToRoslynText + + return FSharpInlineHint(span, displayParts.ToImmutableArray(), getDescriptionAsync) + } diff --git a/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs b/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs index 53820400d94..0cc76527d1a 100644 --- a/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs +++ b/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs @@ -7,6 +7,8 @@ open System.ComponentModel.Composition open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints open Microsoft.VisualStudio.FSharp.Editor open Microsoft.VisualStudio.FSharp.Editor.Telemetry +open Internal.Utilities.CancellableTasks +open System.Threading.Tasks // So the Roslyn interface is called IFSharpInlineHintsService // but our implementation is called just HintsService. @@ -20,21 +22,23 @@ type internal RoslynAdapter [] (settings: EditorOptions) = interface IFSharpInlineHintsService with member _.GetInlineHintsAsync(document, _, cancellationToken) = - async { + cancellableTask { let hintKinds = OptionParser.getHintKinds settings.Advanced if hintKinds.IsEmpty then return ImmutableArray.Empty else + let! cancellationToken = CancellableTask.getCurrentCancellationToken () + let hintKindsSerialized = hintKinds |> Set.map Hints.serialize |> String.concat "," TelemetryReporter.reportEvent "hints" [ ("hints.kinds", hintKindsSerialized) ] - let! sourceText = document.GetTextAsync cancellationToken |> Async.AwaitTask + let! sourceText = document.GetTextAsync cancellationToken let! nativeHints = HintService.getHintsForDocument sourceText document hintKinds userOpName cancellationToken - let roslynHints = - nativeHints |> Seq.map (NativeToRoslynHintConverter.convert sourceText) + let tasks = nativeHints |> Seq.map (fun hint -> NativeToRoslynHintConverter.convert sourceText hint cancellationToken) + + let! roslynHints = Task.WhenAll(tasks) return roslynHints.ToImmutableArray() - } - |> RoslynHelpers.StartAsyncAsTask cancellationToken + } |> CancellableTask.start cancellationToken diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index c9b25aad44d..57cbd86265a 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -14,21 +14,18 @@ open FSharp.Compiler open FSharp.Compiler.CodeAnalysis open FSharp.NativeInterop open Microsoft.VisualStudio -open Microsoft.VisualStudio.Editor open Microsoft.VisualStudio.FSharp.Editor open Microsoft.VisualStudio.LanguageServices open Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService open Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem -open Microsoft.VisualStudio.LanguageServices.ProjectSystem open Microsoft.VisualStudio.Shell open Microsoft.VisualStudio.Shell.Interop open Microsoft.VisualStudio.Text.Outlining open Microsoft.CodeAnalysis.ExternalAccess.FSharp -open Microsoft.CodeAnalysis.Host open Microsoft.CodeAnalysis.Host.Mef -open Microsoft.VisualStudio.FSharp.Editor.WorkspaceExtensions open Microsoft.VisualStudio.FSharp.Editor.Telemetry -open System.Threading.Tasks +open Internal.Utilities.CancellableTasks.CancellableTaskBuilder +open Internal.Utilities.CancellableTasks #nowarn "9" // NativePtr.toNativeInt #nowarn "57" // Experimental stuff @@ -50,9 +47,9 @@ type internal RoamingProfileStorageLocation(keyName: string) = unsubstitutedKeyName.Replace("%LANGUAGE%", substituteLanguageName) -[] +[] [, ServiceLayer.Default)>] -type internal FSharpWorkspaceServiceFactory [] +type internal FSharpWorkspaceServiceFactory [] ( metadataAsSourceService: FSharpMetadataAsSourceService ) = @@ -76,7 +73,7 @@ type internal FSharpWorkspaceServiceFactory [ try let md = - Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetMetadata( + LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetMetadata( workspace, path, timeStamp @@ -238,19 +235,19 @@ type private FSharpSolutionEvents(projectManager: FSharpProjectOptionsManager, m member _.OnQueryUnloadProject(_, _) = VSConstants.E_NOTIMPL -[, Microsoft.CodeAnalysis.Host.Mef.ServiceLayer.Default)>] +[, ServiceLayer.Default)>] type internal FSharpSettingsFactory [] (settings: EditorOptions) = - interface Microsoft.CodeAnalysis.Host.Mef.IWorkspaceServiceFactory with + interface Host.Mef.IWorkspaceServiceFactory with member _.CreateService(_) = upcast settings [] -[, "F# Tools", "F# Interactive", 6000s, 6001s, true)>] // true = supports automation +[, "F# Tools", "F# Interactive", 6000s, 6001s, true)>] // true = supports automation [] // <-- resource ID for localised name -[, +[, Orientation = ToolWindowOrientation.Bottom, Style = VsDockStyle.Tabbed, PositionX = 0, @@ -308,15 +305,15 @@ type internal FSharpPackage() as this = inherit AbstractPackage() let mutable vfsiToolWindow = - Unchecked.defaultof + Unchecked.defaultof let GetToolWindowAsITestVFSI () = if vfsiToolWindow = Unchecked.defaultof<_> then vfsiToolWindow <- - this.FindToolWindow(typeof, 0, true) - :?> Microsoft.VisualStudio.FSharp.Interactive.FsiToolWindow + this.FindToolWindow(typeof, 0, true) + :?> FSharp.Interactive.FsiToolWindow - vfsiToolWindow :> Microsoft.VisualStudio.FSharp.Interactive.ITestVFSI + vfsiToolWindow :> FSharp.Interactive.ITestVFSI let mutable solutionEventsOpt = None @@ -326,105 +323,78 @@ type internal FSharpPackage() as this = #endif // FSI-LINKAGE-POINT: unsited init - do Microsoft.VisualStudio.FSharp.Interactive.Hooks.fsiConsoleWindowPackageCtorUnsited (this :> Package) + do FSharp.Interactive.Hooks.fsiConsoleWindowPackageCtorUnsited (this :> Package) override this.InitializeAsync(cancellationToken: CancellationToken, progress: IProgress) : Tasks.Task = // `base.` methods can't be called in the `async` builder, so we have to cache it let baseInitializeAsync = base.InitializeAsync(cancellationToken, progress) - let task = - async { - do! baseInitializeAsync |> Async.AwaitTask - - let! commandService = this.GetServiceAsync(typeof) |> Async.AwaitTask // FSI-LINKAGE-POINT - let commandService = commandService :?> OleMenuCommandService - - let packageInit () = - // FSI-LINKAGE-POINT: sited init - Microsoft.VisualStudio.FSharp.Interactive.Hooks.fsiConsoleWindowPackageInitalizeSited (this :> Package) commandService - - // FSI-LINKAGE-POINT: private method GetDialogPage forces fsi options to be loaded - let _fsiPropertyPage = - this.GetDialogPage(typeof) - - let workspace = this.ComponentModel.GetService() - - let _ = - this.ComponentModel.DefaultExportProvider.GetExport() + foregroundCancellableTask { + do! baseInitializeAsync - let optionsManager = - workspace - .Services - .GetService() - .FSharpProjectOptionsManager + let! commandService = this.GetServiceAsync(typeof) + let commandService = commandService :?> OleMenuCommandService - let metadataAsSource = - this - .ComponentModel - .DefaultExportProvider - .GetExport() - .Value + // Switch to UI thread + do! this.JoinableTaskFactory.SwitchToMainThreadAsync() - let solution = this.GetServiceAsync(typeof).Result - let solution = solution :?> IVsSolution - let solutionEvents = FSharpSolutionEvents(optionsManager, metadataAsSource) - let rdt = this.GetServiceAsync(typeof).Result - let rdt = rdt :?> IVsRunningDocumentTable + // FSI-LINKAGE-POINT: sited init + FSharp.Interactive.Hooks.fsiConsoleWindowPackageInitalizeSited (this :> Package) commandService - solutionEventsOpt <- Some(solutionEvents) - solution.AdviseSolutionEvents(solutionEvents) |> ignore + // FSI-LINKAGE-POINT: private method GetDialogPage forces fsi options to be loaded + let _fsiPropertyPage = this.GetDialogPage(typeof) - let projectContextFactory = - this.ComponentModel.GetService() + let workspace = this.ComponentModel.GetService() + let _ = this.ComponentModel.DefaultExportProvider.GetExport() + let optionsManager = workspace.Services.GetService().FSharpProjectOptionsManager + let metadataAsSource = this.ComponentModel.DefaultExportProvider.GetExport().Value - let miscFilesWorkspace = - this.ComponentModel.GetService() + let! solution = this.GetServiceAsync(typeof) + let solution = solution :?> IVsSolution - let _singleFileWorkspaceMap = - new SingleFileWorkspaceMap( - FSharpMiscellaneousFileService(workspace, miscFilesWorkspace, projectContextFactory), - rdt - ) + let solutionEvents = FSharpSolutionEvents(optionsManager, metadataAsSource) - let _legacyProjectWorkspaceMap = - new LegacyProjectWorkspaceMap(solution, optionsManager, projectContextFactory) + let! rdt = this.GetServiceAsync(typeof) + let rdt = rdt :?> IVsRunningDocumentTable - () + solutionEventsOpt <- Some(solutionEvents) + solution.AdviseSolutionEvents(solutionEvents) |> ignore - let awaiter = this.JoinableTaskFactory.SwitchToMainThreadAsync().GetAwaiter() - - if awaiter.IsCompleted then - packageInit () // already on the UI thread - else - awaiter.OnCompleted(fun () -> packageInit ()) - - } - |> Async.StartAsTask + let projectContextFactory = this.ComponentModel.GetService() + let miscFilesWorkspace = this.ComponentModel.GetService() + do SingleFileWorkspaceMap( + FSharpMiscellaneousFileService( + workspace, + miscFilesWorkspace, + projectContextFactory + ), + rdt) |> ignore - upcast task // convert Task to Task + } |> CancellableTask.startAsTask cancellationToken + - override this.RoslynLanguageName = FSharpConstants.FSharpLanguageName + override _.RoslynLanguageName = FSharpConstants.FSharpLanguageName (*override this.CreateWorkspace() = this.ComponentModel.GetService() *) override this.CreateLanguageService() = FSharpLanguageService(this) override this.CreateEditorFactories() = seq { yield FSharpEditorFactory(this) :> IVsEditorFactory } - override this.RegisterMiscellaneousFilesWorkspaceInformation(miscFilesWorkspace) = + override _.RegisterMiscellaneousFilesWorkspaceInformation(miscFilesWorkspace) = miscFilesWorkspace.RegisterLanguage(Guid(FSharpConstants.languageServiceGuidString), FSharpConstants.FSharpLanguageName, ".fsx") - interface Microsoft.VisualStudio.FSharp.Interactive.ITestVFSI with - member this.SendTextInteraction(s: string) = + interface FSharp.Interactive.ITestVFSI with + member _.SendTextInteraction(s: string) = GetToolWindowAsITestVFSI().SendTextInteraction(s) - member this.GetMostRecentLines(n: int) : string[] = + member _.GetMostRecentLines(n: int) : string[] = GetToolWindowAsITestVFSI().GetMostRecentLines(n) [] type internal FSharpLanguageService(package: FSharpPackage) = inherit AbstractLanguageService(package) - override this.Initialize() = + override _.Initialize() = base.Initialize() let globalOptions = @@ -459,7 +429,7 @@ type internal FSharpLanguageService(package: FSharpPackage) = override _.DebuggerLanguageId = CompilerEnvironment.GetDebuggerLanguageID() override _.CreateContext(_, _, _, _, _) = - raise (System.NotImplementedException()) + raise (NotImplementedException()) override this.SetupNewTextView(textView) = base.SetupNewTextView(textView) @@ -476,10 +446,10 @@ type internal FSharpLanguageService(package: FSharpPackage) = outliningManager.Enabled <- settings.Advanced.IsOutliningEnabled [] -[)>] -type internal HackCpsCommandLineChanges [] +[)>] +type internal HackCpsCommandLineChanges [] ( - [)>] workspace: VisualStudioWorkspace + [)>] workspace: VisualStudioWorkspace ) = static let projectDisplayNameOf projectFileName = @@ -488,7 +458,7 @@ type internal HackCpsCommandLineChanges [] + [] /// This handles commandline change notifications from the Dotnet Project-system /// Prior to VS 15.7 path contained path to project file, post 15.7 contains target binpath /// binpath is more accurate because a project file can have multiple in memory projects based on configuration @@ -511,11 +481,7 @@ type internal HackCpsCommandLineChanges [ projectId | false, _ -> - Microsoft - .CodeAnalysis - .ExternalAccess - .FSharp - .LanguageServices + LanguageServices .FSharpVisualStudioWorkspaceExtensions .GetOrCreateProjectIdForPath(workspace, path, projectDisplayNameOf path) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs b/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs index 00c85540299..c75e845ba7e 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs @@ -24,6 +24,8 @@ open Microsoft.VisualStudio.Core.Imaging open Microsoft.VisualStudio.Imaging open Microsoft.CodeAnalysis.ExternalAccess.FSharp +open Internal.Utilities.CancellableTasks.CancellableTaskBuilder +open Internal.Utilities.CancellableTasks type private FSharpGlyph = FSharp.Compiler.EditorServices.FSharpGlyph type private Glyph = Microsoft.CodeAnalysis.ExternalAccess.FSharp.FSharpGlyph @@ -698,33 +700,33 @@ module internal Tokenizer = langVersion, cancellationToken: CancellationToken ) : ResizeArray = - let result = new ResizeArray() - - try - let sourceTokenizer = FSharpSourceTokenizer(defines, fileName, langVersion) - let lines = sourceText.Lines - let sourceTextData = getSourceTextData (documentKey, defines, lines.Count) - - let startLine = lines.GetLineFromPosition(textSpan.Start).LineNumber - let endLine = lines.GetLineFromPosition(textSpan.End).LineNumber - - let lineDataResults = - getFromRefreshedTokenCache (lines, startLine, endLine, sourceTokenizer, sourceTextData, cancellationToken) - - for lineData, _ in lineDataResults do - result.AddRange( - lineData.ClassifiedSpans - |> Array.filter (fun token -> - textSpan.Contains(token.TextSpan.Start) - || textSpan.Contains(token.TextSpan.End - 1) - || (token.TextSpan.Start <= textSpan.Start && textSpan.End <= token.TextSpan.End)) - ) + let result = new ResizeArray() + + try + let sourceTokenizer = FSharpSourceTokenizer(defines, fileName, langVersion) + let lines = sourceText.Lines + let sourceTextData = getSourceTextData (documentKey, defines, lines.Count) + + let startLine = lines.GetLineFromPosition(textSpan.Start).LineNumber + let endLine = lines.GetLineFromPosition(textSpan.End).LineNumber + + let lineDataResults = + getFromRefreshedTokenCache (lines, startLine, endLine, sourceTokenizer, sourceTextData, cancellationToken) + + for lineData, _ in lineDataResults do + result.AddRange( + lineData.ClassifiedSpans + |> Array.filter (fun token -> + textSpan.Contains(token.TextSpan.Start) + || textSpan.Contains(token.TextSpan.End - 1) + || (token.TextSpan.Start <= textSpan.Start && textSpan.End <= token.TextSpan.End)) + ) - with - | :? System.OperationCanceledException -> reraise () - | ex -> Assert.Exception(ex) + with + | :? System.OperationCanceledException -> reraise () + | ex -> Assert.Exception(ex) - result + result /// Returns symbol at a given position. let private getSymbolFromSavedTokens diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs index bd8c79a9a5e..b5417ecb3f5 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs @@ -147,7 +147,7 @@ type internal FSharpNavigateToSearchService [] let! ct = Async.CancellationToken let! sourceText = document.GetTextAsync ct |> Async.AwaitTask - let processItem item = + let processItem (item: NavigableItem) = asyncMaybe { do! Option.guard (kinds.Contains(navigateToItemKindToRoslynKind item.Kind)) diff --git a/vsintegration/tests/FSharp.Editor.Tests/DocumentDiagnosticAnalyzerTests.fs b/vsintegration/tests/FSharp.Editor.Tests/DocumentDiagnosticAnalyzerTests.fs index f5a3bd134fe..4d4929a81b3 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/DocumentDiagnosticAnalyzerTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/DocumentDiagnosticAnalyzerTests.fs @@ -7,22 +7,25 @@ open Microsoft.CodeAnalysis open Microsoft.VisualStudio.FSharp.Editor open FSharp.Editor.Tests.Helpers open FSharp.Test +open Internal.Utilities.CancellableTasks +open System.Threading type DocumentDiagnosticAnalyzerTests() = let startMarker = "(*start*)" let endMarker = "(*end*)" let getDiagnostics (fileContents: string) = - async { - let document = - RoslynTestHelpers.CreateSolution(fileContents) - |> RoslynTestHelpers.GetSingleDocument - - let! syntacticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Syntax) - let! semanticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Semantic) - return syntacticDiagnostics.AddRange(semanticDiagnostics) - } - |> Async.RunSynchronously + let task = + cancellableTask { + let document = + RoslynTestHelpers.CreateSolution(fileContents) + |> RoslynTestHelpers.GetSingleDocument + + let! syntacticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Syntax) + let! semanticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Semantic) + return syntacticDiagnostics.AddRange(semanticDiagnostics) + } |> CancellableTask.start CancellationToken.None + task.Result member private this.VerifyNoErrors(fileContents: string, ?additionalFlags: string[]) = let errors = getDiagnostics fileContents diff --git a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj index d21d6e3c6eb..3e5c4de421e 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj +++ b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj @@ -6,6 +6,7 @@ false false true + $(NoWarn);FS3511 diff --git a/vsintegration/tests/FSharp.Editor.Tests/Hints/HintTestFramework.fs b/vsintegration/tests/FSharp.Editor.Tests/Hints/HintTestFramework.fs index 9a0111810dd..f3a28d0641a 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/Hints/HintTestFramework.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/Hints/HintTestFramework.fs @@ -7,6 +7,8 @@ open Microsoft.VisualStudio.FSharp.Editor open Microsoft.VisualStudio.FSharp.Editor.Hints open Hints open FSharp.Editor.Tests.Helpers +open Internal.Utilities.CancellableTasks +open System.Threading module HintTestFramework = @@ -60,21 +62,22 @@ module HintTestFramework = project.Documents let getHints (document: Document) hintKinds = - async { - let! ct = Async.CancellationToken - - let getTooltip hint = - async { - let! roslynTexts = hint.GetTooltip document - return roslynTexts |> Seq.map (fun roslynText -> roslynText.Text) |> String.concat "" - } - - let! sourceText = document.GetTextAsync ct |> Async.AwaitTask - let! hints = HintService.getHintsForDocument sourceText document hintKinds "test" ct - let! tooltips = hints |> Seq.map getTooltip |> Async.Parallel - return tooltips |> Seq.zip hints |> Seq.map convert - } - |> Async.RunSynchronously + let task = + cancellableTask { + let! ct = CancellableTask.getCurrentCancellationToken () + + let getTooltip hint = + async { + let! roslynTexts = hint.GetTooltip document + return roslynTexts |> Seq.map (fun roslynText -> roslynText.Text) |> String.concat "" + } + + let! sourceText = document.GetTextAsync ct |> Async.AwaitTask + let! hints = HintService.getHintsForDocument sourceText document hintKinds "test" ct + let! tooltips = hints |> Seq.map getTooltip |> Async.Parallel + return tooltips |> Seq.zip hints |> Seq.map convert + } |> CancellableTask.start CancellationToken.None + task.Result let getTypeHints document = getHints document (set [ HintKind.TypeHint ]) From 187ea2e13008f1a488feb7592d734f66a767f61b Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Fri, 5 May 2023 15:02:21 +0200 Subject: [PATCH 02/20] iteration --- src/Compiler/Utilities/CancellableTask.fs | 8 +- .../Completion/CompletionProvider.fs | 142 +++++++++--------- .../Completion/CompletionService.fs | 6 +- .../Completion/CompletionUtils.fs | 9 +- .../Diagnostics/UnusedDeclarationsAnalyzer.fs | 13 +- .../src/FSharp.Editor/FSharp.Editor.fsproj | 2 +- ...Adapter.fs => FSharpInlineHintsService.fs} | 16 +- .../src/FSharp.Editor/Hints/HintService.fs | 6 +- .../src/FSharp.Editor/Hints/Hints.fs | 2 +- .../src/FSharp.Editor/Hints/OptionParser.fs | 2 +- .../Navigation/NavigateToSearchService.fs | 29 ++-- .../QuickInfo/QuickInfoProvider.fs | 26 ++-- .../CompletionProviderTests.fs | 57 ++++--- .../FsxCompletionProviderTests.fs | 7 +- 14 files changed, 176 insertions(+), 149 deletions(-) rename vsintegration/src/FSharp.Editor/Hints/{RoslynAdapter.fs => FSharpInlineHintsService.fs} (79%) diff --git a/src/Compiler/Utilities/CancellableTask.fs b/src/Compiler/Utilities/CancellableTask.fs index 7d505b20df7..33e18d35651 100644 --- a/src/Compiler/Utilities/CancellableTask.fs +++ b/src/Compiler/Utilities/CancellableTask.fs @@ -692,23 +692,23 @@ module CancellableTasks = /// Turn option into "awaitable", will return cancelled task if None /// /// Option instance to bind on - member inline _.Source(s: 'T option) = + (*member inline _.Source(s: 'T option) = (fun (_ct: CancellationToken) -> match s with | Some x -> Task.FromResult<'T>(x).GetAwaiter() | None -> Task.FromCanceled<'T>(CancellationToken(true)).GetAwaiter() - ) + )*) /// /// Turn a value option into "awaitable", will return cancelled task if None /// /// Option instance to bind on - member inline _.Source(s: 'T voption) = + (*member inline _.Source(s: 'T voption) = (fun (_ct: CancellationToken) -> match s with | ValueSome x -> Task.FromResult<'T>(x).GetAwaiter() | ValueNone -> Task.FromCanceled<'T>(CancellationToken(true)).GetAwaiter() - ) + )*) /// Allows the computation expression to turn other types into other types /// diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index 963997eb804..f600c7f3d2b 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -20,6 +20,7 @@ open FSharp.Compiler.EditorServices open FSharp.Compiler.Syntax open FSharp.Compiler.Text open FSharp.Compiler.Tokenization +open Internal.Utilities.CancellableTasks module Logger = Microsoft.VisualStudio.FSharp.Editor.Logger @@ -103,7 +104,8 @@ type internal FSharpCompletionProvider caretPosition: int, trigger: CompletionTriggerKind, getInfo: (unit -> DocumentId * string * string list * string option), - intelliSenseOptions: IntelliSenseOptions + intelliSenseOptions: IntelliSenseOptions, + cancellationToken: CancellationToken ) = if caretPosition = 0 then false @@ -129,7 +131,7 @@ type internal FSharpCompletionProvider else let documentId, filePath, defines, langVersion = getInfo () - CompletionUtils.shouldProvideCompletion (documentId, filePath, defines, langVersion, sourceText, triggerPosition) + CompletionUtils.shouldProvideCompletion (documentId, filePath, defines, langVersion, sourceText, triggerPosition, cancellationToken) && (triggerChar = '.' || (intelliSenseOptions.ShowAfterCharIsTyped && CompletionUtils.isStartingNewWord (sourceText, triggerPosition))) @@ -141,12 +143,13 @@ type internal FSharpCompletionProvider getAllSymbols: FSharpCheckFileResults -> AssemblySymbol list ) = - asyncMaybe { + cancellableTask { let! parseResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync("ProvideCompletionsAsyncAux") - |> liftAsync - let! sourceText = document.GetTextAsync() + let! ct = CancellableTask.getCurrentCancellationToken () + + let! sourceText = document.GetTextAsync(ct) let textLines = sourceText.Lines let caretLinePos = textLines.GetLinePosition(caretPosition) let caretLine = textLines.GetLineFromPosition(caretPosition) @@ -290,40 +293,42 @@ type internal FSharpCompletionProvider let defines, langVersion = document.GetFSharpQuickDefinesAndLangVersion() (documentId, document.FilePath, defines, Some langVersion) - FSharpCompletionProvider.ShouldTriggerCompletionAux(sourceText, caretPosition, trigger.Kind, getInfo, settings.IntelliSense) + FSharpCompletionProvider.ShouldTriggerCompletionAux(sourceText, caretPosition, trigger.Kind, getInfo, settings.IntelliSense, CancellationToken.None) override _.ProvideCompletionsAsync(context: Completion.CompletionContext) = - asyncMaybe { + cancellableTask { use _logBlock = Logger.LogBlockMessage context.Document.Name LogEditorFunctionId.Completion_ProvideCompletionsAsync + let! ct = CancellableTask.getCurrentCancellationToken () + let document = context.Document - let! sourceText = context.Document.GetTextAsync(context.CancellationToken) + let! sourceText = context.Document.GetTextAsync(ct) let defines, langVersion = document.GetFSharpQuickDefinesAndLangVersion() - do! - Option.guard ( - CompletionUtils.shouldProvideCompletion ( - document.Id, - document.FilePath, - defines, - Some langVersion, - sourceText, - context.Position - ) + let shouldProvideCompetion = + CompletionUtils.shouldProvideCompletion ( + document.Id, + document.FilePath, + defines, + Some langVersion, + sourceText, + context.Position, + ct ) - let getAllSymbols (fileCheckResults: FSharpCheckFileResults) = - if settings.IntelliSense.IncludeSymbolsFromUnopenedNamespacesOrModules then - assemblyContentProvider.GetAllEntitiesInProjectAndReferencedAssemblies(fileCheckResults) - else - [] + if shouldProvideCompetion then + let getAllSymbols (fileCheckResults: FSharpCheckFileResults) = + if settings.IntelliSense.IncludeSymbolsFromUnopenedNamespacesOrModules then + assemblyContentProvider.GetAllEntitiesInProjectAndReferencedAssemblies(fileCheckResults) + else + [] - let! results = FSharpCompletionProvider.ProvideCompletionsAsyncAux(context.Document, context.Position, getAllSymbols) - context.AddItems(results) - } - |> Async.Ignore - |> RoslynHelpers.StartAsyncUnitAsTask context.CancellationToken + let! results = FSharpCompletionProvider.ProvideCompletionsAsyncAux(context.Document, context.Position, getAllSymbols) + + context.AddItems results + + } |> CancellableTask.startAsTask context.CancellationToken override _.GetDescriptionAsync ( @@ -331,7 +336,7 @@ type internal FSharpCompletionProvider completionItem: Completion.CompletionItem, cancellationToken: CancellationToken ) : Task = - async { + cancellableTask { use _logBlock = Logger.LogBlockMessage document.Name LogEditorFunctionId.Completion_GetDescriptionAsync @@ -363,11 +368,10 @@ type internal FSharpCompletionProvider match completionItem.Properties.TryGetValue KeywordDescription with | true, keywordDescription -> return CompletionDescription.FromText(keywordDescription) | false, _ -> return CompletionDescription.Empty - } - |> RoslynHelpers.StartAsyncAsTask cancellationToken + } |> CancellableTask.start cancellationToken override _.GetChangeAsync(document, item, _, cancellationToken) : Task = - async { + cancellableTask { use _logBlock = Logger.LogBlockMessage document.Name LogEditorFunctionId.Completion_GetChangeAsync @@ -393,56 +397,48 @@ type internal FSharpCompletionProvider | true, x -> x | _ -> item.DisplayText - return! - asyncMaybe { - let! ns = - match item.Properties.TryGetValue NamespaceToOpenPropName with - | true, ns -> Some ns - | _ -> None - - let! sourceText = document.GetTextAsync(cancellationToken) - let textWithItemCommitted = - sourceText.WithChanges(TextChange(item.Span, nameInCode)) + match item.Properties.TryGetValue NamespaceToOpenPropName with + | false, _ -> + return CompletionChange.Create(TextChange(item.Span, nameInCode)) + | true, ns -> + let! sourceText = document.GetTextAsync(cancellationToken) - let line = sourceText.Lines.GetLineFromPosition(item.Span.Start) + let textWithItemCommitted = + sourceText.WithChanges(TextChange(item.Span, nameInCode)) - let! parseResults = - document.GetFSharpParseResultsAsync(nameof (FSharpCompletionProvider)) - |> liftAsync + let line = sourceText.Lines.GetLineFromPosition(item.Span.Start) - let fullNameIdents = - fullName |> Option.map (fun x -> x.Split '.') |> Option.defaultValue [||] + let! parseResults = document.GetFSharpParseResultsAsync(nameof (FSharpCompletionProvider)) - let insertionPoint = - if settings.CodeFixes.AlwaysPlaceOpensAtTopLevel then - OpenStatementInsertionPoint.TopLevel - else - OpenStatementInsertionPoint.Nearest + let fullNameIdents = + fullName |> Option.map (fun x -> x.Split '.') |> Option.defaultValue [||] - let ctx = - ParsedInput.FindNearestPointToInsertOpenDeclaration - line.LineNumber - parseResults.ParseTree - fullNameIdents - insertionPoint + let insertionPoint = + if settings.CodeFixes.AlwaysPlaceOpensAtTopLevel then + OpenStatementInsertionPoint.TopLevel + else + OpenStatementInsertionPoint.Nearest - let finalSourceText, changedSpanStartPos = - OpenDeclarationHelper.insertOpenDeclaration textWithItemCommitted ctx ns + let ctx = + ParsedInput.FindNearestPointToInsertOpenDeclaration + line.LineNumber + parseResults.ParseTree + fullNameIdents + insertionPoint - let fullChangingSpan = TextSpan.FromBounds(changedSpanStartPos, item.Span.End) + let finalSourceText, changedSpanStartPos = + OpenDeclarationHelper.insertOpenDeclaration textWithItemCommitted ctx ns - let changedSpan = - TextSpan.FromBounds(changedSpanStartPos, item.Span.End + (finalSourceText.Length - sourceText.Length)) + let fullChangingSpan = TextSpan.FromBounds(changedSpanStartPos, item.Span.End) - let changedText = finalSourceText.ToString(changedSpan) + let changedSpan = + TextSpan.FromBounds(changedSpanStartPos, item.Span.End + (finalSourceText.Length - sourceText.Length)) - return - CompletionChange - .Create(TextChange(fullChangingSpan, changedText)) - .WithNewPosition(Nullable(changedSpan.End)) - } - |> Async.map (Option.defaultValue (CompletionChange.Create(TextChange(item.Span, nameInCode)))) + let changedText = finalSourceText.ToString(changedSpan) - } - |> RoslynHelpers.StartAsyncAsTask cancellationToken + return + CompletionChange + .Create(TextChange(fullChangingSpan, changedText)) + .WithNewPosition(Nullable(changedSpan.End)) + } |> CancellableTask.start cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs index 4311a1e552d..b0383be4298 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs @@ -4,7 +4,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition open System.Collections.Immutable - +open System.Threading open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Completion open Microsoft.CodeAnalysis.Host @@ -58,7 +58,7 @@ type internal FSharpCompletionService let defines, langVersion = projectInfoManager.GetCompilationDefinesAndLangVersionForEditingDocument(document) - CompletionUtils.getDefaultCompletionListSpan (sourceText, caretIndex, documentId, document.FilePath, defines, Some langVersion) + CompletionUtils.getDefaultCompletionListSpan (sourceText, caretIndex, documentId, document.FilePath, defines, Some langVersion, CancellationToken.None) [] [, FSharpConstants.FSharpLanguageName)>] @@ -71,7 +71,7 @@ type internal FSharpCompletionServiceFactory [] interface ILanguageServiceFactory with member _.CreateLanguageService(hostLanguageServices: HostLanguageServices) : ILanguageService = upcast - new FSharpCompletionService( + FSharpCompletionService( hostLanguageServices.WorkspaceServices.Workspace, serviceProvider, assemblyContentProvider, diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs index c261cea6cc5..5d116390bc2 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs @@ -97,7 +97,8 @@ module internal CompletionUtils = defines: string list, langVersion: string option, sourceText: SourceText, - triggerPosition: int + triggerPosition: int, + ct: CancellationToken ) : bool = let textLines = sourceText.Lines let triggerLine = textLines.GetLineFromPosition triggerPosition @@ -110,7 +111,7 @@ module internal CompletionUtils = Some filePath, defines, langVersion, - CancellationToken.None + ct ) classifiedSpans.Count = 0 @@ -140,7 +141,7 @@ module internal CompletionUtils = | CompletionItemKind.Method(isExtension = true) -> 7 /// Indicates the text span to be replaced by a committed completion list item. - let getDefaultCompletionListSpan (sourceText: SourceText, caretIndex, documentId, filePath, defines, langVersion) = + let getDefaultCompletionListSpan (sourceText: SourceText, caretIndex, documentId, filePath, defines, langVersion, ct: CancellationToken) = // Gets connected identifier-part characters backward and forward from caret. let getIdentifierChars () = @@ -183,7 +184,7 @@ module internal CompletionUtils = Some filePath, defines, langVersion, - CancellationToken.None + ct ) let isBacktickIdentifier (classifiedSpan: ClassifiedSpan) = diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs index cc6b9ffa0bf..0f1eadbb592 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs @@ -10,6 +10,7 @@ open System.Diagnostics open Microsoft.CodeAnalysis open FSharp.Compiler.EditorServices open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics +open Internal.Utilities.CancellableTasks [)>] type internal UnusedDeclarationsAnalyzer [] () = @@ -17,22 +18,20 @@ type internal UnusedDeclarationsAnalyzer [] () = interface IFSharpUnusedDeclarationsDiagnosticAnalyzer with member _.AnalyzeSemanticsAsync(descriptor, document, cancellationToken) = - if document.Project.IsFSharpMiscellaneousOrMetadata && not document.IsFSharpScript then + if (document.Project.IsFSharpMiscellaneousOrMetadata && not document.IsFSharpScript) + || not document.Project.IsFSharpCodeFixesUnusedDeclarationsEnabled then Threading.Tasks.Task.FromResult(ImmutableArray.Empty) else - asyncMaybe { - do! Option.guard document.Project.IsFSharpCodeFixesUnusedDeclarationsEnabled + cancellableTask { do Trace.TraceInformation("{0:n3} (start) UnusedDeclarationsAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof (UnusedDeclarationsAnalyzer)) - |> liftAsync let! unusedRanges = UnusedDeclarations.getUnusedDeclarations (checkResults, (isScriptFile document.FilePath)) - |> liftAsync let! sourceText = document.GetTextAsync() @@ -40,6 +39,4 @@ type internal UnusedDeclarationsAnalyzer [] () = unusedRanges |> Seq.map (fun m -> Diagnostic.Create(descriptor, RoslynHelpers.RangeToLocation(m, sourceText, document.FilePath))) |> Seq.toImmutableArray - } - |> Async.map (Option.defaultValue ImmutableArray.Empty) - |> RoslynHelpers.StartAsyncAsTask cancellationToken + } |> CancellableTask.start cancellationToken diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index 6ba152f9233..8ebb090ab1c 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -142,7 +142,7 @@ - + diff --git a/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs b/vsintegration/src/FSharp.Editor/Hints/FSharpInlineHintsService.fs similarity index 79% rename from vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs rename to vsintegration/src/FSharp.Editor/Hints/FSharpInlineHintsService.fs index 0cc76527d1a..508d1152f47 100644 --- a/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs +++ b/vsintegration/src/FSharp.Editor/Hints/FSharpInlineHintsService.fs @@ -16,29 +16,29 @@ open System.Threading.Tasks // e.g. signature hints above the line, pipeline hints on the side and so on. [)>] -type internal RoslynAdapter [] (settings: EditorOptions) = +type internal FSharpInlineHintsService [] (settings: EditorOptions) = static let userOpName = "Hints" interface IFSharpInlineHintsService with member _.GetInlineHintsAsync(document, _, cancellationToken) = - cancellableTask { - let hintKinds = OptionParser.getHintKinds settings.Advanced + let hintKinds = OptionParser.getHintKinds settings.Advanced - if hintKinds.IsEmpty then - return ImmutableArray.Empty - else + if hintKinds.IsEmpty then + Task.FromResult ImmutableArray.Empty + else + cancellableTask { let! cancellationToken = CancellableTask.getCurrentCancellationToken () let hintKindsSerialized = hintKinds |> Set.map Hints.serialize |> String.concat "," TelemetryReporter.reportEvent "hints" [ ("hints.kinds", hintKindsSerialized) ] let! sourceText = document.GetTextAsync cancellationToken - let! nativeHints = HintService.getHintsForDocument sourceText document hintKinds userOpName cancellationToken + let! nativeHints = HintService.getHintsForDocument sourceText document hintKinds userOpName let tasks = nativeHints |> Seq.map (fun hint -> NativeToRoslynHintConverter.convert sourceText hint cancellationToken) let! roslynHints = Task.WhenAll(tasks) return roslynHints.ToImmutableArray() - } |> CancellableTask.start cancellationToken + } |> CancellableTask.start cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Hints/HintService.fs b/vsintegration/src/FSharp.Editor/Hints/HintService.fs index cfc2dfbf66c..1af7bc7b207 100644 --- a/vsintegration/src/FSharp.Editor/Hints/HintService.fs +++ b/vsintegration/src/FSharp.Editor/Hints/HintService.fs @@ -27,12 +27,12 @@ module HintService = |> Seq.collect (InlineParameterNameHints(parseResults).GetHintsForUnionCase symbol) | _ -> [] - let rec getHints hintKinds acc = + let rec loop hintKinds acc = match hintKinds with | [] -> acc - | hintKind :: hintKinds -> getHintsPerKind hintKind :: acc |> getHints hintKinds + | hintKind :: hintKinds -> (getHintsPerKind hintKind) :: (loop hintKinds acc) - getHints (hintKinds |> Set.toList) [] + loop (hintKinds |> Set.toList) [] let private getHintsForSymbol (sourceText: SourceText) parseResults hintKinds (symbol, symbolUses) = let hints = getHints sourceText parseResults hintKinds symbolUses symbol diff --git a/vsintegration/src/FSharp.Editor/Hints/Hints.fs b/vsintegration/src/FSharp.Editor/Hints/Hints.fs index 08792fb967d..6064884ecef 100644 --- a/vsintegration/src/FSharp.Editor/Hints/Hints.fs +++ b/vsintegration/src/FSharp.Editor/Hints/Hints.fs @@ -22,7 +22,7 @@ module Hints = GetTooltip: Document -> CancellableTask } - let serialize kind = + let inline serialize kind = match kind with | TypeHint -> "type" | ParameterNameHint -> "parameterName" diff --git a/vsintegration/src/FSharp.Editor/Hints/OptionParser.fs b/vsintegration/src/FSharp.Editor/Hints/OptionParser.fs index 4b318490111..437298b35db 100644 --- a/vsintegration/src/FSharp.Editor/Hints/OptionParser.fs +++ b/vsintegration/src/FSharp.Editor/Hints/OptionParser.fs @@ -7,7 +7,7 @@ open Hints module OptionParser = - let getHintKinds options = + let inline getHintKinds options = Set [ if options.IsInlineTypeHintsEnabled then diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs index b5417ecb3f5..5f94f9be02f 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs @@ -17,6 +17,7 @@ open Microsoft.VisualStudio.LanguageServices open Microsoft.VisualStudio.Text.PatternMatching open FSharp.Compiler.EditorServices +open Internal.Utilities.CancellableTasks [); Shared>] type internal FSharpNavigateToSearchService [] @@ -35,8 +36,9 @@ type internal FSharpNavigateToSearchService [] cache.Clear() let getNavigableItems (document: Document) = - async { - let! currentVersion = document.GetTextVersionAsync() |> Async.AwaitTask + cancellableTask { + let! ct = CancellableTask.getCurrentCancellationToken () + let! currentVersion = document.GetTextVersionAsync(ct) match cache.TryGetValue document.Id with | true, (version, items) when version = currentVersion -> return items @@ -143,12 +145,13 @@ type internal FSharpNavigateToSearchService [] patternMatcher.TryMatch $"{item.Container.FullName}.{name}" |> Option.ofNullable let processDocument (tryMatch: NavigableItem -> PatternMatch option) (kinds: IImmutableSet) (document: Document) = - async { - let! ct = Async.CancellationToken - let! sourceText = document.GetTextAsync ct |> Async.AwaitTask + cancellableTask { + let! ct = CancellableTask.getCurrentCancellationToken () + + let! sourceText = document.GetTextAsync ct let processItem (item: NavigableItem) = - asyncMaybe { + asyncMaybe { // TODO: make a flat cancellable task do! Option.guard (kinds.Contains(navigateToItemKindToRoslynKind item.Kind)) let! m = tryMatch item @@ -182,14 +185,18 @@ type internal FSharpNavigateToSearchService [] kinds, cancellationToken ) : Task> = - async { + cancellableTask { let tryMatch = createMatcherFor searchPattern - let! results = project.Documents |> Seq.map (processDocument tryMatch kinds) |> Async.Parallel + let! ct = CancellableTask.getCurrentCancellationToken () + + let tasks = Seq.map (fun doc -> processDocument tryMatch kinds doc ct) project.Documents + + let! results = Task.WhenAll(tasks) return results |> Array.concat |> Array.toImmutableArray } - |> RoslynHelpers.StartAsyncAsTask cancellationToken + |> CancellableTask.start cancellationToken member _.SearchDocumentAsync ( @@ -198,11 +205,11 @@ type internal FSharpNavigateToSearchService [] kinds, cancellationToken ) : Task> = - async { + cancellableTask { let! result = processDocument (createMatcherFor searchPattern) kinds document return result |> Array.toImmutableArray } - |> RoslynHelpers.StartAsyncAsTask cancellationToken + |> CancellableTask.start cancellationToken member _.KindsProvided = kindsProvided diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index c8e893070a5..865d6d219e8 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -18,6 +18,7 @@ open Microsoft.VisualStudio.FSharp.Editor open FSharp.Compiler.Text open Microsoft.IO open FSharp.Compiler.EditorServices +open Internal.Utilities.CancellableTasks type internal FSharpAsyncQuickInfoSource ( @@ -108,20 +109,27 @@ type internal FSharpAsyncQuickInfoSource override _.Dispose() = () // no cleanup necessary override _.GetQuickInfoItemAsync(session: IAsyncQuickInfoSession, cancellationToken: CancellationToken) : Task = - asyncMaybe { + cancellableTask { let document = textBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges() - let! triggerPoint = session.GetTriggerPoint(textBuffer.CurrentSnapshot) |> Option.ofNullable - let position = triggerPoint.Position + let triggerPoint = session.GetTriggerPoint(textBuffer.CurrentSnapshot) - let! tipdata = - FSharpAsyncQuickInfoSource.TryGetToolTip(document, position, ?width = editorOptions.QuickInfo.DescriptionWidth) + if not triggerPoint.HasValue then + return Unchecked.defaultof<_> + else + let position = triggerPoint.Value.Position - return! getQuickInfoItem tipdata - } - |> Async.map Option.toObj - |> RoslynHelpers.StartAsyncAsTask cancellationToken + let! tipdata = + FSharpAsyncQuickInfoSource.TryGetToolTip(document, position, ?width = editorOptions.QuickInfo.DescriptionWidth) + + match tipdata with + | Some tipdata -> + let! tipdata = getQuickInfoItem tipdata + return Option.toObj tipdata + | None -> return Unchecked.defaultof<_> + + } |> CancellableTask.start cancellationToken [)>] [] diff --git a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs index 8bcf3168379..77018bc0515 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs @@ -2,10 +2,13 @@ namespace FSharp.Editor.Tests +open Internal.Utilities.CancellableTasks + module CompletionProviderTests = open System open System.Linq + open System.Threading open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Completion open Microsoft.CodeAnalysis.Text @@ -30,10 +33,11 @@ module CompletionProviderTests = |> RoslynTestHelpers.GetSingleDocument let results = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> [])) - |> Async.RunSynchronously - |> Option.defaultValue (ResizeArray()) - |> Seq.map (fun result -> result.DisplayText) + let task = + FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> [])) + |> CancellableTask.start CancellationToken.None + + task.Result |> Seq.map (fun result -> result.DisplayText) let expectedFound = expected |> List.filter results.Contains @@ -77,9 +81,10 @@ module CompletionProviderTests = |> RoslynTestHelpers.GetSingleDocument let actual = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> [])) - |> Async.RunSynchronously - |> Option.defaultValue (ResizeArray()) + let task = + FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> [])) + |> CancellableTask.start CancellationToken.None + task.Result |> Seq.toList // sort items as Roslyn do - by `SortText` |> List.sortBy (fun x -> x.SortText) @@ -105,7 +110,7 @@ module CompletionProviderTests = let sourceText = SourceText.From(fileContents) let resultSpan = - CompletionUtils.getDefaultCompletionListSpan (sourceText, caretPosition, documentId, filePath, [], None) + CompletionUtils.getDefaultCompletionListSpan (sourceText, caretPosition, documentId, filePath, [], None, CancellationToken.None) Assert.Equal(expected, sourceText.ToString(resultSpan)) @@ -140,7 +145,8 @@ System.Console.WriteLine(x + y) caretPosition, CompletionTriggerKind.Insertion, mkGetInfo documentId, - IntelliSenseOptions.Default + IntelliSenseOptions.Default, + CancellationToken.None ) triggered @@ -161,7 +167,8 @@ System.Console.WriteLine(x + y) caretPosition, triggerKind, mkGetInfo documentId, - IntelliSenseOptions.Default + IntelliSenseOptions.Default, + CancellationToken.None ) Assert.False(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") @@ -178,7 +185,8 @@ System.Console.WriteLine(x + y) caretPosition, CompletionTriggerKind.Insertion, mkGetInfo documentId, - IntelliSenseOptions.Default + IntelliSenseOptions.Default, + CancellationToken.None ) Assert.False(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") @@ -202,7 +210,8 @@ System.Console.WriteLine() caretPosition, CompletionTriggerKind.Insertion, mkGetInfo documentId, - IntelliSenseOptions.Default + IntelliSenseOptions.Default, + CancellationToken.None ) Assert.False(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") @@ -239,7 +248,8 @@ let z = $"abc {System.Console.WriteLine(x + y)} def" caretPosition, CompletionTriggerKind.Insertion, mkGetInfo documentId, - IntelliSenseOptions.Default + IntelliSenseOptions.Default, + CancellationToken.None ) triggered @@ -265,7 +275,8 @@ System.Console.WriteLine() caretPosition, CompletionTriggerKind.Insertion, mkGetInfo documentId, - IntelliSenseOptions.Default + IntelliSenseOptions.Default, + CancellationToken.None ) Assert.False(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") @@ -288,7 +299,8 @@ let f() = caretPosition, CompletionTriggerKind.Insertion, mkGetInfo documentId, - IntelliSenseOptions.Default + IntelliSenseOptions.Default, + CancellationToken.None ) Assert.False(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger on operators") @@ -311,7 +323,8 @@ module Foo = module end caretPosition, CompletionTriggerKind.Insertion, mkGetInfo documentId, - IntelliSenseOptions.Default + IntelliSenseOptions.Default, + CancellationToken.None ) Assert.True(triggered, "Completion should trigger on Attributes.") @@ -334,7 +347,8 @@ printfn "%d" !f caretPosition, CompletionTriggerKind.Insertion, mkGetInfo documentId, - IntelliSenseOptions.Default + IntelliSenseOptions.Default, + CancellationToken.None ) Assert.True(triggered, "Completion should trigger after typing an identifier that follows a dereference operator (!).") @@ -358,7 +372,8 @@ use ptr = fixed &p caretPosition, CompletionTriggerKind.Insertion, mkGetInfo documentId, - IntelliSenseOptions.Default + IntelliSenseOptions.Default, + CancellationToken.None ) Assert.True(triggered, "Completion should trigger after typing an identifier that follows an addressOf operator (&).") @@ -391,7 +406,8 @@ xVal**y caretPosition, CompletionTriggerKind.Insertion, mkGetInfo documentId, - IntelliSenseOptions.Default + IntelliSenseOptions.Default, + CancellationToken.None ) Assert.True(triggered, "Completion should trigger after typing an identifier that follows a mathematical operation") @@ -412,7 +428,8 @@ l""" caretPosition, CompletionTriggerKind.Insertion, mkGetInfo documentId, - IntelliSenseOptions.Default + IntelliSenseOptions.Default, + CancellationToken.None ) Assert.True( diff --git a/vsintegration/tests/FSharp.Editor.Tests/FsxCompletionProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/FsxCompletionProviderTests.fs index 48276422bdd..4a949802ebf 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/FsxCompletionProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/FsxCompletionProviderTests.fs @@ -4,10 +4,12 @@ namespace FSharp.Editor.Tests open System open System.Collections.Generic +open System.Threading open Xunit open Microsoft.VisualStudio.FSharp.Editor open FSharp.Compiler.CodeAnalysis open FSharp.Editor.Tests.Helpers +open Internal.Utilities.CancellableTasks // AppDomain helper type Worker() = @@ -31,10 +33,9 @@ type Worker() = let actual = let x = FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> [])) - |> Async.RunSynchronously + |> CancellableTask.start CancellationToken.None - x - |> Option.defaultValue (ResizeArray()) + x.Result |> Seq.toList // sort items as Roslyn do - by `SortText` |> List.sortBy (fun x -> x.SortText) From 4710007e55b13778c86ff3a9558ced867b31c4f0 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Fri, 5 May 2023 16:07:21 +0200 Subject: [PATCH 03/20] iteration: quickinfo, help context --- .../Commands/HelpContextService.fs | 35 +++---- .../QuickInfo/QuickInfoProvider.fs | 92 ++++++++++--------- .../Structure/BlockStructureService.fs | 12 +-- .../HelpContextServiceTests.fs | 8 +- .../QuickInfoProviderTests.fs | 8 +- .../FSharp.Editor.Tests/QuickInfoTests.fs | 7 +- 6 files changed, 93 insertions(+), 69 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs index 88f5e5ae669..14939cb083e 100644 --- a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs @@ -14,18 +14,19 @@ open FSharp.Compiler.EditorServices open FSharp.Compiler.Syntax open FSharp.Compiler.Text open Microsoft.CodeAnalysis +open Internal.Utilities.CancellableTasks [] [, FSharpConstants.FSharpLanguageName)>] type internal FSharpHelpContextService [] () = - static member GetHelpTerm(document: Document, span: TextSpan, tokens: List) : Async = - asyncMaybe { + static member GetHelpTerm(document: Document, span: TextSpan, tokens: List) : CancellableTask = + cancellableTask { + let! cancellationToken = CancellableTask.getCurrentCancellationToken () let! _, check = document.GetFSharpParseAndCheckResultsAsync(nameof (FSharpHelpContextService)) - |> liftAsync - let! sourceText = document.GetTextAsync() |> liftTaskAsync + let! sourceText = document.GetTextAsync(cancellationToken) let textLines = sourceText.Lines let lineInfo = textLines.GetLineFromPosition(span.Start) let line = lineInfo.LineNumber @@ -76,7 +77,7 @@ type internal FSharpHelpContextService [] () = | otherwise -> otherwise, col match tokenInformation with - | None -> return! None + | None -> return "" | Some token -> match token.ClassificationType with | ClassificationTypeNames.Keyword @@ -85,17 +86,20 @@ type internal FSharpHelpContextService [] () = | ClassificationTypeNames.Comment -> return "comment_FS" | ClassificationTypeNames.Identifier -> try - let! (s, colAtEndOfNames, _) = QuickParse.GetCompleteIdentifierIsland false lineText col + let island = QuickParse.GetCompleteIdentifierIsland false lineText col - if check.HasFullTypeCheckInfo then + match island with + | Some (s, colAtEndOfNames, _) when check.HasFullTypeCheckInfo -> let qualId = PrettyNaming.GetLongNameFromString s - return! check.GetF1Keyword(Line.fromZ line, colAtEndOfNames, lineText, qualId) - else - return! None + let f1Keyword = check.GetF1Keyword(Line.fromZ line, colAtEndOfNames, lineText, qualId) + + return Option.defaultValue "" f1Keyword + + | _ -> return "" with e -> Assert.Exception e - return! None - | _ -> return! None + return "" + | _ -> return "" } interface IHelpContextService with @@ -103,7 +107,8 @@ type internal FSharpHelpContextService [] () = member this.Product = FSharpConstants.FSharpLanguageLongName member this.GetHelpTermAsync(document, textSpan, cancellationToken) = - asyncMaybe { + cancellableTask { + let! cancellationToken = CancellableTask.getCurrentCancellationToken () let! sourceText = document.GetTextAsync(cancellationToken) let defines, langVersion = document.GetFSharpQuickDefinesAndLangVersion() let textLine = sourceText.Lines.GetLineFromPosition(textSpan.Start) @@ -120,8 +125,6 @@ type internal FSharpHelpContextService [] () = ) return! FSharpHelpContextService.GetHelpTerm(document, textSpan, classifiedSpans) - } - |> Async.map (Option.defaultValue "") - |> RoslynHelpers.StartAsyncAsTask cancellationToken + } |> CancellableTask.start cancellationToken member this.FormatSymbol(_symbol) = Unchecked.defaultof<_> diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index 865d6d219e8..88fb7f63382 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -29,7 +29,7 @@ type internal FSharpAsyncQuickInfoSource ) = let getQuickInfoItem (sourceText, (document: Document), (lexerSymbol: LexerSymbol), (ToolTipText elements)) = - asyncMaybe { + cancellableTask { let documentationBuilder = XmlDocumentation.CreateDocumentationBuilder(xmlMemberIndexService) @@ -57,52 +57,60 @@ type internal FSharpAsyncQuickInfoSource ) let content = elements |> List.map getSingleContent - do! Option.guard (not content.IsEmpty) - - let! textSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, lexerSymbol.Range) - - let trackingSpan = - textBuffer.CurrentSnapshot.CreateTrackingSpan(textSpan.Start, textSpan.Length, SpanTrackingMode.EdgeInclusive) - - return QuickInfoItem(trackingSpan, QuickInfoViewProvider.stackWithSeparators content) + + if content.IsEmpty then + return None + else + let textSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, lexerSymbol.Range) + + match textSpan with + | None -> return None + | Some textSpan -> + let trackingSpan = + textBuffer.CurrentSnapshot.CreateTrackingSpan(textSpan.Start, textSpan.Length, SpanTrackingMode.EdgeInclusive) + + return Some(QuickInfoItem(trackingSpan, QuickInfoViewProvider.stackWithSeparators content)) } static member TryGetToolTip(document: Document, position, ?width) = - asyncMaybe { + cancellableTask { let userOpName = "getQuickInfo" - + let! cancellationToken = CancellableTask.getCurrentCancellationToken () let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, true, true, userOpName) - let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(userOpName) |> liftAsync - let! cancellationToken = Async.CancellationToken |> liftAsync - let! sourceText = document.GetTextAsync cancellationToken - let range = lexerSymbol.Range - let textLinePos = sourceText.Lines.GetLinePosition position - let fcsTextLineNumber = Line.fromZ textLinePos.Line - let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() - - let tooltip = - match lexerSymbol.Kind with - | LexerSymbolKind.Keyword -> checkFileResults.GetKeywordTooltip(lexerSymbol.FullIsland) - | LexerSymbolKind.String -> - checkFileResults.GetToolTip( - fcsTextLineNumber, - range.EndColumn, - lineText, - lexerSymbol.FullIsland, - FSharp.Compiler.Tokenization.FSharpTokenTag.String, - ?width = width - ) - | _ -> - checkFileResults.GetToolTip( - fcsTextLineNumber, - range.EndColumn, - lineText, - lexerSymbol.FullIsland, - FSharp.Compiler.Tokenization.FSharpTokenTag.IDENT, - ?width = width - ) - - return sourceText, document, lexerSymbol, tooltip + + match lexerSymbol with + | None -> return None + | Some lexerSymbol -> + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(userOpName) + let! sourceText = document.GetTextAsync cancellationToken + let range = lexerSymbol.Range + let textLinePos = sourceText.Lines.GetLinePosition position + let fcsTextLineNumber = Line.fromZ textLinePos.Line + let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() + + let tooltip = + match lexerSymbol.Kind with + | LexerSymbolKind.Keyword -> checkFileResults.GetKeywordTooltip(lexerSymbol.FullIsland) + | LexerSymbolKind.String -> + checkFileResults.GetToolTip( + fcsTextLineNumber, + range.EndColumn, + lineText, + lexerSymbol.FullIsland, + FSharp.Compiler.Tokenization.FSharpTokenTag.String, + ?width = width + ) + | _ -> + checkFileResults.GetToolTip( + fcsTextLineNumber, + range.EndColumn, + lineText, + lexerSymbol.FullIsland, + FSharp.Compiler.Tokenization.FSharpTokenTag.IDENT, + ?width = width + ) + + return Some(sourceText, document, lexerSymbol, tooltip) } interface IAsyncQuickInfoSource with diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index ae00c344192..7db105473e6 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -147,6 +147,7 @@ module internal BlockStructure = | _, _ -> None) open BlockStructure +open Internal.Utilities.CancellableTasks [)>] type internal FSharpBlockStructureService [] () = @@ -154,17 +155,16 @@ type internal FSharpBlockStructureService [] () = interface IFSharpBlockStructureService with member _.GetBlockStructureAsync(document, cancellationToken) : Task = - asyncMaybe { + cancellableTask { + let! cancellationToken = CancellableTask.getCurrentCancellationToken () + let! sourceText = document.GetTextAsync(cancellationToken) let! parseResults = document.GetFSharpParseResultsAsync(nameof (FSharpBlockStructureService)) - |> liftAsync return createBlockSpans document.Project.IsFSharpBlockStructureEnabled sourceText parseResults.ParseTree |> Seq.toImmutableArray - } - |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) - |> Async.map FSharpBlockStructure - |> RoslynHelpers.StartAsyncAsTask(cancellationToken) + |> FSharpBlockStructure + } |> CancellableTask.start cancellationToken diff --git a/vsintegration/tests/FSharp.Editor.Tests/HelpContextServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/HelpContextServiceTests.fs index 6088e79e9e6..ad5adc52120 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/HelpContextServiceTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/HelpContextServiceTests.fs @@ -10,6 +10,7 @@ open Microsoft.VisualStudio.FSharp.Editor open Microsoft.IO open FSharp.Editor.Tests.Helpers open Microsoft.CodeAnalysis.Text +open Internal.Utilities.CancellableTasks type HelpContextServiceTests() = let getMarkers (source: string) = @@ -52,14 +53,17 @@ type HelpContextServiceTests() = CancellationToken.None ) - FSharpHelpContextService.GetHelpTerm(document, span, classifiedSpans) - |> Async.RunSynchronously + let task = + FSharpHelpContextService.GetHelpTerm(document, span, classifiedSpans) + |> CancellableTask.start CancellationToken.None + task.Result ] let equalLength = (expectedKeywords.Length = res.Length) Assert.True(equalLength) for (exp, res) in List.zip expectedKeywords res do + let exp = Option.defaultValue "" exp Assert.Equal(exp, res) let TestF1Keywords (expectedKeywords, lines) = diff --git a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs index 91e504577f8..84ad48882c2 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs @@ -3,6 +3,7 @@ namespace FSharp.Editor.Tests open System +open System.Threading open Xunit open FSharp.Compiler.EditorServices open FSharp.Compiler.CodeAnalysis @@ -10,6 +11,7 @@ open Microsoft.VisualStudio.FSharp.Editor open Microsoft.VisualStudio.FSharp.Editor.QuickInfo open FSharp.Editor.Tests.Helpers open FSharp.Test +open Internal.Utilities.CancellableTasks type public AssemblyResolverTestFixture() = @@ -94,8 +96,10 @@ module QuickInfoProviderTests = let caretPosition = programText.IndexOf(symbol) + symbol.Length - 1 let quickInfo = - FSharpAsyncQuickInfoSource.TryGetToolTip(document, caretPosition) - |> Async.RunSynchronously + let task = + FSharpAsyncQuickInfoSource.TryGetToolTip(document, caretPosition) + |> CancellableTask.start CancellationToken.None + task.Result let actual = quickInfo diff --git a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs index 706d79c59bf..1487c2960a3 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs @@ -2,10 +2,12 @@ namespace FSharp.Editor.Tests +open System.Threading open Microsoft.VisualStudio.FSharp.Editor open Microsoft.VisualStudio.FSharp.Editor.QuickInfo open Xunit open FSharp.Editor.Tests.Helpers +open Internal.Utilities.CancellableTasks module QuickInfo = open FSharp.Compiler.EditorServices @@ -31,7 +33,10 @@ module QuickInfo = let document = RoslynTestHelpers.CreateSolution(code) |> RoslynTestHelpers.GetSingleDocument - let! _, _, _, tooltip = FSharpAsyncQuickInfoSource.TryGetToolTip(document, caretPosition) + let! _, _, _, tooltip = + FSharpAsyncQuickInfoSource.TryGetToolTip(document, caretPosition) + |> CancellableTask.start CancellationToken.None + |> Async.AwaitTask return tooltip } |> Async.RunSynchronously From 350669e29cd899f450ee6df5673df5823e87c282 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Fri, 5 May 2023 16:10:01 +0200 Subject: [PATCH 04/20] fantomas --- .fantomasignore | 4 +- .../Classification/ClassificationService.fs | 11 +-- .../Commands/HelpContextService.fs | 10 ++- .../Completion/CompletionProvider.fs | 45 +++++++----- .../Completion/CompletionService.fs | 10 ++- .../Completion/CompletionUtils.fs | 31 +++----- .../Debugging/LanguageDebugInfoService.fs | 3 +- .../Diagnostics/DocumentDiagnosticAnalyzer.fs | 6 +- .../Diagnostics/UnusedDeclarationsAnalyzer.fs | 15 ++-- .../Hints/FSharpInlineHintsService.fs | 9 ++- .../Hints/NativeToRoslynHintConverter.fs | 3 +- .../LanguageService/LanguageService.fs | 73 ++++++++++--------- .../LanguageService/Tokenizer.fs | 50 ++++++------- .../Navigation/NavigateToSearchService.fs | 3 +- .../QuickInfo/QuickInfoProvider.fs | 5 +- .../Structure/BlockStructureService.fs | 6 +- .../CompletionProviderTests.fs | 1 + .../DocumentDiagnosticAnalyzerTests.fs | 4 +- .../HelpContextServiceTests.fs | 1 + .../Hints/HintTestFramework.fs | 6 +- .../QuickInfoProviderTests.fs | 1 + .../FSharp.Editor.Tests/QuickInfoTests.fs | 1 + 22 files changed, 168 insertions(+), 130 deletions(-) diff --git a/.fantomasignore b/.fantomasignore index d2f57d7fc7f..342f81a3b2f 100644 --- a/.fantomasignore +++ b/.fantomasignore @@ -12,7 +12,7 @@ vsintegration/* !vsintegration/tests/FSharp.Editor.Tests artifacts/ -# Explicitly unformatted implementation +# Explicitly unformatted implementation src/Compiler/Checking/AccessibilityLogic.fs src/Compiler/Checking/AttributeChecking.fs src/Compiler/Checking/AugmentWithHashCompare.fs @@ -90,6 +90,7 @@ src/FSharp.Core/seqcore.fs src/Compiler/AbstractIL/ilwrite.fs src/Compiler/Utilities/lib.fs +src/Compiler/Utilities/CancellableTask.fs src/Compiler/Service/IncrementalBuild.fs src/Compiler/Service/ServiceAssemblyContent.fs src/Compiler/Service/ServiceDeclarationLists.fs @@ -113,4 +114,3 @@ src/FSharp.Core/list.fsi src/FSharp.Core/Query.fsi src/FSharp.Core/resumable.fsi src/FSharp.Core/async.fsi - diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index b3fa677fc1c..2197f9b21c7 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -145,8 +145,7 @@ type internal FSharpClassificationService [] () = | _ -> () static let toSemanticClassificationLookup (d: SemanticClassificationData) = - let lookup = - Dictionary>() + let lookup = Dictionary>() let f (dataItem: SemanticClassificationItem) = let items = @@ -181,7 +180,7 @@ type internal FSharpClassificationService [] () = let! cancellationToken = CancellableTask.getCurrentCancellationToken () - let defines, _= document.GetFSharpQuickDefinesAndLangVersion() + let defines, _ = document.GetFSharpQuickDefinesAndLangVersion() let! sourceText = document.GetTextAsync(cancellationToken) // For closed documents, only get classification for the text within the span. @@ -193,7 +192,8 @@ type internal FSharpClassificationService [] () = else let! classifiedSpans = getLexicalClassifications (document.FilePath, defines, sourceText, textSpan) result.AddRange(classifiedSpans) - } |> CancellableTask.startAsTask cancellationToken + } + |> CancellableTask.startAsTask cancellationToken member _.AddSemanticClassificationsAsync ( @@ -227,7 +227,8 @@ type internal FSharpClassificationService [] () = let classificationData = checkResults.GetSemanticClassification(Some targetRange) addSemanticClassification sourceText textSpan classificationData result - } |> CancellableTask.startAsTask cancellationToken + } + |> CancellableTask.startAsTask cancellationToken // Do not perform classification if we don't have project options (#defines matter) member _.AdjustStaleClassification(_: SourceText, classifiedSpan: ClassifiedSpan) : ClassifiedSpan = classifiedSpan diff --git a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs index 14939cb083e..0de5059ed24 100644 --- a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs @@ -23,8 +23,7 @@ type internal FSharpHelpContextService [] () = static member GetHelpTerm(document: Document, span: TextSpan, tokens: List) : CancellableTask = cancellableTask { let! cancellationToken = CancellableTask.getCurrentCancellationToken () - let! _, check = - document.GetFSharpParseAndCheckResultsAsync(nameof (FSharpHelpContextService)) + let! _, check = document.GetFSharpParseAndCheckResultsAsync(nameof (FSharpHelpContextService)) let! sourceText = document.GetTextAsync(cancellationToken) let textLines = sourceText.Lines @@ -91,7 +90,9 @@ type internal FSharpHelpContextService [] () = match island with | Some (s, colAtEndOfNames, _) when check.HasFullTypeCheckInfo -> let qualId = PrettyNaming.GetLongNameFromString s - let f1Keyword = check.GetF1Keyword(Line.fromZ line, colAtEndOfNames, lineText, qualId) + + let f1Keyword = + check.GetF1Keyword(Line.fromZ line, colAtEndOfNames, lineText, qualId) return Option.defaultValue "" f1Keyword @@ -125,6 +126,7 @@ type internal FSharpHelpContextService [] () = ) return! FSharpHelpContextService.GetHelpTerm(document, textSpan, classifiedSpans) - } |> CancellableTask.start cancellationToken + } + |> CancellableTask.start cancellationToken member this.FormatSymbol(_symbol) = Unchecked.defaultof<_> diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index f600c7f3d2b..7cbe58be088 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -131,7 +131,15 @@ type internal FSharpCompletionProvider else let documentId, filePath, defines, langVersion = getInfo () - CompletionUtils.shouldProvideCompletion (documentId, filePath, defines, langVersion, sourceText, triggerPosition, cancellationToken) + CompletionUtils.shouldProvideCompletion ( + documentId, + filePath, + defines, + langVersion, + sourceText, + triggerPosition, + cancellationToken + ) && (triggerChar = '.' || (intelliSenseOptions.ShowAfterCharIsTyped && CompletionUtils.isStartingNewWord (sourceText, triggerPosition))) @@ -144,8 +152,7 @@ type internal FSharpCompletionProvider ) = cancellableTask { - let! parseResults, checkFileResults = - document.GetFSharpParseAndCheckResultsAsync("ProvideCompletionsAsyncAux") + let! parseResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync("ProvideCompletionsAsyncAux") let! ct = CancellableTask.getCurrentCancellationToken () @@ -293,7 +300,14 @@ type internal FSharpCompletionProvider let defines, langVersion = document.GetFSharpQuickDefinesAndLangVersion() (documentId, document.FilePath, defines, Some langVersion) - FSharpCompletionProvider.ShouldTriggerCompletionAux(sourceText, caretPosition, trigger.Kind, getInfo, settings.IntelliSense, CancellationToken.None) + FSharpCompletionProvider.ShouldTriggerCompletionAux( + sourceText, + caretPosition, + trigger.Kind, + getInfo, + settings.IntelliSense, + CancellationToken.None + ) override _.ProvideCompletionsAsync(context: Completion.CompletionContext) = cancellableTask { @@ -325,10 +339,11 @@ type internal FSharpCompletionProvider [] let! results = FSharpCompletionProvider.ProvideCompletionsAsyncAux(context.Document, context.Position, getAllSymbols) - + context.AddItems results - - } |> CancellableTask.startAsTask context.CancellationToken + + } + |> CancellableTask.startAsTask context.CancellationToken override _.GetDescriptionAsync ( @@ -368,7 +383,8 @@ type internal FSharpCompletionProvider match completionItem.Properties.TryGetValue KeywordDescription with | true, keywordDescription -> return CompletionDescription.FromText(keywordDescription) | false, _ -> return CompletionDescription.Empty - } |> CancellableTask.start cancellationToken + } + |> CancellableTask.start cancellationToken override _.GetChangeAsync(document, item, _, cancellationToken) : Task = cancellableTask { @@ -397,10 +413,8 @@ type internal FSharpCompletionProvider | true, x -> x | _ -> item.DisplayText - match item.Properties.TryGetValue NamespaceToOpenPropName with - | false, _ -> - return CompletionChange.Create(TextChange(item.Span, nameInCode)) + | false, _ -> return CompletionChange.Create(TextChange(item.Span, nameInCode)) | true, ns -> let! sourceText = document.GetTextAsync(cancellationToken) @@ -421,11 +435,7 @@ type internal FSharpCompletionProvider OpenStatementInsertionPoint.Nearest let ctx = - ParsedInput.FindNearestPointToInsertOpenDeclaration - line.LineNumber - parseResults.ParseTree - fullNameIdents - insertionPoint + ParsedInput.FindNearestPointToInsertOpenDeclaration line.LineNumber parseResults.ParseTree fullNameIdents insertionPoint let finalSourceText, changedSpanStartPos = OpenDeclarationHelper.insertOpenDeclaration textWithItemCommitted ctx ns @@ -441,4 +451,5 @@ type internal FSharpCompletionProvider CompletionChange .Create(TextChange(fullChangingSpan, changedText)) .WithNewPosition(Nullable(changedSpan.End)) - } |> CancellableTask.start cancellationToken + } + |> CancellableTask.start cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs index b0383be4298..d879b718b98 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs @@ -58,7 +58,15 @@ type internal FSharpCompletionService let defines, langVersion = projectInfoManager.GetCompilationDefinesAndLangVersionForEditingDocument(document) - CompletionUtils.getDefaultCompletionListSpan (sourceText, caretIndex, documentId, document.FilePath, defines, Some langVersion, CancellationToken.None) + CompletionUtils.getDefaultCompletionListSpan ( + sourceText, + caretIndex, + documentId, + document.FilePath, + defines, + Some langVersion, + CancellationToken.None + ) [] [, FSharpConstants.FSharpLanguageName)>] diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs index 5d116390bc2..e2cf7b2e114 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs @@ -104,15 +104,7 @@ module internal CompletionUtils = let triggerLine = textLines.GetLineFromPosition triggerPosition let classifiedSpans = - Tokenizer.getClassifiedSpans ( - documentId, - sourceText, - triggerLine.Span, - Some filePath, - defines, - langVersion, - ct - ) + Tokenizer.getClassifiedSpans (documentId, sourceText, triggerLine.Span, Some filePath, defines, langVersion, ct) classifiedSpans.Count = 0 || // we should provide completion at the start of empty line, where there are no tokens at all @@ -141,7 +133,16 @@ module internal CompletionUtils = | CompletionItemKind.Method(isExtension = true) -> 7 /// Indicates the text span to be replaced by a committed completion list item. - let getDefaultCompletionListSpan (sourceText: SourceText, caretIndex, documentId, filePath, defines, langVersion, ct: CancellationToken) = + let getDefaultCompletionListSpan + ( + sourceText: SourceText, + caretIndex, + documentId, + filePath, + defines, + langVersion, + ct: CancellationToken + ) = // Gets connected identifier-part characters backward and forward from caret. let getIdentifierChars () = @@ -177,15 +178,7 @@ module internal CompletionUtils = // the majority of common cases. let classifiedSpans = - Tokenizer.getClassifiedSpans ( - documentId, - sourceText, - line.Span, - Some filePath, - defines, - langVersion, - ct - ) + Tokenizer.getClassifiedSpans (documentId, sourceText, line.Span, Some filePath, defines, langVersion, ct) let isBacktickIdentifier (classifiedSpan: ClassifiedSpan) = classifiedSpan.ClassificationType = ClassificationTypeNames.Identifier diff --git a/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs b/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs index 52d6284aabe..bb0f66a8f79 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs @@ -79,4 +79,5 @@ type internal FSharpLanguageDebugInfoService [] () = | Some textSpan -> FSharpDebugDataTipInfo(textSpan, sourceText.GetSubText(textSpan).ToString()) return result - } |> CancellableTask.start cancellationToken + } + |> CancellableTask.start cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs index a38f03b9d38..6ce87ccc099 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs @@ -112,7 +112,8 @@ type internal FSharpDocumentDiagnosticAnalyzer [] () = return ImmutableArray.Empty else return! FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Syntax) - } |> CancellableTask.start cancellationToken + } + |> CancellableTask.start cancellationToken member _.AnalyzeSemanticsAsync(document: Document, cancellationToken: CancellationToken) : Task> = cancellableTask { @@ -120,4 +121,5 @@ type internal FSharpDocumentDiagnosticAnalyzer [] () = return ImmutableArray.Empty else return! FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Semantic) - } |> CancellableTask.start cancellationToken \ No newline at end of file + } + |> CancellableTask.start cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs index 0f1eadbb592..2ecbe7ffd2d 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs @@ -18,8 +18,10 @@ type internal UnusedDeclarationsAnalyzer [] () = interface IFSharpUnusedDeclarationsDiagnosticAnalyzer with member _.AnalyzeSemanticsAsync(descriptor, document, cancellationToken) = - if (document.Project.IsFSharpMiscellaneousOrMetadata && not document.IsFSharpScript) - || not document.Project.IsFSharpCodeFixesUnusedDeclarationsEnabled then + if + (document.Project.IsFSharpMiscellaneousOrMetadata && not document.IsFSharpScript) + || not document.Project.IsFSharpCodeFixesUnusedDeclarationsEnabled + then Threading.Tasks.Task.FromResult(ImmutableArray.Empty) else @@ -27,11 +29,9 @@ type internal UnusedDeclarationsAnalyzer [] () = do Trace.TraceInformation("{0:n3} (start) UnusedDeclarationsAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) - let! _, checkResults = - document.GetFSharpParseAndCheckResultsAsync(nameof (UnusedDeclarationsAnalyzer)) + let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof (UnusedDeclarationsAnalyzer)) - let! unusedRanges = - UnusedDeclarations.getUnusedDeclarations (checkResults, (isScriptFile document.FilePath)) + let! unusedRanges = UnusedDeclarations.getUnusedDeclarations (checkResults, (isScriptFile document.FilePath)) let! sourceText = document.GetTextAsync() @@ -39,4 +39,5 @@ type internal UnusedDeclarationsAnalyzer [] () = unusedRanges |> Seq.map (fun m -> Diagnostic.Create(descriptor, RoslynHelpers.RangeToLocation(m, sourceText, document.FilePath))) |> Seq.toImmutableArray - } |> CancellableTask.start cancellationToken + } + |> CancellableTask.start cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Hints/FSharpInlineHintsService.fs b/vsintegration/src/FSharp.Editor/Hints/FSharpInlineHintsService.fs index 508d1152f47..a00623c0560 100644 --- a/vsintegration/src/FSharp.Editor/Hints/FSharpInlineHintsService.fs +++ b/vsintegration/src/FSharp.Editor/Hints/FSharpInlineHintsService.fs @@ -26,7 +26,7 @@ type internal FSharpInlineHintsService [] (settings: Edito if hintKinds.IsEmpty then Task.FromResult ImmutableArray.Empty - else + else cancellableTask { let! cancellationToken = CancellableTask.getCurrentCancellationToken () @@ -36,9 +36,12 @@ type internal FSharpInlineHintsService [] (settings: Edito let! sourceText = document.GetTextAsync cancellationToken let! nativeHints = HintService.getHintsForDocument sourceText document hintKinds userOpName - let tasks = nativeHints |> Seq.map (fun hint -> NativeToRoslynHintConverter.convert sourceText hint cancellationToken) + let tasks = + nativeHints + |> Seq.map (fun hint -> NativeToRoslynHintConverter.convert sourceText hint cancellationToken) let! roslynHints = Task.WhenAll(tasks) return roslynHints.ToImmutableArray() - } |> CancellableTask.start cancellationToken + } + |> CancellableTask.start cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs b/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs index 8fa842b49d7..2fab2e28ef4 100644 --- a/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs +++ b/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs @@ -30,7 +30,8 @@ module NativeToRoslynHintConverter = cancellableTask { let! taggedText = hint.GetTooltip doc return taggedText |> List.map nativeToRoslynText |> ImmutableArray.CreateRange - } |> CancellableTask.start ct + } + |> CancellableTask.start ct cancellableTask { let span = rangeToSpan hint.Range sourceText diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 57cbd86265a..02e6ac7a51b 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -49,10 +49,7 @@ type internal RoamingProfileStorageLocation(keyName: string) = [] [, ServiceLayer.Default)>] -type internal FSharpWorkspaceServiceFactory [] - ( - metadataAsSourceService: FSharpMetadataAsSourceService - ) = +type internal FSharpWorkspaceServiceFactory [] (metadataAsSourceService: FSharpMetadataAsSourceService) = // We have a lock just in case if multi-threads try to create a new IFSharpWorkspaceService - // but we only want to have a single instance of the FSharpChecker regardless if there are multiple instances of IFSharpWorkspaceService. @@ -73,11 +70,7 @@ type internal FSharpWorkspaceServiceFactory [] | :? VisualStudioWorkspace as workspace -> try let md = - LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetMetadata( - workspace, - path, - timeStamp - ) + LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetMetadata(workspace, path, timeStamp) let amd = (md :?> AssemblyMetadata) let mmd = amd.GetModules().[0] @@ -304,14 +297,11 @@ type internal FSharpSettingsFactory [] (settin type internal FSharpPackage() as this = inherit AbstractPackage() - let mutable vfsiToolWindow = - Unchecked.defaultof + let mutable vfsiToolWindow = Unchecked.defaultof let GetToolWindowAsITestVFSI () = if vfsiToolWindow = Unchecked.defaultof<_> then - vfsiToolWindow <- - this.FindToolWindow(typeof, 0, true) - :?> FSharp.Interactive.FsiToolWindow + vfsiToolWindow <- this.FindToolWindow(typeof, 0, true) :?> FSharp.Interactive.FsiToolWindow vfsiToolWindow :> FSharp.Interactive.ITestVFSI @@ -342,12 +332,26 @@ type internal FSharpPackage() as this = FSharp.Interactive.Hooks.fsiConsoleWindowPackageInitalizeSited (this :> Package) commandService // FSI-LINKAGE-POINT: private method GetDialogPage forces fsi options to be loaded - let _fsiPropertyPage = this.GetDialogPage(typeof) + let _fsiPropertyPage = + this.GetDialogPage(typeof) let workspace = this.ComponentModel.GetService() - let _ = this.ComponentModel.DefaultExportProvider.GetExport() - let optionsManager = workspace.Services.GetService().FSharpProjectOptionsManager - let metadataAsSource = this.ComponentModel.DefaultExportProvider.GetExport().Value + + let _ = + this.ComponentModel.DefaultExportProvider.GetExport() + + let optionsManager = + workspace + .Services + .GetService() + .FSharpProjectOptionsManager + + let metadataAsSource = + this + .ComponentModel + .DefaultExportProvider + .GetExport() + .Value let! solution = this.GetServiceAsync(typeof) let solution = solution :?> IVsSolution @@ -360,18 +364,18 @@ type internal FSharpPackage() as this = solutionEventsOpt <- Some(solutionEvents) solution.AdviseSolutionEvents(solutionEvents) |> ignore - let projectContextFactory = this.ComponentModel.GetService() - let miscFilesWorkspace = this.ComponentModel.GetService() - do SingleFileWorkspaceMap( - FSharpMiscellaneousFileService( - workspace, - miscFilesWorkspace, - projectContextFactory - ), - rdt) |> ignore + let projectContextFactory = + this.ComponentModel.GetService() + + let miscFilesWorkspace = + this.ComponentModel.GetService() + + do + SingleFileWorkspaceMap(FSharpMiscellaneousFileService(workspace, miscFilesWorkspace, projectContextFactory), rdt) + |> ignore - } |> CancellableTask.startAsTask cancellationToken - + } + |> CancellableTask.startAsTask cancellationToken override _.RoslynLanguageName = FSharpConstants.FSharpLanguageName (*override this.CreateWorkspace() = this.ComponentModel.GetService() *) @@ -428,8 +432,7 @@ type internal FSharpLanguageService(package: FSharpPackage) = override _.LanguageServiceId = new Guid(FSharpConstants.languageServiceGuidString) override _.DebuggerLanguageId = CompilerEnvironment.GetDebuggerLanguageID() - override _.CreateContext(_, _, _, _, _) = - raise (NotImplementedException()) + override _.CreateContext(_, _, _, _, _) = raise (NotImplementedException()) override this.SetupNewTextView(textView) = base.SetupNewTextView(textView) @@ -481,9 +484,11 @@ type internal HackCpsCommandLineChanges [ projectId | false, _ -> - LanguageServices - .FSharpVisualStudioWorkspaceExtensions - .GetOrCreateProjectIdForPath(workspace, path, projectDisplayNameOf path) + LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetOrCreateProjectIdForPath( + workspace, + path, + projectDisplayNameOf path + ) let path = Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetProjectFilePath( diff --git a/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs b/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs index c75e845ba7e..46607dc7607 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs @@ -700,33 +700,33 @@ module internal Tokenizer = langVersion, cancellationToken: CancellationToken ) : ResizeArray = - let result = new ResizeArray() - - try - let sourceTokenizer = FSharpSourceTokenizer(defines, fileName, langVersion) - let lines = sourceText.Lines - let sourceTextData = getSourceTextData (documentKey, defines, lines.Count) - - let startLine = lines.GetLineFromPosition(textSpan.Start).LineNumber - let endLine = lines.GetLineFromPosition(textSpan.End).LineNumber - - let lineDataResults = - getFromRefreshedTokenCache (lines, startLine, endLine, sourceTokenizer, sourceTextData, cancellationToken) - - for lineData, _ in lineDataResults do - result.AddRange( - lineData.ClassifiedSpans - |> Array.filter (fun token -> - textSpan.Contains(token.TextSpan.Start) - || textSpan.Contains(token.TextSpan.End - 1) - || (token.TextSpan.Start <= textSpan.Start && textSpan.End <= token.TextSpan.End)) - ) + let result = new ResizeArray() + + try + let sourceTokenizer = FSharpSourceTokenizer(defines, fileName, langVersion) + let lines = sourceText.Lines + let sourceTextData = getSourceTextData (documentKey, defines, lines.Count) + + let startLine = lines.GetLineFromPosition(textSpan.Start).LineNumber + let endLine = lines.GetLineFromPosition(textSpan.End).LineNumber - with - | :? System.OperationCanceledException -> reraise () - | ex -> Assert.Exception(ex) + let lineDataResults = + getFromRefreshedTokenCache (lines, startLine, endLine, sourceTokenizer, sourceTextData, cancellationToken) + + for lineData, _ in lineDataResults do + result.AddRange( + lineData.ClassifiedSpans + |> Array.filter (fun token -> + textSpan.Contains(token.TextSpan.Start) + || textSpan.Contains(token.TextSpan.End - 1) + || (token.TextSpan.Start <= textSpan.Start && textSpan.End <= token.TextSpan.End)) + ) + + with + | :? System.OperationCanceledException -> reraise () + | ex -> Assert.Exception(ex) - result + result /// Returns symbol at a given position. let private getSymbolFromSavedTokens diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs index 5f94f9be02f..1d57296f644 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs @@ -190,7 +190,8 @@ type internal FSharpNavigateToSearchService [] let! ct = CancellableTask.getCurrentCancellationToken () - let tasks = Seq.map (fun doc -> processDocument tryMatch kinds doc ct) project.Documents + let tasks = + Seq.map (fun doc -> processDocument tryMatch kinds doc ct) project.Documents let! results = Task.WhenAll(tasks) diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index 88fb7f63382..d89fee1dc97 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -57,7 +57,7 @@ type internal FSharpAsyncQuickInfoSource ) let content = elements |> List.map getSingleContent - + if content.IsEmpty then return None else @@ -137,7 +137,8 @@ type internal FSharpAsyncQuickInfoSource return Option.toObj tipdata | None -> return Unchecked.defaultof<_> - } |> CancellableTask.start cancellationToken + } + |> CancellableTask.start cancellationToken [)>] [] diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index 7db105473e6..5c5688b8a95 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -160,11 +160,11 @@ type internal FSharpBlockStructureService [] () = let! sourceText = document.GetTextAsync(cancellationToken) - let! parseResults = - document.GetFSharpParseResultsAsync(nameof (FSharpBlockStructureService)) + let! parseResults = document.GetFSharpParseResultsAsync(nameof (FSharpBlockStructureService)) return createBlockSpans document.Project.IsFSharpBlockStructureEnabled sourceText parseResults.ParseTree |> Seq.toImmutableArray |> FSharpBlockStructure - } |> CancellableTask.start cancellationToken + } + |> CancellableTask.start cancellationToken diff --git a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs index 77018bc0515..784fee00fe7 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs @@ -84,6 +84,7 @@ module CompletionProviderTests = let task = FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> [])) |> CancellableTask.start CancellationToken.None + task.Result |> Seq.toList // sort items as Roslyn do - by `SortText` diff --git a/vsintegration/tests/FSharp.Editor.Tests/DocumentDiagnosticAnalyzerTests.fs b/vsintegration/tests/FSharp.Editor.Tests/DocumentDiagnosticAnalyzerTests.fs index 4d4929a81b3..aab7903b6c9 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/DocumentDiagnosticAnalyzerTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/DocumentDiagnosticAnalyzerTests.fs @@ -24,7 +24,9 @@ type DocumentDiagnosticAnalyzerTests() = let! syntacticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Syntax) let! semanticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Semantic) return syntacticDiagnostics.AddRange(semanticDiagnostics) - } |> CancellableTask.start CancellationToken.None + } + |> CancellableTask.start CancellationToken.None + task.Result member private this.VerifyNoErrors(fileContents: string, ?additionalFlags: string[]) = diff --git a/vsintegration/tests/FSharp.Editor.Tests/HelpContextServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/HelpContextServiceTests.fs index ad5adc52120..1c73d904b32 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/HelpContextServiceTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/HelpContextServiceTests.fs @@ -56,6 +56,7 @@ type HelpContextServiceTests() = let task = FSharpHelpContextService.GetHelpTerm(document, span, classifiedSpans) |> CancellableTask.start CancellationToken.None + task.Result ] diff --git a/vsintegration/tests/FSharp.Editor.Tests/Hints/HintTestFramework.fs b/vsintegration/tests/FSharp.Editor.Tests/Hints/HintTestFramework.fs index f3a28d0641a..3eb54c7ecc5 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/Hints/HintTestFramework.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/Hints/HintTestFramework.fs @@ -62,7 +62,7 @@ module HintTestFramework = project.Documents let getHints (document: Document) hintKinds = - let task = + let task = cancellableTask { let! ct = CancellableTask.getCurrentCancellationToken () @@ -76,7 +76,9 @@ module HintTestFramework = let! hints = HintService.getHintsForDocument sourceText document hintKinds "test" ct let! tooltips = hints |> Seq.map getTooltip |> Async.Parallel return tooltips |> Seq.zip hints |> Seq.map convert - } |> CancellableTask.start CancellationToken.None + } + |> CancellableTask.start CancellationToken.None + task.Result let getTypeHints document = diff --git a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs index 84ad48882c2..35ee681f5f5 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs @@ -99,6 +99,7 @@ module QuickInfoProviderTests = let task = FSharpAsyncQuickInfoSource.TryGetToolTip(document, caretPosition) |> CancellableTask.start CancellationToken.None + task.Result let actual = diff --git a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs index 1487c2960a3..eab21e15abb 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs @@ -37,6 +37,7 @@ module QuickInfo = FSharpAsyncQuickInfoSource.TryGetToolTip(document, caretPosition) |> CancellableTask.start CancellationToken.None |> Async.AwaitTask + return tooltip } |> Async.RunSynchronously From 0641626f4df2ef2d3a142c9049b93166c98e8f37 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Fri, 5 May 2023 17:14:38 +0200 Subject: [PATCH 05/20] todo --- .../src/FSharp.Editor/Navigation/FindDefinitionService.fs | 2 +- .../src/FSharp.Editor/Navigation/NavigateToSearchService.fs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindDefinitionService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindDefinitionService.fs index 884ea193da4..1864e99c508 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindDefinitionService.fs @@ -20,5 +20,5 @@ type internal FSharpFindDefinitionService [] (metadataAsSo member _.FindDefinitionsAsync(document: Document, position: int, cancellationToken: CancellationToken) = let navigation = FSharpNavigation(metadataAsSource, document, rangeStartup) - let definitions = navigation.FindDefinitions(position, cancellationToken) + let definitions = navigation.FindDefinitions(position, cancellationToken) // TODO: probably will need to be async all the way down ImmutableArray.CreateRange(definitions) |> Task.FromResult diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs index 1d57296f644..d5ae1236b65 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs @@ -152,6 +152,7 @@ type internal FSharpNavigateToSearchService [] let processItem (item: NavigableItem) = asyncMaybe { // TODO: make a flat cancellable task + do! Option.guard (kinds.Contains(navigateToItemKindToRoslynKind item.Kind)) let! m = tryMatch item From 4440d4ffd1f117a24adc6133d3c8e383942450e4 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Thu, 11 May 2023 16:14:00 +0200 Subject: [PATCH 06/20] moved tasks to editor project, fixed comment colouring bug --- src/Compiler/FSharp.Compiler.Service.fsproj | 2 - .../Classification/ClassificationService.fs | 100 ++++++++++-------- .../Commands/HelpContextService.fs | 2 +- .../FSharp.Editor/Common/CancellableTasks.fs | 4 +- .../Completion/CompletionProvider.fs | 2 +- .../Debugging/LanguageDebugInfoService.fs | 3 +- .../Diagnostics/DocumentDiagnosticAnalyzer.fs | 3 +- .../Diagnostics/UnusedDeclarationsAnalyzer.fs | 2 +- .../src/FSharp.Editor/FSharp.Editor.fsproj | 1 + .../Hints/FSharpInlineHintsService.fs | 2 +- .../src/FSharp.Editor/Hints/HintService.fs | 2 +- .../src/FSharp.Editor/Hints/Hints.fs | 2 +- .../Hints/InlineParameterNameHints.fs | 2 +- .../Hints/InlineReturnTypeHints.fs | 2 +- .../FSharp.Editor/Hints/InlineTypeHints.fs | 2 +- .../Hints/NativeToRoslynHintConverter.fs | 2 +- .../LanguageService/LanguageService.fs | 3 +- .../LanguageService/Tokenizer.fs | 3 +- .../Navigation/NavigateToSearchService.fs | 2 +- .../QuickInfo/QuickInfoProvider.fs | 2 +- .../Structure/BlockStructureService.fs | 2 +- .../CompletionProviderTests.fs | 2 +- .../DocumentDiagnosticAnalyzerTests.fs | 2 +- .../FsxCompletionProviderTests.fs | 2 +- .../HelpContextServiceTests.fs | 2 +- .../Hints/HintTestFramework.fs | 2 +- .../QuickInfoProviderTests.fs | 2 +- .../FSharp.Editor.Tests/QuickInfoTests.fs | 2 +- 28 files changed, 80 insertions(+), 79 deletions(-) rename src/Compiler/Utilities/CancellableTask.fs => vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs (99%) diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 31b2529ce92..d95ad840eff 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -76,7 +76,6 @@ - @@ -92,7 +91,6 @@ - diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index 2197f9b21c7..15dba38506f 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -16,8 +16,7 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Classification open FSharp.Compiler.EditorServices open FSharp.Compiler.Tokenization -open Internal.Utilities.CancellableTasks.CancellableTaskBuilder -open Internal.Utilities.CancellableTasks +open CancellableTasks // IEditorClassificationService is marked as Obsolete, but is still supported. The replacement (IClassificationService) // is internal to Microsoft.CodeAnalysis.Workspaces which we don't have internals visible to. Rather than add yet another @@ -71,47 +70,45 @@ type DocumentCache<'Value when 'Value: not struct>() = [)>] type internal FSharpClassificationService [] () = - static let getLexicalClassifications (filePath: string, defines, text: SourceText, textSpan: TextSpan) = - cancellableTask { - let! ct = CancellableTask.getCurrentCancellationToken () - - let text = text.GetSubText(textSpan) - let result = ImmutableArray.CreateBuilder() - - let tokenCallback = - fun (tok: FSharpToken) -> - let spanKind = - if tok.IsKeyword then - ClassificationTypeNames.Keyword - elif tok.IsNumericLiteral then - ClassificationTypeNames.NumericLiteral - elif tok.IsCommentTrivia then - ClassificationTypeNames.Comment - elif tok.IsStringLiteral then - ClassificationTypeNames.StringLiteral - else - ClassificationTypeNames.Text - - match RoslynHelpers.TryFSharpRangeToTextSpan(text, tok.Range) with - | Some span -> result.Add(ClassifiedSpan(TextSpan(textSpan.Start + span.Start, span.Length), spanKind)) - | _ -> () - - let flags = - FSharpLexerFlags.Default - &&& ~~~FSharpLexerFlags.Compiling - &&& ~~~FSharpLexerFlags.UseLexFilter - - FSharpLexer.Tokenize( - text.ToFSharpSourceText(), - tokenCallback, - filePath = filePath, - conditionalDefines = defines, - flags = flags, - ct = ct - ) - - return result.ToImmutable() - } + static let getLexicalClassifications (filePath: string, defines, text: SourceText, textSpan: TextSpan, ct: CancellationToken) = + + let text = text.GetSubText(textSpan) + let result = ImmutableArray.CreateBuilder() + + let tokenCallback = + fun (tok: FSharpToken) -> + let spanKind = + if tok.IsKeyword then + ClassificationTypeNames.Keyword + elif tok.IsNumericLiteral then + ClassificationTypeNames.NumericLiteral + elif tok.IsCommentTrivia then + ClassificationTypeNames.Comment + elif tok.IsStringLiteral then + ClassificationTypeNames.StringLiteral + else + ClassificationTypeNames.Text + + match RoslynHelpers.TryFSharpRangeToTextSpan(text, tok.Range) with + | Some span -> result.Add(ClassifiedSpan(TextSpan(textSpan.Start + span.Start, span.Length), spanKind)) + | _ -> () + + let flags = + FSharpLexerFlags.Default + &&& ~~~FSharpLexerFlags.Compiling + &&& ~~~FSharpLexerFlags.UseLexFilter + + FSharpLexer.Tokenize( + text.ToFSharpSourceText(), + tokenCallback, + filePath = filePath, + conditionalDefines = defines, + flags = flags, + ct = ct + ) + + result.ToImmutable() + static let addSemanticClassification sourceText @@ -180,18 +177,27 @@ type internal FSharpClassificationService [] () = let! cancellationToken = CancellableTask.getCurrentCancellationToken () - let defines, _ = document.GetFSharpQuickDefinesAndLangVersion() + let defines, langVersion = document.GetFSharpQuickDefinesAndLangVersion() let! sourceText = document.GetTextAsync(cancellationToken) // For closed documents, only get classification for the text within the span. // This may be inaccurate for multi-line tokens such as string literals, but this is ok for now // as it's better than having to tokenize a big part of a file which in return will allocate a lot and hurt find all references performance. if not (document.Project.Solution.Workspace.IsDocumentOpen document.Id) then - let! classifiedSpans = getLexicalClassifications (document.FilePath, defines, sourceText, textSpan) + let classifiedSpans = getLexicalClassifications (document.FilePath, defines, sourceText, textSpan, cancellationToken) result.AddRange(classifiedSpans) else - let! classifiedSpans = getLexicalClassifications (document.FilePath, defines, sourceText, textSpan) - result.AddRange(classifiedSpans) + result.AddRange( + Tokenizer.getClassifiedSpans ( + document.Id, + sourceText, + textSpan, + Some(document.FilePath), + defines, + Some langVersion, + cancellationToken + ) + ) } |> CancellableTask.startAsTask cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs index 0de5059ed24..bba2e4a4cd6 100644 --- a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs @@ -14,7 +14,7 @@ open FSharp.Compiler.EditorServices open FSharp.Compiler.Syntax open FSharp.Compiler.Text open Microsoft.CodeAnalysis -open Internal.Utilities.CancellableTasks +open CancellableTasks [] [, FSharpConstants.FSharpLanguageName)>] diff --git a/src/Compiler/Utilities/CancellableTask.fs b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs similarity index 99% rename from src/Compiler/Utilities/CancellableTask.fs rename to vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs index 33e18d35651..0e052daa959 100644 --- a/src/Compiler/Utilities/CancellableTask.fs +++ b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs @@ -1,6 +1,6 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace Internal.Utilities +namespace Microsoft.VisualStudio.FSharp.Editor // Don't warn about the resumable code invocation #nowarn "3513" diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index 7cbe58be088..e9dd49a1c05 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -20,7 +20,7 @@ open FSharp.Compiler.EditorServices open FSharp.Compiler.Syntax open FSharp.Compiler.Text open FSharp.Compiler.Tokenization -open Internal.Utilities.CancellableTasks +open CancellableTasks module Logger = Microsoft.VisualStudio.FSharp.Editor.Logger diff --git a/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs b/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs index bb0f66a8f79..ce952c78ade 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs @@ -13,8 +13,7 @@ open Microsoft.CodeAnalysis.Classification open FSharp.Compiler.EditorServices open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor.Implementation.Debugging -open Internal.Utilities.CancellableTasks.CancellableTaskBuilder -open Internal.Utilities.CancellableTasks +open CancellableTasks [)>] type internal FSharpLanguageDebugInfoService [] () = diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs index 6ce87ccc099..904f7e49713 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs @@ -13,8 +13,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics open FSharp.Compiler.Diagnostics -open Internal.Utilities.CancellableTasks.CancellableTaskBuilder -open Internal.Utilities.CancellableTasks +open CancellableTasks [] type internal DiagnosticsType = diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs index 2ecbe7ffd2d..f8de2140da1 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs @@ -10,7 +10,7 @@ open System.Diagnostics open Microsoft.CodeAnalysis open FSharp.Compiler.EditorServices open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics -open Internal.Utilities.CancellableTasks +open CancellableTasks [)>] type internal UnusedDeclarationsAnalyzer [] () = diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index 8ebb090ab1c..6ed01ddf1d7 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -26,6 +26,7 @@ LegacyResolver.txt + diff --git a/vsintegration/src/FSharp.Editor/Hints/FSharpInlineHintsService.fs b/vsintegration/src/FSharp.Editor/Hints/FSharpInlineHintsService.fs index a00623c0560..3db930e27a3 100644 --- a/vsintegration/src/FSharp.Editor/Hints/FSharpInlineHintsService.fs +++ b/vsintegration/src/FSharp.Editor/Hints/FSharpInlineHintsService.fs @@ -7,7 +7,7 @@ open System.ComponentModel.Composition open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints open Microsoft.VisualStudio.FSharp.Editor open Microsoft.VisualStudio.FSharp.Editor.Telemetry -open Internal.Utilities.CancellableTasks +open CancellableTasks open System.Threading.Tasks // So the Roslyn interface is called IFSharpInlineHintsService diff --git a/vsintegration/src/FSharp.Editor/Hints/HintService.fs b/vsintegration/src/FSharp.Editor/Hints/HintService.fs index 1af7bc7b207..95b12190f85 100644 --- a/vsintegration/src/FSharp.Editor/Hints/HintService.fs +++ b/vsintegration/src/FSharp.Editor/Hints/HintService.fs @@ -7,7 +7,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.VisualStudio.FSharp.Editor open FSharp.Compiler.Symbols open Hints -open Internal.Utilities.CancellableTasks +open CancellableTasks module HintService = diff --git a/vsintegration/src/FSharp.Editor/Hints/Hints.fs b/vsintegration/src/FSharp.Editor/Hints/Hints.fs index 6064884ecef..a554c905ba7 100644 --- a/vsintegration/src/FSharp.Editor/Hints/Hints.fs +++ b/vsintegration/src/FSharp.Editor/Hints/Hints.fs @@ -4,7 +4,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor.Hints open Microsoft.CodeAnalysis open FSharp.Compiler.Text -open Internal.Utilities.CancellableTasks +open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks module Hints = diff --git a/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs b/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs index 4486e569b8d..f428a144cfb 100644 --- a/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs +++ b/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs @@ -9,7 +9,7 @@ open FSharp.Compiler.EditorServices open FSharp.Compiler.Symbols open FSharp.Compiler.Text open Hints -open Internal.Utilities.CancellableTasks +open CancellableTasks type InlineParameterNameHints(parseResults: FSharpParseFileResults) = diff --git a/vsintegration/src/FSharp.Editor/Hints/InlineReturnTypeHints.fs b/vsintegration/src/FSharp.Editor/Hints/InlineReturnTypeHints.fs index a05c2bb74ca..1a984df33c7 100644 --- a/vsintegration/src/FSharp.Editor/Hints/InlineReturnTypeHints.fs +++ b/vsintegration/src/FSharp.Editor/Hints/InlineReturnTypeHints.fs @@ -7,7 +7,7 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Symbols open FSharp.Compiler.Text open Hints -open Internal.Utilities.CancellableTasks +open CancellableTasks type InlineReturnTypeHints(parseFileResults: FSharpParseFileResults, symbol: FSharpMemberOrFunctionOrValue) = diff --git a/vsintegration/src/FSharp.Editor/Hints/InlineTypeHints.fs b/vsintegration/src/FSharp.Editor/Hints/InlineTypeHints.fs index 5a902067cf3..38c6bda6a2d 100644 --- a/vsintegration/src/FSharp.Editor/Hints/InlineTypeHints.fs +++ b/vsintegration/src/FSharp.Editor/Hints/InlineTypeHints.fs @@ -8,7 +8,7 @@ open FSharp.Compiler.Symbols open FSharp.Compiler.Text open FSharp.Compiler.Text.Position open Hints -open Internal.Utilities.CancellableTasks +open CancellableTasks type InlineTypeHints(parseResults: FSharpParseFileResults, symbol: FSharpMemberOrFunctionOrValue) = diff --git a/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs b/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs index 2fab2e28ef4..61442b6b547 100644 --- a/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs +++ b/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs @@ -10,7 +10,7 @@ open Microsoft.VisualStudio.FSharp.Editor open Microsoft.CodeAnalysis open FSharp.Compiler.Text open Hints -open Internal.Utilities.CancellableTasks +open CancellableTasks module NativeToRoslynHintConverter = diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 02e6ac7a51b..720b8d272e9 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -24,8 +24,7 @@ open Microsoft.VisualStudio.Text.Outlining open Microsoft.CodeAnalysis.ExternalAccess.FSharp open Microsoft.CodeAnalysis.Host.Mef open Microsoft.VisualStudio.FSharp.Editor.Telemetry -open Internal.Utilities.CancellableTasks.CancellableTaskBuilder -open Internal.Utilities.CancellableTasks +open CancellableTasks #nowarn "9" // NativePtr.toNativeInt #nowarn "57" // Experimental stuff diff --git a/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs b/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs index 46607dc7607..ea2b41ca3d0 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs @@ -24,8 +24,7 @@ open Microsoft.VisualStudio.Core.Imaging open Microsoft.VisualStudio.Imaging open Microsoft.CodeAnalysis.ExternalAccess.FSharp -open Internal.Utilities.CancellableTasks.CancellableTaskBuilder -open Internal.Utilities.CancellableTasks +open CancellableTasks type private FSharpGlyph = FSharp.Compiler.EditorServices.FSharpGlyph type private Glyph = Microsoft.CodeAnalysis.ExternalAccess.FSharp.FSharpGlyph diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs index d5ae1236b65..0311769f4d8 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs @@ -17,7 +17,7 @@ open Microsoft.VisualStudio.LanguageServices open Microsoft.VisualStudio.Text.PatternMatching open FSharp.Compiler.EditorServices -open Internal.Utilities.CancellableTasks +open CancellableTasks [); Shared>] type internal FSharpNavigateToSearchService [] diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index d89fee1dc97..db0f3933703 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -18,7 +18,7 @@ open Microsoft.VisualStudio.FSharp.Editor open FSharp.Compiler.Text open Microsoft.IO open FSharp.Compiler.EditorServices -open Internal.Utilities.CancellableTasks +open CancellableTasks type internal FSharpAsyncQuickInfoSource ( diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index 5c5688b8a95..0be41a6b07e 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -147,7 +147,7 @@ module internal BlockStructure = | _, _ -> None) open BlockStructure -open Internal.Utilities.CancellableTasks +open CancellableTasks [)>] type internal FSharpBlockStructureService [] () = diff --git a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs index 784fee00fe7..4e52874d8b3 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs @@ -2,7 +2,7 @@ namespace FSharp.Editor.Tests -open Internal.Utilities.CancellableTasks +open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks module CompletionProviderTests = diff --git a/vsintegration/tests/FSharp.Editor.Tests/DocumentDiagnosticAnalyzerTests.fs b/vsintegration/tests/FSharp.Editor.Tests/DocumentDiagnosticAnalyzerTests.fs index aab7903b6c9..ed6b6091e7c 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/DocumentDiagnosticAnalyzerTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/DocumentDiagnosticAnalyzerTests.fs @@ -7,8 +7,8 @@ open Microsoft.CodeAnalysis open Microsoft.VisualStudio.FSharp.Editor open FSharp.Editor.Tests.Helpers open FSharp.Test -open Internal.Utilities.CancellableTasks open System.Threading +open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks type DocumentDiagnosticAnalyzerTests() = let startMarker = "(*start*)" diff --git a/vsintegration/tests/FSharp.Editor.Tests/FsxCompletionProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/FsxCompletionProviderTests.fs index 4a949802ebf..49955dedbb4 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/FsxCompletionProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/FsxCompletionProviderTests.fs @@ -9,7 +9,7 @@ open Xunit open Microsoft.VisualStudio.FSharp.Editor open FSharp.Compiler.CodeAnalysis open FSharp.Editor.Tests.Helpers -open Internal.Utilities.CancellableTasks +open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks // AppDomain helper type Worker() = diff --git a/vsintegration/tests/FSharp.Editor.Tests/HelpContextServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/HelpContextServiceTests.fs index 1c73d904b32..f52ef63b53c 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/HelpContextServiceTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/HelpContextServiceTests.fs @@ -10,7 +10,7 @@ open Microsoft.VisualStudio.FSharp.Editor open Microsoft.IO open FSharp.Editor.Tests.Helpers open Microsoft.CodeAnalysis.Text -open Internal.Utilities.CancellableTasks +open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks type HelpContextServiceTests() = let getMarkers (source: string) = diff --git a/vsintegration/tests/FSharp.Editor.Tests/Hints/HintTestFramework.fs b/vsintegration/tests/FSharp.Editor.Tests/Hints/HintTestFramework.fs index 3eb54c7ecc5..d252d678d2f 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/Hints/HintTestFramework.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/Hints/HintTestFramework.fs @@ -7,8 +7,8 @@ open Microsoft.VisualStudio.FSharp.Editor open Microsoft.VisualStudio.FSharp.Editor.Hints open Hints open FSharp.Editor.Tests.Helpers -open Internal.Utilities.CancellableTasks open System.Threading +open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks module HintTestFramework = diff --git a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs index 35ee681f5f5..30d58f70965 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs @@ -11,7 +11,7 @@ open Microsoft.VisualStudio.FSharp.Editor open Microsoft.VisualStudio.FSharp.Editor.QuickInfo open FSharp.Editor.Tests.Helpers open FSharp.Test -open Internal.Utilities.CancellableTasks +open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks type public AssemblyResolverTestFixture() = diff --git a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs index eab21e15abb..e5188b5eef8 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs @@ -7,7 +7,7 @@ open Microsoft.VisualStudio.FSharp.Editor open Microsoft.VisualStudio.FSharp.Editor.QuickInfo open Xunit open FSharp.Editor.Tests.Helpers -open Internal.Utilities.CancellableTasks +open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks module QuickInfo = open FSharp.Compiler.EditorServices From 555fd73a2cb07ea9459097339f7ec44bafb11fc7 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Thu, 11 May 2023 16:20:02 +0200 Subject: [PATCH 07/20] fantomas --- .fantomasignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.fantomasignore b/.fantomasignore index 342f81a3b2f..479fa45ee4f 100644 --- a/.fantomasignore +++ b/.fantomasignore @@ -90,11 +90,11 @@ src/FSharp.Core/seqcore.fs src/Compiler/AbstractIL/ilwrite.fs src/Compiler/Utilities/lib.fs -src/Compiler/Utilities/CancellableTask.fs src/Compiler/Service/IncrementalBuild.fs src/Compiler/Service/ServiceAssemblyContent.fs src/Compiler/Service/ServiceDeclarationLists.fs src/Compiler/Service/ServiceErrorResolutionHints.fs +vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs # Fantomas limitations on signature files (to investigate) From 418c79f4d3821a0fbd954c9b2938db622b148b52 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Thu, 18 May 2023 17:11:32 +0200 Subject: [PATCH 08/20] Fantomas + PR feedback --- .../FSharp.Editor/Classification/ClassificationService.fs | 5 +++-- vsintegration/src/FSharp.Editor/Hints/HintService.fs | 7 +------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index 91e51559ea1..75fad4c2473 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -109,7 +109,6 @@ type internal FSharpClassificationService [] () = ) result.ToImmutable() - static let addSemanticClassification sourceText @@ -198,7 +197,9 @@ type internal FSharpClassificationService [] () = TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSyntacticCalssifications, eventProps) if not isOpenDocument then - let classifiedSpans = getLexicalClassifications (document.FilePath, defines, sourceText, textSpan, cancellationToken) + let classifiedSpans = + getLexicalClassifications (document.FilePath, defines, sourceText, textSpan, cancellationToken) + result.AddRange(classifiedSpans) else result.AddRange( diff --git a/vsintegration/src/FSharp.Editor/Hints/HintService.fs b/vsintegration/src/FSharp.Editor/Hints/HintService.fs index 95b12190f85..c35059c0c1c 100644 --- a/vsintegration/src/FSharp.Editor/Hints/HintService.fs +++ b/vsintegration/src/FSharp.Editor/Hints/HintService.fs @@ -27,12 +27,7 @@ module HintService = |> Seq.collect (InlineParameterNameHints(parseResults).GetHintsForUnionCase symbol) | _ -> [] - let rec loop hintKinds acc = - match hintKinds with - | [] -> acc - | hintKind :: hintKinds -> (getHintsPerKind hintKind) :: (loop hintKinds acc) - - loop (hintKinds |> Set.toList) [] + hintKinds |> Set.toList |> List.map getHintsPerKind let private getHintsForSymbol (sourceText: SourceText) parseResults hintKinds (symbol, symbolUses) = let hints = getHints sourceText parseResults hintKinds symbolUses symbol From bf51b312ca1065fc96e3b0dd6cf9d79b3efd90eb Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Thu, 18 May 2023 18:02:32 +0200 Subject: [PATCH 09/20] Update vsintegration/src/FSharp.Editor/Hints/HintService.fs Co-authored-by: Andrii Chebukin --- vsintegration/src/FSharp.Editor/Hints/HintService.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/Hints/HintService.fs b/vsintegration/src/FSharp.Editor/Hints/HintService.fs index c35059c0c1c..6d9f342b974 100644 --- a/vsintegration/src/FSharp.Editor/Hints/HintService.fs +++ b/vsintegration/src/FSharp.Editor/Hints/HintService.fs @@ -27,7 +27,7 @@ module HintService = |> Seq.collect (InlineParameterNameHints(parseResults).GetHintsForUnionCase symbol) | _ -> [] - hintKinds |> Set.toList |> List.map getHintsPerKind + hintKinds |> Seq.map getHintsPerKind |> Set.toList let private getHintsForSymbol (sourceText: SourceText) parseResults hintKinds (symbol, symbolUses) = let hints = getHints sourceText parseResults hintKinds symbolUses symbol From 008df2b0bc5e816e751c541fb13065cb43324e63 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Fri, 19 May 2023 16:10:08 +0200 Subject: [PATCH 10/20] Revert "Update vsintegration/src/FSharp.Editor/Hints/HintService.fs" This reverts commit bf51b312ca1065fc96e3b0dd6cf9d79b3efd90eb. --- vsintegration/src/FSharp.Editor/Hints/HintService.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/Hints/HintService.fs b/vsintegration/src/FSharp.Editor/Hints/HintService.fs index 6d9f342b974..c35059c0c1c 100644 --- a/vsintegration/src/FSharp.Editor/Hints/HintService.fs +++ b/vsintegration/src/FSharp.Editor/Hints/HintService.fs @@ -27,7 +27,7 @@ module HintService = |> Seq.collect (InlineParameterNameHints(parseResults).GetHintsForUnionCase symbol) | _ -> [] - hintKinds |> Seq.map getHintsPerKind |> Set.toList + hintKinds |> Set.toList |> List.map getHintsPerKind let private getHintsForSymbol (sourceText: SourceText) parseResults hintKinds (symbol, symbolUses) = let hints = getHints sourceText parseResults hintKinds symbolUses symbol From 709563eeff0283c50677190ef8cfefeba2f5aba2 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 22 May 2023 17:09:53 +0200 Subject: [PATCH 11/20] Update CancellableTasks.fs --- .../FSharp.Editor/Common/CancellableTasks.fs | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs index 0e052daa959..11ef1ebe152 100644 --- a/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs +++ b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs @@ -1,5 +1,25 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - +// +// Implementation is taken from IcedTasks (https://github.com/TheAngryByrd/IcedTasks/blob/72638c8719014ae963f2662449c99f87090041d1/LICENSE.md?plain=1#L1-L21), under MIT license +// Which was originally written in 2016 by Robert Peele (humbobst@gmail.com) +// New operator-based overload resolution for F# 4.0 compatibility by Gustavo Leon in 2018. +// Revised for insertion into FSharp.Core by Microsoft, 2019. +// Revised to implement CancellationToken semantics +// +// Original notice: +// To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights +// to this software to the public domain worldwide. This software is distributed without any warranty. +// IcedTasks MIT notice (https://github.com/TheAngryByrd/IcedTasks/blob/72638c8719014ae963f2662449c99f87090041d1/LICENSE.md?plain=1#L1-L21): +// MIT License + +// Copyright (c) [year] [fullname] + +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + namespace Microsoft.VisualStudio.FSharp.Editor // Don't warn about the resumable code invocation @@ -934,4 +954,4 @@ module CancellableTasks = let! rightResult = rightStarted return leftResult, rightResult } - |> CancellableTask.getAwaiter \ No newline at end of file + |> CancellableTask.getAwaiter From 20b4921a375597de0886c5290a9bcf1b78229d4d Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Tue, 23 May 2023 16:12:42 +0200 Subject: [PATCH 12/20] Added missing event properties for classifications event --- .../FSharp.Editor/Classification/ClassificationService.fs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index 75fad4c2473..80613818f9c 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -250,9 +250,11 @@ type internal FSharpClassificationService [] () = addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result | _ -> - let eventProps = + let eventProps : (string * obj) array = [| - "isOpenDocument", isOpenDocument :> obj + "context.document.project.id", document.Project.Id.Id.ToString() + "context.document.id", document.Id.Id.ToString() + "isOpenDocument", isOpenDocument "textSpanLength", textSpan.Length "cacheHit", false |] From 327b215381eea3e79f599b5b937dbc210347e2df Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Tue, 23 May 2023 16:15:19 +0200 Subject: [PATCH 13/20] Added measurements for startup --- .../LanguageService/LanguageService.fs | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 25570918669..01ea40c2def 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -107,8 +107,6 @@ type internal FSharpWorkspaceServiceFactory [] | _ -> let checker = lazy - TelemetryReporter.ReportSingleEvent(TelemetryEvents.LanguageServiceStarted, [||]) - let editorOptions = workspace.Services.GetService() let enableParallelReferenceResolution = @@ -132,6 +130,22 @@ type internal FSharpWorkspaceServiceFactory [] let enablePartialTypeChecking = editorOptions.LanguageServicePerformance.EnablePartialTypeChecking + use _eventDuration = + TelemetryReporter.ReportSingleEventWithDuration( + TelemetryEvents.LanguageServiceStarted, + [| + nameof enableLiveBuffers, enableLiveBuffers + nameof useSyntaxTreeCache, useSyntaxTreeCache + nameof enableParallelReferenceResolution, enableParallelReferenceResolution + nameof enableFastFindReferences, enableFastFindReferences + nameof isInlineParameterNameHintsEnabled, isInlineParameterNameHintsEnabled + nameof isInlineTypeHintsEnabled, isInlineTypeHintsEnabled + nameof isInlineReturnTypeHintsEnabled, isInlineReturnTypeHintsEnabled + nameof enablePartialTypeChecking, enablePartialTypeChecking + |], + TelemetryThrottlingStrategy.NoThrottling + ) + let checker = FSharpChecker.Create( projectCacheSize = 5000, // We do not care how big the cache is. VS will actually tell FCS to clear caches, so this is fine. @@ -151,19 +165,7 @@ type internal FSharpWorkspaceServiceFactory [] useSyntaxTreeCache = useSyntaxTreeCache ) - TelemetryReporter.ReportSingleEvent( - TelemetryEvents.LanguageServiceStarted, - [| - nameof enableLiveBuffers, enableLiveBuffers - nameof useSyntaxTreeCache, useSyntaxTreeCache - nameof enableParallelReferenceResolution, enableParallelReferenceResolution - nameof enableFastFindReferences, enableFastFindReferences - nameof isInlineParameterNameHintsEnabled, isInlineParameterNameHintsEnabled - nameof isInlineTypeHintsEnabled, isInlineTypeHintsEnabled - nameof isInlineReturnTypeHintsEnabled, isInlineReturnTypeHintsEnabled - nameof enablePartialTypeChecking, enablePartialTypeChecking - |] - ) + if enableLiveBuffers then workspace.WorkspaceChanged.Add(fun args -> From 523f5ead2bee26b31f3c11e3a7eb5704aa4ea688 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 14:23:25 +0000 Subject: [PATCH 14/20] Automated command ran: fantomas Co-authored-by: vzarytovskii <1260985+vzarytovskii@users.noreply.github.com> --- .../src/FSharp.Editor/Classification/ClassificationService.fs | 2 +- .../src/FSharp.Editor/LanguageService/LanguageService.fs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index 80613818f9c..0f069a13b1c 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -250,7 +250,7 @@ type internal FSharpClassificationService [] () = addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result | _ -> - let eventProps : (string * obj) array = + let eventProps: (string * obj) array = [| "context.document.project.id", document.Project.Id.Id.ToString() "context.document.id", document.Id.Id.ToString() diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 01ea40c2def..864a772fc59 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -165,8 +165,6 @@ type internal FSharpWorkspaceServiceFactory [] useSyntaxTreeCache = useSyntaxTreeCache ) - - if enableLiveBuffers then workspace.WorkspaceChanged.Add(fun args -> if args.DocumentId <> null then From cdf5e5fab6b80e09009ac0001e796f44084f156b Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Wed, 24 May 2023 18:15:37 +0200 Subject: [PATCH 15/20] Added more settings for testing, added cache for hints --- .../Classification/ClassificationService.fs | 41 +---------------- .../src/FSharp.Editor/Common/DocumentCache.fs | 41 +++++++++++++++++ .../Debugging/BreakpointResolutionService.fs | 29 +++++++----- .../src/FSharp.Editor/FSharp.Editor.fsproj | 1 + .../Hints/FSharpInlineHintsService.fs | 3 -- .../src/FSharp.Editor/Hints/HintService.fs | 35 +++++++++++---- .../src/FSharp.Editor/Hints/Hints.fs | 7 +-- .../LanguageService/LanguageService.fs | 28 ++++++++++-- .../FSharp.Editor/Options/EditorOptions.fs | 8 ++++ .../Structure/BlockStructureService.fs | 1 - ...nguageServicePerformanceOptionControl.xaml | 17 +++++++ .../FSharp.UIResources/Strings.Designer.cs | 45 +++++++++++++++++++ .../src/FSharp.UIResources/Strings.resx | 15 +++++++ .../src/FSharp.UIResources/xlf/Strings.cs.xlf | 25 +++++++++++ .../src/FSharp.UIResources/xlf/Strings.de.xlf | 25 +++++++++++ .../src/FSharp.UIResources/xlf/Strings.es.xlf | 25 +++++++++++ .../src/FSharp.UIResources/xlf/Strings.fr.xlf | 25 +++++++++++ .../src/FSharp.UIResources/xlf/Strings.it.xlf | 25 +++++++++++ .../src/FSharp.UIResources/xlf/Strings.ja.xlf | 25 +++++++++++ .../src/FSharp.UIResources/xlf/Strings.ko.xlf | 25 +++++++++++ .../src/FSharp.UIResources/xlf/Strings.pl.xlf | 25 +++++++++++ .../FSharp.UIResources/xlf/Strings.pt-BR.xlf | 25 +++++++++++ .../src/FSharp.UIResources/xlf/Strings.ru.xlf | 25 +++++++++++ .../src/FSharp.UIResources/xlf/Strings.tr.xlf | 25 +++++++++++ .../xlf/Strings.zh-Hans.xlf | 25 +++++++++++ .../xlf/Strings.zh-Hant.xlf | 25 +++++++++++ .../FSharp.Editor.Tests.fsproj | 4 ++ .../UnitTests/VisualFSharp.UnitTests.fsproj | 1 + 28 files changed, 530 insertions(+), 71 deletions(-) create mode 100644 vsintegration/src/FSharp.Editor/Common/DocumentCache.fs diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index 80613818f9c..2937c8f1969 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -29,45 +29,6 @@ open Microsoft.VisualStudio.FSharp.Editor.Telemetry type SemanticClassificationData = SemanticClassificationView type SemanticClassificationLookup = IReadOnlyDictionary> -[] -type DocumentCache<'Value when 'Value: not struct>() = - /// Anything under two seconds, the caching stops working, meaning it won't actually cache the item. - /// Two seconds is just enough to keep the data around long enough to handle a flood of a requests asking for the same data - /// in a short period of time. - [] - let slidingExpirationSeconds = 2. - - let cache = new MemoryCache("fsharp-cache") - - let policy = - CacheItemPolicy(SlidingExpiration = TimeSpan.FromSeconds slidingExpirationSeconds) - - member _.TryGetValueAsync(doc: Document) = - cancellableTask { - let! ct = CancellableTask.getCurrentCancellationToken () - let! currentVersion = doc.GetTextVersionAsync ct - - match cache.Get(doc.Id.ToString()) with - | null -> return ValueNone - | :? (VersionStamp * 'Value) as value -> - if fst value = currentVersion then - return ValueSome(snd value) - else - return ValueNone - | _ -> return ValueNone - } - - member _.SetAsync(doc: Document, value: 'Value) = - cancellableTask { - let! ct = CancellableTask.getCurrentCancellationToken () - let! currentVersion = doc.GetTextVersionAsync ct - cache.Set(doc.Id.ToString(), (currentVersion, value), policy) - } - - interface IDisposable with - - member _.Dispose() = cache.Dispose() - [)>] type internal FSharpClassificationService [] () = @@ -159,7 +120,7 @@ type internal FSharpClassificationService [] () = Collections.ObjectModel.ReadOnlyDictionary lookup :> IReadOnlyDictionary<_, _> - let semanticClassificationCache = new DocumentCache() + let semanticClassificationCache = new DocumentCache("fsharp-semantic-classification-cache") interface IFSharpClassificationService with // Do not perform classification if we don't have project options (#defines matter) diff --git a/vsintegration/src/FSharp.Editor/Common/DocumentCache.fs b/vsintegration/src/FSharp.Editor/Common/DocumentCache.fs new file mode 100644 index 00000000000..c8561540c87 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/Common/DocumentCache.fs @@ -0,0 +1,41 @@ +namespace Microsoft.VisualStudio.FSharp.Editor + +open System +open System.Runtime.Caching +open Microsoft.CodeAnalysis +open CancellableTasks + +[] +type DocumentCache<'Value when 'Value: not struct>(name: string, ?cacheItemPolicy: CacheItemPolicy) = + + [] + let defaultSlidingExpiration = 2. + + let cache = new MemoryCache(name) + + let policy = defaultArg cacheItemPolicy (CacheItemPolicy(SlidingExpiration = (TimeSpan.FromSeconds defaultSlidingExpiration))) + + member _.TryGetValueAsync(doc: Document) = + cancellableTask { + let! ct = CancellableTask.getCurrentCancellationToken () + let! currentVersion = doc.GetTextVersionAsync ct + + match cache.Get(doc.Id.ToString()) with + | null -> return ValueNone + | :? (VersionStamp * 'Value) as value -> + if fst value = currentVersion then + return ValueSome(snd value) + else + return ValueNone + | _ -> return ValueNone + } + + member _.SetAsync(doc: Document, value: 'Value) = + cancellableTask { + let! ct = CancellableTask.getCurrentCancellationToken () + let! currentVersion = doc.GetTextVersionAsync ct + cache.Set(doc.Id.ToString(), (currentVersion, value), policy) + } + + interface IDisposable with + member _.Dispose() = cache.Dispose() \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs index f239a1b7856..b565ed84714 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs @@ -16,15 +16,16 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor.Implementation.Debuggin open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Text open FSharp.Compiler.Text.Position +open CancellableTasks [)>] type internal FSharpBreakpointResolutionService [] () = static member GetBreakpointLocation(document: Document, textSpan: TextSpan) = - async { - let! ct = Async.CancellationToken + cancellableTask { + let! ct = CancellableTask.getCurrentCancellationToken () - let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask + let! sourceText = document.GetTextAsync(ct) let textLinePos = sourceText.Lines.GetLinePosition(textSpan.Start) @@ -47,21 +48,27 @@ type internal FSharpBreakpointResolutionService [] () = } interface IFSharpBreakpointResolutionService with - member this.ResolveBreakpointAsync + member _.ResolveBreakpointAsync ( document: Document, textSpan: TextSpan, cancellationToken: CancellationToken ) : Task = - asyncMaybe { + cancellableTask { let! range = FSharpBreakpointResolutionService.GetBreakpointLocation(document, textSpan) - let! sourceText = document.GetTextAsync(cancellationToken) - let! span = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) - return FSharpBreakpointResolutionResult.CreateSpanResult(document, span) + match range with + | None -> return Unchecked.defaultof<_> + | Some range -> + let! sourceText = document.GetTextAsync(cancellationToken) + let span = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) + + match span with + | None -> return Unchecked.defaultof<_> + | Some span -> + return FSharpBreakpointResolutionResult.CreateSpanResult(document, span) } - |> Async.map Option.toObj - |> RoslynHelpers.StartAsyncAsTask cancellationToken + |> CancellableTask.start cancellationToken // FSROSLYNTODO: enable placing breakpoints by when user supplies fully-qualified function names - member this.ResolveBreakpointsAsync(_, _, _) : Task> = + member _.ResolveBreakpointsAsync(_, _, _) : Task> = Task.FromResult(Enumerable.Empty()) diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index d7e09b7b091..d27a81b91c2 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -27,6 +27,7 @@ LegacyResolver.txt + diff --git a/vsintegration/src/FSharp.Editor/Hints/FSharpInlineHintsService.fs b/vsintegration/src/FSharp.Editor/Hints/FSharpInlineHintsService.fs index 6178593acdb..fe30a2bd5ca 100644 --- a/vsintegration/src/FSharp.Editor/Hints/FSharpInlineHintsService.fs +++ b/vsintegration/src/FSharp.Editor/Hints/FSharpInlineHintsService.fs @@ -30,9 +30,6 @@ type internal FSharpInlineHintsService [] (settings: Edito cancellableTask { let! cancellationToken = CancellableTask.getCurrentCancellationToken () - let hintKindsSerialized = hintKinds |> Set.map Hints.serialize |> String.concat "," - TelemetryReporter.ReportSingleEvent(TelemetryEvents.Hints, [| ("hints.kinds", hintKindsSerialized) |]) - let! sourceText = document.GetTextAsync cancellationToken let! nativeHints = HintService.getHintsForDocument sourceText document hintKinds userOpName diff --git a/vsintegration/src/FSharp.Editor/Hints/HintService.fs b/vsintegration/src/FSharp.Editor/Hints/HintService.fs index c35059c0c1c..f3a7dbf7766 100644 --- a/vsintegration/src/FSharp.Editor/Hints/HintService.fs +++ b/vsintegration/src/FSharp.Editor/Hints/HintService.fs @@ -2,15 +2,20 @@ namespace Microsoft.VisualStudio.FSharp.Editor.Hints +open System + open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.VisualStudio.FSharp.Editor open FSharp.Compiler.Symbols open Hints open CancellableTasks +open Microsoft.VisualStudio.FSharp.Editor.Telemetry module HintService = + let semanticClassificationCache = new DocumentCache("fsharp-hints-cache") + let private getHints sourceText parseResults hintKinds symbolUses (symbol: FSharpSymbol) = let getHintsPerKind hintKind = @@ -36,14 +41,26 @@ module HintService = let getHintsForDocument sourceText (document: Document) hintKinds userOpName = cancellableTask { if isSignatureFile document.FilePath then - return [] + return List.empty else - let! cancellationToken = CancellableTask.getCurrentCancellationToken () - let! parseResults, checkResults = document.GetFSharpParseAndCheckResultsAsync userOpName - - return - checkResults.GetAllUsesOfAllSymbolsInFile cancellationToken - |> Seq.groupBy (fun symbolUse -> symbolUse.Symbol) - |> Seq.collect (getHintsForSymbol sourceText parseResults hintKinds) - |> Seq.toList + let hintKindsSerialized = hintKinds |> Set.map Hints.serialize |> String.concat "," + match! semanticClassificationCache.TryGetValueAsync document with + | ValueSome nativeHints -> + do TelemetryReporter.ReportSingleEvent(TelemetryEvents.Hints, [| ("hints.kinds", hintKindsSerialized); ("cacheHit", true) |]) + return nativeHints + | ValueNone -> + do TelemetryReporter.ReportSingleEvent(TelemetryEvents.Hints, [| ("hints.kinds", hintKindsSerialized); ("cacheHit", false) |]) + + let! cancellationToken = CancellableTask.getCurrentCancellationToken () + let! parseResults, checkResults = document.GetFSharpParseAndCheckResultsAsync userOpName + + let nativeHints = + checkResults.GetAllUsesOfAllSymbolsInFile cancellationToken + |> Seq.groupBy (fun symbolUse -> symbolUse.Symbol) + |> Seq.collect (getHintsForSymbol sourceText parseResults hintKinds) + |> Seq.toList + + do! semanticClassificationCache.SetAsync(document, nativeHints) + + return nativeHints } diff --git a/vsintegration/src/FSharp.Editor/Hints/Hints.fs b/vsintegration/src/FSharp.Editor/Hints/Hints.fs index a554c905ba7..0dd7fbff263 100644 --- a/vsintegration/src/FSharp.Editor/Hints/Hints.fs +++ b/vsintegration/src/FSharp.Editor/Hints/Hints.fs @@ -8,6 +8,7 @@ open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks module Hints = + [] type HintKind = | TypeHint | ParameterNameHint @@ -24,6 +25,6 @@ module Hints = let inline serialize kind = match kind with - | TypeHint -> "type" - | ParameterNameHint -> "parameterName" - | ReturnTypeHint -> "returnType" + | HintKind.TypeHint -> "type" + | HintKind.ParameterNameHint -> "parameterName" + | HintKind.ReturnTypeHint -> "returnType" diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 01ea40c2def..3baccc86ee1 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -130,6 +130,22 @@ type internal FSharpWorkspaceServiceFactory [] let enablePartialTypeChecking = editorOptions.LanguageServicePerformance.EnablePartialTypeChecking + // Default should be false + let keepAllBackgroundResolutions = + editorOptions.LanguageServicePerformance.KeepAllBackgroundResolutions + + // Default should be false + let keepAllBackgroundSymbolUses = + editorOptions.LanguageServicePerformance.KeepAllBackgroundSymbolUses + + // Default should be true + let enableBackgroundItemKeyStoreAndSemanticClassification = + editorOptions.LanguageServicePerformance.EnableBackgroundItemKeyStoreAndSemanticClassification + + // Default should be true + let captureIdentifiersWhenParsing = + editorOptions.LanguageServicePerformance.CaptureIdentifiersWhenParsing + use _eventDuration = TelemetryReporter.ReportSingleEventWithDuration( TelemetryEvents.LanguageServiceStarted, @@ -142,6 +158,10 @@ type internal FSharpWorkspaceServiceFactory [] nameof isInlineTypeHintsEnabled, isInlineTypeHintsEnabled nameof isInlineReturnTypeHintsEnabled, isInlineReturnTypeHintsEnabled nameof enablePartialTypeChecking, enablePartialTypeChecking + nameof keepAllBackgroundResolutions, keepAllBackgroundResolutions + nameof keepAllBackgroundSymbolUses, keepAllBackgroundSymbolUses + nameof enableBackgroundItemKeyStoreAndSemanticClassification, enableBackgroundItemKeyStoreAndSemanticClassification + nameof captureIdentifiersWhenParsing, captureIdentifiersWhenParsing |], TelemetryThrottlingStrategy.NoThrottling ) @@ -149,14 +169,14 @@ type internal FSharpWorkspaceServiceFactory [] let checker = FSharpChecker.Create( projectCacheSize = 5000, // We do not care how big the cache is. VS will actually tell FCS to clear caches, so this is fine. - keepAllBackgroundResolutions = false, + keepAllBackgroundResolutions = keepAllBackgroundResolutions, legacyReferenceResolver = LegacyMSBuildReferenceResolver.getResolver (), tryGetMetadataSnapshot = tryGetMetadataSnapshot, - keepAllBackgroundSymbolUses = false, - enableBackgroundItemKeyStoreAndSemanticClassification = true, + keepAllBackgroundSymbolUses = keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification = enableBackgroundItemKeyStoreAndSemanticClassification, enablePartialTypeChecking = enablePartialTypeChecking, parallelReferenceResolution = enableParallelReferenceResolution, - captureIdentifiersWhenParsing = true, + captureIdentifiersWhenParsing = captureIdentifiersWhenParsing, documentSource = (if enableLiveBuffers then DocumentSource.Custom getSource diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index 137bcce687d..73b8997dd55 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -84,6 +84,10 @@ type LanguageServicePerformanceOptions = EnableFastFindReferencesAndRename: bool EnablePartialTypeChecking: bool UseSyntaxTreeCache: bool + KeepAllBackgroundResolutions: bool + KeepAllBackgroundSymbolUses: bool + EnableBackgroundItemKeyStoreAndSemanticClassification: bool + CaptureIdentifiersWhenParsing: bool } static member Default = @@ -94,6 +98,10 @@ type LanguageServicePerformanceOptions = EnableFastFindReferencesAndRename = true EnablePartialTypeChecking = true UseSyntaxTreeCache = FSharpExperimentalFeaturesEnabledAutomatically + KeepAllBackgroundResolutions = false + KeepAllBackgroundSymbolUses = false + EnableBackgroundItemKeyStoreAndSemanticClassification = true + CaptureIdentifiersWhenParsing = true } [] diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index 0be41a6b07e..7a80409f505 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -3,7 +3,6 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition -open System.Collections.Immutable open System.Threading.Tasks open Microsoft.CodeAnalysis diff --git a/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml b/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml index 798fe5ad1b1..554e5d76e09 100644 --- a/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml +++ b/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml @@ -70,6 +70,23 @@ Content="{x:Static local:Strings.Enable_Fast_Find_References}"/> + + + + + + + + + diff --git a/vsintegration/src/FSharp.UIResources/Strings.Designer.cs b/vsintegration/src/FSharp.UIResources/Strings.Designer.cs index 1ce713052f2..69eca550813 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.Designer.cs +++ b/vsintegration/src/FSharp.UIResources/Strings.Designer.cs @@ -87,6 +87,15 @@ public static string Block_Structure { } } + /// + /// Looks up a localized string similar to Capture identifiers while parsing. + /// + public static string Capture_Identifiers_When_Parsing { + get { + return ResourceManager.GetString("Capture_Identifiers_When_Parsing", resourceCulture); + } + } + /// /// Looks up a localized string similar to Code Fixes. /// @@ -132,6 +141,15 @@ public static string Dot_underline { } } + /// + /// Looks up a localized string similar to Keep background symbol keys. + /// + public static string Enable_Background_ItemKeyStore_And_Semantic_Classification { + get { + return ResourceManager.GetString("Enable_Background_ItemKeyStore_And_Semantic_Classification", resourceCulture); + } + } + /// /// Looks up a localized string similar to Enable fast find references & rename (experimental). /// @@ -258,6 +276,24 @@ public static string IntelliSense_Performance { } } + /// + /// Looks up a localized string similar to Keep all background intermediate resolutions (increases memory usage). + /// + public static string Keep_All_Background_Resolutions { + get { + return ResourceManager.GetString("Keep_All_Background_Resolutions", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Keep all background symbol uses (increases memory usage). + /// + public static string Keep_All_Background_Symbol_Uses { + get { + return ResourceManager.GetString("Keep_All_Background_Symbol_Uses", resourceCulture); + } + } + /// /// Looks up a localized string similar to Performance. /// @@ -267,6 +303,15 @@ public static string Language_Service_Performance { } } + /// + /// Looks up a localized string similar to Language service settings (advanced). + /// + public static string Language_Service_Settings { + get { + return ResourceManager.GetString("Language_Service_Settings", resourceCulture); + } + } + /// /// Looks up a localized string similar to Live Buffers (experimental). /// diff --git a/vsintegration/src/FSharp.UIResources/Strings.resx b/vsintegration/src/FSharp.UIResources/Strings.resx index 12f2ce0f947..744428afe80 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.resx +++ b/vsintegration/src/FSharp.UIResources/Strings.resx @@ -231,6 +231,21 @@ Find References Performance Options + + Language service settings (advanced) + + + Keep all background intermediate resolutions (increases memory usage) + + + Keep all background symbol uses (increases memory usage) + + + Keep background symbol keys + + + Capture identifiers while parsing + Use live (unsaved) buffers for checking (restart required) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf index 1d431306de6..da3fb391af1 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf @@ -12,6 +12,16 @@ Vždy umístit otevřené příkazy na nejvyšší úroveň + + Capture identifiers while parsing + Capture identifiers while parsing + + + + Keep background symbol keys + Keep background symbol keys + + Enable fast find references & rename (experimental) Povolit odkazy rychlého hledání a přejmenování (experimentální) @@ -62,6 +72,16 @@ Povolit paralelní referenční rozlišení + + Keep all background intermediate resolutions (increases memory usage) + Keep all background intermediate resolutions (increases memory usage) + + + + Keep all background symbol uses (increases memory usage) + Keep all background symbol uses (increases memory usage) + + Performance Výkon @@ -77,6 +97,11 @@ _Tečkované podtržení + + Language service settings (advanced) + Language service settings (advanced) + + Live Buffers (experimental) Živé vyrovnávací paměti (experimentální) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf index 0b4a44eaa31..2dc2d80b0d0 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf @@ -12,6 +12,16 @@ open-Anweisungen immer an oberster Ebene platzieren + + Capture identifiers while parsing + Capture identifiers while parsing + + + + Keep background symbol keys + Keep background symbol keys + + Enable fast find references & rename (experimental) Schnellsuche und Umbenennen von Verweisen aktivieren (experimentell) @@ -62,6 +72,16 @@ Parallele Verweisauflösung aktivieren + + Keep all background intermediate resolutions (increases memory usage) + Keep all background intermediate resolutions (increases memory usage) + + + + Keep all background symbol uses (increases memory usage) + Keep all background symbol uses (increases memory usage) + + Performance Leistung @@ -77,6 +97,11 @@ Ge_punktete Unterstreichung + + Language service settings (advanced) + Language service settings (advanced) + + Live Buffers (experimental) Livepuffer (experimentell) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf index b3b7158342b..78b806cf790 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf @@ -12,6 +12,16 @@ Colocar siempre las instrucciones open en el nivel superior + + Capture identifiers while parsing + Capture identifiers while parsing + + + + Keep background symbol keys + Keep background symbol keys + + Enable fast find references & rename (experimental) Habilitar referencias de búsqueda rápida y cambio de nombre (experimental) @@ -62,6 +72,16 @@ Habilitar resolución de referencias paralelas + + Keep all background intermediate resolutions (increases memory usage) + Keep all background intermediate resolutions (increases memory usage) + + + + Keep all background symbol uses (increases memory usage) + Keep all background symbol uses (increases memory usage) + + Performance Rendimiento @@ -77,6 +97,11 @@ S_ubrayado punto + + Language service settings (advanced) + Language service settings (advanced) + + Live Buffers (experimental) Búferes activos (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf index 5970e89584b..d4b627066e5 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf @@ -12,6 +12,16 @@ Placer toujours les instructions open au niveau supérieur + + Capture identifiers while parsing + Capture identifiers while parsing + + + + Keep background symbol keys + Keep background symbol keys + + Enable fast find references & rename (experimental) Activer les références de recherche rapide et renommer (expérimental) @@ -62,6 +72,16 @@ Activer la résolution de référence parallèle + + Keep all background intermediate resolutions (increases memory usage) + Keep all background intermediate resolutions (increases memory usage) + + + + Keep all background symbol uses (increases memory usage) + Keep all background symbol uses (increases memory usage) + + Performance Performances @@ -77,6 +97,11 @@ S_oulignement avec des points + + Language service settings (advanced) + Language service settings (advanced) + + Live Buffers (experimental) Live Buffers (expérimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf index c7f7d17d24b..43a27f66f45 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf @@ -12,6 +12,16 @@ Inserisci sempre le istruzioni OPEN al primo livello + + Capture identifiers while parsing + Capture identifiers while parsing + + + + Keep background symbol keys + Keep background symbol keys + + Enable fast find references & rename (experimental) Abilitare la ricerca rapida dei riferimenti e la ridenominazione (sperimentale) @@ -62,6 +72,16 @@ Abilitare risoluzione riferimenti paralleli + + Keep all background intermediate resolutions (increases memory usage) + Keep all background intermediate resolutions (increases memory usage) + + + + Keep all background symbol uses (increases memory usage) + Keep all background symbol uses (increases memory usage) + + Performance Prestazioni @@ -77,6 +97,11 @@ Sottolineatura _punteggiata + + Language service settings (advanced) + Language service settings (advanced) + + Live Buffers (experimental) Buffer in tempo reale (sperimentale) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf index d699a836bcb..05f4288e8e8 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf @@ -12,6 +12,16 @@ Open ステートメントを常に最上位に配置する + + Capture identifiers while parsing + Capture identifiers while parsing + + + + Keep background symbol keys + Keep background symbol keys + + Enable fast find references & rename (experimental) 高速検索参照の有効化と名前の変更 (試験段階) @@ -62,6 +72,16 @@ 並列参照解決を有効にする + + Keep all background intermediate resolutions (increases memory usage) + Keep all background intermediate resolutions (increases memory usage) + + + + Keep all background symbol uses (increases memory usage) + Keep all background symbol uses (increases memory usage) + + Performance パフォーマンス @@ -77,6 +97,11 @@ 点線の下線(_O) + + Language service settings (advanced) + Language service settings (advanced) + + Live Buffers (experimental) ライブ バッファー (試験段階) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf index c603eb32700..26b4befb283 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf @@ -12,6 +12,16 @@ 항상 최상위에 open 문 배치 + + Capture identifiers while parsing + Capture identifiers while parsing + + + + Keep background symbol keys + Keep background symbol keys + + Enable fast find references & rename (experimental) 빠른 찾기 참조 및 이름 바꾸기 사용(실험적) @@ -62,6 +72,16 @@ 병렬 참조 해상도 사용 + + Keep all background intermediate resolutions (increases memory usage) + Keep all background intermediate resolutions (increases memory usage) + + + + Keep all background symbol uses (increases memory usage) + Keep all background symbol uses (increases memory usage) + + Performance 성능 @@ -77,6 +97,11 @@ 점 밑줄(_O) + + Language service settings (advanced) + Language service settings (advanced) + + Live Buffers (experimental) 라이브 버퍼(실험적) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf index 798997dddf3..fd8a7d9767f 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf @@ -12,6 +12,16 @@ Zawsze umieszczaj otwarte instrukcje na najwyższym poziomie + + Capture identifiers while parsing + Capture identifiers while parsing + + + + Keep background symbol keys + Keep background symbol keys + + Enable fast find references & rename (experimental) Włącz szybkie znajdowanie odwołań i zmień nazwę (eksperymentalne) @@ -62,6 +72,16 @@ Włącz równoległe rozpoznawanie odwołań + + Keep all background intermediate resolutions (increases memory usage) + Keep all background intermediate resolutions (increases memory usage) + + + + Keep all background symbol uses (increases memory usage) + Keep all background symbol uses (increases memory usage) + + Performance Wydajność @@ -77,6 +97,11 @@ P_odkreślenie z kropek + + Language service settings (advanced) + Language service settings (advanced) + + Live Buffers (experimental) Aktywne bufory (eksperymentalne) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf index 110cdc0ee86..a6e151fed23 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf @@ -12,6 +12,16 @@ Sempre coloque as instruções abertas no nível superior + + Capture identifiers while parsing + Capture identifiers while parsing + + + + Keep background symbol keys + Keep background symbol keys + + Enable fast find references & rename (experimental) Habilitar localizar referências rapidamente e renomear (experimental) @@ -62,6 +72,16 @@ Habilitar a resolução de referência paralela + + Keep all background intermediate resolutions (increases memory usage) + Keep all background intermediate resolutions (increases memory usage) + + + + Keep all background symbol uses (increases memory usage) + Keep all background symbol uses (increases memory usage) + + Performance Desempenho @@ -77,6 +97,11 @@ Sublinhado p_ontilhado + + Language service settings (advanced) + Language service settings (advanced) + + Live Buffers (experimental) Buffers Dinâmicos (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf index fabb6126bed..1bcad7fe621 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf @@ -12,6 +12,16 @@ Всегда располагайте открытые операторы на верхнем уровне + + Capture identifiers while parsing + Capture identifiers while parsing + + + + Keep background symbol keys + Keep background symbol keys + + Enable fast find references & rename (experimental) Включить быстрый поиск ссылок и переименование (экспериментальная версия) @@ -62,6 +72,16 @@ Включить параллельное разрешение ссылок + + Keep all background intermediate resolutions (increases memory usage) + Keep all background intermediate resolutions (increases memory usage) + + + + Keep all background symbol uses (increases memory usage) + Keep all background symbol uses (increases memory usage) + + Performance Производительность @@ -77,6 +97,11 @@ Пу_нктирное подчеркивание + + Language service settings (advanced) + Language service settings (advanced) + + Live Buffers (experimental) Динамические буферы (экспериментальная функция) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf index 892af3ca3e8..05935441a9a 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf @@ -12,6 +12,16 @@ Açık deyimleri her zaman en üst düzeye yerleştir + + Capture identifiers while parsing + Capture identifiers while parsing + + + + Keep background symbol keys + Keep background symbol keys + + Enable fast find references & rename (experimental) Başvuruları hızlı bulma ve yeniden adlandırmayı etkinleştir (deneysel) @@ -62,6 +72,16 @@ Paralel başvuru çözümlemeyi etkinleştir + + Keep all background intermediate resolutions (increases memory usage) + Keep all background intermediate resolutions (increases memory usage) + + + + Keep all background symbol uses (increases memory usage) + Keep all background symbol uses (increases memory usage) + + Performance Performans @@ -77,6 +97,11 @@ N_okta alt çizgi + + Language service settings (advanced) + Language service settings (advanced) + + Live Buffers (experimental) Canlı Arabellekler (deneysel) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf index 6e0ff2e91df..71be5eec586 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf @@ -12,6 +12,16 @@ 始终在顶层放置 open 语句 + + Capture identifiers while parsing + Capture identifiers while parsing + + + + Keep background symbol keys + Keep background symbol keys + + Enable fast find references & rename (experimental) 启用快速查找引用和重命名(实验性) @@ -62,6 +72,16 @@ 启用并行引用解析 + + Keep all background intermediate resolutions (increases memory usage) + Keep all background intermediate resolutions (increases memory usage) + + + + Keep all background symbol uses (increases memory usage) + Keep all background symbol uses (increases memory usage) + + Performance 性能 @@ -77,6 +97,11 @@ 点下划线(_O) + + Language service settings (advanced) + Language service settings (advanced) + + Live Buffers (experimental) 实时缓冲区(实验性) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf index 1ed09d1f289..e045c7812e2 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf @@ -12,6 +12,16 @@ 一律將 open 陳述式放在最上層 + + Capture identifiers while parsing + Capture identifiers while parsing + + + + Keep background symbol keys + Keep background symbol keys + + Enable fast find references & rename (experimental) 啟用快速尋找參考和重新命名 (實驗性) @@ -62,6 +72,16 @@ 啟用平行參考解析 + + Keep all background intermediate resolutions (increases memory usage) + Keep all background intermediate resolutions (increases memory usage) + + + + Keep all background symbol uses (increases memory usage) + Keep all background symbol uses (increases memory usage) + + Performance 效能 @@ -77,6 +97,11 @@ 點線底線(_O) + + Language service settings (advanced) + Language service settings (advanced) + + Live Buffers (experimental) 即時緩衝區 (實驗性) diff --git a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj index 3e5c4de421e..e1aae0a95a1 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj +++ b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj @@ -45,6 +45,10 @@ + + + + diff --git a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj index 1ee9d2b4a4a..15c15bf400b 100644 --- a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj +++ b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj @@ -104,6 +104,7 @@ + From 2ddefa69b5308d561e3f2018642277e9adbaf3fc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 24 May 2023 16:29:46 +0000 Subject: [PATCH 16/20] Automated command ran: fantomas Co-authored-by: vzarytovskii <1260985+vzarytovskii@users.noreply.github.com> --- .../Classification/ClassificationService.fs | 3 ++- .../src/FSharp.Editor/Common/DocumentCache.fs | 9 +++++---- .../Debugging/BreakpointResolutionService.fs | 4 ++-- .../src/FSharp.Editor/Hints/HintService.fs | 17 ++++++++++++++--- .../LanguageService/LanguageService.fs | 6 ++++-- 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index 817ba91bf8a..6afae8b6aaf 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -120,7 +120,8 @@ type internal FSharpClassificationService [] () = Collections.ObjectModel.ReadOnlyDictionary lookup :> IReadOnlyDictionary<_, _> - let semanticClassificationCache = new DocumentCache("fsharp-semantic-classification-cache") + let semanticClassificationCache = + new DocumentCache("fsharp-semantic-classification-cache") interface IFSharpClassificationService with // Do not perform classification if we don't have project options (#defines matter) diff --git a/vsintegration/src/FSharp.Editor/Common/DocumentCache.fs b/vsintegration/src/FSharp.Editor/Common/DocumentCache.fs index c8561540c87..f29e4c2e200 100644 --- a/vsintegration/src/FSharp.Editor/Common/DocumentCache.fs +++ b/vsintegration/src/FSharp.Editor/Common/DocumentCache.fs @@ -13,9 +13,10 @@ type DocumentCache<'Value when 'Value: not struct>(name: string, ?cacheItemPolic let cache = new MemoryCache(name) - let policy = defaultArg cacheItemPolicy (CacheItemPolicy(SlidingExpiration = (TimeSpan.FromSeconds defaultSlidingExpiration))) + let policy = + defaultArg cacheItemPolicy (CacheItemPolicy(SlidingExpiration = (TimeSpan.FromSeconds defaultSlidingExpiration))) - member _.TryGetValueAsync(doc: Document) = + member _.TryGetValueAsync(doc: Document) = cancellableTask { let! ct = CancellableTask.getCurrentCancellationToken () let! currentVersion = doc.GetTextVersionAsync ct @@ -30,7 +31,7 @@ type DocumentCache<'Value when 'Value: not struct>(name: string, ?cacheItemPolic | _ -> return ValueNone } - member _.SetAsync(doc: Document, value: 'Value) = + member _.SetAsync(doc: Document, value: 'Value) = cancellableTask { let! ct = CancellableTask.getCurrentCancellationToken () let! currentVersion = doc.GetTextVersionAsync ct @@ -38,4 +39,4 @@ type DocumentCache<'Value when 'Value: not struct>(name: string, ?cacheItemPolic } interface IDisposable with - member _.Dispose() = cache.Dispose() \ No newline at end of file + member _.Dispose() = cache.Dispose() diff --git a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs index b565ed84714..ff927ef8742 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs @@ -56,6 +56,7 @@ type internal FSharpBreakpointResolutionService [] () = ) : Task = cancellableTask { let! range = FSharpBreakpointResolutionService.GetBreakpointLocation(document, textSpan) + match range with | None -> return Unchecked.defaultof<_> | Some range -> @@ -64,8 +65,7 @@ type internal FSharpBreakpointResolutionService [] () = match span with | None -> return Unchecked.defaultof<_> - | Some span -> - return FSharpBreakpointResolutionResult.CreateSpanResult(document, span) + | Some span -> return FSharpBreakpointResolutionResult.CreateSpanResult(document, span) } |> CancellableTask.start cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Hints/HintService.fs b/vsintegration/src/FSharp.Editor/Hints/HintService.fs index f3a7dbf7766..c257ffa0102 100644 --- a/vsintegration/src/FSharp.Editor/Hints/HintService.fs +++ b/vsintegration/src/FSharp.Editor/Hints/HintService.fs @@ -14,7 +14,8 @@ open Microsoft.VisualStudio.FSharp.Editor.Telemetry module HintService = - let semanticClassificationCache = new DocumentCache("fsharp-hints-cache") + let semanticClassificationCache = + new DocumentCache("fsharp-hints-cache") let private getHints sourceText parseResults hintKinds symbolUses (symbol: FSharpSymbol) = @@ -44,12 +45,22 @@ module HintService = return List.empty else let hintKindsSerialized = hintKinds |> Set.map Hints.serialize |> String.concat "," + match! semanticClassificationCache.TryGetValueAsync document with | ValueSome nativeHints -> - do TelemetryReporter.ReportSingleEvent(TelemetryEvents.Hints, [| ("hints.kinds", hintKindsSerialized); ("cacheHit", true) |]) + do + TelemetryReporter.ReportSingleEvent( + TelemetryEvents.Hints, + [| ("hints.kinds", hintKindsSerialized); ("cacheHit", true) |] + ) + return nativeHints | ValueNone -> - do TelemetryReporter.ReportSingleEvent(TelemetryEvents.Hints, [| ("hints.kinds", hintKindsSerialized); ("cacheHit", false) |]) + do + TelemetryReporter.ReportSingleEvent( + TelemetryEvents.Hints, + [| ("hints.kinds", hintKindsSerialized); ("cacheHit", false) |] + ) let! cancellationToken = CancellableTask.getCurrentCancellationToken () let! parseResults, checkResults = document.GetFSharpParseAndCheckResultsAsync userOpName diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index d54ebd6b4ee..d008115d892 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -160,7 +160,8 @@ type internal FSharpWorkspaceServiceFactory [] nameof enablePartialTypeChecking, enablePartialTypeChecking nameof keepAllBackgroundResolutions, keepAllBackgroundResolutions nameof keepAllBackgroundSymbolUses, keepAllBackgroundSymbolUses - nameof enableBackgroundItemKeyStoreAndSemanticClassification, enableBackgroundItemKeyStoreAndSemanticClassification + nameof enableBackgroundItemKeyStoreAndSemanticClassification, + enableBackgroundItemKeyStoreAndSemanticClassification nameof captureIdentifiersWhenParsing, captureIdentifiersWhenParsing |], TelemetryThrottlingStrategy.NoThrottling @@ -173,7 +174,8 @@ type internal FSharpWorkspaceServiceFactory [] legacyReferenceResolver = LegacyMSBuildReferenceResolver.getResolver (), tryGetMetadataSnapshot = tryGetMetadataSnapshot, keepAllBackgroundSymbolUses = keepAllBackgroundSymbolUses, - enableBackgroundItemKeyStoreAndSemanticClassification = enableBackgroundItemKeyStoreAndSemanticClassification, + enableBackgroundItemKeyStoreAndSemanticClassification = + enableBackgroundItemKeyStoreAndSemanticClassification, enablePartialTypeChecking = enablePartialTypeChecking, parallelReferenceResolution = enableParallelReferenceResolution, captureIdentifiersWhenParsing = captureIdentifiersWhenParsing, From 4d93e7248c6296493583989ba12f04772aaa8172 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Thu, 25 May 2023 11:58:07 +0200 Subject: [PATCH 17/20] Fix test --- .../FSharp.Editor.Tests/BreakpointResolutionServiceTests.fs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vsintegration/tests/FSharp.Editor.Tests/BreakpointResolutionServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/BreakpointResolutionServiceTests.fs index 7322e981aba..fd24ee84001 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/BreakpointResolutionServiceTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/BreakpointResolutionServiceTests.fs @@ -3,6 +3,7 @@ namespace FSharp.Editor.Tests open System +open System.Threading open Xunit open Microsoft.CodeAnalysis.Text open Microsoft.VisualStudio.FSharp.Editor @@ -59,8 +60,8 @@ let main argv = TextSpan.FromBounds(searchPosition, searchPosition + searchToken.Length) let actualResolutionOption = - FSharpBreakpointResolutionService.GetBreakpointLocation(document, searchSpan) - |> Async.RunSynchronously + let task = FSharpBreakpointResolutionService.GetBreakpointLocation(document, searchSpan) CancellationToken.None + task.Result match actualResolutionOption with | None -> Assert.True(expectedResolution.IsNone, "BreakpointResolutionService failed to resolve breakpoint position") From 65ef0f5a5080926eb6eaa9983f83e07d7c738fca Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Thu, 25 May 2023 14:22:57 +0200 Subject: [PATCH 18/20] fantomas --- .../FSharp.Editor.Tests/BreakpointResolutionServiceTests.fs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vsintegration/tests/FSharp.Editor.Tests/BreakpointResolutionServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/BreakpointResolutionServiceTests.fs index fd24ee84001..08a73dc2816 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/BreakpointResolutionServiceTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/BreakpointResolutionServiceTests.fs @@ -60,7 +60,9 @@ let main argv = TextSpan.FromBounds(searchPosition, searchPosition + searchToken.Length) let actualResolutionOption = - let task = FSharpBreakpointResolutionService.GetBreakpointLocation(document, searchSpan) CancellationToken.None + let task = + FSharpBreakpointResolutionService.GetBreakpointLocation (document, searchSpan) CancellationToken.None + task.Result match actualResolutionOption with From c4d980fde4420745823a86fd68edddd559e4a7ee Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Thu, 25 May 2023 14:23:57 +0200 Subject: [PATCH 19/20] Enable perf telemetry automatically with posibillity to opt out --- vsintegration/src/FSharp.Editor/Options/EditorOptions.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index 73b8997dd55..dd33df48d63 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -124,7 +124,7 @@ type AdvancedOptions = IsInlineParameterNameHintsEnabled = false IsInlineReturnTypeHintsEnabled = false IsLiveBuffersEnabled = FSharpExperimentalFeaturesEnabledAutomatically - SendAdditionalTelemetry = FSharpExperimentalFeaturesEnabledAutomatically + SendAdditionalTelemetry = true } [] From 95b6cdc51dbf47a021cc55f5a0525c09c472f2d7 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Thu, 25 May 2023 15:22:20 +0200 Subject: [PATCH 20/20] Update vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj Co-authored-by: Petr --- vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index d27a81b91c2..598c72145aa 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -27,7 +27,7 @@ LegacyResolver.txt - +