-module(spanner).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/spanner.gleam").
-export([start/0, between/2, advance/2, label/2]).
-export_type([position/0, span/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.
?MODULEDOC(" Spanner — source code position and span tracking.\n").
-type position() :: {position, integer(), integer(), integer()}.
-type span() :: {span, position(), position()}.
-file("src/spanner.gleam", 16).
?DOC(" Returns the initial position in a source file: line 1, column 1, byte offset 0.\n").
-spec start() -> position().
start() ->
{position, 1, 1, 0}.
-file("src/spanner.gleam", 21).
?DOC(" Creates a span between two positions.\n").
-spec between(position(), position()) -> span().
between(Start, End) ->
{span, Start, End}.
-file("src/spanner.gleam", 28).
?DOC(
" Advances a position in a source string.\n"
" Handles `\\r\\n` and `\\r` line endings. Columns count graphemes, so\n"
" multi-byte characters like `é` advance the column by 1.\n"
).
-spec advance(position(), binary()) -> position().
advance(Position, Source) ->
Normalized = begin
_pipe = Source,
_pipe@1 = gleam@string:replace(_pipe, <<"\r\n"/utf8>>, <<"\n"/utf8>>),
gleam@string:replace(_pipe@1, <<"\r"/utf8>>, <<"\n"/utf8>>)
end,
Lines = gleam@string:split(Normalized, <<"\n"/utf8>>),
case Lines of
[_] ->
{position,
erlang:element(2, Position),
erlang:element(3, Position) + string:length(Normalized),
erlang:element(4, Position) + erlang:byte_size(Source)};
_ ->
Last_line@1 = case gleam@list:last(Lines) of
{ok, Last_line} -> Last_line;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"spanner"/utf8>>,
function => <<"advance"/utf8>>,
line => 43,
value => _assert_fail,
start => 1191,
'end' => 1234,
pattern_start => 1202,
pattern_end => 1215})
end,
{position,
(erlang:element(2, Position) + erlang:length(Lines)) - 1,
string:length(Last_line@1) + 1,
erlang:element(4, Position) + erlang:byte_size(Source)}
end.
-file("src/spanner.gleam", 54).
?DOC(" Formats a position with a filename, e.g. `\"src/main.gl:3:5\"`.\n").
-spec label(binary(), span()) -> binary().
label(Filename, Span) ->
<<<<<<<<Filename/binary, ":"/utf8>>/binary,
(erlang:integer_to_binary(
erlang:element(2, erlang:element(2, Span))
))/binary>>/binary,
":"/utf8>>/binary,
(erlang:integer_to_binary(erlang:element(3, erlang:element(2, Span))))/binary>>.