Code plugin refactoring

- Unified handling of key, value options
- Added url, link_text, and title options
- Gists can now accept start, end, range, title, url, link_text, linenos and marks options
- Code accessibility improvements (hiding line numbers) #864
This commit is contained in:
Brandon Mathis 2012-12-23 00:38:01 -06:00
parent be443098d6
commit 0fcbc225e4
6 changed files with 156 additions and 167 deletions

View File

@ -125,17 +125,12 @@ figure.code {
overflow-x: auto;
// allows line number to be read by screen readers but won't be selected when copying code
[data-line]:before {
content: attr(data-line);
font-size: 0;
line-height: 0;
color: transparent;
}
[data-line]:before { content: attr(data-line); }
td {
line-height: 1.45em;
font-size: 13px;
div { padding: .8em; }
pre { padding: .8em; }
}
.main {

View File

@ -12,22 +12,28 @@ module BacktickCodeBlock
markup = $1 || ''
code = $2.to_s
linenos = get_linenos(markup)
markup = replace_linenos(markup)
marks = get_marks(markup)
markup = replace_marks(markup)
start = get_start(markup)
markup = replace_start(markup)
options = parse_markup(markup)
@lang = options[:lang]
@title = options[:title]
@lineos = options[:lineos]
@marks = options[:marks]
@url = options[:url]
@link_text = options[:link_text]
@start = options[:start]
markup = clean_markup(markup)
if markup =~ AllOptions
highlight(code, $1, {caption: $2, url: $3, anchor: $4 || 'Link', linenos: linenos, start: start, marks: marks})
@lang ||= $1
@title ||= $2
@url ||= $3
@link_text ||= $4
elsif markup =~ LangCaption
highlight(code, $1, {caption: $2 || nil, linenos: linenos, start: start, marks: marks})
@lang ||= $1
@title ||= $2
else
highlight(code, 'plain', {linenos: linenos, start: start})
@lang = 'plain'
end
highlight(code, @lang, {title: @title, url: @url, link_text: @link_text, linenos: @linenos, marks: @marks, start: @start })
end
end
end

View File

@ -1,6 +1,6 @@
# Title: Simple Code Blocks for Jekyll
# Author: Brandon Mathis http://brandonmathis.com
# Description: Write codeblocks with semantic HTML5 <figure> and <figcaption> elements and optional syntax highlighting — all with a simple, intuitive interface.
# Description: Write codeblocks with semantic HTML5 <figure> and <figtitle> elements and optional syntax highlighting — all with a simple, intuitive interface.
#
# Syntax:
# {% codeblock [title] [url] [link text] %}
@ -25,7 +25,7 @@
# Output:
#
# <figure class='code'>
# <figcaption><span>Got pain? painrelief.sh</span> <a href="http://site.com/painrelief.sh">Download it!</a>
# <figtitle><span>Got pain? painrelief.sh</span> <a href="http://site.com/painrelief.sh">Download it!</a>
# <div class="highlight"><pre><code class="sh">
# -- nicely escaped highlighted code --
# </code></pre></div>
@ -48,44 +48,42 @@ module Jekyll
class CodeBlock < Liquid::Block
include HighlightCode
CaptionUrlTitle = /(\S[\S\s]*)\s+(https?:\/\/)(\S+)\s+(.+)/i
CaptionUrl = /(\S[\S\s]*)\s+(https?:\/\/)(\S+)/i
Caption = /(\S[\S\s]*)/
TitleUrlLinkText = /(\S[\S\s]*)\s+(https?:\/\/)(\S+)\s+(.+)/i
TitleUrl = /(\S[\S\s]*)\s+(https?:\/\/)(\S+)/i
Title = /(\S[\S\s]*)/
def initialize(tag_name, markup, tokens)
@caption = nil
@url = nil
@title = nil
@lang = get_lang(markup)
markup = replace_lang(markup)
options = parse_markup(markup)
@lang = options[:lang]
@title = options[:title]
@lineos = options[:lineos]
@marks = options[:marks]
@url = options[:url]
@link_text = options[:link_text]
@start = options[:start]
markup = clean_markup(markup)
@linenos = get_linenos(markup)
markup = replace_linenos(markup)
@marks = get_marks(markup)
markup = replace_marks(markup)
@start = get_start(markup)
markup = replace_start(markup)
if markup =~ CaptionUrlTitle
@caption = $1
@url = $2 + $3
@anchor = $4
elsif markup =~ CaptionUrl
@caption = $1
@url = $2 + $3
elsif markup =~ Caption
@caption = $1
if markup =~ TitleUrlLinkText
@title ||= $1
@url ||= $2 + $3
@link_text ||= $4
elsif markup =~ TitleUrl
@title ||= $1
@url ||= $2 + $3
elsif markup =~ Title
@title ||= $1
end
if @caption =~ /\S[\S\s]*\w+\.(\w+)/ && @lang.nil?
@lang = $1
# grab lang from filename in title
if @title =~ /\S[\S\s]*\w+\.(\w+)/ && @lang.nil?
@lang ||= $1
end
super
end
def render(context)
code = super.strip
code = highlight(code, @lang, {caption: @caption, url: @url, anchor: @anchor, start: @start, marks: @marks, linenos: @linenos})
code = highlight(code, @lang, {title: @title, url: @url, link_text: @link_text, start: @start, marks: @marks, linenos: @linenos})
code = context['pygments_prefix'] + code if context['pygments_prefix']
code = code + context['pygments_suffix'] if context['pygments_suffix']
code

View File

@ -15,18 +15,42 @@ require './plugins/pygments_code'
module Jekyll
class GistTag < Liquid::Tag
include HighlightCode
def initialize(tag_name, text, token)
def initialize(tag_name, markup, token)
super
@text = text
@cache_disabled = false
@cache_folder = File.expand_path "../.gist-cache", File.dirname(__FILE__)
options = parse_markup(markup)
@lang = options[:lang]
@title = options[:title]
@lineos = options[:lineos]
@marks = options[:marks]
@url = options[:url]
@link_text = options[:link_text]
@start = options[:start]
@end = options[:end]
@markup = clean_markup(markup)
FileUtils.mkdir_p @cache_folder
end
def render(context)
if parts = @text.match(/([\d]*) (.*)/)
if parts = @markup.match(/([\d]*) (.*)/)
gist, file = parts[1].strip, parts[2].strip
get_cached_gist(gist, file) || get_gist_from_web(gist, file)
code = get_cached_gist(gist, file) || get_gist_from_web(gist, file)
length = code.lines.count
@end ||= length
return "#{file} is #{length} lines long, cannot begin at line #{@start}" if @start > length
return "#{file} is #{length} lines long, cannot read beyond line #{@end}" if @end > length
if @start > 1 or @end < length
code = code.split(/\n/).slice(@start -1, @end + 1 - @start).join("\n")
end
lang = file.empty? ? @lang || '' : file.split('.')[-1]
link = "https://gist.github.com/#{gist}"
title = file.empty? ? "Gist: #{gist}" : file
highlight(code, lang, { title: @title || title, url: link, link_text: @link_text || 'Gist page', marks: @marks, linenos: @linenos, start: @start })
else
""
end
@ -71,18 +95,15 @@ module Jekyll
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new raw_uri.request_uri
data = https.request request
lang = file.empty? ? '' : file.split('.')[-1]
link = "https://gist.github.com/#{gist}"
caption = file.empty? ? "Gist: #{gist}" : file
data = highlight(data.body, lang, { linenos: true, start: 1, caption: caption, url: link, anchor: 'Gist page' })
cache gist, file, data unless @cache_disabled
code = data.body.to_s
data
cache gist, file, code unless @cache_disabled
code
end
end
class GistTagNoCache < GistTag
def initialize(tag_name, text, token)
def initialize(tag_name, markup, token)
super
@cache_disabled = true
end

View File

@ -10,14 +10,14 @@
#
# This will import test.js from source/downloads/code/javascripts/test.js
# and output the contents in a syntax highlighted code block inside a figure,
# with a figcaption listing the file name and download link
# with a figtitle listing the file name and download link
#
# Example 2:
# You can also include an optional title for the <figcaption>
# You can also include an optional title for the <figtitle>
#
# {% include_code javascripts/test.js Example 2 %}
#
# will output a figcaption with the title: Example 2 (test.js)
# will output a figtitle with the title: Example 2 (test.js)
#
require './plugins/pygments_code'
@ -32,31 +32,22 @@ module Jekyll
@title = nil
@title_old = nil
@lang = get_lang(markup)
markup = replace_lang(markup)
@linenos = get_linenos(markup)
markup = replace_linenos(markup)
@marks = get_marks(markup)
markup = replace_marks(markup)
@start = get_start(markup)
markup = replace_start(markup)
@end = get_end(markup)
markup = replace_end(markup)
range = get_range(markup, @start, @end)
@start = range[:start]
@end = range[:end]
markup = replace_range(markup)
options = parse_markup(markup)
@lang = options[:lang]
@title = options[:title]
@lineos = options[:lineos]
@marks = options[:marks]
@url = options[:url]
@link_text = options[:link_text]
@start = options[:start]
@end = options[:end]
markup = clean_markup(markup)
if markup.strip =~ /(^\S*\.\S+) *(.+)?/i
@file = $1
@title = $2 || nil
@title ||= $2
elsif markup.strip =~ /(.*?)(\S*\.\S+)\Z/i # Title before file is deprecated in 2.1
@title_old = $1 || nil
@title_old = $1
@file = $2
end
super
@ -68,7 +59,7 @@ module Jekyll
file = code_path + @file
unless @title_old.nil?
@title = @title_old
@title ||= @title_old
puts "### ------------ WARNING ------------ ###"
puts "This include_code syntax is deprecated "
puts "Correct syntax: path/to/file.ext [title]"
@ -89,7 +80,8 @@ module Jekyll
Dir.chdir(code_path) do
code = file.read
length = code.lines.count
@end ||= length
@end ||= length
@start ||= 1
return "#{file} is #{length} lines long, cannot begin at line #{@start}" if @start > length
return "#{file} is #{length} lines long, cannot read beyond line #{@end}" if @end > length
if @start > 1 or @end < length
@ -98,7 +90,7 @@ module Jekyll
@lang = file.extname.sub('.','') unless @lang
title = @title ? "#{@title} (#{file.basename})" : file.basename
url = "/#{code_dir}/#{@file}"
highlight(code, @lang, {caption: title, url: url, anchor: 'view raw', start: @start, marks: @marks, linenos: @linenos })
highlight(code, @lang, {title: title, url: url, link_text: @link_text || 'view raw', start: @start, marks: @marks, linenos: @linenos })
end
end
end

View File

@ -38,13 +38,13 @@ module HighlightCode
lang = 'csharp' if lang == 'cs'
lang = 'plain' if lang == '' or lang.nil? or !lang
caption = options[:caption] || nil
url = options[:url] || nil
anchor = options[:anchor] || nil
wrap = options[:wrap] || true
marks = options[:marks]
linenos = options[:linenos]
start = options[:start]
url = options[:url] || nil
title = options[:title] || (url ? ' ' : nil)
link_text = options[:link_text] || nil
wrap = options[:wrap] || true
marks = options[:marks]
linenos = options[:linenos]
start = options[:start] || 1
if lang == 'plain'
# Escape html tags
@ -58,45 +58,42 @@ module HighlightCode
end
code = tableize_code(code, lang, { linenos: linenos, start: start, marks: marks })
caption = captionize(caption, url, anchor) if caption
title = captionize(title, url, link_text) if title
figure = "<figure class='code'>#{caption}#{code}</figure>"
figure = "<figure class='code'>#{title}#{code}</figure>"
figure = safe_wrap(figure) if wrap
figure
end
def captionize (caption, url, anchor)
def captionize (caption, url, link_text)
figcaption = "<figcaption>#{caption}"
figcaption += "<a href='#{url}'>#{anchor.strip || 'link'}</a>" if url
figcaption += "<a href='#{url}'>#{(link_text || 'link').strip}</a>" if url
figcaption += "</figcaption>"
end
def tableize_code (code, lang, options = {})
start = options[:start]
lines = options[:linenos].nil? ? true : options[:linenos]
marks = options[:marks] || []
start = options[:start] || 1
lines = options[:linenos] || true
marks = options[:marks] || []
table = "<table class='highlight'>"
table += number_lines(start, code.lines.count, marks) if lines
table += "<td class='main #{'unnumbered' unless lines} #{lang}'><div>"
if marks.size
code.lines.each_with_index do |line,index|
classes = 'line'
if marks.include? index + start
classes += ' marked'
classes += ' start' unless marks.include? index - 1 + start
classes += ' end' unless marks.include? index + 1 + start
end
table += "<pre data-line='line #{index + start}' class='#{classes}'>#{line}</pre>"
table += "<td class='main #{'unnumbered' unless lines} #{lang}'><pre>"
code.lines.each_with_index do |line,index|
classes = 'line'
if marks.include? index + start
classes += ' marked'
classes += ' start' unless marks.include? index - 1 + start
classes += ' end' unless marks.include? index + 1 + start
end
else
table += code.gsub /^((.+)?(\n?))/, '<span class=\'line\'>\1</span>'
line = line.strip.empty? ? ' ' : line
table += "<div class='#{classes}'>#{line}</div>"
end
table +="</div></td></tr></table>"
table +="</pre></td></tr></table>"
end
def number_lines (start, count, marks)
start ||= 1
lines = "<td class='line-numbers' aria-hidden='true'><div>"
lines = "<td class='line-numbers' aria-hidden='true'><pre>"
count.times do |index|
classes = 'line-number'
if marks.include? index + start
@ -104,21 +101,44 @@ module HighlightCode
classes += ' start' unless marks.include? index - 1 + start
classes += ' end' unless marks.include? index + 1 + start
end
lines += "<pre class='#{classes}'>#{index + start}</pre>"
lines += "<div data-line='#{index + start}' class='#{classes}'></div>"
end
lines += "</div></td>"
lines += "</pre></td>"
end
def get_lang (input)
lang = nil
if input =~ /\s*lang:(\w+)/i
lang = $1
end
lang
def parse_markup (input)
lang = input.match(/\s*lang:(\w+)/i)
title = input.match(/\s*title:\s*(("(.+?)")|('(.+?)')|(\S+))/i)
linenos = input.match(/\s*linenos:(\w+)/i)
marks = get_marks(input)
url = input.match(/\s*url:\s*(("(.+?)")|('(.+?)')|(\S+))/i)
link_text = input.match(/\s*link_text:\s*(("(.+?)")|('(.+?)')|(\S+))/i)
start = input.match(/\s*start:(\d+)/i)
endline = input.match(/\s*end:(\d+)/i)
opts = {
lang: (lang.nil? ? nil : lang[1]),
title: (title.nil? ? nil : title[3] || title[5] || title[6]),
linenos: (linenos.nil? ? nil : linenos[1]),
marks: marks,
url: (url.nil? ? nil : url[3] || url[5] || url[6]),
start: (start.nil? ? nil : start[1].to_i),
end: (endline.nil? ? nil : endline[1].to_i),
link_text: (link_text.nil? ? nil : link_text[3] || link_text[5] || link_text[6])
}
opts.merge(get_range(input, opts[:start], opts[:end]))
end
def replace_lang (input)
input.sub(/ *lang:\w+/i, '')
def clean_markup (input)
input.sub(/\s*lang:\w+/i, ''
).sub(/\s*title:\s*(("(.+?)")|('(.+?)')|(\S+))/i, ''
).sub(/\s*url:(\S+)/i, ''
).sub(/\s*link_text:\s*(("(.+?)")|('(.+?)')|(\S+))/i, ''
).sub(/\s*mark:\d\S*/i,''
).sub(/\s*linenos:false/i,''
).sub(/\s*start:\d+/i,''
).sub(/\s*end:\d+/i,''
).sub(/\s*range:\d+-\d+/i,'')
end
def get_marks (input)
@ -135,46 +155,6 @@ module HighlightCode
marks
end
def replace_marks (input)
input.sub(/ *mark:\d\S*/i,'')
end
def get_linenos (input)
linenos = true
if input =~ / *linenos:false/i
linenos = false
end
linenos
end
def replace_linenos (input)
input.sub(/ *linenos:false/i,'')
end
def get_start (input)
start = 1
if input =~ / *start:(\d+)/i
start = $1.to_i
end
start
end
def replace_start (input)
input.sub(/ *start:\d+/i,'')
end
def get_end (input)
endline = nil
if input =~ / *end:(\d+)/i
endline = $1.to_i
end
endline
end
def replace_end (input)
input.sub(/ *end:\d+/i,'')
end
def get_range (input, start, endline)
if input =~ / *range:(\d+)-(\d+)/i
start = $1.to_i
@ -182,7 +162,4 @@ module HighlightCode
end
{start: start, end: endline}
end
def replace_range (input)
input.sub(/ *range:\d+-\d+/i,'')
end
end