Skip to content

Commit

Permalink
Inline type hints for F# (dotnet#14159)
Browse files Browse the repository at this point in the history
  • Loading branch information
psfinaki authored Oct 25, 2022
1 parent 0a7260a commit a5dfa94
Show file tree
Hide file tree
Showing 25 changed files with 534 additions and 2 deletions.
3 changes: 3 additions & 0 deletions vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@
<Compile Include="Build\SetGlobalPropertiesForSdkProjects.fs" />
<Compile Include="AutomaticCompletion\BraceCompletionSessionProvider.fsi" />
<Compile Include="AutomaticCompletion\BraceCompletionSessionProvider.fs" />
<Compile Include="Hints\HintService.fs" />
<Compile Include="Hints\NativeToRoslynHintConverter.fs" />
<Compile Include="Hints\RoslynAdapter.fs" />
<Compile Include="Lens\LensDisplayService.fs" />
<Compile Include="Lens\LensService.fs" />
<Compile Include="Lens\LensProvider.fs" />
Expand Down
79 changes: 79 additions & 0 deletions vsintegration/src/FSharp.Editor/Hints/HintService.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// 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.CodeAnalysis
open Microsoft.VisualStudio.FSharp.Editor
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.Symbols
open FSharp.Compiler.Text

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) =
match symbolUse.Symbol with
| :? FSharpMemberOrFunctionOrValue as mfvSymbol
when isValidForHint parseResults mfvSymbol symbolUse ->

[ {
Range = symbolUse.Range
Parts = getHintParts mfvSymbol symbolUse
} ]

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

let getHintsForDocument (document: Document) userOpName cancellationToken =
async {
if isSignatureFile document.FilePath
then
return []
else
let! parseResults, checkResults =
document.GetFSharpParseAndCheckResultsAsync userOpName

return
checkResults.GetAllUsesOfAllSymbolsInFile cancellationToken
|> Seq.toList
|> List.collect (getHintsForSymbol parseResults)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace Microsoft.VisualStudio.FSharp.Editor.Hints

open System.Collections.Immutable
open Microsoft.CodeAnalysis.Text
open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints
open Microsoft.VisualStudio.FSharp.Editor
open FSharp.Compiler.Text
open HintService

module NativeToRoslynHintConverter =

let rangeToSpan range sourceText =
let symbolSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, range)
let overshadowLength = 0 // anything >0 means overlaying the code
TextSpan(symbolSpan.End, overshadowLength)

let nativeToRoslynText (taggedText: TaggedText) =
let tag = RoslynHelpers.roslynTag taggedText.Tag
let text = taggedText.Text
RoslynTaggedText(tag, text)

let convert sourceText hint =
let span = rangeToSpan hint.Range sourceText
let displayParts = hint.Parts |> Seq.map nativeToRoslynText
FSharpInlineHint(span, displayParts.ToImmutableArray())
38 changes: 38 additions & 0 deletions vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace Microsoft.VisualStudio.FSharp.Editor.Hints

open System.Collections.Immutable
open System.ComponentModel.Composition
open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints
open Microsoft.VisualStudio.FSharp.Editor

// So the Roslyn interface is called IFSharpInlineHintsService
// but our implementation is called just HintsService.
// That's because we'll likely use this API for things other than inline hints,
// e.g. signature hints above the line, pipeline hints on the side and so on.

[<Export(typeof<IFSharpInlineHintsService>)>]
type internal RoslynAdapter
[<ImportingConstructor>]
(settings: EditorOptions) =

static let userOpName = "Hints"

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 roslynHints =
nativeHints
|> Seq.map (NativeToRoslynHintConverter.convert sourceText)

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 @@ -85,10 +85,12 @@ type LensOptions =
[<CLIMutable>]
type AdvancedOptions =
{ IsBlockStructureEnabled: bool
IsOutliningEnabled: bool }
IsOutliningEnabled: bool
IsInlineHintsEnabled: bool }
static member Default =
{ IsBlockStructureEnabled = true
IsOutliningEnabled = true }
IsOutliningEnabled = true
IsInlineHintsEnabled = false }

[<CLIMutable>]
type FormattingOptions =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
<CheckBox x:Name="toggleOutloning" IsChecked="{Binding IsOutliningEnabled}"
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}"/>
</GroupBox>
</StackPanel>
</ScrollViewer>
</Grid>
Expand Down
18 changes: 18 additions & 0 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.

6 changes: 6 additions & 0 deletions vsintegration/src/FSharp.UIResources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@
<data name="Show_Outlining" xml:space="preserve">
<value>Show outlining and collapsible nodes for F# code</value>
</data>
<data name="Inline_Hints" xml:space="preserve">
<value>Inline Hints</value>
</data>
<data name="Show_Inline_Hints" xml:space="preserve">
<value>Display inline type hints (experimental)</value>
</data>
<data name="Time_until_stale_completion" xml:space="preserve">
<value>Time until stale results are used (in milliseconds)</value>
</data>
Expand Down
10 changes: 10 additions & 0 deletions vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
<target state="translated">Vždy umístit otevřené příkazy na nejvyšší úroveň</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
Expand Down Expand Up @@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">Zobrazit s_ymboly v neotevřených oborech názvů</target>
Expand Down
10 changes: 10 additions & 0 deletions vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
<target state="translated">open-Anweisungen immer an oberster Ebene platzieren</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
Expand Down Expand Up @@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">S_ymbole in nicht geöffneten Namespaces anzeigen</target>
Expand Down
10 changes: 10 additions & 0 deletions vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
<target state="translated">Colocar siempre las instrucciones open en el nivel superior</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
Expand Down Expand Up @@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">Mostrar sím_bolos en espacios de nombres sin abrir</target>
Expand Down
10 changes: 10 additions & 0 deletions vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
<target state="translated">Placer toujours les instructions open au niveau supérieur</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
Expand Down Expand Up @@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">Afficher les sym_boles dans les espaces de noms non ouverts</target>
Expand Down
10 changes: 10 additions & 0 deletions vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
<target state="translated">Inserisci sempre le istruzioni OPEN al primo livello</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
Expand Down Expand Up @@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">Mostra si_mboli in spazi dei nomi non aperti</target>
Expand Down
10 changes: 10 additions & 0 deletions vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
<target state="translated">Open ステートメントを常に最上位に配置する</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
Expand Down Expand Up @@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">開かれていない名前空間の記号を表示する(_Y)</target>
Expand Down
10 changes: 10 additions & 0 deletions vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
<target state="translated">항상 최상위에 open 문 배치</target>
<note />
</trans-unit>
<trans-unit id="Inline_Hints">
<source>Inline Hints</source>
<target state="new">Inline Hints</target>
<note />
</trans-unit>
<trans-unit id="Lens">
<source>Lens</source>
<target state="translated">Lens</target>
Expand Down Expand Up @@ -77,6 +82,11 @@
<target state="new">Parallelization (requires restart)</target>
<note />
</trans-unit>
<trans-unit id="Show_Inline_Hints">
<source>Display inline type hints (experimental)</source>
<target state="new">Display inline type hints (experimental)</target>
<note />
</trans-unit>
<trans-unit id="Show_all_symbols">
<source>Show s_ymbols in unopened namespaces</source>
<target state="translated">열려 있지 않은 네임스페이스에 기호 표시(_Y)</target>
Expand Down
Loading

0 comments on commit a5dfa94

Please sign in to comment.