Skip to content

Commit 7945446

Browse files
committed
Allow preserving sigil metadata in container cursor to quoted
Closes #14243.
1 parent 2e7a49d commit 7945446

6 files changed

Lines changed: 124 additions & 50 deletions

File tree

lib/elixir/lib/code/fragment.ex

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,14 +1234,43 @@ defmodule Code.Fragment do
12341234
the cursor. This is necessary to correctly complete anonymous functions
12351235
and the left-hand side of `->`
12361236
1237+
* `:preserve_sigils` (since v1.20.0) - preserve sigil cursor location
1238+
(see "Tracking sigils" section below)
1239+
1240+
## Tracking sigils
1241+
1242+
The `:preserve_sigils` option can be used to track cursor positions inside
1243+
a sigil.
1244+
1245+
If the sigil is terminated abruptly, the `sigil_*` call will have the cursor
1246+
as the second argument:
1247+
1248+
iex> Code.Fragment.container_cursor_to_quoted("~r/foo", preserve_sigils: true)
1249+
{:ok,
1250+
{:sigil_r, [delimiter: "/", line: 1],
1251+
[{:<<>>, [line: 1], ["foo"]}, {:__cursor__, [line: 1, column: 7], []}]}}
1252+
1253+
In case the sigil is completed and has zero or more modifiers, the cursor will
1254+
be nested in the list, with all previous delimiters specified:
1255+
1256+
iex> Code.Fragment.container_cursor_to_quoted("~r/foo/i", preserve_sigils: true)
1257+
{:ok,
1258+
{:sigil_r, [delimiter: "/", line: 1],
1259+
[{:<<>>, [line: 1], ["foo"]}, [105, {:__cursor__, [line: 1, column: 9], []}]]}}
1260+
1261+
If the cursor is after the sigil, then it is discarded as everything else:
1262+
1263+
iex> Code.Fragment.container_cursor_to_quoted("~r/foo/i ", preserve_sigils: true)
1264+
{:ok, {:__cursor__, [line: 1], []}}
12371265
"""
12381266
@doc since: "1.13.0"
12391267
@spec container_cursor_to_quoted(List.Chars.t(), container_cursor_to_quoted_opts()) ::
12401268
{:ok, Macro.t()} | {:error, {location :: keyword, binary | {binary, binary}, binary}}
12411269
def container_cursor_to_quoted(fragment, opts \\ []) do
12421270
{trailing_fragment, opts} = Keyword.pop(opts, :trailing_fragment)
1271+
{preserve_sigils?, opts} = Keyword.pop(opts, :preserve_sigils, false)
12431272
opts = Keyword.take(opts, [:columns, :token_metadata, :literal_encoder])
1244-
opts = [check_terminators: {:cursor, []}] ++ opts
1273+
opts = [check_terminators: {:cursor, preserve_sigils?, []}] ++ opts
12451274

12461275
file = Keyword.get(opts, :file, "nofile")
12471276
line = Keyword.get(opts, :line, 1)
@@ -1272,7 +1301,7 @@ defmodule Code.Fragment do
12721301
Enum.split_while(rev_terminators, &(elem(&1, 0) not in [:do, :fn])),
12731302
true <- maybe_missing_stab?(rev_tokens, true),
12741303
opts =
1275-
Keyword.put(opts, :check_terminators, {:cursor, before_start}),
1304+
Keyword.put(opts, :check_terminators, {:cursor, false, before_start}),
12761305
{:error, {meta, _, ~c"end"}, _rest, _warnings, trailing_rev_tokens} <-
12771306
:elixir_tokenizer.tokenize(to_charlist(trailing_fragment), line, column, opts) do
12781307
trailing_tokens =

lib/elixir/src/elixir_interpolation.erl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ extract([], _Buffer, _Output, Line, Column, #elixir_tokenizer{cursor_completion=
2020
{error, {string, Line, Column, io_lib:format("missing terminator: ~ts", [[Last]]), []}};
2121

2222
extract([], Buffer, Output, Line, Column, Scope, _Interpol, _Last) ->
23-
finish_extraction([], Buffer, Output, Line, Column, Scope);
23+
finish_extraction([], false, Buffer, Output, Line, Column, Scope);
2424

2525
extract([Last | Rest], Buffer, Output, Line, Column, Scope, _Interpol, Last) ->
26-
finish_extraction(Rest, Buffer, Output, Line, Column + 1, Scope);
26+
finish_extraction(Rest, true, Buffer, Output, Line, Column + 1, Scope);
2727

2828
%% Going through the string
2929

@@ -113,7 +113,7 @@ extract_char(Rest, Buffer, Output, Line, Column, Scope, Interpol, Last) ->
113113
extract_nl(Rest, Buffer, Output, Line, Scope, Interpol, [H,H,H] = Last) ->
114114
case strip_horizontal_space(Rest, Buffer, 1) of
115115
{[H,H,H|NewRest], _NewBuffer, Column} ->
116-
finish_extraction(NewRest, Buffer, Output, Line + 1, Column + 3, Scope);
116+
finish_extraction(NewRest, true, Buffer, Output, Line + 1, Column + 3, Scope);
117117
{NewRest, NewBuffer, Column} ->
118118
extract(NewRest, NewBuffer, Output, Line + 1, Column, Scope, Interpol, Last)
119119
end;
@@ -267,13 +267,13 @@ unescape_map(E) -> E.
267267

268268
% Extract Helpers
269269

270-
finish_extraction(Remaining, Buffer, Output, Line, Column, Scope) ->
270+
finish_extraction(Remaining, Done, Buffer, Output, Line, Column, Scope) ->
271271
Final = case build_string(Buffer, Output) of
272272
[] -> [[]];
273273
F -> F
274274
end,
275275

276-
{Line, Column, lists:reverse(Final), Remaining, Scope}.
276+
{Line, Column, lists:reverse(Final), Remaining, Done, Scope}.
277277

278278
build_string([], Output) -> Output;
279279
build_string(Buffer, Output) -> [lists:reverse(Buffer) | Output].

lib/elixir/src/elixir_tokenizer.erl

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ tokenize(String, Line, Column, Opts) ->
121121
lists:foldl(fun
122122
({check_terminators, false}, Acc) ->
123123
Acc#elixir_tokenizer{cursor_completion=false, terminators=none};
124-
({check_terminators, {cursor, Terminators}}, Acc) ->
125-
Acc#elixir_tokenizer{cursor_completion=prune_and_cursor, terminators=Terminators};
124+
({check_terminators, {cursor, Sigils, Terminators}}, Acc) ->
125+
Acc#elixir_tokenizer{cursor_completion={prune_and_cursor, Sigils}, terminators=Terminators};
126126
({existing_atoms_only, ExistingAtomsOnly}, Acc) when is_boolean(ExistingAtomsOnly) ->
127127
Acc#elixir_tokenizer{existing_atoms_only=ExistingAtomsOnly};
128128
({static_atoms_encoder, StaticAtomsEncoder}, Acc) when is_function(StaticAtomsEncoder) ->
@@ -144,12 +144,9 @@ tokenize(String, Line, Opts) ->
144144

145145
tokenize([], Line, Column, #elixir_tokenizer{cursor_completion=Cursor} = Scope, Tokens) when Cursor /= false ->
146146
#elixir_tokenizer{ascii_identifiers_only=Ascii, terminators=Terminators, warnings=Warnings} = Scope,
147-
148-
{CursorColumn, AccTerminators, AccTokens} =
149-
add_cursor(Line, Column, Cursor, Terminators, Tokens),
150-
147+
{CursorColumn, AccTokens} = add_cursor(Line, Column, Cursor, Tokens),
151148
AllWarnings = maybe_unicode_lint_warnings(Ascii, Tokens, Warnings),
152-
{ok, Line, CursorColumn, AllWarnings, AccTokens, AccTerminators};
149+
{ok, Line, CursorColumn, AllWarnings, AccTokens, Terminators};
153150

154151
tokenize([], EndLine, EndColumn, #elixir_tokenizer{terminators=[{Start, {StartLine, StartColumn, _}, _} | _]} = Scope, Tokens) ->
155152
End = terminator(Start),
@@ -515,7 +512,7 @@ tokenize([$:, H | T] = Original, Line, Column, BaseScope, Tokens) when ?is_quote
515512
end,
516513

517514
case elixir_interpolation:extract(Line, Column + 2, Scope, true, T, H) of
518-
{NewLine, NewColumn, Parts, Rest, InterScope} ->
515+
{NewLine, NewColumn, Parts, Rest, _Done, InterScope} ->
519516
NewScope = case is_unnecessary_quote(Parts, InterScope) of
520517
true ->
521518
WarnMsg = io_lib:format(
@@ -788,7 +785,7 @@ handle_char(_) -> false.
788785

789786
handle_heredocs(T, Line, Column, H, Scope, Tokens) ->
790787
case extract_heredoc_with_interpolation(Line, Column, Scope, true, T, H) of
791-
{ok, NewLine, NewColumn, Parts, Rest, NewScope} ->
788+
{ok, NewLine, NewColumn, Parts, Rest, _Done, NewScope} ->
792789
case unescape_tokens(Parts, Line, Column, NewScope) of
793790
{ok, Unescaped} ->
794791
Token = {heredoc_type(H), {Line, Column, nil}, NewColumn - 4, Unescaped},
@@ -807,7 +804,7 @@ handle_strings(T, Line, Column, H, Scope, Tokens) ->
807804
{error, Reason} ->
808805
interpolation_error(Reason, [H | T], Scope, Tokens, " (for string starting at line ~B)", [Line], Line, Column-1, [H], [H]);
809806

810-
{NewLine, NewColumn, Parts, [$: | Rest], InterScope} when ?is_space(hd(Rest)) ->
807+
{NewLine, NewColumn, Parts, [$: | Rest], _Done, InterScope} when ?is_space(hd(Rest)) ->
811808
NewScope = case is_unnecessary_quote(Parts, InterScope) of
812809
true ->
813810
WarnMsg = io_lib:format(
@@ -850,7 +847,7 @@ handle_strings(T, Line, Column, H, Scope, Tokens) ->
850847
error(Reason, Rest, NewScope, Tokens)
851848
end;
852849

853-
{NewLine, NewColumn, Parts, Rest, InterScope} ->
850+
{NewLine, NewColumn, Parts, Rest, _Done, InterScope} ->
854851
NewScope =
855852
case H of
856853
$' ->
@@ -961,7 +958,7 @@ handle_dot([$., H | T] = Original, Line, Column, DotInfo, BaseScope, Tokens) whe
961958
end,
962959

963960
case elixir_interpolation:extract(Line, Column + 1, Scope, true, T, H) of
964-
{NewLine, NewColumn, [Part], Rest, InterScope} when is_list(Part) ->
961+
{NewLine, NewColumn, [Part], Rest, _Done, InterScope} when is_list(Part) ->
965962
NewScope = case is_unnecessary_quote([Part], InterScope) of
966963
true ->
967964
WarnMsg = io_lib:format(
@@ -992,7 +989,7 @@ handle_dot([$., H | T] = Original, Line, Column, DotInfo, BaseScope, Tokens) whe
992989
error(Reason, Original, NewScope, Tokens)
993990
end;
994991

995-
{_NewLine, _NewColumn, _Parts, Rest, NewScope} ->
992+
{_NewLine, _NewColumn, _Parts, Rest, _Done, NewScope} ->
996993
Message = "interpolation is not allowed when calling function/macro. Found interpolation in a call starting with: ",
997994
error({?LOC(Line, Column), Message, [H]}, Rest, NewScope, Tokens);
998995
{error, Reason} ->
@@ -1149,11 +1146,11 @@ unsafe_to_atom(List, Line, Column, #elixir_tokenizer{}) when is_list(List) ->
11491146
end
11501147
end.
11511148

1152-
collect_modifiers([H | T], Buffer) when ?is_downcase(H) or ?is_upcase(H) or ?is_digit(H) ->
1153-
collect_modifiers(T, [H | Buffer]);
1149+
collect_modifiers([H | T], Column, Buffer) when ?is_downcase(H) or ?is_upcase(H) or ?is_digit(H) ->
1150+
collect_modifiers(T, Column + 1, [H | Buffer]);
11541151

1155-
collect_modifiers(Rest, Buffer) ->
1156-
{Rest, lists:reverse(Buffer)}.
1152+
collect_modifiers(Rest, Column, Buffer) ->
1153+
{Rest, Column, lists:reverse(Buffer)}.
11571154

11581155
%% Heredocs
11591156

@@ -1164,14 +1161,14 @@ extract_heredoc_with_interpolation(Line, Column, Scope, Interpol, T, H) ->
11641161
%% spaces later. This new line is removed by calling "tl"
11651162
%% in the final heredoc body three lines below.
11661163
case elixir_interpolation:extract(Line, Column, Scope, Interpol, [$\n|Headerless], [H,H,H]) of
1167-
{NewLine, NewColumn, Parts0, Rest, InterScope} ->
1164+
{NewLine, NewColumn, Parts0, Rest, Done, InterScope} ->
11681165
Indent = NewColumn - 4,
11691166
Fun = fun(Part, Acc) -> extract_heredoc_indent(Part, Acc, Indent) end,
11701167
{Parts1, {ShouldWarn, _}} = lists:mapfoldl(Fun, {false, Line}, Parts0),
11711168
Parts2 = extract_heredoc_head(Parts1),
11721169
NewScope = maybe_heredoc_warn(ShouldWarn, Column, InterScope, H),
11731170
try
1174-
{ok, NewLine, NewColumn, tokens_to_binary(Parts2), Rest, NewScope}
1171+
{ok, NewLine, NewColumn, tokens_to_binary(Parts2), Rest, Done, NewScope}
11751172
catch
11761173
error:#{'__struct__' := 'Elixir.UnicodeConversionError', message := Message} ->
11771174
{error, interpolation_format(Message, " (for heredoc starting at line ~B)", [Line], Line, Column, [H, H, H], [H, H, H])}
@@ -1746,9 +1743,9 @@ sigil_name_error() ->
17461743
tokenize_sigil_contents([H, H, H | T] = Original, [S | _] = SigilName, Line, Column, Scope, Tokens)
17471744
when ?is_quote(H) ->
17481745
case extract_heredoc_with_interpolation(Line, Column, Scope, ?is_downcase(S), T, H) of
1749-
{ok, NewLine, NewColumn, Parts, Rest, NewScope} ->
1746+
{ok, NewLine, NewColumn, Parts, Rest, Done, NewScope} ->
17501747
Indentation = NewColumn - 4,
1751-
add_sigil_token(SigilName, Line, Column, NewLine, NewColumn, Parts, Rest, NewScope, Tokens, Indentation, <<H, H, H>>);
1748+
add_sigil_token(SigilName, Line, Column, NewLine, NewColumn, Parts, Rest, Done, NewScope, Tokens, Indentation, <<H, H, H>>);
17521749

17531750
{error, Reason} ->
17541751
error(Reason, [$~] ++ SigilName ++ Original, Scope, Tokens)
@@ -1757,9 +1754,10 @@ tokenize_sigil_contents([H, H, H | T] = Original, [S | _] = SigilName, Line, Col
17571754
tokenize_sigil_contents([H | T] = Original, [S | _] = SigilName, Line, Column, Scope, Tokens)
17581755
when ?is_sigil(H) ->
17591756
case elixir_interpolation:extract(Line, Column + 1, Scope, ?is_downcase(S), T, sigil_terminator(H)) of
1760-
{NewLine, NewColumn, Parts, Rest, NewScope} ->
1757+
{NewLine, NewColumn, Parts, Rest, Done, NewScope} ->
17611758
Indentation = nil,
1762-
add_sigil_token(SigilName, Line, Column, NewLine, NewColumn, tokens_to_binary(Parts), Rest, NewScope, Tokens, Indentation, <<H>>);
1759+
add_sigil_token(SigilName, Line, Column, NewLine, NewColumn,
1760+
tokens_to_binary(Parts), Rest, Done, NewScope, Tokens, Indentation, <<H>>);
17631761

17641762
{error, Reason} ->
17651763
Sigil = [$~, S, H],
@@ -1779,7 +1777,7 @@ tokenize_sigil_contents([H | _] = Original, SigilName, Line, Column, Scope, Toke
17791777
tokenize_sigil_contents([], _SigilName, Line, Column, Scope, Tokens) ->
17801778
tokenize([], Line, Column, Scope, Tokens).
17811779

1782-
add_sigil_token(SigilName, Line, Column, NewLine, NewColumn, Parts, Rest, Scope, Tokens, Indentation, Delimiter) ->
1780+
add_sigil_token(SigilName, Line, Column, NewLine, NewColumn, Parts, Rest, Done, Scope, Tokens, Indentation, Delimiter) ->
17831781
TokenColumn = Column - 1 - length(SigilName),
17841782
MaybeEncoded = case SigilName of
17851783
% Single-letter sigils present no risk of atom exhaustion (limited possibilities)
@@ -1788,9 +1786,12 @@ add_sigil_token(SigilName, Line, Column, NewLine, NewColumn, Parts, Rest, Scope,
17881786
end,
17891787
case MaybeEncoded of
17901788
{ok, Atom} ->
1791-
{Final, Modifiers} = collect_modifiers(Rest, []),
1792-
Token = {sigil, {Line, TokenColumn, nil}, Atom, Parts, Modifiers, Indentation, Delimiter},
1793-
NewColumnWithModifiers = NewColumn + length(Modifiers),
1789+
{Final, NewColumnWithModifiers, Modifiers} = case Done of
1790+
true -> collect_modifiers(Rest, NewColumn, []);
1791+
false -> {Rest, NewColumn, false}
1792+
end,
1793+
Token = {sigil, {Line, TokenColumn, {NewLine, NewColumnWithModifiers}},
1794+
Atom, Parts, Modifiers, Indentation, Delimiter},
17941795
tokenize(Final, NewLine, NewColumnWithModifiers, Scope, [Token | Tokens]);
17951796

17961797
{error, Reason} ->
@@ -1903,9 +1904,19 @@ format_error({Location, Error, Token}) ->
19031904

19041905
%% Cursor handling
19051906

1906-
add_cursor(_Line, Column, noprune, Terminators, Tokens) ->
1907-
{Column, Terminators, Tokens};
1908-
add_cursor(Line, Column, prune_and_cursor, Terminators, Tokens) ->
1907+
add_cursor(_Line, Column, noprune, Tokens) ->
1908+
{Column, Tokens};
1909+
1910+
add_cursor(Line, Column, {prune_and_cursor, true},
1911+
[{sigil, {_, _, {Line, Column}} = Location, Name, Parts, Modifier, Identation, Delimiter} | Rest]) ->
1912+
Cursor = {'__cursor__', [{line, Line}, {column, Column}], []},
1913+
CursorModifier = case Modifier of
1914+
false -> Cursor;
1915+
List -> List ++ [Cursor]
1916+
end,
1917+
{Column + 12, [{sigil, Location, Name, Parts, CursorModifier, Identation, Delimiter} | Rest]};
1918+
1919+
add_cursor(Line, Column, {prune_and_cursor, _}, Tokens) ->
19091920
PrePrunedTokens = prune_identifier(Tokens),
19101921
PrunedTokens = prune_tokens(PrePrunedTokens, []),
19111922
CursorTokens = [
@@ -1914,7 +1925,7 @@ add_cursor(Line, Column, prune_and_cursor, Terminators, Tokens) ->
19141925
{paren_identifier, {Line, Column, nil}, '__cursor__'}
19151926
| PrunedTokens
19161927
],
1917-
{Column + 12, Terminators, CursorTokens}.
1928+
{Column + 12, CursorTokens}.
19181929

19191930
prune_identifier([{identifier, _, _} | Tokens]) -> Tokens;
19201931
prune_identifier(Tokens) -> Tokens.

lib/elixir/test/elixir/code_fragment_test.exs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,6 +1485,40 @@ defmodule CodeFragmentTest do
14851485
assert cc2q!("foo(123, ~r/") == s2q!("foo(123, __cursor__())")
14861486
end
14871487

1488+
test "sigils" do
1489+
assert cc2q!("foo(123, ~") == s2q!("foo(123, __cursor__())")
1490+
assert cc2q!("foo(123, ~r") == s2q!("foo(123, __cursor__())")
1491+
assert cc2q!("foo(123, ~r/") == s2q!("foo(123, __cursor__())")
1492+
assert cc2q!("foo(123, ~r/foo") == s2q!("foo(123, __cursor__())")
1493+
assert cc2q!("foo(123, ~r//") == s2q!("foo(123, __cursor__())")
1494+
assert cc2q!("foo(123, ~r//i") == s2q!("foo(123, __cursor__())")
1495+
assert cc2q!("foo(123, ~r//i ") == s2q!("foo(123, __cursor__())")
1496+
1497+
drop_metadata = fn ast ->
1498+
Macro.prewalk(ast, fn node ->
1499+
Macro.update_meta(node, &Keyword.drop(&1, [:delimiter, :column]))
1500+
end)
1501+
end
1502+
1503+
assert cc2q!("foo(123, ~", preserve_sigils: true) == s2q!("foo(123, __cursor__())")
1504+
assert cc2q!("foo(123, ~r", preserve_sigils: true) == s2q!("foo(123, __cursor__())")
1505+
1506+
assert cc2q!("foo(123, ~r/", preserve_sigils: true) |> drop_metadata.() ==
1507+
s2q!("foo(123, sigil_r(<<\"\">>, __cursor__()))")
1508+
1509+
assert cc2q!("foo(123, ~r/foo", preserve_sigils: true) |> drop_metadata.() ==
1510+
s2q!("foo(123, sigil_r(<<\"foo\">>, __cursor__()))")
1511+
1512+
assert cc2q!("foo(123, ~r//", preserve_sigils: true) |> drop_metadata.() ==
1513+
s2q!("foo(123, sigil_r(<<\"\">>, [__cursor__()]))")
1514+
1515+
assert cc2q!("foo(123, ~r//i", preserve_sigils: true) |> drop_metadata.() ==
1516+
s2q!("foo(123, sigil_r(<<\"\">>, [?i, __cursor__()]))")
1517+
1518+
assert cc2q!("foo(123, ~r//i ", preserve_sigils: true) |> drop_metadata.() ==
1519+
s2q!("foo(123, __cursor__())")
1520+
end
1521+
14881522
test "no warnings" do
14891523
assert cc2q!(~s"?\\ ") == s2q!("__cursor__()")
14901524
assert cc2q!(~s"{fn -> end, ") == s2q!("{fn -> nil end, __cursor__()}")

lib/elixir/test/erlang/string_test.erl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ extract_interpolations(String) ->
1515
case elixir_interpolation:extract(1, 1, #elixir_tokenizer{}, true, String ++ [$"], $") of
1616
{error, Error} ->
1717
Error;
18-
{_, _, Parts, _, _} ->
18+
{_, _, Parts, _, _, _} ->
1919
Parts
2020
end.
2121

@@ -26,7 +26,7 @@ extract_interpolations_without_interpolation_test() ->
2626

2727
extract_interpolations_with_escaped_interpolation_test() ->
2828
["f\\#{o}o"] = extract_interpolations("f\\#{o}o"),
29-
{1, 10, ["f\\#{o}o"], [], _} =
29+
{1, 10, ["f\\#{o}o"], [], _, _} =
3030
elixir_interpolation:extract(1, 2, #elixir_tokenizer{}, true, "f\\#{o}o\"", $").
3131

3232
extract_interpolations_with_interpolation_test() ->

lib/elixir/test/erlang/tokenizer_test.erl

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -263,22 +263,22 @@ vc_merge_conflict_test() ->
263263
tokenize_error("<<<<<<< HEAD\n[1, 2, 3]").
264264

265265
sigil_terminator_test() ->
266-
[{sigil, {1, 1, nil}, sigil_r, [<<"foo">>], "", nil, <<"/">>}] = tokenize("~r/foo/"),
267-
[{sigil, {1, 1, nil}, sigil_r, [<<"foo">>], "", nil, <<"[">>}] = tokenize("~r[foo]"),
268-
[{sigil, {1, 1, nil}, sigil_r, [<<"foo">>], "", nil, <<"\"">>}] = tokenize("~r\"foo\""),
269-
[{sigil, {1, 1, nil}, sigil_r, [<<"foo">>], "", nil, <<"/">>},
266+
[{sigil, {1, 1, {1, 8}}, sigil_r, [<<"foo">>], [], nil, <<"/">>}] = tokenize("~r/foo/"),
267+
[{sigil, {1, 1, {1, 8}}, sigil_r, [<<"foo">>], [], nil, <<"[">>}] = tokenize("~r[foo]"),
268+
[{sigil, {1, 1, {1, 8}}, sigil_r, [<<"foo">>], [], nil, <<"\"">>}] = tokenize("~r\"foo\""),
269+
[{sigil, {1, 1, {1, 8}}, sigil_r, [<<"foo">>], [], nil, <<"/">>},
270270
{comp_op, {1, 9, nil}, '=='},
271271
{identifier, {1, 12, _}, bar}] = tokenize("~r/foo/ == bar"),
272-
[{sigil, {1, 1, nil}, sigil_r, [<<"foo">>], "iu", nil, <<"/">>},
272+
[{sigil, {1, 1, {1, 10}}, sigil_r, [<<"foo">>], "iu", nil, <<"/">>},
273273
{comp_op, {1, 11, nil}, '=='},
274274
{identifier, {1, 14, _}, bar}] = tokenize("~r/foo/iu == bar"),
275-
[{sigil, {1, 1, nil}, sigil_M, [<<"1 2 3">>], "u8", nil, <<"[">>}] = tokenize("~M[1 2 3]u8").
275+
[{sigil, {1, 1, {1, 12}}, sigil_M, [<<"1 2 3">>], "u8", nil, <<"[">>}] = tokenize("~M[1 2 3]u8").
276276

277277
sigil_heredoc_test() ->
278-
[{sigil, {1, 1, nil}, sigil_S, [<<"sigil heredoc\n">>], "", 0, <<"\"\"\"">>}] = tokenize("~S\"\"\"\nsigil heredoc\n\"\"\""),
279-
[{sigil, {1, 1, nil}, sigil_S, [<<"sigil heredoc\n">>], "", 0, <<"'''">>}] = tokenize("~S'''\nsigil heredoc\n'''"),
280-
[{sigil, {1, 1, nil}, sigil_S, [<<"sigil heredoc\n">>], "", 2, <<"\"\"\"">>}] = tokenize("~S\"\"\"\n sigil heredoc\n \"\"\""),
281-
[{sigil, {1, 1, nil}, sigil_s, [<<"sigil heredoc\n">>], "", 2, <<"\"\"\"">>}] = tokenize("~s\"\"\"\n sigil heredoc\n \"\"\"").
278+
[{sigil, {1, 1, {3, 4}}, sigil_S, [<<"sigil heredoc\n">>], [], 0, <<"\"\"\"">>}] = tokenize("~S\"\"\"\nsigil heredoc\n\"\"\""),
279+
[{sigil, {1, 1, {3, 4}}, sigil_S, [<<"sigil heredoc\n">>], [], 0, <<"'''">>}] = tokenize("~S'''\nsigil heredoc\n'''"),
280+
[{sigil, {1, 1, {3, 6}}, sigil_S, [<<"sigil heredoc\n">>], [], 2, <<"\"\"\"">>}] = tokenize("~S\"\"\"\n sigil heredoc\n \"\"\""),
281+
[{sigil, {1, 1, {3, 6}}, sigil_s, [<<"sigil heredoc\n">>], [], 2, <<"\"\"\"">>}] = tokenize("~s\"\"\"\n sigil heredoc\n \"\"\"").
282282

283283
invalid_sigil_delimiter_test() ->
284284
{[{line, 1}, {column, 1}], "invalid sigil delimiter: ", Message} = tokenize_error("~s\\"),

0 commit comments

Comments
 (0)