Начав писать топик про мои извращения с TeX-ом понял, что очень недостаёт нормальной подсветки синтаксиса. Гугление по Хабру и окрестностям навело на пару редакторов, которые у меня не заработали и описание подрихтованного formatter-а для pygments.
Решив «а чем я хуже», набросал «на коленке» скрипт на python-е, который мне код-то и раскрашивает.
Вскрытие показало, чтопациент умер от вскрытия использовать найденный на Хабре код без доработок не получится — то ли заточен под старую версию pygments, то ли ещё что. В общем, полез я в документацию и в первом же примере мне попался HTML 3.2 Formatter весьма похожий на найденный ранее.
Сравнение показало, что оба formatter-а если не близнецы, то братья — по новым правилам обязательным является определение метода format, которым не пользовался уважаемый barbuza, ну и пришлось поправить Хабра-специфичные теги.
Дальше, вооружившись питоном с регэкспами, решил: буду обрабатывать текст и подменять в нём блоки с кодом, оформленным по аналогии с ХабраРедактор-ом на раскрашенный код:
Т.е. всё, что внутнри блока, ограниченного code с единственным атрибутом class прогоняем через подходящий lexer pygments.
На выходе получаем наш текст, в котором все подобные блоки раскрашены в соответствии с выбранным стилем pygments.
Исправление форматтера состояло из 3-х частей — замены обычных строк на юникодные, добавление функции экранировки HTML символов из блога barbuza с небольшой правкой и добавление «тупой» функции упаковки кодов цвета из 6 символов в 3, если позволяет цвет.
Осталось написать кусок, который будет вызавать наш форматтер и задавать ему дополнительные параметры.
Тут, для облегчения жизни, нам пригодится модуль optparse для разбора параметров командной строки:
Вот, собственно, и всё. Осталось прочитать указанный файл, обработать его на предмет раскраски кода и записать в указанный файл или на экран, если выходной файл не указан. Попахивает быдлокодом, но оптимизировать элементарно лень. Поскольку уже есть мысли подоработке всего этого безобразия — рефакторинг отложим на потом.
В итоге я получил работоспособную консольную утилиту, позволяющую мне «лёгким движением руки» © раскрашивать исхоный код статей, набранных в Far-e, GEdit-e или Midnight Commandere.
Исходный код целиком (с некоторым количеством «мусора») доступен по адресу dumpz.org/17521.
Уже сейчас видятся некоторые направления развития:
ЗЫ: Этот текст подготовлен в GEdit и раскрашен при помощи habra-colorer-а со стилем default
ЗЫЫ: В процессе написания выяснил, что Хабр «не любит» одинокую цифру «0» внутри тегов font. Пришлось обмануть добавлением хитрого пробела с кодом ‌.
Решив «а чем я хуже», набросал «на коленке» скрипт на python-е, который мне код-то и раскрашивает.
Вскрытие показало, что
Сравнение показало, что оба formatter-а если не близнецы, то братья — по новым правилам обязательным является определение метода format, которым не пользовался уважаемый barbuza, ну и пришлось поправить Хабра-специфичные теги.
Обрабатываем код
Дальше, вооружившись питоном с регэкспами, решил: буду обрабатывать текст и подменять в нём блоки с кодом, оформленным по аналогии с ХабраРедактор-ом на раскрашенный код:
Здесь идёт какой-то текст
<code class="python"><!-- здесь не должно быть ничего, кроме пробелов -->
#!/usr/bin/env python
import sys
</code><!-- здесь не должно быть ничего, кроме пробелов -->
Т.е. всё, что внутнри блока, ограниченного code с единственным атрибутом class прогоняем через подходящий lexer pygments.
def preparse_text(text, linenos=False, style=None):
"""Extract code blocks from raw text, render via pygments and return as unicode string"""
R = re.compile(ur'^\s*<code class="(?P<class>.*?)">\s*$(?P<code>.*?)^\s*</code>\s*$', re.I|re.U|re.S|re.M)
out = []
prev = 0
ar = {'linenos':False}
if linenos:
ar['linenos']='inline'
if style:
ar['style'] = style
for s in R.finditer(text):
fmt = OldHtmlFormatter(**ar)
out.append(text[prev:s.start()])
lx = get_lexer_by_name(s.group('class'))
if not lx:
lx = guess_lexer(s.group('code'))
if lx:
s0 = s.group('code')
s0 = s0.replace(u' ', u'\u00a0') #
src = highlight(s0, lx, fmt) # .replace(u'\n', '<br/>\n') # for preview
else:
src = u'<code>%s</code>' % s.group('code')
out.append(src)
prev = s.end()
del lx
del fmt
lx = None
out.append(text[prev:])
return u''.join(out)
На выходе получаем наш текст, в котором все подобные блоки раскрашены в соответствии с выбранным стилем pygments.
Исправление форматтера состояло из 3-х частей — замены обычных строк на юникодные, добавление функции экранировки HTML символов из блога barbuza с небольшой правкой и добавление «тупой» функции упаковки кодов цвета из 6 символов в 3, если позволяет цвет.
Консольная утилита
Осталось написать кусок, который будет вызавать наш форматтер и задавать ему дополнительные параметры.
Тут, для облегчения жизни, нам пригодится модуль optparse для разбора параметров командной строки:
# Создаём парсера и заполняем допустимыми опциями командной строки
p = OptionParser(usage='usage: %prog [options] input_file')
p.add_option('-f','--file', metavar="FILE", help="Write output to FILE")
p.add_option('-s','--style', metavar="STYLE",help="Use color STYLE for formatting")
p.add_option('--htm','--html', action="store_true", help="Add extra html headers in output")
p.add_option('--list-styles', action="store_true", help="Show list of supported styles")
p.add_option('--list-languages', action="store_true", help="Show list of supported languages")
# Собственно обработка командной строки, опции в 'op', имя входного файла в 'a[0]'
op,a = p.parse_args()
# попросили список стилей - нате вам, и выход
if op.list_styles:
from pygments.styles import get_all_styles
print "Supported color styles:"
for s in get_all_styles():
print u"\t%s" % (s,)
sys.exit(0)
# попросили список языков - тоже нате :)
if op.list_languages:
from pygments.lexers import get_all_lexers
print "Supported languages and aliases:"
ss = list(get_all_lexers())
ss.sort(key=lambda x:x[0].lower())
for s in ss:
print s[0]
if s[1]:
print "\t", ", ".join(s[1])
sys.exit(0)
# если нечего обрабатывать, то и работать не будем
if len(a) != 1:
print "No input file specified!"
sys.exit(1)
Вот, собственно, и всё. Осталось прочитать указанный файл, обработать его на предмет раскраски кода и записать в указанный файл или на экран, если выходной файл не указан. Попахивает быдлокодом, но оптимизировать элементарно лень. Поскольку уже есть мысли подоработке всего этого безобразия — рефакторинг отложим на потом.
srcfile = a[0]
dstfile = op.file
f = unicode(open(srcfile, 'rb').read(), 'utf-8', 'replace') # Таки я уверен, что входной файл в utf8 ^)
s = preparse_text(f, False, op.style).encode('utf-8') # Обработали
if dstfile:
fn = open(dstfile, 'wb')
else:
fn = sys.stdout
# если просят HTML заголовок для предросмотра в браузере -- их есть у нас
if op.htm:
fn.write('<html><head><meta http-equiv="content-type" content="text/html; charset=\'utf8\'"/></head><body>\n')
fn.write(s)
if op.htm:
fn.write('</body></html>\n')
try:
close(fn) # У stdout нет метода close, игнорируем эту ошибку
except:
pass
Итог
В итоге я получил работоспособную консольную утилиту, позволяющую мне «лёгким движением руки» © раскрашивать исхоный код статей, набранных в Far-e, GEdit-e или Midnight Commandere.
Исходный код целиком (с некоторым количеством «мусора») доступен по адресу dumpz.org/17521.
TODO
Уже сейчас видятся некоторые направления развития:
- «Причесать» раскрашивающую часть — добавить возможность вывода номеров строк и автоматическую экранировку вложенных кодов (сейчас мне пришлось добавить комментарии после тега code в первом примере
- Есть мысль сделать GUI на PyQt с предпросмотром a'la Хабр
- Можно нарисовать пару-тройку собственных стилей, благо это довольно просто.
Использованные материалы
- Хабрахабр
- Блог хабраюзера barbuza в качестве идеи и функции экранировки HTML символов
- ХабраРедактор От SoftCoder-а
- Python 2.6
- Pygments
- Сайт раскраски исходных кодов s-c.me
- Сайт расшаривания дампов dumpz.org
ЗЫ: Этот текст подготовлен в GEdit и раскрашен при помощи habra-colorer-а со стилем default
ЗЫЫ: В процессе написания выяснил, что Хабр «не любит» одинокую цифру «0» внутри тегов font. Пришлось обмануть добавлением хитрого пробела с кодом ‌.