diff --git a/src/chisels.jl b/src/chisels.jl index 5cb2531..5076da3 100644 --- a/src/chisels.jl +++ b/src/chisels.jl @@ -471,6 +471,76 @@ function any_leaf(pred::F, node::Node) where {F} end end +# TODO: Alternative non-recursive definition that only looks at the current layer +# ``` +# contains_outer_newline(kids, opening_leaf_idx, closing_leaf_idx) +# ``` +function is_multiline_between_idxs(ctx, node::Node, opening_idx::Int, closing_idx::Int) + @assert !is_leaf(node) + kids = verified_kids(node) + # Check for newline nodes + if any(y -> any_leaf(x -> kind(x) === K"NewlineWs", kids[y]), (opening_idx + 1):(closing_idx - 1)) + return true + end + # Recurse into multiline triple-strings + pos = position(ctx.fmt_io) + for i in 1:opening_idx + accept_node!(ctx, kids[i]) + end + for i in (opening_idx + 1):(closing_idx - 1) + kid = kids[i] + ipos = position(ctx.fmt_io) + if contains_multiline_triple_string(ctx, kid) + seek(ctx.fmt_io, pos) + return true + end + seek(ctx.fmt_io, ipos) + accept_node!(ctx, kid) + end + seek(ctx.fmt_io, pos) + return false +end + +function contains_multiline_triple_string(ctx, node::Node) + # If this is a leaf just advance the stream + if is_leaf(node) + accept_node!(ctx, node) + return false + end + kids = verified_kids(node) + pos = position(ctx.fmt_io) + # If this is a triple string we inspect it + if kind(node) in KSet"string cmdstring" && JuliaSyntax.has_flags(node, JuliaSyntax.TRIPLE_STRING_FLAG) + triplekind, triplestring, itemkind = kind(node) === K"string" ? + (K"\"\"\"", "\"\"\"", K"String") : (K"```", "```", K"CmdString") + # Look for K"String"s ending in `\n` + for (i, kid) in pairs(kids) + if i === firstindex(kids) || i === lastindex(kids) + @assert kind(kid) === triplekind + @assert String(read_bytes(ctx, kid)) == triplestring + end + if kind(kid) === itemkind + if endswith(String(read_bytes(ctx, kid)), "\n") + return true + end + else + accept_node!(ctx, kid) + end + end + @assert position(ctx.fmt_io) == pos + span(node) + else + for kid in kids + kpos = position(ctx.fmt_io) + if contains_multiline_triple_string(ctx, kid) + return true + end + seek(ctx.fmt_io, kpos) + accept_node!(ctx, kid) + end + end + return false +end + ########################## # Utilities for IOBuffer # ########################## diff --git a/src/main.jl b/src/main.jl index b3515ac..8d4d032 100644 --- a/src/main.jl +++ b/src/main.jl @@ -35,12 +35,15 @@ function print_help() println(io, " julia -m Runic [] ...") println(io) printstyled(io, "DESCRIPTION\n", bold = true) - println(io, """ + println( + io, """ `Runic.main` (typically invoked as `julia -m Runic`) formats Julia source code using the Runic.jl formatter. - """) + """, + ) printstyled(io, "OPTIONS\n", bold = true) - println(io, """ + println( + io, """ ... Input path(s) (files and/or directories) to process. For directories, all files (recursively) with the '*.jl' suffix are used as input files. @@ -69,7 +72,8 @@ function print_help() Output file to write formatted code to. If the specified file is `-` output is written to stdout. This option can not be used together with multiple input paths. - """) + """, + ) return end diff --git a/src/runestone.jl b/src/runestone.jl index 4597992..b5396bb 100644 --- a/src/runestone.jl +++ b/src/runestone.jl @@ -387,7 +387,8 @@ function spaces_in_listlike(ctx::Context, node::Node) # Multiline lists require leading and trailing newline # multiline = contains_outer_newline(kids, opening_leaf_idx, closing_leaf_idx) - multiline = any(y -> any_leaf(x -> kind(x) === K"NewlineWs", kids[y]), (opening_leaf_idx + 1):(closing_leaf_idx - 1)) + # multiline = any(y -> any_leaf(x -> kind(x) === K"NewlineWs", kids[y]), (opening_leaf_idx + 1):(closing_leaf_idx - 1)) + multiline = is_multiline_between_idxs(ctx, node, opening_leaf_idx, closing_leaf_idx) is_named_tuple = kind(node) === K"tuple" && n_items == 1 && kind(kids[first_item_idx]) === K"parameters" @@ -2126,7 +2127,8 @@ function indent_listlike( open_idx == close_idx && return nothing # Check whether we expect leading/trailing newlines # multiline = contains_outer_newline(kids, open_idx, close_idx) - multiline = any(y -> any_leaf(x -> kind(x) === K"NewlineWs", kids[y]), (open_idx + 1):(close_idx - 1)) + # multiline = any(y -> any_leaf(x -> kind(x) === K"NewlineWs", kids[y]), (open_idx + 1):(close_idx - 1)) + multiline = is_multiline_between_idxs(ctx, node, open_idx, close_idx) if !multiline # TODO: This should be fine? If there are no newlines it should be safe to just # don't indent anything in this node? diff --git a/test/runtests.jl b/test/runtests.jl index 8f290e2..7415c9a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -711,6 +711,11 @@ end # Functors @test format_string("function$(sp)(a::A)(b)\nx\nend") == "function (a::A)(b)\n x\nend" + # Multiline strings inside lists + for trip in ("\"\"\"", "```") + @test format_string("println(io, $(trip)\n$(sp)a\n$(sp)\n$(sp)b\n$(sp)$(trip))") == + "println(\n io, $(trip)\n a\n\n b\n $(trip),\n)" + end end end