From bc43ad71ef2c1ab6cf56d94ac93df0bdc0e2b741 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 23 Feb 2016 18:05:10 -0500 Subject: [PATCH] Replace Gollum `[[_TOC_]]` tag with result of TableOfContentsFilter Closes #2494 --- lib/banzai/filter/gollum_tags_filter.rb | 35 ++++++++++--- lib/banzai/pipeline/wiki_pipeline.rb | 6 ++- .../banzai/filter/gollum_tags_filter_spec.rb | 52 +++++++++++++++++++ 3 files changed, 85 insertions(+), 8 deletions(-) diff --git a/lib/banzai/filter/gollum_tags_filter.rb b/lib/banzai/filter/gollum_tags_filter.rb index fe01dae4850..bcf5297e382 100644 --- a/lib/banzai/filter/gollum_tags_filter.rb +++ b/lib/banzai/filter/gollum_tags_filter.rb @@ -50,21 +50,30 @@ module Banzai # See https://github.com/gollum/gollum/wiki # # Rubular: http://rubular.com/r/7dQnE5CUCH - TAGS_PATTERN = %r{\[\[(.+?)\]\]} + TAGS_PATTERN = %r{\[\[(.+?)\]\]}.freeze # Pattern to match allowed image extensions - ALLOWED_IMAGE_EXTENSIONS = %r{.+(jpg|png|gif|svg|bmp)\z}i + ALLOWED_IMAGE_EXTENSIONS = %r{.+(jpg|png|gif|svg|bmp)\z}i.freeze def call search_text_nodes(doc).each do |node| - content = node.content + # A Gollum ToC tag is `[[_TOC_]]`, but due to MarkdownFilter running + # before this one, it will be converted into `[[TOC]]`, so it + # needs special-case handling + if toc_tag?(node) + next unless result[:toc].present? - next unless content.match(TAGS_PATTERN) + process_toc_tag(node) + else + content = node.content - html = process_tag($1) + next unless content =~ TAGS_PATTERN - if html && html != node.content - node.replace(html) + html = process_tag($1) + + if html && html != node.content + node.replace(html) + end end end @@ -73,6 +82,12 @@ module Banzai private + # Replace an entire `[[TOC]]` node with the result generated by + # TableOfContentsFilter + def process_toc_tag(node) + node.parent.parent.replace(result[:toc]) + end + # Process a single tag into its final HTML form. # # tag - The String tag contents (the stuff inside the double brackets). @@ -108,6 +123,12 @@ module Banzai end end + def toc_tag?(node) + node.content == 'TOC' && + node.parent.name == 'em' && + node.parent.parent.text == '[[TOC]]' + end + def image?(path) path =~ ALLOWED_IMAGE_EXTENSIONS end diff --git a/lib/banzai/pipeline/wiki_pipeline.rb b/lib/banzai/pipeline/wiki_pipeline.rb index 50b5450e70b..bc2bf111971 100644 --- a/lib/banzai/pipeline/wiki_pipeline.rb +++ b/lib/banzai/pipeline/wiki_pipeline.rb @@ -4,7 +4,11 @@ module Banzai module Pipeline class WikiPipeline < FullPipeline def self.filters - super.insert(1, Filter::GollumTagsFilter) + @filters ||= begin + filters = super + toc = filters.index(Filter::TableOfContentsFilter) + filters.insert(toc + 1, Filter::GollumTagsFilter) + end end end end diff --git a/spec/lib/banzai/filter/gollum_tags_filter_spec.rb b/spec/lib/banzai/filter/gollum_tags_filter_spec.rb index 38baa819957..11cc290d6d0 100644 --- a/spec/lib/banzai/filter/gollum_tags_filter_spec.rb +++ b/spec/lib/banzai/filter/gollum_tags_filter_spec.rb @@ -86,4 +86,56 @@ describe Banzai::Filter::GollumTagsFilter, lib: true do expect(doc.at_css('a')['href']).to eq 'wiki-slug' end end + + context 'table of contents' do + let(:pipeline) { Banzai::Pipeline[:wiki] } + + it 'replaces the tag with the TableOfContentsFilter result' do + markdown = <<-MD.strip_heredoc + [[_TOC_]] + + ## Header + + Foo + MD + + result = pipeline.call(markdown, project_wiki: project_wiki, project: project) + + aggregate_failures do + expect(result[:output].text).not_to include '[[_TOC_]]' + expect(result[:output].text).not_to include '[[' + expect(result[:output].to_html).to include(result[:toc]) + end + end + + it 'is case-sensitive' do + markdown = <<-MD.strip_heredoc + [[_toc_]] + + # Header 1 + + Foo + MD + + output = pipeline.to_html(markdown, project_wiki: project_wiki, project: project) + + expect(output).to include('[[toc]]') + end + + it 'handles an empty pipeline result' do + # No Markdown headers in this doc, so `result[:toc]` will be empty + markdown = <<-MD.strip_heredoc + [[_TOC_]] + + Foo + MD + + output = pipeline.to_html(markdown, project_wiki: project_wiki, project: project) + + aggregate_failures do + expect(output).not_to include('