summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Allen <dallen@redhat.com>2013-04-23 00:33:31 -0600
committerDan Allen <dallen@redhat.com>2013-04-23 14:20:35 -0600
commit3a0969cbff400a685a3ba3cadfbea0a2b67aa234 (patch)
tree2926993a9e771b7e30138b9f5a3387085737a323
parent8d9241ea92fb8d70ff53b9565f31bbcf56a1348b (diff)
resolves #269 implement toc::[] macro
-rw-r--r--README.asciidoc9
-rwxr-xr-xlib/asciidoctor.rb4
-rw-r--r--lib/asciidoctor/backends/docbook45.rb10
-rw-r--r--lib/asciidoctor/backends/html5.rb34
-rw-r--r--lib/asciidoctor/document.rb13
-rw-r--r--lib/asciidoctor/lexer.rb7
-rw-r--r--test/document_test.rb4
-rw-r--r--test/sections_test.rb230
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