From 55e51cbe6f78317594ae4b26bb9b93cf0306a695 Mon Sep 17 00:00:00 2001 From: gryf Date: Sat, 25 Dec 2010 15:19:08 +0100 Subject: [PATCH] Pygments sourcecode directive improvements --- README.rst | 72 ++++++++-- doc/vimblogger_ft.txt | 131 ++++++++++++------ ftplugin/rst/rst2blogger/main.py | 11 ++ ftplugin/rst/rst2blogger/rest.py | 50 ++++++- ftplugin/rst/rst2blogger/scripts/style2css.py | 73 ++++++++++ ftplugin/rst/rst2blogger/tests/test_rest.py | 122 ++++++++++++++++ ftplugin/rst/vimblogger_ft.vim | 11 +- 7 files changed, 413 insertions(+), 57 deletions(-) create mode 100755 ftplugin/rst/rst2blogger/scripts/style2css.py diff --git a/README.rst b/README.rst index dfab1ee..20d3614 100644 --- a/README.rst +++ b/README.rst @@ -4,11 +4,11 @@ vimblogger_ft ============= -vimblogger_ft is a simple reStructuredText_ to Blogger Interface through VIm_. +vimblogger_ft is a simple reStructuredText_ to Blogger interface through VIm_. -As the name suggest it is a filetype plugin, which helps to create blog articles -in rsST format and send them to blog site. It also provides commands for preview -in browser and delete articles. +As the name suggest it is a filetype plugin, which helps to create blog +articles in rsST format and send them to blog site. It also provides commands +for preview in browser and delete articles. Requirements ------------ @@ -22,7 +22,7 @@ Other requirements: - gdata_ - docutils_ - - pygments_ (optional) + - Pygments_ (optional) - Blogger account @@ -33,7 +33,6 @@ Download_, edit the vba with VIm and type:: :so % - Or, clone this repository and put files in your ``~/.vim`` directory. Usage @@ -93,7 +92,7 @@ reST document structure It is assumed, that following template will be used:: :Id: - :Title: Title for the blog + :Title: Title for the blog article :Date: :Modified: :Tags: some, tags @@ -146,15 +145,62 @@ Note, that ``.. more`` will became HTML comment ```` which will prevent from displaying entire post on the bloggers front page, but will not have any visible effect during preview in browser. -Additionally, if pygments is installed, there is sourcecode directive, -simple syntax highlighter using Pygments module. Very simple usage could -be as follows:: +Pygments code highlighting +-------------------------- + +Additionally, if Pygments is installed, there is ``sourcecode`` directive, +simple syntax highlighter using Pygments module. Very simple usage for Python +code could be as follows:: .. sourcecode:: python import vim print vim.current.buffer.name +Note, that ``sourcecode`` directive requires argument with the name of the +lexer to use. If wrong/non existent lexer is provided, it will fall back to +*text* lexer. For more information about available lexers, please refer to +Pygments documentation. + +Directive ``sourcecode`` supports two options: ``:linenos:`` and +``:cssclass:``. + +``:linenos:`` takes zero or one argument - if no arguments is provided, line +numbers will be visible starting form 1. Provided integer will be the number +of the first line. + +``:cssclass:`` can be use for changing default class name for block of code. +Default class can be changed by appropriate option for plugin (see +documentation), and defaults to "highlight". + +It is possible to use VIm colorschemes like desert (which is distributed with +VIm), Zenburn_, Lucius_, Wombat_, inkpot_ or any other with Pygments. +Assuming, that colorscheme *desert* should be used, there are two steps to +achive it. + +First, python module containing Pygments *Style* class has to be generated. +There is apropriate convertion tool in Pygments distribution - +``scripts/vim2pygments.py``. Uage is simple as:: + + python Pygments/scripts/vim2pygments.py [path/to/vim/colors]/desert.vim > desert.py + +Which will create new python module ``desert.py`` containing class +``DessertStyle``. + +To generate CSS stylesheet, it's enough to:: + + python rst2blogger/scripts/style2css.py desert.py -c VimDesert > desert.css + +VimDesert is the name of the class, which passed as an argument to +``:cssclass:`` option of directive ``sourceocode``. It will be used as a main +CSS class for code top ``
`` element. So, above example will looks like +this:: + + .. sourcecode:: python + :cssclass: VimDesert + + import vim + print vim.current.buffer.name Note: All headings for generated HTML by ``:SendBlogArticle`` will be shifted by 3, so the first heading will become

, second

and so @@ -165,6 +211,10 @@ limitation. .. _VIm: http://www.vim.org .. _gdata: http://code.google.com/p/gdata-python-client .. _docutils: http://docutils.sourceforge.net -.. _pygments: http://pygments.org +.. _Pygments: http://pygments.org .. _reStructuredText: http://docutils.sourceforge.net/rst.html .. _Download: http://www.vim.org/scripts/script.php?script_id=3367 +.. _Zenburn: http://www.vim.org/scripts/script.php?script_id=415 +.. _inkpot: http://www.vim.org/scripts/script.php?script_id=1143 +.. _Lucius: http://www.vim.org/scripts/script.php?script_id=2536 +.. _Wombat: http://www.vim.org/scripts/script.php?script_id=1778 diff --git a/doc/vimblogger_ft.txt b/doc/vimblogger_ft.txt index 81b3075..90e368d 100644 --- a/doc/vimblogger_ft.txt +++ b/doc/vimblogger_ft.txt @@ -1,4 +1,4 @@ -*vimblogger_ft.txt* reStructuredText to Blogger Interface +*vimblogger_ft.txt* reStructuredText to Blogger interface Author: Roman Dobosz, gryf73 at gmail com Simple interface to create blog articles in rsST format. It provides @@ -15,7 +15,7 @@ Other requirements: - Python (tested with version 2.6, should work also in others) - gdata http://code.google.com/p/gdata-python-client - docutils http://docutils.sourceforge.net - - pygments http://pygments.org (optional) + - Pygments http://pygments.org (optional) - Blogger account ----------------------------------------------------------------------- @@ -44,8 +44,8 @@ When article is done, |:SendBlogArticle| will send it to the server. Output provided by |:PreviewBlogArticle| without any css stylesheet will look pretty raw, so it is generally good idea to -grab stylesheets from blog itself, and tweak it a little, and add to -list in |g:blogger_stylesheets|. They will be automatically linked to +grab stylesheets from blog itself, tweak it a little, and add to list +in |g:blogger_stylesheets|. They will be automatically linked to generated preview file. Unfortunately, this script has several limitations, like it is @@ -110,6 +110,12 @@ g:blogger_stylesheets (default: []) one wanted to save stylesheets from his own blog, so that article can be displayed almost in the same way as in blog. + *g:blogger_pygments_class* +g:blogger_pygments_class (default: "") + + Name of default CSS class for usage with Pygments. When not + provided or empty, it'll use defaults from Pygments: "highlight". + ======================================================================== Commands~ @@ -136,33 +142,32 @@ Commands~ reST document structure~ It is assumed, that following template will be used: - ------8<----- -:Id: -:Title: Title for the blog -:Date: -:Modified: -:Tags: some, tags - -Penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla -facilisis massa ut massa. Sed nisi purus, malesuada eu, porta vulputate, -suscipit auctor, nunc. Vestibulum convallis, augue eu luctus malesuada, -mi ante mattis odio, ac venenatis neque sem vitae nisi. - -.. more - - -heading -------- - -**Congue** mi, quis posuere augue nulla a augue. Pellentesque sed est. -Mauris cursus urna id lectus. Integer dignissim feugiat eros. Sed tempor -volutpat dolor. Vestibulum vel lectus nec mauris semper adipiscing. - -Aliquam tincidunt enim sit amet tellus. Sed mauris nulla, semper -tincidunt, luctus a, sodales eget, leo. Sed ligula augue, cursus et. ------>8----- - +> + :Id: + :Title: Title for the blog article + :Date: + :Modified: + :Tags: some, tags + + Penatibus et magnis dis parturient montes, nascetur ridiculus mus. + Nulla facilisis massa ut massa. Sed nisi purus, malesuada eu, porta + vulputate, suscipit auctor, nunc. Vestibulum convallis, augue eu luctus + malesuada, mi ante mattis odio, ac venenatis neque sem vitae nisi. + + .. more + + + heading + ------- + + **Congue** mi, quis posuere augue nulla a augue. Pellentesque sed est. + Mauris cursus urna id lectus. Integer dignissim feugiat eros. Sed + tempor volutpat dolor. Vestibulum vel lectus nec mauris semper + adipiscing. + + Aliquam tincidunt enim sit amet tellus. Sed mauris nulla, semper + tincidunt, luctus a, sodales eget, leo. Sed ligula augue, cursus et. +< reST document (optionally) starts with *docinfo* section (first several lines, that are starting from ":" character) separaded from other content with one empty line. @@ -203,18 +208,65 @@ Note, that `.. more' will became HTML comment `' which will prevent from displaying entire post on the bloggers front page, but will not have any visible effect during preview in browser. -Additionally, if pygments is installed, there is sourcecode directive, -simple syntax highlighter using Pygments module. Very simple usage could -be as follows: +======================================================================== +Pygments code highlighting~ ------8<----- -.. sourcecode:: python +Additionally, if Pygments is installed, there is ``sourcecode`` +directive, simple syntax highlighter using Pygments module. Very simple +usage for Python code could be as follows: +> + .. sourcecode:: python + + import vim + print vim.current.buffer.name +< +Note, that `sourcecode' directive requires argument with the name of the +lexer to use. If wrong/non existent lexer is provided, it will fall back +to `text' lexer. For more information about available lexers, please +refer to Pygments documentation. - import vim - print vim.current.buffer.name +Directive `sourcecode' supports two options: `:linenos:' and +`:cssclass:'. ------>8----- +`:linenos:' takes zero or one argument - if no arguments is provided, +line numbers will be visible starting form 1. Provided integer will be +the number of the first line. +`:cssclass:' can be use for changing default class name for block of +code. Default class can be changed by appropriate option for plugin +(see documentation), and defaults to "highlight". + +It is possible to use VIm colorschemes like desert (which is distributed +with VIm), Zenburn, Lucius, Wombat, inkpot or any other with Pygments. +Assuming, that colorscheme `desert' should be used, there are two steps +to achive it. + +First, python module containing Pygments `Style' class has to be +generated. There is appropriate conversion tool in Pygments +distribution - `scripts/vim2pygments.py'. Uage is simple as: +> + python Pygments/scripts/vim2pygments.py \ + path/to/vim/colors/desert.vim > desert.py +< +Which will create new python module ``desert.py`` containing class +`DessertStyle`'. + +To generate CSS stylesheet, it's enough to: +> + python rst2blogger/scripts/style2css.py desert.py \ + -c VimDesert > desert.css +< +VimDesert is the name of the class, which passed as an argument to +`:cssclass:' option of directive `sourceocode'. It will be used as a +main CSS class for code top `
' element. So, above example will +looks like this: +> + .. sourcecode:: python + :cssclass: VimDesert + + import vim + print vim.current.buffer.name +< Note: All headings for generated HTML by |:SendBlogArticle| will be shifted by 3, so the first heading will become

, second

and so on, to fit into blogger template (well, most of them). Remember, that @@ -224,6 +276,7 @@ limitation. ======================================================================== Changelog~ +0.2 Pygments sourcecode directive improvements 0.1 First release vim:tw=72:fo=tcq2:isk=!-~,^*,^|,^":ts=8:ft=help:norl: diff --git a/ftplugin/rst/rst2blogger/main.py b/ftplugin/rst/rst2blogger/main.py index b2acde6..2229edf 100644 --- a/ftplugin/rst/rst2blogger/main.py +++ b/ftplugin/rst/rst2blogger/main.py @@ -13,6 +13,11 @@ from xml.parsers.expat import ExpatError import vim from rst2blogger.rest import blogPreview, blogArticleString +try: + from rst2blogger.rest import register +except ImportError: + pass + from rst2blogger.blogger import VimBlogger @@ -35,6 +40,12 @@ class Rst2Blogger(object): self.maxarticles = int(vim.eval("g:blogger_maxarticles")) self.confirm_del = int(vim.eval("g:blogger_confirm_del")) self.stylesheets = vim.eval("g:blogger_stylesheets") + self.pygments_class = vim.eval("g:blogger_pygments_class") + print self.pygments_class + try: + register(self.pygments_class) + except NameError: + pass def preview(self): """ diff --git a/ftplugin/rst/rst2blogger/rest.py b/ftplugin/rst/rst2blogger/rest.py index d5cabe4..84e79c9 100644 --- a/ftplugin/rst/rst2blogger/rest.py +++ b/ftplugin/rst/rst2blogger/rest.py @@ -17,6 +17,32 @@ try: from pygments.lexers import get_lexer_by_name, TextLexer from pygments.formatters import HtmlFormatter + def register(cssclass=None): + print "register" + if cssclass: + Pygments.cssclass = cssclass + directives.register_directive('sourcecode', Pygments) + + def _positive_int_or_1(argument): + """ + Converts the argument into an integer. Returns positive integer. In + case of integers smaller than 1, returns 1. In case of None, returns + 1. + """ + if argument is None: + return 1 + + retval = 1 + try: + retval = int(argument) + except ValueError: + pass + + if retval < 1: + return 1 + + return retval + class Pygments(Directive): """ Source code syntax highlighting. @@ -24,7 +50,11 @@ try: required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True + option_spec = {'linenos': _positive_int_or_1, + 'cssclass': directives.unchanged_required} has_content = True + cssclass = None + def run(self): self.assert_has_content() @@ -33,12 +63,26 @@ try: except ValueError: # no lexer found - use the text one instead of an exception lexer = TextLexer() + # take an arbitrary option if more than one is given - formatter = HtmlFormatter(noclasses=True) + kwargs = {'full': False, + 'noclasses': False} + + if self.options and 'linenos' in self.options: + kwargs['linenos'] = 'inline' + kwargs['linenostart'] = self.options['linenos'] + + if Pygments.cssclass: + kwargs['cssclass'] = Pygments.cssclass + + if self.options and 'cssclass' in self.options: + kwargs['cssclass'] = self.options['cssclass'] + + formatter = HtmlFormatter(**kwargs) + parsed = highlight(u'\n'.join(self.content), lexer, formatter) return [nodes.raw('', parsed, format='html')] - - directives.register_directive('sourcecode', Pygments) + register() except ImportError: pass diff --git a/ftplugin/rst/rst2blogger/scripts/style2css.py b/ftplugin/rst/rst2blogger/scripts/style2css.py new file mode 100755 index 0000000..b639c87 --- /dev/null +++ b/ftplugin/rst/rst2blogger/scripts/style2css.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +""" +Generate CSS stylesheet out of provided Style class module, which is an output +from the vim2pygments.py[1] script. + +That stylesheet (with any necessary, additional modifications) can be used +with vimblogger_ft[2] VIm plugin, but also for general pygments usage. + +Usage: + style2css + +[1] vim2pygments is part of the Pygments module, and can be found in scripts + directory of the Pygments distribution. +[2] +""" + +import optparse +import os + +from pygments.formatters import HtmlFormatter + + +class Pygments2CSS(object): + def __init__(self, modulefn, cssclass): + self.style_class = None + self.cssclass = cssclass + if not self.cssclass.startswith("."): + self.cssclass = "." + self.cssclass + + mod = os.path.splitext(os.path.basename(modulefn))[0] + + try: + module = __import__("%s" % mod) + except ImportError: + print('Error: %s should be in PYTHONPATH or current' + ' directory, and should contain valid Style derived' + ' class' % modulefn) + raise + + for item in dir(module): + if item != 'Style' and item.endswith('Style'): + self.style_class = getattr(module, item) + break + else: + raise ValueError("Error: Wrong module?") + + def out(self): + formatter = HtmlFormatter(style=self.style_class) + print "%s { background-color: %s }" % \ + (self.cssclass, self.style_class.background_color) + for line in formatter.get_style_defs().split("\n"): + print "%s" % self.cssclass, line + +if __name__ == "__main__": + parser = optparse.OptionParser("usage: %prog [options] stylefile.py\n" + "Where stylefile.py is a product of the" + " vim2pygments.py script Pygments " + "distribution.") + parser.add_option("-c", "--class", + dest="cssclass", + type="str", + help="Main CSS class name. Defaults to 'syntax'", + default="syntax") + (options, args) = parser.parse_args() + + if len(args) != 1: + parser.error("stylefile.py is required") + + if not (os.path.exists(args[0]) and os.path.isfile(args[0])): + parser.error("%s not found" % args[0]) + + p2css = Pygments2CSS(args[0], options.cssclass) + p2css.out() diff --git a/ftplugin/rst/rst2blogger/tests/test_rest.py b/ftplugin/rst/rst2blogger/tests/test_rest.py index aade474..275d87b 100644 --- a/ftplugin/rst/rst2blogger/tests/test_rest.py +++ b/ftplugin/rst/rst2blogger/tests/test_rest.py @@ -11,6 +11,69 @@ sys.path.insert(0, this_dir) from rst2blogger.rest import blogArticleString, blogPreview from rst2blogger.tests.shared import REST_ARTICLE +LINENOS1 = """ + .. sourcecode:: python + :linenos: + + import vim + print vim.current.buffer.name + +""" + +LINENOS2 = """ + .. sourcecode:: python + :linenos: -1 + + import vim + print vim.current.buffer.name + +""" + +LINENOS3 = """ + .. sourcecode:: python + :linenos: 0 + + import vim + print vim.current.buffer.name + +""" + +LINENOS4 = """ + .. sourcecode:: python + :linenos: 12 + + import vim + print vim.current.buffer.name + +""" + +LINENOS5 = """ + .. sourcecode:: python + :linenos: this is wrong + + import vim + print vim.current.buffer.name + +""" + +CSSCLASS1 = """ + .. sourcecode:: python + :cssclass: + + import vim + print vim.current.buffer.name + +""" + +CSSCLASS2 = """ + .. sourcecode:: python + :cssclass: Dessert256 + + import vim + print vim.current.buffer.name + +""" + class TestBlogPreview(unittest.TestCase): """ @@ -75,5 +138,64 @@ class TestBlogArticleString(unittest.TestCase): self.assertEqual(attrs['date'], "2010-12-12T12:36:36+01:00") self.assertEqual(attrs['tags'], "this is a test, Blogger, rest") + +class TestBlogArticlePytgments(unittest.TestCase): + """ + Test cases for sourcecode directive + """ + def test_linenos_no_args(self): + """ + Test linenos option with no additional arguments + """ + html_out, _ = blogArticleString(LINENOS1) + self.assertTrue('
1' in html_out)
+
+    def test_linenos_with_arg1(self):
+        """
+        Test linenos option with correct argument type but wrong value.
+        Should count from 1 in this case.
+        """
+        html_out, _ = blogArticleString(LINENOS2)
+        self.assertTrue('
1' in html_out)
+
+    def test_linenos_with_arg2(self):
+        """
+        Test linenos option with correct argument type but wrong value.
+        Should count from 1 in this case.
+        """
+        html_out, _ = blogArticleString(LINENOS3)
+        self.assertTrue('
1' in html_out)
+
+    def test_linenos_with_arg3(self):
+        """
+        Test linenos option with correct argument type and correct value.
+        Should count from 1 in this case.
+        """
+        html_out, _ = blogArticleString(LINENOS4)
+        self.assertTrue('
12' in html_out)
+
+    def test_linenos_with_wrong_arg(self):
+        """
+        Test linenos option with wrong argument type. Should count from 1.
+        """
+        html_out, _ = blogArticleString(LINENOS5)
+        self.assertTrue('
1' in html_out)
+
+    def test_cssclass_failure(self):
+        """
+        Test cssclass option with no arguments. Should complain with system
+        message.
+        """
+        html_out, _ = blogArticleString(CSSCLASS1)
+        self.assertTrue('System Message: ERROR/3' in html_out)
+
+    def test_cssclass_correct(self):
+        """
+        Test cssclass option with Dessert256 as an argument. Should be used as
+        a main div CSS class.
+        """
+        html_out, _ = blogArticleString(CSSCLASS2)
+        self.assertTrue('
' in html_out) + if __name__ == "__main__": unittest.main() diff --git a/ftplugin/rst/vimblogger_ft.vim b/ftplugin/rst/vimblogger_ft.vim index 137b2ce..52ab961 100644 --- a/ftplugin/rst/vimblogger_ft.vim +++ b/ftplugin/rst/vimblogger_ft.vim @@ -1,6 +1,7 @@ " reST to blogger vim interface. " Provide some convinient commands for creating preview from the reST file " and to send articles to blog. +" VERSION: 0.2 if exists("b:did_rst_plugin") finish " load only once @@ -44,6 +45,10 @@ if !exists("g:blogger_stylesheets") let g:blogger_stylesheets = [] endif +if !exists("g:blogger_pygments_class") + let g:blogger_pygments_class = "" +endif + python << EOF import os import sys @@ -53,10 +58,8 @@ import vim scriptdir = os.path.dirname(vim.eval('expand("")')) sys.path.insert(0, scriptdir) -try: - from rst2blogger.main import Rst2Blogger -except ImportError: - print "Plugin vimblogger cannot be loaded, due to lack of required modules" +# Will raise exception, if one of required moudles is missing +from rst2blogger.main import Rst2Blogger EOF if !exists(":PreviewBlogArticle")