Commit b61a44fe authored by Robert Speicher's avatar Robert Speicher

Merge pull request #9288 from jirutka/asciidoc

Improve AsciiDoc rendering

See https://github.com/gitlabhq/gitlabhq/pull/9288
parents f5f0ed55 92133a89
...@@ -68,6 +68,8 @@ v 7.11.0 (unreleased) ...@@ -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
......
...@@ -102,7 +102,7 @@ gem 'rdoc', '~>3.6' ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -222,8 +222,12 @@ module ApplicationHelper
end end
def render_markup(file_name, file_content) def render_markup(file_name, file_content)
GitHub::Markup.render(file_name, file_content). if asciidoc?(file_name)
force_encoding(file_content.encoding).html_safe asciidoc(file_content)
else
GitHub::Markup.render(file_name, file_content).
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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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
......
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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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')
......
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' ...@@ -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 ...@@ -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
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