diff options
| -rw-r--r-- | CHANGELOG.adoc | 1 | ||||
| -rw-r--r-- | lib/asciidoctor/abstract_block.rb | 26 | ||||
| -rw-r--r-- | test/api_test.rb | 50 |
3 files changed, 72 insertions, 5 deletions
diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 44fd3184..01d26075 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -31,6 +31,7 @@ Enhancements:: * add more conventional styles to quote block when it has the excerpt role (#2092) * colspecs can be separated by semi-colon instead of comma (#2798) * change AbstractBlock#find_by to respond to StopIteration exception; stop traversal after matching ID (#2900) + * change AbstractBlock#find_by to honor return values :skip and :skip_children from filter block to skip node and its descendants or just its descendants, respectively (#2067) Fixes:: diff --git a/lib/asciidoctor/abstract_block.rb b/lib/asciidoctor/abstract_block.rb index c3423ac7..2c47814c 100644 --- a/lib/asciidoctor/abstract_block.rb +++ b/lib/asciidoctor/abstract_block.rb @@ -136,10 +136,16 @@ class AbstractBlock < AbstractNode @next_section_index > 0 end - # Public: Query for all descendant block-level nodes in the document tree - # that match the specified selector (context, style, id, and/or role). If a - # Ruby block is given, it's used as an additional filter. If no selector or - # Ruby block is supplied, all block-level nodes in the tree are returned. + # Public: Walk the document tree and find all block-level nodes that match + # the specified selector (context, style, id, role, and/or custom filter). + # + # If a Ruby block is given, it's treated as an supplemental filter. If the + # filter returns true, the node is accepted and traversal continues. If the + # filter returns false, the node is rejected, but traversal continues. If the + # filter returns :skip, the node and all its descendants are rejected. If the + # filter returns :skip_children, the node is accepted, but its descendants + # are rejected. If no selector or filter block is supplied, all block-level + # nodes in the tree are returned. # # Examples # @@ -174,7 +180,17 @@ class AbstractBlock < AbstractNode result.replace block_given? ? ((yield self) ? [self] : []) : [self] raise ::StopIteration elsif block_given? - result << self if (yield self) + if (verdict = yield self) + case verdict + when :skip_children + result << self + return result + when :skip + return result + else + result << self + end + end else result << self end diff --git a/test/api_test.rb b/test/api_test.rb index c411a8fd..4daa3b20 100644 --- a/test/api_test.rb +++ b/test/api_test.rb @@ -576,6 +576,56 @@ content assert_equal 'Section', result[0].title end + test 'find_by should skip node and its children if block returns :skip' do + input = <<-EOS +paragraph 1 + +==== +paragraph 2 + +term:: ++ +paragraph 3 +==== + +paragraph 4 + EOS + doc = Asciidoctor.load input + result = doc.find_by do |candidate| + ctx = candidate.context + if ctx == :example + :skip + elsif ctx == :paragraph + true + end + end + refute_nil result + assert_equal 2, result.size + assert_equal :paragraph, result[0].context + assert_equal :paragraph, result[1].context + end + + test 'find_by should accept node but skip its children if block returns :skip_children' do + input = <<-EOS +==== +paragraph 2 + +term:: ++ +paragraph 3 +==== + EOS + doc = Asciidoctor.load input + result = doc.find_by do |candidate| + if candidate.context == :example + :skip_children + end + end + refute_nil result + assert_equal 1, result.size + assert_equal :example, result[0].context + end + test 'find_by should stop looking for blocks when StopIteration is raised' do input = <<-EOS paragraph 1 |
