Class: Nanoc::Filters::ColorizeSyntax

Inherits:
Nanoc::Filter show all
Defined in:
lib/nanoc/filters/colorize_syntax.rb

Constant Summary

DEFAULT_COLORIZER =

The default colorizer to use for a language if the colorizer for that language is not overridden.

:coderay
SIMON_HIGHLIGHT_OPT_MAP =
{
    :wrap => '-W',
    :include_style => '-I',
    :line_numbers  => '-l',
}

Constants inherited from Nanoc::Filter

Nanoc::Filter::TMP_BINARY_ITEMS_DIR

Instance Attribute Summary

Attributes inherited from Nanoc::Filter

#assigns

Instance Method Summary (collapse)

Methods inherited from Nanoc::Filter

#depend_on, #filename, from_binary?, #initialize, #output_filename, requires, setup, #setup_and_run, to_binary?, type

Methods included from PluginRegistry::PluginMethods

#all, #identifier, #identifiers, #named, #register

Methods inherited from Context

#get_binding, #initialize

Constructor Details

This class inherits a constructor from Nanoc::Filter

Instance Method Details

- (String) coderay(code, language, params = {})

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Runs the code through CodeRay.

Parameters:

  • code (String)

    The code to colorize

  • language (String)

    The language the code is written in

  • params (Hash) (defaults to: {})

    Parameters to pass on to CodeRay

Returns:

  • (String)

    The colorized output



157
158
159
160
161
# File 'lib/nanoc/filters/colorize_syntax.rb', line 157

def coderay(code, language, params = {})
  require 'coderay'

  ::CodeRay.scan(code, language).html(params)
end

- (Object) coderay_postprocess(language, element)

Wraps the element in <div class="CodeRay"><div class="code">



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/nanoc/filters/colorize_syntax.rb', line 277

def coderay_postprocess(language, element)
  # Skip if we're a free <code>
  return if element.parent.nil?

  # <div class="code">
  div_inner = Nokogiri::XML::Node.new('div', element.document)
  div_inner['class'] = 'code'
  div_inner.children = element.dup

  # <div class="CodeRay">
  div_outer = Nokogiri::XML::Node.new('div', element.document)
  div_outer['class'] = 'CodeRay'
  div_outer.children = div_inner

  # orig element
  element.swap div_outer
end

- (String) dummy(code, language, params = {})

Returns the input itself, not performing any code highlighting.

Parameters:

  • code (String)

    The code to colorize

  • language (String)

    The language the code is written in (unused)

Returns:

  • (String)

    The colorized output, which is identical to the input in this case



171
172
173
# File 'lib/nanoc/filters/colorize_syntax.rb', line 171

def dummy(code, language, params = {})
  code
end

- (String) pygmentize(code, language, params = {})

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Runs the content through pygmentize, the commandline frontend for Pygments.

Parameters:

  • code (String)

    The code to colorize

  • language (String)

    The language the code is written in

  • params (Hash) (defaults to: {})

    a customizable set of options

Options Hash (params):

  • :encoding (String, Symbol)

    The encoding of the code block

Returns:

  • (String)

    The colorized output



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/nanoc/filters/colorize_syntax.rb', line 187

def pygmentize(code, language, params = {})
  require 'systemu'
  check_availability('pygmentize', '-V')

  params[:encoding] ||= 'utf-8'
  params[:nowrap]   ||= 'True'

  # Build command
  cmd = [ 'pygmentize', '-l', language, '-f', 'html' ]
  cmd << '-O' << params.map { |k, v| "#{k}=#{v}" }.join(',') unless params.empty?

  # Run command
  stdout = StringIO.new
  systemu cmd, 'stdin' => code, 'stdout' => stdout

  # Get result
  stdout.rewind
  stdout.read
end

- (String) pygmentsrb(code, language, params = {})

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Runs the content through Pygments via pygments.rb.

Parameters:

  • code (String)

    The code to colorize

  • language (String)

    The language the code is written in

Returns:

  • (String)

    The colorized output



217
218
219
220
221
222
223
224
225
226
227
# File 'lib/nanoc/filters/colorize_syntax.rb', line 217

def pygmentsrb(code, language, params = {})
  require 'pygments'

  args = params.dup
  args[:lexer] ||= language
  args[:options] ||= {}
  args[:options][:encoding] ||= 'utf-8'
  args[:options][:nowrap]   ||= 'True'

  Pygments.highlight(code, args)
end

- (String) run(content, params = {})

Syntax-highlights code blocks in the given content. Code blocks should be enclosed in pre elements that contain a code element. The code element should have an indication of the language the code is in. There are two possible ways of adding such an indication:

  1. A HTML class starting with language- and followed by the code language, as specified by HTML5. For example, <code class="language-ruby">.

  2. A comment on the very first line of the code block in the format #!language where language is the language the code is in. For example, #!ruby.

Options for individual colorizers will be taken from the #run options’ value for the given colorizer. For example, if the filter is invoked with a :coderay => coderay_options_hash option, the coderay_options_hash hash will be passed to the CodeRay colorizer.

Currently, the following colorizers are supported:

Additional colorizer implementations are welcome!

Examples:

Using a class to indicate type of code be highlighted


<pre><code class="language-ruby">
def foo
  "asdf"
end
</code></pre>

Using a comment to indicate type of code be highlighted


<pre><code>
#!ruby
def foo
  "asdf"
end
</code></pre>

Invoking the filter with custom parameters


filter :colorize_syntax,
       :colorizers => { :ruby => :coderay },
       :coderay    => { :line_numbers => :list }

Parameters:

  • content (String)

    The content to filter

  • params (Hash) (defaults to: {})

    a customizable set of options

Options Hash (params):

  • :default_colorizer (Symbol) — default: DEFAULT_COLORIZER

    The default colorizer, i.e. the colorizer that will be used when the colorizer is not overriden for a specific language.

  • :syntax (Symbol) — default: :html

    The syntax to use, which can be :html, :xml or :xhtml, the latter two being the same.

  • :colorizers (Hash) — default: {}

    A hash containing a mapping of programming languages (symbols, not strings) onto colorizers (symbols).

  • :outside_pre (Boolean) — default: false

    true if the colorizer should be applied on code elements outside pre elements, false if only code elements inside pre elements should be colorized.

  • :is_fullpage (Symbol) — default: false

    Whether to treat the input as a full HTML page or a page fragment. When true, HTML boilerplate such as the doctype, html, head and body elements will be added.

Returns:

  • (String)

    The filtered content



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/nanoc/filters/colorize_syntax.rb', line 85

def run(content, params = {})
  # Take colorizers from parameters
  @colorizers = Hash.new(params[:default_colorizer] || DEFAULT_COLORIZER)
  (params[:colorizers] || {}).each_pair do |language, colorizer|
    @colorizers[language] = colorizer
  end

  # Determine syntax (HTML or XML)
  syntax = params[:syntax] || :html
  case syntax
  when :html
    klass = Nokogiri::HTML
  when :xml, :xhtml
    klass = Nokogiri::XML
  else
    raise "unknown syntax: #{syntax.inspect} (expected :html or :xml)"
  end

  # Colorize
  is_fullpage = params.fetch(:is_fullpage) { false }
  doc = is_fullpage ? klass.parse(content, nil, 'UTF-8') : klass.fragment(content)
  selector = params[:outside_pre] ? 'code' : 'pre > code'
  doc.css(selector).each do |element|
    # Get language
    has_class = false
    language = nil
    if element['class']
      # Get language from class
      match = element['class'].match(/(^| )language-([^ ]+)/)
      language = match[2] if match
      has_class = true if language
    else
      # Get language from comment line
      match = element.inner_text.strip.split[0].match(/^#!([^\/][^\n]*)$/)
      language = match[1] if match
      element.content = element.content.sub(/^#!([^\/][^\n]*)$\n/, '') if language
    end

    # Give up if there is no hope left
    next if language.nil?

    # Highlight
    raw = strip(element.inner_text)
    highlighted_code = highlight(raw, language, params)
    element.children = Nokogiri::HTML.fragment(strip(highlighted_code), 'utf-8')

    # Add language-something class
    unless has_class
      klass = element['class'] || ''
      klass << ' ' unless [' ', nil].include?(klass[-1, 1])
      klass << "language-#{language}"
      element['class'] = klass
    end

    highlight_postprocess(language, element.parent)
  end

  method = "to_#{syntax}".to_sym
  doc.send(method, :encoding => 'UTF-8')
end

- (String) simon_highlight(code, language, params = {})

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Runs the content through Highlight.

Parameters:

  • code (String)

    The code to colorize

  • language (String)

    The language the code is written in

  • params (Hash) (defaults to: {})

    a customizable set of options

Options Hash (params):

  • :style (String)

    The style to use

Returns:

  • (String)

    The colorized output

Since:

  • 3.2.0



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/nanoc/filters/colorize_syntax.rb', line 248

def simon_highlight(code, language, params = {})
  require 'systemu'

  check_availability('highlight', '--version')

  # Build command
  cmd = [ 'highlight', '--syntax', language, '--fragment' ]
  params.each do |key, value|
    if SIMON_HIGHLIGHT_OPT_MAP[key]
      cmd << SIMON_HIGHLIGHT_OPT_MAP[key]
    else
      # TODO allow passing other options
      case key
      when :style
        cmd << '--style' << params[:style]
      end
    end
  end

  # Run command
  stdout = StringIO.new
  systemu cmd, 'stdin' => code, 'stdout' => stdout

  # Get result
  stdout.rewind
  stdout.read
end