Browse Source

Indent nested modules, fixes #5.

pull/19/head
Fredrik Ekre 2 years ago
parent
commit
d260c13108
No known key found for this signature in database
GPG Key ID: DE82E6D5E364C0A2
  1. 10
      src/Runic.jl
  2. 67
      src/runestone.jl
  3. 25
      test/runtests.jl

10
src/Runic.jl

@ -123,6 +123,7 @@ mutable struct Context
prev_sibling::Union{Node, Nothing} prev_sibling::Union{Node, Nothing}
next_sibling::Union{Node, Nothing} next_sibling::Union{Node, Nothing}
# parent::Union{Node, Nothing} # parent::Union{Node, Nothing}
lineage_kinds::Vector{JuliaSyntax.Kind}
end end
function Context( function Context(
@ -151,9 +152,10 @@ function Context(
indent_level = 0 indent_level = 0
call_depth = 0 call_depth = 0
prev_sibling = next_sibling = nothing prev_sibling = next_sibling = nothing
lineage_kinds = JuliaSyntax.Kind[]
return Context( return Context(
src_str, src_tree, src_io, fmt_io, fmt_tree, quiet, verbose, assert, debug, check, src_str, src_tree, src_io, fmt_io, fmt_tree, quiet, verbose, assert, debug, check,
diff, call_depth, indent_level, prev_sibling, next_sibling, diff, call_depth, indent_level, prev_sibling, next_sibling, lineage_kinds,
) )
end end
@ -200,6 +202,7 @@ function format_node_with_kids!(ctx::Context, node::Node)
next_sibling = ctx.next_sibling next_sibling = ctx.next_sibling
ctx.prev_sibling = nothing ctx.prev_sibling = nothing
ctx.next_sibling = nothing ctx.next_sibling = nothing
push!(ctx.lineage_kinds, kind(node))
# The new node parts. `kids′` aliases `kids` and only copied below if any of the # The new node parts. `kids′` aliases `kids` and only copied below if any of the
# nodes change ("copy-on-write"). # nodes change ("copy-on-write").
@ -255,6 +258,7 @@ function format_node_with_kids!(ctx::Context, node::Node)
# Reset the siblings # Reset the siblings
ctx.prev_sibling = prev_sibling ctx.prev_sibling = prev_sibling
ctx.next_sibling = next_sibling ctx.next_sibling = next_sibling
pop!(ctx.lineage_kinds)
ctx.call_depth -= 1 ctx.call_depth -= 1
# Return a new node if any of the kids changed # Return a new node if any of the kids changed
if any_kid_changed if any_kid_changed
@ -501,8 +505,8 @@ function format_tree!(ctx::Context)
root′ = root′′ root′ = root′′
end end
# The root node must only change once. # The root node must only change once.
if (itr += 1) == 2 if (itr += 1) > 100
error("root node modified more than once") error("root node modified more than 100 times?")
end end
end end
# Truncate the output at the root span # Truncate the output at the root span

67
src/runestone.jl

@ -1152,6 +1152,69 @@ function indent_comparison(ctx::Context, node::Node)
return continue_all_newlines(ctx, node) return continue_all_newlines(ctx, node)
end end
# Indent a nested module
function indent_module(ctx::Context, node::Node)
kids = verified_kids(node)
any_kid_changed = false
# First node is the module keyword
mod_idx = 1
mod_node = kids[mod_idx]
@assert is_leaf(mod_node) && kind(mod_node) in KSet"module baremodule"
if !has_tag(mod_node, TAG_INDENT)
kids[mod_idx] = add_tag(mod_node, TAG_INDENT)
any_kid_changed = true
end
# Second node is the space between keyword and name
# TODO: Make sure there is just a single space
space_idx = 2
space_node = kids[space_idx]
@assert is_leaf(space_node) && kind(space_node) === K"Whitespace"
# Third node is the module identifier
id_idx = 3
id_node = kids[id_idx]
@assert kind(id_node) === K"Identifier"
# Fourth node is the module body block.
block_idx = 4
block_node′ = indent_block(ctx, kids[block_idx])
if block_node′ !== nothing
kids[block_idx] = block_node′
any_kid_changed = true
end
# Fifth node is the closing end keyword
end_idx = 5
end_node = kids[end_idx]
@assert is_leaf(end_node) && kind(end_node) === K"end"
if !has_tag(end_node, TAG_DEDENT)
kids[end_idx] = add_tag(end_node, TAG_DEDENT)
any_kid_changed = true
end
@assert verified_kids(node) === kids
return any_kid_changed ? node : nothing
end
# The only thing at top level that we need to indent are modules which don't occupy the full
# top level expression, for example a file with an inner module followed by some code.
function indent_toplevel(ctx::Context, node::Node)
@assert kind(node) === K"toplevel"
kids = verified_kids(node)
mod_idx = findfirst(x -> kind(x) === K"module", kids)
if mod_idx === nothing || count(!JuliaSyntax.is_whitespace, kids) == 1
# No module or module that is the only top level expression
return nothing
end
any_kid_changed = false
while mod_idx !== nothing
mod_node = kids[mod_idx]
mod_node′ = indent_module(ctx, mod_node)
if mod_node′ !== nothing
kids[mod_idx] = mod_node′
any_kid_changed = true
end
mod_idx = findnext(x -> kind(x) === K"module", kids, mod_idx + 1)
end
return any_kid_changed ? node : nothing
end
function insert_delete_mark_newlines(ctx::Context, node::Node) function insert_delete_mark_newlines(ctx::Context, node::Node)
if is_leaf(node) if is_leaf(node)
return nothing return nothing
@ -1201,6 +1264,10 @@ function insert_delete_mark_newlines(ctx::Context, node::Node)
return indent_array_row(ctx, node) return indent_array_row(ctx, node)
elseif kind(node) === K"comparison" elseif kind(node) === K"comparison"
return indent_comparison(ctx, node) return indent_comparison(ctx, node)
elseif kind(node) === K"toplevel"
return indent_toplevel(ctx, node)
elseif kind(node) === K"module" && findlast(x -> x === K"module", ctx.lineage_kinds) !== nothing
return indent_module(ctx, node)
end end
return nothing return nothing
end end

25
test/runtests.jl

@ -388,6 +388,31 @@ end
# do-end # do-end
@test format_string("open() do\n$(sp)a\n$(sp)end") == "open() do\n a\nend" @test format_string("open() do\n$(sp)a\n$(sp)end") == "open() do\n a\nend"
@test format_string("open() do io\n$(sp)a\n$(sp)end") == "open() do io\n a\nend" @test format_string("open() do io\n$(sp)a\n$(sp)end") == "open() do io\n a\nend"
# module-end, baremodule-end
for b in ("", "bare")
# Just a module
@test format_string("$(b)module A\n$(sp)x\n$(sp)end") == "$(b)module A\nx\nend"
# Comment before
@test format_string("# c\n$(b)module A\n$(sp)x\n$(sp)end") ==
"# c\n$(b)module A\nx\nend"
# Docstring before
@test format_string("\"doc\"\n$(b)module A\n$(sp)x\n$(sp)end") ==
"\"doc\"\n$(b)module A\nx\nend"
# code before
@test format_string("f\n$(b)module A\n$(sp)x\n$(sp)end") ==
"f\n$(b)module A\n x\nend"
@test format_string("f\n$(b)module A\n$(sp)x\n$(sp)end\n$(b)module B\n$(sp)x\n$(sp)end") ==
"f\n$(b)module A\n x\nend\n$(b)module B\n x\nend"
# code after
@test format_string("$(b)module A\n$(sp)x\n$(sp)end\nf") ==
"$(b)module A\n x\nend\nf"
# nested modules
@test format_string("$(b)module A\n$(sp)$(b)module B\n$(sp)x\n$(sp)end\n$(sp)end") ==
"$(b)module A\n$(b)module B\n x\nend\nend"
# nested documented modules
@test format_string("\"doc\"\n$(b)module A\n\"doc\"\n$(b)module B\n$(sp)x\n$(sp)end\n$(sp)end") ==
"\"doc\"\n$(b)module A\n\"doc\"\n$(b)module B\n x\nend\nend"
end
end end
end end

Loading…
Cancel
Save