From a48f07cfad3cab5c1e535729e3d1e804b14510f1 Mon Sep 17 00:00:00 2001 From: Abel Salgado Romero Date: Sun, 9 Oct 2022 10:30:02 +0200 Subject: [PATCH] Expose Section's sectnum property Update changelog --- CHANGELOG.adoc | 1 + .../java/org/asciidoctor/ast/Section.java | 20 ++++ .../jruby/ast/impl/SectionImpl.java | 10 ++ .../jruby/ast/impl/SectionImplTest.java | 100 ++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 asciidoctorj-core/src/test/java/org/asciidoctor/jruby/ast/impl/SectionImplTest.java diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index f650a0fe5..ad735f5b3 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -22,6 +22,7 @@ Improvement:: * Upgrade to JRuby 9.3.8.0 (#1116) * Upgrade to tilt 2.0.11 (#1109) * Upgrade to asciimath 2.0.4 (#1109) +* Expose `sectnum` property in Section interface (#1121) Bug Fixes:: diff --git a/asciidoctorj-api/src/main/java/org/asciidoctor/ast/Section.java b/asciidoctorj-api/src/main/java/org/asciidoctor/ast/Section.java index 6a34902b8..ee76e5708 100644 --- a/asciidoctorj-api/src/main/java/org/asciidoctor/ast/Section.java +++ b/asciidoctorj-api/src/main/java/org/asciidoctor/ast/Section.java @@ -74,4 +74,24 @@ public interface Section extends StructuralNode { */ boolean isNumbered(); + + /** + * Get the section number for the current Section. + *

+ * The section number is a dot-separated String that uniquely describes the position of this + * Section in the document. Each entry represents a level of nesting. The value of each entry is + * the 1-based outline number of the Section amongst its numbered sibling Sections. + */ + String getSectnum(); + + /** + * Get the section number for the current Section. + *

+ * The section number is a dot-separated String that uniquely describes the position of this + * Section in the document. Each entry represents a level of nesting. The value of each entry is + * the 1-based outline number of the Section amongst its numbered sibling Sections. + * + * @param delimiter the delimiter to separate the number for each level + */ + String getSectnum(String delimiter); } diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/SectionImpl.java b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/SectionImpl.java index e047f9819..3e7982400 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/SectionImpl.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/ast/impl/SectionImpl.java @@ -70,4 +70,14 @@ public boolean isNumbered() { return getBoolean("numbered"); } + @Override + public String getSectnum() { + return getString("sectnum"); + } + + @Override + public String getSectnum(String delimiter) { + return getString("sectnum", new Object[]{delimiter}); + } + } diff --git a/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/ast/impl/SectionImplTest.java b/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/ast/impl/SectionImplTest.java new file mode 100644 index 000000000..156d34a59 --- /dev/null +++ b/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/ast/impl/SectionImplTest.java @@ -0,0 +1,100 @@ +package org.asciidoctor.jruby.ast.impl; + +import org.asciidoctor.Asciidoctor; +import org.asciidoctor.Attributes; +import org.asciidoctor.Options; +import org.asciidoctor.ast.Document; +import org.asciidoctor.ast.Section; +import org.junit.Test; + +import java.util.Collections; + +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class SectionImplTest { + + private Asciidoctor asciidoctor = Asciidoctor.Factory.create(); + + + @Test + public void should_return_valid_sectnum_when_sectionNumbers_are_enabled() { + final String source = sectionsSample(); + + Document document = loadDocument(source, true); + + Section node1 = findSectionNode(document, 1); + assertThat(node1.getSectnum(), is("1.")); + assertThat(node1.isNumbered(), is(TRUE)); + + Section node2 = findSectionNode(document, 2); + assertThat(node2.getSectnum(), is("1.1.")); + assertThat(node2.isNumbered(), is(TRUE)); + } + + @Test + public void should_return_invalid_sectnum_when_sectionNumbers_are_not_enabled() { + final String source = sectionsSample(); + + Document document = loadDocument(source, false); + + Section node1 = findSectionNode(document, 1); + assertThat(node1.getSectnum(), is(".")); + assertThat(node1.isNumbered(), is(FALSE)); + + Section node2 = findSectionNode(document, 2); + assertThat(node2.getSectnum(), is("..")); + assertThat(node2.isNumbered(), is(FALSE)); + } + + @Test + public void should_return_sectnum_with_custom_delimiter_when_sectionNumbers_are_enabled() { + final String source = sectionsSample(); + + Document document = loadDocument(source, true); + + Section node1 = findSectionNode(document, 1); + assertThat(node1.getSectnum("_"), is("1_")); + + Section node2 = findSectionNode(document, 2); + assertThat(node2.getSectnum("*"), is("1*1*")); + } + + @Test + public void should_return_sectnum_with_custom_delimiter_when_sectionNumbers_are_not_enabled() { + final String source = sectionsSample(); + + Document document = loadDocument(source, false); + + Section node1 = findSectionNode(document, 1); + assertThat(node1.getSectnum("_"), is("_")); + + Section node2 = findSectionNode(document, 2); + assertThat(node2.getSectnum("*"), is("**")); + } + + private Document loadDocument(String source, boolean sectionNumbers) { + Attributes attributes = Attributes.builder().sectionNumbers(sectionNumbers).build(); + Options options = Options.builder().attributes(attributes).build(); + Document document = asciidoctor.load(source, options); + return document; + } + + private Section findSectionNode(Document document, int level) { + return (Section) document.findBy(Collections.singletonMap("context", ":section")) + .stream() + .filter(n -> n.getLevel() == level) + .findFirst() + .get(); + } + + static String sectionsSample() { + return "= Document Title\n\n" + + "== Section A\n\n" + + "Section A paragraph.\n\n" + + "=== Section A Subsection\n\n" + + "Section A 'subsection' paragraph.\n\n"; + } +}