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. Если у кого то есть желания дополнить или доработать код — добро пожаловать в комментарии.