Skip to content
Closed
13 changes: 11 additions & 2 deletions src/erlfmt_format.erl
Original file line number Diff line number Diff line change
Expand Up @@ -259,9 +259,18 @@ do_expr_to_algebra({clauses, _Meta, Clauses}) ->
clauses_to_algebra(Clauses);
do_expr_to_algebra({body, _Meta, Exprs}) ->
block_to_algebra(Exprs);
do_expr_to_algebra({sigil, _Meta, Prefix, Content, Suffix}) ->
do_expr_to_algebra({sigil, _Meta, Prefix, {string, ContentMeta, _Value}, Suffix}) ->
PrefixDoc = concat(<<"~">>, do_expr_to_algebra(Prefix)),
concat(concat(PrefixDoc, do_expr_to_algebra(Content)), do_expr_to_algebra(Suffix));
Text = erlfmt_scan:get_anno(text, ContentMeta),
SigilDoc = concat(concat(PrefixDoc, string(Text)), do_expr_to_algebra(Suffix)),
case string:split(Text, "\n", all) of
% Triple-quoted sigils don't need force_breaks
["\"\"\"" ++ _ | _] -> SigilDoc;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will not work correctly for multiple nesting levels. We need to do the same logic as is done in normal triple-quoted strings in string_to_algebra, just add prefix and suffix around it.
Furthermore, ideally we'd avoid duplicating this logic and just share the code.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you clarify what specific aspect of nesting isn't working correctly and provide a specific example of a case that's currently broken?

I want to make sure I understand the issue properly before implementing a fix.

A concrete failing test case would help me understand the exact issue you're referring to.

% Single-line sigils don't need force_breaks
[_] -> SigilDoc;
% Multi-line non-triple-quoted sigils need force_breaks
_ -> concat([force_breaks(), SigilDoc])
end;
do_expr_to_algebra({sigil_prefix, _Meta, ''}) ->
<<"">>;
do_expr_to_algebra({sigil_prefix, _Meta, SigilName}) ->
Expand Down
166 changes: 165 additions & 1 deletion test/erlfmt_format_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,171 @@ sigils(Config) when is_list(Config) ->
),
?assertSame("~s\"\"\"\n\\tabc\n\\tdef\n\"\"\"\n"),
?assertSame("\"\"\"\nTest\nMultiline\n\"\"\"\n"),
?assertSame("~\"\"\"\nTest\nMultiline\n\"\"\"\n").
?assertSame("~\"\"\"\nTest\nMultiline\n\"\"\"\n"),
%% Test multiline sigils in lists cause list to break
?assertFormat(
"[~\"\n"
" foo\n"
" \"].\n",
"[\n"
" ~\"\n"
" foo\n"
" \"\n"
"].\n"
),
%% Triple-quoted sigils don't cause container breaks (they handle indentation properly)
?assertSame(
"[~\"\"\"\n"
" foo\n"
" bar\n"
" \"\"\"].\n"
),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't correct because of the issue how triple-quoted strings I handled that I mentioned before.

It should be formatted as:

[
    ~"""
    foo
    bar
    """
].

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

%% Triple-quoted sigils with prefixes also don't break containers
?assertSame(
"[~s\"\"\"\n"
" content\n"
" with prefix\n"
" \"\"\"].\n"
),
%% Triple-quoted sigils in tuples don't break containers
?assertSame(
"{~\"\"\"\n"
" tuple content\n"
" \"\"\", other}.\n"
),
%% Triple-quoted sigils in function calls don't break containers
?assertSame(
"process(~\"\"\"\n"
" function arg\n"
" \"\"\").\n"
),
%% Edge case: minimal triple-quoted sigil content
?assertSame(
"[~\"\"\"\n"
"\"\"\"].\n"
),
%% Mixed triple-quoted and regular multiline sigils show different behavior
?assertFormat(
"[~\"\"\"\n"
" triple quoted\n"
" \"\"\", ~\"\n"
" regular multiline\n"
" \"].\n",
"[\n"
" ~\"\"\"\n"
" triple quoted\n"
" \"\"\",\n"
" ~\"\n"
" regular multiline\n"
" \"\n"
"].\n"
),
%% Test sigils with prefixes and modifiers in lists
?assertFormat(
"[~s\"\n"
" foo\n"
" \"].\n",
"[\n"
" ~s\"\n"
" foo\n"
" \"\n"
"].\n"
),
?assertFormat(
"[~b\"\n"
" foo\n"
" \"x].\n",
"[\n"
" ~b\"\n"
" foo\n"
" \"x\n"
"].\n"
),
%% Test mixed regular and sigil strings in lists
?assertFormat(
"[\"regular\n"
" multiline\", ~\"sigil\n"
" multiline\"].\n",
"[\n"
" \"regular\\n\"\n"
" \" multiline\",\n"
" ~\"sigil\n"
" multiline\"\n"
"].\n"
),
%% Test sigils in tuples
?assertFormat(
"{~\"\n"
" foo\n"
" \", bar}.\n",
"{\n"
" ~\"\n"
" foo\n"
" \",\n"
" bar\n"
"}.\n"
),
%% Test sigils in function calls
?assertFormat(
"foo(~\"\n"
" multiline\n"
" \").\n",
"foo(\n"
" ~\"\n"
" multiline\n"
" \"\n"
").\n"
),
%% Test sigils in function arguments with other expressions
?assertFormat(
"foo(\"regular\n"
" string\", ~\"sigil\n"
" string\", atom).\n",
"foo(\n"
" \"regular\\n\"\n"
" \" string\",\n"
" ~\"sigil\n"
" string\",\n"
" atom\n"
").\n"
),
%% Test sigils in maps
?assertFormat(
"#{key => ~\"\n"
" value\n"
" \"}.\n",
"#{\n"
" key =>\n"
" ~\"\n"
" value\n"
" \"\n"
"}.\n"
),
%% Test sigils in records
?assertFormat(
"#rec{field = ~\"\n"
" value\n"
" \"}.\n",
"#rec{\n"
" field =\n"
" ~\"\n"
" value\n"
" \"\n"
"}.\n"
),
%% Test sigils cause containers to break but preserve sigil content
?assertFormat(
"test() ->\n"
" [~\"\n"
" \", b, c].\n",
"test() ->\n"
" [\n"
" ~\"\n"
" \",\n"
" b,\n"
" c\n"
" ].\n"
).

dotted(Config) when is_list(Config) ->
?assertSame("<0.1.2>\n"),
Expand Down
Loading