Skip to content

Commit d1cd836

Browse files
authored
Inline Hints for F# (#12927)
Note the following behavior: * Type hints are at definition/binding, not use * Type hints do not show if something already has an annotation * Parameter name hints appear for F# functions and methods, using the named argument syntax * Named parameters get no hints * Lambdas that are let-bound will have a type hint, but other let-bound F# functions will not Out of scope: * Type hints for "normal" function declarations. * It just looks bad and there isn't a good way to play hints without getting smushed together with the type hint for the last parameter. * Lambda function declarations, yes. * Show inferred types for _ in a pattern * Note - this can be done orthogonally and also support quickinfo * Add QuickInfo - * Note, this can be done orthogonally. * We need a way to resolve the symbol we're showing the type for and get quickinfo from * No such API exists in the compiler today
1 parent d4fcbb7 commit d1cd836

File tree

12 files changed

+1727
-29
lines changed

12 files changed

+1727
-29
lines changed

src/fsharp/NicePrint.fs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,7 +1105,18 @@ module PrintTypes =
11051105

11061106
let prettyLayoutOfTypeNoConstraints denv ty =
11071107
let ty, _cxs = PrettyTypes.PrettifyType denv.g ty
1108-
layoutTypeWithInfoAndPrec denv SimplifyTypes.typeSimplificationInfo0 5 ty
1108+
layoutTypeWithInfoAndPrec denv SimplifyTypes.typeSimplificationInfo0 5 ty
1109+
1110+
let layoutOfValReturnType denv (v: ValRef) =
1111+
match v.ValReprInfo with
1112+
| None ->
1113+
let _, tau = v.TypeScheme
1114+
let _argtysl, rty = stripFunTy denv.g tau
1115+
layoutReturnType denv SimplifyTypes.typeSimplificationInfo0 rty
1116+
| Some (ValReprInfo(_typars, argInfos, _retInfo)) ->
1117+
let tau = v.TauType
1118+
let _c, rty = GetTopTauTypeInFSharpForm denv.g argInfos tau Range.range0
1119+
layoutReturnType denv SimplifyTypes.typeSimplificationInfo0 rty
11091120

11101121
let layoutAssemblyName _denv (ty: TType) =
11111122
ty.GetAssemblyName()
@@ -2435,7 +2446,9 @@ let prettyLayoutOfValOrMember denv infoReader typarInst v = PrintTastMemberOrVal
24352446

24362447
let prettyLayoutOfValOrMemberNoInst denv infoReader v = PrintTastMemberOrVals.prettyLayoutOfValOrMemberNoInst denv infoReader v
24372448

2438-
let prettyLayoutOfMemberNoInstShort denv v = PrintTastMemberOrVals.prettyLayoutOfMemberNoInstShort denv v
2449+
let prettyLayoutOfMemberNoInstShort denv v = PrintTastMemberOrVals.prettyLayoutOfMemberNoInstShort denv v
2450+
2451+
let layoutOfValReturnType denv v = v |> PrintTypes.layoutOfValReturnType denv
24392452

24402453
let prettyLayoutOfInstAndSig denv x = PrintTypes.prettyLayoutOfInstAndSig denv x
24412454

src/fsharp/NicePrint.fsi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ val prettyLayoutOfValOrMemberNoInst: denv:DisplayEnv -> infoReader:InfoReader ->
109109

110110
val prettyLayoutOfMemberNoInstShort: denv:DisplayEnv -> v:Val -> Layout
111111

112+
val layoutOfValReturnType: denv:DisplayEnv -> v:ValRef -> Layout
113+
112114
val prettyLayoutOfInstAndSig: denv:DisplayEnv -> TyparInst * TTypes * TType -> TyparInst * (TTypes * TType) * (Layout list * Layout) * Layout
113115

114116
val minimalStringsOfTwoTypes: denv:DisplayEnv -> t1:TType -> t2:TType -> string * string * string

src/fsharp/service/ServiceParamInfoLocations.fs

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,19 @@ open FSharp.Compiler.Text.Range
88
open FSharp.Compiler.Syntax
99
open FSharp.Compiler.SyntaxTreeOps
1010

11+
type TupledArgumentLocation = { IsNamedArgument: bool; ArgumentRange: range }
12+
1113
[<Sealed>]
12-
type ParameterLocations(longId: string list, longIdRange: range, openParenLocation: pos, tupleEndLocations: pos list, isThereACloseParen: bool, namedParamNames: string option list) =
14+
type ParameterLocations
15+
(
16+
longId: string list,
17+
longIdRange: range,
18+
openParenLocation: pos,
19+
argRanges: TupledArgumentLocation list,
20+
tupleEndLocations: pos list,
21+
isThereACloseParen: bool,
22+
namedParamNames: string option list
23+
) =
1324

1425
let tupleEndLocations = Array.ofList tupleEndLocations
1526
let namedParamNames = Array.ofList namedParamNames
@@ -30,6 +41,7 @@ type ParameterLocations(longId: string list, longIdRange: range, openParenLocati
3041
member this.TupleEndLocations = tupleEndLocations
3142
member this.IsThereACloseParen = isThereACloseParen
3243
member this.NamedParamNames = namedParamNames
44+
member this.ArgumentLocations = argRanges |> Array.ofList
3345

3446
[<AutoOpen>]
3547
module internal ParameterLocationsImpl =
@@ -54,7 +66,7 @@ module internal ParameterLocationsImpl =
5466
| _ -> None
5567

5668
type FindResult =
57-
| Found of openParen: pos * commasAndCloseParen: (pos * string option) list * hasClosedParen: bool
69+
| Found of openParen: pos * argRanges: TupledArgumentLocation list * commasAndCloseParen: (pos * string option) list * hasClosedParen: bool
5870
| NotFound
5971

6072
let digOutIdentFromStaticArg (StripParenTypes synType) =
@@ -90,7 +102,8 @@ module internal ParameterLocationsImpl =
90102
match inner with
91103
| None ->
92104
if SyntaxTraversal.rangeContainsPosLeftEdgeExclusiveAndRightEdgeInclusive parenRange pos then
93-
Found (parenRange.Start, [(parenRange.End, getNamedParamName synExpr)], rpRangeOpt.IsSome), None
105+
let argRanges = [{ IsNamedArgument = (getNamedParamName synExpr).IsSome; ArgumentRange = synExpr.Range }]
106+
Found (parenRange.Start, argRanges, [(parenRange.End, getNamedParamName synExpr)], rpRangeOpt.IsSome), None
94107
else
95108
NotFound, None
96109
| _ -> NotFound, None
@@ -107,8 +120,12 @@ module internal ParameterLocationsImpl =
107120
match inner with
108121
| None ->
109122
if SyntaxTraversal.rangeContainsPosLeftEdgeExclusiveAndRightEdgeInclusive parenRange pos then
123+
// argRange, isNamed
124+
let argRanges =
125+
synExprList
126+
|> List.map (fun e -> { IsNamedArgument = (getNamedParamName e).IsSome; ArgumentRange = e.Range })
110127
let commasAndCloseParen = ((synExprList, commaRanges@[parenRange]) ||> List.map2 (fun e c -> c.End, getNamedParamName e))
111-
let r = Found (parenRange.Start, commasAndCloseParen, rpRangeOpt.IsSome)
128+
let r = Found (parenRange.Start, argRanges, commasAndCloseParen, rpRangeOpt.IsSome)
112129
r, None
113130
else
114131
NotFound, None
@@ -127,14 +144,14 @@ module internal ParameterLocationsImpl =
127144

128145
| SynExpr.ArbitraryAfterError (_debugStr, range) -> // single argument when e.g. after open paren you hit EOF
129146
if SyntaxTraversal.rangeContainsPosEdgesExclusive range pos then
130-
let r = Found (range.Start, [(range.End, None)], false)
147+
let r = Found (range.Start, [], [(range.End, None)], false)
131148
r, None
132149
else
133150
NotFound, None
134151

135152
| SynExpr.Const (SynConst.Unit, unitRange) ->
136153
if SyntaxTraversal.rangeContainsPosEdgesExclusive unitRange pos then
137-
let r = Found (unitRange.Start, [(unitRange.End, None)], true)
154+
let r = Found (unitRange.Start, [], [(unitRange.End, None)], true)
138155
r, None
139156
else
140157
NotFound, None
@@ -145,7 +162,7 @@ module internal ParameterLocationsImpl =
145162
| None ->
146163
if SyntaxTraversal.rangeContainsPosEdgesExclusive e.Range pos then
147164
// any other expression doesn't start with parens, so if it was the target of an App, then it must be a single argument e.g. "f x"
148-
Found (e.Range.Start, [ (e.Range.End, None) ], false), Some inner
165+
Found (e.Range.Start, [], [ (e.Range.End, None) ], false), Some inner
149166
else
150167
NotFound, Some inner
151168
| _ -> NotFound, Some inner
@@ -157,7 +174,7 @@ module internal ParameterLocationsImpl =
157174
let betweenTheBrackets = mkRange wholem.FileName openm.Start wholem.End
158175
if SyntaxTraversal.rangeContainsPosEdgesExclusive betweenTheBrackets pos && args |> List.forall isStaticArg then
159176
let commasAndCloseParen = [ for c in commas -> c.End ] @ [ wholem.End ]
160-
Some (ParameterLocations(pathOfLid lid, lidm, openm.Start, commasAndCloseParen, closemOpt.IsSome, args |> List.map digOutIdentFromStaticArg))
177+
Some (ParameterLocations(pathOfLid lid, lidm, openm.Start, [], commasAndCloseParen, closemOpt.IsSome, args |> List.map digOutIdentFromStaticArg))
161178
else
162179
None
163180
| _ ->
@@ -173,9 +190,9 @@ module internal ParameterLocationsImpl =
173190
| SynExpr.New (_, synType, synExpr, _) ->
174191
let constrArgsResult, cacheOpt = searchSynArgExpr traverseSynExpr pos synExpr
175192
match constrArgsResult, cacheOpt with
176-
| Found(parenLoc, args, isThereACloseParen), _ ->
193+
| Found(parenLoc, argRanges, commasAndCloseParen, isThereACloseParen), _ ->
177194
let typeName = getTypeName synType
178-
Some (ParameterLocations(typeName, synType.Range, parenLoc, args |> List.map fst, isThereACloseParen, args |> List.map snd))
195+
Some (ParameterLocations(typeName, synType.Range, parenLoc, argRanges, commasAndCloseParen |> List.map fst, isThereACloseParen, commasAndCloseParen |> List.map snd))
179196
| NotFound, Some cache ->
180197
cache
181198
| _ ->
@@ -194,7 +211,7 @@ module internal ParameterLocationsImpl =
194211
if SyntaxTraversal.rangeContainsPosEdgesExclusive typeArgsm pos then
195212
// We found it, dig out ident
196213
match digOutIdentFromFuncExpr synExpr with
197-
| Some(lid, lidRange) -> Some (ParameterLocations(lid, lidRange, op.idRange.Start, [ wholem.End ], false, []))
214+
| Some(lid, lidRange) -> Some (ParameterLocations(lid, lidRange, op.idRange.Start, [], [ wholem.End ], false, []))
198215
| None -> None
199216
else
200217
None
@@ -209,7 +226,7 @@ module internal ParameterLocationsImpl =
209226
// Search the argument
210227
let xResult, cacheOpt = searchSynArgExpr traverseSynExpr pos synExpr2
211228
match xResult, cacheOpt with
212-
| Found(parenLoc, args, isThereACloseParen), _ ->
229+
| Found(parenLoc, argRanges, commasAndCloseParen, isThereACloseParen), _ ->
213230
// We found it, dig out ident
214231
match digOutIdentFromFuncExpr synExpr with
215232
| Some(lid, lidRange) ->
@@ -219,7 +236,7 @@ module internal ParameterLocationsImpl =
219236
// For now, we don't support infix operators.
220237
None
221238
else
222-
Some (ParameterLocations(lid, lidRange, parenLoc, args |> List.map fst, isThereACloseParen, args |> List.map snd))
239+
Some (ParameterLocations(lid, lidRange, parenLoc, argRanges, commasAndCloseParen |> List.map fst, isThereACloseParen, commasAndCloseParen |> List.map snd))
223240
| None -> None
224241
| NotFound, Some cache -> cache
225242
| _ -> traverseSynExpr synExpr2
@@ -232,7 +249,8 @@ module internal ParameterLocationsImpl =
232249
let typeArgsm = mkRange openm.FileName openm.Start wholem.End
233250
if SyntaxTraversal.rangeContainsPosEdgesExclusive typeArgsm pos && tyArgs |> List.forall isStaticArg then
234251
let commasAndCloseParen = [ for c in commas -> c.End ] @ [ wholem.End ]
235-
let r = ParameterLocations(["dummy"], synExpr.Range, openm.Start, commasAndCloseParen, closemOpt.IsSome, tyArgs |> List.map digOutIdentFromStaticArg)
252+
let argRanges = tyArgs |> List.map (fun tyarg -> { IsNamedArgument = false; ArgumentRange = tyarg.Range })
253+
let r = ParameterLocations(["dummy"], synExpr.Range, openm.Start, argRanges, commasAndCloseParen, closemOpt.IsSome, tyArgs |> List.map digOutIdentFromStaticArg)
236254
Some r
237255
else
238256
None
@@ -253,10 +271,10 @@ module internal ParameterLocationsImpl =
253271
// inherit ty(expr) --- treat it like an application (constructor call)
254272
let xResult, _cacheOpt = searchSynArgExpr defaultTraverse pos expr
255273
match xResult with
256-
| Found(parenLoc, args, isThereACloseParen) ->
274+
| Found(parenLoc, argRanges, commasAndCloseParen, isThereACloseParen) ->
257275
// we found it, dig out ident
258276
let typeName = getTypeName ty
259-
let r = ParameterLocations(typeName, ty.Range, parenLoc, args |> List.map fst, isThereACloseParen, args |> List.map snd)
277+
let r = ParameterLocations(typeName, ty.Range, parenLoc, argRanges, commasAndCloseParen |> List.map fst, isThereACloseParen, commasAndCloseParen |> List.map snd)
260278
Some r
261279
| NotFound -> None
262280
else None
@@ -276,7 +294,6 @@ type ParameterLocations with
276294
r
277295
| _ -> None
278296

279-
280297
module internal SynExprAppLocationsImpl =
281298
let rec private searchSynArgExpr traverseSynExpr expr ranges =
282299
match expr with

src/fsharp/service/ServiceParamInfoLocations.fsi

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ namespace FSharp.Compiler.EditorServices
1010
open FSharp.Compiler.Syntax
1111
open FSharp.Compiler.Text
1212

13+
/// Represents the location of a tupled argument, which can optionally be a named argument.
14+
type TupledArgumentLocation = { IsNamedArgument: bool; ArgumentRange: range }
15+
1316
/// Represents the locations relevant to activating parameter info in an IDE
1417
[<Sealed>]
1518
type public ParameterLocations =
@@ -33,7 +36,10 @@ type public ParameterLocations =
3336
member IsThereACloseParen : bool
3437

3538
/// Either empty or a name if an actual named parameter; f(0,a=4,?b=None) would be [|None; Some "a"; Some "b"|]
36-
member NamedParamNames : string option []
39+
member NamedParamNames : string option []
40+
41+
/// Array of locations for each argument, and a flag if that argument is named
42+
member ArgumentLocations: TupledArgumentLocation []
3743

3844
/// Find the information about parameter info locations at a particular source location
3945
static member Find : pos * ParsedInput -> ParameterLocations option

0 commit comments

Comments
 (0)