Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib/rouge
diff options
context:
space:
mode:
authorStefan Tatschner <stefan@sevenbyte.org>2015-06-28 21:42:41 +0300
committerStefan Tatschner <rumpelsepp@sevenbyte.org>2015-07-29 09:32:00 +0300
commit00ff84d3c02e551cb7c4be3f71e1836e9f5abce2 (patch)
tree7ace300aa8116137fd73d035944c6b796853f646 /lib/rouge
parentf736721c5bb8f1f6031d36cc726cbd11cc499a72 (diff)
Add HTMLGitlab formatter
This custom formatter for rouge is needed to generate HTML output specifically for gitlab. Since its usecase is mostly suitable for gitlab it had been rejected upstream: https://github.com/jneen/rouge/pull/268 Thanks a lot to @stanhu, @jneen and @tsigo for review!
Diffstat (limited to 'lib/rouge')
-rw-r--r--lib/rouge/formatters/html_gitlab.rb168
1 files changed, 168 insertions, 0 deletions
diff --git a/lib/rouge/formatters/html_gitlab.rb b/lib/rouge/formatters/html_gitlab.rb
new file mode 100644
index 00000000000..485af6832d7
--- /dev/null
+++ b/lib/rouge/formatters/html_gitlab.rb
@@ -0,0 +1,168 @@
+require 'cgi'
+
+module Rouge
+ module Formatters
+ class HTMLGitlab < Rouge::Formatter
+ tag 'html_gitlab'
+
+ # Creates a new <tt>Rouge::Formatter::HTMLGitlab</tt> instance.
+ #
+ # [+nowrap+] If set to True, don't wrap the output at all, not
+ # even inside a <tt><pre></tt> tag (default: false).
+ # [+cssclass+] CSS class for the wrapping <tt><div></tt> tag
+ # (default: 'highlight').
+ # [+linenos+] If set to 'table', output line numbers as a table
+ # with two cells, one containing the line numbers,
+ # the other the whole code. This is copy paste friendly,
+ # but may cause alignment problems with some browsers
+ # or fonts. If set to 'inline', the line numbers will
+ # be integrated in the <tt><pre></tt> tag that contains
+ # the code (default: nil).
+ # [+linenostart+] The line number for the first line (default: 1).
+ # [+lineanchors+] If set to true the formatter will wrap each output
+ # line in an anchor tag with a name of L-linenumber.
+ # This allows easy linking to certain lines
+ # (default: false).
+ # [+lineanchorsid+] If lineanchors is true the name of the anchors can
+ # be changed with lineanchorsid to e.g. foo-linenumber
+ # (default: 'L').
+ # [+anchorlinenos+] If set to true, will wrap line numbers in <tt><a></tt>
+ # tags. Used in combination with linenos and lineanchors
+ # (default: false).
+ # [+inline_theme+] Inline CSS styles for the <pre> tag (default: false).
+ def initialize(
+ nowrap: false,
+ cssclass: 'highlight',
+ linenos: nil,
+ linenostart: 1,
+ lineanchors: false,
+ lineanchorsid: 'L',
+ anchorlinenos: false,
+ inline_theme: nil
+ )
+ @nowrap = nowrap
+ @cssclass = cssclass
+ @linenos = linenos
+ @linenostart = linenostart
+ @lineanchors = lineanchors
+ @lineanchorsid = lineanchorsid
+ @anchorlinenos = anchorlinenos
+ @inline_theme = Theme.find(@inline_theme).new if @inline_theme.is_a?(String)
+ end
+
+ def render(tokens)
+ case @linenos
+ when 'table'
+ render_tableized(tokens)
+ when 'inline'
+ render_untableized(tokens)
+ else
+ render_untableized(tokens)
+ end
+ end
+
+ alias_method :format, :render
+
+ private
+
+ def render_untableized(tokens)
+ data = process_tokens(tokens)
+
+ html = ''
+ html << "<pre class=\"#{@cssclass}\"><code>" unless @nowrap
+ html << wrap_lines(data[:code])
+ html << "</code></pre>\n" unless @nowrap
+ html
+ end
+
+ def render_tableized(tokens)
+ data = process_tokens(tokens)
+
+ html = ''
+ html << "<div class=\"#{@cssclass}\">" unless @nowrap
+ html << '<table><tbody>'
+ html << "<td class=\"linenos\"><pre>"
+ html << wrap_linenos(data[:numbers])
+ html << '</pre></td>'
+ html << "<td class=\"lines\"><pre><code>"
+ html << wrap_lines(data[:code])
+ html << '</code></pre></td>'
+ html << '</tbody></table>'
+ html << '</div>' unless @nowrap
+ html
+ end
+
+ def process_tokens(tokens)
+ num_lines = 0
+ last_val = ''
+ rendered = ''
+
+ tokens.each do |tok, val|
+ last_val = val
+ num_lines += val.scan(/\n/).size
+ rendered << span(tok, val)
+ end
+
+ numbers = (@linenostart..num_lines + @linenostart - 1).to_a
+
+ { numbers: numbers, code: rendered }
+ end
+
+ def wrap_linenos(numbers)
+ if @anchorlinenos
+ numbers.map! do |number|
+ "<a href=\"##{@lineanchorsid}#{number}\">#{number}</a>"
+ end
+ end
+ numbers.join("\n")
+ end
+
+ def wrap_lines(rendered)
+ if @lineanchors
+ lines = rendered.split("\n")
+ lines = lines.each_with_index.map do |line, index|
+ number = index + @linenostart
+
+ if @linenos == 'inline'
+ "<a name=\"L#{number}\"></a>" \
+ "<span class=\"linenos\">#{number}</span>" \
+ "<span id=\"#{@lineanchorsid}#{number}\" class=\"line\">#{line}" \
+ '</span>'
+ else
+ "<span id=\"#{@lineanchorsid}#{number}\" class=\"line\">#{line}" \
+ '</span>'
+ end
+ end
+ lines.join("\n")
+ else
+ if @linenos == 'inline'
+ lines = rendered.split("\n")
+ lines = lines.each_with_index.map do |line, index|
+ number = index + @linenostart
+ "<span class=\"linenos\">#{number}</span>#{line}"
+ end
+ lines.join("\n")
+ else
+ rendered
+ end
+ end
+ end
+
+ def span(tok, val)
+ # http://stackoverflow.com/a/1600584/2587286
+ val = CGI.escapeHTML(val)
+
+ if tok.shortname.empty?
+ val
+ else
+ if @inline_theme
+ rules = @inline_theme.style_for(tok).rendered_rules
+ "<span style=\"#{rules.to_a.join(';')}\">#{val}</span>"
+ else
+ "<span class=\"#{tok.shortname}\">#{val}</span>"
+ end
+ end
+ end
+ end
+ end
+end