diff --git a/README.md b/README.md index 8cf280a..a1b7cef 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,7 @@ julia-master --project -m Runic --check --diff "${files[@]}" This is a list of things that Runic currently is doing: + - [Line width limit](#line-width-limit) - [Indentation](#indentation) - [Spaces around operators, assignment, etc](#spaces-around-operators-assignment-etc) - [Spaces around keywords](#spaces-around-keywords) @@ -172,7 +173,7 @@ This is a list of things that Runic currently is doing: - [Literal floating point numbers](#literal-floating-point-numbers) - [Literal hex and oct numbers](#literal-hex-and-oct-numbers) - [Parentheses around operator calls in colon](#parentheses-around-operator-calls-in-colon) - - [`in` instead of `∈` and `=`]()#in-instead-of--and- + - [`in` instead of `∈` and `=`](#in-instead-of--and-) - [Braces around right hand side of `where`](#braces-around-right-hand-side-of-where) - [Trailing whitespace](#trailing-whitespace) @@ -256,6 +257,26 @@ x = a + b * d ``` +### Vertical spacing + +Runic removes empty vertical spacing so that there are at maximum two empty lines between +expressions. Examples: +```diff +-function f() +- x = 1 +- +- +- +- return x +-end ++function f() ++ x = 1 ++ ++ ++ return x ++end +``` + ### Spaces around operators, assignment, etc Runic formats spaces around infix operators, assignments, comparison chains, and type diff --git a/src/Runic.jl b/src/Runic.jl index b88614c..4414ccc 100644 --- a/src/Runic.jl +++ b/src/Runic.jl @@ -299,6 +299,7 @@ function format_node!(ctx::Context, node::Node)::Union{Node, Nothing, NullNode} ctx.call_depth += 1 @return_something insert_delete_mark_newlines(ctx, node) @return_something trim_trailing_whitespace(ctx, node) + @return_something max_three_consecutive_newlines(ctx, node) @return_something format_hex_literals(ctx, node) @return_something format_oct_literals(ctx, node) @return_something format_float_literals(ctx, node) diff --git a/src/runestone.jl b/src/runestone.jl index e8658f6..787224d 100644 --- a/src/runestone.jl +++ b/src/runestone.jl @@ -1312,6 +1312,31 @@ function parens_around_op_calls_in_colon(ctx::Context, node::Node) end end + +# Remove more than two newlines in a row +function max_three_consecutive_newlines(ctx::Context, node::Node) + is_leaf(node) && return nothing + kids = verified_kids(node) + idx = findfirst(x -> kind(x) === K"NewlineWs", kids) + while idx !== nothing + if idx + 3 <= length(kids) && + (kind(kids[idx + 1]) == kind(kids[idx + 2]) == kind(kids[idx + 3]) == K"NewlineWs") + kids′ = Vector{Node}(undef, length(kids) - 1) + for (i, kid) in pairs(kids) + if i == idx + replace_bytes!(ctx, "", span(kids[idx])) + else + accept_node!(ctx, kid) + kids′[i < idx ? i : (i - 1)] = kid + end + end + return make_node(node, kids′) + end + idx = findnext(x -> kind(x) === K"NewlineWs", kids, idx + 1) + end + return nothing +end + # This function materialized all indentations marked by `insert_delete_mark_newlines`. function four_space_indent(ctx::Context, node::Node) kind(node) === K"NewlineWs" || return nothing diff --git a/test/runtests.jl b/test/runtests.jl index e59c7db..a118faf 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -722,3 +722,16 @@ end "$(o)\n a, # a\n b,\n$(c)" end end + +@testset "max three consecutive newlines" begin + f, g = "f() = 1", "g() = 2" + for n in 1:5 + nl = "\n" + m = min(n, 3) + @test format_string(f * nl^n * g) == f * nl^m * g + @test format_string("module A" * nl^n * "end") == "module A" * nl^m * "end" + @test format_string("function f()" * nl^n * "end") == "function f()" * nl^m * "end" + @test format_string("function f()" * nl^2 * "x = 1" * nl^n * "end") == + "function f()" * nl^2 * " x = 1" * nl^m * "end" + end +end