Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#1097 incoming call hierarchy #1164

Merged
merged 13 commits into from
Oct 10, 2023
95 changes: 50 additions & 45 deletions src/FsAutoComplete.Core/AdaptiveExtensions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -480,46 +480,44 @@ module AsyncAVal =
let ofTask (value: Task<'a>) = ConstantVal(value) :> asyncaval<_>

let ofCancellableTask (value: CancellableTask<'a>) =
ConstantVal(
let cts = new CancellationTokenSource()

let cancel () =
cts.Cancel()
cts.Dispose()

let real =
task {
try
return! value cts.Token
finally
{ new AbstractVal<'a>() with
member x.Compute t =
let cts = new CancellationTokenSource()

let cancel () =
cts.Cancel()
cts.Dispose()
}

AdaptiveCancellableTask(cancel, real)
)
:> asyncaval<_>
let real =
task {
try
return! value cts.Token
finally
cts.Dispose()
}

AdaptiveCancellableTask(cancel, real) }
:> asyncaval<_>

let ofAsync (value: Async<'a>) =
ConstantVal(
let cts = new CancellationTokenSource()

let cancel () =
cts.Cancel()
cts.Dispose()

let real =
task {
try
return! Async.StartImmediateAsTask(value, cts.Token)
finally
{ new AbstractVal<'a>() with
member x.Compute t =
let cts = new CancellationTokenSource()

let cancel () =
cts.Cancel()
cts.Dispose()
}

AdaptiveCancellableTask(cancel, real)
)
:> asyncaval<_>
let real =
task {
try
return! Async.StartImmediateAsTask(value, cts.Token)
finally
cts.Dispose()
}

AdaptiveCancellableTask(cancel, real) }
:> asyncaval<_>

/// <summary>
/// Creates an async adaptive value evaluation the given value.
Expand Down Expand Up @@ -604,7 +602,13 @@ module AsyncAVal =
/// adaptive inputs.
/// </summary>
let mapSync (mapping: 'a -> CancellationToken -> 'b) (input: asyncaval<'a>) =
map (fun a ct -> Task.FromResult(mapping a ct)) input
map
(fun a ct ->
if ct.IsCancellationRequested then
Task.FromCanceled<_>(ct)
else
Task.FromResult(mapping a ct))
Comment on lines +607 to +610
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oooohhhh, this is subtle. good spot.

input

/// <summary>
/// Returns a new async adaptive value that adaptively applies the mapping function to the given
Expand Down Expand Up @@ -729,11 +733,10 @@ module AsyncAVal =

/// Returns a new async adaptive value that adaptively applies the mapping function to the given
/// optional adaptive inputs.
let mapOption f (value: asyncaval<'a option>) : asyncaval<'b option> =
let mapOption (f: 'a -> CancellationToken -> 'b) (value: asyncaval<'a option>) : asyncaval<'b option> =
mapSync (fun data ctok -> data |> Option.map (fun d -> f d ctok)) value

type AsyncAValBuilder() =

member inline x.MergeSources(v1: asyncaval<'T1>, v2: asyncaval<'T2>) =
(v1, v2)
||> AsyncAVal.map2 (fun a b ctok ->
Expand All @@ -742,24 +745,24 @@ type AsyncAValBuilder() =
else
Task.FromResult(a, b))


// member inline x.MergeSources3(v1 : aval<'T1>, v2 : aval<'T2>, v3 : aval<'T3>) =
// AVal.map3 (fun a b c -> a,b,c) v1 v2 v3

member inline x.BindReturn(value: asyncaval<'T1>, [<InlineIfLambda>] mapping: 'T1 -> CancellationToken -> Task<'T2>) =
AsyncAVal.map mapping value

member inline x.BindReturn(value: asyncaval<'T1>, [<InlineIfLambda>] mapping: 'T1 -> Async<'T2>) =
AsyncAVal.mapAsync mapping value

member inline x.BindReturn(value: asyncaval<'T1>, [<InlineIfLambda>] mapping: 'T1 -> Task<'T2>) =
AsyncAVal.map (fun data _ -> mapping data) value

member inline x.Bind(value: asyncaval<'T1>, [<InlineIfLambda>] mapping: 'T1 -> CancellationToken -> asyncaval<'T2>) =
AsyncAVal.bind (mapping) value
AsyncAVal.bind mapping value

member inline x.Bind(value: asyncaval<'T1>, [<InlineIfLambda>] mapping: 'T1 -> asyncaval<'T2>) =
AsyncAVal.bind (fun data _ -> mapping data) value
AsyncAVal.bind
(fun data ct ->
if ct.IsCancellationRequested then
AsyncAVal.ConstantVal(Task.FromCanceled<_> ct)
else
mapping data)
value


member inline x.Return(value: 'T) = AsyncAVal.constant value

Expand All @@ -775,12 +778,14 @@ module AsyncAValBuilderExtensions =

member inline x.Source(value: aval<'T>) = AsyncAVal.ofAVal value
member inline x.Source(value: Task<'T>) = AsyncAVal.ofTask value
member inline x.Source(value: Async<'T>) = AsyncAVal.ofAsync value
member inline x.Source(value: CancellableTask<'T>) = AsyncAVal.ofCancellableTask value

member inline x.BindReturn(value: asyncaval<'T1>, [<InlineIfLambda>] mapping: 'T1 -> CancellationToken -> 'T2) =
AsyncAVal.mapSync (fun data ctok -> mapping data ctok) value

member inline x.BindReturn(value: asyncaval<'T1>, [<InlineIfLambda>] mapping: 'T1 -> 'T2) =
AsyncAVal.mapSync (fun data ctok -> mapping data) value
AsyncAVal.mapSync (fun data _ -> mapping data) value

module AMapAsync =

Expand Down
13 changes: 3 additions & 10 deletions src/FsAutoComplete.Core/AdaptiveExtensions.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -323,8 +323,6 @@ type AsyncAValBuilder =
member inline Bind:
value: asyncaval<'T1> * mapping: ('T1 -> System.Threading.CancellationToken -> asyncaval<'T2>) -> asyncaval<'T2>

member inline BindReturn: value: asyncaval<'T1> * mapping: ('T1 -> System.Threading.Tasks.Task<'T2>) -> asyncaval<'T2>

member inline BindReturn: value: asyncaval<'T1> * mapping: ('T1 -> Async<'T2>) -> asyncaval<'T2>

member inline BindReturn:
Expand All @@ -341,24 +339,19 @@ type AsyncAValBuilder =

[<AutoOpen>]
module AsyncAValBuilderExtensions =

open IcedTasks
val asyncAVal: AsyncAValBuilder

type AsyncAValBuilder with

member inline Source: value: FSharp.Data.Adaptive.aval<'T> -> asyncaval<'T>

type AsyncAValBuilder with

member inline Source: value: System.Threading.Tasks.Task<'T> -> asyncaval<'T>

type AsyncAValBuilder with
member inline Source: value: Async<'T> -> asyncaval<'T>
member inline Source: value: CancellableTask<'T> -> asyncaval<'T>

member inline BindReturn:
value: asyncaval<'T1> * mapping: ('T1 -> System.Threading.CancellationToken -> 'T2) -> asyncaval<'T2>

type AsyncAValBuilder with

member inline BindReturn: value: asyncaval<'T1> * mapping: ('T1 -> 'T2) -> asyncaval<'T2>

module AMapAsync =
Expand Down
29 changes: 16 additions & 13 deletions src/FsAutoComplete.Core/Commands.fs
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,17 @@ module AsyncResult =
let recoverCancellation (ar: Async<Result<CoreResponse<'t>, exn>>) =
recoverCancellationGeneric ar (sprintf "Request cancelled (exn was %A)" >> CoreResponse.InfoRes)

let recoverCancellationIgnore (ar: Async<Result<unit, exn>>) = AsyncResult.foldResult id ignore ar
let recoverCancellationIgnore (ar: Async<Result<unit, exn>>) = ar |> AsyncResult.foldResult id (ignore<exn>)

[<RequireQualifiedAccess>]
type NotificationEvent =
| ParseError of errors: FSharpDiagnostic[] * file: string<LocalPath>
| ParseError of errors: FSharpDiagnostic[] * file: string<LocalPath> * version: int
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really wonder if we need some kind of 'file@version' abstraction here. Not now, but just immediately comes to mind.

| Workspace of ProjectSystem.ProjectResponse
| AnalyzerMessage of messages: FSharp.Analyzers.SDK.Message[] * file: string<LocalPath>
| UnusedOpens of file: string<LocalPath> * opens: Range[]
| AnalyzerMessage of messages: FSharp.Analyzers.SDK.Message[] * file: string<LocalPath> * version: int
| UnusedOpens of file: string<LocalPath> * opens: Range[] * version: int
// | Lint of file: string<LocalPath> * warningsWithCodes: Lint.EnrichedLintWarning list
| UnusedDeclarations of file: string<LocalPath> * decls: range[]
| SimplifyNames of file: string<LocalPath> * names: SimplifyNames.SimplifiableRange[]
| UnusedDeclarations of file: string<LocalPath> * decls: range[] * version: int
| SimplifyNames of file: string<LocalPath> * names: SimplifyNames.SimplifiableRange[] * version: int
| Canceled of errorMessage: string
| FileParsed of string<LocalPath>
| TestDetected of file: string<LocalPath> * tests: TestAdapter.TestAdapterEntry<range>[]
Expand Down Expand Up @@ -1251,7 +1251,7 @@ type Commands
//Diagnostics handler - Triggered by `CheckCore`
do
disposables.Add
<| fileChecked.Publish.Subscribe(fun (parseAndCheck, file, _) ->
<| fileChecked.Publish.Subscribe(fun (parseAndCheck, file, version) ->
async {
try
NotificationEvent.FileParsed file |> notify.Trigger
Expand All @@ -1265,7 +1265,7 @@ type Commands
|> Array.distinctBy (fun e ->
e.Severity, e.ErrorNumber, e.StartLine, e.StartColumn, e.EndLine, e.EndColumn, e.Message)

(errors, file) |> NotificationEvent.ParseError |> notify.Trigger
(errors, file, version) |> NotificationEvent.ParseError |> notify.Trigger
with _ ->
()
}
Expand All @@ -1274,7 +1274,7 @@ type Commands
//Analyzers handler - Triggered by `CheckCore`
do
disposables.Add
<| fileChecked.Publish.Subscribe(fun (parseAndCheck, file, _) ->
<| fileChecked.Publish.Subscribe(fun (parseAndCheck, file, version) ->
async {
if hasAnalyzers then
try
Expand All @@ -1298,7 +1298,7 @@ type Commands
parseAndCheck.GetAllEntities
)

(res, file) |> NotificationEvent.AnalyzerMessage |> notify.Trigger
(res, file, version) |> NotificationEvent.AnalyzerMessage |> notify.Trigger

Loggers.analyzers.info (
Log.setMessage "end analysis of {file}"
Expand Down Expand Up @@ -2435,6 +2435,7 @@ type Commands
let isScript = Utils.isAScript (UMX.untag file)

let! (opts, source) = state.TryGetFileCheckerOptionsWithSource file
let version = state.TryGetFileVersion file |> Option.defaultValue 0

let tyResOpt = checker.TryGetRecentCheckResultsForFile(file, opts, source)

Expand All @@ -2444,13 +2445,14 @@ type Commands
let! unused = UnusedDeclarations.getUnusedDeclarations (tyRes.GetCheckResults, isScript)
let unused = unused |> Seq.toArray

notify.Trigger(NotificationEvent.UnusedDeclarations(file, unused))
notify.Trigger(NotificationEvent.UnusedDeclarations(file, unused, version))
}
|> Async.Ignore<Result<unit, _>>

member x.CheckSimplifiedNames file : Async<unit> =
asyncResult {
let! (opts, source) = state.TryGetFileCheckerOptionsWithLines file
let version = state.TryGetFileVersion file |> Option.defaultValue 0

let tyResOpt = checker.TryGetRecentCheckResultsForFile(file, opts, source)

Expand All @@ -2461,7 +2463,7 @@ type Commands

let! simplified = SimplifyNames.getSimplifiableNames (tyRes.GetCheckResults, getSourceLine)
let simplified = Array.ofSeq simplified
notify.Trigger(NotificationEvent.SimplifyNames(file, simplified))
notify.Trigger(NotificationEvent.SimplifyNames(file, simplified, version))
}
|> Async.Ignore<Result<unit, _>>
|> x.AsCancellable file
Expand All @@ -2470,14 +2472,15 @@ type Commands
member x.CheckUnusedOpens file : Async<unit> =
asyncResult {
let! (opts, source) = state.TryGetFileCheckerOptionsWithLines file
let version = state.TryGetFileVersion file |> Option.defaultValue 0

match checker.TryGetRecentCheckResultsForFile(file, opts, source) with
| None -> return ()
| Some tyRes ->
let! unused =
UnusedOpens.getUnusedOpens (tyRes.GetCheckResults, (fun i -> (source: ISourceText).GetLineString(i - 1)))

notify.Trigger(NotificationEvent.UnusedOpens(file, (unused |> List.toArray)))
notify.Trigger(NotificationEvent.UnusedOpens(file, (unused |> List.toArray), version))

}
|> Async.Ignore<Result<unit, _>>
Expand Down
55 changes: 55 additions & 0 deletions src/FsAutoComplete.Core/FCSPatches.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ open FSharp.Compiler.Syntax
open FSharp.Compiler.Text
open FsAutoComplete.UntypedAstUtils
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.EditorServices

module internal SynExprAppLocationsImpl =
let rec private searchSynArgExpr traverseSynExpr expr ranges =
Expand Down Expand Up @@ -350,6 +351,60 @@ type FSharpParseFileResults with
| _ -> defaultTraverse expr }
)

member scope.ClassifyBinding(binding: SynBinding) =
match binding with
| SynBinding(valData = SynValData(memberFlags = None)) -> FSharpGlyph.Delegate
| _ -> FSharpGlyph.Method

member scope.TryRangeOfNameOfNearestOuterBindingOrMember pos =
let tryGetIdentRangeFromBinding binding =
let glyph = scope.ClassifyBinding binding

match binding with
| SynBinding(headPat = headPat) ->
match headPat with
| SynPat.LongIdent(longDotId = longIdentWithDots) ->
Some(binding.RangeOfBindingWithRhs, glyph, longIdentWithDots.LongIdent)
| SynPat.As(rhsPat = SynPat.Named(ident = SynIdent(ident, _); isThisVal = false))
| SynPat.Named(SynIdent(ident, _), false, _, _) -> Some(binding.RangeOfBindingWithRhs, glyph, [ ident ])
| _ -> None

let rec walkBinding expr workingRange =
match expr with

// This lets us dive into subexpressions that may contain the binding we're after
| SynExpr.Sequential(_, _, expr1, expr2, _) ->
if Range.rangeContainsPos expr1.Range pos then
walkBinding expr1 workingRange
else
walkBinding expr2 workingRange

| SynExpr.LetOrUse(bindings = bindings; body = bodyExpr) ->
let potentialNestedRange =
bindings
|> List.tryFind (fun binding -> Range.rangeContainsPos binding.RangeOfBindingWithRhs pos)
|> Option.bind tryGetIdentRangeFromBinding

match potentialNestedRange with
| Some range -> walkBinding bodyExpr range
| None -> walkBinding bodyExpr workingRange

| _ -> Some workingRange

let visitor =
{ new SyntaxVisitorBase<_>() with
override _.VisitExpr(_, _, defaultTraverse, expr) = defaultTraverse expr

override _.VisitBinding(_path, defaultTraverse, binding) =
match binding with
| SynBinding(expr = expr) as b when Range.rangeContainsPos b.RangeOfBindingWithRhs pos ->
match tryGetIdentRangeFromBinding b with
| Some range -> walkBinding expr range
| None -> None
| _ -> defaultTraverse binding }

SyntaxTraversal.Traverse(pos, scope.ParseTree, visitor)

module SyntaxTreeOps =
open FSharp.Compiler.Syntax

Expand Down
5 changes: 5 additions & 0 deletions src/FsAutoComplete.Core/FCSPatches.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ open FSharp.Compiler.Syntax
open FSharp.Compiler.Text
open FsAutoComplete.UntypedAstUtils
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.EditorServices

type LanguageFeatureShim =
new: langFeature: string -> LanguageFeatureShim
Expand All @@ -27,3 +28,7 @@ module LanguageVersionShim =

module SyntaxTreeOps =
val synExprContainsError: SynExpr -> bool

type FSharpParseFileResults with

member TryRangeOfNameOfNearestOuterBindingOrMember: pos: pos -> option<range * FSharpGlyph * LongIdent>
Loading