Skip to content

Commit

Permalink
Insert parens around op calls in colon, fixes #3.
Browse files Browse the repository at this point in the history
  • Loading branch information
fredrikekre committed Jun 22, 2024
1 parent 8a56dd8 commit abfc1e5
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 27 deletions.
1 change: 1 addition & 0 deletions src/Runic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ function format_node!(ctx::Context, node::Node)::Union{Node, Nothing, NullNode}
@return_something spaces_around_anonymous_function(ctx, node)
@return_something spaces_around_keywords(ctx, node)
@return_something no_spaces_around_colon_etc(ctx, node)
@return_something parens_around_op_calls_in_colon(ctx, node)
@return_something for_loop_use_in(ctx, node)
@return_something braces_around_where_rhs(ctx, node)
@return_something four_space_indent(ctx, node)
Expand Down
2 changes: 1 addition & 1 deletion src/chisels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ function has_newline_after_non_whitespace(node::Node)
# Everything is whitespace...
return any(x -> kind(x) === K"NewlineWs", kids)
end
return any(x -> kind(x) === K"NewlineWs", kids[idx + 1:end]) ||
return any(x -> kind(x) === K"NewlineWs", kids[(idx + 1):end]) ||
has_newline_after_non_whitespace(kids[idx])
# if idx === nothing
# # All is whitespace, check if any of the kids is a newline
Expand Down
110 changes: 85 additions & 25 deletions src/runestone.jl
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ function spaces_around_x(ctx::Context, node::Node, is_x::F, n_leaves_per_x::Int
# Whitespace node but replace since not single space
any_changes = true
if kids′ === kids
kids′ = kids[1:i - 1]
kids′ = kids[1:(i - 1)]
end
push!(kids′, ws)
replace_bytes!(ctx, " ", span(kid))
Expand All @@ -213,7 +213,7 @@ function spaces_around_x(ctx::Context, node::Node, is_x::F, n_leaves_per_x::Int
accept_node!(ctx, kid′)
any_changes = true
if kids′ === kids
kids′ = kids[1:i - 1]
kids′ = kids[1:(i - 1)]
end
push!(kids′, kid′)
end
Expand All @@ -230,7 +230,7 @@ function spaces_around_x(ctx::Context, node::Node, is_x::F, n_leaves_per_x::Int
# Not a whitespace node, insert one
any_changes = true
if kids′ === kids
kids′ = kids[1:i - 1]
kids′ = kids[1:(i - 1)]
end
push!(kids′, ws)
replace_bytes!(ctx, " ", 0)
Expand Down Expand Up @@ -334,7 +334,7 @@ function spaces_in_listlike(ctx::Context, node::Node)

n_items = count(
x -> !(JuliaSyntax.is_whitespace(x) || kind(x) === K","),
@view(kids[opening_leaf_idx + 1:closing_leaf_idx - 1]),
@view(kids[(opening_leaf_idx + 1):(closing_leaf_idx - 1)]),
)
last_item_idx = findprev(x -> !(JuliaSyntax.is_whitespace(x) || kind(x) in KSet", ;"), kids, closing_leaf_idx - 1)
if last_item_idx <= opening_leaf_idx
Expand All @@ -358,7 +358,7 @@ function spaces_in_listlike(ctx::Context, node::Node)
require_trailing_comma = has_tag(node, TAG_TRAILING_COMMA)
elseif n_items > 0
require_trailing_comma = any(
x -> kind(x) === K"NewlineWs", @view(kids[last_item_idx + 1:closing_leaf_idx - 1]),
x -> kind(x) === K"NewlineWs", @view(kids[(last_item_idx + 1):(closing_leaf_idx - 1)]),
) || has_newline_after_non_whitespace(kids[last_item_idx])
end
expect_trailing_comma = require_trailing_comma
Expand Down Expand Up @@ -397,7 +397,7 @@ function spaces_in_listlike(ctx::Context, node::Node)
replace_bytes!(ctx, "", span(kid′))
this_kid_changed = true
if kids′ === kids
kids′ = kids[1:i - 1]
kids′ = kids[1:(i - 1)]
end
elseif kind(kid′) === K"NewlineWs" ||
(kind(kid′) === K"Whitespace" && peek(i) === K"Comment")
Expand Down Expand Up @@ -426,7 +426,7 @@ function spaces_in_listlike(ctx::Context, node::Node)
any_kid_changed |= this_kid_changed
if any_kid_changed
if kids′ === kids
kids′ = kids[1:i - 1]
kids′ = kids[1:(i - 1)]
end
push!(kids′, kid′)
end
Expand All @@ -452,7 +452,7 @@ function spaces_in_listlike(ctx::Context, node::Node)
# (no state change)
this_kid_changed = true
if kids′ === kids
kids′ = kids[1:i - 1]
kids′ = kids[1:(i - 1)]
end
replace_bytes!(ctx, "", span(kid′))
elseif kind(kid′) === K"NewlineWs" ||
Expand All @@ -463,15 +463,15 @@ function spaces_in_listlike(ctx::Context, node::Node)
# - there is a comma coming but it is on the next line (weird)
# - there is a comment with no space before it
next_non_ws_idx = findnext(
!JuliaSyntax.is_whitespace, @view(kids[1:closing_leaf_idx - 1]), i + 1,
!JuliaSyntax.is_whitespace, @view(kids[1:(closing_leaf_idx - 1)]), i + 1,
)
next_kind = next_non_ws_idx === nothing ? nothing : kind(kids[next_non_ws_idx])
# Insert a comma if there isn't one coming
if trailing && next_kind !== K","
@assert expect_trailing_comma
this_kid_changed = true
if kids′ === kids
kids′ = kids[1:i - 1]
kids′ = kids[1:(i - 1)]
end
replace_bytes!(ctx, ",", 0)
push!(kids′, comma)
Expand All @@ -486,7 +486,7 @@ function spaces_in_listlike(ctx::Context, node::Node)
@assert kind(node) in KSet"call dotcall curly" # TODO: Can this happen for named tuples?
@assert i === last_item_idx
@assert findnext(
!JuliaSyntax.is_whitespace, @view(kids[1:closing_leaf_idx - 1]), i + 1,
!JuliaSyntax.is_whitespace, @view(kids[1:(closing_leaf_idx - 1)]), i + 1,
) === nothing
if kind(first_leaf(kid′)) === K"Whitespace"
# Delete the whitespace leaf
Expand All @@ -511,7 +511,7 @@ function spaces_in_listlike(ctx::Context, node::Node)
accept_node!(ctx, kid′)
if any_kid_changed
if kids′ === kids
kids′ = kids[1:i - 1]
kids′ = kids[1:(i - 1)]
end
push!(kids′, kid′)
end
Expand All @@ -531,7 +531,7 @@ function spaces_in_listlike(ctx::Context, node::Node)
# Wrong span, replace it
this_kid_changed = true
if kids′ === kids
kids′ = kids[1:i - 1]
kids′ = kids[1:(i - 1)]
end
replace_bytes!(ctx, " ", span(kid′))
accept_node!(ctx, ws)
Expand Down Expand Up @@ -568,7 +568,7 @@ function spaces_in_listlike(ctx::Context, node::Node)
kid′ = replace_first_leaf(kid′, ws)
this_kid_changed = true
if kids′ === kids
kids′ = kids[1:i - 1]
kids′ = kids[1:(i - 1)]
end
replace_bytes!(ctx, " ", span(ws_node))
accept_node!(ctx, kid′)
Expand All @@ -579,7 +579,7 @@ function spaces_in_listlike(ctx::Context, node::Node)
# Insert a standalone space kid and then accept the current node
this_kid_changed = true
if kids′ === kids
kids′ = kids[1:i - 1]
kids′ = kids[1:(i - 1)]
end
replace_bytes!(ctx, " ", 0)
push!(kids′, ws)
Expand All @@ -598,7 +598,7 @@ function spaces_in_listlike(ctx::Context, node::Node)
# removed
this_kid_changed = true
if kids′ === kids
kids′ = kids[1:i - 1]
kids′ = kids[1:(i - 1)]
end
replace_bytes!(ctx, "", span(kid′))
elseif kind(kid′) === K"NewlineWs" ||
Expand All @@ -620,7 +620,7 @@ function spaces_in_listlike(ctx::Context, node::Node)
@assert expect_trailing_comma
any_kid_changed = true
if kids′ === kids
kids′ = kids[1:closing_leaf_idx - 1]
kids′ = kids[1:(closing_leaf_idx - 1)]
end
replace_bytes!(ctx, ",", 0)
push!(kids′, comma)
Expand Down Expand Up @@ -714,7 +714,7 @@ function no_spaces_around_x(ctx::Context, node::Node, is_x::F) where {F}
# Ignore it but need to copy kids and re-write bytes
any_changes = true
if kids′ === kids
kids′ = kids[1:i - 1]
kids′ = kids[1:(i - 1)]
end
replace_bytes!(ctx, "", span(kid))
else
Expand Down Expand Up @@ -742,7 +742,7 @@ function no_spaces_around_x(ctx::Context, node::Node, is_x::F) where {F}
end
if any_changes
if kids === kids′
kids′ = kids[1:i - 1]
kids′ = kids[1:(i - 1)]
end
push!(kids′, kid)
end
Expand Down Expand Up @@ -848,7 +848,7 @@ function spaces_around_keywords(ctx::Context, node::Node)
# Replace with single space.
any_changes = true
if kids′ === kids
kids′ = kids[1:i - 1]
kids′ = kids[1:(i - 1)]
end
replace_bytes!(ctx, " ", span(kid))
push!(kids′, ws)
Expand All @@ -865,7 +865,7 @@ function spaces_around_keywords(ctx::Context, node::Node)
accept_node!(ctx, kid′)
any_changes = true
if kids′ === kids
kids′ = kids[1:i - 1]
kids′ = kids[1:(i - 1)]
end
push!(kids′, kid′)
end
Expand All @@ -876,7 +876,7 @@ function spaces_around_keywords(ctx::Context, node::Node)
@assert kind(node) === K"where"
any_changes = true
if kids′ === kids
kids′ = kids[1:i - 1]
kids′ = kids[1:(i - 1)]
end
# Insert the space before/after the kid depending on whether we are looking
# for a space before or after a keyword
Expand Down Expand Up @@ -1041,8 +1041,8 @@ function braces_around_where_rhs(ctx::Context, node::Node)
return nothing
end
# Wrap the rhs in a braces node
kids′ = kids[1:rhs_idx - 1]
for i in 1:rhs_idx - 1
kids′ = kids[1:(rhs_idx - 1)]
for i in 1:(rhs_idx - 1)
accept_node!(ctx, kids[i])
end
opening_brace = Node(JuliaSyntax.SyntaxHead(K"{", 0), 1)
Expand All @@ -1059,7 +1059,7 @@ function braces_around_where_rhs(ctx::Context, node::Node)
replace_bytes!(ctx, "}", 0)
accept_node!(ctx, closing_brace)
# Accept any remaining kids
for i in rhs_idx + 1:length(kids)
for i in (rhs_idx + 1):length(kids)
accept_node!(ctx, kids[i])
push!(kids′, kids[i])
end
Expand All @@ -1068,6 +1068,66 @@ function braces_around_where_rhs(ctx::Context, node::Node)
return make_node(node, kids′)
end

function parens_around_op_calls_in_colon(ctx::Context, node::Node)
if !(is_infix_op_call(node) && kind(infix_op_call_op(node)) === K":")
return nothing
end

kids = verified_kids(node)
kids′ = kids
any_changes = false
pos = position(ctx.fmt_io)

for i in eachindex(kids)
kid = kids[i]
if is_infix_op_call(kid)
if kids′ === kids
kids′ = kids[1:(i - 1)]
end
grandkids = verified_kids(kid)
first_non_ws = findfirst(!JuliaSyntax.is_whitespace, grandkids)::Int
last_non_ws = findlast(!JuliaSyntax.is_whitespace, grandkids)::Int
# Extract whitespace grandkids to become kids
for j in 1:(first_non_ws - 1)
accept_node!(ctx, grandkids[j])
push!(kids′, grandkids[j])
end
# Create the parens node
opening_paren = Node(JuliaSyntax.SyntaxHead(K"(", 0), 1)
replace_bytes!(ctx, "(", 0)
accept_node!(ctx, opening_paren)
parens_kids = [opening_paren]
kid′_kids = grandkids[first_non_ws:last_non_ws]
kid′ = make_node(kid, kid′_kids)
accept_node!(ctx, kid′)
push!(parens_kids, kid′)
closing_paren = Node(JuliaSyntax.SyntaxHead(K")", 0), 1)
replace_bytes!(ctx, ")", 0)
accept_node!(ctx, closing_paren)
push!(parens_kids, closing_paren)
parens = Node(JuliaSyntax.SyntaxHead(K"parens", 0), parens_kids)
push!(kids′, parens)
for j in (last_non_ws + 1):length(grandkids)
accept_node!(ctx, grandkids[j])
push!(kids′, grandkids[j])
end
any_changes = true
else
accept_node!(ctx, kid)
any_changes && push!(kids′, kid)
end
end
# Reset stream
seek(ctx.fmt_io, pos)
# Rebuild node and return
if any_changes
node′ = make_node(node, kids′)
return node′
else
return nothing
end
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
Expand Down
19 changes: 18 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ end
# Exceptions to the rule: `:` and `^`
# a:b
@test format_string("$(sp)a$(sp):$(sp)b$(sp)") == "$(sp)a:b$(sp)"
@test format_string("$(sp)a + a$(sp):$(sp)b + b$(sp)") == "$(sp)a + a:b + b$(sp)"
@test format_string("$(sp)a + a$(sp):$(sp)b + b$(sp)") == "$(sp)(a + a):(b + b)$(sp)"
@test format_string("$(sp)(1 + 2)$(sp):$(sp)(1 + 3)$(sp)") ==
"$(sp)(1 + 2):(1 + 3)$(sp)"
# a:b:c
Expand Down Expand Up @@ -594,3 +594,20 @@ end

end
end

@testset "parens around op calls in colon" begin
for a in ("a + a", "a + a * a"), sp in ("", " ", " ")
@show a, sp
@test format_string("$(a)$(sp):$(sp)$(a)") == "($(a)):($(a))"
@test format_string("$(a)$(sp):$(sp)$(a)$(sp):$(sp)$(a)") == "($(a)):($(a)):($(a))"
@test format_string("$(a)$(sp):$(sp)$(a)$(sp):$(sp)$(a):$(a)") == "(($(a)):($(a)):($(a))):($(a))"
end
# No-ops
for p in ("", "()"), sp in ("", " ", " ")
@test format_string("a$(p)$(sp):$(sp)b$(p)") == "a$(p):b$(p)"
@test format_string("a$(p)$(sp):$(sp)b$(p)$(sp):$(sp)c$(p)") == "a$(p):b$(p):c$(p)"
end
# Edgecase: leading whitespace so that the paren have to be inserted in the middle of
# the node
Runic.format_string("i in a + b:c") == "i in (a + b):c"
end

0 comments on commit abfc1e5

Please sign in to comment.