Skip to content
Merged
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
4 changes: 4 additions & 0 deletions vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,12 @@
<Compile Include="Build\SetGlobalPropertiesForSdkProjects.fs" />
<Compile Include="AutomaticCompletion\BraceCompletionSessionProvider.fsi" />
<Compile Include="AutomaticCompletion\BraceCompletionSessionProvider.fs" />
<Compile Include="Hints\Hints.fs" />
<Compile Include="Hints\InlineTypeHints.fs" />
<Compile Include="Hints\InlineParameterNameHints.fs" />
<Compile Include="Hints\HintService.fs" />
<Compile Include="Hints\NativeToRoslynHintConverter.fs" />
<Compile Include="Hints\OptionParser.fs" />
<Compile Include="Hints\RoslynAdapter.fs" />
<Compile Include="Lens\LensDisplayService.fs" />
<Compile Include="Lens\LensService.fs" />
Expand Down
64 changes: 14 additions & 50 deletions vsintegration/src/FSharp.Editor/Hints/HintService.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,64 +6,28 @@ open Microsoft.CodeAnalysis
open Microsoft.VisualStudio.FSharp.Editor
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.Symbols
open FSharp.Compiler.Text
open Hints

module HintService =

// Relatively convenient for testing
type NativeHint = {
Range: range
Parts: TaggedText list
}

let private isValidForHint
(parseFileResults: FSharpParseFileResults)
(symbol: FSharpMemberOrFunctionOrValue)
(symbolUse: FSharpSymbolUse) =

let isNotAnnotatedManually =
not (parseFileResults.IsTypeAnnotationGivenAtPosition symbolUse.Range.Start)

let isNotAfterDot =
symbolUse.IsFromDefinition
&& not symbol.IsMemberThisValue

let isNotTypeAlias =
not symbol.IsConstructorThisValue

symbol.IsValue // we'll be adding other stuff gradually here
&& isNotAnnotatedManually
&& isNotAfterDot
&& isNotTypeAlias

let private getHintParts
(symbol: FSharpMemberOrFunctionOrValue)
(symbolUse: FSharpSymbolUse) =

match symbol.GetReturnTypeLayout symbolUse.DisplayContext with
| Some typeInfo ->
let colon = TaggedText(TextTag.Text, ": ")
colon :: (typeInfo |> Array.toList)

// not sure when this can happen but better safe than sorry
| None ->
[]

let private getHintsForSymbol parseResults (symbolUse: FSharpSymbolUse) =
let private getHintsForSymbol parseResults hintKinds (symbolUse: FSharpSymbolUse) =
match symbolUse.Symbol with
| :? FSharpMemberOrFunctionOrValue as mfvSymbol
when isValidForHint parseResults mfvSymbol symbolUse ->
| :? FSharpMemberOrFunctionOrValue as symbol
when hintKinds |> Set.contains HintKind.TypeHint
&& InlineTypeHints.isValidForHint parseResults symbol symbolUse ->

[ {
Range = symbolUse.Range
Parts = getHintParts mfvSymbol symbolUse
} ]
InlineTypeHints.getHints symbol symbolUse

| :? FSharpMemberOrFunctionOrValue as symbol
when hintKinds |> Set.contains HintKind.ParameterNameHint
&& InlineParameterNameHints.isValidForHint symbol ->

InlineParameterNameHints.getHints parseResults symbol symbolUse

// we'll be adding other stuff gradually here
| _ ->
[]

let getHintsForDocument (document: Document) userOpName cancellationToken =
let getHintsForDocument (document: Document) hintKinds userOpName cancellationToken =
async {
if isSignatureFile document.FilePath
then
Expand All @@ -75,5 +39,5 @@ module HintService =
return
checkResults.GetAllUsesOfAllSymbolsInFile cancellationToken
|> Seq.toList
|> List.collect (getHintsForSymbol parseResults)
|> List.collect (getHintsForSymbol parseResults hintKinds)
}
18 changes: 18 additions & 0 deletions vsintegration/src/FSharp.Editor/Hints/Hints.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace Microsoft.VisualStudio.FSharp.Editor.Hints

open FSharp.Compiler.Text

module Hints =

type HintKind =
| TypeHint
| ParameterNameHint

// Relatively convenient for testing
type NativeHint = {
Kind: HintKind
Range: range
Parts: TaggedText list
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace Microsoft.VisualStudio.FSharp.Editor.Hints

open Microsoft.VisualStudio.FSharp.Editor
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.Symbols
open FSharp.Compiler.Text
open Hints

module InlineParameterNameHints =

let private getHint (range: range, parameter: FSharpParameter) =
{
Kind = HintKind.ParameterNameHint
Range = range.StartRange
Parts = [ TaggedText(TextTag.Text, $"{parameter.DisplayName} = ") ]
}

let private doesParameterNameExist (parameter: FSharpParameter) =
parameter.DisplayName <> ""

let isValidForHint (symbol: FSharpMemberOrFunctionOrValue) =
// is there a better way?
let isNotBuiltInOperator =
symbol.DeclaringEntity
|> Option.exists (fun entity -> entity.CompiledName <> "Operators")

symbol.IsFunction
&& isNotBuiltInOperator // arguably, hints for those would be rather useless
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is missing a symbolUse.IsFromUse check, because without it function definitions are eligible for a hint, even though you will later find no applied arguments, so nothing is shown. I will fix it in the member and ctor PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right, so the behavior is by coincidence :) Yes, this will be a valid check, thanks.


let getHints
(parseResults: FSharpParseFileResults)
(symbol: FSharpMemberOrFunctionOrValue)
(symbolUse: FSharpSymbolUse) =

let parameters = symbol.CurriedParameterGroups |> Seq.concat
let ranges = parseResults.GetAllArgumentsForFunctionApplicationAtPosition symbolUse.Range.Start

match ranges with
| Some ranges ->
parameters
|> Seq.zip ranges
|> Seq.where (snd >> doesParameterNameExist)
|> Seq.map getHint
|> Seq.toList

// this is the case at least for custom operators
| None -> []
54 changes: 54 additions & 0 deletions vsintegration/src/FSharp.Editor/Hints/InlineTypeHints.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace Microsoft.VisualStudio.FSharp.Editor.Hints

open Microsoft.VisualStudio.FSharp.Editor
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.Symbols
open FSharp.Compiler.Text
open Hints

module InlineTypeHints =

let private getHintParts
(symbol: FSharpMemberOrFunctionOrValue)
(symbolUse: FSharpSymbolUse) =

match symbol.GetReturnTypeLayout symbolUse.DisplayContext with
| Some typeInfo ->
let colon = TaggedText(TextTag.Text, ": ")
colon :: (typeInfo |> Array.toList)

// not sure when this can happen
| None ->
[]

let private getHint symbol (symbolUse: FSharpSymbolUse) =
{
Kind = HintKind.TypeHint
Range = symbolUse.Range.EndRange
Parts = getHintParts symbol symbolUse
}

let isValidForHint
(parseFileResults: FSharpParseFileResults)
(symbol: FSharpMemberOrFunctionOrValue)
(symbolUse: FSharpSymbolUse) =

let isNotAnnotatedManually =
not (parseFileResults.IsTypeAnnotationGivenAtPosition symbolUse.Range.Start)

let isNotAfterDot =
symbolUse.IsFromDefinition
&& not symbol.IsMemberThisValue

let isNotTypeAlias =
not symbol.IsConstructorThisValue

symbol.IsValue // we'll be adding other stuff gradually here
&& isNotAnnotatedManually
&& isNotAfterDot
&& isNotTypeAlias

let getHints symbol symbolUse =
[ getHint symbol symbolUse ]
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ open Microsoft.CodeAnalysis.Text
open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints
open Microsoft.VisualStudio.FSharp.Editor
open FSharp.Compiler.Text
open HintService
open Hints

module NativeToRoslynHintConverter =

Expand Down
16 changes: 16 additions & 0 deletions vsintegration/src/FSharp.Editor/Hints/OptionParser.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace Microsoft.VisualStudio.FSharp.Editor.Hints

open Microsoft.VisualStudio.FSharp.Editor
open Hints

module OptionParser =

let getHintKinds options =
Set
[ if options.IsInlineTypeHintsEnabled then
HintKind.TypeHint

if options.IsInlineParameterNameHintsEnabled then
HintKind.ParameterNameHint ]
24 changes: 13 additions & 11 deletions vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,19 @@ type internal RoslynAdapter
interface IFSharpInlineHintsService with
member _.GetInlineHintsAsync(document, _, cancellationToken) =
async {
if not settings.Advanced.IsInlineHintsEnabled
then return ImmutableArray.Empty

else
let! sourceText = document.GetTextAsync cancellationToken |> Async.AwaitTask
let! nativeHints =
HintService.getHintsForDocument document userOpName cancellationToken
let! sourceText = document.GetTextAsync cancellationToken |> Async.AwaitTask
let hintKinds = OptionParser.getHintKinds settings.Advanced

let! nativeHints =
HintService.getHintsForDocument
document
hintKinds
userOpName
cancellationToken

let roslynHints =
nativeHints
|> Seq.map (NativeToRoslynHintConverter.convert sourceText)
let roslynHints =
nativeHints
|> Seq.map (NativeToRoslynHintConverter.convert sourceText)

return roslynHints.ToImmutableArray()
return roslynHints.ToImmutableArray()
} |> RoslynHelpers.StartAsyncAsTask cancellationToken
6 changes: 4 additions & 2 deletions vsintegration/src/FSharp.Editor/Options/EditorOptions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,13 @@ type LensOptions =
type AdvancedOptions =
{ IsBlockStructureEnabled: bool
IsOutliningEnabled: bool
IsInlineHintsEnabled: bool }
IsInlineTypeHintsEnabled: bool
IsInlineParameterNameHintsEnabled: bool }
static member Default =
{ IsBlockStructureEnabled = true
IsOutliningEnabled = true
IsInlineHintsEnabled = false }
IsInlineTypeHintsEnabled = false
IsInlineParameterNameHintsEnabled = false }

[<CLIMutable>]
type FormattingOptions =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@
Content="{x:Static local:Strings.Show_Outlining}"/>
</GroupBox>
<GroupBox Header="{x:Static local:Strings.Inline_Hints}">
<CheckBox x:Name="toggleInlineHints" IsChecked="{Binding IsInlineHintsEnabled}"
Content="{x:Static local:Strings.Show_Inline_Hints}"/>
<StackPanel>
<CheckBox x:Name="toggleInlineTypeHints" IsChecked="{Binding IsInlineTypeHintsEnabled}"
Content="{x:Static local:Strings.Show_Inline_Type_Hints}"/>
<CheckBox x:Name="toggleParameterNameHints" IsChecked="{Binding IsInlineParameterNameHintsEnabled}"
Content="{x:Static local:Strings.Show_Inline_Parameter_Name_Hints}"/>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>
Expand Down
17 changes: 13 additions & 4 deletions vsintegration/src/FSharp.UIResources/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion vsintegration/src/FSharp.UIResources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@
<data name="Inline_Hints" xml:space="preserve">
<value>Inline Hints</value>
</data>
<data name="Show_Inline_Hints" xml:space="preserve">
<data name="Show_Inline_Type_Hints" xml:space="preserve">
<value>Display inline type hints (experimental)</value>
</data>
<data name="Time_until_stale_completion" xml:space="preserve">
Expand Down Expand Up @@ -243,4 +243,7 @@
<data name="Enable_Parallel_Reference_Resolution" xml:space="preserve">
<value>Enable parallel reference resolution</value>
</data>
<data name="Show_Inline_Parameter_Name_Hints" xml:space="preserve">
<value>Display inline parameter name hints (experimental)</value>
</data>
</root>
7 changes: 6 additions & 1 deletion vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,12 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<trans-unit id="Show_Inline_Parameter_Name_Hints">
<source>Display inline parameter name hints (experimental)</source>
<target state="new">Display inline parameter name hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Type_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
Expand Down
7 changes: 6 additions & 1 deletion vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,12 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<trans-unit id="Show_Inline_Parameter_Name_Hints">
<source>Display inline parameter name hints (experimental)</source>
<target state="new">Display inline parameter name hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Type_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
Expand Down
Loading