Skip to content

Commit 286663f

Browse files
committed
Add append! and prepend! methods for NodeChildren
1 parent fb7a21c commit 286663f

File tree

3 files changed

+65
-3
lines changed

3 files changed

+65
-3
lines changed

docs/src/node.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ insert_before!
5353
insert_after!
5454
Base.push!(::NodeChildren{T}, ::T) where {T <: Node}
5555
Base.pushfirst!(::NodeChildren{T}, ::T) where {T <: Node}
56+
Base.append!(::NodeChildren{T}, ::Any) where T
57+
Base.prepend!(::NodeChildren{T}, ::Any) where T
5658
```
5759

5860
!!! note "Mutating the .children property"

src/node.jl

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,10 @@ should only rely on the following documented APIs:
6060
[`last`](@ref Base.last(::NodeChildren)),
6161
[`isempty`](@ref Base.isempty(::NodeChildren))
6262
- Appending or prepending new children to a parent node can be done with the
63-
[`push!`](@ref Base.push!(children::NodeChildren{T}, child::T) where {T <: Node}) and
64-
[`pushfirst!`](@ref Base.pushfirst!(children::NodeChildren{T}, child::T) where {T <: Node})
63+
[`push!`](@ref Base.push!(children::NodeChildren{T}, child::T) where {T <: Node}),
64+
[`pushfirst!`](@ref Base.pushfirst!(children::NodeChildren{T}, child::T) where {T <: Node}),
65+
[`append!`](@ref Base.append!(::NodeChildren{T}, ::Any) where T),
66+
and [`prepend!`](@ref Base.prepend!(::NodeChildren{T}, ::Any) where T)
6567
methods
6668
6769
Other ways to work with child nodes that do not directly reference `.children` are:
@@ -401,6 +403,50 @@ function insert_before!(node::T, sibling::T) where {T <: Node}
401403
return node
402404
end
403405

406+
"""
407+
append!(node.children::NodeChildren, children) -> NodeChildren
408+
409+
Adds all the elements of the iterable `children` to the end of the list of children of `node`.
410+
If any of `children` are part of another tree, then they are unlinked from that tree first
411+
(see [`unlink!`](@ref)). Returns the iterator over children.
412+
413+
!!! warning "Error during an append"
414+
415+
The operation is not atomic, and an error during an `append!` (e.g. due to an element of
416+
the wrong type in `children`) can result in a partial append of the new children, similar
417+
to how `append!` behaves with arrays
418+
(see [JuliaLang/julia#15868](https://github.com/JuliaLang/julia/issues/15868)).
419+
"""
420+
function Base.append!(nodechildren::NodeChildren{T}, children) where T
421+
for child in children
422+
isa(child, T) || throw(ArgumentError("invalid element type ($(typeof(child))) in children, expected $T"))
423+
push!(nodechildren, child)
424+
end
425+
return nodechildren
426+
end
427+
428+
"""
429+
prepend!(node.children::NodeChildren, children) -> NodeChildren
430+
431+
Adds all the elements of the iterable `children` to the beginning of the list of children of `node`.
432+
If any of `children` are part of another tree, then they are unlinked from that tree first
433+
(see [`unlink!`](@ref)). Returns the iterator over children.
434+
435+
!!! warning "Error during a prepend"
436+
437+
The operation is not atomic, and an error during a `prepend!` (e.g. due to an element of
438+
the wrong type in `children`) can result in a partial prepend of the new children, similar
439+
to how `append!` behaves with arrays
440+
(see [JuliaLang/julia#15868](https://github.com/JuliaLang/julia/issues/15868)).
441+
"""
442+
function Base.prepend!(nodechildren::NodeChildren{T}, children) where T
443+
for child in Iterators.reverse(children)
444+
isa(child, T) || throw(ArgumentError("invalid element type ($(typeof(child))) in children, expected $T"))
445+
pushfirst!(nodechildren, child)
446+
end
447+
return nodechildren
448+
end
449+
404450
# Check if this is a root node. Next and previous should also be nothing, but this is not
405451
# enforced. This function is currently not part of the public API.
406452
isrootnode(node::Node) = isnothing(node.parent)

test/node.jl

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using MarkdownAST: MarkdownAST,
22
Node, haschildren, @ast, copy_tree,
3-
Document, Paragraph, Strong, Link, BlockQuote, CodeBlock, HTMLBlock,
3+
Document, Paragraph, Strong, Link, BlockQuote, CodeBlock, HTMLBlock, Heading, Code, DisplayMath,
44
NodeChildren
55
using Test
66

@@ -193,4 +193,18 @@ _startswith(prefix) = s -> startswith(s, prefix)
193193
end
194194
end
195195
"""
196+
197+
# append! and prepend!
198+
n = @ast Document() do
199+
DisplayMath("")
200+
DisplayMath("")
201+
end
202+
cs = Node.([Paragraph(), CodeBlock("", ""), BlockQuote()])
203+
@test append!(n.children, cs) isa NodeChildren
204+
@test typeof.(getproperty.(n.children, :element)) == [DisplayMath, DisplayMath, Paragraph, CodeBlock, BlockQuote]
205+
@test prepend!(n.children, cs) isa NodeChildren
206+
@test typeof.(getproperty.(n.children, :element)) == [Paragraph, CodeBlock, BlockQuote, DisplayMath, DisplayMath]
207+
# test, but with a generic iterator
208+
@test append!(n.children, c for c in cs) isa NodeChildren
209+
@test typeof.(getproperty.(n.children, :element)) == [DisplayMath, DisplayMath, Paragraph, CodeBlock, BlockQuote]
196210
end

0 commit comments

Comments
 (0)