Skip to content

Commit

Permalink
filter out hints that don't have meaningful names, or that match thei…
Browse files Browse the repository at this point in the history
…r user-provided input texts
  • Loading branch information
baronfel committed Apr 27, 2022
1 parent 331c71a commit 3c654ae
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 23 deletions.
86 changes: 69 additions & 17 deletions src/FsAutoComplete.Core/InlayHints.fs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type HintKind =

type Hint =
{ Text: string
InsertText: string option
Pos: Position
Kind: HintKind }

Expand Down Expand Up @@ -90,14 +91,59 @@ type FSharp.Compiler.CodeAnalysis.FSharpParseFileResults with
let result = SyntaxTraversal.Traverse(pos, x.ParseTree, visitor)
result.IsSome

let getFirstPositionAfterParen (str: string) startPos =
let private getFirstPositionAfterParen (str: string) startPos =
match str with
| null -> -1
| str when startPos > str.Length -> -1
| str -> str.IndexOf('(') + 1

let private maxHintLength = 30

let inline private truncated (s: string) =
if s.Length > maxHintLength then
s.Substring(0, maxHintLength) + "..."
else
s

let private isNotWellKnownName =
let names = Set.ofList [
"mapping"
"format"
"value"
"x"
]

fun (p: FSharpParameter) ->
match p.Name with
| None -> true
| Some n -> not (Set.contains n names)

let inline hasName (p: FSharpParameter) =
not (String.IsNullOrEmpty p.DisplayName)
&& p.DisplayName <> "````"

let inline isMeaningfulName (p: FSharpParameter) =
p.DisplayName.Length > 2

let inline doesNotMatchArgumentText (parameterName: string) (userArgumentText: string) =
parameterName <> userArgumentText
&& not (userArgumentText.StartsWith parameterName)

/// </summary>
/// We filter out parameters that generate lots of noise in hints.
/// * parameter has a name
/// * parameter is one of a set of 'known' names that clutter (like printfn formats)
/// * parameter has length > 2
/// * parameter does not match (or is an extension of) the user-entered text
/// </summary>
let shouldCreateHint (p: FSharpParameter) (matchingArgumentText: string) =
hasName p
&& isNotWellKnownName p
&& isMeaningfulName p
&& doesNotMatchArgumentText p.DisplayName matchingArgumentText

let provideHints (text: NamedText, p: ParseAndCheckResults, range: Range) : Async<Hint []> =
async {
asyncResult {
let parseFileResults, checkFileResults = p.GetParseResults, p.GetCheckResults
let! cancellationToken = Async.CancellationToken

Expand Down Expand Up @@ -125,19 +171,18 @@ let provideHints (text: NamedText, p: ParseAndCheckResults, range: Range) : Asyn
&& 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 layout =
": "
+ funcOrValue.ReturnParameter.Type.Format symbolUse.DisplayContext

let layout = $": {truncated(funcOrValue.ReturnParameter.Type.Format symbolUse.DisplayContext)}"
let insertText = $": {funcOrValue.ReturnParameter.Type.Format symbolUse.DisplayContext}"

let hint =
{ Text = layout
InsertText = Some insertText
Pos = symbolUse.Range.End
Kind = Type }

Expand All @@ -163,14 +208,16 @@ let provideHints (text: NamedText, p: ParseAndCheckResults, range: Range) : Asyn
else
for idx = 0 to minLength - 1 do
let appliedArgRange = appliedArgRanges.[idx]
let definitionArgName = definitionArgs.[idx].DisplayName
let! appliedArgText = text[appliedArgRange]
let definitionArg = definitionArgs.[idx]
let definitionArgName = definitionArg.DisplayName

if
not (String.IsNullOrWhiteSpace(definitionArgName))
&& definitionArgName <> "````"
shouldCreateHint definitionArg appliedArgText
then
let hint =
{ Text = definitionArgName + " ="
{ Text = $"{truncated definitionArgName} ="
InsertText = None
Pos = appliedArgRange.Start
Kind = Parameter }

Expand Down Expand Up @@ -201,9 +248,11 @@ let provideHints (text: NamedText, p: ParseAndCheckResults, range: Range) : Asyn
|> Array.ofSeq // TODO: need ArgumentLocations to be surfaced

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 paramLocationInfo = tupledParamInfos.ArgumentLocations.[idx]
let param = parameters.[idx]
let paramName = param.DisplayName

// if shouldCreateHint param && paramLocationInfo.IsNamedArgument then
// let hint = { Text = paramName + " ="; Pos = paramLocationInfo.ArgumentRange.Start; Kind = Parameter }
// parameterHints.Add(hint)
()
Expand All @@ -219,11 +268,13 @@ let provideHints (text: NamedText, p: ParseAndCheckResults, range: Range) : Asyn

for idx = 0 to appliedArgRanges.Length - 1 do
let appliedArgRange = appliedArgRanges.[idx]
let definitionArgName = definitionArgs.[idx].DisplayName
let! appliedArgText = text[appliedArgRange]
let definitionArg = definitionArgs.[idx]

if not (String.IsNullOrWhiteSpace(definitionArgName)) then
if shouldCreateHint definitionArg appliedArgText then
let hint =
{ Text = definitionArgName + " ="
{ Text = $"{truncated definitionArg.DisplayName} ="
InsertText = None
Pos = appliedArgRange.Start
Kind = Parameter }

Expand All @@ -235,3 +286,4 @@ let provideHints (text: NamedText, p: ParseAndCheckResults, range: Range) : Asyn

return typeHints.AddRange(parameterHints).ToArray()
}
|> AsyncResult.foldResult id (fun _ -> [||])
8 changes: 5 additions & 3 deletions src/FsAutoComplete/FsAutoComplete.Lsp.fs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type InlayHintKind = Type | Parameter

type LSPInlayHint = {
Text : string
InsertText: string option
Pos : Types.Position
Kind : InlayHintKind
}
Expand Down Expand Up @@ -844,9 +845,9 @@ type FSharpLspServer(backgroundServiceEnabled: bool, state: State, lspClient: FS
let getAbstractClassStubReplacements () = abstractClassStubReplacements ()

codeFixes <-
[|
Run.ifEnabled
(fun _ -> config.UnusedOpensAnalyzer)
[|
Run.ifEnabled
(fun _ -> config.UnusedOpensAnalyzer)
(RemoveUnusedOpens.fix getFileLines)
Run.ifEnabled
(fun _ -> config.ResolveNamespaces)
Expand Down Expand Up @@ -2706,6 +2707,7 @@ type FSharpLspServer(backgroundServiceEnabled: bool, state: State, lspClient: FS
hints
|> Array.map (fun h -> {
Text = h.Text
InsertText = h.InsertText
Pos = fcsPosToLsp h.Pos
Kind = mapHintKind h.Kind
})
Expand Down
6 changes: 3 additions & 3 deletions src/FsAutoComplete/LspHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -523,9 +523,9 @@ type PlainNotification= { Content: string }

/// Notification when a `TextDocument` is completely analyzed:
/// F# Compiler checked file & all Analyzers (like `UnusedOpensAnalyzer`) are done.
///
///
/// Used to signal all Diagnostics for this `TextDocument` are collected and sent.
/// -> For tests to get all Diagnostics of `TextDocument`
/// -> For tests to get all Diagnostics of `TextDocument`
type DocumentAnalyzedNotification = {
TextDocument: VersionedTextDocumentIdentifier
}
Expand Down Expand Up @@ -841,7 +841,7 @@ let encodeSemanticHighlightRanges (rangesAndHighlights: (struct(Ionide.LanguageS

match rangesAndHighlights.Length with
| 0 -> None
/// only 1 entry, so compute the line from the 0 position
// only 1 entry, so compute the line from the 0 position
| 1 ->
Some (
computeLine fileStart rangesAndHighlights.[0]
Expand Down

0 comments on commit 3c654ae

Please sign in to comment.