Current section
Files
Jump to
Current section
Files
src/glance_armstrong.erl
-module(glance_armstrong).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/glance_armstrong.gleam").
-export([format_diagnostic_without_span/1, format_reference_line/3, format_diagnostic_without_span_with_tips/2, format_source_diagnostic/3, format_source_diagnostic_with_tips/4, format_glance_parse_error/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Two-line terminal diagnostics: `line | source` then padded `^` + message.\n"
" Used by [`schema_definition.format_parse_error`](schema_definition.html#format_parse_error).\n"
).
-file("src/glance_armstrong.gleam", 130).
-spec gutter_display_width(binary()) -> integer().
gutter_display_width(Gutter) ->
string:length(Gutter).
-file("src/glance_armstrong.gleam", 44).
?DOC(" Same visual style when no byte span exists (unknown or out-of-range).\n").
-spec format_diagnostic_without_span(binary()) -> binary().
format_diagnostic_without_span(Message) ->
Gutter = <<" — | "/utf8>>,
Code_line = <<Gutter/binary, "<no source location>"/utf8>>,
Pad = gutter_display_width(Gutter),
Pointer = <<<<(gleam@string:repeat(<<" "/utf8>>, Pad))/binary, "^ "/utf8>>/binary,
Message/binary>>,
<<<<Code_line/binary, "\n"/utf8>>/binary, Pointer/binary>>.
-file("src/glance_armstrong.gleam", 113).
-spec render_gutter_block(integer(), binary(), integer(), integer(), binary()) -> binary().
render_gutter_block(Line_no, Line_text, Caret_col, Caret_width, Message) ->
Num = gleam@string:pad_start(
erlang:integer_to_binary(Line_no),
4,
<<" "/utf8>>
),
Gutter = <<Num/binary, " | "/utf8>>,
Code_line = <<Gutter/binary, Line_text/binary>>,
Pad_width = gutter_display_width(Gutter) + Caret_col,
Spaces = gleam@string:repeat(<<" "/utf8>>, Pad_width),
Carets = gleam@string:repeat(<<"^"/utf8>>, gleam@int:max(1, Caret_width)),
Pointer = <<<<<<Spaces/binary, Carets/binary>>/binary, " "/utf8>>/binary,
Message/binary>>,
<<<<Code_line/binary, "\n"/utf8>>/binary, Pointer/binary>>.
-file("src/glance_armstrong.gleam", 104).
?DOC(" Same gutter as [`format_source_diagnostic`](#format_source_diagnostic), for an arbitrary line (e.g. extra notes).\n").
-spec format_reference_line(integer(), binary(), binary()) -> binary().
format_reference_line(Line_no, Line_text, Message) ->
W = gleam@int:min(40, gleam@int:max(1, erlang:byte_size(Line_text))),
render_gutter_block(Line_no, Line_text, 0, W, Message).
-file("src/glance_armstrong.gleam", 146).
-spec format_one_tip_block(binary()) -> binary().
format_one_tip_block(Tip) ->
case gleam@string:split(Tip, <<"\n"/utf8>>) of
[] ->
<<" • "/utf8>>;
[First | Rest] ->
Head = <<" • "/utf8, First/binary>>,
gleam@list:fold(Rest, Head, fun(Acc, Line) -> case Line of
<<""/utf8>> ->
<<Acc/binary, "\n"/utf8>>;
_ ->
<<<<Acc/binary, "\n "/utf8>>/binary,
Line/binary>>
end end)
end.
-file("src/glance_armstrong.gleam", 141).
-spec format_tip_blocks(list(binary())) -> binary().
format_tip_blocks(Tips) ->
_pipe = gleam@list:map(Tips, fun format_one_tip_block/1),
gleam@string:join(_pipe, <<"\n"/utf8>>).
-file("src/glance_armstrong.gleam", 134).
-spec format_tips_suffix(list(binary())) -> binary().
format_tips_suffix(Tips) ->
case Tips of
[] ->
<<""/utf8>>;
Lines ->
<<"\n\n"/utf8, (format_tip_blocks(Lines))/binary>>
end.
-file("src/glance_armstrong.gleam", 63).
?DOC(" Like [`format_diagnostic_without_span`](#format_diagnostic_without_span), then bullet tips.\n").
-spec format_diagnostic_without_span_with_tips(binary(), list(binary())) -> binary().
format_diagnostic_without_span_with_tips(Message, Tips) ->
<<(format_diagnostic_without_span(Message))/binary,
(format_tips_suffix(Tips))/binary>>.
-file("src/glance_armstrong.gleam", 161).
-spec generic_param_highlight_bytes(binary()) -> gleam@option:option({integer(),
integer()}).
generic_param_highlight_bytes(Line_text) ->
case gleam@string:split(Line_text, <<"("/utf8>>) of
[Before, After_open | _] ->
Open_col = erlang:byte_size(Before),
case gleam@string:split(After_open, <<")"/utf8>>) of
[Inside | _] ->
W = (1 + erlang:byte_size(Inside)) + 1,
{some, {Open_col, gleam@int:max(1, W)}};
_ ->
none
end;
_ ->
none
end.
-file("src/glance_armstrong.gleam", 177).
-spec source_lines_with_byte_starts(binary()) -> list({integer(), binary()}).
source_lines_with_byte_starts(Source) ->
Parts = gleam@string:split(Source, <<"\n"/utf8>>),
{Rows_rev, _} = gleam@list:fold(
Parts,
{[], 0},
fun(State, Line_text) ->
{Acc_rows, Byte_offset} = State,
{[{Byte_offset, Line_text} | Acc_rows],
(Byte_offset + erlang:byte_size(Line_text)) + 1}
end
),
lists:reverse(Rows_rev).
-file("src/glance_armstrong.gleam", 190).
-spec source_indexed_lines(binary()) -> list({integer(), integer(), binary()}).
source_indexed_lines(Source) ->
Raw = source_lines_with_byte_starts(Source),
gleam@list:index_map(
Raw,
fun(Pair, I) ->
{Byte_start, Text} = Pair,
{I + 1, Byte_start, Text}
end
).
-file("src/glance_armstrong.gleam", 82).
-spec format_unexpected_eof_diagnostic(binary()) -> binary().
format_unexpected_eof_diagnostic(Source) ->
Indexed = source_indexed_lines(Source),
case gleam@list:last(Indexed) of
{error, nil} ->
format_diagnostic_without_span(
<<"unexpected end of input when parsing Gleam (empty file)"/utf8>>
);
{ok, {Line_no, _, Line_text}} ->
Line_blen = erlang:byte_size(Line_text),
Caret_col = gleam@int:max(0, Line_blen - 1),
render_gutter_block(
Line_no,
Line_text,
Caret_col,
1,
<<"unexpected end of input when parsing Gleam"/utf8>>
)
end.
-file("src/glance_armstrong.gleam", 198).
-spec find_line_for_byte(list({integer(), integer(), binary()}), integer()) -> {ok,
{integer(), integer(), binary()}} |
{error, nil}.
find_line_for_byte(Indexed, Byte_pos) ->
On_line = fun(Row) ->
{_, Start, Text} = Row,
After_start = Byte_pos >= Start,
Line_past = Start + erlang:byte_size(Text),
Before_end = Byte_pos =< Line_past,
After_start andalso Before_end
end,
case gleam@list:filter(Indexed, On_line) of
[Row@1 | _] ->
{ok, Row@1};
[] ->
case gleam@list:last(Indexed) of
{ok, Row@2} ->
{ok, Row@2};
{error, nil} ->
{error, nil}
end
end.
-file("src/glance_armstrong.gleam", 11).
?DOC(" `line | full line text` then a second line with spaces, carets, and `message`.\n").
-spec format_source_diagnostic(binary(), glance:span(), binary()) -> binary().
format_source_diagnostic(Source, Span, Message) ->
{span, Raw_start, Raw_end} = Span,
Start_byte = gleam@int:max(0, Raw_start),
End_byte = gleam@int:max(Start_byte, Raw_end),
Indexed = source_indexed_lines(Source),
case find_line_for_byte(Indexed, Start_byte) of
{error, nil} ->
format_diagnostic_without_span(Message);
{ok, {Line_no, Line_start, Line_text}} ->
Col = Start_byte - Line_start,
Line_blen = erlang:byte_size(Line_text),
Col_clamped = gleam@int:min(Col, Line_blen),
Max_w = gleam@int:max(1, Line_blen - Col_clamped),
Raw_w = End_byte - Start_byte,
{Caret_col, Caret_width} = case generic_param_highlight_bytes(
Line_text
) of
{some, {C, W}} ->
case gleam_stdlib:contains_string(
Message,
<<"generic parameters"/utf8>>
) of
true ->
{C, W};
false ->
{Col_clamped,
gleam@int:max(1, gleam@int:min(Raw_w, Max_w))}
end;
none ->
{Col_clamped, gleam@int:max(1, gleam@int:min(Raw_w, Max_w))}
end,
render_gutter_block(
Line_no,
Line_text,
Caret_col,
Caret_width,
Message
)
end.
-file("src/glance_armstrong.gleam", 53).
?DOC(" Like [`format_source_diagnostic`](#format_source_diagnostic), then bullet tips (for longer help text).\n").
-spec format_source_diagnostic_with_tips(
binary(),
glance:span(),
binary(),
list(binary())
) -> binary().
format_source_diagnostic_with_tips(Source, Span, Message, Tips) ->
<<(format_source_diagnostic(Source, Span, Message))/binary,
(format_tips_suffix(Tips))/binary>>.
-file("src/glance_armstrong.gleam", 70).
-spec format_glance_parse_error(binary(), glance:error()) -> binary().
format_glance_parse_error(Source, Error) ->
case Error of
unexpected_end_of_input ->
format_unexpected_eof_diagnostic(Source);
{unexpected_token, _, Pos} ->
format_source_diagnostic(
Source,
{span, erlang:element(2, Pos), erlang:element(2, Pos) + 1},
<<"unexpected token"/utf8>>
)
end.