Skip to content

Commit

Permalink
Line continuation indent for triple-strings
Browse files Browse the repository at this point in the history
This patch introduces line continuation based indent for triple strings,
which typically span multiple lines without any explicit newlines in the
syntax tree (since they are hidden inside the string).

This result in the following changes, some of which are clearly
bugfixes:

Right hand side of an assignment:
```diff
 x = """
-foo
-"""
+    foo
+    """
```

Operator chains:
```diff
 """
 abc
 """ * """
-def
-"""
+    def
+    """
```

Operator chain as assignment right hand side:
```diff
 x = """
-abc
-""" * """
-def
-"""
+    abc
+    """ * """
+    def
+    """
```

Implicit tuples:
```diff
 """
 abc
 """,
   """
-def
-"""
+    def
+    """
```
  • Loading branch information
fredrikekre committed Aug 16, 2024
1 parent c30af80 commit e83bc2f
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 45 deletions.
5 changes: 5 additions & 0 deletions src/chisels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,11 @@ function is_triple_string_macro(node)
return false
end

function is_triple_thing(node)
return is_triple_string(node) || is_triple_string_macro(node) ||
(kind(node) === K"juxtapose" && is_triple_string_macro(verified_kids(node)[1]))
end

##########################
# Utilities for IOBuffer #
##########################
Expand Down
6 changes: 3 additions & 3 deletions src/main.jl
Original file line number Diff line number Diff line change
Expand Up @@ -292,9 +292,9 @@ function main(argv)
write(A, ctx.src_str)
write(B, seekstart(ctx.fmt_io))
cmd = ```
$(git) --no-pager diff --color=always --no-index --no-prefix
$(relpath(A, dir)) $(relpath(B, dir))
```
$(git) --no-pager diff --color=always --no-index --no-prefix
$(relpath(A, dir)) $(relpath(B, dir))
```
# `ignorestatus` because --no-index implies --exit-code
cmd = setenv(ignorestatus(cmd); dir = dir)
cmd = pipeline(cmd, stdout = stderr, stderr = stderr)
Expand Down
94 changes: 76 additions & 18 deletions src/runestone.jl
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,14 @@ function format_float_literals(ctx::Context, node::Node)
str = String(read_bytes(ctx, node))
# Check and shortcut the happy path first
r = r"""
^
(?:[+-])? # Optional sign
(?:(?:[1-9]\d*)|0) # Non-zero followed by any digit, or just a single zero
\. # Decimal point
(?:(?:\d*[1-9])|0) # Any digit with a final nonzero, or just a single zero
(?:[ef][+-]?(?:[1-9]\d*|0))?
$
"""x
^
(?:[+-])? # Optional sign
(?:(?:[1-9]\d*)|0) # Non-zero followed by any digit, or just a single zero
\. # Decimal point
(?:(?:\d*[1-9])|0) # Any digit with a final nonzero, or just a single zero
(?:[ef][+-]?(?:[1-9]\d*|0))?
$
"""x
if occursin(r, str)
return nothing
end
Expand Down Expand Up @@ -2053,10 +2053,18 @@ function indent_newlines_between_indices(
if !indent_closing_token && i == close_idx - 1 && kind(kid) === K"NewlineWs"
continue
end
# Tag all direct NewlineWs kids
if kind(kid) === K"NewlineWs" && !has_tag(kid, TAG_LINE_CONT)
# Tag all direct NewlineWs kids
kid = add_tag(kid, TAG_LINE_CONT)
this_kid_changed = true
elseif is_triple_thing(kid) && (i != open_idx || has_tag(node, TAG_LINE_CONT))
# TODO: Might be too course to use the tag on the node here...
# Tag triple strings and triple string macros
kid′ = indent_triple_thing(ctx, kid)
if kid′ !== nothing
kid = kid′
this_kid_changed = true
end
end
# NewlineWs nodes can also hide as the first or last leaf of a node, tag'em.
# Skip leading newline if this kid is the first one
Expand Down Expand Up @@ -2449,6 +2457,29 @@ function indent_short_circuit(ctx::Context, node::Node)
return indent_op_call(ctx, node)
end

function indent_triple_thing(ctx::Context, node::Node)
@assert is_triple_thing(node)
if is_triple_string(node)
return has_tag(node, TAG_LINE_CONT) ? nothing : add_tag(node, TAG_LINE_CONT)
elseif is_triple_string_macro(node)
kids = verified_kids(node)
@assert is_triple_string(kids[2])
kid′ = indent_triple_thing(ctx, kids[2])
kid′ === nothing && return nothing
kids′ = copy(kids)
kids′[2] = kid′
return make_node(node, kids′)
else
@assert kind(node) === K"juxtapose" && is_triple_string_macro(verified_kids(node)[1])
kids = verified_kids(node)
kid′ = indent_triple_thing(ctx, kids[1])
kid′ === nothing && return nothing
kids′ = copy(kids)
kids′[1] = kid′
return make_node(node, kids′)
end
end

# TODO: This function can be used for more things than just indent_using I think. Perhaps
# with a max_depth parameter.
function continue_all_newlines(
Expand All @@ -2465,6 +2496,13 @@ function continue_all_newlines(
else
return nothing
end
elseif is_triple_thing(node)
# Check skip_first inside to break the recursion and considier triple strings leafs
if !(skip_first && is_first)
return indent_triple_thing(ctx, node)
else
return nothing
end
else
any_kid_changed = false
kids = verified_kids(node)
Expand Down Expand Up @@ -2508,12 +2546,6 @@ function indent_assignment(ctx::Context, node::Node)
rhsidx = findnext(!JuliaSyntax.is_whitespace, kids, eqidx + 1)::Int
r = (eqidx + 1):(rhsidx - 1)
length(r) == 0 && return nothing
if length(r) == 1 && kind(kids[r[1]]) === K"Whitespace"
return nothing
end
if !any(i -> kind(kids[i]) === K"NewlineWs", r)
return nothing
end
rhs = kids[rhsidx]
# Some right hand sides have more "inertia" towards indentation. This is so that we
# will end up with e.g.
Expand All @@ -2533,8 +2565,7 @@ function indent_assignment(ctx::Context, node::Node)
# x = if cond
# end
# ```
blocklike = kind(rhs) in KSet"if try function let" ||
is_triple_string(rhs) || is_triple_string_macro(rhs)
blocklike = kind(rhs) in KSet"if try function let"
blocklike && return nothing # TODO: Perhaps delete superfluous newlines?
# Continue all newlines between the `=` and the rhs
kids′ = kids
Expand All @@ -2554,9 +2585,33 @@ function indent_assignment(ctx::Context, node::Node)
push!(kids′, kid′)
end
end
if is_triple_thing(rhs)
# If the RHS is a triple string it should be indented
rhs′ = indent_triple_thing(ctx, rhs)
if rhs′ !== nothing
rhs = rhs′
changed = true
end
if changed
if kids′ === kids
kids′ = kids[1:(rhsidx - 1)]
end
push!(kids′, rhs)
end
elseif !has_tag(rhs, TAG_LINE_CONT)
# Mark the rhs for line continuation
rhs = add_tag(rhs, TAG_LINE_CONT)
changed = true
if kids′ === kids
kids′ = kids[1:(rhsidx - 1)]
end
push!(kids′, rhs)
else
changed && push!(kids′, rhs)
end
if changed
@assert kids !== kids′
for i in rhsidx:length(kids)
for i in (rhsidx + 1):length(kids)
push!(kids′, kids[i])
end
return make_node(node, kids′)
Expand Down Expand Up @@ -2786,6 +2841,9 @@ function indent_multiline_strings(ctx::Context, node::Node)
triplekind = kind(node) === K"string" ? K"\"\"\"" : K"```"
itemkind = kind(node) === K"string" ? K"String" : K"CmdString"
indent_span = 4 * ctx.indent_level
if has_tag(node, TAG_LINE_CONT)
indent_span += 4
end
indented = indent_span > 0

pos = position(ctx.fmt_io)
Expand Down
84 changes: 60 additions & 24 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -735,13 +735,26 @@ end
# Blocklike RHS
for thing in (
"if c\n x\nend", "try\n x\ncatch\n y\nend",
"\"\"\"\nfoo\n\"\"\"", "r\"\"\"\nfoo\n\"\"\"",
"```\nfoo\n```", "r```\nfoo\n```",
"let c = 1\n c\nend", "function()\n x\nend",
)
@test format_string("a =$(nl)$(thing)") == "a =$(nl)$(thing)"
@test format_string("a =$(nl)# comment$(nl)$(thing)") ==
"a =$(nl)# comment$(nl)$(thing)"
end
# Triplestring RHS
for (o, c) in (
("\"\"\"", "\"\"\""), ("r\"\"\"", "\"\"\""),
("```", "```"), ("r```", "```"), ("```", "```x"),
)
thing = "$(o)\nfoo\n$(c)"
ithing = "$(o)\n foo\n $(c)"
@test format_string("a =$(nl)$(thing)") == "a =$(nl) $(ithing)"
@test format_string("a =$(nl)# comment$(nl)$(thing)") ==
"a =$(nl) # comment$(nl) $(ithing)"
@test format_string("a = $(thing)") == "a = $(ithing)"
@test format_string("a = #=comment=#$(thing)") ==
"a = #=comment=# $(ithing)"
end
end
# using/import
for verb in ("using", "import")
Expand Down Expand Up @@ -916,7 +929,7 @@ end
@test format_string("$(sp)$(otriple)\n$(sp)a\n\n$(sp)b\n$(sp)$(ctriple)") ===
"$(sp)$(otriple)\na\n\nb\n$(ctriple)"
@test format_string("x = $(otriple)\n$(sp)a\n$(sp)b\n$(sp)$(ctriple)") ===
"x = $(otriple)\na\nb\n$(ctriple)"
"x = $(otriple)\n a\n b\n $(ctriple)"
@test format_string("$(sp)$(otriple)a\n$(sp)a\n$(sp)b\n$(sp)$(ctriple)") ===
"$(sp)$(otriple)a\na\nb\n$(ctriple)"
@test format_string("$(sp)$(otriple)\n$(sp)a\$(b)c\n$(sp)$(ctriple)") ===
Expand All @@ -927,7 +940,7 @@ end
@test format_string("begin\n$(sp)$(otriple)\n$(sp)a\n$(sp)\n$(sp)b\n$(sp)$(ctriple)\nend") ===
"begin\n $(otriple)\n a\n\n b\n $(ctriple)\nend"
@test format_string("begin\n$(sp)x = $(otriple)\n$(sp)a\n$(sp)b\n$(sp)$(ctriple)\nend") ===
"begin\n x = $(otriple)\n a\n b\n $(ctriple)\nend"
"begin\n x = $(otriple)\n a\n b\n $(ctriple)\nend"
@test format_string("begin\n$(sp)$(otriple)a\n$(sp)a\n$(sp)b\n$(sp)$(ctriple)\nend") ===
"begin\n $(otriple)a\n a\n b\n $(ctriple)\nend"
@test format_string("begin\n$(sp)$(otriple)\n$(sp)a\$(b)c\n$(sp)$(ctriple)\nend") ===
Expand All @@ -937,6 +950,29 @@ end
"$(otriple)\na\\\nb\n$(ctriple)"
@test format_string("begin\n$(otriple)\n$(sp)a\\\n$(sp)b\n$(sp)$(ctriple)\nend") ===
"begin\n $(otriple)\n a\\\n b\n $(ctriple)\nend"
# Triple strings with continuation indent
@test format_string("x = $(otriple)\n$(sp)a\n$(sp)b\n$(sp)$(ctriple)") ===
"x = $(otriple)\n a\n b\n $(ctriple)"
@test format_string("$(otriple)\n$(sp)a\n$(sp)b\n$(sp)$(ctriple) * $(otriple)\n$(sp)a\n$(sp)b\n$(sp)$(ctriple)") ===
"$(otriple)\na\nb\n$(ctriple) * $(otriple)\n a\n b\n $(ctriple)"
@test format_string("$(otriple)\n$(sp)a\n$(sp)b\n$(sp)$(ctriple) *\n$(otriple)\n$(sp)a\n$(sp)b\n$(sp)$(ctriple)") ===
"$(otriple)\na\nb\n$(ctriple) *\n $(otriple)\n a\n b\n $(ctriple)"
# Implicit tuple
@test format_string("$(otriple)\nabc\n$(ctriple), $(otriple)\ndef\n$(ctriple)") ===
"$(otriple)\nabc\n$(ctriple), $(otriple)\n def\n $(ctriple)"
@test format_string("$(otriple)\nabc\n$(ctriple),\n$(otriple)\ndef\n$(ctriple)") ===
"$(otriple)\nabc\n$(ctriple),\n $(otriple)\n def\n $(ctriple)"
# Operator chains
@test format_string("$(otriple)\nabc\n$(ctriple) * $(otriple)\ndef\n$(ctriple)") ===
"$(otriple)\nabc\n$(ctriple) * $(otriple)\n def\n $(ctriple)"
@test format_string("$(otriple)\nabc\n$(ctriple) *\n$(otriple)\ndef\n$(ctriple)") ===
"$(otriple)\nabc\n$(ctriple) *\n $(otriple)\n def\n $(ctriple)"
@test format_string("x = $(otriple)\nabc\n$(ctriple) *\n$(otriple)\ndef\n$(ctriple)") ===
"x = $(otriple)\n abc\n $(ctriple) *\n $(otriple)\n def\n $(ctriple)"
@test format_string("x = $(otriple)\nabc\n$(ctriple) *\n\"def\"") ===
"x = $(otriple)\n abc\n $(ctriple) *\n \"def\""
@test format_string("x = \"abc\" *\n$(otriple)\ndef\n$(ctriple)") ===
"x = \"abc\" *\n $(otriple)\n def\n $(ctriple)"
end
end

Expand All @@ -961,13 +997,13 @@ end
end
""",
) == """
function f()
$off
1+1
$on
1 + 1
end
"""
function f()
$off
1+1
$on
1 + 1
end
"""
@test format_string(
"""
function f()
Expand All @@ -978,13 +1014,13 @@ end
end
""",
) == """
function f()
$off
1 + 1
$bon
1 + 1
end
"""
function f()
$off
1 + 1
$bon
1 + 1
end
"""
@test format_string(
"""
function f()
Expand All @@ -994,12 +1030,12 @@ end
end
""",
) == """
function f()
$off
1 + 1
1 + 1
end
"""
function f()
$off
1 + 1
1 + 1
end
"""
end
end

Expand Down

0 comments on commit e83bc2f

Please sign in to comment.