Skip to content

Commit 89a2a4c

Browse files
authored
More completions (#1545)
* Add better support for completing keywords * Add support for completing map and list comprehensions * Add support for completing type after :: * Add test for completion of comprehensions
1 parent 9c0d48e commit 89a2a4c

File tree

3 files changed

+320
-38
lines changed

3 files changed

+320
-38
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-module(completion_more).
2+
3+
lc() ->
4+
[
5+
[].
6+
7+
mc() ->
8+
#{
9+
#{}.

apps/els_lsp/src/els_completion_provider.erl

+244-37
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,9 @@ find_completions(
293293
%% Check for "[...] #"
294294
[{'#', _} | _] ->
295295
definitions(Document, record);
296+
%% Check for "#{"
297+
[{'{', _}, {'#', _} | _] ->
298+
[map_comprehension_completion_item(Document, Line, Column)];
296299
%% Check for "[...] #anything"
297300
[_, {'#', _} | _] ->
298301
definitions(Document, record);
@@ -336,6 +339,9 @@ find_completions(
336339
Attribute =:= behaviour; Attribute =:= behavior
337340
->
338341
[item_kind_module(Module) || Module <- behaviour_modules("")];
342+
%% Check for "["
343+
[{'[', _} | _] ->
344+
[list_comprehension_completion_item(Document, Line, Column)];
339345
%% Check for "[...] fun atom"
340346
[{atom, _, _}, {'fun', _} | _] ->
341347
bifs(function, ItemFormat = arity_only) ++
@@ -345,42 +351,26 @@ find_completions(
345351
{ItemFormat, _POIKind} =
346352
completion_context(Document, Line, Column, Tokens),
347353
complete_type_definition(Document, Name, ItemFormat);
354+
%% Check for "::"
355+
[{'::', _} | _] = Tokens ->
356+
{ItemFormat, _POIKind} =
357+
completion_context(Document, Line, Column, Tokens),
358+
complete_type_definition(Document, '', ItemFormat);
348359
%% Check for ":: atom"
349360
[{atom, _, Name}, {'::', _} | _] = Tokens ->
350361
{ItemFormat, _POIKind} =
351362
completion_context(Document, Line, Column, Tokens),
352363
complete_type_definition(Document, Name, ItemFormat);
353364
%% Check for "[...] atom"
354365
[{atom, _, Name} | _] = Tokens ->
355-
NameBinary = atom_to_binary(Name, utf8),
356-
{ItemFormat, POIKind} = completion_context(Document, Line, Column, Tokens),
357-
case ItemFormat of
358-
arity_only ->
359-
#{text := Text} = Document,
360-
case
361-
is_in(Document, Line, Column, [nifs]) orelse
362-
is_in_heuristic(Text, <<"nifs">>, Line - 1)
363-
of
364-
true ->
365-
definitions(Document, POIKind, ItemFormat, false);
366-
_ ->
367-
%% Only complete unexported definitions when in
368-
%% export
369-
unexported_definitions(Document, POIKind)
370-
end;
371-
_ ->
372-
case complete_record_field(Opts, Tokens) of
373-
[] ->
374-
keywords(POIKind, ItemFormat) ++
375-
bifs(POIKind, ItemFormat) ++
376-
atoms(Document, NameBinary) ++
377-
all_record_fields(Document, NameBinary) ++
378-
modules(NameBinary) ++
379-
definitions(Document, POIKind, ItemFormat) ++
380-
snippets(POIKind, ItemFormat);
381-
RecordFields ->
382-
RecordFields
383-
end
366+
complete_atom(Name, Tokens, Opts);
367+
%% Treat keywords as atom completion
368+
[{Name, _} | _] = Tokens ->
369+
case lists:member(Name, keywords()) of
370+
true ->
371+
complete_atom(Name, Tokens, Opts);
372+
false ->
373+
[]
384374
end;
385375
Tokens ->
386376
?LOG_DEBUG(
@@ -392,6 +382,100 @@ find_completions(
392382
find_completions(_Prefix, _TriggerKind, _Opts) ->
393383
[].
394384

385+
-spec list_comprehension_completion_item(els_dt_document:item(), line(), column()) ->
386+
completion_item().
387+
list_comprehension_completion_item(#{text := Text}, Line, Column) ->
388+
Suffix =
389+
try els_text:get_char(Text, Line, Column + 1) of
390+
{ok, $]} ->
391+
%% Don't include ']' if next character is a ']'
392+
%% I.e if cursor is at []
393+
%% ^
394+
<<"">>;
395+
_ ->
396+
<<"]">>
397+
catch
398+
_:_:_ ->
399+
<<"]">>
400+
end,
401+
InsertText =
402+
case snippet_support() of
403+
true ->
404+
<<"${3:Expr} || ${2:Elem} <- ${1:List}", Suffix/binary>>;
405+
false ->
406+
<<"Expr || Elem <- List", Suffix/binary>>
407+
end,
408+
#{
409+
label => <<"[Expr || Elem <- List]">>,
410+
kind => ?COMPLETION_ITEM_KIND_KEYWORD,
411+
insertTextFormat => ?INSERT_TEXT_FORMAT_SNIPPET,
412+
insertText => InsertText
413+
}.
414+
415+
-spec map_comprehension_completion_item(els_dt_document:item(), line(), column()) ->
416+
completion_item().
417+
map_comprehension_completion_item(#{text := Text}, Line, Column) ->
418+
Suffix =
419+
try els_text:get_char(Text, Line, Column + 1) of
420+
{ok, $}} ->
421+
%% Don't include '}' if next character is a '}'
422+
%% I.e if cursor is at #{}
423+
%% ^
424+
<<"">>;
425+
_ ->
426+
<<"}">>
427+
catch
428+
_:_:_ ->
429+
<<"}">>
430+
end,
431+
InsertText =
432+
case snippet_support() of
433+
true ->
434+
<<"${4:K} => ${5:V} || ${2:K} => ${3:V} <- ${1:Map}", Suffix/binary>>;
435+
false ->
436+
<<"K => V || K := V <- Map", Suffix/binary>>
437+
end,
438+
#{
439+
label => <<"#{K => V || K := V <- Map}">>,
440+
kind => ?COMPLETION_ITEM_KIND_KEYWORD,
441+
insertTextFormat => ?INSERT_TEXT_FORMAT_SNIPPET,
442+
insertText => InsertText
443+
}.
444+
445+
-spec complete_atom(atom(), [any()], map()) -> [completion_item()].
446+
complete_atom(Name, Tokens, Opts) ->
447+
#{document := Document, line := Line, column := Column} = Opts,
448+
NameBinary = atom_to_binary(Name, utf8),
449+
{ItemFormat, POIKind} = completion_context(Document, Line, Column, Tokens),
450+
case ItemFormat of
451+
arity_only ->
452+
#{text := Text} = Document,
453+
case
454+
is_in(Document, Line, Column, [nifs]) orelse
455+
is_in_heuristic(Text, <<"nifs">>, Line - 1)
456+
of
457+
true ->
458+
definitions(Document, POIKind, ItemFormat, false);
459+
_ ->
460+
%% Only complete unexported definitions when in
461+
%% export
462+
unexported_definitions(Document, POIKind)
463+
end;
464+
_ ->
465+
case complete_record_field(Opts, Tokens) of
466+
[] ->
467+
keywords(POIKind, ItemFormat) ++
468+
bifs(POIKind, ItemFormat) ++
469+
atoms(Document, NameBinary) ++
470+
all_record_fields(Document, NameBinary) ++
471+
modules(NameBinary) ++
472+
definitions(Document, POIKind, ItemFormat) ++
473+
snippets(POIKind, ItemFormat);
474+
RecordFields ->
475+
RecordFields
476+
end
477+
end.
478+
395479
-spec complete_record_field(map(), list()) -> items().
396480
complete_record_field(_Opts, [{atom, _, _}, {'=', _} | _]) ->
397481
[];
@@ -999,7 +1083,15 @@ keywords(type_definition, _ItemFormat) ->
9991083
keywords(_POIKind, arity_only) ->
10001084
[];
10011085
keywords(_POIKind, _ItemFormat) ->
1002-
Keywords = [
1086+
Keywords = keywords(),
1087+
[
1088+
keyword_completion_item(K, snippet_support())
1089+
|| K <- Keywords
1090+
].
1091+
1092+
-spec keywords() -> [atom()].
1093+
keywords() ->
1094+
[
10031095
'after',
10041096
'and',
10051097
'andalso',
@@ -1015,9 +1107,11 @@ keywords(_POIKind, _ItemFormat) ->
10151107
'cond',
10161108
'div',
10171109
'end',
1110+
'else',
10181111
'fun',
10191112
'if',
10201113
'let',
1114+
'maybe',
10211115
'not',
10221116
'of',
10231117
'or',
@@ -1027,15 +1121,128 @@ keywords(_POIKind, _ItemFormat) ->
10271121
'try',
10281122
'when',
10291123
'xor'
1030-
],
1031-
[
1032-
#{
1033-
label => atom_to_binary(K, utf8),
1034-
kind => ?COMPLETION_ITEM_KIND_KEYWORD
1035-
}
1036-
|| K <- Keywords
10371124
].
10381125

1126+
-spec keyword_completion_item(_, _) -> _.
1127+
keyword_completion_item('case', true) ->
1128+
#{
1129+
label => <<"case">>,
1130+
kind => ?COMPLETION_ITEM_KIND_KEYWORD,
1131+
insertTextFormat => ?INSERT_TEXT_FORMAT_SNIPPET,
1132+
insertText =>
1133+
<<
1134+
"case ${1:Exprs} of\n"
1135+
" ${2:Pattern} ->\n"
1136+
" ${3:Body}\n"
1137+
"end"
1138+
>>
1139+
};
1140+
keyword_completion_item('try', true) ->
1141+
#{
1142+
label => <<"try">>,
1143+
kind => ?COMPLETION_ITEM_KIND_KEYWORD,
1144+
insertTextFormat => ?INSERT_TEXT_FORMAT_SNIPPET,
1145+
insertText =>
1146+
<<
1147+
"try ${1:Exprs}\n"
1148+
"catch\n"
1149+
" ${2:Class}:${3:ExceptionPattern}:${4:Stacktrace} ->\n"
1150+
" ${5:ExceptionBody}\n"
1151+
"end"
1152+
>>
1153+
};
1154+
keyword_completion_item('catch', true) ->
1155+
#{
1156+
label => <<"catch">>,
1157+
kind => ?COMPLETION_ITEM_KIND_KEYWORD,
1158+
insertTextFormat => ?INSERT_TEXT_FORMAT_SNIPPET,
1159+
insertText =>
1160+
<<
1161+
"catch\n"
1162+
" ${1:Class}:${2:ExceptionPattern}:${3:Stacktrace} ->\n"
1163+
" ${4:ExceptionBody}\n"
1164+
"end"
1165+
>>
1166+
};
1167+
keyword_completion_item('begin', true) ->
1168+
#{
1169+
label => <<"begin">>,
1170+
kind => ?COMPLETION_ITEM_KIND_KEYWORD,
1171+
insertTextFormat => ?INSERT_TEXT_FORMAT_SNIPPET,
1172+
insertText =>
1173+
<<
1174+
"begin\n"
1175+
" ${1:Body}\n"
1176+
"end"
1177+
>>
1178+
};
1179+
keyword_completion_item('maybe', true) ->
1180+
#{
1181+
label => <<"maybe">>,
1182+
kind => ?COMPLETION_ITEM_KIND_KEYWORD,
1183+
insertTextFormat => ?INSERT_TEXT_FORMAT_SNIPPET,
1184+
insertText =>
1185+
<<
1186+
"maybe\n"
1187+
" ${1:Body}\n"
1188+
"end"
1189+
>>
1190+
};
1191+
keyword_completion_item('after', true) ->
1192+
#{
1193+
label => <<"after">>,
1194+
kind => ?COMPLETION_ITEM_KIND_KEYWORD,
1195+
insertTextFormat => ?INSERT_TEXT_FORMAT_SNIPPET,
1196+
insertText =>
1197+
<<
1198+
"after\n"
1199+
" ${1:Duration} ->\n"
1200+
" ${2:Body}"
1201+
>>
1202+
};
1203+
keyword_completion_item('else', true) ->
1204+
#{
1205+
label => <<"else">>,
1206+
kind => ?COMPLETION_ITEM_KIND_KEYWORD,
1207+
insertTextFormat => ?INSERT_TEXT_FORMAT_SNIPPET,
1208+
insertText =>
1209+
<<
1210+
"else\n"
1211+
" ${1:Pattern} ->\n"
1212+
" ${2:Body}"
1213+
>>
1214+
};
1215+
keyword_completion_item('of', true) ->
1216+
#{
1217+
label => <<"of">>,
1218+
kind => ?COMPLETION_ITEM_KIND_KEYWORD,
1219+
insertTextFormat => ?INSERT_TEXT_FORMAT_SNIPPET,
1220+
insertText =>
1221+
<<
1222+
"of\n"
1223+
" ${1:Pattern} ->\n"
1224+
" ${2:Body}"
1225+
>>
1226+
};
1227+
keyword_completion_item('receive', true) ->
1228+
#{
1229+
label => <<"receive">>,
1230+
kind => ?COMPLETION_ITEM_KIND_KEYWORD,
1231+
insertTextFormat => ?INSERT_TEXT_FORMAT_SNIPPET,
1232+
insertText =>
1233+
<<
1234+
"receive\n"
1235+
" ${1:Pattern} ->\n"
1236+
" ${2:Body}\n"
1237+
"end"
1238+
>>
1239+
};
1240+
keyword_completion_item(K, _SnippetSupport) ->
1241+
#{
1242+
label => atom_to_binary(K, utf8),
1243+
kind => ?COMPLETION_ITEM_KIND_KEYWORD
1244+
}.
1245+
10391246
%%==============================================================================
10401247
%% Built-in functions
10411248
%%==============================================================================

0 commit comments

Comments
 (0)