Current section

Files

Jump to
caffeine_lang src caffeine_lang@source_snippet.erl
Raw

src/caffeine_lang@source_snippet.erl

-module(caffeine_lang@source_snippet).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/caffeine_lang/source_snippet.gleam").
-export([extract_snippet/4]).
-export_type([source_snippet/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type source_snippet() :: {source_snippet, binary()}.
-file("src/caffeine_lang/source_snippet.gleam", 66).
-spec extract_lines_loop(
list(binary()),
integer(),
integer(),
integer(),
list({integer(), binary()})
) -> list({integer(), binary()}).
extract_lines_loop(Lines, Current, Start, End, Acc) ->
case Lines of
[] ->
Acc;
[Line | Rest] ->
case Current > End of
true ->
Acc;
false ->
New_acc = case Current >= Start of
true ->
[{Current, Line} | Acc];
false ->
Acc
end,
extract_lines_loop(Rest, Current + 1, Start, End, New_acc)
end
end.
-file("src/caffeine_lang/source_snippet.gleam", 57).
?DOC(" Extracts lines from start_line to end_line (1-indexed, inclusive).\n").
-spec extract_lines(list(binary()), integer(), integer()) -> list({integer(),
binary()}).
extract_lines(Lines, Start_line, End_line) ->
_pipe = extract_lines_loop(Lines, 1, Start_line, End_line, []),
lists:reverse(_pipe).
-file("src/caffeine_lang/source_snippet.gleam", 91).
?DOC(" Formats a single line with its line number gutter.\n").
-spec format_line(integer(), binary(), integer()) -> binary().
format_line(Line_num, Content, Gutter_width) ->
Num_str = erlang:integer_to_binary(Line_num),
Padding = gleam@string:repeat(
<<" "/utf8>>,
Gutter_width - string:length(Num_str)
),
<<<<<<Padding/binary, Num_str/binary>>/binary, " | "/utf8>>/binary,
Content/binary>>.
-file("src/caffeine_lang/source_snippet.gleam", 98).
?DOC(" Formats the marker line with carets under the error position.\n").
-spec format_marker(integer(), integer(), integer()) -> binary().
format_marker(Column, Span_width, Gutter_width) ->
Gutter_space = gleam@string:repeat(<<" "/utf8>>, Gutter_width),
Col_space = gleam@string:repeat(<<" "/utf8>>, gleam@int:max(0, Column - 1)),
Carets = gleam@string:repeat(<<"^"/utf8>>, Span_width),
<<<<<<Gutter_space/binary, " | "/utf8>>/binary, Col_space/binary>>/binary,
Carets/binary>>.
-file("src/caffeine_lang/source_snippet.gleam", 116).
-spec insert_marker_loop(
list(binary()),
integer(),
binary(),
integer(),
list(binary())
) -> list(binary()).
insert_marker_loop(Lines, Error_index, Marker, Current, Acc) ->
case Lines of
[] ->
Acc;
[Line | Rest] ->
New_acc = case Current =:= Error_index of
true ->
[Marker, Line | Acc];
false ->
[Line | Acc]
end,
insert_marker_loop(Rest, Error_index, Marker, Current + 1, New_acc)
end.
-file("src/caffeine_lang/source_snippet.gleam", 106).
?DOC(" Inserts the marker line after the error line in the context.\n").
-spec insert_marker(list(binary()), integer(), integer(), binary()) -> list(binary()).
insert_marker(Context_lines, Error_line, Start_line, Marker_line) ->
_pipe = insert_marker_loop(
Context_lines,
Error_line - Start_line,
Marker_line,
0,
[]
),
lists:reverse(_pipe).
-file("src/caffeine_lang/source_snippet.gleam", 17).
?DOC(
" Extracts a source snippet around the given line/column position.\n"
" Shows 1 line of context above and below the error line.\n"
" Adds a caret (^) marker under the error position.\n"
" Line and column are 1-indexed.\n"
).
-spec extract_snippet(
binary(),
integer(),
integer(),
gleam@option:option(integer())
) -> source_snippet().
extract_snippet(Source_content, Line, Column, End_column) ->
Lines = gleam@string:split(Source_content, <<"\n"/utf8>>),
Total_lines = erlang:length(Lines),
Start_line = gleam@int:max(1, Line - 1),
End_line = gleam@int:min(Total_lines, Line + 1),
Gutter_width = string:length(erlang:integer_to_binary(End_line)),
Context_lines = begin
_pipe = extract_lines(Lines, Start_line, End_line),
gleam@list:map(
_pipe,
fun(Pair) ->
{Line_num, Line_content} = Pair,
format_line(Line_num, Line_content, Gutter_width)
end
)
end,
Span_width = case End_column of
{some, End_col} ->
gleam@int:max(1, End_col - Column);
none ->
1
end,
Marker_line = format_marker(Column, Span_width, Gutter_width),
Rendered = begin
_pipe@1 = insert_marker(Context_lines, Line, Start_line, Marker_line),
gleam@string:join(_pipe@1, <<"\n"/utf8>>)
end,
{source_snippet, Rendered}.