diff options
| author | Dan Allen <dan.j.allen@gmail.com> | 2018-04-15 19:48:22 -0600 |
|---|---|---|
| committer | Dan Allen <dan.j.allen@gmail.com> | 2018-04-18 22:49:20 -0600 |
| commit | 00acebbb4a2ff30516ae71a063d563d6c86ccf4f (patch) | |
| tree | 754d4fc46ceca7d1547f1f5e1b335c0911a766d2 | |
| parent | 26af4f9a848303686bf0306f540a73577e997ba3 (diff) | |
resolves #2433 warn if nested sections are used where not allowed
- only allow nested sections in appendix, preface, and abstract sections
- add tests to verify warning
- fix tests that use nested sections in sections that don't support them
| -rw-r--r-- | CHANGELOG.adoc | 1 | ||||
| -rw-r--r-- | lib/asciidoctor/parser.rb | 15 | ||||
| -rw-r--r-- | test/sections_test.rb | 179 |
3 files changed, 168 insertions, 27 deletions
diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 02934079..f9c5bb23 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -88,6 +88,7 @@ Bug fixes:: Improvements / Refactoring:: * log a warning message if an unterminated delimited block is detected (#1133, PR #2612) + * log a warning when nested section is found inside special section that doesn't support nested sections (#2433, PR #2672) * resolve / expand parent references in start path passed to PathResolver#system_path (#2642, PR #2644) * update PathResolver#expand_path to resolve parent references (#2642, PR #2644) * allow start path passed to PathResolver#system_path to be outside jail if target brings resolved path back inside jail (#2642, PR #2644) diff --git a/lib/asciidoctor/parser.rb b/lib/asciidoctor/parser.rb index cc037c0e..3ddd13e1 100644 --- a/lib/asciidoctor/parser.rb +++ b/lib/asciidoctor/parser.rb @@ -291,7 +291,7 @@ class Parser current_level = 0 if parent.attributes.key? 'fragment' - expected_next_level = nil + expected_next_level = -1 # small tweak to allow subsequent level-0 sections for book doctype elsif doctype == 'book' expected_next_level, expected_next_level_alt = 1, 0 @@ -304,7 +304,14 @@ class Parser # clear attributes except for title attribute, which must be carried over to next content block attributes = (title = attributes['title']) ? { 'title' => title } : {} expected_next_level = (current_level = section.level) + 1 - part = current_level == 0 && doctype == 'book' + if current_level == 0 + part = doctype == 'book' + elsif current_level == 1 && section.special + # NOTE preface and abstract sections are only permitted in the book doctype + unless (sectname = section.sectname) == 'appendix' || sectname == 'preface' || sectname == 'abstract' + expected_next_level = nil + end + end end reader.skip_blank_lines @@ -327,10 +334,12 @@ class Parser if next_level == 0 && doctype != 'book' logger.error message_with_context 'level 0 sections can only be used when doctype is book', :source_location => reader.cursor elsif expected_next_level - unless next_level == expected_next_level || (expected_next_level_alt && next_level == expected_next_level_alt) + unless next_level == expected_next_level || (expected_next_level_alt && next_level == expected_next_level_alt) || expected_next_level < 0 expected_condition = expected_next_level_alt ? %(expected levels #{expected_next_level_alt} or #{expected_next_level}) : %(expected level #{expected_next_level}) logger.warn message_with_context %(section title out of sequence: #{expected_condition}, got level #{next_level}), :source_location => reader.cursor end + else + logger.error message_with_context %(#{sectname} sections do not support nested sections), :source_location => reader.cursor end # the attributes returned are those that are orphaned new_section, attributes = next_section reader, section, attributes diff --git a/test/sections_test.rb b/test/sections_test.rb index 05120d69..180188d3 100644 --- a/test/sections_test.rb +++ b/test/sections_test.rb @@ -578,6 +578,123 @@ content end end + context 'Nesting' do + test 'should log error if subsections are found in special sections in article that do not support subsections' do + input = <<-EOS += Document Title + +== Section + +=== Subsection of Section + +allowed + +[appendix] +== Appendix + +=== Subsection of Appendix + +allowed + +[glossary] +== Glossary + +=== Subsection of Glossary + +not allowed + +[bibliography] +== Bibliography + +=== Subsection of Bibliography + +not allowed + EOS + + using_memory_logger do |logger| + render_embedded_string input + assert_messages logger, [ + [:ERROR, '<stdin>: line 19: glossary sections do not support nested sections', Hash], + [:ERROR, '<stdin>: line 26: bibliography sections do not support nested sections', Hash], + ] + end + end + + test 'should log error if subsections are found in special sections in book that do not support subsections' do + input = <<-EOS += Document Title +:doctype: book + +[preface] += Preface + +=== Subsection of Preface + +allowed + +[colophon] += Colophon + +=== Subsection of Colophon + +not allowed + +[dedication] += Dedication + +=== Subsection of Dedication + +not allowed + += Part 1 + +[abstract] +== Abstract + +=== Subsection of Abstract + +allowed + +== Chapter 1 + +=== Subsection of Chapter + +allowed + +[appendix] += Appendix + +=== Subsection of Appendix + +allowed + +[glossary] += Glossary + +=== Subsection of Glossary + +not allowed + +[bibliography] += Bibliography + +=== Subsection of Bibliography + +not allowed + EOS + + using_memory_logger do |logger| + render_embedded_string input + assert_messages logger, [ + [:ERROR, '<stdin>: line 14: colophon sections do not support nested sections', Hash], + [:ERROR, '<stdin>: line 21: dedication sections do not support nested sections', Hash], + [:ERROR, '<stdin>: line 50: glossary sections do not support nested sections', Hash], + [:ERROR, '<stdin>: line 57: bibliography sections do not support nested sections', Hash] + ] + end + end + end + context 'Markdown-style headings' do test 'atx document title with leading marker' do input = <<-EOS @@ -1707,17 +1824,20 @@ Terms test 'should not number special sections or their subsections by default except for appendices' do input = <<-EOS +:doctype: book :sectnums: -[dedication] -== Dedication +[preface] +== Preface -=== Dedication Subsection +=== Preface Subsection content == Section One +content + [appendix] == Attribute Options @@ -1739,8 +1859,8 @@ Terms EOS output = render_embedded_string input - assert_xpath '(//h2)[1][text()="Dedication"]', output, 1 - assert_xpath '(//h3)[1][text()="Dedication Subsection"]', output, 1 + assert_xpath '(//h2)[1][text()="Preface"]', output, 1 + assert_xpath '(//h3)[1][text()="Preface Subsection"]', output, 1 assert_xpath '(//h2)[2][text()="1. Section One"]', output, 1 assert_xpath '(//h2)[3][text()="Appendix A: Attribute Options"]', output, 1 assert_xpath '(//h2)[4][text()="Appendix B: Migration"]', output, 1 @@ -1750,18 +1870,21 @@ Terms test 'should not number special sections or their subsections in toc by default except for appendices' do input = <<-EOS +:doctype: book :sectnums: :toc: -[dedication] -== Dedication +[preface] +== Preface -=== Dedication Subsection +=== Preface Subsection content == Section One +content + [appendix] == Attribute Options @@ -1783,8 +1906,8 @@ Terms EOS output = render_string input - assert_xpath '//*[@id="toc"]/ul//li/a[text()="Dedication"]', output, 1 - assert_xpath '//*[@id="toc"]/ul//li/a[text()="Dedication Subsection"]', output, 1 + assert_xpath '//*[@id="toc"]/ul//li/a[text()="Preface"]', output, 1 + assert_xpath '//*[@id="toc"]/ul//li/a[text()="Preface Subsection"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="1. Section One"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="Appendix A: Attribute Options"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="Appendix B: Migration"]', output, 1 @@ -1794,17 +1917,20 @@ Terms test 'should number special sections and their subsections when sectnums is all' do input = <<-EOS +:doctype: book :sectnums: all -[dedication] -== Dedication +[preface] +== Preface -=== Dedication Subsection +=== Preface Subsection content == Section One +content + [appendix] == Attribute Options @@ -1826,29 +1952,33 @@ Terms EOS output = render_embedded_string input - assert_xpath '(//h2)[1][text()="1. Dedication"]', output, 1 - assert_xpath '(//h3)[1][text()="1.1. Dedication Subsection"]', output, 1 - assert_xpath '(//h2)[2][text()="2. Section One"]', output, 1 + # FIXME numbering is borked + assert_xpath '(//h2)[1][text()="1. Preface"]', output, 1 + assert_xpath '(//h3)[1][text()="1.1. Preface Subsection"]', output, 1 + assert_xpath '(//h2)[2][text()="1. Section One"]', output, 1 assert_xpath '(//h2)[3][text()="Appendix A: Attribute Options"]', output, 1 assert_xpath '(//h2)[4][text()="Appendix B: Migration"]', output, 1 assert_xpath '(//h3)[2][text()="B.1. Gotchas"]', output, 1 - assert_xpath '(//h2)[5][text()="3. Glossary"]', output, 1 + assert_xpath '(//h2)[5][text()="2. Glossary"]', output, 1 end test 'should number special sections and their subsections in toc when sectnums is all' do input = <<-EOS +:doctype: book :sectnums: all :toc: -[dedication] -== Dedication +[preface] +== Preface -=== Dedication Subsection +=== Preface Subsection content == Section One +contennt + [appendix] == Attribute Options @@ -1870,13 +2000,14 @@ Terms EOS output = render_string input - assert_xpath '//*[@id="toc"]/ul//li/a[text()="1. Dedication"]', output, 1 - assert_xpath '//*[@id="toc"]/ul//li/a[text()="1.1. Dedication Subsection"]', output, 1 - assert_xpath '//*[@id="toc"]/ul//li/a[text()="2. Section One"]', output, 1 + # FIXME numbering is borked + assert_xpath '//*[@id="toc"]/ul//li/a[text()="1. Preface"]', output, 1 + assert_xpath '//*[@id="toc"]/ul//li/a[text()="1.1. Preface Subsection"]', output, 1 + assert_xpath '//*[@id="toc"]/ul//li/a[text()="1. Section One"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="Appendix A: Attribute Options"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="Appendix B: Migration"]', output, 1 assert_xpath '//*[@id="toc"]/ul//li/a[text()="B.1. Gotchas"]', output, 1 - assert_xpath '//*[@id="toc"]/ul//li/a[text()="3. Glossary"]', output, 1 + assert_xpath '//*[@id="toc"]/ul//li/a[text()="2. Glossary"]', output, 1 end test 'level 0 special sections in multipart book should be rendered as level 1' do |
