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

Quickinfo Tooltip and GotoDefinition Navigation Improvements #2683

Merged
merged 29 commits into from
Apr 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e293b8c
full type name in tooltip, provisional tab preferred
majocha Mar 21, 2017
da8c8d1
more entities made navigable
majocha Mar 22, 2017
4f64530
use IGoToDefinition service
majocha Mar 22, 2017
fd885b3
this is used only here
majocha Mar 22, 2017
ed3f702
MEF import FSharpGotoDefinitionService into QuickInfoProvider
cloudRoutine Mar 22, 2017
cf9dc1a
speed up gotoDefinition
cloudRoutine Mar 22, 2017
41e70c3
additional GotoDefn navigation strategies
cloudRoutine Mar 22, 2017
b47ff45
quickinfo navigation stays in its lane
cloudRoutine Mar 22, 2017
c2820d7
fix unittests
cloudRoutine Mar 22, 2017
684a3db
restore recursive matchingDoc
cloudRoutine Mar 22, 2017
6f55b66
asynchronous navigation from tooltips
cloudRoutine Mar 22, 2017
d649de8
fix cross project .fs -> .fs and .fsi -> .fsi Navigation
cloudRoutine Mar 23, 2017
d3f695e
cleanup and extra documentation
cloudRoutine Mar 24, 2017
449fe02
missed this one
majocha Mar 24, 2017
032dbb2
gotodefinition sig <-> impl at declaration location
cloudRoutine Mar 24, 2017
eddee9e
Merge pull request #1 from majocha/cR-navigable
cloudRoutine Mar 24, 2017
6fa19e3
fix async workflow
cloudRoutine Mar 24, 2017
00df745
animate status bar search and timeout on msgs
cloudRoutine Mar 26, 2017
b21a41a
Better links styling
majocha Mar 27, 2017
b661fb5
Merge pull request #2 from majocha/navigable-improvements
cloudRoutine Mar 27, 2017
5bbb077
Merge remote-tracking branch 'Microsoft/master' into navigable-improv…
cloudRoutine Mar 28, 2017
e0f8161
integrate sig doccoms
cloudRoutine Mar 28, 2017
0709e17
fix error introduced by prior merge
cloudRoutine Mar 29, 2017
9b49be6
fixed invalid type access in `getUnusedOpens`
cloudRoutine Mar 29, 2017
d5b8980
fix invalid span bug in `symbolIsFullyQualified`
cloudRoutine Mar 29, 2017
926c754
check if normalized doccom text matches
cloudRoutine Mar 29, 2017
597dc4a
cleanup status bar usage
cloudRoutine Mar 29, 2017
0ee04af
fix underline pen position, code cleanup and formatting
vasily-kirichenko Mar 31, 2017
c19adce
do not show links for symbol itself
vasily-kirichenko Apr 1, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions src/fsharp/NicePrint.fs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ module internal PrintUtilities =
if isAttribute then
defaultArg (String.tryDropSuffix name "Attribute") name
else name
let tyconTextL = NavigableTaggedText.Create(tagEntityRefName tcref demangled, tcref.DefinitionRange) |> wordL
let tyconTextL =
tagEntityRefName tcref demangled
|> mkNav tcref.DefinitionRange
|> wordL
if denv.shortTypeNames then
tyconTextL
else
Expand Down Expand Up @@ -1104,8 +1107,9 @@ module private PrintTastMemberOrVals =
let stat = PrintTypes.layoutMemberFlags membInfo.MemberFlags
let _tps,argInfos,rty,_ = GetTypeOfMemberInFSharpForm denv.g v

let mkNameL niceMethodTypars tagFunction name =
let nameL = DemangleOperatorNameAsLayout tagFunction name
let mkNameL niceMethodTypars tagFunction name =
let nameL =
DemangleOperatorNameAsLayout (tagFunction >> mkNav v.DefinitionRange) name
let nameL =
if denv.showMemberContainers then
layoutTyconRef denv v.MemberApparentParent ^^ SepL.dot ^^ nameL
Expand Down Expand Up @@ -1157,7 +1161,10 @@ module private PrintTastMemberOrVals =
let env = SimplifyTypes.CollectInfo true [tau] cxs
let cxs = env.postfixConstraints
let argInfos,rty = GetTopTauTypeInFSharpForm denv.g (arityOfVal v).ArgInfos tau v.Range
let nameL = wordL ((if v.IsModuleBinding then tagModuleBinding else tagUnknownEntity) v.DisplayName)
let nameL =
(if v.IsModuleBinding then tagModuleBinding else tagUnknownEntity) v.DisplayName
|> mkNav v.DefinitionRange
|> wordL
let nameL = layoutAccessibility denv v.Accessibility nameL
let nameL =
if v.IsMutable && not denv.suppressMutableKeyword then
Expand Down Expand Up @@ -1389,7 +1396,7 @@ module private TastDefinitionPrinting =

let layoutExtensionMember denv (v:Val) =
let tycon = v.MemberApparentParent.Deref
let nameL = wordL (tagMethod tycon.DisplayName)
let nameL = tagMethod tycon.DisplayName |> mkNav v.DefinitionRange |> wordL
let nameL = layoutAccessibility denv tycon.Accessibility nameL // "type-accessibility"
let tps =
match PartitionValTyparsForApparentEnclosingType denv.g v with
Expand All @@ -1402,7 +1409,10 @@ module private TastDefinitionPrinting =
aboveListL (List.map (layoutExtensionMember denv) vs)

let layoutRecdField addAccess denv (fld:RecdField) =
let lhs = wordL (tagRecordField fld.Name)
let lhs =
tagRecordField fld.Name
|> mkNav fld.DefinitionRange
|> wordL
let lhs = (if addAccess then layoutAccessibility denv fld.Accessibility lhs else lhs)
let lhs = if fld.IsMutable then wordL (tagKeyword "mutable") --- lhs else lhs
(lhs ^^ RightL.colon) --- layoutType denv fld.FormalType
Expand All @@ -1426,7 +1436,7 @@ module private TastDefinitionPrinting =
sepListL (wordL (tagPunctuation "*")) (List.mapi (layoutUnionOrExceptionField denv isGenerated) fields)

let layoutUnionCase denv prefixL (ucase:UnionCase) =
let nmL = DemangleOperatorNameAsLayout tagUnionCase ucase.Id.idText
let nmL = DemangleOperatorNameAsLayout (tagUnionCase >> mkNav ucase.DefinitionRange) ucase.Id.idText
//let nmL = layoutAccessibility denv ucase.Accessibility nmL
match ucase.RecdFields with
| [] -> (prefixL ^^ nmL)
Expand Down Expand Up @@ -1611,6 +1621,7 @@ module private TastDefinitionPrinting =
elif isInterfaceTy g ty then Some "interface", tagInterface n
elif isClassTy g ty then (if simplified then None else Some "class" ), tagClass n
else None, tagUnknownType n
let name = mkNav tycon.DefinitionRange name
let nameL = layoutAccessibility denv tycon.Accessibility (wordL name)
let denv = denv.AddAccessibility tycon.Accessibility
let lhsL =
Expand Down
12 changes: 8 additions & 4 deletions src/fsharp/TastOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2506,11 +2506,16 @@ let tagEntityRefName (xref: EntityRef) name =
elif xref.IsRecordTycon then tagRecord name
else tagClass name

let fullDisplayTextOfTyconRef r = fullNameOfEntityRef (fun (tc:TyconRef) -> tc.DisplayNameWithStaticParametersAndUnderscoreTypars) r

let fullNameOfEntityRefAsLayout nmF (xref: EntityRef) =
let n = NavigableTaggedText.Create(tagEntityRefName xref (nmF xref), xref.DefinitionRange) |> wordL
let navigableText =
tagEntityRefName xref (nmF xref)
|> mkNav xref.DefinitionRange
|> wordL
match fullNameOfParentOfEntityRefAsLayout xref with
| None -> n
| Some pathText -> pathText ^^ SepL.dot ^^ n
| None -> navigableText
| Some pathText -> pathText ^^ SepL.dot ^^ navigableText

let fullNameOfParentOfValRef vref =
match vref with
Expand All @@ -2534,7 +2539,6 @@ let fullNameOfParentOfValRefAsLayout vref =
let fullDisplayTextOfParentOfModRef r = fullNameOfParentOfEntityRef r

let fullDisplayTextOfModRef r = fullNameOfEntityRef (fun (x:EntityRef) -> x.DemangledModuleOrNamespaceName) r
let fullDisplayTextOfTyconRef r = fullNameOfEntityRef (fun (tc:TyconRef) -> tc.DisplayNameWithStaticParametersAndUnderscoreTypars) r
Copy link
Contributor

Choose a reason for hiding this comment

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

Unneeded change. (This function was moved up a few lines to capture full type name but that is not implemented now)

let fullDisplayTextOfTyconRefAsLayout r = fullNameOfEntityRefAsLayout (fun (tc:TyconRef) -> tc.DisplayNameWithStaticParametersAndUnderscoreTypars) r
let fullDisplayTextOfExnRef r = fullNameOfEntityRef (fun (tc:TyconRef) -> tc.DisplayNameWithStaticParametersAndUnderscoreTypars) r
let fullDisplayTextOfExnRefAsLayout r = fullNameOfEntityRefAsLayout (fun (tc:TyconRef) -> tc.DisplayNameWithStaticParametersAndUnderscoreTypars) r
Expand Down
9 changes: 4 additions & 5 deletions src/fsharp/layout.fs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@ type layout = Internal.Utilities.StructuredFormat.Layout
type LayoutTag = Internal.Utilities.StructuredFormat.LayoutTag
type TaggedText = Internal.Utilities.StructuredFormat.TaggedText

type NavigableTaggedText(tag, text, range: Range.range) =
type NavigableTaggedText(taggedText: TaggedText, range: Range.range) =
member val Range = range
interface TaggedText with
member x.Tag = tag
member x.Text = text
static member Create(tt: TaggedText, range) =
NavigableTaggedText(tt.Tag, tt.Text, range)
member x.Tag = taggedText.Tag
member x.Text = taggedText.Text
let mkNav r t = NavigableTaggedText(t, r)

let spaces n = new String(' ',n)

Expand Down
4 changes: 2 additions & 2 deletions src/fsharp/layout.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ type LayoutTag = Internal.Utilities.StructuredFormat.LayoutTag
type TaggedText = Internal.Utilities.StructuredFormat.TaggedText

type NavigableTaggedText =
new : LayoutTag * string * Range.range -> NavigableTaggedText
new : TaggedText * Range.range -> NavigableTaggedText
member Range: Range.range
static member Create: TaggedText * Range.range -> NavigableTaggedText
interface TaggedText
val mkNav : Range.range -> TaggedText -> NavigableTaggedText

module TaggedTextOps = Internal.Utilities.StructuredFormat.TaggedTextOps

Expand Down
12 changes: 7 additions & 5 deletions src/fsharp/vs/ServiceDeclarations.fs
Original file line number Diff line number Diff line change
Expand Up @@ -834,7 +834,7 @@ module internal ItemDescriptionsImpl =
wordL (tagText (FSComp.SR.typeInfoUnionCase())) ^^
NicePrint.layoutTyconRef denv ucinfo.TyconRef ^^
sepL (tagPunctuation ".") ^^
wordL (tagUnionCase (DecompileOpName uc.Id.idText)) ^^
wordL (tagUnionCase (DecompileOpName uc.Id.idText) |> mkNav uc.DefinitionRange) ^^
RightL.colon ^^
(if List.isEmpty recd then emptyL else NicePrint.layoutUnionCases denv recd ^^ WordL.arrow) ^^
NicePrint.layoutTy denv rty
Expand All @@ -845,7 +845,7 @@ module internal ItemDescriptionsImpl =
let items = apinfo.ActiveTags
let layout =
wordL (tagText ((FSComp.SR.typeInfoActivePatternResult()))) ^^
wordL (tagActivePatternResult (List.item idx items)) ^^
wordL (tagActivePatternResult (List.item idx items) |> mkNav apinfo.Range) ^^
RightL.colon ^^
NicePrint.layoutTy denv ty
FSharpStructuredToolTipElement.Single(layout, xml)
Expand All @@ -859,7 +859,7 @@ module internal ItemDescriptionsImpl =
let _, ptau, _cxs = PrettyTypes.PrettifyTypes1 denv.g tau
let layout =
wordL (tagText (FSComp.SR.typeInfoActiveRecognizer())) ^^
wordL (tagActivePatternCase apref.Name) ^^
wordL (tagActivePatternCase apref.Name |> mkNav v.DefinitionRange) ^^
RightL.colon ^^
NicePrint.layoutTy denv ptau ^^
OutputFullName isDecl pubpath_of_vref fullDisplayTextOfValRefAsLayout v
Expand All @@ -879,7 +879,7 @@ module internal ItemDescriptionsImpl =
let layout =
NicePrint.layoutTyconRef denv rfinfo.TyconRef ^^
SepL.dot ^^
wordL (tagRecordField (DecompileOpName rfield.Name)) ^^
wordL (tagRecordField (DecompileOpName rfield.Name) |> mkNav rfield.DefinitionRange) ^^
RightL.colon ^^
NicePrint.layoutTy denv ty ^^
(
Expand Down Expand Up @@ -1001,7 +1001,9 @@ module internal ItemDescriptionsImpl =

let layout =
wordL (tagKeyword kind) ^^
wordL (if definiteNamespace then tagNamespace (fullDisplayTextOfModRef modref) else (tagModule modref.DemangledModuleOrNamespaceName))
(if definiteNamespace then tagNamespace (fullDisplayTextOfModRef modref) else (tagModule modref.DemangledModuleOrNamespaceName)
|> mkNav modref.DefinitionRange
|> wordL)
if not definiteNamespace then
let namesToAdd =
([],modrefs)
Expand Down
108 changes: 108 additions & 0 deletions vsintegration/src/FSharp.Editor/Common/CodeAnalysisExtensions.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
[<AutoOpen>]
module internal Microsoft.VisualStudio.FSharp.Editor.CodeAnalysisExtensions

open Microsoft.CodeAnalysis
open Microsoft.FSharp.Compiler.Range

type Project with

/// Returns the projectIds of all projects within the same solution that directly reference this project
member this.GetDependentProjectIds () =
this.Solution.GetProjectDependencyGraph().GetProjectsThatDirectlyDependOnThisProject this.Id


/// Returns all projects within the same solution that directly reference this project.
member this.GetDependentProjects () =
this.Solution.GetProjectDependencyGraph().GetProjectsThatDirectlyDependOnThisProject this.Id
|> Seq.map this.Solution.GetProject


/// Returns the ProjectIds of all of the projects that this project directly or transitively depneds on
member this.GetProjectIdsOfAllProjectsThisProjectDependsOn () =
let graph = this.Solution.GetProjectDependencyGraph()
let transitiveDependencies = graph.GetProjectsThatThisProjectTransitivelyDependsOn this.Id
let directDependencies = graph.GetProjectsThatThisProjectDirectlyDependsOn this.Id
Seq.append directDependencies transitiveDependencies


/// The list all of the projects that this project directly or transitively depneds on
member this.GetAllProjectsThisProjectDependsOn () =
this.GetProjectIdsOfAllProjectsThisProjectDependsOn ()
|> Seq.map this.Solution.GetProject


type Solution with

/// Try to get a document inside the solution using the document's name
member self.TryGetDocumentNamed docName =
self.Projects |> Seq.tryPick (fun proj ->
proj.Documents |> Seq.tryFind (fun doc -> doc.Name = docName))


/// Try to find the documentId corresponding to the provided filepath within this solution
member self.TryGetDocumentFromPath filePath =
self.GetDocumentIdsWithFilePath filePath
Copy link
Contributor

Choose a reason for hiding this comment

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

This will sometimes fail because FSharp often returns file paths like e.g. C:\visualfsharp\src\fsharp\FSharp.LanguageService.Compiler\..\vs\service.fsi
I did normalization with GetFullPathSafe to work around.

|> Seq.tryHead |> Option.map (fun docId -> self.GetDocument docId)


/// Try to get a project inside the solution using the project's id
member self.TryGetProject (projId:ProjectId) =
if self.ContainsProject projId then Some (self.GetProject projId) else None


/// Returns the projectIds of all projects within this solution that directly reference the provided project
member self.GetDependentProjects (projectId:ProjectId) =
self.GetProjectDependencyGraph().GetProjectsThatDirectlyDependOnThisProject projectId
|> Seq.map self.GetProject


/// Returns the projectIds of all projects within this solution that directly reference the provided project
member self.GetDependentProjectIds (projectId:ProjectId) =
self.GetProjectDependencyGraph().GetProjectsThatDirectlyDependOnThisProject projectId


/// Returns the ProjectIds of all of the projects that directly or transitively depends on
member self.GetProjectIdsOfAllProjectReferences (projectId:ProjectId) =
let graph = self.GetProjectDependencyGraph()
let transitiveDependencies = graph.GetProjectsThatThisProjectTransitivelyDependsOn projectId
let directDependencies = graph.GetProjectsThatThisProjectDirectlyDependsOn projectId
Seq.append directDependencies transitiveDependencies


/// Returns all of the projects that this project that directly or transitively depends on
member self.GetAllProjectsThisProjectDependsOn (projectId:ProjectId) =
self.GetProjectIdsOfAllProjectReferences projectId
|> Seq.map self.GetProject


/// Try to retrieve the corresponding DocumentId for the range's file in the solution
/// and if a projectId is provided, only try to find the document within that project
/// or a project referenced by that project
member self.TryGetDocumentIdFromFSharpRange (range:range,?projectId:ProjectId) =

let filePath = System.IO.Path.GetFullPathSafe range.FileName
let checkProjectId (docId:DocumentId) =
if projectId.IsSome then docId.ProjectId = projectId.Value else false
//The same file may be present in many projects. We choose one from current or referenced project.
let rec matchingDoc = function
| [] -> None
| (docId:DocumentId)::_ when checkProjectId docId -> Some docId
| docId::tail ->
match projectId with
| Some projectId ->
if self.GetDependentProjectIds docId.ProjectId |> Seq.contains projectId
then Some docId
else matchingDoc tail
| None -> Some docId

self.GetDocumentIdsWithFilePath filePath |> List.ofSeq |> matchingDoc


/// Try to retrieve the corresponding Document for the range's file in the solution
/// and if a projectId is provided, only try to find the document within that project
/// or a project referenced by that project
member self.TryGetDocumentFromFSharpRange (range:range,?projectId:ProjectId) =
match projectId with
| Some projectId -> self.TryGetDocumentIdFromFSharpRange (range, projectId)
| None -> self.TryGetDocumentIdFromFSharpRange range
|> Option.map self.GetDocument
27 changes: 26 additions & 1 deletion vsintegration/src/FSharp.Editor/Common/CommonConstants.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,46 @@ open Microsoft.CodeAnalysis.Classification

[<RequireQualifiedAccess>]
module internal FSharpCommonConstants =

[<Literal>]
/// "871D2A70-12A2-4e42-9440-425DD92A4116"
let packageGuidString = "871D2A70-12A2-4e42-9440-425DD92A4116"

[<Literal>]
/// "BC6DD5A5-D4D6-4dab-A00D-A51242DBAF1B"
let languageServiceGuidString = "BC6DD5A5-D4D6-4dab-A00D-A51242DBAF1B"

[<Literal>]
/// "4EB7CCB7-4336-4FFD-B12B-396E9FD079A9"
let editorFactoryGuidString = "4EB7CCB7-4336-4FFD-B12B-396E9FD079A9"

[<Literal>]
/// "9B164E40-C3A2-4363-9BC5-EB4039DEF653"
let svsSettingsPersistenceManagerGuidString = "9B164E40-C3A2-4363-9BC5-EB4039DEF653"

[<Literal>]
/// "F#"
let FSharpLanguageName = "F#"

[<Literal>]
/// "F#"
let FSharpContentTypeName = "F#"

[<Literal>]
/// "F# Signature Help"
let FSharpSignatureHelpContentTypeName = "F# Signature Help"

[<Literal>]
/// "F# Language Service"
let FSharpLanguageServiceCallbackName = "F# Language Service"

[<Literal>]
/// "FSharp"
let FSharpLanguageLongName = "FSharp"

[<RequireQualifiedAccess>]
module internal FSharpProviderConstants =

[<Literal>]
let FSharpLanguageLongName = "FSharp"
/// "Session Capturing Quick Info Source Provider"
let SessionCapturingProvider = "Session Capturing Quick Info Source Provider"
8 changes: 6 additions & 2 deletions vsintegration/src/FSharp.Editor/Common/CommonHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -410,8 +410,12 @@ module internal Extensions =
open Microsoft.VisualStudio.FSharp.Editor.Logging

type System.IServiceProvider with

/// Retrieve a MEF Visual Studio Service of type 'T
member x.GetService<'T>() = x.GetService(typeof<'T>) :?> 'T
member x.GetService<'T, 'S>() = x.GetService(typeof<'S>) :?> 'T

/// Retrieve a SVs MEF Service of type 'S and cast it to type 'T
member x.GetService<'S,'T>() = x.GetService(typeof<'S>) :?> 'T

type Path with
static member GetFullPathSafe path =
Expand Down Expand Up @@ -526,7 +530,7 @@ module internal Extensions =
match declarationLocation with
| Some loc ->
let filePath = Path.GetFullPathSafe loc.FileName
let isScript = String.Equals(Path.GetExtension(filePath), ".fsx", StringComparison.OrdinalIgnoreCase)
let isScript = isScriptFile filePath
if isScript && filePath = currentDocument.FilePath then
Some SymbolDeclarationLocation.CurrentDocument
elif isScript then
Expand Down
14 changes: 0 additions & 14 deletions vsintegration/src/FSharp.Editor/Common/CommonRoslynHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,6 @@ module internal CommonRoslynHelpers =

let CollectTaggedText (list: List<_>) (t:TaggedText) = list.Add(TaggedText(roslynTag t.Tag, t.Text))

let CollectNavigableText (list: List<_>) (t: TaggedText) =
let rangeOpt =
match t with
| :? NavigableTaggedText as n -> Some n.Range
| _ -> None
list.Add(roslynTag t.Tag, t.Text, rangeOpt)

let StartAsyncAsTask cancellationToken computation =
let computation =
async {
Expand Down Expand Up @@ -349,10 +342,3 @@ module internal OpenDeclarationHelper =
else sourceText
sourceText, minPos |> Option.defaultValue 0

[<AutoOpen>]
module internal RoslynExtensions =
type Project with
/// The list of all other projects within the same solution that reference this project.
member this.GetDependentProjects() =
this.Solution.GetProjectDependencyGraph().GetProjectsThatDirectlyDependOnThisProject(this.Id)
|> Seq.map this.Solution.GetProject
Loading