Skip to content

Commit 6b4aa74

Browse files
committed
Add autocomplete for props in JSX components.
First working version of autocomplete for JSX props.
1 parent 61f7e7b commit 6b4aa74

File tree

3 files changed

+93
-63
lines changed

3 files changed

+93
-63
lines changed

Changes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# master
22
- Fix issue in jump-to-definition on Windows. (See https://github.com/rescript-lang/rescript-vscode/issues/98) where the wrong URI was generated.
33
- Don't show file path on hover.
4+
- Add autocomplete for props in JSX components.
45

56
## Release 1.0.6 of rescript-vscode
67
This [commit](https://github.com/rescript-lang/rescript-editor-support/commit/03ee0d97b250474028d4fb08eac81ddb21ccb082) is vendored in [rescript-vscode 1.0.6](https://github.com/rescript-lang/rescript-vscode/releases/tag/1.0.6).

src/rescript-editor-support/NewCompletions.re

Lines changed: 88 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,6 @@ let getItems =
491491
let localModuleNames =
492492
allModules
493493
|> Utils.filterMap(name =>
494-
/* Log.log("Checking " ++ name); */
495494
Utils.startsWith(name, suffix) && !String.contains(name, '-')
496495
? Some((
497496
env.file.uri,
@@ -610,40 +609,78 @@ let mkItem = (~name, ~kind, ~detail, ~deprecated, ~docstring, ~uri, ~pos_lnum) =
610609

611610
let processCompletable =
612611
(
612+
~findItems,
613613
~full,
614614
~package,
615-
~state,
616615
~pos,
617616
~rawOpens,
618-
~allModules,
619617
completable: PartialParser.completable,
620618
) =>
621619
switch (completable) {
622-
| Cjsx(componentName, id) =>
623-
["first", "second"]
624-
|> List.map(name =>
625-
mkItem(
626-
~name=name ++ " " ++ componentName ++ " " ++ id,
627-
~kind=4,
628-
~deprecated=None,
629-
~detail="",
630-
~docstring=[],
631-
~uri=full.file.uri,
632-
~pos_lnum=fst(pos),
633-
)
634-
)
620+
| Cjsx(componentPath, prefix) =>
621+
let items = findItems(~exact=true, componentPath @ ["make"]);
622+
let labels = {
623+
switch (items) {
624+
| [(_uri, {SharedTypes.item: Value(typ)}), ..._] =>
625+
let rec getFields = (texp: Types.type_expr) =>
626+
switch (texp.desc) {
627+
| Tfield(name, _, t1, t2) =>
628+
let fields = t2 |> getFields;
629+
[(name, t1), ...fields];
630+
631+
| Tlink(te) => te |> getFields
632+
| Tvar(None) => []
633+
| _ => []
634+
};
635+
let rec getLabels = (t: Types.type_expr) =>
636+
switch (t.desc) {
637+
| Tlink(t1)
638+
| Tsubst(t1) => getLabels(t1)
639+
| Tarrow(
640+
Nolabel,
641+
{
642+
desc:
643+
Tconstr /* Js.t */(_, [{desc: Tobject(tObj, _)}], _) |
644+
Tobject(tObj, _),
645+
},
646+
_,
647+
_,
648+
) =>
649+
getFields(tObj)
650+
| _ => []
651+
};
652+
typ |> getLabels;
653+
| _ => []
654+
};
655+
};
635656

636-
| Cpath(parts) =>
637-
let items =
638-
getItems(
639-
~full,
640-
~package,
641-
~rawOpens,
642-
~getModule=State.fileForModule(state, ~package),
643-
~allModules,
644-
~pos,
645-
~parts,
657+
let mkLabel_ = (name, typString) =>
658+
mkItem(
659+
~name,
660+
~kind=4,
661+
~deprecated=None,
662+
~detail=typString,
663+
~docstring=[],
664+
~uri=full.file.uri,
665+
~pos_lnum=fst(pos),
646666
);
667+
let mkLabel = ((name, typ)) =>
668+
mkLabel_(name, typ |> Shared.typeToString);
669+
let keyLabel = mkLabel_("key", "string");
670+
671+
if (labels == []) {
672+
[];
673+
} else {
674+
[
675+
keyLabel,
676+
...labels
677+
|> List.filter(((name, _t)) => Utils.startsWith(name, prefix))
678+
|> List.map(mkLabel),
679+
];
680+
};
681+
682+
| Cpath(parts) =>
683+
let items = parts |> findItems(~exact=false);
647684
/* TODO(#107): figure out why we're getting duplicates. */
648685
items
649686
|> Utils.dedup
@@ -671,19 +708,8 @@ let processCompletable =
671708
);
672709

673710
| Cpipe(s) =>
674-
let getItems = parts =>
675-
getItems(
676-
~full,
677-
~package,
678-
~rawOpens,
679-
~getModule=State.fileForModule(state, ~package),
680-
~allModules,
681-
~pos,
682-
~parts,
683-
);
684-
685711
let getLhsType = (~lhs, ~partialName) => {
686-
switch (getItems([lhs])) {
712+
switch ([lhs] |> findItems(~exact=true)) {
687713
| [(_uri, {SharedTypes.item: Value(t)}), ..._] =>
688714
Some((t, partialName))
689715
| _ => None
@@ -701,7 +727,7 @@ let processCompletable =
701727

702728
let removePackageOpens = modulePath =>
703729
switch (modulePath) {
704-
| [toplevel, ...rest] when package.opens |> List.mem(toplevel) => rest
730+
| [toplevel, ...rest] when package.TopTypes.opens |> List.mem(toplevel) => rest
705731
| _ => modulePath
706732
};
707733

@@ -756,7 +782,7 @@ let processCompletable =
756782
modulePathMinusOpens == ""
757783
? name : modulePathMinusOpens ++ "." ++ name;
758784
let parts = modulePath @ [partialName];
759-
let items = getItems(parts);
785+
let items = parts |> findItems(~exact=false);
760786
items
761787
|> List.filter(((_, {item})) =>
762788
switch (item) {
@@ -839,19 +865,8 @@ let processCompletable =
839865
|> List.map(mkDecorator);
840866

841867
| Clabel(funPath, prefix) =>
842-
let getItems = parts =>
843-
getItems(
844-
~full,
845-
~package,
846-
~rawOpens,
847-
~getModule=State.fileForModule(state, ~package),
848-
~allModules,
849-
~pos,
850-
~parts,
851-
);
852-
853868
let labels = {
854-
switch (getItems(funPath)) {
869+
switch (funPath |> findItems(~exact=true)) {
855870
| [(_uri, {SharedTypes.item: Value(typ)}), ..._] =>
856871
let rec getLabels = (t: Types.type_expr) =>
857872
switch (t.desc) {
@@ -899,15 +914,28 @@ let computeCompletions = (~full, ~maybeText, ~package, ~pos, ~state) => {
899914
let rawOpens = PartialParser.findOpens(text, offset);
900915
let allModules =
901916
package.TopTypes.localModules @ package.dependencyModules;
917+
let findItems = (~exact, parts) => {
918+
let items =
919+
getItems(
920+
~full,
921+
~package,
922+
~rawOpens,
923+
~getModule=State.fileForModule(state, ~package),
924+
~allModules,
925+
~pos,
926+
~parts,
927+
);
928+
switch (parts |> List.rev) {
929+
| [last, ..._] when exact =>
930+
items
931+
|> List.filter(((_uri, {SharedTypes.name: {txt}})) =>
932+
txt == last
933+
)
934+
| _ => items
935+
};
936+
};
902937
completable
903-
|> processCompletable(
904-
~full,
905-
~package,
906-
~state,
907-
~pos,
908-
~rawOpens,
909-
~allModules,
910-
);
938+
|> processCompletable(~findItems, ~full, ~package, ~pos, ~rawOpens);
911939
}
912940
}
913941
};

src/rescript-editor-support/PartialParser.re

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ type completable =
170170
| Cdecorator(string) // e.g. @module
171171
| Clabel(list(string), string) // e.g. (["M", "foo"], "label") for M.foo(...~label...)
172172
| Cpath(list(string)) // e.g. ["M", "foo"] for M.foo
173-
| Cjsx(string, string) // E.g. ["M", "id"] for <M ... id
173+
| Cjsx(list(string), string) // E.g. (["M", "Comp"], "id") for <M.Comp ... id
174174
| Cpipe(string); // E.g. "x->foo"
175175

176176
let findCompletable = (text, offset) => {
@@ -186,10 +186,11 @@ let findCompletable = (text, offset) => {
186186
let parts = Str.split(Str.regexp_string("."), s);
187187
let parts = s.[len - 1] == '.' ? parts @ [""] : parts;
188188
switch (parts) {
189-
| [id] when String.lowercase_ascii(id) == id && false /* TODO */ =>
189+
| [id] when String.lowercase_ascii(id) == id =>
190190
switch (findJsxContext(text, offset - len - 1)) {
191191
| None => Cpath(parts)
192-
| Some(componentName) => Cjsx(componentName, id)
192+
| Some(componentName) =>
193+
Cjsx(Str.split(Str.regexp_string("."), componentName), id)
193194
}
194195
| _ => Cpath(parts)
195196
};

0 commit comments

Comments
 (0)