Skip to content

Commit

Permalink
Show leaf Kinds when printing syntax trees
Browse files Browse the repository at this point in the history
The leaf kind is fairly critical information - this change ensures it's
shown by default when printing syntax trees.

Conversely, the byte offset and line/column is generally not very
useful, so hide this by default.
  • Loading branch information
c42f committed Aug 9, 2024
1 parent b92fc5e commit 06dcba9
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 44 deletions.
47 changes: 28 additions & 19 deletions src/syntax_tree.jl
Original file line number Diff line number Diff line change
Expand Up @@ -139,61 +139,70 @@ function leaf_string(ex)
end

function _show_syntax_node(io, current_filename, node::AbstractSyntaxNode,
indent, show_byte_offsets)
fname = filename(node)
indent, show_location, show_kind)
line, col = source_location(node)
posstr = "$(lpad(line, 4)):$(rpad(col,3))"
if show_byte_offsets
posstr *= "$(lpad(first_byte(node),6)):$(rpad(last_byte(node),6))"
if show_location
fname = filename(node)
# Add filename if it's changed from the previous node
if fname != current_filename[]
println(io, indent, " -file- │ ", repr(fname))
current_filename[] = fname
end
posstr = "$(lpad(line, 4)):$(rpad(col,3))$(lpad(first_byte(node),6)):$(rpad(last_byte(node),6))"
else
posstr = ""
end
val = node.val
nodestr = is_leaf(node) ? leaf_string(node) : "[$(untokenize(head(node)))]"
treestr = string(indent, nodestr)
# Add filename if it's changed from the previous node
if fname != current_filename[]
#println(io, "# ", fname)
treestr = string(rpad(treestr, 40), "$fname")
current_filename[] = fname
if show_kind && is_leaf(node)
treestr = rpad(treestr, 40)*" :: "*string(kind(node))
end
println(io, posstr, treestr)
if !is_leaf(node)
new_indent = indent*" "
for n in children(node)
_show_syntax_node(io, current_filename, n, new_indent, show_byte_offsets)
_show_syntax_node(io, current_filename, n, new_indent, show_location, show_kind)
end
end
end

function _show_syntax_node_sexpr(io, node::AbstractSyntaxNode)
function _show_syntax_node_sexpr(io, node::AbstractSyntaxNode, show_kind)
if is_leaf(node)
if is_error(node)
print(io, "(", untokenize(head(node)), ")")
else
print(io, leaf_string(node))
if show_kind
print(io, "::", kind(node))
end
end
else
print(io, "(", untokenize(head(node)))
first = true
for n in children(node)
print(io, ' ')
_show_syntax_node_sexpr(io, n)
_show_syntax_node_sexpr(io, n, show_kind)
first = false
end
print(io, ')')
end
end

function Base.show(io::IO, ::MIME"text/plain", node::AbstractSyntaxNode; show_byte_offsets=false)
println(io, "line:col│$(show_byte_offsets ? " byte_range │" : "") tree │ file_name")
_show_syntax_node(io, Ref(""), node, "", show_byte_offsets)
function Base.show(io::IO, ::MIME"text/plain", node::AbstractSyntaxNode; show_location=false, show_kind=true)
println(io, "SyntaxNode:")
if show_location
println(io, "line:col│ byte_range │ tree")
end
_show_syntax_node(io, Ref(""), node, "", show_location, show_kind)
end

function Base.show(io::IO, ::MIME"text/x.sexpression", node::AbstractSyntaxNode)
_show_syntax_node_sexpr(io, node)
function Base.show(io::IO, ::MIME"text/x.sexpression", node::AbstractSyntaxNode; show_kind=false)
_show_syntax_node_sexpr(io, node, show_kind)
end

function Base.show(io::IO, node::AbstractSyntaxNode)
_show_syntax_node_sexpr(io, node)
_show_syntax_node_sexpr(io, node, false)
end

function Base.push!(node::SN, child::SN) where SN<:AbstractSyntaxNode
Expand Down
64 changes: 39 additions & 25 deletions test/syntax_tree.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,9 @@
# as `lastindex(t, 2)` isn't well defined

@test sprint(show, t) == "(call-i (call-i a * b) + c)"
str = sprint(show, MIME("text/plain"), t)
# These tests are deliberately quite relaxed to avoid being too specific about display style
@test occursin("line:col", str)
@test occursin("call-i", str)
@test sprint(io->show(io, MIME("text/x.sexpression"), t, show_kind=true)) ==
"(call-i (call-i a::Identifier *::* b::Identifier) +::+ c::Identifier)"

@test sprint(highlight, child(t, 1, 3)) == "a*b + c\n# ╙"
@test sprint(highlight, t.source, t.raw, 1, 3) == "a*b + c\n# ╙"

Expand Down Expand Up @@ -69,30 +68,45 @@ end
@testset "SyntaxNode pretty printing" begin
t = parsestmt(SyntaxNode, "f(a*b,\n c)", filename="foo.jl")
@test sprint(show, MIME("text/plain"), t) == """
line:col│ tree │ file_name
1:1 │[call] │foo.jl
1:1 │ f
1:3 │ [call-i]
1:3 │ a
1:4 │ *
1:5 │ b
2:3 │ c
SyntaxNode:
[call]
f :: Identifier
[call-i]
a :: Identifier
* :: *
b :: Identifier
c :: Identifier
"""
@test sprint(io->show(io, MIME("text/plain"), t, show_byte_offsets=true)) == """
line:col│ byte_range │ tree │ file_name
1:1 │ 1:11 │[call] │foo.jl
1:1 │ 1:1 │ f

@test sprint(io->show(io, MIME("text/plain"), t, show_location=true)) == """
SyntaxNode:
line:col│ byte_range │ tree
-file- │ "foo.jl"
1:1 │ 1:11 │[call]
1:1 │ 1:1 │ f :: Identifier
1:3 │ 3:5 │ [call-i]
1:3 │ 3:3 │ a
1:4 │ 4:4 │ *
1:5 │ 5:5 │ b
2:3 │ 10:10 │ c
1:3 │ 3:3 │ a :: Identifier
1:4 │ 4:4 │ * :: *
1:5 │ 5:5 │ b :: Identifier
2:3 │ 10:10 │ c :: Identifier
"""

t,_ = parsestmt(SyntaxNode, "begin a end\nbegin b end", 13)
@test sprint(show, MIME("text/plain"), t) == """
line:col│ tree │ file_name
1:1 │[block]
1:7 │ b
@test sprint(io->show(io, MIME("text/plain"), t, show_kind=false)) == """
SyntaxNode:
[call]
f
[call-i]
a
*
b
c
"""

t,_ = parsestmt(SyntaxNode, "begin a end\nbegin b end", 13, first_line=100)
@test sprint(io->show(io, MIME("text/plain"), t, show_location=true)) == """
SyntaxNode:
line:col│ byte_range │ tree
100:1 │ 13:23 │[block]
100:7 │ 19:19 │ b :: Identifier
"""
end

0 comments on commit 06dcba9

Please sign in to comment.