From ea2764005409cc031bb5c4dfa8c27d0b3790077d Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Tue, 23 Nov 2021 14:44:42 +0100 Subject: [PATCH 1/3] Extend autocompletion to handle nested objects. Such as `nestedObj["x"]["y"]`. --- CHANGELOG.md | 2 +- analysis/src/NewCompletions.ml | 48 ++++++----- analysis/src/PartialParser.ml | 22 +++-- analysis/tests/src/Completion.res | 23 +++-- .../tests/src/expected/Completion.res.txt | 84 ++++++++++++------- 5 files changed, 111 insertions(+), 68 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2b8bec39..ae9d0bf79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ - Fix issue in JSX autocomplete when the component is declared external. - Fix jump-to-definition for uncurried calls. - Fix issue where values for autocomplete were pulled from implementations instead of interfaces. -- Add autocompletion for object access of the form foo["bar"]. +- Add autocompletion for object access of the form `foo["x"]` and `foo["x"]["y"]["z"]`. - Fix issue with autocomplete then punned props are used in JSX. E.g. ``. - Fix issue with JSX autocompletion not working after `foo=#variant`. diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index b69a43af6..2ac68f59b 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -1145,28 +1145,38 @@ let processCompletable ~findItems ~full ~package ~rawOpens |> List.filter (fun (name, _t) -> Utils.startsWith name prefix && not (List.mem name identsSeen)) |> List.map mkLabel - | Cobj (lhs, prefix) -> - let labels = + | Cobj (lhs, path, prefix) -> + let rec getFields (texp : Types.type_expr) = + match texp.desc with + | Tfield (name, _, t1, t2) -> + let fields = t2 |> getFields in + (name, t1) :: fields + | Tlink te -> te |> getFields + | Tvar None -> [] + | _ -> [] + in + let rec getObj (t : Types.type_expr) = + match t.desc with + | Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> getObj t1 + | Tobject (tObj, _) -> getFields tObj + | _ -> [] + in + let fields = match [lhs] |> findItems ~exact:true with - | {SharedTypes.item = Value typ} :: _ -> - let rec getFields (texp : Types.type_expr) = - match texp.desc with - | Tfield (name, _, t1, t2) -> - let fields = t2 |> getFields in - (name, t1) :: fields - | Tlink te -> te |> getFields - | Tvar None -> [] - | _ -> [] - in - let rec getObj (t : Types.type_expr) = - match t.desc with - | Tlink t1 | Tsubst t1 -> getObj t1 - | Tobject (tObj, _) -> getFields tObj - | _ -> [] - in - getObj typ + | {SharedTypes.item = Value typ} :: _ -> getObj typ | _ -> [] in + let rec resolvePath fields path = + match path with + | name :: restPath -> ( + match fields |> List.find_opt (fun (n, _) -> n = name) with + | Some (_, fieldType) -> + let innerFields = getObj fieldType in + resolvePath innerFields restPath + | None -> []) + | [] -> fields + in + let labels = resolvePath fields path in let mkLabel_ name typString = mkItem ~name ~kind:4 ~deprecated:None ~detail:typString ~docstring:[] in diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index e7cb8da88..9917ea0c7 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -190,7 +190,8 @@ type completable = | Cpath of string list (** e.g. ["M", "foo"] for M.foo *) | Cjsx of string list * string * string list (** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for foo" *) let isLowercaseIdent id = @@ -234,16 +235,23 @@ let findCompletable text offset = in let mkObj ~off ~partialName = let off = skipWhite text off in - let rec loop i = - if i < 0 then Some (String.sub text 0 (i - 1)) + let rec loop off path i = + if i < 0 then Some ([], String.sub text 0 (i - 1)) else match text.[i] with - | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '_' -> loop (i - 1) - | _ -> Some (String.sub text (i + 1) (off - i)) + | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '_' -> loop off path (i - 1) + | ']' when i > 1 && text.[i - 1] = '"' -> + let i0 = i - 2 in + let i1 = startOfLident text i0 in + let ident = String.sub text i1 (i0 - i1 + 1) in + if ident <> "" && i1 > 1 && text.[i1 - 1] = '"' && text.[i1 - 2] = '[' + then loop (off - i + i1 - 3) (ident :: path) (i1 - 3) + else None + | _ -> Some (path, String.sub text (i + 1) (off - i)) in - match loop off with + match loop off [] off with | None -> None - | Some lhs -> Some (Cobj (lhs, partialName)) + | Some (path, lhs) -> Some (Cobj (lhs, path, partialName)) in let suffix i = String.sub text (i + 1) (offset - (i + 1)) in diff --git a/analysis/tests/src/Completion.res b/analysis/tests/src/Completion.res index b8457f67b..64a55d8b2 100644 --- a/analysis/tests/src/Completion.res +++ b/analysis/tests/src/Completion.res @@ -3,7 +3,6 @@ module MyList = Belt.List //^com Array. //^com Array.m - module Dep: { @ocaml.doc("Some doc comment") @deprecated("Use customDouble instead") let customDouble: int => int @@ -30,11 +29,11 @@ let op = Some(3) module ForAuto = { type t = int - let abc = (x:t, _y:int) => x - let abd = (x:t, _y:int) => x + let abc = (x: t, _y: int) => x + let abd = (x: t, _y: int) => x } -let fa:ForAuto.t = 34 +let fa: ForAuto.t = 34 //^com fa-> //^com "hello"->Js.Dict.u @@ -42,8 +41,7 @@ let fa:ForAuto.t = 34 module O = { module Comp = { @react.component - let make = (~first="", ~zoo=3, ~second) => - React.string(first ++ second ++ string_of_int(zoo)) + let make = (~first="", ~zoo=3, ~second) => React.string(first ++ second ++ string_of_int(zoo)) } } @@ -65,10 +63,17 @@ let zzz = 11 //^com let x = Lib.foo(~age={3+4}, ~ -let _ = Lib.foo(//~age, -//^com ~ -~age=3, ~name="") +let _ = Lib.foo( + //~age, + //^com ~ + ~age=3, + ~name="", +) let someObj = {"name": "a", "age": 32} //^com someObj["a + +let nestedObj = {"x": {"y": {"name": "a", "age": 32}}} + +//^com nestedObj["x"]["y"][" diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 0737535ab..f3cd6c752 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -337,7 +337,7 @@ Complete tests/src/Completion.res 2:2 "documentation": {"kind": "markdown", "value": " [mem a l] is true if and only if [a] is equal\n to an element of [l].\n @since 4.03.0 "} }] -Complete tests/src/Completion.res 12:2 +Complete tests/src/Completion.res 11:2 [{ "label": "customDouble", "kind": 12, @@ -346,7 +346,7 @@ Complete tests/src/Completion.res 12:2 "documentation": {"kind": "markdown", "value": "Deprecated: Use customDouble instead\n\nSome doc comment"} }] -Complete tests/src/Completion.res 19:2 +Complete tests/src/Completion.res 18:2 [{ "label": "age", "kind": 4, @@ -361,7 +361,7 @@ Complete tests/src/Completion.res 19:2 "documentation": null }] -Complete tests/src/Completion.res 21:2 +Complete tests/src/Completion.res 20:2 [{ "label": "Js.Array2.mapi", "kind": 12, @@ -376,7 +376,7 @@ Complete tests/src/Completion.res 21:2 "documentation": null }] -Complete tests/src/Completion.res 23:2 +Complete tests/src/Completion.res 22:2 [{ "label": "Js.String2.toUpperCase", "kind": 12, @@ -385,7 +385,7 @@ Complete tests/src/Completion.res 23:2 "documentation": {"kind": "markdown", "value": "\n `toUpperCase str` converts `str` to upper case using the locale-insensitive case mappings in the Unicode Character Database. Notice that the conversion can expand the number of letters in the result; for example the German `ß` capitalizes to two `S`es in a row.\n\n ```\n toUpperCase \"abc\" = \"ABC\";;\n toUpperCase {js|Straße|js} = {js|STRASSE|js};;\n toLowerCase {js|πς|js} = {js|ΠΣ|js};;\n ```\n"} }] -Complete tests/src/Completion.res 27:2 +Complete tests/src/Completion.res 26:2 [{ "label": "Belt.Option.eqU", "kind": 12, @@ -400,7 +400,7 @@ Complete tests/src/Completion.res 27:2 "documentation": {"kind": "markdown", "value": "\n `eq optValue1 optvalue2 predicate`\n\n Evaluates two optional values for equality with respect to a predicate function.\n\n If both `optValue1` and `optValue2` are `None`, returns `true`.\n\n If one of the arguments is `Some value` and the other is `None`, returns `false`\n\n If arguments are `Some value1` and `Some value2`, returns the result of `predicate value1 value2`;\n the `predicate` function must return a `bool`\n\n ```\n let clockEqual = (fun a b -> a mod 12 = b mod 12);;\n eq (Some 3) (Some 15) clockEqual = true;;\n eq (Some 3) None clockEqual = false;;\n eq None (Some 3) clockEqual = false;;\n eq None None clockEqual = true;;\n ```\n"} }] -Complete tests/src/Completion.res 36:2 +Complete tests/src/Completion.res 35:2 [{ "label": "ForAuto.abc", "kind": 12, @@ -415,7 +415,7 @@ Complete tests/src/Completion.res 36:2 "documentation": null }] -Complete tests/src/Completion.res 38:2 +Complete tests/src/Completion.res 37:2 [{ "label": "unsafeGet", "kind": 12, @@ -430,7 +430,7 @@ Complete tests/src/Completion.res 38:2 "documentation": {"kind": "markdown", "value": " Experimental internal function "} }] -Complete tests/src/Completion.res 50:2 +Complete tests/src/Completion.res 48:2 [{ "label": "zzz", "kind": 12, @@ -439,7 +439,7 @@ Complete tests/src/Completion.res 50:2 "documentation": null }] -Complete tests/src/Completion.res 52:2 +Complete tests/src/Completion.res 50:2 [{ "label": "zoo", "kind": 4, @@ -464,86 +464,91 @@ DocumentSymbol tests/src/Completion.res { "name": "Dep", "kind": 2, - "location": {"uri": "Completion.res", "range": {"start": {"line": 6, "character": 7}, "end": {"line": 11, "character": 1}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 5, "character": 7}, "end": {"line": 10, "character": 1}}} }, { "name": "customDouble", "kind": 12, - "location": {"uri": "Completion.res", "range": {"start": {"line": 7, "character": 2}, "end": {"line": 8, "character": 30}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 6, "character": 2}, "end": {"line": 7, "character": 30}}} }, { "name": "Lib", "kind": 2, - "location": {"uri": "Completion.res", "range": {"start": {"line": 15, "character": 7}, "end": {"line": 18, "character": 1}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 14, "character": 7}, "end": {"line": 17, "character": 1}}} }, { "name": "foo", "kind": 12, - "location": {"uri": "Completion.res", "range": {"start": {"line": 16, "character": 6}, "end": {"line": 16, "character": 9}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 15, "character": 6}, "end": {"line": 15, "character": 9}}} }, { "name": "next", "kind": 12, - "location": {"uri": "Completion.res", "range": {"start": {"line": 17, "character": 6}, "end": {"line": 17, "character": 10}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 16, "character": 6}, "end": {"line": 16, "character": 10}}} }, { "name": "op", "kind": 13, - "location": {"uri": "Completion.res", "range": {"start": {"line": 26, "character": 4}, "end": {"line": 26, "character": 6}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 25, "character": 4}, "end": {"line": 25, "character": 6}}} }, { "name": "ForAuto", "kind": 2, - "location": {"uri": "Completion.res", "range": {"start": {"line": 30, "character": 7}, "end": {"line": 34, "character": 1}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 29, "character": 7}, "end": {"line": 33, "character": 1}}} }, { "name": "t", "kind": 26, - "location": {"uri": "Completion.res", "range": {"start": {"line": 31, "character": 2}, "end": {"line": 31, "character": 14}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 30, "character": 2}, "end": {"line": 30, "character": 14}}} }, { "name": "abc", "kind": 12, - "location": {"uri": "Completion.res", "range": {"start": {"line": 32, "character": 6}, "end": {"line": 32, "character": 9}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 31, "character": 6}, "end": {"line": 31, "character": 9}}} }, { "name": "abd", "kind": 12, - "location": {"uri": "Completion.res", "range": {"start": {"line": 33, "character": 6}, "end": {"line": 33, "character": 9}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 32, "character": 6}, "end": {"line": 32, "character": 9}}} }, { "name": "fa", "kind": 13, - "location": {"uri": "Completion.res", "range": {"start": {"line": 36, "character": 4}, "end": {"line": 36, "character": 6}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 35, "character": 4}, "end": {"line": 35, "character": 6}}} }, { "name": "O", "kind": 2, - "location": {"uri": "Completion.res", "range": {"start": {"line": 41, "character": 7}, "end": {"line": 47, "character": 1}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 40, "character": 7}, "end": {"line": 45, "character": 1}}} }, { "name": "Comp", "kind": 2, - "location": {"uri": "Completion.res", "range": {"start": {"line": 42, "character": 9}, "end": {"line": 46, "character": 3}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 41, "character": 9}, "end": {"line": 44, "character": 3}}} }, { "name": "make", "kind": 12, - "location": {"uri": "Completion.res", "range": {"start": {"line": 44, "character": 8}, "end": {"line": 44, "character": 12}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 43, "character": 8}, "end": {"line": 43, "character": 12}}} }, { "name": "zzz", "kind": 13, - "location": {"uri": "Completion.res", "range": {"start": {"line": 49, "character": 4}, "end": {"line": 49, "character": 7}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 47, "character": 4}, "end": {"line": 47, "character": 7}}} }, { "name": "someObj", "kind": 19, - "location": {"uri": "Completion.res", "range": {"start": {"line": 71, "character": 4}, "end": {"line": 71, "character": 11}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 72, "character": 4}, "end": {"line": 72, "character": 11}}} +}, +{ + "name": "nestedObj", + "kind": 19, + "location": {"uri": "Completion.res", "range": {"start": {"line": 76, "character": 4}, "end": {"line": 76, "character": 13}}} } ] -Complete tests/src/Completion.res 56:2 +Complete tests/src/Completion.res 54:2 [{ "label": "react.component", "kind": 4, @@ -552,7 +557,7 @@ Complete tests/src/Completion.res 56:2 "documentation": null }] -Complete tests/src/Completion.res 58:2 +Complete tests/src/Completion.res 56:2 [{ "label": "component", "kind": 4, @@ -561,7 +566,7 @@ Complete tests/src/Completion.res 58:2 "documentation": null }] -Complete tests/src/Completion.res 60:2 +Complete tests/src/Completion.res 58:2 [{ "label": "age", "kind": 4, @@ -570,7 +575,7 @@ Complete tests/src/Completion.res 60:2 "documentation": null }] -Complete tests/src/Completion.res 62:2 +Complete tests/src/Completion.res 60:2 [{ "label": "name", "kind": 4, @@ -579,7 +584,7 @@ Complete tests/src/Completion.res 62:2 "documentation": null }] -Complete tests/src/Completion.res 64:2 +Complete tests/src/Completion.res 62:2 [{ "label": "name", "kind": 4, @@ -588,7 +593,7 @@ Complete tests/src/Completion.res 64:2 "documentation": null }] -Complete tests/src/Completion.res 67:2 +Complete tests/src/Completion.res 66:4 [{ "label": "age", "kind": 4, @@ -603,7 +608,7 @@ Complete tests/src/Completion.res 67:2 "documentation": null }] -Complete tests/src/Completion.res 72:2 +Complete tests/src/Completion.res 73:2 [{ "label": "age", "kind": 4, @@ -612,3 +617,18 @@ Complete tests/src/Completion.res 72:2 "documentation": null }] +Complete tests/src/Completion.res 77:2 +[{ + "label": "age", + "kind": 4, + "tags": [], + "detail": "int", + "documentation": null + }, { + "label": "name", + "kind": 4, + "tags": [], + "detail": "string", + "documentation": null + }] + From 50b87823021dec978eb6171cf510bcb31ed15879 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Tue, 23 Nov 2021 14:47:42 +0100 Subject: [PATCH 2/3] Un-format example. --- analysis/tests/src/Completion.res | 19 +++--- .../tests/src/expected/Completion.res.txt | 68 +++++++++---------- 2 files changed, 43 insertions(+), 44 deletions(-) diff --git a/analysis/tests/src/Completion.res b/analysis/tests/src/Completion.res index 64a55d8b2..ff845d5cc 100644 --- a/analysis/tests/src/Completion.res +++ b/analysis/tests/src/Completion.res @@ -3,6 +3,7 @@ module MyList = Belt.List //^com Array. //^com Array.m + module Dep: { @ocaml.doc("Some doc comment") @deprecated("Use customDouble instead") let customDouble: int => int @@ -29,11 +30,11 @@ let op = Some(3) module ForAuto = { type t = int - let abc = (x: t, _y: int) => x - let abd = (x: t, _y: int) => x + let abc = (x:t, _y:int) => x + let abd = (x:t, _y:int) => x } -let fa: ForAuto.t = 34 +let fa:ForAuto.t = 34 //^com fa-> //^com "hello"->Js.Dict.u @@ -41,7 +42,8 @@ let fa: ForAuto.t = 34 module O = { module Comp = { @react.component - let make = (~first="", ~zoo=3, ~second) => React.string(first ++ second ++ string_of_int(zoo)) + let make = (~first="", ~zoo=3, ~second) => + React.string(first ++ second ++ string_of_int(zoo)) } } @@ -63,12 +65,9 @@ let zzz = 11 //^com let x = Lib.foo(~age={3+4}, ~ -let _ = Lib.foo( - //~age, - //^com ~ - ~age=3, - ~name="", -) +let _ = Lib.foo(//~age, +//^com ~ +~age=3, ~name="") let someObj = {"name": "a", "age": 32} diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index f3cd6c752..a4fddf26c 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -337,7 +337,7 @@ Complete tests/src/Completion.res 2:2 "documentation": {"kind": "markdown", "value": " [mem a l] is true if and only if [a] is equal\n to an element of [l].\n @since 4.03.0 "} }] -Complete tests/src/Completion.res 11:2 +Complete tests/src/Completion.res 12:2 [{ "label": "customDouble", "kind": 12, @@ -346,7 +346,7 @@ Complete tests/src/Completion.res 11:2 "documentation": {"kind": "markdown", "value": "Deprecated: Use customDouble instead\n\nSome doc comment"} }] -Complete tests/src/Completion.res 18:2 +Complete tests/src/Completion.res 19:2 [{ "label": "age", "kind": 4, @@ -361,7 +361,7 @@ Complete tests/src/Completion.res 18:2 "documentation": null }] -Complete tests/src/Completion.res 20:2 +Complete tests/src/Completion.res 21:2 [{ "label": "Js.Array2.mapi", "kind": 12, @@ -376,7 +376,7 @@ Complete tests/src/Completion.res 20:2 "documentation": null }] -Complete tests/src/Completion.res 22:2 +Complete tests/src/Completion.res 23:2 [{ "label": "Js.String2.toUpperCase", "kind": 12, @@ -385,7 +385,7 @@ Complete tests/src/Completion.res 22:2 "documentation": {"kind": "markdown", "value": "\n `toUpperCase str` converts `str` to upper case using the locale-insensitive case mappings in the Unicode Character Database. Notice that the conversion can expand the number of letters in the result; for example the German `ß` capitalizes to two `S`es in a row.\n\n ```\n toUpperCase \"abc\" = \"ABC\";;\n toUpperCase {js|Straße|js} = {js|STRASSE|js};;\n toLowerCase {js|πς|js} = {js|ΠΣ|js};;\n ```\n"} }] -Complete tests/src/Completion.res 26:2 +Complete tests/src/Completion.res 27:2 [{ "label": "Belt.Option.eqU", "kind": 12, @@ -400,7 +400,7 @@ Complete tests/src/Completion.res 26:2 "documentation": {"kind": "markdown", "value": "\n `eq optValue1 optvalue2 predicate`\n\n Evaluates two optional values for equality with respect to a predicate function.\n\n If both `optValue1` and `optValue2` are `None`, returns `true`.\n\n If one of the arguments is `Some value` and the other is `None`, returns `false`\n\n If arguments are `Some value1` and `Some value2`, returns the result of `predicate value1 value2`;\n the `predicate` function must return a `bool`\n\n ```\n let clockEqual = (fun a b -> a mod 12 = b mod 12);;\n eq (Some 3) (Some 15) clockEqual = true;;\n eq (Some 3) None clockEqual = false;;\n eq None (Some 3) clockEqual = false;;\n eq None None clockEqual = true;;\n ```\n"} }] -Complete tests/src/Completion.res 35:2 +Complete tests/src/Completion.res 36:2 [{ "label": "ForAuto.abc", "kind": 12, @@ -415,7 +415,7 @@ Complete tests/src/Completion.res 35:2 "documentation": null }] -Complete tests/src/Completion.res 37:2 +Complete tests/src/Completion.res 38:2 [{ "label": "unsafeGet", "kind": 12, @@ -430,7 +430,7 @@ Complete tests/src/Completion.res 37:2 "documentation": {"kind": "markdown", "value": " Experimental internal function "} }] -Complete tests/src/Completion.res 48:2 +Complete tests/src/Completion.res 50:2 [{ "label": "zzz", "kind": 12, @@ -439,7 +439,7 @@ Complete tests/src/Completion.res 48:2 "documentation": null }] -Complete tests/src/Completion.res 50:2 +Complete tests/src/Completion.res 52:2 [{ "label": "zoo", "kind": 4, @@ -464,91 +464,91 @@ DocumentSymbol tests/src/Completion.res { "name": "Dep", "kind": 2, - "location": {"uri": "Completion.res", "range": {"start": {"line": 5, "character": 7}, "end": {"line": 10, "character": 1}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 6, "character": 7}, "end": {"line": 11, "character": 1}}} }, { "name": "customDouble", "kind": 12, - "location": {"uri": "Completion.res", "range": {"start": {"line": 6, "character": 2}, "end": {"line": 7, "character": 30}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 7, "character": 2}, "end": {"line": 8, "character": 30}}} }, { "name": "Lib", "kind": 2, - "location": {"uri": "Completion.res", "range": {"start": {"line": 14, "character": 7}, "end": {"line": 17, "character": 1}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 15, "character": 7}, "end": {"line": 18, "character": 1}}} }, { "name": "foo", "kind": 12, - "location": {"uri": "Completion.res", "range": {"start": {"line": 15, "character": 6}, "end": {"line": 15, "character": 9}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 16, "character": 6}, "end": {"line": 16, "character": 9}}} }, { "name": "next", "kind": 12, - "location": {"uri": "Completion.res", "range": {"start": {"line": 16, "character": 6}, "end": {"line": 16, "character": 10}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 17, "character": 6}, "end": {"line": 17, "character": 10}}} }, { "name": "op", "kind": 13, - "location": {"uri": "Completion.res", "range": {"start": {"line": 25, "character": 4}, "end": {"line": 25, "character": 6}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 26, "character": 4}, "end": {"line": 26, "character": 6}}} }, { "name": "ForAuto", "kind": 2, - "location": {"uri": "Completion.res", "range": {"start": {"line": 29, "character": 7}, "end": {"line": 33, "character": 1}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 30, "character": 7}, "end": {"line": 34, "character": 1}}} }, { "name": "t", "kind": 26, - "location": {"uri": "Completion.res", "range": {"start": {"line": 30, "character": 2}, "end": {"line": 30, "character": 14}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 31, "character": 2}, "end": {"line": 31, "character": 14}}} }, { "name": "abc", "kind": 12, - "location": {"uri": "Completion.res", "range": {"start": {"line": 31, "character": 6}, "end": {"line": 31, "character": 9}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 32, "character": 6}, "end": {"line": 32, "character": 9}}} }, { "name": "abd", "kind": 12, - "location": {"uri": "Completion.res", "range": {"start": {"line": 32, "character": 6}, "end": {"line": 32, "character": 9}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 33, "character": 6}, "end": {"line": 33, "character": 9}}} }, { "name": "fa", "kind": 13, - "location": {"uri": "Completion.res", "range": {"start": {"line": 35, "character": 4}, "end": {"line": 35, "character": 6}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 36, "character": 4}, "end": {"line": 36, "character": 6}}} }, { "name": "O", "kind": 2, - "location": {"uri": "Completion.res", "range": {"start": {"line": 40, "character": 7}, "end": {"line": 45, "character": 1}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 41, "character": 7}, "end": {"line": 47, "character": 1}}} }, { "name": "Comp", "kind": 2, - "location": {"uri": "Completion.res", "range": {"start": {"line": 41, "character": 9}, "end": {"line": 44, "character": 3}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 42, "character": 9}, "end": {"line": 46, "character": 3}}} }, { "name": "make", "kind": 12, - "location": {"uri": "Completion.res", "range": {"start": {"line": 43, "character": 8}, "end": {"line": 43, "character": 12}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 44, "character": 8}, "end": {"line": 44, "character": 12}}} }, { "name": "zzz", "kind": 13, - "location": {"uri": "Completion.res", "range": {"start": {"line": 47, "character": 4}, "end": {"line": 47, "character": 7}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 49, "character": 4}, "end": {"line": 49, "character": 7}}} }, { "name": "someObj", "kind": 19, - "location": {"uri": "Completion.res", "range": {"start": {"line": 72, "character": 4}, "end": {"line": 72, "character": 11}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 71, "character": 4}, "end": {"line": 71, "character": 11}}} }, { "name": "nestedObj", "kind": 19, - "location": {"uri": "Completion.res", "range": {"start": {"line": 76, "character": 4}, "end": {"line": 76, "character": 13}}} + "location": {"uri": "Completion.res", "range": {"start": {"line": 75, "character": 4}, "end": {"line": 75, "character": 13}}} } ] -Complete tests/src/Completion.res 54:2 +Complete tests/src/Completion.res 56:2 [{ "label": "react.component", "kind": 4, @@ -557,7 +557,7 @@ Complete tests/src/Completion.res 54:2 "documentation": null }] -Complete tests/src/Completion.res 56:2 +Complete tests/src/Completion.res 58:2 [{ "label": "component", "kind": 4, @@ -566,7 +566,7 @@ Complete tests/src/Completion.res 56:2 "documentation": null }] -Complete tests/src/Completion.res 58:2 +Complete tests/src/Completion.res 60:2 [{ "label": "age", "kind": 4, @@ -575,7 +575,7 @@ Complete tests/src/Completion.res 58:2 "documentation": null }] -Complete tests/src/Completion.res 60:2 +Complete tests/src/Completion.res 62:2 [{ "label": "name", "kind": 4, @@ -584,7 +584,7 @@ Complete tests/src/Completion.res 60:2 "documentation": null }] -Complete tests/src/Completion.res 62:2 +Complete tests/src/Completion.res 64:2 [{ "label": "name", "kind": 4, @@ -593,7 +593,7 @@ Complete tests/src/Completion.res 62:2 "documentation": null }] -Complete tests/src/Completion.res 66:4 +Complete tests/src/Completion.res 67:2 [{ "label": "age", "kind": 4, @@ -608,7 +608,7 @@ Complete tests/src/Completion.res 66:4 "documentation": null }] -Complete tests/src/Completion.res 73:2 +Complete tests/src/Completion.res 72:2 [{ "label": "age", "kind": 4, @@ -617,7 +617,7 @@ Complete tests/src/Completion.res 73:2 "documentation": null }] -Complete tests/src/Completion.res 77:2 +Complete tests/src/Completion.res 76:2 [{ "label": "age", "kind": 4, From b54c76897f82f5726e1df2f2b4b7a880e5715ff0 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Tue, 23 Nov 2021 15:00:00 +0100 Subject: [PATCH 3/3] Fix issue in JSX autocompletion where the `key` label would always appear. --- CHANGELOG.md | 1 + analysis/src/NewCompletions.ml | 12 ++++++++---- analysis/tests/src/expected/Completion.res.txt | 6 ------ analysis/tests/src/expected/Div.res.txt | 6 ------ analysis/tests/src/expected/Jsx.res.txt | 12 ------------ 5 files changed, 9 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae9d0bf79..3f5d548b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Add autocompletion for object access of the form `foo["x"]` and `foo["x"]["y"]["z"]`. - Fix issue with autocomplete then punned props are used in JSX. E.g. ``. - Fix issue with JSX autocompletion not working after `foo=#variant`. +- Fix issue in JSX autocompletion where the `key` label would always appear. ## 1.1.3 diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index 2ac68f59b..091d104f8 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -879,14 +879,16 @@ let processCompletable ~findItems ~full ~package ~rawOpens mkItem ~name ~kind:4 ~deprecated:None ~detail:typString ~docstring:[] in let mkLabel (name, typ) = mkLabel_ name typ in - let keyLabel = mkLabel_ "key" "string" in + let keyLabels = + if Utils.startsWith "key" prefix then [mkLabel_ "key" "string"] else [] + in if domLabels = [] then [] else (domLabels |> List.filter (fun (name, _t) -> Utils.startsWith name prefix && not (List.mem name identsSeen)) |> List.map mkLabel) - @ [keyLabel] + @ keyLabels | Cjsx (componentPath, prefix, identsSeen) -> let items = findItems ~exact:true (componentPath @ ["make"]) in let labels = @@ -936,14 +938,16 @@ let processCompletable ~findItems ~full ~package ~rawOpens mkItem ~name ~kind:4 ~deprecated:None ~detail:typString ~docstring:[] in let mkLabel (name, typ) = mkLabel_ name (typ |> Shared.typeToString) in - let keyLabel = mkLabel_ "key" "string" in + let keyLabels = + if Utils.startsWith "key" prefix then [mkLabel_ "key" "string"] else [] + in if labels = [] then [] else (labels |> List.filter (fun (name, _t) -> Utils.startsWith name prefix && not (List.mem name identsSeen)) |> List.map mkLabel) - @ [keyLabel] + @ keyLabels | Cpath parts -> let items = parts |> findItems ~exact:false in (* TODO(#107): figure out why we're getting duplicates. *) diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index a4fddf26c..f49246691 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -446,12 +446,6 @@ Complete tests/src/Completion.res 52:2 "tags": [], "detail": "option", "documentation": null - }, { - "label": "key", - "kind": 4, - "tags": [], - "detail": "string", - "documentation": null }] DocumentSymbol tests/src/Completion.res diff --git a/analysis/tests/src/expected/Div.res.txt b/analysis/tests/src/expected/Div.res.txt index 0c0057dba..2d9a106ab 100644 --- a/analysis/tests/src/expected/Div.res.txt +++ b/analysis/tests/src/expected/Div.res.txt @@ -8,11 +8,5 @@ Complete tests/src/Div.res 3:3 "tags": [], "detail": "{\"__html\": string}", "documentation": null - }, { - "label": "key", - "kind": 4, - "tags": [], - "detail": "string", - "documentation": null }] diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt index 464155fce..6c6ff43d3 100644 --- a/analysis/tests/src/expected/Jsx.res.txt +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -35,12 +35,6 @@ Complete tests/src/Jsx.res 9:2 "tags": [], "detail": "option", "documentation": null - }, { - "label": "key", - "kind": 4, - "tags": [], - "detail": "string", - "documentation": null }] Complete tests/src/Jsx.res 11:2 @@ -182,12 +176,6 @@ Complete tests/src/Jsx.res 52:2 "tags": [], "detail": "option", "documentation": null - }, { - "label": "key", - "kind": 4, - "tags": [], - "detail": "string", - "documentation": null }] Complete tests/src/Jsx.res 54:2