From 347490760a7f533cd1fd88b046f0ac582eb857f6 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Thu, 1 Aug 2024 02:51:45 +0200 Subject: [PATCH] Fix formatting of multiline triple strings with \-escaped newlines Closes #29. --- src/runestone.jl | 44 ++++++++++++++++++++++++++++++++++++++------ test/runtests.jl | 5 +++++ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/runestone.jl b/src/runestone.jl index d7031a2..23b544e 100644 --- a/src/runestone.jl +++ b/src/runestone.jl @@ -2792,18 +2792,50 @@ function indent_multiline_strings(ctx::Context, node::Node) kid = kids[idx] if state === :expect_something if kind(kid) === itemkind - if indented && read_bytes(ctx, kid)[end] == UInt8('\n') + if indented && span(kid) > 0 && read_bytes(ctx, kid)[end] == UInt8('\n') state = :expect_indent_ws end accept_node!(ctx, kid) any_changes && push!(kids′, kid) elseif kind(kid) === K"Whitespace" - # Delete this one - replace_bytes!(ctx, "", span(kid)) - if kids′ === kids - kids′ = kids[1:(idx - 1)] + bytes = read_bytes(ctx, kid) + # Multiline strings with trailing \ will have non-space characters in the + # Whitespace node. These should be preserved. + # TODO: Maybe this should be continue-indent to highlight the continuation? + if length(bytes) == 2 + indent_span && bytes[1] === UInt8('\\') && bytes[2] === UInt8('\n') + @assert all(x -> x in (UInt8(' '), UInt8('\t')), @view(bytes[3:end])) + # This node is correct + accept_node!(ctx, kid) + any_changes && push!(kids′, kid) + elseif length(bytes) >= 2 && bytes[1] === UInt8('\\') && bytes[2] === UInt8('\n') + @assert all(x -> x in (UInt8(' '), UInt8('\t')), @view(bytes[3:end])) + if length(bytes) < 2 + indent_span + # Insert the missing spaces + while length(bytes) < 2 + indent_span + push!(bytes, UInt8(' ')) + end + else + @assert length(bytes) > 2 + indent_span + # Truncate spaces + resize!(bytes, 2 + indent_span) + end + replace_bytes!(ctx, bytes, span(kid)) + if kids′ === kids + kids′ = kids[1:(idx - 1)] + end + kid′ = Node(JuliaSyntax.SyntaxHead(K"Whitespace", JuliaSyntax.TRIVIA_FLAG), length(bytes)) + accept_node!(ctx, kid′) + push!(kids′, kid′) + any_changes = true + else + # Delete this node completely + @assert all(x -> x in (UInt8(' '), UInt8('\t')), bytes) + replace_bytes!(ctx, "", span(kid)) + if kids′ === kids + kids′ = kids[1:(idx - 1)] + end + any_changes = true end - any_changes = true else accept_node!(ctx, kid) any_changes && push!(kids′, kid) diff --git a/test/runtests.jl b/test/runtests.jl index 9ec27a2..97e5fb9 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -926,5 +926,10 @@ end "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") === "begin\n $(otriple)\n a\$(b)c\n $(ctriple)\nend" + # Line continuation with `\` + @test format_string("$(otriple)\n$(sp)a\\\n$(sp)b\n$(sp)$(ctriple)") === + "$(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" end end