Skip to content

Commit a5dfa94

Browse files
authored
Inline type hints for F# (#14159)
1 parent 0a7260a commit a5dfa94

25 files changed

+534
-2
lines changed

vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@
126126
<Compile Include="Build\SetGlobalPropertiesForSdkProjects.fs" />
127127
<Compile Include="AutomaticCompletion\BraceCompletionSessionProvider.fsi" />
128128
<Compile Include="AutomaticCompletion\BraceCompletionSessionProvider.fs" />
129+
<Compile Include="Hints\HintService.fs" />
130+
<Compile Include="Hints\NativeToRoslynHintConverter.fs" />
131+
<Compile Include="Hints\RoslynAdapter.fs" />
129132
<Compile Include="Lens\LensDisplayService.fs" />
130133
<Compile Include="Lens\LensService.fs" />
131134
<Compile Include="Lens\LensProvider.fs" />
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
2+
3+
namespace Microsoft.VisualStudio.FSharp.Editor.Hints
4+
5+
open Microsoft.CodeAnalysis
6+
open Microsoft.VisualStudio.FSharp.Editor
7+
open FSharp.Compiler.CodeAnalysis
8+
open FSharp.Compiler.Symbols
9+
open FSharp.Compiler.Text
10+
11+
module HintService =
12+
13+
// Relatively convenient for testing
14+
type NativeHint = {
15+
Range: range
16+
Parts: TaggedText list
17+
}
18+
19+
let private isValidForHint
20+
(parseFileResults: FSharpParseFileResults)
21+
(symbol: FSharpMemberOrFunctionOrValue)
22+
(symbolUse: FSharpSymbolUse) =
23+
24+
let isNotAnnotatedManually =
25+
not (parseFileResults.IsTypeAnnotationGivenAtPosition symbolUse.Range.Start)
26+
27+
let isNotAfterDot =
28+
symbolUse.IsFromDefinition
29+
&& not symbol.IsMemberThisValue
30+
31+
let isNotTypeAlias =
32+
not symbol.IsConstructorThisValue
33+
34+
symbol.IsValue // we'll be adding other stuff gradually here
35+
&& isNotAnnotatedManually
36+
&& isNotAfterDot
37+
&& isNotTypeAlias
38+
39+
let private getHintParts
40+
(symbol: FSharpMemberOrFunctionOrValue)
41+
(symbolUse: FSharpSymbolUse) =
42+
43+
match symbol.GetReturnTypeLayout symbolUse.DisplayContext with
44+
| Some typeInfo ->
45+
let colon = TaggedText(TextTag.Text, ": ")
46+
colon :: (typeInfo |> Array.toList)
47+
48+
// not sure when this can happen but better safe than sorry
49+
| None ->
50+
[]
51+
52+
let private getHintsForSymbol parseResults (symbolUse: FSharpSymbolUse) =
53+
match symbolUse.Symbol with
54+
| :? FSharpMemberOrFunctionOrValue as mfvSymbol
55+
when isValidForHint parseResults mfvSymbol symbolUse ->
56+
57+
[ {
58+
Range = symbolUse.Range
59+
Parts = getHintParts mfvSymbol symbolUse
60+
} ]
61+
62+
// we'll be adding other stuff gradually here
63+
| _ ->
64+
[]
65+
66+
let getHintsForDocument (document: Document) userOpName cancellationToken =
67+
async {
68+
if isSignatureFile document.FilePath
69+
then
70+
return []
71+
else
72+
let! parseResults, checkResults =
73+
document.GetFSharpParseAndCheckResultsAsync userOpName
74+
75+
return
76+
checkResults.GetAllUsesOfAllSymbolsInFile cancellationToken
77+
|> Seq.toList
78+
|> List.collect (getHintsForSymbol parseResults)
79+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
2+
3+
namespace Microsoft.VisualStudio.FSharp.Editor.Hints
4+
5+
open System.Collections.Immutable
6+
open Microsoft.CodeAnalysis.Text
7+
open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints
8+
open Microsoft.VisualStudio.FSharp.Editor
9+
open FSharp.Compiler.Text
10+
open HintService
11+
12+
module NativeToRoslynHintConverter =
13+
14+
let rangeToSpan range sourceText =
15+
let symbolSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, range)
16+
let overshadowLength = 0 // anything >0 means overlaying the code
17+
TextSpan(symbolSpan.End, overshadowLength)
18+
19+
let nativeToRoslynText (taggedText: TaggedText) =
20+
let tag = RoslynHelpers.roslynTag taggedText.Tag
21+
let text = taggedText.Text
22+
RoslynTaggedText(tag, text)
23+
24+
let convert sourceText hint =
25+
let span = rangeToSpan hint.Range sourceText
26+
let displayParts = hint.Parts |> Seq.map nativeToRoslynText
27+
FSharpInlineHint(span, displayParts.ToImmutableArray())
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
2+
3+
namespace Microsoft.VisualStudio.FSharp.Editor.Hints
4+
5+
open System.Collections.Immutable
6+
open System.ComponentModel.Composition
7+
open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints
8+
open Microsoft.VisualStudio.FSharp.Editor
9+
10+
// So the Roslyn interface is called IFSharpInlineHintsService
11+
// but our implementation is called just HintsService.
12+
// That's because we'll likely use this API for things other than inline hints,
13+
// e.g. signature hints above the line, pipeline hints on the side and so on.
14+
15+
[<Export(typeof<IFSharpInlineHintsService>)>]
16+
type internal RoslynAdapter
17+
[<ImportingConstructor>]
18+
(settings: EditorOptions) =
19+
20+
static let userOpName = "Hints"
21+
22+
interface IFSharpInlineHintsService with
23+
member _.GetInlineHintsAsync(document, _, cancellationToken) =
24+
async {
25+
if not settings.Advanced.IsInlineHintsEnabled
26+
then return ImmutableArray.Empty
27+
28+
else
29+
let! sourceText = document.GetTextAsync cancellationToken |> Async.AwaitTask
30+
let! nativeHints =
31+
HintService.getHintsForDocument document userOpName cancellationToken
32+
33+
let roslynHints =
34+
nativeHints
35+
|> Seq.map (NativeToRoslynHintConverter.convert sourceText)
36+
37+
return roslynHints.ToImmutableArray()
38+
} |> RoslynHelpers.StartAsyncAsTask cancellationToken

vsintegration/src/FSharp.Editor/Options/EditorOptions.fs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,12 @@ type LensOptions =
8585
[<CLIMutable>]
8686
type AdvancedOptions =
8787
{ IsBlockStructureEnabled: bool
88-
IsOutliningEnabled: bool }
88+
IsOutliningEnabled: bool
89+
IsInlineHintsEnabled: bool }
8990
static member Default =
9091
{ IsBlockStructureEnabled = true
91-
IsOutliningEnabled = true }
92+
IsOutliningEnabled = true
93+
IsInlineHintsEnabled = false }
9294

9395
[<CLIMutable>]
9496
type FormattingOptions =

vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
<CheckBox x:Name="toggleOutloning" IsChecked="{Binding IsOutliningEnabled}"
2525
Content="{x:Static local:Strings.Show_Outlining}"/>
2626
</GroupBox>
27+
<GroupBox Header="{x:Static local:Strings.Inline_Hints}">
28+
<CheckBox x:Name="toggleInlineHints" IsChecked="{Binding IsInlineHintsEnabled}"
29+
Content="{x:Static local:Strings.Show_Inline_Hints}"/>
30+
</GroupBox>
2731
</StackPanel>
2832
</ScrollViewer>
2933
</Grid>

vsintegration/src/FSharp.UIResources/Strings.Designer.cs

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vsintegration/src/FSharp.UIResources/Strings.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,12 @@
189189
<data name="Show_Outlining" xml:space="preserve">
190190
<value>Show outlining and collapsible nodes for F# code</value>
191191
</data>
192+
<data name="Inline_Hints" xml:space="preserve">
193+
<value>Inline Hints</value>
194+
</data>
195+
<data name="Show_Inline_Hints" xml:space="preserve">
196+
<value>Display inline type hints (experimental)</value>
197+
</data>
192198
<data name="Time_until_stale_completion" xml:space="preserve">
193199
<value>Time until stale results are used (in milliseconds)</value>
194200
</data>

vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
<target state="translated">Vždy umístit otevřené příkazy na nejvyšší úroveň</target>
88
<note />
99
</trans-unit>
10+
<trans-unit id="Inline_Hints">
11+
<source>Inline Hints</source>
12+
<target state="new">Inline Hints</target>
13+
<note />
14+
</trans-unit>
1015
<trans-unit id="Lens">
1116
<source>Lens</source>
1217
<target state="translated">Lens</target>
@@ -77,6 +82,11 @@
7782
<target state="new">Parallelization (requires restart)</target>
7883
<note />
7984
</trans-unit>
85+
<trans-unit id="Show_Inline_Hints">
86+
<source>Display inline type hints (experimental)</source>
87+
<target state="new">Display inline type hints (experimental)</target>
88+
<note />
89+
</trans-unit>
8090
<trans-unit id="Show_all_symbols">
8191
<source>Show s_ymbols in unopened namespaces</source>
8292
<target state="translated">Zobrazit s_ymboly v neotevřených oborech názvů</target>

vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
<target state="translated">open-Anweisungen immer an oberster Ebene platzieren</target>
88
<note />
99
</trans-unit>
10+
<trans-unit id="Inline_Hints">
11+
<source>Inline Hints</source>
12+
<target state="new">Inline Hints</target>
13+
<note />
14+
</trans-unit>
1015
<trans-unit id="Lens">
1116
<source>Lens</source>
1217
<target state="translated">Lens</target>
@@ -77,6 +82,11 @@
7782
<target state="new">Parallelization (requires restart)</target>
7883
<note />
7984
</trans-unit>
85+
<trans-unit id="Show_Inline_Hints">
86+
<source>Display inline type hints (experimental)</source>
87+
<target state="new">Display inline type hints (experimental)</target>
88+
<note />
89+
</trans-unit>
8090
<trans-unit id="Show_all_symbols">
8191
<source>Show s_ymbols in unopened namespaces</source>
8292
<target state="translated">S_ymbole in nicht geöffneten Namespaces anzeigen</target>

0 commit comments

Comments
 (0)