From af1b7377f46db0d5e096a5b2d60309aa64629ad6 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 16 Aug 2024 11:32:12 +0200 Subject: [PATCH] Line continuation indent for triple-strings (#45) 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: 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 + """ ``` Note that single triple strings as a right hand side is excluded from the indent rule, similar to having `if/try/let/...` blocks as a right hand side. --- src/chisels.jl | 5 ++++ src/runestone.jl | 65 ++++++++++++++++++++++++++++++++++++++------- test/runtests.jl | 69 +++++++++++++++++++++++++++++++++--------------- 3 files changed, 108 insertions(+), 31 deletions(-) diff --git a/src/chisels.jl b/src/chisels.jl index 6a89915..09de51f 100644 --- a/src/chisels.jl +++ b/src/chisels.jl @@ -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 # ########################## diff --git a/src/runestone.jl b/src/runestone.jl index 43b4933..2dd5931 100644 --- a/src/runestone.jl +++ b/src/runestone.jl @@ -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 @@ -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( @@ -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) @@ -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. @@ -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" || is_triple_thing(rhs) blocklike && return nothing # TODO: Perhaps delete superfluous newlines? # Continue all newlines between the `=` and the rhs kids′ = kids @@ -2554,9 +2585,20 @@ function indent_assignment(ctx::Context, node::Node) push!(kids′, kid′) end end + # Mark the rhs for line continuation + if !has_tag(rhs, TAG_LINE_CONT) + 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′) @@ -2786,6 +2828,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) diff --git a/test/runtests.jl b/test/runtests.jl index 772fa6f..d73f559 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -735,12 +735,16 @@ end # Blocklike RHS for thing in ( "if c\n x\nend", "try\n x\ncatch\n y\nend", + "let c = 1\n c\nend", "function()\n x\nend", "\"\"\"\nfoo\n\"\"\"", "r\"\"\"\nfoo\n\"\"\"", - "```\nfoo\n```", "r```\nfoo\n```", + "```\nfoo\n```", "r```\nfoo\n```", "```\nfoo\n```x", ) @test format_string("a =$(nl)$(thing)") == "a =$(nl)$(thing)" @test format_string("a =$(nl)# comment$(nl)$(thing)") == "a =$(nl)# comment$(nl)$(thing)" + @test format_string("a = $(thing)") == "a = $(thing)" + @test format_string("a = #=comment=#$(sp)$(thing)") == + "a = #=comment=# $(thing)" end end # using/import @@ -937,6 +941,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)\na\nb\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 @@ -961,13 +988,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() @@ -978,13 +1005,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() @@ -994,12 +1021,12 @@ end end """, ) == """ - function f() - $off - 1 + 1 - 1 + 1 - end - """ + function f() + $off + 1 + 1 + 1 + 1 + end + """ end end