-module(caffeine_lang@position_utils). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/caffeine_lang/position_utils.gleam"). -export([find_name_position/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. -file("src/caffeine_lang/position_utils.gleam", 58). -spec is_word_char(binary()) -> boolean(). is_word_char(G) -> case G of <<"_"/utf8>> -> true; _ -> case gleam@string:to_utf_codepoints(G) of [Cp] -> Code = gleam_stdlib:identity(Cp), (((Code >= 65) andalso (Code =< 90)) orelse ((Code >= 97) andalso (Code =< 122))) orelse ((Code >= 48) andalso (Code =< 57)); _ -> false end end. -file("src/caffeine_lang/position_utils.gleam", 30). ?DOC( " Searches for `name` as a whole word within `line`, starting from `offset`.\n" " Returns Ok(column) as 0-indexed offset on match, Error(Nil) if not found.\n" ). -spec find_whole_word(binary(), binary(), integer()) -> {ok, integer()} | {error, nil}. find_whole_word(Line, Name, Offset) -> case gleam@string:split_once(Line, Name) of {error, _} -> {error, nil}; {ok, {Before, After}} -> Col = Offset + string:length(Before), Before_ok = case gleam@string:last(Before) of {ok, C} -> not is_word_char(C); {error, _} -> true end, After_ok = case gleam@string:first(After) of {ok, C@1} -> not is_word_char(C@1); {error, _} -> true end, case Before_ok andalso After_ok of true -> {ok, Col}; false -> Skip = string:length(Before) + string:length(Name), find_whole_word( gleam@string:drop_start(Line, Skip), Name, Offset + Skip ) end end. -file("src/caffeine_lang/position_utils.gleam", 12). -spec find_in_lines(list(binary()), binary(), integer()) -> {integer(), integer()}. find_in_lines(Lines, Name, Line_num) -> case Lines of [] -> {1, 1}; [First | Rest] -> case find_whole_word(First, Name, 0) of {ok, Col} -> {Line_num, Col + 1}; {error, _} -> find_in_lines(Rest, Name, Line_num + 1) end end. -file("src/caffeine_lang/position_utils.gleam", 7). ?DOC( " Finds the 1-indexed line and column of the first whole-word occurrence\n" " of a name in source. Returns #(1, 1) if not found.\n" ). -spec find_name_position(binary(), binary()) -> {integer(), integer()}. find_name_position(Content, Name) -> Lines = gleam@string:split(Content, <<"\n"/utf8>>), find_in_lines(Lines, Name, 1).