Commit b093cb35 authored by Douwe Maan's avatar Douwe Maan

Use Gitlab::Markdown for Asciidoc and ReferenceExtractor pipelines

parent da9746e5
...@@ -65,13 +65,16 @@ module GitlabMarkdownHelper ...@@ -65,13 +65,16 @@ module GitlabMarkdownHelper
end end
def asciidoc(text) def asciidoc(text)
Gitlab::Asciidoc.render(text, { Gitlab::Asciidoc.render(text,
commit: @commit, project: @project,
project: @project, current_user: (current_user if defined?(current_user)),
project_wiki: @project_wiki,
# RelativeLinkFilter
project_wiki: @project_wiki,
requested_path: @path, requested_path: @path,
ref: @ref ref: @ref,
}) commit: @commit
)
end end
# Return the first line of +text+, up to +max_chars+, after parsing the line # Return the first line of +text+, up to +max_chars+, after parsing the line
......
require 'asciidoctor' require 'asciidoctor'
require 'html/pipeline'
module Gitlab module Gitlab
# Parser/renderer for the AsciiDoc format that uses Asciidoctor and filters # Parser/renderer for the AsciiDoc format that uses Asciidoctor and filters
# the resulting HTML through HTML pipeline filters. # the resulting HTML through HTML pipeline filters.
module Asciidoc module Asciidoc
# Provide autoload paths for filters to prevent a circular dependency error
autoload :RelativeLinkFilter, 'gitlab/markdown/relative_link_filter'
DEFAULT_ADOC_ATTRS = [ DEFAULT_ADOC_ATTRS = [
'showtitle', 'idprefix=user-content-', 'idseparator=-', 'env=gitlab', 'showtitle', 'idprefix=user-content-', 'idseparator=-', 'env=gitlab',
'env-gitlab', 'source-highlighter=html-pipeline' 'env-gitlab', 'source-highlighter=html-pipeline'
...@@ -24,13 +20,11 @@ module Gitlab ...@@ -24,13 +20,11 @@ module Gitlab
# :requested_path # :requested_path
# :ref # :ref
# asciidoc_opts - a Hash of options to pass to the Asciidoctor converter # asciidoc_opts - a Hash of options to pass to the Asciidoctor converter
# html_opts - a Hash of options for HTML output:
# :xhtml - output XHTML instead of HTML
# #
def self.render(input, context, asciidoc_opts = {}, html_opts = {}) def self.render(input, context, asciidoc_opts = {})
asciidoc_opts = asciidoc_opts.reverse_merge( asciidoc_opts.reverse_merge!(
safe: :secure, safe: :secure,
backend: html_opts[:xhtml] ? :xhtml5 : :html5, backend: :html5,
attributes: [] attributes: []
) )
asciidoc_opts[:attributes].unshift(*DEFAULT_ADOC_ATTRS) asciidoc_opts[:attributes].unshift(*DEFAULT_ADOC_ATTRS)
...@@ -38,23 +32,10 @@ module Gitlab ...@@ -38,23 +32,10 @@ module Gitlab
html = ::Asciidoctor.convert(input, asciidoc_opts) html = ::Asciidoctor.convert(input, asciidoc_opts)
if context[:project] if context[:project]
result = HTML::Pipeline.new(filters).call(html, context) html = Gitlab::Markdown.render(html, context.merge(pipeline: :asciidoc))
save_opts = html_opts[:xhtml] ?
Nokogiri::XML::Node::SaveOptions::AS_XHTML : 0
html = result[:output].to_html(save_with: save_opts)
end end
html.html_safe html.html_safe
end end
private
def self.filters
[
Gitlab::Markdown::RelativeLinkFilter
]
end
end end
end end
...@@ -21,9 +21,9 @@ module Gitlab ...@@ -21,9 +21,9 @@ module Gitlab
# Returns an HTML-safe String # Returns an HTML-safe String
def self.render(text, context = {}) def self.render(text, context = {})
cache_key = context.delete(:cache_key) cache_key = context.delete(:cache_key)
cache_key = full_cache_key(cache_key, context[:pipeline])
if cache_key if cache_key
cache_key = full_cache_key(cache_key, context[:pipeline])
Rails.cache.fetch(cache_key) do Rails.cache.fetch(cache_key) do
cacheless_render(text, context) cacheless_render(text, context)
end end
...@@ -32,25 +32,21 @@ module Gitlab ...@@ -32,25 +32,21 @@ module Gitlab
end end
end end
# Provide autoload paths for filters to prevent a circular dependency error def self.render_result(text, context = {})
autoload :AutolinkFilter, 'gitlab/markdown/autolink_filter' pipeline = context[:pipeline] || :full
autoload :CommitRangeReferenceFilter, 'gitlab/markdown/commit_range_reference_filter'
autoload :CommitReferenceFilter, 'gitlab/markdown/commit_reference_filter' html_pipeline = html_pipelines[pipeline]
autoload :EmojiFilter, 'gitlab/markdown/emoji_filter'
autoload :ExternalIssueReferenceFilter, 'gitlab/markdown/external_issue_reference_filter' transformers = context_transformers[pipeline]
autoload :ExternalLinkFilter, 'gitlab/markdown/external_link_filter' context = transformers.reduce(context) { |context, transformer| transformer.call(context) }
autoload :IssueReferenceFilter, 'gitlab/markdown/issue_reference_filter'
autoload :LabelReferenceFilter, 'gitlab/markdown/label_reference_filter' html_pipeline.call(text, context)
autoload :MarkdownFilter, 'gitlab/markdown/markdown_filter' end
autoload :MergeRequestReferenceFilter, 'gitlab/markdown/merge_request_reference_filter'
autoload :RedactorFilter, 'gitlab/markdown/redactor_filter' def self.cached?(cache_key, pipeline: :full)
autoload :RelativeLinkFilter, 'gitlab/markdown/relative_link_filter' cache_key = full_cache_key(cache_key, pipeline)
autoload :SanitizationFilter, 'gitlab/markdown/sanitization_filter' cache_key ? Rails.cache.exist?(cache_key) : false
autoload :SnippetReferenceFilter, 'gitlab/markdown/snippet_reference_filter' end
autoload :SyntaxHighlightFilter, 'gitlab/markdown/syntax_highlight_filter'
autoload :TableOfContentsFilter, 'gitlab/markdown/table_of_contents_filter'
autoload :TaskListFilter, 'gitlab/markdown/task_list_filter'
autoload :UserReferenceFilter, 'gitlab/markdown/user_reference_filter'
# Perform post-processing on an HTML String # Perform post-processing on an HTML String
# #
...@@ -66,21 +62,39 @@ module Gitlab ...@@ -66,21 +62,39 @@ module Gitlab
# #
# Returns an HTML-safe String # Returns an HTML-safe String
def self.post_process(html, context) def self.post_process(html, context)
doc = html_pipelines[:post_process].to_document(html, context) html_pipeline = html_pipelines[:post_process]
if context[:xhtml] if context[:xhtml]
doc.to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML) html_pipeline.to_document(html, context).to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML)
else else
doc.to_html html_pipeline.to_html(html, context)
end.html_safe end.html_safe
end end
private private
FILTERS = {
plain_markdown: [ # Provide autoload paths for filters to prevent a circular dependency error
Gitlab::Markdown::MarkdownFilter autoload :AutolinkFilter, 'gitlab/markdown/autolink_filter'
], autoload :CommitRangeReferenceFilter, 'gitlab/markdown/commit_range_reference_filter'
gfm: [ autoload :CommitReferenceFilter, 'gitlab/markdown/commit_reference_filter'
autoload :EmojiFilter, 'gitlab/markdown/emoji_filter'
autoload :ExternalIssueReferenceFilter, 'gitlab/markdown/external_issue_reference_filter'
autoload :ExternalLinkFilter, 'gitlab/markdown/external_link_filter'
autoload :IssueReferenceFilter, 'gitlab/markdown/issue_reference_filter'
autoload :LabelReferenceFilter, 'gitlab/markdown/label_reference_filter'
autoload :MarkdownFilter, 'gitlab/markdown/markdown_filter'
autoload :MergeRequestReferenceFilter, 'gitlab/markdown/merge_request_reference_filter'
autoload :RedactorFilter, 'gitlab/markdown/redactor_filter'
autoload :RelativeLinkFilter, 'gitlab/markdown/relative_link_filter'
autoload :SanitizationFilter, 'gitlab/markdown/sanitization_filter'
autoload :SnippetReferenceFilter, 'gitlab/markdown/snippet_reference_filter'
autoload :SyntaxHighlightFilter, 'gitlab/markdown/syntax_highlight_filter'
autoload :TableOfContentsFilter, 'gitlab/markdown/table_of_contents_filter'
autoload :TaskListFilter, 'gitlab/markdown/task_list_filter'
autoload :UserReferenceFilter, 'gitlab/markdown/user_reference_filter'
def self.gfm_filters
@gfm_filters ||= [
Gitlab::Markdown::SyntaxHighlightFilter, Gitlab::Markdown::SyntaxHighlightFilter,
Gitlab::Markdown::SanitizationFilter, Gitlab::Markdown::SanitizationFilter,
...@@ -99,71 +113,99 @@ module Gitlab ...@@ -99,71 +113,99 @@ module Gitlab
Gitlab::Markdown::LabelReferenceFilter, Gitlab::Markdown::LabelReferenceFilter,
Gitlab::Markdown::TaskListFilter Gitlab::Markdown::TaskListFilter
], ]
end
full: [:plain_markdown, :gfm], def self.all_filters
atom: :full, @all_filters ||= {
email: :full, plain_markdown: [
description: :full, Gitlab::Markdown::MarkdownFilter
single_line: :gfm, ],
gfm: gfm_filters,
full: [:plain_markdown, :gfm],
atom: :full,
email: :full,
description: :full,
single_line: :gfm,
asciidoc: [
Gitlab::Markdown::RelativeLinkFilter
],
post_process: [
Gitlab::Markdown::RelativeLinkFilter,
Gitlab::Markdown::RedactorFilter
],
reference_extraction: [
Gitlab::Markdown::ReferenceGathererFilter
]
}
end
post_process: [ def self.all_context_transformers
Gitlab::Markdown::RelativeLinkFilter, @all_context_transformers ||= {
Gitlab::Markdown::RedactorFilter gfm: {
] only_path: true,
}
# EmojiFilter
CONTEXT_TRANSFORMERS = { asset_host: Gitlab::Application.config.asset_host,
gfm: { asset_root: Gitlab.config.gitlab.base_url
only_path: true, },
full: :gfm,
# EmojiFilter
asset_host: Gitlab::Application.config.asset_host, atom: [
asset_root: Gitlab.config.gitlab.base_url :full,
}, {
full: :gfm, only_path: false,
xhtml: true
atom: [ }
:full, ],
{ email: [
only_path: false, :full,
xhtml: true {
} only_path: false
], }
email: [ ],
:full, description: [
{ only_path: false } :full,
], {
description: [ # SanitizationFilter
:full, inline_sanitization: true
{ }
# SanitizationFilter ],
inline_sanitization: true single_line: :gfm,
post_process: {
post_process: true
} }
],
single_line: :gfm,
post_process: {
post_process: true
} }
} end
def self.html_filters
@html_filters ||= Hash.new do |hash, pipeline|
filters = get_filters(pipeline)
hash[pipeline] = filters if pipeline.is_a?(Symbol)
filters
end
end
def self.html_pipelines def self.html_pipelines
@html_pipelines ||= Hash.new do |hash, pipeline| @html_pipelines ||= Hash.new do |hash, pipeline|
filters = get_filters(pipeline) filters = get_filters(pipeline)
HTML::Pipeline.new(filters) html_pipeline = HTML::Pipeline.new(filters)
hash[pipeline] = html_pipeline if pipeline.is_a?(Symbol)
html_pipeline
end end
end end
def self.cacheless_render(text, context = {}) def self.context_transformers
pipeline = context[:pipeline] || :full @context_transformers ||= Hash.new do |hash, pipeline|
transformers = get_context_transformers(pipeline)
html_pipeline = html_pipelines[pipeline] hash[pipeline] = transformers if pipeline.is_a?(Symbol)
transformers
transformers = get_context_transformers(pipeline) end
context = transformers.reduce(context) { |context, transformer| transformer.call(context) }
html_pipeline.to_html(text, context)
end end
def self.get_filters(pipelines) def self.get_filters(pipelines)
...@@ -172,9 +214,9 @@ module Gitlab ...@@ -172,9 +214,9 @@ module Gitlab
when Class when Class
pipeline pipeline
when Symbol when Symbol
get_filters(FILTERS[pipeline]) html_filters[all_filters[pipeline]]
when Array when Array
get_filters(pipeline) html_filters[pipeline]
end end
end.compact end.compact
end end
...@@ -187,14 +229,26 @@ module Gitlab ...@@ -187,14 +229,26 @@ module Gitlab
when Proc when Proc
pipeline pipeline
when Symbol when Symbol
get_context_transformers(CONTEXT_TRANSFORMERS[pipeline]) context_transformers[all_context_transformers[pipeline]]
when Array when Array
get_context_transformers(pipeline) context_transformers[pipeline]
end end
end.compact end.compact
end end
def self.cacheless_render(text, context = {})
result = render_result(text, context)
output = result[:output]
if output.respond_to?(:to_html)
output.to_html
else
output.to_s
end
end
def self.full_cache_key(cache_key, pipeline = :full) def self.full_cache_key(cache_key, pipeline = :full)
return unless cache_key && pipeline.is_a?(Symbol)
["markdown", *cache_key, pipeline] ["markdown", *cache_key, pipeline]
end end
end end
......
...@@ -14,7 +14,8 @@ module Gitlab ...@@ -14,7 +14,8 @@ module Gitlab
def analyze(text, cache_key: nil) def analyze(text, cache_key: nil)
references.clear references.clear
@document = Gitlab::Markdown.render(text, project: project, cache_key: cache_key) @pipeline = Gitlab::Markdown.cached?(cache_key, pipeline: :full) ? :full : :plain_markdown
@html = Gitlab::Markdown.render(text, project: project, cache_key: cache_key, pipeline: @pipeline)
end end
%i(user label issue merge_request snippet commit commit_range).each do |type| %i(user label issue merge_request snippet commit commit_range).each do |type|
...@@ -45,14 +46,19 @@ module Gitlab ...@@ -45,14 +46,19 @@ module Gitlab
filter = Gitlab::Markdown.const_get(klass) filter = Gitlab::Markdown.const_get(klass)
context = { context = {
project: project, pipeline: [:reference_extraction],
current_user: current_user,
project: project,
current_user: current_user,
# ReferenceGathererFilter
load_lazy_references: false, load_lazy_references: false,
reference_filter: filter reference_filter: filter
} }
result = self.class.pipeline.call(@document, context) context[:pipeline].unshift(filter) unless @pipeline == :full
result = Gitlab::Markdown.render_result(@html, context)
values = result[:references][filter_type].uniq values = result[:references][filter_type].uniq
if @load_lazy_references if @load_lazy_references
...@@ -61,9 +67,5 @@ module Gitlab ...@@ -61,9 +67,5 @@ module Gitlab
values values
end end
def self.pipeline
@pipeline ||= HTML::Pipeline.new([Gitlab::Markdown::ReferenceGathererFilter])
end
end end
end end
...@@ -50,9 +50,9 @@ module Gitlab ...@@ -50,9 +50,9 @@ module Gitlab
filtered_html = '<b>ASCII</b>' filtered_html = '<b>ASCII</b>'
allow(Asciidoctor).to receive(:convert).and_return(html) allow(Asciidoctor).to receive(:convert).and_return(html)
expect_any_instance_of(HTML::Pipeline).to receive(:call) expect(Gitlab::Markdown).to receive(:render)
.with(html, context) .with(html, context.merge(pipeline: :asciidoc))
.and_return(output: Nokogiri::HTML.fragment(filtered_html)) .and_return(filtered_html)
expect( render('foo', context) ).to eql filtered_html expect( render('foo', context) ).to eql filtered_html
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment