Zen Сoding — расширение написаное на Python и JavaScript для большинства текстовых редакторов и IDE, которое позволяет очень быстро писать HTML и CSS. С его помощью можно написав всего лишь html:5 получить готовый каркас HTML5 документа с доктайпом и шапкой. Охват Zen Coding’а — большое количество редакторов, но о текстовом редакторе Geany забыли.
Я занимаюсь веб‐разработкой, поэтому мне необходим Zen Coding для того, чтобы избавиться от рутины. Я пишу всё в Geany так как он очень лёгкий, быстрый, хорошо подсвечивает синтаксис, да и большего мне не нужно.
И после нескольких часов гугления я не нашёл необходимого плагина для Geany. Не долго думая, решил его написать сам.
Ну, началось…
Я скачал версию Zen Coding'а, написанную на питоне, а так же нашёл мануал по написанию плагинов для Geany. После некоторого времени изучения мануала по написанию плагинов я понял, что их необходимо писать на си, и начал думать, как привязать питон к си. На изучение сего вопроса ушло чуть больше суток.
В первую очередь я написал скрипт на питоне, который разворачивает аббревиатуру с помощью Zen Coding в строке. Через некоторое время получилось нечто вроде:
import sys
#пытаемся импортировать библиотеку Zen Coding
try:
from zencoding import zen_core
from zencoding.zen_settings import zen_settings
except:
print "error importing zen-codign library...
"
#объявляем класс для хранения значения позиции курсора
#не знаю почему, но не получилось заюзать просто переменную.
class ZObj:
def __init__(self):
self._o = -1
def set(self, i):
self._o = i
def get(self):
return self._o
zobj = ZObj()
#функция для разворачивания аббревиатур.
#в качестве первого агрумента передаётся вся строка с аббревиатурой
#в качестве второго агрумента передаётся текущая позиция курсора в редакторе
#и в качестве третьего агрумента передаётся mime-типа документа,
#text/html или text/css
def expand(line, currpos, mime):
try:
#сбрасываем значение переменной.
zobj.set(-1)
#находим аббревиатуру в строке
abbr = zen_core.find_abbr_in_line(line, currpos)
abbrlist = list(abbr)
if(abbrlist[0] == ''):
raise
etype = ''
if(mime == 'text/css'):
etype = 'css'
else:
etype = 'html'
#разворачиваем аббревиатуру
expanded = zen_core.expand_abbreviation(abbrlist[0], etype)
if(expanded == ''):
raise
#заменяем аббревиатуру в строке и вычисляем необходимую позицию курсора.
abrlen = abbrlist[0].__len__()
line = expanded.join([line[:currpos-abrlen], line[currpos:]])
currentPosition = line.find(expanded, currpos-abrlen) + expanded.__len__()
dpos = line.find('|')
if(dpos != -1):
currentPosition = dpos
line = line.replace('|', '')
#сохраняем текущую позицию курсора
zobj.set(currentPosition)
return line
except Exception as inst:
print inst
return ''
#функция для получения текущей позиции курсора
def getpos():
return zobj.get()
Как видно, кода не так и много, и не очень он сложный. Всё просто и предельно ясно.
Дальше была работа, связанная с написанием самого плагина на си. Чуть больше чем через пару дней появился код вида:
#include#include #include GeanyPlugin *geany_plugin; GeanyData *geany_data; GeanyFunctions *geany_functions; PyObject *pName, *pValue, *main_module, *expand, *getpos; PLUGIN_VERSION_CHECK(147) PLUGIN_SET_INFO("Zen-Coding", "Zen-Coding for Geany", "0.3.1", "POPSuL "); enum { EXPAND_KB, COUNT_KB }; PLUGIN_KEY_GROUP(zen_group, COUNT_KB) //хэндлер вызываемый при нажатии кнопки назначеной для разворачивания static void expand_handler(G_GNUC_UNUSED guint key_id) { GeanyDocument *doc = document_get_current(); int currl = (int)sci_get_current_line(doc->editor->sci); int startpos = sci_get_position_from_line(doc->editor->sci, currl); char *cline = (char*)sci_get_line(doc->editor->sci, currl); int ccpos = sci_get_current_position(doc->editor->sci); int cpos = ccpos - startpos; int nlen = strlen(cline); char *mime = doc->file_type->mime_type; if(nlen > 0) { //создаём список агрументов pName = PyTuple_New(3); PyObject *line = PyString_FromString(cline); PyObject *pos = PyInt_FromLong((long) cpos); PyObject *type = PyString_FromString(mime); PyTuple_SetItem(pName, 0, line); PyTuple_SetItem(pName, 1, pos); PyTuple_SetItem(pName, 2, type); //вызываме функцию expand pValue = PyObject_CallObject(expand, pName); char *result = PyString_AsString(pValue); //получаем позицию курсора pName = PyTuple_New(0); pValue = PyObject_CallObject(getpos, pName); int ncpos = (int) PyLong_AsLong(pValue); if(strlen(result) > 0) { //заменяем "старую" строку новой и устанавливаем //курсор на нужную позицию int endpos = startpos + nlen; sci_set_selection_start(doc->editor->sci, startpos); sci_set_selection_end(doc->editor->sci, endpos); sci_replace_sel(doc->editor->sci, result); if(ncpos != -1) { endpos = startpos + ncpos; } sci_set_current_position(doc->editor->sci, endpos, TRUE); } else { printf("error result, %s", result); } Py_DECREF(pName); Py_DECREF(pValue); } } //функция для инициализации плагина void plugin_init(G_GNUC_UNUSED GeanyData *data) { //инициализируем питон Py_Initialize(); //импортируем необходимые пакеты PyRun_SimpleString("import os, sys"); PyRun_SimpleString("sys.path.append(r'/usr/lib/geany-plugins/geanyzen')"); PyRun_SimpleString("from zencoding.geany_zencoding import expand, getpos"); //получаем указатели на необходимые модули и функции main_module = PyImport_AddModule("__main__"); expand = PyObject_GetAttrString(main_module, "expand"); getpos = PyObject_GetAttrString(main_module, "getpos"); //вешаем хэндлер expand_handler на ctrl+e keybindings_set_item(plugin_key_group, EXPAND_KB, expand_handler, GDK_e, GDK_CONTROL_MASK, "expand", "expand", NULL ); } //функция для выгрузки всего ненужного из памяти void plugin_cleanup(void) { Py_DECREF(main_module); Py_DECREF(expand); Py_DECREF(getpos); Py_Finalize(); }
Тут вроде бы всё также предельно ясно. Отсутствие опыта написания программ на питоне и си очень сказалось на красоте (и скорее всего качестве) кода.
Что получилось
В итоге, примерно где‐то через неделю, появился вполне работоспособный плагин для Geany. Тормозов и внезапных падений редактора не было замечено. Даже аббревиатура вида div*100>div*100 разворачивается менее чем за секунду. Это вам не netbeans… :)
Если вы тоже захотели научить свой Geany разворачивать аббревиатуры добро пожаловать на google code.
Для сборки плагина потребуется qmake, make, checkinstall, gcc, geany и libgtk2.0-dev, если у вас всё это уже есть, то для сборки необходимо из папки плагина выполнить последовательно:
qmake checkinstall -D
и во второй команде нажать три раза Enter. Если всё прошло успешно, и вам не вывалилось в консоль куча ошибок — sudo dpkg -i geanyzen*.deb. Если и тут всё прошло успешно, запускайте Geany, в меню «инструменты» выбирайте пункт «менеджер модулей», включайте модуль Zen Coding. Если же модуля у вас нет в списке, значит не судьба. :)
Для того чтобы было удобнее разворачивать, рекомендую поменять сочетания клавиш на alt+space (сочетания клавиш в меню «Справка» находятся).
P.S. Приведённый здесь код не претендует на самый лучший или что‐то в этом роде, и я так же не несу ответственности за него, так как он распространяется «as is», как есть.
P.S. S. Если у кого то есть желания дополнить или доработать код — добро пожаловать в комментарии.