diff options
| author | Dan Allen <dallen@redhat.com> | 2013-04-23 00:33:31 -0600 |
|---|---|---|
| committer | Dan Allen <dallen@redhat.com> | 2013-04-23 14:20:35 -0600 |
| commit | 3a0969cbff400a685a3ba3cadfbea0a2b67aa234 (patch) | |
| tree | 2926993a9e771b7e30138b9f5a3387085737a323 | |
| parent | 8d9241ea92fb8d70ff53b9565f31bbcf56a1348b (diff) | |
resolves #269 implement toc::[] macro
| -rw-r--r-- | README.asciidoc | 9 | ||||
| -rwxr-xr-x | lib/asciidoctor.rb | 4 | ||||
| -rw-r--r-- | lib/asciidoctor/backends/docbook45.rb | 10 | ||||
| -rw-r--r-- | lib/asciidoctor/backends/html5.rb | 34 | ||||
| -rw-r--r-- | lib/asciidoctor/document.rb | 13 | ||||
| -rw-r--r-- | lib/asciidoctor/lexer.rb | 7 | ||||
| -rw-r--r-- | test/document_test.rb | 4 | ||||
| -rw-r--r-- | test/sections_test.rb | 230 |
8 files changed, 291 insertions, 20 deletions
diff --git a/README.asciidoc b/README.asciidoc index 9c7c4df4..beb2e740 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -273,8 +273,13 @@ NOTE: In general, Asciidoctor handles whitespace much more intelligently * Asciidoctor reifies the toc in the header of the document instead of relying on JavaScript to create it -* Asciidoctor sets css class on toc element, read from the +toc-class+ - attribute. It defaults to toc attribute name (+toc+ or +toc2+). +* Asciidoctor sets CSS class on toc element, read from the +toc-class+ + attribute; defaults to toc attribute name (+toc+ or +toc2+). + +* Asciidoctor honors the id, title, role and levels attributes set on + the toc macro. + +* Asciidoctor does not output two tocs with the same id. * Asciidoctor is nice about using a section title syntax inside a delimited block by simply ignoring it (AsciiDoc issues warnings) diff --git a/lib/asciidoctor.rb b/lib/asciidoctor.rb index 6b00c41a..a8c825aa 100755 --- a/lib/asciidoctor.rb +++ b/lib/asciidoctor.rb @@ -487,6 +487,10 @@ module Asciidoctor # TODO build from SECTION_LEVELS keys :section_underline => /^(?:=|-|~|\^|\+)+$/, + # toc::[] + # toc::[levels=2] + :toc => /^toc::\[(.*?)\]$/, + # * Foo (up to 5 consecutive asterisks) # - Foo # REVIEW leading space has already been stripped, so may not need in regex diff --git a/lib/asciidoctor/backends/docbook45.rb b/lib/asciidoctor/backends/docbook45.rb index 0920a302..9c4999c1 100644 --- a/lib/asciidoctor/backends/docbook45.rb +++ b/lib/asciidoctor/backends/docbook45.rb @@ -122,6 +122,16 @@ class EmbeddedTemplate < BaseTemplate end end +class BlockTocTemplate < BaseTemplate + def result(node) + '' + end + + def template + :invoke_result + end +end + class BlockPreambleTemplate < BaseTemplate def template @template ||= @eruby.new <<-EOF diff --git a/lib/asciidoctor/backends/html5.rb b/lib/asciidoctor/backends/html5.rb index 51e34e8f..b1bcd1d1 100644 --- a/lib/asciidoctor/backends/html5.rb +++ b/lib/asciidoctor/backends/html5.rb @@ -101,7 +101,7 @@ class DocumentTemplate < BaseTemplate <% if attr? :revdate %><span id="revdate"><%= attr :revdate %></span><% end %> <% if attr? :revremark %><br><span id="revremark"><%= attr :revremark %></span><% end %> <% end %> - <% if attr? :toc %> + <% if (attr? :toc) && (attr? 'toc-placement', 'auto') %> <div id="toc" class="<%= attr 'toc-class', 'toc' %>"> <div id="toctitle"><%= attr 'toc-title' %></div> <%= template.class.outline(self, (attr :toclevels, 2).to_i) %> @@ -150,6 +150,38 @@ class EmbeddedTemplate < BaseTemplate end end +class BlockTocTemplate < BaseTemplate + # id must be unique if this macro is used > once or when built-in one is present + def result(node) + doc = node.document + + return '' unless doc.attr?('toc') + + if node.id + id_attr = %( id="#{node.id}") + title_id_attr = '' + elsif doc.embedded? || !doc.attr?('toc-placement', 'auto') + id_attr = ' id="toc"' + title_id_attr = ' id="toctitle"' + else + id_attr = '' + title_id_attr = '' + end + title = node.title? ? node.title : (doc.attr 'toc-title') + levels = node.attr?('levels') ? node.attr('levels').to_i : doc.attr('toclevels', 2).to_i + role = node.attr?('role') ? node.attr('role') : doc.attr('toc-class', 'toc') + + %(\n<div#{id_attr} class="#{role}"> +<div#{title_id_attr} class="title">#{title}</div> +#{DocumentTemplate.outline(doc, levels)} +</div>) + end + + def template + :invoke_result + end +end + class BlockPreambleTemplate < BaseTemplate def template @template ||= @eruby.new <<-EOS diff --git a/lib/asciidoctor/document.rb b/lib/asciidoctor/document.rb index 380b21d7..bbe6a02b 100644 --- a/lib/asciidoctor/document.rb +++ b/lib/asciidoctor/document.rb @@ -129,9 +129,11 @@ class Document < AbstractBlock @attributes['asciidoctor'] = '' @attributes['asciidoctor-version'] = VERSION - @attributes['sectids'] = '' @attributes['encoding'] = 'UTF-8' - @attributes['notitle'] = '' if !@options[:header_footer] + @attributes['sectids'] = '' + @attributes['notitle'] = '' unless @options[:header_footer] + @attributes['embedded'] = '' unless @options[:header_footer] + @attributes['toc-placement'] = 'auto' # language strings # TODO load these based on language settings @@ -143,9 +145,11 @@ class Document < AbstractBlock @attributes['appendix-caption'] = 'Appendix' @attributes['example-caption'] = 'Example' @attributes['figure-caption'] = 'Figure' + #@attributes['listing-caption'] = 'Listing' @attributes['table-caption'] = 'Table' @attributes['toc-title'] = 'Table of Contents' + # attribute overrides are attributes that can only be set from the commandline @attribute_overrides = options[:attributes] || {} # the only way to set the include-depth attribute is via the document options @@ -330,6 +334,11 @@ class Document < AbstractBlock !@parent_document.nil? end + def embedded? + # QUESTION should this be !@options[:header_footer] ? + @attributes.has_key? 'embedded' + end + # Make the raw source for the Document available. def source @reader.source.join if @reader diff --git a/lib/asciidoctor/lexer.rb b/lib/asciidoctor/lexer.rb index 86270282..19c81b3c 100644 --- a/lib/asciidoctor/lexer.rb +++ b/lib/asciidoctor/lexer.rb @@ -357,6 +357,13 @@ class Lexer # drop the line if target resolves to nothing return nil end + + # NOTE we're letting the toc macro have attributes + elsif (match = this_line.match(REGEXP[:toc])) + block = Block.new(parent, :toc) + block.parse_attributes(match[1], [], :sub_result => false, :into => attributes) + break + end end diff --git a/test/document_test.rb b/test/document_test.rb index afdc06ed..3372327b 100644 --- a/test/document_test.rb +++ b/test/document_test.rb @@ -296,7 +296,7 @@ preamble assert !renderer.nil? views = renderer.views assert !views.nil? - assert_equal 30, views.size + assert_equal 31, views.size assert views.has_key? 'document' assert views['document'].is_a?(Asciidoctor::HTML5::DocumentTemplate) assert_equal 'ERB', views['document'].eruby.to_s @@ -312,7 +312,7 @@ preamble assert !renderer.nil? views = renderer.views assert !views.nil? - assert_equal 30, views.size + assert_equal 31, views.size assert views.has_key? 'document' assert views['document'].is_a?(Asciidoctor::DocBook45::DocumentTemplate) assert_equal 'ERB', views['document'].eruby.to_s diff --git a/test/sections_test.rb b/test/sections_test.rb index 051f3535..a0380830 100644 --- a/test/sections_test.rb +++ b/test/sections_test.rb @@ -817,7 +817,7 @@ fin. end context 'Table of Contents' do - test 'should render table of contents if toc attribute is set' do + test 'should render table of contents in header if toc attribute is set' do input = <<-EOS = Article :toc: @@ -839,17 +839,17 @@ While they were waiting... That's all she wrote! EOS output = render_string input - assert_xpath '//*[@id="toc"][@class="toc"]', output, 1 - assert_xpath '//*[@id="toc"]/*[@id="toctitle"][text()="Table of Contents"]', output, 1 - assert_xpath '//*[@id="toc"]/ol', output, 1 - assert_xpath '//*[@id="toc"]//ol', output, 2 - assert_xpath '//*[@id="toc"]/ol/li', output, 4 - assert_xpath '//*[@id="toc"]/ol/li[1]/a[@href="#_section_one"][text()="1. Section One"]', output, 1 - assert_xpath '//*[@id="toc"]/ol/li/ol/li', output, 1 - assert_xpath '//*[@id="toc"]/ol/li/ol/li/a[@href="#_interlude"][text()="2.1. Interlude"]', output, 1 + assert_xpath '//*[@id="header"]//*[@id="toc"][@class="toc"]', output, 1 + assert_xpath '//*[@id="header"]//*[@id="toc"]/*[@id="toctitle"][text()="Table of Contents"]', output, 1 + assert_xpath '//*[@id="header"]//*[@id="toc"]/ol', output, 1 + assert_xpath '//*[@id="header"]//*[@id="toc"]//ol', output, 2 + assert_xpath '//*[@id="header"]//*[@id="toc"]/ol/li', output, 4 + assert_xpath '//*[@id="header"]//*[@id="toc"]/ol/li[1]/a[@href="#_section_one"][text()="1. Section One"]', output, 1 + assert_xpath '//*[@id="header"]//*[@id="toc"]/ol/li/ol/li', output, 1 + assert_xpath '//*[@id="header"]//*[@id="toc"]/ol/li/ol/li/a[@href="#_interlude"][text()="2.1. Interlude"]', output, 1 end - test 'should render table of contents if toc2 attribute is set' do + test 'should render table of contents in header if toc2 attribute is set' do input = <<-EOS = Article :toc2: @@ -864,8 +864,212 @@ They couldn't believe their eyes when... EOS output = render_string input - assert_xpath '//*[@id="toc"][@class="toc2"]', output, 1 - assert_xpath '//*[@id="toc"]/ol/li[1]/a[@href="#_section_one"][text()="1. Section One"]', output, 1 + assert_xpath '//*[@id="header"]//*[@id="toc"][@class="toc2"]', output, 1 + assert_xpath '//*[@id="header"]//*[@id="toc"]/ol/li[1]/a[@href="#_section_one"][text()="1. Section One"]', output, 1 + end + + test 'should use document attributes toc-class, toc-title and toclevels to create toc' do + input = <<-EOS += Article +:toc: +:toc-title: Contents +:toc-class: toc2 +:toclevels: 1 + +== Section 1 + +=== Section 1.1 + +==== Section 1.1.1 + +==== Section 1.1.2 + +=== Section 1.2 + +== Section 2 + +Fin. + EOS + output = render_string input + assert_css '#header #toc', output, 1 + assert_css '#header #toc.toc2', output, 1 + assert_css '#header #toc li', output, 2 + assert_css '#header #toc #toctitle', output, 1 + assert_xpath '//*[@id="header"]//*[@id="toc"]/*[@id="toctitle"][text()="Contents"]', output, 1 + end + + test 'should not render table of contents if toc-placement attribute is unset' do + input = <<-EOS += Article +:toc-placement!: + +== Section One + +It was a dark and stormy night... + +== Section Two + +They couldn't believe their eyes when... + EOS + + output = render_string input + assert_xpath '//*[@id="toc"]', output, 0 + end + + test 'should render table of contents at location of toc macro' do + input = <<-EOS += Article +:toc: +:toc-placement!: + +Once upon a time... + +toc::[] + +== Section One + +It was a dark and stormy night... + +== Section Two + +They couldn't believe their eyes when... + EOS + + output = render_string input + assert_css '#preamble #toc', output, 1 + assert_css '#preamble .paragraph + #toc', output, 1 + end + + test 'should render table of contents at location of toc macro in embedded document' do + input = <<-EOS += Article +:toc: +:toc-placement!: + +Once upon a time... + +toc::[] + +== Section One + +It was a dark and stormy night... + +== Section Two + +They couldn't believe their eyes when... + EOS + + output = render_string input, :header_footer => false + assert_css '#preamble:root #toc', output, 1 + assert_css '#preamble:root .paragraph + #toc', output, 1 + end + + test 'should not assign toc id to more than one toc' do + input = <<-EOS += Article +:toc: + +Once upon a time... + +toc::[] + +== Section One + +It was a dark and stormy night... + +== Section Two + +They couldn't believe their eyes when... + EOS + + output = render_string input + + assert_css '#toc', output, 1 + assert_css '#toctitle', output, 1 + assert_xpath '(//*[@class="toc"])[2][not(@id)]', output, 1 + assert_xpath '(//*[@class="toc"])[2]/*[@class="title"][not(@id)]', output, 1 + end + + test 'should use global attributes for toc-title, toc-class and toclevels for toc macro' do + input = <<-EOS += Article +:toc: +:toc-placement!: +:toc-title: Contents +:toc-class: contents +:toclevels: 1 + +Preamble. + +toc::[] + +== Section 1 + +=== Section 1.1 + +==== Section 1.1.1 + +==== Section 1.1.2 + +=== Section 1.2 + +== Section 2 + +Fin. + EOS + + output = render_string input + assert_css '#toc', output, 1 + assert_css '#toctitle', output, 1 + assert_css '#preamble #toc', output, 1 + assert_css '#preamble #toc.contents', output, 1 + assert_xpath '//*[@id="toc"]/*[@class="title"][text() = "Contents"]', output, 1 + assert_css '#toc li', output, 2 + assert_xpath '(//*[@id="toc"]//li)[1]/a[text() = "1. Section 1"]', output, 1 + assert_xpath '(//*[@id="toc"]//li)[2]/a[text() = "2. Section 2"]', output, 1 + end + + test 'should honor id, title, role and level attributes on toc macro' do + input = <<-EOS += Article +:toc: +:toc-placement!: +:toc-title: Ignored +:toc-class: ignored +:toclevels: 5 +:tocdepth: 1 + +Preamble. + +[[contents]] +[role="contents"] +.Contents +toc::[levels={tocdepth}] + +== Section 1 + +=== Section 1.1 + +==== Section 1.1.1 + +==== Section 1.1.2 + +=== Section 1.2 + +== Section 2 + +Fin. + EOS + + output = render_string input + assert_css '#toc', output, 0 + assert_css '#toctitle', output, 0 + assert_css '#preamble #contents', output, 1 + assert_css '#preamble #contents.contents', output, 1 + assert_xpath '//*[@id="contents"]/*[@class="title"][text() = "Contents"]', output, 1 + assert_css '#contents li', output, 2 + assert_xpath '(//*[@id="contents"]//li)[1]/a[text() = "1. Section 1"]', output, 1 + assert_xpath '(//*[@id="contents"]//li)[2]/a[text() = "2. Section 2"]', output, 1 end end @@ -985,7 +1189,7 @@ That's all she wrote! assert_xpath '/book/part[2]/chapter[1]/title[text() = "Chapter Three"]', output, 1 end - test 'wip subsections in preface and appendix should start at level 2' do + test 'subsections in preface and appendix should start at level 2' do input = <<-EOS = Multipart Book Doc Writer |
