Jekyll Source Code Plugin
I created the following Jekyll plugins to add support for code blocks with downloadable file link headers and custom titles.
To Use
Place the file in a "_code" post relative folder and the following in the post Front Matter.
source_codes:
- file: ../_code/source_code.rb
language: ruby
title: source_code.rb
- file: ../_code/generate_source_code.rb
language: ruby
title: generate_source_code.rb
- file: ../_code/redcarpet-custom.rb
language: ruby
title: redcarpet-custom.rb
# John Paul Newman
module Jekyll
class SourceCode < Liquid::Tag
include Jekyll::Converters::Markdown::RedcarpetParser::WithPygments
safe = true
def initialize(tag_name, source_codes_file_filter, tokens = nil)
super
@source_codes_file_filter = source_codes_file_filter.strip
end
def render(context)
unless context.registers[:page].key?('source_codes')
raise 'Please include source_codes in front matter'
end
page_path = context.registers[:page]['path']
page_path = page_path[0..-9] if page_path =~ %r{\/#excerpt$}
post_path = File.dirname(page_path)
sc = context.registers[:page]['source_codes'].find {|e| e['file'] == @source_codes_file_filter}
if sc.nil?
raise "Please include '#{@source_codes_file_filter}' file in source_codes within the front matter"
end
file_path = File.join(post_path, @source_codes_file_filter)
text = File.open(file_path).read
block_code(text, sc['language'], sc['file'], sc['title'])
end
end
end
Liquid::Template.register_tag('source_code', Jekyll::SourceCode)
# John Paul Newman
require 'fileutils'
module Jekyll
# Monkey-patch an accessor for a page's containing folder, since
# we need it to generate the source code.
class Page
def subfolder
@dir
end
end
# Sub-class Jekyll::StaticFile to allow recovery from unimportant exception
# when writing the source code file.
class StaticSourceCodeFile < StaticFile
def initialize(site, base, dir, name, collection = nil, sub_path = '_code')
super(site, base, dir, name, collection)
@sub_path = sub_path
end
def write(dest)
super(dest) rescue ArgumentError
true
end
def destination(dest)
File.join(dest, @sub_path, @name)
end
end
class SourceCodeGenerator < Generator
safe true
priority :low
def generate(site)
posts = site.site_payload['site']['posts']
posts.each do |post|
url = post.url
url = '/' + url unless url =~ %r{^\/}
url = url[0..-11] if url =~ %r{\/index.html$}
next unless post.populate_tags.key?('source_code')
post.populate_tags['source_code'].each do |sc|
file_name = File.basename(sc['file'])
file_subfolder = File.dirname(sc['file'])
target_subpath = File.join(File.dirname(url), file_subfolder)
target_path = File.join(site.config['destination'], target_subpath)
FileUtils.mkdir_p target_path unless File.directory?(target_path)
# source_file = File.join(File.dirname(post.path), sc['file'])
Jekyll.logger.debug 'SourceCodeGenerator: target_path', target_path
base_folder = File.join(site.source, '_posts')
site.static_files << Jekyll::StaticSourceCodeFile.new(site,
base_folder,
file_subfolder,
file_name,
nil,
target_subpath)
end
end
end
end
end
Based on plugin from http://manidesto.github.io/better-code-blocks-in-jekyll and modified to add code path and title.
# John Paul Newman
# Based On: http://manidesto.github.io/better-code-blocks-in-jekyll
module Jekyll
module Converters
class Markdown
class RedcarpetParser
module WithPygments
include CommonMethods
def block_code(code, lang, code_file_path=nil, title=nil)
require 'pygments'
lang = lang && lang.split.first || 'text'
options = { :encoding => 'utf-8',
:lineanchors =>'line' }
html = add_code_tags(
Pygments.highlight(code,
:lexer => lang,
:options => options),
lang
)
if lang == 'text'
output = html
else
require 'nokogiri'
html_doc = Nokogiri::HTML.fragment(html)
header_div = Nokogiri::XML::Node.new("div", html_doc)
header_div.content = lang
header_div.set_attribute('class', 'code-block-header')
header_div.content = ''
header_left_div = Nokogiri::XML::Node.new("div", html_doc)
header_left_div.set_attribute('class', 'code-block-header-left')
header_left_div.content = ''
header_div.add_child(header_left_div)
header_right_div = Nokogiri::XML::Node.new("div", html_doc)
header_right_div.set_attribute('class', 'code-block-header-right')
header_right_div.content = '-'
header_div.add_child(header_right_div)
title ||= lang
if code_file_path
a = Nokogiri::XML::Node.new("a", html_doc)
a.content = title
a.set_attribute('href', code_file_path)
header_left_div.add_child(a)
end
html_doc.first_element_child.first_element_child.add_previous_sibling(header_div)
output = html_doc.to_s
end
end
end
end
end
end
end