From a1af98341bc50629bdeae4faa03c9e26cd56644e Mon Sep 17 00:00:00 2001 From: BooksBaum <15612932+Booksbaum@users.noreply.github.com> Date: Thu, 28 Apr 2022 21:26:40 +0200 Subject: [PATCH 01/12] Extract hint creation into own functions Add additional Test function to use positions marked in source --- src/FsAutoComplete.Core/InlayHints.fs | 110 ++++++++++-------- .../InlayHintTests.fs | 54 +++++++-- 2 files changed, 103 insertions(+), 61 deletions(-) diff --git a/src/FsAutoComplete.Core/InlayHints.fs b/src/FsAutoComplete.Core/InlayHints.fs index 32c987be8..1f717b254 100644 --- a/src/FsAutoComplete.Core/InlayHints.fs +++ b/src/FsAutoComplete.Core/InlayHints.fs @@ -10,7 +10,6 @@ open FSharp.UMX open System.Linq open System.Collections.Immutable open FSharp.Compiler.CodeAnalysis -open System.Text type HintKind = | Parameter @@ -51,7 +50,7 @@ let private getArgumentsFor (state: FsAutoComplete.State, p: ParseAndCheckResult let private isSignatureFile (f: string) = System.IO.Path.GetExtension(UMX.untag f) = ".fsi" -type FSharp.Compiler.CodeAnalysis.FSharpParseFileResults with +type private FSharp.Compiler.CodeAnalysis.FSharpParseFileResults with // duplicates + extends the logic in FCS to match bindings of the form `let x: int = 12` // so that they are considered logically the same as a 'typed' SynPat member x.IsTypeAnnotationGivenAtPositionPatched pos = @@ -105,29 +104,55 @@ let truncated (s: string) = else s -let private isNotWellKnownName = - let names = Set.ofList [ - "mapping" - "format" - "value" - "x" - ] +let private createParamHint + (range: Range) + (paramName: string) + = + let format p = p + " =" + { + Text = format (truncated paramName) + InsertText = None + Pos = range.Start + Kind = Parameter + } +let private createTypeHint + (range: Range) + (ty: FSharpType) + (displayContext: FSharpDisplayContext) + = + let ty = ty.Format displayContext + let format ty = ": " + ty + { + Text = format (truncated ty) + InsertText = Some (format ty) + Pos = range.End + Kind = Type + } - fun (p: FSharpParameter) -> - match p.Name with - | None -> true - | Some n -> not (Set.contains n names) +module private ShouldCreate = + let private isNotWellKnownName = + let names = Set.ofList [ + "mapping" + "format" + "value" + "x" + ] -let inline hasName (p: FSharpParameter) = - not (String.IsNullOrEmpty p.DisplayName) - && p.DisplayName <> "````" + fun (p: FSharpParameter) -> + match p.Name with + | None -> true + | Some n -> not (Set.contains n names) -let inline isMeaningfulName (p: FSharpParameter) = - p.DisplayName.Length > 2 + let inline private hasName (p: FSharpParameter) = + not (String.IsNullOrEmpty p.DisplayName) + && p.DisplayName <> "````" -let inline doesNotMatchArgumentText (parameterName: string) (userArgumentText: string) = - parameterName <> userArgumentText - && not (userArgumentText.StartsWith parameterName) + let inline private isMeaningfulName (p: FSharpParameter) = + p.DisplayName.Length > 2 + + let inline private doesNotMatchArgumentText (parameterName: string) (userArgumentText: string) = + parameterName <> userArgumentText + && not (userArgumentText.StartsWith parameterName) /// /// We filter out parameters that generate lots of noise in hints. @@ -136,11 +161,15 @@ let inline doesNotMatchArgumentText (parameterName: string) (userArgumentText: s /// * parameter has length > 2 /// * parameter does not match (or is an extension of) the user-entered text /// -let shouldCreateHint (p: FSharpParameter) (matchingArgumentText: string) = - hasName p - && isNotWellKnownName p - && isMeaningfulName p - && doesNotMatchArgumentText p.DisplayName matchingArgumentText + let paramHint + (func: FSharpMemberOrFunctionOrValue) + (p: FSharpParameter) + (argumentText: string) + = + hasName p + && isNotWellKnownName p + && isMeaningfulName p + && doesNotMatchArgumentText p.DisplayName argumentText let provideHints (text: NamedText, p: ParseAndCheckResults, range: Range) : Async = @@ -177,16 +206,7 @@ let provideHints (text: NamedText, p: ParseAndCheckResults, range: Range) : Asyn | :? FSharpMemberOrFunctionOrValue as funcOrValue when isValidForTypeHint funcOrValue symbolUse -> - - 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 } - + let hint = createTypeHint symbolUse.Range funcOrValue.ReturnParameter.Type symbolUse.DisplayContext typeHints.Add(hint) | :? FSharpMemberOrFunctionOrValue as func when func.IsFunction && not symbolUse.IsFromDefinition -> @@ -214,14 +234,9 @@ let provideHints (text: NamedText, p: ParseAndCheckResults, range: Range) : Asyn let definitionArgName = definitionArg.DisplayName if - shouldCreateHint definitionArg appliedArgText + ShouldCreate.paramHint func definitionArg appliedArgText then - let hint = - { Text = $"{truncated definitionArgName} =" - InsertText = None - Pos = appliedArgRange.Start - Kind = Parameter } - + let hint = createParamHint appliedArgRange definitionArgName parameterHints.Add(hint) | :? FSharpMemberOrFunctionOrValue as methodOrConstructor when methodOrConstructor.IsConstructor -> // TODO: support methods when this API comes into FCS @@ -272,13 +287,8 @@ let provideHints (text: NamedText, p: ParseAndCheckResults, range: Range) : Asyn let! appliedArgText = text[appliedArgRange] let definitionArg = definitionArgs.[idx] - if shouldCreateHint definitionArg appliedArgText then - let hint = - { Text = $"{truncated definitionArg.DisplayName} =" - InsertText = None - Pos = appliedArgRange.Start - Kind = Parameter } - + if ShouldCreate.paramHint methodOrConstructor definitionArg appliedArgText then + let hint = createParamHint appliedArgRange definitionArg.DisplayName parameterHints.Add(hint) | _ -> () diff --git a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs index 5fc6ab4b0..7ed7d6cc8 100644 --- a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs @@ -19,7 +19,7 @@ module InlayHints = open Utils.TextEdit open FSharpx.Control - let from (text, (line, char), kind) : LSPInlayHint = + let private at (text, pos, kind) : LSPInlayHint = { Text = match kind with | InlayHintKind.Type -> ": " + InlayHints.truncated text @@ -30,16 +30,18 @@ module InlayHints = match kind with | InlayHintKind.Type -> Some(": " + text) | InlayHintKind.Parameter -> None - Pos = { Line = line; Character = char } + Pos = pos Kind = kind } + let private from (text, (line, char), kind) = + at (text, { Line=line; Character=char}, kind) - let check (server: Async) (documentText: string) (expectedHints: _ list) = - async { - let (range, text) = - documentText - |> Text.trimTripleQuotation - |> Cursor.assertExtractRange + let private check' + (server: CachedServer) + (text: string) + (range: Range) + (expected: LSPInlayHint array) + = async { let! (doc, diags) = server |> Server.createUntitledDocument text use doc = doc // ensure doc gets closed (disposed) after test @@ -48,13 +50,43 @@ module InlayHints = | diags -> failtest $"Should not have had check errors, but instead had %A{diags}" let! actual = Document.inlayHintsAt range doc - let expected = expectedHints |> List.map from |> Array.ofList Expect.equal actual expected "Expected the given set of hints" } + let check (server: CachedServer) (documentText: string) (expectedHints: _ list) = + let (range, text) = + documentText + |> Text.trimTripleQuotation + |> Cursor.assertExtractRange + let expected = expectedHints |> List.map from |> Array.ofList + check' server text range expected + + let private extractCursorsInsideRange (text: string) = + let (text, poss) = + text + |> Text.trimTripleQuotation + |> Cursors.extract + let range = + { Start = poss |> List.head; End = poss |> List.last } + let poss = + let count = poss |> List.length + poss[1..(count-2)] + + (text, range, poss) + + let checkRange (server: CachedServer) (documentText: string) (expectedHints: _ list) = + let (text, range, poss) = documentText |> extractCursorsInsideRange + let expected = + List.zip poss expectedHints + |> List.map (fun (pos, (name, kind)) -> at (name, pos, kind)) + |> List.toArray + check' server text range expected + +let param (name: string) = (name, InlayHintKind.Parameter) +let ty (name: string) = (name, InlayHintKind.Type) let tests state = - serverTestList (nameof Core.InlayHints) state defaultConfigDto None (fun server -> - [ testCaseAsync "let-bound function parameter type hints" + serverTestList (nameof Core.InlayHints) state defaultConfigDto None (fun server -> [ + testCaseAsync "let-bound function parameter type hints" <| InlayHints.check server """ From 21f3a4ac2a9012979102d03f92c2a1da5c2033c3 Mon Sep 17 00:00:00 2001 From: BooksBaum <15612932+Booksbaum@users.noreply.github.com> Date: Fri, 29 Apr 2022 11:51:34 +0200 Subject: [PATCH 02/12] Better error message when cursors & hints don't match Prev: error when collecting expecto tests -> outside of tests Now: error when running corresponding test -> inside a test --- test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs index 7ed7d6cc8..c24adb758 100644 --- a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs @@ -52,13 +52,14 @@ module InlayHints = let! actual = Document.inlayHintsAt range doc Expect.equal actual expected "Expected the given set of hints" } - let check (server: CachedServer) (documentText: string) (expectedHints: _ list) = + let check (server: CachedServer) (documentText: string) (expectedHints: _ list) = async { let (range, text) = documentText |> Text.trimTripleQuotation |> Cursor.assertExtractRange let expected = expectedHints |> List.map from |> Array.ofList - check' server text range expected + do! check' server text range expected + } let private extractCursorsInsideRange (text: string) = let (text, poss) = @@ -73,13 +74,15 @@ module InlayHints = (text, range, poss) - let checkRange (server: CachedServer) (documentText: string) (expectedHints: _ list) = + let checkRange (server: CachedServer) (documentText: string) (expectedHints: _ list) = async { let (text, range, poss) = documentText |> extractCursorsInsideRange + Expect.equal (poss |> List.length) (expectedHints |> List.length) $"Expected Hints & position cursors to match, but there were {expectedHints |> List.length} expected hints and {poss |> List.length} position cursors" let expected = List.zip poss expectedHints |> List.map (fun (pos, (name, kind)) -> at (name, pos, kind)) |> List.toArray - check' server text range expected + do! check' server text range expected + } let param (name: string) = (name, InlayHintKind.Parameter) let ty (name: string) = (name, InlayHintKind.Type) From dcf9d7481e22384d60a64e52dfb5a48fb2efb907 Mon Sep 17 00:00:00 2001 From: BooksBaum <15612932+Booksbaum@users.noreply.github.com> Date: Sat, 30 Apr 2022 20:32:44 +0200 Subject: [PATCH 03/12] Hide parameter hint based on pre/postfix of param & arg --- src/FsAutoComplete.Core/InlayHints.fs | 100 ++++++- .../InlayHintTests.fs | 249 ++++++++++++++++-- 2 files changed, 330 insertions(+), 19 deletions(-) diff --git a/src/FsAutoComplete.Core/InlayHints.fs b/src/FsAutoComplete.Core/InlayHints.fs index 1f717b254..52de493b1 100644 --- a/src/FsAutoComplete.Core/InlayHints.fs +++ b/src/FsAutoComplete.Core/InlayHints.fs @@ -150,6 +150,103 @@ module private ShouldCreate = let inline private isMeaningfulName (p: FSharpParameter) = p.DisplayName.Length > 2 + /// Doesn't consider lower/upper cases: + /// * `areSame "foo" "FOO" = true` + /// * `areSame "Foo" "Foo" = true` + let inline private areSame (a: ReadOnlySpan) (b: ReadOnlySpan) = + a.Equals(b, StringComparison.OrdinalIgnoreCase) + /// Boundary checks: + /// * word boundary (-> upper case letter) + /// `"foo" |> isPrefixOf "fooBar"` + /// Doesn't consider capitalization, except for word boundary after prefix: + /// * `foo` prefix of `fooBar` + /// * `foo` not prefix of `foobar` + let inline private isPrefixOf (root: ReadOnlySpan) (check: ReadOnlySpan) = + root.StartsWith(check, StringComparison.OrdinalIgnoreCase) + && + ( + // same + root.Length <= check.Length + || + // rest must start with upper case -> new word + Char.IsUpper root[check.Length] + ) + /// Boundary checks: + /// * word boundary (-> upper case letter) + /// `"bar" |> isPostifxOf "fooBar"` + /// * `.` boundary (-> property access) + /// `"bar" |> isPostifxOf "data.bar"` + /// + /// Doesn't consider capitalization, except for word boundary at start of postfix: + /// * `bar` postfix of `fooBar` + /// * `bar` not postfix of `foobar` + let inline private isPostfixOf (root: ReadOnlySpan) (check: ReadOnlySpan) = + root.EndsWith(check, StringComparison.OrdinalIgnoreCase) + && + ( + root.Length <= check.Length + || + // postfix must start with upper case -> word boundary + Char.IsUpper root[root.Length - check.Length] + ) + + let inline private removeLeadingUnderscore (name: ReadOnlySpan) = + name.TrimStart '_' + let inline private removeTrailingTick (name: ReadOnlySpan) = + name.TrimEnd '\'' + let inline private extractLastIdentifier (name: ReadOnlySpan) = + // exclude backticks for now: might contain `.` -> difficult to split + if name.StartsWith "``" || name.EndsWith "``" then + name + else + match name.LastIndexOf '.' with + | -1 -> name + | i -> name.Slice(i+1) + /// Note: when in parens: might not be an identifier, but expression! + /// + /// Note: might result in invalid expression (because no matching parens `string (2)` -> `string (2`) + let inline private trimParensAndSpace (name: ReadOnlySpan) = + name.TrimStart("( ").TrimEnd(" )") + + /// Note: including `.` + let inline private isLongIdentifier (name: ReadOnlySpan) = + // name |> Seq.forall PrettyNaming.IsLongIdentifierPartCharacter + let mutable valid = true + let mutable i = 0 + while valid && i < name.Length do + if PrettyNaming.IsLongIdentifierPartCharacter name[i] then + i <- i + 1 + else + valid <- false + valid + + + let private areSimilar (paramName: string) (argumentText: string) = + // no pipe with span ... + let paramName = removeTrailingTick (removeLeadingUnderscore (paramName.AsSpan())) + let argumentName = + let argumentText = argumentText.AsSpan() + let argTextNoParens = trimParensAndSpace argumentText + + if isLongIdentifier argTextNoParens then + removeTrailingTick (removeLeadingUnderscore (extractLastIdentifier argTextNoParens)) + else + //todo: expression -> early out? or further processing? special processing? + argumentText + + //todo: all useful? + + // // covered by each isPre/PostfixOf + // areSame paramName argumentName + // || + isPrefixOf argumentName paramName + || + isPostfixOf argumentName paramName + || + isPrefixOf paramName argumentName + || + isPostfixOf paramName argumentName + let inline private doesNotMatchArgumentText (parameterName: string) (userArgumentText: string) = parameterName <> userArgumentText && not (userArgumentText.StartsWith parameterName) @@ -169,7 +266,8 @@ module private ShouldCreate = hasName p && isNotWellKnownName p && isMeaningfulName p - && doesNotMatchArgumentText p.DisplayName argumentText + // && doesNotMatchArgumentText p.DisplayName argumentText + && (not (areSimilar p.DisplayName argumentText)) let provideHints (text: NamedText, p: ParseAndCheckResults, range: Range) : Async = diff --git a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs index c24adb758..902d2161d 100644 --- a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs @@ -89,13 +89,14 @@ let ty (name: string) = (name, InlayHintKind.Type) let tests state = serverTestList (nameof Core.InlayHints) state defaultConfigDto None (fun server -> [ + testList "type hint" [ testCaseAsync "let-bound function parameter type hints" - <| InlayHints.check - server - """ - $0let tryFindFile p = p + "hi"$0 - """ - [ "string", (0, 17), InlayHintKind.Type ] + <| InlayHints.check + server + """ + $0let tryFindFile p = p + "hi"$0 + """ + [ "string", (0, 17), InlayHintKind.Type ] testCaseAsync "value let binding type hint" <| InlayHints.check @@ -105,6 +106,22 @@ let tests state = """ [ "string", (0, 5), InlayHintKind.Type ] + testCaseAsync "no type hint for an explicitly-typed binding" + <| InlayHints.check server """$0let s: string = "hi"$0""" [] + + testCaseAsync "type hints are truncated to 30 characters" + <| InlayHints.check + server + """ + $0let t = Some (Some (Some (Some (Some (Some (Some (Some (Some (Some (Some (Some (Some (Some (Some ()))))))))))))))$0 + """ + [ "unit option option option option option option option option option option option option option option option", + (0, 5), + InlayHintKind.Type ] + ] + + testList "parameter hint" [ + testCaseAsync "parameter names aren't yet implemented, will fail when we update FCS" <| InlayHints.check server """$0 System.Environment.GetEnvironmentVariable "Blah" |> ignore$0""" [] @@ -130,9 +147,6 @@ let tests state = """ [] - testCaseAsync "no type hint for an explicitly-typed binding" - <| InlayHints.check server """$0let s: string = "hi"$0""" [] - testCaseAsync "no hint for a function with a short parameter name" <| InlayHints.check server @@ -143,12 +157,211 @@ let tests state = """ [] - testCaseAsync "type hints are truncated to 30 characters" - <| InlayHints.check - server - """ - $0let t = Some (Some (Some (Some (Some (Some (Some (Some (Some (Some (Some (Some (Some (Some (Some ()))))))))))))))$0 - """ - [ "unit option option option option option option option option option option option option option option option", - (0, 5), - InlayHintKind.Type ] ]) + testCaseAsync "show: param & variable have different names" <| + InlayHints.checkRange server + """ + let f beta = () + let alpha = 42 + + $0f $0alpha$0 + """ + [ param "beta" ] + + testCaseAsync "hide: param & variable have same name" <| + InlayHints.checkRange server + """ + let f alpha = () + let alpha = 42 + + $0f alpha$0 + """ + [ ] + testCaseAsync "hide: variable prefix of param" <| + InlayHints.checkRange server + """ + let f specialNumber = () + let special = 2 + + $0f special$0 + """ + [ ] + testCaseAsync "hide: variable postfix of param" <| + InlayHints.checkRange server + """ + let f specialNumber = () + let number = 2 + + $0f number$0 + """ + [ ] + //todo: or hide? + testCaseAsync "show: variable infix of param" <| + InlayHints.checkRange server + """ + let f extraSpecialNumber = () + let special = 2 + + $0f $0special$0 + """ + [ param "extraSpecialNumber" ] + //todo: or hide? + testCaseAsync "show: variable prefix of param, but no word boundary" <| + InlayHints.checkRange server + """ + let f specialnumber = () + let special = 2 + + $0f $0special$0 + """ + [ param "specialnumber" ] + //todo: or hide? + testCaseAsync "show: variable postfix of param, but no word boundary" <| + InlayHints.checkRange server + """ + let f specialnumber = () + let number = 2 + + $0f $0number$0 + """ + [ param "specialnumber" ] + + testCaseAsync "hide: arg is prefix of param with leading _" <| + InlayHints.checkRange server + """ + let f _specialNumber = () + let special = 2 + + $0f special$0 + """ + [] + testCaseAsync "hide: arg is postfix of param with trailing '" <| + InlayHints.checkRange server + """ + let f specialNumber' = () + let number = 2 + + $0f number$0 + """ + [] + testCaseAsync "hide: arg is prefix of param with trailing ' in arg" <| + InlayHints.checkRange server + """ + let f specialNumber = () + let special' = 2 + + $0f special'$0 + """ + [] + + testCaseAsync "hide: param prefix of arg" <| + InlayHints.checkRange server + """ + let f special = () + let specialNumber = 2 + + $0f specialNumber$0 + """ + [] + testCaseAsync "hide: param postfix of arg" <| + InlayHints.checkRange server + """ + let f number = () + let specialNumber = 2 + + $0f specialNumber$0 + """ + [] + + testCaseAsync "hide: arg is field access with same name as param (upper case start)" <| + InlayHints.checkRange server + """ + type Data = { + Number: int + } + let f number = () + let data: Data = { Number = 2 } + + $0f data.Number$0 + """ + [] + testCaseAsync "hide: arg is field access with same name as param (lower case start)" <| + InlayHints.checkRange server + """ + type Data = { + number: int + } + let f number = () + let data: Data = { number = 2 } + + $0f data.number$0 + """ + [] + testCaseAsync "hide: arg is field access prefix of param (upper case start)" <| + InlayHints.checkRange server + """ + type Data = { + Special: int + } + let f specialNumber = () + let data: Data = { Special = 2 } + + $0f data.Special$0 + """ + [] + testCaseAsync "hide: arg is field access, param is prefix of arg" <| + InlayHints.checkRange server + """ + type Data = { + SpecialNumber: int + } + let f special = () + let data: Data = { SpecialNumber = 2 } + + $0f data.SpecialNumber$0 + """ + [] + + testCaseAsync "hide: arg in parens same as param" <| + InlayHints.checkRange server + """ + let f alpha = () + let alpha = 42 + + $0f (alpha)$0 + """ + [ ] + testCaseAsync "hide: arg in parens and spaces same as param" <| + InlayHints.checkRange server + """ + let f alpha = () + let alpha = 42 + + $0f ( alpha )$0 + """ + [ ] + //todo: or hide? based on: what is last? but then (`alpha <| 1`, `1 |> alpha 2`, etc?) -> too complex to detect + testCaseAsync "show: expr including param name in parens" <| + InlayHints.checkRange server + """ + let f alpha = () + let alpha x = x + 3 + + $0f $0(1 |> alpha)$0 + """ + [ param "alpha" ] + + //todo: inspect most left/right identifier? extract function name? look for left of `.`? use ast? + testCaseAsync "show: any expression" <| + InlayHints.checkRange server + """ + let f (alpha, beta, gamma) = () + let alpha = 1 + let beta = 2 + let gamma = 2 + + $0f ($0string alpha, $0beta.ToString(), $0gamma |> string)$0 + """ + [ param "alpha"; param "beta"; param "gamma" ] + + ] + ]) From 1e93ea2e0dff3bd77c001236b7dc9c1c423ca5f7 Mon Sep 17 00:00:00 2001 From: BooksBaum <15612932+Booksbaum@users.noreply.github.com> Date: Fri, 29 Apr 2022 15:28:40 +0200 Subject: [PATCH 04/12] Hide Param HInt for operators --- src/FsAutoComplete.Core/InlayHints.fs | 21 +++++++++++-------- .../InlayHintTests.fs | 20 ++++++++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/FsAutoComplete.Core/InlayHints.fs b/src/FsAutoComplete.Core/InlayHints.fs index 52de493b1..7f4fef3f8 100644 --- a/src/FsAutoComplete.Core/InlayHints.fs +++ b/src/FsAutoComplete.Core/InlayHints.fs @@ -150,6 +150,9 @@ module private ShouldCreate = let inline private isMeaningfulName (p: FSharpParameter) = p.DisplayName.Length > 2 + let inline private isOperator (func: FSharpMemberOrFunctionOrValue) = + func.CompiledName.StartsWith "op_" + /// Doesn't consider lower/upper cases: /// * `areSame "foo" "FOO" = true` /// * `areSame "Foo" "Foo" = true` @@ -220,7 +223,6 @@ module private ShouldCreate = valid <- false valid - let private areSimilar (paramName: string) (argumentText: string) = // no pipe with span ... let paramName = removeTrailingTick (removeLeadingUnderscore (paramName.AsSpan())) @@ -229,7 +231,7 @@ module private ShouldCreate = let argTextNoParens = trimParensAndSpace argumentText if isLongIdentifier argTextNoParens then - removeTrailingTick (removeLeadingUnderscore (extractLastIdentifier argTextNoParens)) + removeTrailingTick (extractLastIdentifier argTextNoParens) else //todo: expression -> early out? or further processing? special processing? argumentText @@ -251,13 +253,13 @@ module private ShouldCreate = parameterName <> userArgumentText && not (userArgumentText.StartsWith parameterName) -/// -/// 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 -/// + /// + /// 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 + /// let paramHint (func: FSharpMemberOrFunctionOrValue) (p: FSharpParameter) @@ -266,6 +268,7 @@ module private ShouldCreate = hasName p && isNotWellKnownName p && isMeaningfulName p + && (not (isOperator func)) // && doesNotMatchArgumentText p.DisplayName argumentText && (not (areSimilar p.DisplayName argumentText)) diff --git a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs index 902d2161d..6bc9955ae 100644 --- a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs @@ -362,6 +362,26 @@ let tests state = $0f ($0string alpha, $0beta.ToString(), $0gamma |> string)$0 """ [ param "alpha"; param "beta"; param "gamma" ] + + testCaseAsync "hide: unary operator" <| + InlayHints.checkRange server + """ + let (~+.) listWithNumbers = List.map ((+) 1) listWithNumbers + let data = [1..5] + + $0+. data$0 + """ + [] + testCaseAsync "hide: binary operator" <| + InlayHints.checkRange server + """ + let (+.) listWithNumbers numberToAdd = List.map ((+) numberToAdd) listWithNumbers + let data = [1..5] + + $0data +. 5$0 + """ + [] + ] ]) From d741e3a206daf7039275219b2700da01295c19c2 Mon Sep 17 00:00:00 2001 From: BooksBaum <15612932+Booksbaum@users.noreply.github.com> Date: Sat, 30 Apr 2022 11:17:05 +0200 Subject: [PATCH 05/12] Don't show param hint when param name postfix of param name Example: func: `validateRange`; param: `range` --- src/FsAutoComplete.Core/InlayHints.fs | 10 ++++++++++ test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs | 10 +++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/FsAutoComplete.Core/InlayHints.fs b/src/FsAutoComplete.Core/InlayHints.fs index 7f4fef3f8..b2ec23f10 100644 --- a/src/FsAutoComplete.Core/InlayHints.fs +++ b/src/FsAutoComplete.Core/InlayHints.fs @@ -253,6 +253,15 @@ module private ShouldCreate = parameterName <> userArgumentText && not (userArgumentText.StartsWith parameterName) + let private isParamNamePostfixOfFuncName + (func: FSharpMemberOrFunctionOrValue) + (paramName: string) + = + let funcName = func.DisplayName.AsSpan() + let paramName = removeLeadingUnderscore (paramName.AsSpan()) + + isPostfixOf funcName paramName + /// /// We filter out parameters that generate lots of noise in hints. /// * parameter has a name @@ -271,6 +280,7 @@ module private ShouldCreate = && (not (isOperator func)) // && doesNotMatchArgumentText p.DisplayName argumentText && (not (areSimilar p.DisplayName argumentText)) + && (not (isParamNamePostfixOfFuncName func p.DisplayName)) let provideHints (text: NamedText, p: ParseAndCheckResults, range: Range) : Async = diff --git a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs index 6bc9955ae..ff8b500fc 100644 --- a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs @@ -382,6 +382,14 @@ let tests state = """ [] - + testCaseAsync "hide: func name ends with param name" <| + InlayHints.checkRange server + """ + let validateRange range = () + let data = 42 + + $0validateRange data$0 + """ + [] ] ]) From 183b8a97a1a34c859b06059211b525e6721f1d15 Mon Sep 17 00:00:00 2001 From: BooksBaum <15612932+Booksbaum@users.noreply.github.com> Date: Sat, 30 Apr 2022 12:21:24 +0200 Subject: [PATCH 06/12] More practical example names in tests --- .../InlayHintTests.fs | 98 +++++++++---------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs index ff8b500fc..147f7cb24 100644 --- a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs @@ -179,96 +179,96 @@ let tests state = testCaseAsync "hide: variable prefix of param" <| InlayHints.checkRange server """ - let f specialNumber = () - let special = 2 + let f rangeCoveringExpr = () + let range = 2 - $0f special$0 + $0f range$0 """ [ ] testCaseAsync "hide: variable postfix of param" <| InlayHints.checkRange server """ - let f specialNumber = () - let number = 2 + let f exactRange = () + let range = 2 - $0f number$0 + $0f range$0 """ [ ] //todo: or hide? testCaseAsync "show: variable infix of param" <| InlayHints.checkRange server """ - let f extraSpecialNumber = () - let special = 2 + let f exactRangeCoveringExpr = () + let range = 2 - $0f $0special$0 + $0f $0range$0 """ - [ param "extraSpecialNumber" ] + [ param "exactRangeCoveringExpr" ] //todo: or hide? testCaseAsync "show: variable prefix of param, but no word boundary" <| InlayHints.checkRange server """ - let f specialnumber = () - let special = 2 + let f rangecover = () + let range = 2 - $0f $0special$0 + $0f $0range$0 """ - [ param "specialnumber" ] + [ param "rangecover" ] //todo: or hide? testCaseAsync "show: variable postfix of param, but no word boundary" <| InlayHints.checkRange server """ - let f specialnumber = () - let number = 2 + let f exactrange = () + let range = 2 - $0f $0number$0 + $0f $0range$0 """ - [ param "specialnumber" ] + [ param "exactrange" ] testCaseAsync "hide: arg is prefix of param with leading _" <| InlayHints.checkRange server """ - let f _specialNumber = () - let special = 2 + let f _rangeCoveringExpr = () + let range = 2 - $0f special$0 + $0f range$0 """ [] testCaseAsync "hide: arg is postfix of param with trailing '" <| InlayHints.checkRange server """ - let f specialNumber' = () - let number = 2 + let f exactRange' = () + let range = 2 - $0f number$0 + $0f range$0 """ [] testCaseAsync "hide: arg is prefix of param with trailing ' in arg" <| InlayHints.checkRange server """ - let f specialNumber = () - let special' = 2 + let f rangeCoveringExpr = () + let range' = 2 - $0f special'$0 + $0f range'$0 """ [] testCaseAsync "hide: param prefix of arg" <| InlayHints.checkRange server """ - let f special = () - let specialNumber = 2 + let f range = () + let rangeCoveringExpr = 2 - $0f specialNumber$0 + $0f rangeCoveringExpr$0 """ [] testCaseAsync "hide: param postfix of arg" <| InlayHints.checkRange server """ - let f number = () - let specialNumber = 2 + let f range = () + let exactRange = 2 - $0f specialNumber$0 + $0f exactRange$0 """ [] @@ -276,48 +276,48 @@ let tests state = InlayHints.checkRange server """ type Data = { - Number: int + Range: int } - let f number = () - let data: Data = { Number = 2 } + let f range = () + let data: Data = { Range = 2 } - $0f data.Number$0 + $0f data.Range$0 """ [] testCaseAsync "hide: arg is field access with same name as param (lower case start)" <| InlayHints.checkRange server """ type Data = { - number: int + range: int } - let f number = () - let data: Data = { number = 2 } + let f range = () + let data: Data = { range = 2 } - $0f data.number$0 + $0f data.range$0 """ [] testCaseAsync "hide: arg is field access prefix of param (upper case start)" <| InlayHints.checkRange server """ type Data = { - Special: int + Range: int } - let f specialNumber = () - let data: Data = { Special = 2 } + let f rangeCoveringExpr = () + let data: Data = { Range = 2 } - $0f data.Special$0 + $0f data.Range$0 """ [] testCaseAsync "hide: arg is field access, param is prefix of arg" <| InlayHints.checkRange server """ type Data = { - SpecialNumber: int + RangeCoveringExpr: int } - let f special = () - let data: Data = { SpecialNumber = 2 } + let f range = () + let data: Data = { RangeCoveringExpr = 2 } - $0f data.SpecialNumber$0 + $0f data.RangeCoveringExpr$0 """ [] From 43f5436b37b28475b31aa111183d6a15554fbecb Mon Sep 17 00:00:00 2001 From: BooksBaum <15612932+Booksbaum@users.noreply.github.com> Date: Sat, 30 Apr 2022 15:24:22 +0200 Subject: [PATCH 07/12] Adjust XML comment --- src/FsAutoComplete.Core/InlayHints.fs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/FsAutoComplete.Core/InlayHints.fs b/src/FsAutoComplete.Core/InlayHints.fs index b2ec23f10..c89859145 100644 --- a/src/FsAutoComplete.Core/InlayHints.fs +++ b/src/FsAutoComplete.Core/InlayHints.fs @@ -264,10 +264,12 @@ module private ShouldCreate = /// /// We filter out parameters that generate lots of noise in hints. - /// * parameter has a name + /// * parameter has no 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 + /// * parameter does match or is a pre/postfix of user-entered text + /// * user-entered text does match or is a pre/postfix of parameter + /// * parameter is postfix of function name /// let paramHint (func: FSharpMemberOrFunctionOrValue) @@ -278,7 +280,6 @@ module private ShouldCreate = && isNotWellKnownName p && isMeaningfulName p && (not (isOperator func)) - // && doesNotMatchArgumentText p.DisplayName argumentText && (not (areSimilar p.DisplayName argumentText)) && (not (isParamNamePostfixOfFuncName func p.DisplayName)) From 72eff4e7d2e5691b37cef581b8aed90a706e2d15 Mon Sep 17 00:00:00 2001 From: BooksBaum <15612932+Booksbaum@users.noreply.github.com> Date: Sat, 30 Apr 2022 19:40:53 +0200 Subject: [PATCH 08/12] Add special param cases for common function Unlike `isNotWellKnownName`, not just by param name, but function (and its module/namespace) too. I removed params `mapping` & `format` from common well known names, and just hide them when used with a collection (`List.xxx`, `Array.xxx` functions) or one of the printf functions. --- src/FsAutoComplete.Core/InlayHints.fs | 45 +++++++++- .../InlayHintTests.fs | 82 +++++++++++++++++++ 2 files changed, 123 insertions(+), 4 deletions(-) diff --git a/src/FsAutoComplete.Core/InlayHints.fs b/src/FsAutoComplete.Core/InlayHints.fs index c89859145..fecc1a157 100644 --- a/src/FsAutoComplete.Core/InlayHints.fs +++ b/src/FsAutoComplete.Core/InlayHints.fs @@ -132,8 +132,6 @@ let private createTypeHint module private ShouldCreate = let private isNotWellKnownName = let names = Set.ofList [ - "mapping" - "format" "value" "x" ] @@ -143,6 +141,43 @@ module private ShouldCreate = | None -> true | Some n -> not (Set.contains n names) + let private isWellKnownParameterOrFunction + (func: FSharpMemberOrFunctionOrValue) + (param: FSharpParameter) + = + let startsWith (v: string) (fullName: string) = + if fullName.StartsWith v then + Some () + else + None + // doesn't differentiate between modules, types, namespaces + // -> is just for documentation in code + let (|Module|_|) = startsWith + let (|Type|_|) = startsWith + let (|Namespace|_|) = startsWith + + match func.FullName with + | Module "Microsoft.FSharp.Core.Option" -> + // don't show param named `option`, but other params for Option + match param.Name with + | Some "option" -> true + | _ -> false + | Module "Microsoft.FSharp.Core.ValueOption" -> + match param.Name with + | Some "voption" -> true + | _ -> false + | Module "Microsoft.FSharp.Core.ExtraTopLevelOperators" // only printf-members have `format` + | Module "Microsoft.FSharp.Core.Printf" -> + // don't show param named `format` + match param.Name with + | Some "format" -> true + | _ -> false + | Namespace "Microsoft.FSharp.Collections" -> + match param.Name with + | Some "mapping" -> true + | _ -> false + | _ -> false + let inline private hasName (p: FSharpParameter) = not (String.IsNullOrEmpty p.DisplayName) && p.DisplayName <> "````" @@ -265,8 +300,9 @@ module private ShouldCreate = /// /// We filter out parameters that generate lots of noise in hints. /// * parameter has no name - /// * parameter is one of a set of 'known' names that clutter (like printfn formats) /// * parameter has length > 2 + /// * parameter is one of a set of 'known' names that clutter (like printfn formats) + /// * param & function is "well known"/commonly used /// * parameter does match or is a pre/postfix of user-entered text /// * user-entered text does match or is a pre/postfix of parameter /// * parameter is postfix of function name @@ -277,8 +313,9 @@ module private ShouldCreate = (argumentText: string) = hasName p - && isNotWellKnownName p && isMeaningfulName p + && isNotWellKnownName p + && (not (isWellKnownParameterOrFunction func p)) && (not (isOperator func)) && (not (areSimilar p.DisplayName argumentText)) && (not (isParamNamePostfixOfFuncName func p.DisplayName)) diff --git a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs index 147f7cb24..0337b5fdb 100644 --- a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs @@ -391,5 +391,87 @@ let tests state = $0validateRange data$0 """ [] + + testList "special names" [ + testList "mapping" [ + testCaseAsync "hide: for List" <| + InlayHints.checkRange server + """ + $0[1..3] |> List.map id$0 + """ + [] + testCaseAsync "hide: for Array" <| + InlayHints.checkRange server + """ + $0[|1..3|] |> Array.map id$0 + """ + [] + testCaseAsync "show: for custom function" <| + InlayHints.checkRange server + """ + let doStuff mapping = () + $0doStuff $042$0 + """ + [ param "mapping" ] + ] + testList "option" [ + testCaseAsync "hide: for Option" <| + InlayHints.checkRange server + """ + $0Option.count (Some 3)$0 + """ + [] + testCaseAsync "show: for custom function" <| + InlayHints.checkRange server + """ + let doStuff option = () + $0doStuff $042$0 + """ + [ param "option" ] + ] + testList "voption" [ + testCaseAsync "hide: for ValueOption" <| + InlayHints.checkRange server + """ + $0ValueOption.count (ValueSome 3)$0 + """ + [] + testCaseAsync "show: for custom function" <| + InlayHints.checkRange server + """ + let doStuff voption = () + $0doStuff $042$0 + """ + [ param "voption" ] + ] + testList "format" [ + testCaseAsync "hide: in printfn" <| + InlayHints.checkRange server + """ + $0printfn "foo"$0 + """ + [] + testCaseAsync "hide: in sprintf" <| + InlayHints.checkRange server + """ + $0sprintf "foo"$0 + """ + [] + testCaseAsync "hide: in Core.Printf" <| + // "normal" printf is in `Microsoft.FSharp.Core.ExtraTopLevelOperators` + InlayHints.checkRange server + """ + $0Microsoft.FSharp.Core.Printf.printfn "foo"$0 + """ + [] + testCaseAsync "show: for custom function" <| + InlayHints.checkRange server + """ + let doStuff format = () + $0doStuff $042$0 + """ + [ param "format" ] + ] + ] ] ]) From 73613debc92bad54b665cf415d2690c3eeca13ff Mon Sep 17 00:00:00 2001 From: BooksBaum <15612932+Booksbaum@users.noreply.github.com> Date: Sun, 1 May 2022 12:56:40 +0200 Subject: [PATCH 09/12] Turn Active Patterns into struct ones Note: moved out of function: no attributes on local function/value allowed --- src/FsAutoComplete.Core/InlayHints.fs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/FsAutoComplete.Core/InlayHints.fs b/src/FsAutoComplete.Core/InlayHints.fs index fecc1a157..f7c81684d 100644 --- a/src/FsAutoComplete.Core/InlayHints.fs +++ b/src/FsAutoComplete.Core/InlayHints.fs @@ -141,21 +141,26 @@ module private ShouldCreate = | None -> true | Some n -> not (Set.contains n names) + + [] + let private (|StartsWith|_|) (v: string) (fullName: string) = + if fullName.StartsWith v then + ValueSome () + else + ValueNone + // doesn't differentiate between modules, types, namespaces + // -> is just for documentation in code + [] + let private (|Module|_|) = (|StartsWith|_|) + [] + let private (|Type|_|) = (|StartsWith|_|) + [] + let private (|Namespace|_|) = (|StartsWith|_|) + let private isWellKnownParameterOrFunction (func: FSharpMemberOrFunctionOrValue) (param: FSharpParameter) = - let startsWith (v: string) (fullName: string) = - if fullName.StartsWith v then - Some () - else - None - // doesn't differentiate between modules, types, namespaces - // -> is just for documentation in code - let (|Module|_|) = startsWith - let (|Type|_|) = startsWith - let (|Namespace|_|) = startsWith - match func.FullName with | Module "Microsoft.FSharp.Core.Option" -> // don't show param named `option`, but other params for Option From 309ba23edae1b25c0cf8fea2e713ebf857389d67 Mon Sep 17 00:00:00 2001 From: BooksBaum <15612932+Booksbaum@users.noreply.github.com> Date: Sun, 1 May 2022 15:57:01 +0200 Subject: [PATCH 10/12] Add more param exclusions for collections --- src/FsAutoComplete.Core/InlayHints.fs | 18 +++++- .../InlayHintTests.fs | 58 +++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/FsAutoComplete.Core/InlayHints.fs b/src/FsAutoComplete.Core/InlayHints.fs index f7c81684d..9a3a75ab8 100644 --- a/src/FsAutoComplete.Core/InlayHints.fs +++ b/src/FsAutoComplete.Core/InlayHints.fs @@ -157,6 +157,21 @@ module private ShouldCreate = [] let private (|Namespace|_|) = (|StartsWith|_|) + let private commonCollectionParams = Set.ofList [ + "mapping" + "projection" + "chooser" + "value" + "predicate" + "folder" + "state" + "initializer" + "action" + + "list" + "array" + "source" + ] let private isWellKnownParameterOrFunction (func: FSharpMemberOrFunctionOrValue) (param: FSharpParameter) @@ -179,7 +194,8 @@ module private ShouldCreate = | _ -> false | Namespace "Microsoft.FSharp.Collections" -> match param.Name with - | Some "mapping" -> true + | Some name -> + commonCollectionParams |> Set.contains name | _ -> false | _ -> false diff --git a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs index 0337b5fdb..d693be13d 100644 --- a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs @@ -414,6 +414,64 @@ let tests state = """ [ param "mapping" ] ] + testList "in collections" [ + testCaseAsync "hide: predicate" <| + InlayHints.checkRange server + """ + $0[1..3] |> List.filter ((<) 2)$0 + """ + [] + testCaseAsync "hide: chooser" <| + InlayHints.checkRange server + """ + $0[1..3] |> List.tryPick Some$0 + """ + [] + testCaseAsync "hide: value" <| + InlayHints.checkRange server + """ + $0[1..3] |> List.contains 2$0 + """ + [] + testCaseAsync "hide: projection" <| + InlayHints.checkRange server + """ + $0[1..3] |> List.sumBy id$0 + """ + [] + testCaseAsync "hide: action" <| + InlayHints.checkRange server + """ + $0[1..3] |> List.iter (printfn "%i")$0 + """ + [] + testCaseAsync "hide: folder & state" <| + InlayHints.checkRange server + """ + $0[1..3] |> List.fold (+) 0$0 + """ + [] + + + testCaseAsync "hide: list" <| + InlayHints.checkRange server + """ + $0List.tryLast [1..3]$0 + """ + [] + testCaseAsync "hide: array" <| + InlayHints.checkRange server + """ + $0Array.tryLast [|1..3|]$0 + """ + [] + testCaseAsync "hide: source" <| + InlayHints.checkRange server + """ + $0Seq.tryLast [1..3]$0 + """ + [] + ] testList "option" [ testCaseAsync "hide: for Option" <| InlayHints.checkRange server From 091df45ab79f2b5cb72219ef7815baba69a1f5d8 Mon Sep 17 00:00:00 2001 From: BooksBaum <15612932+Booksbaum@users.noreply.github.com> Date: Sun, 1 May 2022 16:09:17 +0200 Subject: [PATCH 11/12] Add plurals to coll param exclusions --- src/FsAutoComplete.Core/InlayHints.fs | 3 +++ .../FsAutoComplete.Tests.Lsp/InlayHintTests.fs | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/FsAutoComplete.Core/InlayHints.fs b/src/FsAutoComplete.Core/InlayHints.fs index 9a3a75ab8..369403eb0 100644 --- a/src/FsAutoComplete.Core/InlayHints.fs +++ b/src/FsAutoComplete.Core/InlayHints.fs @@ -171,6 +171,9 @@ module private ShouldCreate = "list" "array" "source" + "lists" + "arrays" + "sources" ] let private isWellKnownParameterOrFunction (func: FSharpMemberOrFunctionOrValue) diff --git a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs index d693be13d..720ef4108 100644 --- a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs @@ -471,6 +471,24 @@ let tests state = $0Seq.tryLast [1..3]$0 """ [] + testCaseAsync "hide: lists" <| + InlayHints.checkRange server + """ + $0List.concat []$0 + """ + [] + testCaseAsync "hide: arrays" <| + InlayHints.checkRange server + """ + $0Array.concat [||]$0 + """ + [] + testCaseAsync "hide: sources" <| + InlayHints.checkRange server + """ + $0Seq.concat []$0 + """ + [] ] testList "option" [ testCaseAsync "hide: for Option" <| From da70e8e27e44da4e80ecd6d753264d2755abd575 Mon Sep 17 00:00:00 2001 From: BooksBaum <15612932+Booksbaum@users.noreply.github.com> Date: Sun, 1 May 2022 16:37:30 +0200 Subject: [PATCH 12/12] Remove marker TODOs --- src/FsAutoComplete.Core/InlayHints.fs | 3 --- test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs | 9 ++++----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/FsAutoComplete.Core/InlayHints.fs b/src/FsAutoComplete.Core/InlayHints.fs index 369403eb0..cc3df2ef2 100644 --- a/src/FsAutoComplete.Core/InlayHints.fs +++ b/src/FsAutoComplete.Core/InlayHints.fs @@ -292,11 +292,8 @@ module private ShouldCreate = if isLongIdentifier argTextNoParens then removeTrailingTick (extractLastIdentifier argTextNoParens) else - //todo: expression -> early out? or further processing? special processing? argumentText - //todo: all useful? - // // covered by each isPre/PostfixOf // areSame paramName argumentName // || diff --git a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs index 720ef4108..aba350b12 100644 --- a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs @@ -194,7 +194,6 @@ let tests state = $0f range$0 """ [ ] - //todo: or hide? testCaseAsync "show: variable infix of param" <| InlayHints.checkRange server """ @@ -204,7 +203,6 @@ let tests state = $0f $0range$0 """ [ param "exactRangeCoveringExpr" ] - //todo: or hide? testCaseAsync "show: variable prefix of param, but no word boundary" <| InlayHints.checkRange server """ @@ -214,7 +212,6 @@ let tests state = $0f $0range$0 """ [ param "rangecover" ] - //todo: or hide? testCaseAsync "show: variable postfix of param, but no word boundary" <| InlayHints.checkRange server """ @@ -339,7 +336,6 @@ let tests state = $0f ( alpha )$0 """ [ ] - //todo: or hide? based on: what is last? but then (`alpha <| 1`, `1 |> alpha 2`, etc?) -> too complex to detect testCaseAsync "show: expr including param name in parens" <| InlayHints.checkRange server """ @@ -350,7 +346,10 @@ let tests state = """ [ param "alpha" ] - //todo: inspect most left/right identifier? extract function name? look for left of `.`? use ast? + //ENHANCEMENT: detect some common expressions like: + // * receiving end of pipe: `1 |> alpha`, `alpha <| 1`, `1 |> toAlpha` + // * last function: `1.ToAlpha()` + // * often used convert functions: `string alpha`, `alpha.ToString()` testCaseAsync "show: any expression" <| InlayHints.checkRange server """