1:- module(lsp_completion, [completions_at/3]).
14:- use_module(library(apply), [ maplist/3 ]). 15:- use_module(library(lists), [ numlist/3 ]). 16:- use_module(library(prolog_xref), [ xref_defined/3, xref_source/2 ]). 17:- use_module(library(yall)). 18
19:- include('_lsp_path_add.pl'). 20
21:- use_module(lsp(lsp_utils), [ linechar_offset/3 ]). 22:- use_module(lsp(lsp_changes), [ doc_text_fallback/2 ]). 23
24part_of_prefix(Code) :- code_type(Code, prolog_var_start).
25part_of_prefix(Code) :- code_type(Code, prolog_atom_start).
26part_of_prefix(Code) :- code_type(Code, prolog_identifier_continue).
27
28get_prefix_codes(Stream, Offset, Codes) :-
29 get_prefix_codes(Stream, Offset, [], Codes).
30
31get_prefix_codes(Stream, Offset0, Codes0, Codes) :-
32 peek_code(Stream, Code),
33 part_of_prefix(Code), !,
34 succ(Offset1, Offset0),
35 seek(Stream, Offset1, bof, Offset),
36 get_prefix_codes(Stream, Offset, [Code|Codes0], Codes).
37get_prefix_codes(_, _, Codes, Codes).
38
39prefix_at(File, Position, Prefix) :-
40 doc_text_fallback(File, DocCodes),
41 setup_call_cleanup(
42 open_string(DocCodes, Stream),
43 ( linechar_offset(Stream, Position, _),
44 seek(Stream, -1, current, Offset),
45 get_prefix_codes(Stream, Offset, PrefixCodes),
46 string_codes(Prefix, PrefixCodes) ),
47 close(Stream)
48 ).
49
50completions_at(File, Position, Completions) :-
51 prefix_at(File, Position, Prefix),
52 xref_source(File, [silent(true)]),
53 findall(
54 Result,
55 ( xref_defined(File, Goal, _),
56 functor(Goal, Name, Arity),
57 atom_concat(Prefix, _, Name),
58 ( predicate_arguments(File, Name/Arity, Args) -> true ; args_str(Arity, Args) ),
59 format(string(Func), "~w(~w)$0", [Name, Args]),
60 format(string(Label), "~w/~w", [Name, Arity]),
61 Result = _{label: Label,
62 insertText: Func,
63 insertTextFormat: 2} ),
64 Completions,
65 CompletionsTail
66 ),
67 findall(
68 Result,
69 ( predicate_property(system:Goal, built_in),
70 functor(Goal, Name, Arity),
71 atom_concat(Prefix, _, Name),
72 \+ sub_atom(Name, 0, _, _, '$'),
73 ( predicate_arguments(File, Name/Arity, Args) -> true ; args_str(Arity, Args) ),
74 format(string(Func), "~w(~w)$0", [Name, Args]),
75 format(string(Label), "~w/~w", [Name, Arity]),
76 Result = _{label: Label,
77 insertText: Func,
78 insertTextFormat: 2} ),
79 CompletionsTail
80 ).
81
82predicate_arguments(File, Pred/Arity, ArgsStr) :-
83 lsp_utils:predicate_help(File, Pred/Arity, HelpText),
84 string_concat(Pred, "(", PredName),
85 sub_string(HelpText, BeforeName, NameLen, _, PredName),
86 sub_string(HelpText, BeforeClose, _, _, ")"),
87 BeforeClose > BeforeName, !,
88 ArgsStart is BeforeName + NameLen,
89 ArgsLength is BeforeClose - ArgsStart,
90 sub_string(HelpText, ArgsStart, ArgsLength, _, ArgsStr0),
91 atomic_list_concat(Args, ', ', ArgsStr0),
92 length(Args, Length),
93 Arity = Length,
94 numlist(1, Length, Nums),
95 maplist([Arg, Num, S]>>format(string(S), "${~w:~w}", [Num, Arg]),
96 Args, Nums, Args1),
97 atomic_list_concat(Args1, ', ', ArgsStr).
98
99args_str(Arity, Str) :-
100 numlist(1, Arity, Args),
101 maplist([A, S]>>format(string(S), "${~w:_}", [A]),
102 Args, ArgStrs),
103 atomic_list_concat(ArgStrs, ', ', Str)
LSP Completion
This module implements code completion, based on defined predicates in the file & imports.
Uses
lsp_changes
in order to see the state of the buffer being edited.