From d6887109e34c0be0312d5d03c5c93ff7e2ca6853 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Mon, 27 May 2024 02:01:38 +0200 Subject: [PATCH] Prune spaces around `:`, `^`, and `::`. --- src/Runic.jl | 1 + src/runestone.jl | 69 +++++++++++++++++++++++++++++++++++++++++++++++- test/runtests.jl | 30 ++++++++------------- 3 files changed, 80 insertions(+), 20 deletions(-) diff --git a/src/Runic.jl b/src/Runic.jl index b4dce36..b939dc4 100644 --- a/src/Runic.jl +++ b/src/Runic.jl @@ -195,6 +195,7 @@ function format_node!(ctx::Context, node::JuliaSyntax.GreenNode)::Union{JuliaSyn @return_something format_float_literals(ctx, node) @return_something spaces_around_operators(ctx, node) @return_something spaces_around_assignments(ctx, node) + @return_something no_spaces_around_colon_etc(ctx, node) # If the node is unchanged at this point, just keep going. diff --git a/src/runestone.jl b/src/runestone.jl index 496b55c..54dab73 100644 --- a/src/runestone.jl +++ b/src/runestone.jl @@ -266,7 +266,8 @@ function spaces_around_x(ctx::Context, node::JuliaSyntax.GreenNode, is_x::F) whe end end -# This pass handles spaces around infix operator calls and comparison chains +# This pass handles spaces around infix operator calls, comparison chains, and +# <: and >: operators. function spaces_around_operators(ctx::Context, node::JuliaSyntax.GreenNode) if !( (is_infix_op_call(node) && !(JuliaSyntax.kind(infix_op_call_op(node)) in KSet": ^")) || @@ -289,3 +290,69 @@ function spaces_around_assignments(ctx::Context, node::JuliaSyntax.GreenNode) is_x = x -> is_assignment(x) || JuliaSyntax.kind(x) === K"in" return spaces_around_x(ctx, node, is_x) end + +# Opposite of `spaces_around_x`: remove spaces around `x` +function no_spaces_around_x(ctx::Context, node::JuliaSyntax.GreenNode, is_x::F) where F + @assert JuliaSyntax.haschildren(node) + # TODO: Can't handle NewlineWs here right now + if any(JuliaSyntax.kind(c) === K"NewlineWs" for c in JuliaSyntax.children(node)) + return nothing + end + + children = JuliaSyntax.children(node)::AbstractVector + children′ = children + any_changes = false + original_bytes = node_bytes(ctx, node) + span_sum = 0 + pos = position(ctx.fmt_io) + + looking_for_x = false + + for (i, child) in pairs(children) + span_sum += JuliaSyntax.span(child) + if (i == 1 || i == length(children)) && JuliaSyntax.kind(child) === K"Whitespace" + accept_node!(ctx, child) + any_changes && push!(children′, child) + elseif JuliaSyntax.kind(child) === K"Whitespace" + # Ignore it but need to copy children and re-write bytes + any_changes = true + if children′ === children + children′ = children[1:i - 1] + end + remaining_bytes = @view original_bytes[(span_sum + 1):end] + write_and_reset(ctx, remaining_bytes) + else + @assert JuliaSyntax.kind(child) !== K"Whitespace" + if looking_for_x + @assert is_x(child)::Bool + end + any_changes && push!(children′, child) + accept_node!(ctx, child) + looking_for_x = !looking_for_x + end + end + # Reset stream + seek(ctx.fmt_io, pos) + if any_changes + # Create new node and return it + span′ = mapreduce(JuliaSyntax.span, +, children′; init = 0) + @assert span′ < JuliaSyntax.span(node) + node′ = JuliaSyntax.GreenNode(JuliaSyntax.head(node), span′, children′) + return node′ + else + return nothing + end +end + +# no spaces around `:`, `^`, and `::` +function no_spaces_around_colon_etc(ctx::Context, node::JuliaSyntax.GreenNode) + if !( + (is_infix_op_call(node) && JuliaSyntax.kind(infix_op_call_op(node)) in KSet": ^") || + (JuliaSyntax.kind(node) === K"::" && !is_leaf(node)) + ) + return nothing + end + @assert JuliaSyntax.kind(node) in KSet"call ::" + is_x = x -> is_leaf(x) && JuliaSyntax.kind(x) in KSet": ^ ::" + return no_spaces_around_x(ctx, node, is_x) +end diff --git a/test/runtests.jl b/test/runtests.jl index 50d9f69..c1aae33 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -116,24 +116,16 @@ end "$(sp)sin(α) $(op) cos(β) $(op) tan(γ)$(sp)" end # Exceptions to the rule: `:` and `^` - if sp == "" - # a:b - @test format_string("$(sp)a$(sp):$(sp)b$(sp)") == "a:b" - @test format_string("$(sp)(1 + 2)$(sp):$(sp)(1 + 3)$(sp)") == "(1 + 2):(1 + 3)" - # a:b:c - @test format_string("$(sp)a$(sp):$(sp)b$(sp):$(sp)c$(sp)") == "a:b:c" - @test format_string("$(sp)(1 + 2)$(sp):$(sp)(1 + 3)$(sp):$(sp)(1 + 4)$(sp)") == - "(1 + 2):(1 + 3):(1 + 4)" - # a^b - @test format_string("$(sp)a$(sp)^$(sp)b$(sp)") == "a^b" - else - @test_broken format_string("$(sp)a$(sp):$(sp)b$(sp)") == "a:b" - @test_broken format_string("$(sp)(1 + 2)$(sp):$(sp)(1 + 3)$(sp)") == "(1 + 2):(1 + 3)" - @test_broken format_string("$(sp)a$(sp):$(sp)b$(sp):$(sp)c$(sp)") == "a:b:c" - @test_broken format_string("$(sp)(1 + 2)$(sp):$(sp)(1 + 3)$(sp):$(sp)(1 + 4)$(sp)") == - "(1 + 2):(1 + 3):(1 + 4)" - @test_broken format_string("$(sp)a$(sp)^$(sp)b$(sp)") == "a^b" - end + # a:b + @test format_string("$(sp)a$(sp):$(sp)b$(sp)") == "$(sp)a:b$(sp)" + @test format_string("$(sp)(1 + 2)$(sp):$(sp)(1 + 3)$(sp)") == + "$(sp)(1 + 2):(1 + 3)$(sp)" + # a:b:c + @test format_string("$(sp)a$(sp):$(sp)b$(sp):$(sp)c$(sp)") == "$(sp)a:b:c$(sp)" + @test format_string("$(sp)(1 + 2)$(sp):$(sp)(1 + 3)$(sp):$(sp)(1 + 4)$(sp)") == + "$(sp)(1 + 2):(1 + 3):(1 + 4)$(sp)" + # a^b + @test format_string("$(sp)a$(sp)^$(sp)b$(sp)") == "$(sp)a^b$(sp)" end end @@ -178,7 +170,7 @@ end # K"::" @test format_string("a::T") == "a::T" @test format_string("a::T::S") == "a::T::S" - @test_broken format_string("a :: T") == "a::T" # TODO: Eliminate spaces instead + @test format_string("a :: T") == "a::T" # K"<:" and K">:" @test format_string("a<:T") == "a <: T" @test format_string("a>:T") == "a >: T"