Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/11.0.100.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@
* IL: fix leaking binary view ([PR #20250](https://github.com/dotnet/fsharp/pull/20250))

### Added
* `NavigableItem` carries `ParameterCount` and `TypeParameterCount`, the counts C# and VB give Navigate To to order matches that are otherwise equal. ([PR #20531](https://github.com/dotnet/fsharp/pull/20531))

* Added a "most concrete" tiebreaker for overload resolution (`--langversion:preview`). ([RFC FS-1340](https://github.com/fsharp/fslang-design/pull/834), [PR #19277](https://github.com/dotnet/fsharp/pull/19277))
* Added support for `OverloadResolutionPriorityAttribute` in overload resolution (`--langversion:preview`). ([RFC FS-1338](https://github.com/fsharp/fslang-design/pull/828), [PR #19277](https://github.com/dotnet/fsharp/pull/19277))
Expand Down
2 changes: 2 additions & 0 deletions docs/release-notes/.VisualStudio/18.vNext.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

### Fixed

* Navigate To (Ctrl+T) orders F# matches of the same kind as it orders C# and Visual Basic ones: a match in the file being edited first, then by how far its folder is from that file, then by parameter and type parameter count and name. ([PR #20532](https://github.com/dotnet/fsharp/pull/20532))

* Improve Find All References performance by throttling parallel typechecks. ([PR #20128](https://github.com/dotnet/fsharp/pull/20128))
* Fixed Rename incorrectly renaming `get` and `set` keywords for properties with explicit accessors. ([Issue #18270](https://github.com/dotnet/fsharp/issues/18270), [PR #19252](https://github.com/dotnet/fsharp/pull/19252))
* Fixed Find All References crash when F# project contains non-F# files like `.cshtml`. ([Issue #16394](https://github.com/dotnet/fsharp/issues/16394), [PR #19252](https://github.com/dotnet/fsharp/pull/19252))
Expand Down
63 changes: 54 additions & 9 deletions src/Compiler/Service/ServiceNavigation.fs
Original file line number Diff line number Diff line change
Expand Up @@ -762,10 +762,37 @@ type NavigableItem =
IsSignature: bool
Kind: NavigableItemKind
Container: NavigableContainer
ParameterCount: int
TypeParameterCount: int
}

[<RequireQualifiedAccess>]
module NavigateTo =
let private typeParameterCountOf (typars: SynTyparDecls option) =
match typars with
| Some typars -> typars.TyparDecls.Length
| None -> 0

let rec private isUnitType synType =
match synType with
| SynType.LongIdent(SynLongIdent([ id ], _, _)) -> id.idText = "unit"
| SynType.Paren(innerType, _)
| SynType.WithGlobalConstraints(innerType, _, _) -> isUnitType innerType
| _ -> false

/// The parameters of the compiled method: the parser leaves a solitary unit argument of a signature in its arity,
/// where a binding has already dropped it.
let private parameterCountOfSignature (SynValInfo(curriedArgInfos, _)) (synType: SynType) =
match curriedArgInfos, synType with
| [ [ _ ] ], SynType.Fun(argType = argType) when isUnitType argType -> 0
| [ [ _ ] ], SynType.WithGlobalConstraints(SynType.Fun(argType = argType), _, _) when isUnitType argType -> 0
| _ -> List.sumBy List.length curriedArgInfos

let private parameterCountOfBinding (SynValData(memberFlags = memberFlags; valInfo = SynValInfo(curriedArgInfos, _))) =
match memberFlags, curriedArgInfos with
| Some memberFlags, _self :: argInfos when memberFlags.IsInstance -> List.sumBy List.length argInfos
| _ -> List.sumBy List.length curriedArgInfos

let GetNavigableItems (parsedInput: ParsedInput) : NavigableItem[] =

let convertToDisplayName name =
Expand All @@ -776,7 +803,7 @@ module NavigateTo =

let result = ResizeArray()

let addLongIdent kind (lid: LongIdent) (isSignature: bool) (container: NavigableContainer) =
let addLongIdent kind (lid: LongIdent) (isSignature: bool) (container: NavigableContainer) typeParameterCount =
if not lid.IsEmpty then
let name = textOfLid lid

Expand All @@ -787,10 +814,12 @@ module NavigateTo =
IsSignature = isSignature
Kind = kind
Container = container
ParameterCount = 0
TypeParameterCount = typeParameterCount
}
|> result.Add

let addIdent kind (id: Ident) (isSignature: bool) (container: NavigableContainer) =
let addIdentWithArity kind (id: Ident) (isSignature: bool) (container: NavigableContainer) parameterCount typeParameterCount =
if not (String.IsNullOrEmpty id.idText) then
let name = convertToDisplayName id.idText

Expand All @@ -801,11 +830,16 @@ module NavigateTo =
IsSignature = isSignature
Kind = kind
Container = container
ParameterCount = parameterCount
TypeParameterCount = typeParameterCount
}
|> result.Add

let addIdent kind id isSignature container =
addIdentWithArity kind id isSignature container 0 0

let addModule lid isSig container =
addLongIdent NavigableItemKind.Module lid isSig container
addLongIdent NavigableItemKind.Module lid isSig container 0

let addModuleAbbreviation (id: Ident) isSig container =
addIdent NavigableItemKind.ModuleAbbreviation id isSig container
Expand All @@ -816,14 +850,17 @@ module NavigateTo =
NavigableContainer.Container(NavigableContainerType.Exception, [ id.idText ], container)

let addComponentInfo containerType kind (info: SynComponentInfo) isSig container =
let (SynComponentInfo(typeParams = typeParams)) = info
let lid = info.LongIdent
addLongIdent kind lid isSig container
addLongIdent kind lid isSig container (typeParameterCountOf typeParams)

NavigableContainer.Container(containerType, pathOfLid lid, container)

let addValSig kind synValSig isSig container =
let (SynValSig(ident = SynIdent(id, _))) = synValSig
addIdent kind id isSig container
let (SynValSig(ident = SynIdent(id, _); explicitTypeParams = SynValTyparDecls(typars, _); synType = synType; arity = arity)) =
synValSig

addIdentWithArity kind id isSig container (parameterCountOfSignature arity synType) (typeParameterCountOf typars)

let addField synField isSig container =
let (SynField(idOpt = id)) = synField
Expand Down Expand Up @@ -861,17 +898,25 @@ module NavigateTo =
| Some mf -> mapMemberKind mf.MemberKind
| _ -> NavigableItemKind.ModuleValue

let typeParameterCount =
match headPat with
| SynPat.LongIdent(typarDecls = Some(SynValTyparDecls(typars, _))) -> typeParameterCountOf typars
| _ -> 0

let addBindingIdent id =
addIdentWithArity kind id false container (parameterCountOfBinding valData) typeParameterCount

match headPat with
| SynPat.LongIdent(longDotId = SynLongIdent([ _; id ], _, _)) ->
// instance members
addIdent kind id false container
addBindingIdent id
| SynPat.LongIdent(longDotId = SynLongIdent([ id ], _, _)) ->
// functions
addIdent kind id false container
addBindingIdent id
| SynPat.Named(SynIdent(id, _), _, _, _)
| SynPat.As(_, SynPat.Named(SynIdent(id, _), _, _, _), _) ->
// values
addIdent kind id false container
addBindingIdent id
| _ -> ()

let addMember valSig (memberFlags: SynMemberFlags) isSig container =
Expand Down
20 changes: 14 additions & 6 deletions src/Compiler/Service/ServiceNavigation.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,20 @@ type NavigableContainer =
member Name: string

type NavigableItem =
{ Name: string
NeedsBackticks: bool
Range: range
IsSignature: bool
Kind: NavigableItemKind
Container: NavigableContainer }
{
Name: string
NeedsBackticks: bool
Range: range
IsSignature: bool
Kind: NavigableItemKind
Container: NavigableContainer
/// The number of parameters of the method the declaration compiles to, as C# and VB count them to order
/// equally good Navigate To matches: every curried and tupled argument, without the instance and without a
/// solitary unit argument.
ParameterCount: int
/// The number of explicitly declared type parameters.
TypeParameterCount: int
}

[<RequireQualifiedAccess>]
module public NavigateTo =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3946,10 +3946,14 @@ FSharp.Compiler.EditorServices.NavigableItem: FSharp.Compiler.Text.Range Range
FSharp.Compiler.EditorServices.NavigableItem: FSharp.Compiler.Text.Range get_Range()
FSharp.Compiler.EditorServices.NavigableItem: Int32 GetHashCode()
FSharp.Compiler.EditorServices.NavigableItem: Int32 GetHashCode(System.Collections.IEqualityComparer)
FSharp.Compiler.EditorServices.NavigableItem: Int32 ParameterCount
FSharp.Compiler.EditorServices.NavigableItem: Int32 TypeParameterCount
FSharp.Compiler.EditorServices.NavigableItem: Int32 get_ParameterCount()
FSharp.Compiler.EditorServices.NavigableItem: Int32 get_TypeParameterCount()
FSharp.Compiler.EditorServices.NavigableItem: System.String Name
FSharp.Compiler.EditorServices.NavigableItem: System.String ToString()
FSharp.Compiler.EditorServices.NavigableItem: System.String get_Name()
FSharp.Compiler.EditorServices.NavigableItem: Void .ctor(System.String, Boolean, FSharp.Compiler.Text.Range, Boolean, FSharp.Compiler.EditorServices.NavigableItemKind, FSharp.Compiler.EditorServices.NavigableContainer)
FSharp.Compiler.EditorServices.NavigableItem: Void .ctor(System.String, Boolean, FSharp.Compiler.Text.Range, Boolean, FSharp.Compiler.EditorServices.NavigableItemKind, FSharp.Compiler.EditorServices.NavigableContainer, Int32, Int32)
FSharp.Compiler.EditorServices.NavigableItemKind+Tags: Int32 Constructor
FSharp.Compiler.EditorServices.NavigableItemKind+Tags: Int32 EnumCase
FSharp.Compiler.EditorServices.NavigableItemKind+Tags: Int32 Exception
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
<Compile Include="ExprTests.fs" />
<Compile Include="CSharpProjectAnalysis.fs" />
<Compile Include="ServiceUntypedParseTests.fs" />
<Compile Include="NavigateToTests.fs" />
<Compile Include="PatternMatchCompilationTests.fs" />
<Compile Include="CompletionTests.fs" />
<Compile Include="ScriptOptionsTests.fs" />
Expand Down
63 changes: 63 additions & 0 deletions tests/FSharp.Compiler.Service.Tests/NavigateToTests.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
module FSharp.Compiler.Service.Tests.NavigateToTests

open FSharp.Compiler.EditorServices
open FSharp.Compiler.Service.Tests.Common
open FSharp.Compiler.Syntax
open Xunit

let private implementation =
"""
let value = 1
let curried a b = a + b
let tupledAndCurried (a, b) c = a + b + c
let takesUnit () = 1
let generic<'T> (x: 'T) = x

type C<'T, 'U>() =
member _.Method(a: int, b: int) = a + b
member _.Property = 1
static member Static x y = x + y
"""

let private signature =
"""
module M

val curried: int -> int -> int
val takesUnit: unit -> int
val generic<'T> : 'T * 'T -> 'T

type C<'T> =
member Method: a: int * b: int -> int
abstract Abstract: unit -> unit
"""

let private arityOf (parseTree: ParsedInput) name =
let item =
NavigateTo.GetNavigableItems parseTree
|> Array.find (fun item -> item.Name = name)

item.ParameterCount, item.TypeParameterCount

[<Theory>]
[<InlineData("value", 0, 0)>]
[<InlineData("curried", 2, 0)>]
[<InlineData("tupledAndCurried", 3, 0)>]
[<InlineData("takesUnit", 0, 0)>]
[<InlineData("generic", 1, 1)>]
[<InlineData("C", 0, 2)>]
[<InlineData("Method", 2, 0)>]
[<InlineData("Property", 0, 0)>]
[<InlineData("Static", 2, 0)>]
let ``A declaration in an implementation file counts the parameters it compiles to`` (name: string, parameterCount: int, typeParameterCount: int) =
Assert.Equal((parameterCount, typeParameterCount), arityOf (getParseResults implementation) name)

[<Theory>]
[<InlineData("curried", 2, 0)>]
[<InlineData("takesUnit", 0, 0)>]
[<InlineData("generic", 2, 1)>]
[<InlineData("C", 0, 1)>]
[<InlineData("Method", 2, 0)>]
[<InlineData("Abstract", 0, 0)>]
let ``A declaration in a signature file counts the parameters it compiles to`` (name: string, parameterCount: int, typeParameterCount: int) =
Assert.Equal((parameterCount, typeParameterCount), arityOf (getParseResultsOfSignatureFile signature) name)
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,9 @@ type internal FSharpNavigateToSearchService
ImmutableArray.Create(TaggedText(TextTags.Text, item.Name)),
document,
sourceSpan
)
),
item.ParameterCount,
item.TypeParameterCount
)
| _ -> ()
|]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,8 @@ module HeyHo =
[<Fact>]
let ``nested containers`` () =
assertResultsContain "hh.a.b.g.d" "Delta"

[<Fact>]
let ``results carry the counts Navigate To sorts equal matches by`` () =
let result = navigateToSearch "+>" |> Seq.find (fun i -> i.Name = "+>")
Assert.Equal((2, 0), (result.ParameterCount, result.TypeParameterCount))
Loading