Skip to content

Commit

Permalink
Fixes to listlike code
Browse files Browse the repository at this point in the history
 - Listlike expressions (e.g. tuples, calls, paren-blocks, vectors) that
   span multiple lines now always require a leading and a trailing
   newline (and thus a trailing comma from previous listlike spacing
   code). For example
   ```diff
   -f(a,
   -    b)
   +f(
   +    a,
   +    b,
   +)
   ```
 - Listlike expressions are now hard indents instead of the
   soft/continuation style from before. This looks better.
 - Vectors (K"vect" and K"ref") are now spaced like lists.
 - Fix some bugs in spaces-around-operators when there where hidden
   newlines at the beginning of the call chain.

Closes #9.
  • Loading branch information
fredrikekre committed Jul 5, 2024
1 parent 2b782f4 commit e277ac2
Show file tree
Hide file tree
Showing 6 changed files with 473 additions and 136 deletions.
8 changes: 5 additions & 3 deletions src/Runic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,11 @@ include("chisels.jl")

# Return the result of expr if it doesn't evaluate to `nothing`
macro return_something(expr)
return :(let node = $(esc(expr))
node === nothing || return node
end)
return :(
let node = $(esc(expr))
node === nothing || return node
end
)
end

#######################################################
Expand Down
109 changes: 109 additions & 0 deletions src/chisels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,22 @@ function replace_first_leaf(node::Node, kid′::Union{Node, NullNode})
end
end

function replace_last_leaf(node::Node, kid′::Union{Node, NullNode})
if is_leaf(node)
return kid′
else
kids′ = copy(verified_kids(node))
kid′′ = replace_last_leaf(kids′[end], kid′)
if kid′′ === nullnode
pop!(kids′)
else
kids′[end] = kid′′
end
@assert length(kids′) > 0
return make_node(node, kids′)
end
end

function last_leaf(node::Node)
if is_leaf(node)
return node
Expand Down Expand Up @@ -272,6 +288,33 @@ function is_assignment(node::Node)
# return !is_leaf(node) && JuliaSyntax.is_prec_assignment(node)
end

function unwrap_to_call_or_tuple(x)
is_leaf(x) && return nothing
@assert !is_leaf(x)
if kind(x) in KSet"call tuple"
return x
end
xkids = verified_kids(x)
xi = findfirst(x -> !JuliaSyntax.is_whitespace(x), xkids)::Int
return unwrap_to_call_or_tuple(xkids[xi])
end

function is_longform_anon_function(node::Node)
is_leaf(node) && return false
kind(node) === K"function" || return false
kids = verified_kids(node)
kw = findfirst(x -> kind(x) === K"function", kids)
@assert kw !== nothing
sig = findnext(x -> !JuliaSyntax.is_whitespace(x), kids, kw + 1)::Int
sigkid = kids[sig]
maybe_tuple = unwrap_to_call_or_tuple(sigkid)
if maybe_tuple === nothing
return false
else
return kind(maybe_tuple) === K"tuple"
end
end

# Just like `JuliaSyntax.is_infix_op_call`, but also check that the node is K"call" or
# K"dotcall"
function is_infix_op_call(node::Node)
Expand Down Expand Up @@ -319,6 +362,72 @@ function is_paren_block(node::Node)
return kind(node) === K"block" && JuliaSyntax.has_flags(node, JuliaSyntax.PARENS_FLAG)
end

function first_leaf_predicate(node::Node, pred::F) where {F}
if is_leaf(node)
return pred(node) ? node : nothing
else
kids = verified_kids(node)
for k in kids
r = first_leaf_predicate(k, pred)
if r !== nothing
return r
end
end
return nothing
end
end

function last_leaf_predicate(node::Node, pred::F) where {F}
if is_leaf(node)
return pred(node) ? node : nothing
else
kids = verified_kids(node)
for k in Iterators.reverse(kids)
r = first_leaf_predicate(k, pred)
if r !== nothing
return r
end
end
return nothing
end
end

function contains_outer_newline(kids::Vector{Node}, oidx::Int, cidx::Int; recurse = true)
pred = x -> kind(x) === K"NewlineWs" || !JuliaSyntax.is_whitespace(x)
for i in (oidx + 1):(cidx - 1)
kid = kids[i]
r = first_leaf_predicate(kid, pred)
if r !== nothing && kind(r) === K"NewlineWs"
return true
end
r = last_leaf_predicate(kid, pred)
if r !== nothing && kind(r) === K"NewlineWs"
return true
end
if kind(kid) === K"parameters"
grandkids = verified_kids(kid)
semiidx = findfirst(x -> kind(x) === K";", grandkids)::Int
r = contains_outer_newline(verified_kids(kid), semiidx, length(grandkids) + 1)
if r === true # r can be nothing so `=== true` is intentional
return true
end
end
end
return false
end

function any_leaf(pred::F, node::Node) where {F}
if is_leaf(node)
return pred(node)::Bool
else
kids = verified_kids(node)
for k in kids
any_leaf(pred, k) && return true
end
return false
end
end

##########################
# Utilities for IOBuffer #
##########################
Expand Down
4 changes: 2 additions & 2 deletions src/debug.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ function Base.showerror(io::IO, err::AssertionError)
print(
io,
"Runic.AssertionError: `", err.msg, "`. This is unexpected, " *
"please file an issue with a reproducible example at " *
"https://github.com/fredrikekre/Runic.jl/issues/new.",
"please file an issue with a reproducible example at " *
"https://github.com/fredrikekre/Runic.jl/issues/new.",
)
end

Expand Down
2 changes: 1 addition & 1 deletion src/main.jl
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ function main(argv)
if !(inplace || check || diff || outputfile !== nothing)
return panic(
"at least one of options `-c, --check`, `-d, --diff`, `-i, --inplace`, " *
"or `-o, --output` must be specified",
"or `-o, --output` must be specified",
)
end

Expand Down
Loading

0 comments on commit e277ac2

Please sign in to comment.