From a2963bc567f74500cf016a7c3e1b59870bbcbc62 Mon Sep 17 00:00:00 2001 From: dsyme Date: Fri, 25 Nov 2016 12:34:10 +0000 Subject: [PATCH 1/2] fix goto defn on operators and other items --- vsintegration/src/FSharp.Editor/GoToDefinitionService.fs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vsintegration/src/FSharp.Editor/GoToDefinitionService.fs b/vsintegration/src/FSharp.Editor/GoToDefinitionService.fs index ec2206e8dc4..cc755a7861f 100644 --- a/vsintegration/src/FSharp.Editor/GoToDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/GoToDefinitionService.fs @@ -65,6 +65,14 @@ type internal FSharpGoToDefinitionService [] ([ match classifiedSpan.ClassificationType with + | ClassificationTypeNames.ClassName + | ClassificationTypeNames.DelegateName + | ClassificationTypeNames.EnumName + | ClassificationTypeNames.InterfaceName + | ClassificationTypeNames.ModuleName + | ClassificationTypeNames.StructName + | ClassificationTypeNames.TypeParameterName + | ClassificationTypeNames.Operator | ClassificationTypeNames.Identifier -> match QuickParse.GetCompleteIdentifierIsland true (textLine.ToString()) textLineColumn with | Some(islandIdentifier, islandColumn, isQuoted) -> From ffcb5e62c844b97b09baabc032165b9ad135a44a Mon Sep 17 00:00:00 2001 From: dsyme Date: Fri, 25 Nov 2016 13:33:37 +0000 Subject: [PATCH 2/2] fix gtd --- .../FSharp.Editor/GoToDefinitionService.fs | 113 +++++++++++------- 1 file changed, 68 insertions(+), 45 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/GoToDefinitionService.fs b/vsintegration/src/FSharp.Editor/GoToDefinitionService.fs index cc755a7861f..79f019977e2 100644 --- a/vsintegration/src/FSharp.Editor/GoToDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/GoToDefinitionService.fs @@ -28,8 +28,7 @@ open Microsoft.VisualStudio.Text.Tagging open Microsoft.FSharp.Compiler.Range open Microsoft.FSharp.Compiler.SourceCodeServices -type internal FSharpNavigableItem(document: Document, textSpan: TextSpan, displayString: string) = - member this.DisplayString = displayString +type internal FSharpNavigableItem(document: Document, textSpan: TextSpan) = interface INavigableItem with member this.Glyph = Glyph.BasicFile @@ -52,44 +51,59 @@ type internal FSharpGoToDefinitionService [] ([> = async { + : Async> = + async { let textLine = sourceText.Lines.GetLineFromPosition(position) let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = textLinePos.Line + 1 // Roslyn line numbers are zero-based, FSharp.Compiler.Service line numbers are 1-based let textLineColumn = textLinePos.Character - let classifiedSpanOption = - FSharpColorizationService.GetColorizationData(documentKey, sourceText, textLine.Span, Some(filePath), defines, cancellationToken) - |> Seq.tryFind(fun classifiedSpan -> classifiedSpan.TextSpan.Contains(position)) - - match classifiedSpanOption with - | Some(classifiedSpan) -> - match classifiedSpan.ClassificationType with - | ClassificationTypeNames.ClassName - | ClassificationTypeNames.DelegateName - | ClassificationTypeNames.EnumName - | ClassificationTypeNames.InterfaceName - | ClassificationTypeNames.ModuleName - | ClassificationTypeNames.StructName - | ClassificationTypeNames.TypeParameterName - | ClassificationTypeNames.Operator - | ClassificationTypeNames.Identifier -> - match QuickParse.GetCompleteIdentifierIsland true (textLine.ToString()) textLineColumn with - | Some(islandIdentifier, islandColumn, isQuoted) -> - let qualifiers = if isQuoted then [islandIdentifier] else islandIdentifier.Split '.' |> Array.toList - let! parseResults = FSharpLanguageService.Checker.ParseFileInProject(filePath, sourceText.ToString(), options) - let! checkFileAnswer = FSharpLanguageService.Checker.CheckFileInProject(parseResults, filePath, textVersionHash, sourceText.ToString(), options) - let checkFileResults = - match checkFileAnswer with - | FSharpCheckFileAnswer.Aborted -> failwith "Compilation isn't complete yet" - | FSharpCheckFileAnswer.Succeeded(results) -> results - - let! declarations = checkFileResults.GetDeclarationLocationAlternate (fcsTextLineNumber, islandColumn, textLine.ToString(), qualifiers, false) - - match declarations with - | FSharpFindDeclResult.DeclFound(range) -> return Some(range) - | _ -> return None - | None -> return None + let tryClassifyAtPosition (position: int) = + let classifiedSpanOption = + FSharpColorizationService.GetColorizationData(documentKey, sourceText, textLine.Span, Some(filePath), defines, cancellationToken) + |> Seq.tryFind(fun classifiedSpan -> classifiedSpan.TextSpan.Contains(position)) + + match classifiedSpanOption with + | Some(classifiedSpan) -> + match classifiedSpan.ClassificationType with + | ClassificationTypeNames.ClassName + | ClassificationTypeNames.DelegateName + | ClassificationTypeNames.EnumName + | ClassificationTypeNames.InterfaceName + | ClassificationTypeNames.ModuleName + | ClassificationTypeNames.StructName + | ClassificationTypeNames.TypeParameterName + | ClassificationTypeNames.Identifier -> + match QuickParse.GetCompleteIdentifierIsland true (textLine.ToString()) textLineColumn with + | Some (islandIdentifier, islandColumn, isQuoted) -> + let qualifiers = if isQuoted then [islandIdentifier] else islandIdentifier.Split '.' |> Array.toList + Some (islandColumn, qualifiers) + | None -> None + | ClassificationTypeNames.Operator -> + let islandColumn = sourceText.Lines.GetLinePositionSpan(classifiedSpan.TextSpan).End.Character + Some (islandColumn, [""]) + | _ -> None + | _ -> None + + // Tolerate being on the right of the identifier + let quickParseInfo = + match tryClassifyAtPosition position with + | None when textLineColumn > 0 -> tryClassifyAtPosition (position - 1) + | res -> res + + match quickParseInfo with + | Some (islandColumn, qualifiers) -> + let! parseResults = FSharpLanguageService.Checker.ParseFileInProject(filePath, sourceText.ToString(), options) + let! checkFileAnswer = FSharpLanguageService.Checker.CheckFileInProject(parseResults, filePath, textVersionHash, sourceText.ToString(), options) + let checkFileResults = + match checkFileAnswer with + | FSharpCheckFileAnswer.Aborted -> failwith "Compilation isn't complete yet" + | FSharpCheckFileAnswer.Succeeded(results) -> results + + let! declarations = checkFileResults.GetDeclarationLocationAlternate (fcsTextLineNumber, islandColumn, textLine.ToString(), qualifiers, false) + + match declarations with + | FSharpFindDeclResult.DeclFound(range) -> return Some(range) | _ -> return None | None -> return None } @@ -118,8 +132,8 @@ type internal FSharpGoToDefinitionService [] ([ Async.AwaitTask let refTextSpan = CommonRoslynHelpers.FSharpRangeToTextSpan(refSourceText, range) - let refDisplayString = refSourceText.GetSubText(refTextSpan).ToString() - results.Add(FSharpNavigableItem(refDocument, refTextSpan, refDisplayString)) + results.Add(FSharpNavigableItem(refDocument, refTextSpan)) + | None -> () | None -> () return results.AsEnumerable() @@ -135,13 +149,22 @@ type internal FSharpGoToDefinitionService [] ([ FSharpNavigableItem // F# API provides only one INavigableItem - for presenter in presenters do - presenter.DisplayResult(navigableItem.DisplayString, definitionTask.Result) - true - else - false + if definitionTask.Status = TaskStatus.RanToCompletion && definitionTask.Result.Any() then + let navigableItem = definitionTask.Result.First() // F# API provides only one INavigableItem + let workspace = document.Project.Solution.Workspace + let navigationService = workspace.Services.GetService() + ignore presenters + navigationService.TryNavigateToSpan(workspace, navigableItem.Document.Id, navigableItem.SourceSpan) + + // FSROSLYNTODO: potentially display multiple results here + // If GotoDef returns one result then it should try to jump to a discovered location. If it returns multiple results then it should use + // presenters to render items so user can choose whatever he needs. Given that per comment F# API always returns only one item then we + // should always navigate to definition and get rid of presenters. + // + //let refDisplayString = refSourceText.GetSubText(refTextSpan).ToString() + //for presenter in presenters do + // presenter.DisplayResult(navigableItem.DisplayString, definitionTask.Result) + //true + else false