-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 => <>, 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) -> <<<<<<<>/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>>.