Browse Source

Keep track of previous/next sibling

Sometimes there is a need to check the kind (possibly more) of previous
and next siblings in order to decide on formatting. This patch keeps
track of this in the Context.

This is used for replacing trailing whitespace from consecutive
NewlineWs nodes since sometimes the whitespace is after the LF.
pull/19/head
Fredrik Ekre 2 years ago
parent
commit
01a703f861
No known key found for this signature in database
GPG Key ID: DE82E6D5E364C0A2
  1. 43
      src/Runic.jl
  2. 2
      test/runtests.jl

43
src/Runic.jl

@ -23,6 +23,11 @@ mutable struct Context
# User settings # User settings
verbose::Bool verbose::Bool
debug::Bool debug::Bool
# Current state
# node::Union{JuliaSyntax.GreenNode, Nothing}
prev_sibling::Union{JuliaSyntax.GreenNode, Nothing}
next_sibling::Union{JuliaSyntax.GreenNode, Nothing}
# parent::Union{JuliaSyntax.GreenNode, Nothing}
end end
function Context(src_str; debug::Bool = false, verbose::Bool = debug) function Context(src_str; debug::Bool = false, verbose::Bool = debug)
@ -30,7 +35,15 @@ function Context(src_str; debug::Bool = false, verbose::Bool = debug)
src_tree = JuliaSyntax.parseall(JuliaSyntax.GreenNode, src_str; ignore_warnings=true) src_tree = JuliaSyntax.parseall(JuliaSyntax.GreenNode, src_str; ignore_warnings=true)
fmt_io = IOBuffer() fmt_io = IOBuffer()
fmt_tree = nothing fmt_tree = nothing
return Context(src_str, src_tree, src_io, fmt_io, fmt_tree, verbose, debug) return Context(
src_str, src_tree, src_io, fmt_io, fmt_tree, verbose, debug,
nothing, nothing,
)
end
function next_sibling_kind(ctx::Context)::Union{JuliaSyntax.Kind, Nothing}
next = ctx.next_sibling
return next === nothing ? nothing : JuliaSyntax.kind(next)
end end
# Read the bytes of the current node from the output io # Read the bytes of the current node from the output io
@ -66,16 +79,24 @@ function format_node_with_children!(ctx::Context, node::JuliaSyntax.GreenNode)
if !JuliaSyntax.haschildren(node) if !JuliaSyntax.haschildren(node)
return node return node
end end
# Keep track of the siblings on this stack
prev_sibling = ctx.prev_sibling
next_sibling = ctx.next_sibling
ctx.prev_sibling = nothing
ctx.next_sibling = nothing
# @assert JuliaSyntax.haschildren(node) # @assert JuliaSyntax.haschildren(node)
span_sum = 0 span_sum = 0
original_bytes = node_bytes(ctx, node) # TODO: Read into reusable buffer original_bytes = node_bytes(ctx, node) # TODO: Read into reusable buffer
children = JuliaSyntax.children(node) children = JuliaSyntax.children(node)
# The new node parts # The new node parts
head′ = JuliaSyntax.head(node) head′ = JuliaSyntax.head(node)
children′ = () children′ = children # This aliases until the need to copy below
# Keep track of changes; if no child changes the original node can be returned # Keep track of changes; if no child changes the original node can be returned
any_child_changed = false any_child_changed = false
for (i, child) in pairs(children) for (i, child) in pairs(children)
# Set the siblings: previous from children′, next from children
ctx.prev_sibling = get(children′, i - 1, nothing)
ctx.next_sibling = get(children, i + 1, nothing)
child′ = child child′ = child
span_sum += JuliaSyntax.span(child) span_sum += JuliaSyntax.span(child)
this_child_changed = false this_child_changed = false
@ -86,6 +107,8 @@ function format_node_with_children!(ctx::Context, node::JuliaSyntax.GreenNode)
child′′ = format_node!(ctx, child′) child′′ = format_node!(ctx, child′)
if child′′ === nullnode if child′′ === nullnode
this_child_changed = true this_child_changed = true
# TODO: When this is fixed the sibling setting above needs to handle this
# too
error("TODO: handle removed children") error("TODO: handle removed children")
elseif child′′ === child′ elseif child′′ === child′
child′ = child′′ child′ = child′′
@ -113,13 +136,17 @@ function format_node_with_children!(ctx::Context, node::JuliaSyntax.GreenNode)
end end
any_child_changed |= this_child_changed any_child_changed |= this_child_changed
if any_child_changed if any_child_changed
# Promote children from tuple to array and copy older siblings into it # De-alias the children if needed
if children′ === () if children′ === children
children′ = eltype(children)[children[j] for j in 1:(i-1)] children′ = eltype(children)[children[j] for j in 1:(i-1)]
end end
push!(children′, child′) push!(children′, child′)
end end
end end
# Reset the siblings
ctx.prev_sibling = prev_sibling
ctx.next_sibling = next_sibling
# Return a new node if any of the children changed
if any_child_changed if any_child_changed
span′ = mapreduce(JuliaSyntax.span, +, children′; init=0) span′ = mapreduce(JuliaSyntax.span, +, children′; init=0)
return JuliaSyntax.GreenNode(head′, span′, children′) return JuliaSyntax.GreenNode(head′, span′, children′)
@ -137,6 +164,12 @@ function format_node!(ctx::Context, node::JuliaSyntax.GreenNode)
@assert !JuliaSyntax.haschildren(node) @assert !JuliaSyntax.haschildren(node)
str = String(node_bytes(ctx, node)) str = String(node_bytes(ctx, node))
str′ = replace(str, r"\h*(\r\n|\r|\n)" => '\n') str′ = replace(str, r"\h*(\r\n|\r|\n)" => '\n')
# If the next sibling is also a NewlineWs we can trim trailing
# whitespace from this node too
next_kind = next_sibling_kind(ctx)
if next_kind === K"NewlineWs"
str′ = replace(str′, r"(\r\n|\r|\n)\h*" => '\n')
end
if str != str′ if str != str′
# Write new bytes and reset the stream # Write new bytes and reset the stream
nb = write_and_reset(ctx, str′) nb = write_and_reset(ctx, str′)
@ -393,7 +426,7 @@ function format_tree!(ctx::Context)
# Reset IOs so that the offsets are correct # Reset IOs so that the offsets are correct
seek(ctx.src_io, src_pos) seek(ctx.src_io, src_pos)
seek(ctx.fmt_io, fmt_pos) seek(ctx.fmt_io, fmt_pos)
# Keep track of the depth to break out of infinite loops # Set the root to the current node
root′ = root root′ = root
itr = 0 itr = 0
while true while true

2
test/runtests.jl

@ -10,7 +10,7 @@ using Test:
println(io, " ") # Trailing space on consecutive lines println(io, " ") # Trailing space on consecutive lines
println(io, " ") println(io, " ")
str = String(take!(io)) str = String(take!(io))
@test_broken format_string(str) == "a = 1\nb = 2\n\n\n" @test format_string(str) == "a = 1\nb = 2\n\n\n"
end end
@testset "Hex/oct/bin literal integers" begin @testset "Hex/oct/bin literal integers" begin

Loading…
Cancel
Save