Packages

A markup language to define ER schemas

Current section

Files

Jump to
prequel src prequel@internals@report.erl
Raw

src/prequel@internals@report.erl

-module(prequel@internals@report).
-compile([no_auto_import, nowarn_unused_vars]).
-export([format/1]).
-export_type([report_block/0, report/0, pretty_block/0]).
-type report_block() :: {context_block, prequel@span:span()} |
{error_block,
prequel@span:span(),
gleam@option:option(prequel@span:span()),
binary()}.
-type report() :: {report,
binary(),
binary(),
binary(),
binary(),
integer(),
integer(),
non_empty_list:non_empty_list(report_block()),
gleam@option:option(binary())}.
-type pretty_block() :: {pretty_block,
gleam@string_builder:string_builder(),
integer(),
integer()}.
-spec error_heading(binary(), binary()) -> gleam@string_builder:string_builder().
error_heading(Error_code, Error_name) ->
_pipe = [<<"[ "/utf8>>, Error_code, <<" ] Error: "/utf8>>, Error_name],
_pipe@1 = gleam@list:map(_pipe, fun gleam_community@ansi:red/1),
gleam@string_builder:from_strings(_pipe@1).
-spec highlight_line(
{integer(), binary()},
prequel@span:span(),
fun((binary()) -> binary())
) -> {integer(), binary()}.
highlight_line(Line, Span, Colour) ->
{Line_number, Line@1} = Line,
From = erlang:element(4, Span),
To = erlang:element(5, Span),
_pipe = case prequel@span:is_segment(Span) andalso prequel@span:contains_line(
Span,
Line_number
) of
true ->
prequel@internals@extra@string:highlight_from_to(
Line@1,
From,
To,
Colour
);
false ->
case prequel@span:classify_line(Span, Line_number) of
first ->
prequel@internals@extra@string:highlight_from(
Line@1,
From,
Colour
);
last ->
prequel@internals@extra@string:highlight_up_to(
Line@1,
To,
Colour
);
inside ->
Colour(Line@1);
outside ->
Line@1
end
end,
gleam@pair:new(Line_number, _pipe).
-spec highlight_lines(
list({integer(), binary()}),
prequel@span:span(),
fun((binary()) -> binary())
) -> list({integer(), binary()}).
highlight_lines(Lines, Span, Colour) ->
gleam@list:map(
Lines,
fun(_capture) -> highlight_line(_capture, Span, Colour) end
).
-spec left_pad_size(integer()) -> integer().
left_pad_size(Max_line) ->
Max_line_digits = prequel@internals@extra@int:count_digits(Max_line),
Max_line_digits + 2.
-spec pointed_message(binary(), integer(), integer()) -> gleam@string_builder:string_builder().
pointed_message(Message, Column_start, Max_line) ->
Left_pad_size = left_pad_size(Max_line),
Left_pad = begin
_pipe = [gleam@string:repeat(<<" "/utf8>>, Left_pad_size),
<<"┆"/utf8>>],
gleam@string_builder:from_strings(_pipe)
end,
Inner_pad_size = Column_start,
Inner_pad = gleam@string_builder:from_string(
gleam@string:repeat(<<" "/utf8>>, Inner_pad_size)
),
Extra_pad = gleam@string_builder:from_string(
gleam@string:repeat(<<" "/utf8>>, 3)
),
Max_line_length = 55 - ((Left_pad_size + Inner_pad_size) + 4),
Pointer = begin
_pipe@1 = [<<"╰"/utf8>>, <<"─"/utf8>>, <<" "/utf8>>],
_pipe@2 = gleam@list:map(_pipe@1, fun gleam_community@ansi:red/1),
gleam@string_builder:from_strings(_pipe@2)
end,
First_line_prefix = gleam@string_builder:join(
[Left_pad, Inner_pad, Pointer],
<<""/utf8>>
),
Lines_prefix = gleam@string_builder:join(
[Left_pad, Inner_pad, Extra_pad],
<<""/utf8>>
),
_assert_subject = begin
_pipe@3 = prequel@internals@extra@string:chunks_of(
Message,
Max_line_length
),
gleam@list:map(_pipe@3, fun gleam_community@ansi:red/1)
end,
[First_line | Lines] = case _assert_subject of
[_ | _] -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"prequel/internals/report"/utf8>>,
function => <<"pointed_message"/utf8>>,
line => 433})
end,
First_line@1 = gleam@string_builder:append(First_line_prefix, First_line),
Lines@1 = begin
_pipe@4 = Lines,
gleam@list:map(
_pipe@4,
fun(Line) -> gleam@string_builder:append(Lines_prefix, Line) end
)
end,
_pipe@5 = [First_line@1 | Lines@1],
gleam@string_builder:join(_pipe@5, <<"\n"/utf8>>).
-spec pointing_message(prequel@span:span(), binary(), integer()) -> gleam@string_builder:string_builder().
pointing_message(Pointed, Comment, Max_line) ->
Start = erlang:element(4, Pointed),
End = erlang:element(5, Pointed),
Underline_size = (End - Start) + 1,
Underline = begin
_pipe = [gleam@string:repeat(<<" "/utf8>>, Start - 1),
gleam_community@ansi:red(<<"┬"/utf8>>),
gleam_community@ansi:red(
gleam@string:repeat(<<"─"/utf8>>, Underline_size - 1)
)],
gleam@string_builder:from_strings(_pipe)
end,
Left_pad = gleam@string:repeat(<<" "/utf8>>, left_pad_size(Max_line)),
Prefix = begin
_pipe@1 = [Left_pad, <<"┆"/utf8>>, <<" "/utf8>>],
gleam@string_builder:from_strings(_pipe@1)
end,
First_line = gleam@string_builder:join([Prefix, Underline], <<""/utf8>>),
_pipe@2 = [First_line, pointed_message(Comment, Start, Max_line)],
gleam@string_builder:join(_pipe@2, <<"\n"/utf8>>).
-spec left_pad(integer()) -> binary().
left_pad(Max_line) ->
gleam@string:repeat(<<" "/utf8>>, left_pad_size(Max_line)).
-spec file_heading(binary(), integer(), integer(), integer(), integer()) -> gleam@string_builder:string_builder().
file_heading(File_name, Error_line, Error_column, Max_line, Error_code_length) ->
Error_line@1 = gleam@int:to_string(Error_line),
Error_column@1 = gleam@int:to_string(Error_column),
Line_length = (Error_code_length + 4) - left_pad_size(Max_line),
Line = <<"â•­"/utf8,
(gleam@string:repeat(<<"─"/utf8>>, Line_length - 1))/binary>>,
_pipe = [left_pad(Max_line),
Line,
<<" "/utf8>>,
File_name,
<<":"/utf8>>,
Error_line@1,
<<":"/utf8>>,
Error_column@1],
gleam@string_builder:from_strings(_pipe).
-spec connection_from_file_heading_to_blocks(integer(), integer()) -> gleam@string_builder:string_builder().
connection_from_file_heading_to_blocks(Min_line, Max_line) ->
Vertical_line = case Min_line of
1 ->
<<"│"/utf8>>;
_ ->
<<"┆"/utf8>>
end,
_pipe = [left_pad(Max_line), Vertical_line],
gleam@string_builder:from_strings(_pipe).
-spec dashed_separator(integer()) -> gleam@string_builder:string_builder().
dashed_separator(Max_line) ->
gleam@string_builder:from_strings([left_pad(Max_line), <<"┆"/utf8>>]).
-spec join_two_pretty_blocks(pretty_block(), pretty_block(), integer()) -> pretty_block().
join_two_pretty_blocks(One, Other, Max_line) ->
Joined = begin
_pipe = case prequel@internals@extra@int:is_next_to(
erlang:element(4, One),
erlang:element(3, Other)
) of
true ->
[erlang:element(2, One), erlang:element(2, Other)];
false ->
[erlang:element(2, One),
dashed_separator(Max_line),
erlang:element(2, Other)]
end,
gleam@string_builder:join(_pipe, <<"\n"/utf8>>)
end,
{pretty_block, Joined, erlang:element(3, One), erlang:element(4, Other)}.
-spec join_pretty_blocks(
non_empty_list:non_empty_list(pretty_block()),
integer()
) -> pretty_block().
join_pretty_blocks(Blocks, Max_line) ->
_pipe = Blocks,
non_empty_list:reduce(
_pipe,
fun(One, Other) -> join_two_pretty_blocks(One, Other, Max_line) end
).
-spec add_line_number(integer(), binary(), integer()) -> gleam@string_builder:string_builder().
add_line_number(Line_number, Line, Max_line) ->
Max_line_digits = prequel@internals@extra@int:count_digits(Max_line),
Line_digits = prequel@internals@extra@int:count_digits(Line_number),
Left_pad_size = ((Max_line_digits + 2) - Line_digits) - 1,
Left_pad = gleam@string:repeat(<<" "/utf8>>, Left_pad_size),
_pipe = [Left_pad,
gleam@int:to_string(Line_number),
<<" "/utf8>>,
<<"│"/utf8>>,
<<" "/utf8>>,
Line],
gleam@string_builder:from_strings(_pipe).
-spec block_to_spans(report_block()) -> non_empty_list:non_empty_list(prequel@span:span()).
block_to_spans(Block) ->
case Block of
{context_block, Span} ->
non_empty_list:single(Span);
{error_block, Span@1, Context, _} ->
case Context of
{some, Context_span} ->
non_empty_list:new(Span@1, [Context_span]);
none ->
non_empty_list:single(Span@1)
end
end.
-spec max_line(non_empty_list:non_empty_list(report_block())) -> integer().
max_line(Blocks) ->
_pipe = Blocks,
_pipe@1 = non_empty_list:flat_map(_pipe, fun block_to_spans/1),
_pipe@2 = non_empty_list:flat_map(_pipe@1, fun prequel@span:lines/1),
non_empty_list:reduce(_pipe@2, fun gleam@int:max/2).
-spec min_line(non_empty_list:non_empty_list(report_block())) -> integer().
min_line(Blocks) ->
_pipe = Blocks,
_pipe@1 = non_empty_list:flat_map(_pipe, fun block_to_spans/1),
_pipe@2 = non_empty_list:flat_map(_pipe@1, fun prequel@span:lines/1),
non_empty_list:reduce(_pipe@2, fun gleam@int:min/2).
-spec select_lines_range(list({integer(), binary()}), prequel@span:span()) -> list({integer(),
binary()}).
select_lines_range(Code_lines, Span) ->
_pipe = Code_lines,
_pipe@1 = gleam@list:drop(_pipe, erlang:element(2, Span) - 1),
gleam@list:take(
_pipe@1,
(erlang:element(3, Span) - erlang:element(2, Span)) + 1
).
-spec context_to_string(
prequel@span:span(),
list({integer(), binary()}),
integer()
) -> gleam@string_builder:string_builder().
context_to_string(Span, Code_lines, Max_line) ->
_pipe = select_lines_range(Code_lines, Span),
_pipe@1 = gleam@list:map(
_pipe,
fun(Pair) ->
add_line_number(
erlang:element(1, Pair),
erlang:element(2, Pair),
Max_line
)
end
),
gleam@string_builder:join(_pipe@1, <<"\n"/utf8>>).
-spec simple_error_to_string(
prequel@span:span(),
binary(),
list({integer(), binary()}),
integer()
) -> gleam@string_builder:string_builder().
simple_error_to_string(Pointed, Comment, Code_lines, Max_line) ->
_pipe = select_lines_range(Code_lines, Pointed),
_pipe@1 = highlight_lines(_pipe, Pointed, fun gleam_community@ansi:red/1),
_pipe@2 = gleam@list:map(
_pipe@1,
fun(Pair) ->
add_line_number(
erlang:element(1, Pair),
erlang:element(2, Pair),
Max_line
)
end
),
_pipe@3 = gleam@string_builder:join(_pipe@2, <<"\n"/utf8>>),
_pipe@4 = gleam@string_builder:append(_pipe@3, <<"\n"/utf8>>),
gleam@string_builder:append_builder(
_pipe@4,
pointing_message(Pointed, Comment, Max_line)
).
-spec error_with_context_to_string(
prequel@span:span(),
prequel@span:span(),
binary(),
list({integer(), binary()}),
integer()
) -> gleam@string_builder:string_builder().
error_with_context_to_string(Pointed, Underlined, Comment, Code_lines, Max_line) ->
_pipe = prequel@span:merge(Pointed, Underlined),
_pipe@1 = select_lines_range(Code_lines, _pipe),
_pipe@2 = highlight_lines(_pipe@1, Pointed, fun gleam_community@ansi:red/1),
_pipe@3 = highlight_lines(
_pipe@2,
Underlined,
fun gleam_community@ansi:blue/1
),
_pipe@4 = gleam@list:map(
_pipe@3,
fun(Pair) ->
add_line_number(
erlang:element(1, Pair),
erlang:element(2, Pair),
Max_line
)
end
),
_pipe@5 = gleam@string_builder:join(_pipe@4, <<"\n"/utf8>>),
_pipe@6 = gleam@string_builder:append(_pipe@5, <<"\n"/utf8>>),
gleam@string_builder:append_builder(
_pipe@6,
pointing_message(Pointed, Comment, Max_line)
).
-spec error_to_string(
prequel@span:span(),
gleam@option:option(prequel@span:span()),
binary(),
list({integer(), binary()}),
integer()
) -> gleam@string_builder:string_builder().
error_to_string(Pointed, Underlined, Comment, Code_lines, Max_line) ->
case Underlined of
none ->
simple_error_to_string(Pointed, Comment, Code_lines, Max_line);
{some, Underlined@1} ->
error_with_context_to_string(
Pointed,
Underlined@1,
Comment,
Code_lines,
Max_line
)
end.
-spec block_content_to_string(
list({integer(), binary()}),
report_block(),
integer()
) -> gleam@string_builder:string_builder().
block_content_to_string(Code_lines, Block, Max_line) ->
case Block of
{context_block, Span} ->
context_to_string(Span, Code_lines, Max_line);
{error_block, Pointed, Underlined, Comment} ->
error_to_string(Pointed, Underlined, Comment, Code_lines, Max_line)
end.
-spec block_start_line(report_block()) -> integer().
block_start_line(Block) ->
min_line(non_empty_list:single(Block)).
-spec block_end_line(report_block()) -> integer().
block_end_line(Block) ->
max_line(non_empty_list:single(Block)).
-spec to_pretty_block(report_block(), list({integer(), binary()}), integer()) -> pretty_block().
to_pretty_block(Block, Code_lines, Max_line) ->
Content = block_content_to_string(Code_lines, Block, Max_line),
{pretty_block, Content, block_start_line(Block), block_end_line(Block)}.
-spec blocks_to_string_builder(
binary(),
non_empty_list:non_empty_list(report_block()),
integer()
) -> gleam@string_builder:string_builder().
blocks_to_string_builder(Source_code, Blocks, Max_line) ->
Code_lines = begin
_pipe = gleam@string:split(Source_code, <<"\n"/utf8>>),
gleam@list:index_map(_pipe, fun(Index, Line) -> {Index + 1, Line} end)
end,
_pipe@1 = Blocks,
_pipe@2 = non_empty_list:map(
_pipe@1,
fun(_capture) -> to_pretty_block(_capture, Code_lines, Max_line) end
),
_pipe@3 = join_pretty_blocks(_pipe@2, Max_line),
(fun(Joined) -> erlang:element(2, Joined) end)(_pipe@3).
-spec hint_to_string_builder(gleam@option:option(binary())) -> gleam@string_builder:string_builder().
hint_to_string_builder(Hint) ->
case Hint of
none ->
gleam@string_builder:new();
{some, Hint@1} ->
gleam@string_builder:from_strings([<<"\n"/utf8>>, Hint@1])
end.
-spec format(report()) -> gleam@string_builder:string_builder().
format(Report) ->
Error_code_length = gleam@string:length(erlang:element(5, Report)),
Max_line = max_line(erlang:element(8, Report)),
Min_line = min_line(erlang:element(8, Report)),
_pipe = [error_heading(erlang:element(5, Report), erlang:element(4, Report)),
file_heading(
erlang:element(2, Report),
erlang:element(6, Report),
erlang:element(7, Report),
Max_line,
Error_code_length
),
connection_from_file_heading_to_blocks(Min_line, Max_line),
blocks_to_string_builder(
erlang:element(3, Report),
erlang:element(8, Report),
Max_line
),
hint_to_string_builder(erlang:element(9, Report))],
_pipe@1 = gleam@list:filter(
_pipe,
fun(Builder) -> not gleam@string_builder:is_empty(Builder) end
),
gleam@string_builder:join(_pipe@1, <<"\n"/utf8>>).