mirror of
https://github.com/gryf/jekyll-rst.git
synced 2025-12-19 20:38:06 +01:00
Initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.pyc
|
||||||
20
LICENSE.txt
Normal file
20
LICENSE.txt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
Copyright (c) 2011 Greg Thornton, http://xdissent.com
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
72
README.rst
Normal file
72
README.rst
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
Overview
|
||||||
|
========
|
||||||
|
|
||||||
|
This plugin adds `ReStructuredText`_ support to `Jekyll`_ and `Octopress`_.
|
||||||
|
It renders ReST in posts and pages, and provides a custom directives to
|
||||||
|
support Octopress-compatible syntax highlighting.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
============
|
||||||
|
|
||||||
|
* Jekyll *or* Octopress >= 2.0
|
||||||
|
* Docutils
|
||||||
|
* Pygments
|
||||||
|
* `RbST`_
|
||||||
|
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
1. Install Docutils and Pygments.
|
||||||
|
|
||||||
|
The most convenient way is to use virtualenv_burrito:
|
||||||
|
|
||||||
|
.. sourcecode:: console
|
||||||
|
|
||||||
|
$ curl -s https://raw.github.com/brainsik/virtualenv-burrito/master/virtualenv-burrito.sh | bash
|
||||||
|
$ source /Users/xdissent/.venvburrito/startup.sh
|
||||||
|
$ mkvirtualenv jekyll-rst
|
||||||
|
$ pip install docutils pygments
|
||||||
|
|
||||||
|
2. Install RbST.
|
||||||
|
|
||||||
|
If you use `bundler`_ with Octopress, add ``gem 'RbST'`` to
|
||||||
|
your ``Gemfile`` in the ``development`` group, then run
|
||||||
|
``bundle install``. Otherwise, ``gem install RbST``.
|
||||||
|
|
||||||
|
3. Install the plugin.
|
||||||
|
|
||||||
|
For Jekyll:
|
||||||
|
|
||||||
|
.. sourcecode:: console
|
||||||
|
|
||||||
|
$ cd <jekyll-project-path>
|
||||||
|
$ git submodule add https://github.com/xdissent/jekyll-rst.git _plugins/jekyll-rst
|
||||||
|
|
||||||
|
For Octopress:
|
||||||
|
|
||||||
|
.. sourcecode:: console
|
||||||
|
|
||||||
|
$ cd <octopress-project-path>
|
||||||
|
$ git submodule add https://github.com/xdissent/jekyll-rst.git plugins/jekyll-rst
|
||||||
|
|
||||||
|
4. Start blogging in ReStructuredText. Any file with the ``.rst`` extension
|
||||||
|
will be parsed as ReST and rendered into HTML.
|
||||||
|
|
||||||
|
.. note:: Be sure to activate the ``jekyll-rst`` virtualenv before generating
|
||||||
|
the site by issuing a ``workon jekyll-rst``. I suggest you follow `Harry
|
||||||
|
Marr's advice`_ and create a ``.venv`` file that will automatically
|
||||||
|
activate the ``jekyll-rst`` virtualenv when you ``cd`` into your project.
|
||||||
|
|
||||||
|
Octopress Tips
|
||||||
|
==============
|
||||||
|
|
||||||
|
* Use ``.. more`` in your ReST documents to indicate where Octopress's
|
||||||
|
``excerpt`` tag should split your content for summary views.
|
||||||
|
|
||||||
|
|
||||||
|
.. _ReStructuredText: http://docutils.sourceforge.net/rst.html
|
||||||
|
.. _Jekyll: http://jekyllrb.com/
|
||||||
|
.. _Octopress: http://octopress.com/
|
||||||
|
.. _RbST: http://rubygems.org/gems/RbST
|
||||||
|
.. _bundler: http://gembundler.com/
|
||||||
|
.. _Harry Marr's advice: http://hmarr.com/2010/jan/19/making-virtualenv-play-nice-with-git/
|
||||||
30
converter.rb
Normal file
30
converter.rb
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
require 'rbst'
|
||||||
|
|
||||||
|
module Jekyll
|
||||||
|
class RestConverter < Converter
|
||||||
|
safe true
|
||||||
|
|
||||||
|
priority :low
|
||||||
|
|
||||||
|
def matches(ext)
|
||||||
|
ext =~ /rst/i
|
||||||
|
end
|
||||||
|
|
||||||
|
def output_ext(ext)
|
||||||
|
".html"
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert(content)
|
||||||
|
RbST.executables = {:html => "#{File.expand_path(File.dirname(__FILE__))}/rst2html.py"}
|
||||||
|
RbST.new(content).to_html(:part => :fragment, :initial_header_level => 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Filters
|
||||||
|
def restify(input)
|
||||||
|
site = @context.registers[:site]
|
||||||
|
converter = site.getConverterImpl(Jekyll::RestConverter)
|
||||||
|
converter.convert(input)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
97
directives.py
Normal file
97
directives.py
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# Define a new directive `code-block` (aliased as `sourcecode`) that uses the
|
||||||
|
# `pygments` source highlighter to render code in color.
|
||||||
|
#
|
||||||
|
# Incorporates code from the `Pygments`_ documentation for `Using Pygments in
|
||||||
|
# ReST documents`_ and `Octopress`_.
|
||||||
|
#
|
||||||
|
# .. _Pygments: http://pygments.org/
|
||||||
|
# .. _Using Pygments in ReST documents: http://pygments.org/docs/rstdirective/
|
||||||
|
# .. _Octopress: http://octopress.org/
|
||||||
|
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import md5
|
||||||
|
import __main__
|
||||||
|
|
||||||
|
# Absolute path to pygments cache dir
|
||||||
|
PYGMENTS_CACHE_DIR = os.path.abspath(os.path.join(os.path.dirname(__main__.__file__), '../../.pygments-cache'))
|
||||||
|
|
||||||
|
# Ensure cache dir exists
|
||||||
|
if not os.path.exists(PYGMENTS_CACHE_DIR):
|
||||||
|
os.mkdirs(PYGMENTS_CACHE_DIR)
|
||||||
|
|
||||||
|
from pygments.formatters import HtmlFormatter
|
||||||
|
|
||||||
|
from docutils import nodes
|
||||||
|
from docutils.parsers.rst import directives, Directive
|
||||||
|
|
||||||
|
from pygments import highlight
|
||||||
|
from pygments.lexers import get_lexer_by_name, TextLexer
|
||||||
|
|
||||||
|
class Pygments(Directive):
|
||||||
|
""" Source code syntax hightlighting.
|
||||||
|
"""
|
||||||
|
required_arguments = 1
|
||||||
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = True
|
||||||
|
string_opts = ['title', 'url', 'caption']
|
||||||
|
option_spec = dict([(key, directives.unchanged) for key in string_opts])
|
||||||
|
has_content = True
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.assert_has_content()
|
||||||
|
try:
|
||||||
|
lexer_name = self.arguments[0]
|
||||||
|
lexer = get_lexer_by_name(lexer_name)
|
||||||
|
except ValueError:
|
||||||
|
# no lexer found - use the text one instead of an exception
|
||||||
|
lexer_name = 'text'
|
||||||
|
lexer = TextLexer()
|
||||||
|
formatter = HtmlFormatter()
|
||||||
|
|
||||||
|
# Construct cache filename
|
||||||
|
cache_file = None
|
||||||
|
content_text = u'\n'.join(self.content)
|
||||||
|
cache_file_name = '%s-%s.html' % (lexer_name, md5.new(content_text).hexdigest())
|
||||||
|
cached_path = os.path.join(PYGMENTS_CACHE_DIR, cache_file_name)
|
||||||
|
|
||||||
|
# Look for cached version, otherwise parse
|
||||||
|
if os.path.exists(cached_path):
|
||||||
|
cache_file = open(cached_path, 'r')
|
||||||
|
parsed = cache_file.read()
|
||||||
|
else:
|
||||||
|
parsed = highlight(content_text, lexer, formatter)
|
||||||
|
|
||||||
|
# Strip pre tag and everything outside it
|
||||||
|
pres = re.compile("<pre>(.+)<\/pre>", re.S)
|
||||||
|
stripped = pres.search(parsed).group(1)
|
||||||
|
|
||||||
|
# Create tabular code with line numbers
|
||||||
|
table = '<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers">'
|
||||||
|
lined = ''
|
||||||
|
for idx, line in enumerate(stripped.splitlines(True)):
|
||||||
|
table += '<span class="line-number">%d</span>\n' % (idx + 1)
|
||||||
|
lined += '<span class="line">%s</span>' % line
|
||||||
|
table += '</pre></td><td class="code"><pre><code class="%s">%s</code></pre></td></tr></table></div>' % (lexer_name, lined)
|
||||||
|
|
||||||
|
# Add wrapper with optional caption and link
|
||||||
|
code = '<figure class="code">'
|
||||||
|
if self.options:
|
||||||
|
caption = ('<span>%s</span>' % self.options['caption']) if 'caption' in self.options else ''
|
||||||
|
title = self.options['title'] if 'title' in self.options else 'link'
|
||||||
|
link = ('<a href="%s">%s</a>' % (self.options['url'], title)) if 'url' in self.options else ''
|
||||||
|
|
||||||
|
if caption or link:
|
||||||
|
code += '<figcaption>%s %s</figcaption>' % (caption, link)
|
||||||
|
code += '%s</figure>' % table
|
||||||
|
|
||||||
|
# Write cache
|
||||||
|
if cache_file is None:
|
||||||
|
cache_file = open(cached_path, 'w')
|
||||||
|
cache_file.write(parsed)
|
||||||
|
cache_file.close()
|
||||||
|
|
||||||
|
return [nodes.raw('', code, format='html')]
|
||||||
|
|
||||||
|
directives.register_directive('code-block', Pygments)
|
||||||
|
directives.register_directive('sourcecode', Pygments)
|
||||||
39
rst2html.py
Normal file
39
rst2html.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# :Author: David Goodger, the Pygments team, Guenter Milde
|
||||||
|
# :Date: $Date: $
|
||||||
|
# :Copyright: This module has been placed in the public domain.
|
||||||
|
|
||||||
|
# This is a merge of the `Docutils`_ `rst2html` front end with an extension
|
||||||
|
# suggestion taken from the `Pygments`_ documentation, reworked specifically
|
||||||
|
# for `Octopress`_.
|
||||||
|
#
|
||||||
|
# .. _Pygments: http://pygments.org/
|
||||||
|
# .. _Docutils: http://docutils.sourceforge.net/
|
||||||
|
# .. _Octopress: http://octopress.org/
|
||||||
|
|
||||||
|
"""
|
||||||
|
A front end to docutils, producing HTML with syntax colouring using pygments
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
import locale
|
||||||
|
locale.setlocale(locale.LC_ALL, '')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
from transform import transform
|
||||||
|
from docutils.writers.html4css1 import Writer
|
||||||
|
from docutils.core import default_description
|
||||||
|
from directives import Pygments
|
||||||
|
|
||||||
|
description = ('Generates (X)HTML documents from standalone reStructuredText '
|
||||||
|
'sources. Uses `pygments` to colorize the content of'
|
||||||
|
'"code-block" directives. Needs an adapted stylesheet'
|
||||||
|
+ default_description)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
return transform(writer=Writer(), part='html_body')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print(main())
|
||||||
40
transform.py
Normal file
40
transform.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import sys
|
||||||
|
from docutils.core import publish_parts
|
||||||
|
from optparse import OptionParser
|
||||||
|
from docutils.frontend import OptionParser as DocutilsOptionParser
|
||||||
|
from docutils.parsers.rst import Parser
|
||||||
|
|
||||||
|
def transform(writer=None, part=None):
|
||||||
|
p = OptionParser(add_help_option=False)
|
||||||
|
|
||||||
|
# Collect all the command line options
|
||||||
|
docutils_parser = DocutilsOptionParser(components=(writer, Parser()))
|
||||||
|
for group in docutils_parser.option_groups:
|
||||||
|
p.add_option_group(group.title, None).add_options(group.option_list)
|
||||||
|
|
||||||
|
p.add_option('--part', default=part)
|
||||||
|
|
||||||
|
opts, args = p.parse_args()
|
||||||
|
|
||||||
|
settings = dict({
|
||||||
|
'file_insertion_enabled': False,
|
||||||
|
'raw_enabled': False,
|
||||||
|
}, **opts.__dict__)
|
||||||
|
|
||||||
|
if len(args) == 1:
|
||||||
|
try:
|
||||||
|
content = open(args[0], 'r').read()
|
||||||
|
except IOError:
|
||||||
|
content = args[0]
|
||||||
|
else:
|
||||||
|
content = sys.stdin.read()
|
||||||
|
|
||||||
|
parts = publish_parts(
|
||||||
|
source=content,
|
||||||
|
settings_overrides=settings,
|
||||||
|
writer=writer,
|
||||||
|
)
|
||||||
|
|
||||||
|
if opts.part in parts:
|
||||||
|
return parts[opts.part]
|
||||||
|
return ''
|
||||||
Reference in New Issue
Block a user