From f736721c5bb8f1f6031d36cc726cbd11cc499a72 Mon Sep 17 00:00:00 2001 From: Stefan Tatschner Date: Sun, 28 Jun 2015 20:45:40 +0200 Subject: Replace Rugments with Rouge I have mainly created the rugments fork for the purpose of improving gitlab's highlighting. Nowadays IMO it works way better than the old highlight.js solution. But the development is stuck on my side because of a couple of personal reasons: * I have finished my studies; last months I was writing my master thesis. So there was a huge time problem. I am sorry for that. * I had to move to Munich due to getting a (paid) job. Searching a flat here is horrible... :) * Last but not least, maintaining the same code base in two seperate projects is a mess. I have decided to switch back to rouge due to several reasons: * In the beginning I was quite motivated, but since I start working on my new job next week, the best solution IMO is switching back to upstream rouge. * Rouge is continously improving: https://github.com/jneen/rouge/blob/master/CHANGELOG.md http://rouge.jneen.net/ * There should be absolutely no regressions with this change. Most likely this pull request will almost fix some minor bugs. * One less gem in gitlab is a good thing. since Gitlab is quite a huge bundle of gems. Reducing complexity should be a major milestone. Thanks a lot to @stanhu and @jneen for the review! --- lib/redcarpet/render/gitlab_html.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb index 2f7aff03c2a..04440e4f68d 100644 --- a/lib/redcarpet/render/gitlab_html.rb +++ b/lib/redcarpet/render/gitlab_html.rb @@ -22,10 +22,10 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML ERB::Util.html_escape_once(text) end - # Stolen from Rugments::Plugins::Redcarpet as this module is not required - # from Rugments's gem root. + # Stolen from Rouge::Plugins::Redcarpet as this module is not required + # from Rouge's gem root. def block_code(code, language) - lexer = Rugments::Lexer.find_fancy(language, code) || Rugments::Lexers::PlainText + lexer = Rouge::Lexer.find_fancy(language, code) || Rouge::Lexers::PlainText # XXX HACK: Redcarpet strips hard tabs out of code blocks, # so we assume you're not using leading spaces that aren't tabs, @@ -34,7 +34,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML code.gsub!(/^ /, "\t") end - formatter = Rugments::Formatters::HTML.new( + formatter = Rouge::Formatters::HTMLGitlab.new( cssclass: "code highlight #{@color_scheme} #{lexer.tag}" ) formatter.format(lexer.lex(code)) -- cgit v1.2.3 From 00ff84d3c02e551cb7c4be3f71e1836e9f5abce2 Mon Sep 17 00:00:00 2001 From: Stefan Tatschner Date: Sun, 28 Jun 2015 20:42:41 +0200 Subject: 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! --- lib/rouge/formatters/html_gitlab.rb | 168 ++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 lib/rouge/formatters/html_gitlab.rb (limited to 'lib') 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 Rouge::Formatter::HTMLGitlab instance. + # + # [+nowrap+] If set to True, don't wrap the output at all, not + # even inside a
 tag (default: false).
+      # [+cssclass+]        CSS class for the wrapping 
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
 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 
+      #                     tags. Used in combination with linenos and lineanchors
+      #                     (default: false).
+      # [+inline_theme+]    Inline CSS styles for the 
 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 << "
" unless @nowrap
+        html << wrap_lines(data[:code])
+        html << "
\n" unless @nowrap + html + end + + def render_tableized(tokens) + data = process_tokens(tokens) + + html = '' + html << "
" unless @nowrap + html << '' + html << "' + html << "' + html << '
"
+        html << wrap_linenos(data[:numbers])
+        html << '
"
+        html << wrap_lines(data[:code])
+        html << '
' + html << '
' 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| + "
#{number}" + 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' + "" \ + "#{number}" \ + "#{line}" \ + '' + else + "#{line}" \ + '' + 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 + "#{number}#{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 + "#{val}" + else + "#{val}" + end + end + end + end + end +end -- cgit v1.2.3