diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..146ff22 --- /dev/null +++ b/TODO.md @@ -0,0 +1,24 @@ +# TODOs, notes, and various thoughts + +## Inconsistencies + +- The `spaces_around_operators` rule have the following inconsistencies. + + - `:`, `^`, and `::` instead fall under `no_spaces_around_colon_etc`: + ```julia + # current formatting # "consistent" formatting + a:b a : b ✖ + a^b a ^ b ✖ + a::b a :: b ✖ + ``` + + - `<:` and `<:` fall under `no_spaces_around_colon_etc` if they have no LHS: + ```julia + # current formatting # "consistent" formatting + a <: b a <: b ✔ + a >: b a >: b ✔ + a{c <: b} a{c <: b} ✔ + a{c >: b} a{c >: b} ✔ + a{<:b} a{<: b} ✖ + a{>:b} a{>: b} ✖ + ``` diff --git a/src/chisels.jl b/src/chisels.jl index 9da101b..006a08a 100644 --- a/src/chisels.jl +++ b/src/chisels.jl @@ -49,6 +49,11 @@ function first_leaf(node::JuliaSyntax.GreenNode) end end +# Return number of non-whitespace children +function n_children(node::JuliaSyntax.GreenNode) + return is_leaf(node) ? 0 : count(!JuliaSyntax.is_whitespace, verified_children(node)) +end + # This function exist so that we can type-assert the return value to narrow it down from # `Union{Tuple{}, Vector{JuliaSyntax.GreenNode}}` to `Vector{JuliaSyntax.GreenNode}`. Must # only be called after verifying that the node has children. diff --git a/src/runestone.jl b/src/runestone.jl index 1bf99da..a77ef6e 100644 --- a/src/runestone.jl +++ b/src/runestone.jl @@ -271,7 +271,7 @@ end function spaces_around_operators(ctx::Context, node::JuliaSyntax.GreenNode) if !( (is_infix_op_call(node) && !(JuliaSyntax.kind(infix_op_call_op(node)) in KSet": ^")) || - (JuliaSyntax.kind(node) in KSet"<: >:" && !is_leaf(node)) || + (JuliaSyntax.kind(node) in KSet"<: >:" && n_children(node) == 3) || (JuliaSyntax.kind(node) === K"comparison" && !JuliaSyntax.is_trivia(node)) ) return nothing @@ -308,9 +308,9 @@ function no_spaces_around_x(ctx::Context, node::JuliaSyntax.GreenNode, is_x::F) looking_for_x = false - # K"::" is a special case here since it can be used without an LHS in e.g. function - # definitions like `f(::Int) = ...`. - if JuliaSyntax.kind(node) === K"::" + # K"::", K"<:", and K">:" are special cases here since they can be used without an LHS + # in e.g. `f(::Int) = ...` and `Vector{<:Real}`. + if JuliaSyntax.kind(node) in KSet":: <: >:" looking_for_x = is_x(first_non_whitespace_child(node))::Bool end @@ -354,11 +354,12 @@ end 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)) + (JuliaSyntax.kind(node) === K"::" && !is_leaf(node)) || + (JuliaSyntax.kind(node) in KSet"<: >:" && n_children(node) == 2) ) return nothing end - @assert JuliaSyntax.kind(node) in KSet"call ::" - is_x = x -> is_leaf(x) && JuliaSyntax.kind(x) in KSet": ^ ::" + @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 1b78f9b..5364102 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -187,17 +187,23 @@ end end @testset "whitespace around <: and >:, no whitespace around ::" begin - # K"::" + # K"::" with both LHS and RHS @test format_string("a::T") == "a::T" @test format_string("a::T::S") == "a::T::S" @test format_string("a :: T") == "a::T" + # K"::" with just RHS @test format_string("f(::T)::T = 1") == "f(::T)::T = 1" @test format_string("f(:: T) :: T = 1") == "f(::T)::T = 1" - # K"<:" and K">:" + # K"<:" and K">:" with both LHS and RHS @test format_string("a<:T") == "a <: T" @test format_string("a>:T") == "a >: T" @test format_string("a <: T") == "a <: T" @test format_string("a >: T") == "a >: T" + # K"<:" and K">:" with just RHS + @test format_string("V{<:T}") == "V{<:T}" + @test format_string("V{<: T}") == "V{<:T}" + @test format_string("V{>:T}") == "V{>:T}" + @test format_string("V{>: T}") == "V{>:T}" # K"comparison" for chains @test format_string("a<:T<:S") == "a <: T <: S" @test format_string("a>:T>:S") == "a >: T >: S"