Skip to content

Commit 587bc1e

Browse files
Fix #16761 (#17047)
* Fix #16761 * Fully async version + ignore cancellation on external navigation * Automated command ran: fantomas Co-authored-by: vzarytovskii <1260985+vzarytovskii@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 273cea5 commit 587bc1e

File tree

2 files changed

+83
-7
lines changed

2 files changed

+83
-7
lines changed

vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ open System.Text.RegularExpressions
2929
open CancellableTasks
3030
open Microsoft.VisualStudio.FSharp.Editor.Telemetry
3131
open Microsoft.VisualStudio.Telemetry
32-
open System.Collections.Generic
32+
open Microsoft.VisualStudio.Threading
3333

3434
module private Symbol =
3535
let fullName (root: ISymbol) : string =
@@ -565,6 +565,77 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) =
565565
return this.NavigateToItem(item, cancellationToken)
566566
}
567567

568+
member this.NavigateToExternalDeclarationAsync(targetSymbolUse: FSharpSymbolUse, metadataReferences: seq<MetadataReference>) =
569+
let textOpt =
570+
match targetSymbolUse.Symbol with
571+
| :? FSharpEntity as symbol -> symbol.TryGetMetadataText() |> Option.map (fun text -> text, symbol.DisplayName)
572+
| :? FSharpMemberOrFunctionOrValue as symbol ->
573+
symbol.ApparentEnclosingEntity.TryGetMetadataText()
574+
|> Option.map (fun text -> text, symbol.ApparentEnclosingEntity.DisplayName)
575+
| :? FSharpField as symbol ->
576+
match symbol.DeclaringEntity with
577+
| Some entity ->
578+
let text = entity.TryGetMetadataText()
579+
580+
match text with
581+
| Some text -> Some(text, entity.DisplayName)
582+
| None -> None
583+
| None -> None
584+
| :? FSharpUnionCase as symbol ->
585+
symbol.DeclaringEntity.TryGetMetadataText()
586+
|> Option.map (fun text -> text, symbol.DisplayName)
587+
| _ -> None
588+
589+
match textOpt with
590+
| None -> CancellableTask.singleton false
591+
| Some(text, fileName) ->
592+
foregroundCancellableTask {
593+
let! cancellationToken = CancellableTask.getCancellationToken ()
594+
do! ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken)
595+
596+
let tmpProjInfo, tmpDocInfo =
597+
MetadataAsSource.generateTemporaryDocument (
598+
AssemblyIdentity(targetSymbolUse.Symbol.Assembly.QualifiedName),
599+
fileName,
600+
metadataReferences
601+
)
602+
603+
let tmpShownDocOpt =
604+
metadataAsSource.ShowDocument(tmpProjInfo, tmpDocInfo.FilePath, SourceText.From(text.ToString()))
605+
606+
match tmpShownDocOpt with
607+
| ValueNone -> return false
608+
| ValueSome tmpShownDoc ->
609+
let! _, checkResults = tmpShownDoc.GetFSharpParseAndCheckResultsAsync("NavigateToExternalDeclaration")
610+
611+
let r =
612+
// This tries to find the best possible location of the target symbol's location in the metadata source.
613+
// We really should rely on symbol equality within FCS instead of doing it here,
614+
// but the generated metadata as source isn't perfect for symbol equality.
615+
let symbols = checkResults.GetAllUsesOfAllSymbolsInFile(cancellationToken)
616+
617+
symbols
618+
|> Seq.tryFindV (tryFindExternalSymbolUse targetSymbolUse)
619+
|> ValueOption.map (fun x -> x.Range)
620+
621+
let! span =
622+
cancellableTask {
623+
let! cancellationToken = CancellableTask.getCancellationToken ()
624+
625+
match r with
626+
| ValueNone -> return TextSpan.empty
627+
| ValueSome r ->
628+
let! text = tmpShownDoc.GetTextAsync(cancellationToken)
629+
630+
match RoslynHelpers.TryFSharpRangeToTextSpan(text, r) with
631+
| ValueSome span -> return span
632+
| _ -> return TextSpan.empty
633+
}
634+
635+
let navItem = FSharpGoToDefinitionNavigableItem(tmpShownDoc, span)
636+
return this.NavigateToItem(navItem, cancellationToken)
637+
}
638+
568639
member this.NavigateToExternalDeclaration
569640
(
570641
targetSymbolUse: FSharpSymbolUse,
@@ -706,10 +777,15 @@ type internal FSharpNavigation(metadataAsSource: FSharpMetadataAsSourceService,
706777
let gtd = GoToDefinition(metadataAsSource)
707778
let! result = gtd.FindDefinitionAtPosition(initialDoc, position)
708779

709-
return
710-
match result with
711-
| ValueSome(FSharpGoToDefinitionResult.NavigableItem(navItem), _) -> ImmutableArray.create navItem
712-
| _ -> ImmutableArray.empty
780+
match result with
781+
| ValueSome(FSharpGoToDefinitionResult.NavigableItem(navItem), _) -> return ImmutableArray.create navItem
782+
| ValueSome(FSharpGoToDefinitionResult.ExternalAssembly(targetSymbolUse, metadataReferences), _) ->
783+
do!
784+
gtd.NavigateToExternalDeclarationAsync(targetSymbolUse, metadataReferences)
785+
|> CancellableTask.ignore
786+
787+
return ImmutableArray.empty
788+
| _ -> return ImmutableArray.empty
713789
}
714790

715791
member _.TryGoToDefinition(position, cancellationToken) =

vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ type internal FSharpGoToDefinitionService [<ImportingConstructor>] (metadataAsSo
1818

1919
interface IFSharpGoToDefinitionService with
2020
/// Invoked with Peek Definition.
21-
member _.FindDefinitionsAsync(document: Document, position: int, cancellationToken: CancellationToken) =
21+
member _.FindDefinitionsAsync(document: Document, position: int, _cancellationToken: CancellationToken) =
2222
cancellableTask {
2323
let navigation = FSharpNavigation(metadataAsSource, document, rangeStartup)
2424
let! res = navigation.FindDefinitionsAsync(position)
2525
return (res :> IEnumerable<_>)
2626
}
27-
|> CancellableTask.start cancellationToken
27+
|> CancellableTask.startWithoutCancellation
2828

2929
/// Invoked with Go to Definition.
3030
/// Try to navigate to the definiton of the symbol at the symbolRange in the originDocument

0 commit comments

Comments
 (0)