Merge pull request #9288 from jirutka/asciidoc
Improve AsciiDoc rendering See https://github.com/gitlabhq/gitlabhq/pull/9288
This commit is contained in:
commit
b61a44fe1a
12 changed files with 189 additions and 9 deletions
|
@ -68,6 +68,8 @@ v 7.11.0 (unreleased)
|
||||||
- Spin spinner icon next to "Checking for CI status..." on MR page.
|
- Spin spinner icon next to "Checking for CI status..." on MR page.
|
||||||
- Fix reference links in dashboard activity and ATOM feeds.
|
- Fix reference links in dashboard activity and ATOM feeds.
|
||||||
- Ensure that the first added admin performs repository imports
|
- Ensure that the first added admin performs repository imports
|
||||||
|
- Update Asciidoctor gem to version 1.5.2. (Jakub Jirutka)
|
||||||
|
- Fix resolving of relative links to repository files in AsciiDoc documents. (Jakub Jirutka)
|
||||||
|
|
||||||
v 7.10.2
|
v 7.10.2
|
||||||
- Fix CI links on MR page
|
- Fix CI links on MR page
|
||||||
|
|
2
Gemfile
2
Gemfile
|
@ -102,7 +102,7 @@ gem 'rdoc', '~>3.6'
|
||||||
gem 'org-ruby', '= 0.9.12'
|
gem 'org-ruby', '= 0.9.12'
|
||||||
gem 'creole', '~>0.3.6'
|
gem 'creole', '~>0.3.6'
|
||||||
gem 'wikicloth', '=0.8.1'
|
gem 'wikicloth', '=0.8.1'
|
||||||
gem 'asciidoctor', '= 0.1.4'
|
gem 'asciidoctor', '~> 1.5.2'
|
||||||
|
|
||||||
# Diffs
|
# Diffs
|
||||||
gem 'diffy', '~> 3.0.3'
|
gem 'diffy', '~> 3.0.3'
|
||||||
|
|
|
@ -42,7 +42,7 @@ GEM
|
||||||
arel (5.0.1.20140414130214)
|
arel (5.0.1.20140414130214)
|
||||||
asana (0.0.6)
|
asana (0.0.6)
|
||||||
activeresource (>= 3.2.3)
|
activeresource (>= 3.2.3)
|
||||||
asciidoctor (0.1.4)
|
asciidoctor (1.5.2)
|
||||||
ast (2.0.0)
|
ast (2.0.0)
|
||||||
astrolabe (1.3.0)
|
astrolabe (1.3.0)
|
||||||
parser (>= 2.2.0.pre.3, < 3.0)
|
parser (>= 2.2.0.pre.3, < 3.0)
|
||||||
|
@ -683,7 +683,7 @@ DEPENDENCIES
|
||||||
addressable
|
addressable
|
||||||
annotate (~> 2.6.0.beta2)
|
annotate (~> 2.6.0.beta2)
|
||||||
asana (~> 0.0.6)
|
asana (~> 0.0.6)
|
||||||
asciidoctor (= 0.1.4)
|
asciidoctor (~> 1.5.2)
|
||||||
attr_encrypted (= 1.3.4)
|
attr_encrypted (= 1.3.4)
|
||||||
awesome_print
|
awesome_print
|
||||||
better_errors
|
better_errors
|
||||||
|
|
|
@ -222,8 +222,12 @@ module ApplicationHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_markup(file_name, file_content)
|
def render_markup(file_name, file_content)
|
||||||
|
if asciidoc?(file_name)
|
||||||
|
asciidoc(file_content)
|
||||||
|
else
|
||||||
GitHub::Markup.render(file_name, file_content).
|
GitHub::Markup.render(file_name, file_content).
|
||||||
force_encoding(file_content.encoding).html_safe
|
force_encoding(file_content.encoding).html_safe
|
||||||
|
end
|
||||||
rescue RuntimeError
|
rescue RuntimeError
|
||||||
simple_format(file_content)
|
simple_format(file_content)
|
||||||
end
|
end
|
||||||
|
@ -236,6 +240,10 @@ module ApplicationHelper
|
||||||
Gitlab::MarkdownHelper.gitlab_markdown?(filename)
|
Gitlab::MarkdownHelper.gitlab_markdown?(filename)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def asciidoc?(filename)
|
||||||
|
Gitlab::MarkdownHelper.asciidoc?(filename)
|
||||||
|
end
|
||||||
|
|
||||||
# Overrides ActionView::Helpers::UrlHelper#link_to to add `rel="nofollow"` to
|
# Overrides ActionView::Helpers::UrlHelper#link_to to add `rel="nofollow"` to
|
||||||
# external links
|
# external links
|
||||||
def link_to(name = nil, options = nil, html_options = {})
|
def link_to(name = nil, options = nil, html_options = {})
|
||||||
|
|
|
@ -56,6 +56,16 @@ module GitlabMarkdownHelper
|
||||||
@markdown.render(text).html_safe
|
@markdown.render(text).html_safe
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def asciidoc(text)
|
||||||
|
Gitlab::Asciidoc.render(text, {
|
||||||
|
commit: @commit,
|
||||||
|
project: @project,
|
||||||
|
project_wiki: @project_wiki,
|
||||||
|
requested_path: @path,
|
||||||
|
ref: @ref
|
||||||
|
})
|
||||||
|
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
|
||||||
# as Markdown. HTML tags in the parsed output are not counted toward the
|
# as Markdown. HTML tags in the parsed output are not counted toward the
|
||||||
# +max_chars+ limit. If the length limit falls within a tag's contents, then
|
# +max_chars+ limit. If the length limit falls within a tag's contents, then
|
||||||
|
@ -67,8 +77,11 @@ module GitlabMarkdownHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_wiki_content(wiki_page)
|
def render_wiki_content(wiki_page)
|
||||||
if wiki_page.format == :markdown
|
case wiki_page.format
|
||||||
|
when :markdown
|
||||||
markdown(wiki_page.content)
|
markdown(wiki_page.content)
|
||||||
|
when :asciidoc
|
||||||
|
asciidoc(wiki_page.content)
|
||||||
else
|
else
|
||||||
wiki_page.formatted_content.html_safe
|
wiki_page.formatted_content.html_safe
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,6 +27,8 @@ module TreeHelper
|
||||||
def render_readme(readme)
|
def render_readme(readme)
|
||||||
if gitlab_markdown?(readme.name)
|
if gitlab_markdown?(readme.name)
|
||||||
preserve(markdown(readme.data))
|
preserve(markdown(readme.data))
|
||||||
|
elsif asciidoc?(readme.name)
|
||||||
|
asciidoc(readme.data)
|
||||||
elsif markup?(readme.name)
|
elsif markup?(readme.name)
|
||||||
render_markup(readme.name, readme.data)
|
render_markup(readme.name, readme.data)
|
||||||
else
|
else
|
||||||
|
|
60
lib/gitlab/asciidoc.rb
Normal file
60
lib/gitlab/asciidoc.rb
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
require 'asciidoctor'
|
||||||
|
require 'html/pipeline'
|
||||||
|
|
||||||
|
module Gitlab
|
||||||
|
# Parser/renderer for the AsciiDoc format that uses Asciidoctor and filters
|
||||||
|
# the resulting HTML through HTML pipeline filters.
|
||||||
|
module Asciidoc
|
||||||
|
|
||||||
|
# Provide autoload paths for filters to prevent a circular dependency error
|
||||||
|
autoload :RelativeLinkFilter, 'gitlab/markdown/relative_link_filter'
|
||||||
|
|
||||||
|
DEFAULT_ADOC_ATTRS = [
|
||||||
|
'showtitle', 'idprefix=user-content-', 'idseparator=-', 'env=gitlab',
|
||||||
|
'env-gitlab', 'source-highlighter=html-pipeline'
|
||||||
|
].freeze
|
||||||
|
|
||||||
|
# Public: Converts the provided Asciidoc markup into HTML.
|
||||||
|
#
|
||||||
|
# input - the source text in Asciidoc format
|
||||||
|
# context - a Hash with the template context:
|
||||||
|
# :commit
|
||||||
|
# :project
|
||||||
|
# :project_wiki
|
||||||
|
# :requested_path
|
||||||
|
# :ref
|
||||||
|
# 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 = {})
|
||||||
|
asciidoc_opts = asciidoc_opts.reverse_merge(
|
||||||
|
safe: :secure,
|
||||||
|
backend: html_opts[:xhtml] ? :xhtml5 : :html5,
|
||||||
|
attributes: []
|
||||||
|
)
|
||||||
|
asciidoc_opts[:attributes].unshift(*DEFAULT_ADOC_ATTRS)
|
||||||
|
|
||||||
|
html = ::Asciidoctor.convert(input, asciidoc_opts)
|
||||||
|
|
||||||
|
if context[:project]
|
||||||
|
result = HTML::Pipeline.new(filters).call(html, context)
|
||||||
|
|
||||||
|
save_opts = html_opts[:xhtml] ?
|
||||||
|
Nokogiri::XML::Node::SaveOptions::AS_XHTML : 0
|
||||||
|
|
||||||
|
html = result[:output].to_html(save_with: save_opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
html.html_safe
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def self.filters
|
||||||
|
[
|
||||||
|
Gitlab::Markdown::RelativeLinkFilter
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,7 +9,7 @@ module Gitlab
|
||||||
# Returns boolean
|
# Returns boolean
|
||||||
def markup?(filename)
|
def markup?(filename)
|
||||||
filename.downcase.end_with?(*%w(.textile .rdoc .org .creole .wiki
|
filename.downcase.end_with?(*%w(.textile .rdoc .org .creole .wiki
|
||||||
.mediawiki .rst .adoc .asciidoc .asc))
|
.mediawiki .rst .adoc .ad .asciidoc))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Public: Determines if a given filename is compatible with
|
# Public: Determines if a given filename is compatible with
|
||||||
|
@ -22,6 +22,15 @@ module Gitlab
|
||||||
filename.downcase.end_with?(*%w(.mdown .md .markdown))
|
filename.downcase.end_with?(*%w(.mdown .md .markdown))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Public: Determines if the given filename has AsciiDoc extension.
|
||||||
|
#
|
||||||
|
# filename - Filename string to check
|
||||||
|
#
|
||||||
|
# Returns boolean
|
||||||
|
def asciidoc?(filename)
|
||||||
|
filename.downcase.end_with?(*%w(.adoc .ad .asciidoc))
|
||||||
|
end
|
||||||
|
|
||||||
def previewable?(filename)
|
def previewable?(filename)
|
||||||
gitlab_markdown?(filename) || markup?(filename)
|
gitlab_markdown?(filename) || markup?(filename)
|
||||||
end
|
end
|
||||||
|
|
|
@ -261,12 +261,19 @@ describe ApplicationHelper do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'markup_render' do
|
describe 'render_markup' do
|
||||||
let(:content) { 'Noël' }
|
let(:content) { 'Noël' }
|
||||||
|
|
||||||
it 'should preserve encoding' do
|
it 'should preserve encoding' do
|
||||||
expect(content.encoding.name).to eq('UTF-8')
|
expect(content.encoding.name).to eq('UTF-8')
|
||||||
expect(render_markup('foo.rst', content).encoding.name).to eq('UTF-8')
|
expect(render_markup('foo.rst', content).encoding.name).to eq('UTF-8')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should delegate to #asciidoc when file name corresponds to AsciiDoc" do
|
||||||
|
expect(self).to receive(:asciidoc?).with('foo.adoc').and_return(true)
|
||||||
|
expect(self).to receive(:asciidoc).and_return('NOEL')
|
||||||
|
|
||||||
|
expect(render_markup('foo.adoc', content)).to eq('NOEL')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -110,6 +110,14 @@ describe GitlabMarkdownHelper do
|
||||||
helper.render_wiki_content(@wiki)
|
helper.render_wiki_content(@wiki)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should use Asciidoctor for asciidoc files" do
|
||||||
|
allow(@wiki).to receive(:format).and_return(:asciidoc)
|
||||||
|
|
||||||
|
expect(helper).to receive(:asciidoc).with('wiki content')
|
||||||
|
|
||||||
|
helper.render_wiki_content(@wiki)
|
||||||
|
end
|
||||||
|
|
||||||
it "should use the Gollum renderer for all other file types" do
|
it "should use the Gollum renderer for all other file types" do
|
||||||
allow(@wiki).to receive(:format).and_return(:rdoc)
|
allow(@wiki).to receive(:format).and_return(:rdoc)
|
||||||
formatted_content_stub = double('formatted_content')
|
formatted_content_stub = double('formatted_content')
|
||||||
|
|
59
spec/lib/gitlab/asciidoc_spec.rb
Normal file
59
spec/lib/gitlab/asciidoc_spec.rb
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
require 'nokogiri'
|
||||||
|
|
||||||
|
module Gitlab
|
||||||
|
describe Asciidoc do
|
||||||
|
|
||||||
|
let(:input) { '<b>ascii</b>' }
|
||||||
|
let(:context) { {} }
|
||||||
|
let(:html) { 'H<sub>2</sub>O' }
|
||||||
|
|
||||||
|
context "without project" do
|
||||||
|
|
||||||
|
it "should convert the input using Asciidoctor and default options" do
|
||||||
|
expected_asciidoc_opts = { safe: :secure, backend: :html5,
|
||||||
|
attributes: described_class::DEFAULT_ADOC_ATTRS }
|
||||||
|
|
||||||
|
expect(Asciidoctor).to receive(:convert)
|
||||||
|
.with(input, expected_asciidoc_opts).and_return(html)
|
||||||
|
|
||||||
|
expect( render(input, context) ).to eql html
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with asciidoc_opts" do
|
||||||
|
|
||||||
|
let(:asciidoc_opts) { {safe: :safe, attributes: ['foo']} }
|
||||||
|
|
||||||
|
it "should merge the options with default ones" do
|
||||||
|
expected_asciidoc_opts = { safe: :safe, backend: :html5,
|
||||||
|
attributes: described_class::DEFAULT_ADOC_ATTRS + ['foo'] }
|
||||||
|
|
||||||
|
expect(Asciidoctor).to receive(:convert)
|
||||||
|
.with(input, expected_asciidoc_opts).and_return(html)
|
||||||
|
|
||||||
|
render(input, context, asciidoc_opts)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with project in context" do
|
||||||
|
|
||||||
|
let(:context) { {project: create(:project)} }
|
||||||
|
|
||||||
|
it "should filter converted input via HTML pipeline and return result" do
|
||||||
|
filtered_html = '<b>ASCII</b>'
|
||||||
|
|
||||||
|
allow(Asciidoctor).to receive(:convert).and_return(html)
|
||||||
|
expect_any_instance_of(HTML::Pipeline).to receive(:call)
|
||||||
|
.with(html, context)
|
||||||
|
.and_return(output: Nokogiri::HTML.fragment(filtered_html))
|
||||||
|
|
||||||
|
expect( render('foo', context) ).to eql filtered_html
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def render(*args)
|
||||||
|
described_class.render(*args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,7 +3,7 @@ require 'spec_helper'
|
||||||
describe Gitlab::MarkdownHelper do
|
describe Gitlab::MarkdownHelper do
|
||||||
describe '#markup?' do
|
describe '#markup?' do
|
||||||
%w(textile rdoc org creole wiki
|
%w(textile rdoc org creole wiki
|
||||||
mediawiki rst adoc asciidoc asc).each do |type|
|
mediawiki rst adoc ad asciidoc).each do |type|
|
||||||
it "returns true for #{type} files" do
|
it "returns true for #{type} files" do
|
||||||
expect(Gitlab::MarkdownHelper.markup?("README.#{type}")).to be_truthy
|
expect(Gitlab::MarkdownHelper.markup?("README.#{type}")).to be_truthy
|
||||||
end
|
end
|
||||||
|
@ -25,4 +25,16 @@ describe Gitlab::MarkdownHelper do
|
||||||
expect(Gitlab::MarkdownHelper.gitlab_markdown?('README.rb')).not_to be_truthy
|
expect(Gitlab::MarkdownHelper.gitlab_markdown?('README.rb')).not_to be_truthy
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#asciidoc?' do
|
||||||
|
%w(adoc ad asciidoc ADOC).each do |type|
|
||||||
|
it "returns true for #{type} files" do
|
||||||
|
expect(Gitlab::MarkdownHelper.asciidoc?("README.#{type}")).to be_truthy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false when given a non-asciidoc filename' do
|
||||||
|
expect(Gitlab::MarkdownHelper.asciidoc?('README.rb')).not_to be_truthy
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue