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.200.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### 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))
* F# Interactive gains a JSON-RPC server mode, `--fsi-server-jsonrpc:<pipe name>`, in which a host submits interactions over a named pipe and receives structured results — diagnostics with positions, escaping exceptions, the values each interaction bound, and the session's own process id — instead of recovering them by looking for a `SERVER-PROMPT>` marker in the output text. Program output continues to flow through the redirected console streams. The pipe admits only the user running the session; `--fsi-server-client-pid:<pid>` names the host process whose exit ends the session. `FsiEvaluationSession` exposes both options as `JsonRpcServerPipeName` and `JsonRpcClientProcessId`. The mode is part of the .NET fsi only. ([PR #20396](https://github.com/dotnet/fsharp/pull/20396))

### Fixed
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 @@ -7,6 +7,8 @@

* Go To Definition no longer blocks the UI thread with a bare `Task.Wait`: the synchronous `IFSharpGoToDefinitionService` call now waits through the cancellable threaded-wait dialog, and the editor's `TaskCompletionSource` bridges run their continuations on the thread pool instead of inline on whichever thread finished the check, so repeated F12 on a large solution no longer starves semantic classification and other main-thread work. ([PR #20482](https://github.com/dotnet/fsharp/pull/20482))
* Peek Definition on an F# symbol whose definition lives in metadata no longer deadlocks Visual Studio. Peek holds the main thread in `JoinableTaskFactory.Run` without pumping messages while it asks the language service for the definition, and generating the metadata document needs that same thread; Peek now stops at definitions that already have a document, and Go To Definition, which owns the wait it makes, still opens the generated one. ([PR #20503](https://github.com/dotnet/fsharp/pull/20503))
* 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 @@ -764,10 +764,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 @@ -778,7 +805,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 @@ -789,10 +816,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 @@ -803,11 +832,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 @@ -818,14 +852,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 @@ -863,17 +900,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 @@ -3949,10 +3949,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 @@ -52,6 +52,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