1:- module(lsp_formatter_parser, [ reified_format_for_file/2,
2 emit_reified/2 ]).
11:- use_module(library(apply)). 12:- use_module(library(apply_macros)). 13:- use_module(library(clpfd)). 14:- use_module(library(rbtrees)). 15:- use_module(library(readutil), [ read_file_to_string/3 ]). 16
17:- include('_lsp_path_add.pl'). 18
19:- use_module(lsp(lsp_reading_source), [ file_lines_start_end/2,
20 read_term_positions/2,
21 file_offset_line_position/4 ]). 22
23:- thread_local current_source_string/1.
29reified_format_for_file(Path, Reified) :-
30 retractall(current_source_string(_)),
31 read_file_to_string(Path, FileString, []),
32 read_term_positions(Path, TermsWithPos),
33 setup_call_cleanup(
34 assertz(current_source_string(FileString)),
35 expand_term_positions(TermsWithPos, Reified0),
36 retractall(current_source_string(_))
37 ),
38 sort(1, @=<, Reified0, Reified1),
39 file_lines_start_end(Path, LinesStartEnd),
40 InitState = _{last_line: 1, last_char: 0, line_bounds: LinesStartEnd},
41 add_whitespace_terms(InitState, Reified1, Reified2),
42 simplify_reified_terms(Reified2, Reified).
43
46simplify_reified_terms(In, Out) :-
47 maplist(simplify_reified_term, In, Out).
48
49simplify_reified_term(newline, newline) :- !.
50simplify_reified_term(white(N), white(N)) :- !.
51simplify_reified_term(Term, SimpleTerm) :-
52 53 compound_name_arguments(Term, Name, [_, _|Args]),
54 ( Args = []
55 -> SimpleTerm = Name
56 ; compound_name_arguments(SimpleTerm, Name, Args) ).
62emit_reified(_, []) :- !.
63emit_reified(To, [Term|Rest]) :-
64 emit_reified_(To, Term),
65 emit_reified(To, Rest).
66
67emit_reified_(To, newline) => write(To, "\n").
68emit_reified_(To, white(N)) =>
69 length(Whites, N),
70 maplist(=(0' ), Whites),
71 format(To, "~s", [Whites]).
72emit_reified_(To, comma) => format(To, ",", []).
73emit_reified_(To, simple(T)) =>
74 format(To, "~s", [T]).
75emit_reified_(To, simple_quoted(T)) =>
76 format(To, "'~q'", [T]).
77emit_reified_(To, string(T)), string(T) =>
78 format(To, "~q", [T]).
79emit_reified_(To, string(T)) =>
80 81 format(To, "`~s`", [T]).
82emit_reified_(To, term_begin(Func, _, Parens)) =>
83 ( Parens = true
84 -> Format = "~w("
85 ; Format = "~w" ),
86 format(To, Format, [Func]).
87emit_reified_(To, term_end(Parens, TermState)) =>
88 ( Parens = true
89 -> MaybeClose = ")"
90 ; MaybeClose = "" ),
91 ( TermState = toplevel
92 -> MaybeStop = "."
93 ; MaybeStop = "" ),
94 format(To, "~w~w", [MaybeClose, MaybeStop]).
95emit_reified_(To, list_begin) =>
96 format(To, "[", []).
97emit_reified_(To, list_tail) =>
98 format(To, "|", []).
99emit_reified_(To, list_end) =>
100 format(To, "]", []).
101emit_reified_(To, comment(Text)) =>
102 format(To, "~s", [Text]).
103emit_reified_(To, braces_begin) =>
104 format(To, "{", []).
105emit_reified_(To, braces_end) =>
106 format(To, "}", []).
107emit_reified_(To, parens_begin) =>
108 format(To, "(", []).
109emit_reified_(To, parens_end) =>
110 format(To, ")", []).
111emit_reified_(To, dict_tag('$var'(Tag))) =>
112 format(To, "~w", [Tag]).
113emit_reified_(To, dict_tag(Tag)), var(Tag) =>
114 115 format(To, "_", []).
116emit_reified_(To, dict_tag(Tag)) =>
117 118 format(To, "~w", [Tag]).
119emit_reified_(To, dict_begin) =>
120 format(To, "{", []).
121emit_reified_(To, dict_sep) =>
122 format(To, ":", []).
123emit_reified_(To, dict_end) =>
124 format(To, "}", []).
130add_whitespace_terms(_State, [], [newline]) :- !.
131add_whitespace_terms(State, [Term|Terms], Out) :-
132 arg(1, Term, TermStart),
133 stream_position_at_offset(State.line_bounds, TermStart, Pos),
134 sync_position_whitespace(State, Pos, Out, Out1),
135 Out1 = [Term|Out2],
136 arg(2, Term, TermEnd),
137 stream_position_at_offset(State.line_bounds, TermEnd, EndPos),
138 update_state_position(State, EndPos, State1),
139 add_whitespace_terms(State1, Terms, Out2).
140
141expand_term_positions([], []).
142expand_term_positions([InfoDict|Rest], Expanded0) :-
143 ( InfoDict.comments \= []
144 -> expand_comments_positions(InfoDict.comments, Expanded0, Expanded1)
145 ; Expanded1 = Expanded0 ),
146
147 Term = InfoDict.term,
148 ( Term \= end_of_file 149 -> expand_subterm_positions(Term, toplevel, InfoDict.subterm,
150 Expanded1, Expanded2)
151 ; Expanded2 = Expanded1 ),
152
153 expand_term_positions(Rest, Expanded2).
154
155expand_comments_positions([], Tail, Tail) :- !.
156expand_comments_positions([Comment|Rest], Expanded, Tail) :-
157 expand_comment_positions(Comment, Expanded, Tail0),
158 expand_comments_positions(Rest, Tail0, Tail).
159
160expand_comment_positions(CommentPos-Comment, Expanded, ExpandedTail) :-
161 term_end_position(Comment, CommentEndPosRel),
162 increment_stream_position(CommentPos, CommentEndPosRel, CommentEndPos),
163 stream_position_data(char_count, CommentPos, From),
164 stream_position_data(char_count, CommentEndPos, To),
165 Expanded = [comment(From, To, Comment)|ExpandedTail].
166
167expand_subterm_positions(Term, _TermState, term_position(_From, _To, FFrom, FTo, SubPoses),
168 Expanded, ExTail), functor(Term, ',', _, _) =>
169 170 Expanded = [comma(FFrom, FTo)|ExpandedTail0],
171 functor(Term, _, Arity, _),
172 expand_term_subterms_positions(false, Term, Arity, 1, SubPoses, ExpandedTail0, ExTail).
173expand_subterm_positions(Term, TermState, term_position(From, To, FFrom, FTo, SubPoses),
174 Expanded, ExTail) =>
175 176 functor(Term, _Func, Arity, TermType),
177 current_source_string(FileString),
178 Length is FTo - FFrom,
179 sub_string(FileString, FFrom, Length, _, FuncString),
180 atom_string(Func, FuncString),
181 182 183 184 ( From = FFrom, once(( ( max_subterm_to(SubPoses, SubTermMax), To > SubTermMax ) ;
185 ( Arity == 0, TermType == compound )))
186 -> ( Parens = true, FTo1 is FTo + 1 ) 187 ; ( Parens = false, FTo1 = FTo ) ),
188 Expanded = [term_begin(FFrom, FTo1, Func, TermType, Parens)|ExpandedTail0],
189 expand_term_subterms_positions(Parens, Term, Arity, 1, SubPoses,
190 ExpandedTail0, ExpandedTail1),
191 succ(To0, To),
192 ( TermState =@= toplevel -> To1 is To + 1 ; To1 = To ),
193 ExpandedTail1 = [term_end(To0, To1, Parens, TermState)|ExpandedTail2],
194 maybe_add_comma(TermState, To, ExpandedTail2, ExTail).
195expand_subterm_positions(Term, TermState, string_position(From, To), Expanded, Tail) =>
196 Expanded = [string(From, To, Term)|Tail0],
197 maybe_add_comma(TermState, To, Tail0, Tail).
198expand_subterm_positions(_Term, TermState, From-To, Expanded, Tail) =>
199 current_source_string(FileString),
200 Length is To - From,
201 sub_string(FileString, From, Length, _, SimpleString),
202 Expanded = [simple(From, To, SimpleString)|Tail0],
203 maybe_add_comma(TermState, To, Tail0, Tail).
204expand_subterm_positions(Term, TermState, list_position(From, To, Elms, HasTail), Expanded, Tail) =>
205 assertion(is_listish(Term)),
206 ListBeginTo is From + 1,
207 Expanded = [list_begin(From, ListBeginTo)|Expanded1],
208 expand_list_subterms_positions(Term, Elms, Expanded1, Expanded2),
209 succ(To0, To),
210 ( HasTail = none
211 -> Expanded2 = [list_end(To0, To)|Tail0]
212 ; ( arg(1, HasTail, TailFrom),
213 succ(TailBarFrom, TailFrom),
214 Expanded2 = [list_tail(TailBarFrom, TailFrom)|Expanded3],
215 list_tail(Term, Elms, ListTail),
216 expand_subterm_positions(ListTail, false, HasTail, Expanded3, Expanded4),
217 Expanded4 = [list_end(To0, To)|Tail0] ) ),
218 maybe_add_comma(TermState, To, Tail0, Tail).
219expand_subterm_positions(Term, TermState, brace_term_position(From, To, BracesPos), Expanded, Tail) =>
220 BraceTo is From + 1,
221 Expanded = [braces_begin(From, BraceTo)|Tail0],
222 Term = {Term0},
223 expand_subterm_positions(Term0, false, BracesPos, Tail0, Tail1),
224 succ(To1, To),
225 Tail1 = [braces_end(To1, To)|Tail2],
226 maybe_add_comma(TermState, To1, Tail2, Tail).
227expand_subterm_positions(Term, TermState, parentheses_term_position(From, To, ContentPos),
228 Expanded, Tail) =>
229 ParenTo is From + 1,
230 Expanded = [parens_begin(From, ParenTo)|Tail0],
231 expand_subterm_positions(Term, false, ContentPos, Tail0, Tail1),
232 succ(To1, To),
233 Tail1 = [parens_end(To1, To)|Tail2],
234 maybe_add_comma(TermState, To, Tail2, Tail).
235expand_subterm_positions(Term, TermState, dict_position(_From, To, TagFrom, TagTo, KeyValPos),
236 Expanded, Tail) =>
237 is_dict(Term, Tag),
238 DictBraceTo is TagTo + 1,
239 Expanded = [dict_tag(TagFrom, TagTo, Tag), dict_begin(TagTo, DictBraceTo)|Tail0],
240 expand_dict_kvs_positions(Term, KeyValPos, Tail0, Tail1),
241 succ(To1, To),
242 Tail1 = [dict_end(To1, To)|Tail2],
243 maybe_add_comma(TermState, To, Tail2, Tail).
244
245maybe_add_comma(subterm_item, CommaFrom, Tail0, Tail) :- !,
246 CommaTo is CommaFrom + 1,
247 Tail0 = [comma(CommaFrom, CommaTo)|Tail].
248maybe_add_comma(_, _, Tail, Tail).
249
250is_listish(L) :- \+ var(L), !.
251is_listish([]).
252is_listish([_|_]).
253
254list_tail(Tail, [], Tail) :- !.
255list_tail([_|Rest], [_|PosRest], Tail) :-
256 list_tail(Rest, PosRest, Tail).
257
258max_subterm_to(SubPoses, SubTermMaxTo) :-
259 aggregate_all(max(To),
260 ( member(Pos, SubPoses),
261 arg(2, Pos, To) ),
262 SubTermMaxTo).
263
264expand_dict_kvs_positions(_, [], Tail, Tail) :- !.
265expand_dict_kvs_positions(Dict, [Pos|Poses], Expanded0, Tail) :-
266 Pos = key_value_position(_From, To, SepFrom, SepTo, Key, KeyPos, ValuePos),
267 get_dict(Key, Dict, Value),
268 expand_subterm_positions(Key, false, KeyPos, Expanded0, Expanded1),
269 Expanded1 = [dict_sep(SepFrom, SepTo)|Expanded2],
270 expand_subterm_positions(Value, false, ValuePos, Expanded2, Expanded3),
271 CommaTo is To + 1,
272 ( Poses = [_|_]
273 -> Expanded3 = [comma(To, CommaTo)|Expanded4]
274 ; Expanded3 = Expanded4 ),
275 expand_dict_kvs_positions(Dict, Poses, Expanded4, Tail).
276
278expand_list_subterms_positions(_, [], Tail, Tail) :- !.
279expand_list_subterms_positions([Term|Terms], [Pos|Poses], Expanded, Tail) :-
280 ( Poses = [_|_]
281 -> TermState = subterm_item
282 ; TermState = false ),
283 expand_subterm_positions(Term, TermState, Pos, Expanded, Expanded1),
284 expand_list_subterms_positions(Terms, Poses, Expanded1, Tail).
285
286expand_term_subterms_positions(_Parens, _Term, _Arity, _Arg, [], Tail, Tail) :- !.
287expand_term_subterms_positions(Parens, Term, Arity, Arg, [SubPos|Poses], Expanded, ExpandedTail) :-
288 assertion(between(1, Arity, Arg)),
289 arg(Arg, Term, SubTerm),
290 ( Parens = true, Arg < Arity
291 -> State = subterm_item
292 ; State = false ),
293 expand_subterm_positions(SubTerm, State, SubPos, Expanded, Expanded0),
294 succ(Arg, Arg1),
295 expand_term_subterms_positions(Parens, Term, Arity, Arg1, Poses, Expanded0, ExpandedTail).
296
297increment_stream_position(StartPos, RelPos, EndPos) :-
298 stream_position_data(char_count, StartPos, StartCharCount),
299 stream_position_data(char_count, RelPos, RelCharCount),
300 CharCount is StartCharCount + RelCharCount,
301 stream_position_data(byte_count, StartPos, StartByteCount),
302 stream_position_data(byte_count, RelPos, RelByteCount),
303 ByteCount is StartByteCount + RelByteCount,
304 stream_position_data(line_count, StartPos, StartLineCount),
305 stream_position_data(line_count, RelPos, RelLineCount),
306 stream_position_data(line_position, StartPos, StartLinePosition),
307 stream_position_data(line_position, RelPos, RelLinePosition),
308 ( RelLineCount == 1
309 -> LineCount = StartLineCount,
310 LinePosition is StartLinePosition + RelLinePosition
311 ; ( LineCount is StartLineCount + RelLineCount - 1,
312 LinePosition = RelLinePosition ) ),
313 EndPos = '$stream_position_data'(CharCount, LineCount, LinePosition, ByteCount).
314
315update_state_position(State0, EndPos, State2) :-
316 stream_position_data(line_count, EndPos, EndLineCount),
317 stream_position_data(line_position, EndPos, EndLinePos),
318 put_dict(last_line, State0, EndLineCount, State1),
319 put_dict(last_char, State1, EndLinePos, State2).
320
321sync_position_whitespace(State, TermPos, Expanded, ExpandedTail) :-
322 PrevLineCount = State.last_line,
323 stream_position_data(line_count, TermPos, NewLineCount),
324 NewLines is NewLineCount - PrevLineCount,
325 ( NewLines > 0
326 -> n_copies_of(NewLines, newline, Expanded, Expanded0),
327 PrevLinePosition = 0
328 ; ( Expanded = Expanded0,
329 PrevLinePosition = State.last_char )
330 ),
331
332 stream_position_data(line_position, TermPos, NewLinePosition),
333 Whitespace is NewLinePosition - PrevLinePosition,
334 ( Whitespace > 0
335 -> Expanded0 = [white(Whitespace)|ExpandedTail]
336 ; Expanded0 = ExpandedTail ).
339stream_position_at_offset(LineCharMap, To, EndPos) :-
340 CharCount = To,
341 ByteCount = To, 342 file_offset_line_position(LineCharMap, To, LineCount, LinePosition),
343 344 EndPos = '$stream_position_data'(CharCount, LineCount, LinePosition, ByteCount).
345
347
348term_end_position(Term, Position) :-
349 setup_call_cleanup(
350 open_null_stream(Out),
351 ( set_stream(Out, newline(posix)),
352 write(Out, Term),
353 stream_property(Out, position(Position))
354 ),
355 close(Out)).
356
357n_copies_of(0, _, Tail, Tail) :- !.
358n_copies_of(N, ToCopy, [ToCopy|Rest], Tail) :-
359 N1 is N - 1,
360 n_copies_of(N1, ToCopy, Rest, Tail)
LSP Parser For Formatter
Module for parsing Prolog source code, for subsequent formatting