forked from dotnet/fsharp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathInlineHints.fs
More file actions
144 lines (119 loc) · 8.41 KB
/
Copy pathInlineHints.fs
File metadata and controls
144 lines (119 loc) · 8.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
namespace Microsoft.VisualStudio.FSharp.Editor
open System
open System.Collections.Immutable
open System.Threading
open System.ComponentModel.Composition
open Microsoft.CodeAnalysis
open Microsoft.CodeAnalysis.Text
open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.EditorServices
open FSharp.Compiler.Symbols
open FSharp.Compiler.Syntax
open FSharp.Compiler.Text
[<Export(typeof<IFSharpInlineHintsService>)>]
type internal FSharpInlineHintsService
[<ImportingConstructor>]
(
) =
static let userOpName = "FSharpInlineHints"
static let getFirstPositionAfterParen (str: string) startPos =
match str with
| null -> -1
| str when startPos > str.Length -> -1
| str ->
str.IndexOf('(') + 1
interface IFSharpInlineHintsService with
member _.GetInlineHintsAsync(document: Document, textSpan: TextSpan, cancellationToken: CancellationToken) =
asyncMaybe {
do! Option.guard (not (isSignatureFile document.FilePath))
let! sourceText = document.GetTextAsync(cancellationToken)
let! parseFileResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(userOpName) |> Async.map Some
let range = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText)
let symbolUses =
checkFileResults.GetAllUsesOfAllSymbolsInFile(cancellationToken)
|> Seq.filter (fun su -> Range.rangeContainsRange range su.Range)
let typeHints = ImmutableArray.CreateBuilder()
let parameterHints = ImmutableArray.CreateBuilder()
let isValidForTypeHint (funcOrValue: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) =
let isLambdaIfFunction =
funcOrValue.IsFunction &&
parseFileResults.IsBindingALambdaAtPosition symbolUse.Range.Start
(funcOrValue.IsValue || isLambdaIfFunction) &&
not (parseFileResults.IsTypeAnnotationGivenAtPosition symbolUse.Range.Start) &&
symbolUse.IsFromDefinition &&
not funcOrValue.IsMember &&
not funcOrValue.IsMemberThisValue &&
not funcOrValue.IsConstructorThisValue &&
not (PrettyNaming.IsOperatorDisplayName funcOrValue.DisplayName)
for symbolUse in symbolUses do
match symbolUse.Symbol with
| :? FSharpMemberOrFunctionOrValue as funcOrValue when isValidForTypeHint funcOrValue symbolUse ->
let typeInfo =
funcOrValue.GetReturnTypeLayout symbolUse.DisplayContext
|> Option.defaultValue Array.empty
let displayParts = ImmutableArray.CreateBuilder()
displayParts.Add(RoslynTaggedText(TextTags.Text, ": "))
for tt in typeInfo do
displayParts.Add(RoslynTaggedText(RoslynHelpers.roslynTag tt.Tag, tt.Text))
let symbolSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, symbolUse.Range)
let hint = FSharpInlineHint(TextSpan(symbolSpan.End, 0), displayParts.ToImmutableArray())
typeHints.Add(hint)
| :? FSharpMemberOrFunctionOrValue as func when func.IsFunction && not symbolUse.IsFromDefinition ->
let appliedArgRangesOpt = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion symbolUse.Range.Start
match appliedArgRangesOpt with
| None -> ()
| Some [] -> ()
| Some appliedArgRanges ->
let parameters = func.CurriedParameterGroups |> Seq.concat
let appliedArgRanges = appliedArgRanges |> Array.ofList
let definitionArgs = parameters |> Array.ofSeq
for idx = 0 to appliedArgRanges.Length - 1 do
let appliedArgRange = appliedArgRanges.[idx]
let definitionArgName = definitionArgs.[idx].DisplayName
if not (String.IsNullOrWhiteSpace(definitionArgName)) then
let appliedArgSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, appliedArgRange)
let displayParts = ImmutableArray.Create(RoslynTaggedText(TextTags.Text, definitionArgName + " ="))
let hint = FSharpInlineHint(TextSpan(appliedArgSpan.Start, 0), displayParts)
parameterHints.Add(hint)
| :? FSharpMemberOrFunctionOrValue as methodOrConstructor when methodOrConstructor.IsMethod || methodOrConstructor.IsConstructor ->
let endPosForMethod = symbolUse.Range.End
let line, _ = Position.toZ endPosForMethod
let afterParenPosInLine = getFirstPositionAfterParen (sourceText.Lines.[line].ToString()) (endPosForMethod.Column)
let tupledParamInfos = parseFileResults.FindParameterLocations(Position.fromZ line afterParenPosInLine)
let appliedArgRanges = parseFileResults.GetAllArgumentsForFunctionApplicationAtPostion symbolUse.Range.Start
match tupledParamInfos, appliedArgRanges with
| None, None -> ()
// Prefer looking at the "tupled" view if it exists, even if the other ranges exist.
// M(1, 2) can give results for both, but in that case we want the "tupled" view.
| Some tupledParamInfos, _ ->
let parameters = methodOrConstructor.CurriedParameterGroups |> Seq.concat |> Array.ofSeq
for idx = 0 to parameters.Length - 1 do
let paramLocationInfo = tupledParamInfos.ArgumentLocations.[idx]
let paramName = parameters.[idx].DisplayName
if not paramLocationInfo.IsNamedArgument && not (String.IsNullOrWhiteSpace(paramName)) then
let appliedArgSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, paramLocationInfo.ArgumentRange)
let displayParts = ImmutableArray.Create(RoslynTaggedText(TextTags.Text, paramName + " ="))
let hint = FSharpInlineHint(TextSpan(appliedArgSpan.Start, 0), displayParts)
parameterHints.Add(hint)
// This will only happen for curried methods defined in F#.
| _, Some appliedArgRanges ->
let parameters = methodOrConstructor.CurriedParameterGroups |> Seq.concat
let appliedArgRanges = appliedArgRanges |> Array.ofList
let definitionArgs = parameters |> Array.ofSeq
for idx = 0 to appliedArgRanges.Length - 1 do
let appliedArgRange = appliedArgRanges.[idx]
let definitionArgName = definitionArgs.[idx].DisplayName
if not (String.IsNullOrWhiteSpace(definitionArgName)) then
let appliedArgSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, appliedArgRange)
let displayParts = ImmutableArray.Create(RoslynTaggedText(TextTags.Text, definitionArgName + " ="))
let hint = FSharpInlineHint(TextSpan(appliedArgSpan.Start, 0), displayParts)
parameterHints.Add(hint)
| _ -> ()
let typeHints = typeHints.ToImmutableArray()
let parameterHints = parameterHints.ToImmutableArray()
return typeHints.AddRange(parameterHints)
}
|> Async.map (Option.defaultValue ImmutableArray<_>.Empty)
|> RoslynHelpers.StartAsyncAsTask(cancellationToken)