読者です 読者をやめる 読者になる 読者になる

SilverCityで構文のカラーリングを行う(上)

Python

 ソースコードの構文カラーリングをPythonで行いたいと思い、まずSilverCityを調べてみました。SilverCityはTracでも使われているので、手堅いライブラリかと思ったのですが・・・。


 最新版(バージョン0.96)を試しているのですが、ドキュメントが不十分であったり、コードが必要以上に複雑、冗長であったり、などなど、ちょっと不満の残るライブラリです。しかし、今のところ、代替ライブラリが見つからないので、なんとか快適に使えるように頑張ってみます。

とにかく使ってみる

 SilverCityでは、HTMLGeneratorというクラス(のサブクラス)が、ソースコードをHTMLに変換する処理を行っています。SilverCitySilverCityのドキュメントには、HTMLジェネレーターを明示的にインポートして、HTMLに変換する例が掲載されています。
 しかし、ユーザー入力やファイル拡張子から言語名を判断して、実行時に使用するHTMLジェネレーターを決定したいというケースも多いと思います。SilverCity.LanguageInfoモジュールのfind_generator_by_name関数を使うと、これを実現できます。
 使用例を示します。

# -*- coding: utf-8 -*-
from cStringIO import StringIO
from SilverCity.LanguageInfo import find_generator_by_name

g = find_generator_by_name('ruby')

writer = StringIO()
g().generate_html(writer, "puts 'Hello'")

print writer.getvalue()


 後で述べるように、PythonコードのHTMLジェネレーターにはバグがあるので、あえて、rubyを例に使っています。出力結果は次のようになります。

<span class="p_identifier">puts</span>
<span class="p_default">&nbsp;</span>
<span class="p_tripledouble">\'Hello\'</span>


 構文をカラーリングを行うには、スタイルシートを記述する必要がありますが、SilverCityにはデフォルトのスタイルシートが添付されていますので、それを利用することもできます。標準スタイルシートのファイル名は、次のようにして調べることができます。

>>> SilverCity.get_default_stylesheet_location()
'/usr/local/lib/python2.5/site-packages/SilverCity/default.css'


 find_generator_by_name関数には、次の言語を指定することができます。(SilverCityと、その背後あるScintillaがサポートしている言語は、もっと多く、手動でLexerを作れば、これ以外の言語の構文解析を行うことができます。)

cpp, css, html, perl, python, ruby, sql, yaml, xml, xslt


 しかし、バージョン0.96では、このうちcss, python, sqlは正しく動作しません。プロジェクトのバグトラッキングにも報告されているので、次のバージョンでは修正されるのではないかと思いますが、今のところ、これらの言語のHTMLジェネレーターを使うにはモジュールにパッチを当てるか、手動でLexerを作り、HTMLへの変換を行うようにする必要があります。

--- Python.py	2003-12-07 21:32:22.000000000 +0900
+++ Python.py.new	2006-11-19 03:24:41.000000000 +0900
@@ -11,7 +11,8 @@
         self._properties = properties
         self._lexer = find_lexer_module_by_id(SCLEX_PYTHON)
         self._keyword_lists = [
-            WordList(Keywords.python_keywords)
+            WordList(Keywords.python_keywords),
+            WordList()
                             ]
 
 class PythonHandler(DispatchHandler):
--- CSS.py	2003-12-07 18:17:06.000000000 +0900
+++ CSS.py.new	2006-11-19 03:25:04.000000000 +0900
@@ -12,6 +12,7 @@
         self._lexer = find_lexer_module_by_id(SCLEX_CSS)
         self._keyword_lists = [
             WordList(Keywords.css_keywords),
+            WordList(),
             WordList(Keywords.css_keywords_2),
                                ]
             
--- SQL.py	2003-12-06 23:25:38.000000000 +0900
+++ SQL.py.new	2006-11-19 03:25:14.000000000 +0900
@@ -12,6 +12,8 @@
         self._lexer = find_lexer_module_by_id(SCLEX_SQL)
         self._keyword_lists = [
             WordList(Keywords.sql_keywords),
+            WordList(), WordList(), WordList(), WordList(),
+            WordList(), WordList(), WordList()
                                ]
             
 class SQLHandler(DispatchHandler):


 これで、PythonのHTMLジェネレーターも動くようになりました。手元にあったPythonソースコードをSilverCityを使ってHTMLに変換し、デフォルトのスタイルシートを適用してみました。

SilverCityの標準HTMLジェネレーターの問題点

 SilverCityの標準HTMLジェネレーターは非常に便利なのですが、個人的にはいくつか不満があります。

  1. 出力されるHTML要素のクラス属性は、Cの予約語ならばc_identifier, Python予約語ならばp_identifierといった具合に、冗長です。CSSを簡潔に記述したい場合は、これは望ましいことではありません。
  2. サブクラスを定義する以外に、出力されるHTML要素のクラス属性を変更する手段がありません。
  3. URLやRFCなどの文字列は、自動的にaタグに変換されてしまいます。
  4. 空白は&nbsp;に、改行は
    (スラッシュの前にスペースはなし)に自動的に変換されます。しかも、空白文字にもspanタグを使ってCSSクラスが割り当てられます。これは、preタグを使っているときには望ましい動作ではないかもしれません。

 幸い、標準HTMLジェネレーターを使わずに、Lexerを使って自力でソースコードからHTMLを生成することも簡単にできます。


 「SilverCityで構文のカラーリングを行う(下)」に続く。