Как стать автором
Обновить

Расширяем Emacs своими руками

Время на прочтение 4 мин
Количество просмотров 4.8K
Статья о умелом использовании Emacs моего коллеги, желающего стать хабраГражданином, и наблюдать НЛО. Пожалуйста инвайт на d.klykvin@гмайл.ком

Однажды я открыл для себя игру Годвилль, где герой живёт сам по себе,
и подпинывать его надо когда самому будет не лень. Но, спустя n-ное
количество недель, мне надоело открывать браузер чтобы посмотреть
самочувствие моего героя, и я решил написать расширение для emacs,
которое будет само лезть в сеть и выводить интересующую меня
информацию.
Наверняка эта статья будет неинтересна гуру emacs, но для первых шагов
может пригодиться

Имеем:
— GNU Emacs
— xml со стороны сервера
— базовые знания elisp
— google
— исходники других модулей

Потрошим внутренности



Для начала надо посмотреть какой инструментарий у нас уже
есть. Во-первых, нам понадобится забирать файл с сервера, во-вторых,
парсить его.
Быстрый поиск по /usr/share/emacs/ (узнать откуда у вас
emacs подгружает файлы можно с помощью M-x v load-path) выдал что
загрузкой ссылок занимается некий url, а парсингом — xml так что делаем

M-x load-library url
M-x load-library xml


Теперь можно создавать буфер, где и будет храниться наш код (я
поместил его в ~/.emacs.d/godville.el )

Ищем функции



Обычно я ищу функции тремя способами — C-h a (apropos) листая
исходный код или делая C-h f function-name.
в данном случае поиск по коду оказался лучше, потому что команда "grep
defun url.el" выдала мне все функции которые есть в этом файле
(напоминаю что пакет url не ограничивается одним файлом — просто мне
нужен был именно он)

Огромное преимущество emacs в том, что это lisp-машина, и в любом
месте редактора можно выполнить кусок кода. В grep проскочила
информация о url-retrieve — сразу её и посмотрим.
Прямо в начале файла пишем

(url-retrieve "http://ya.ru")

и вычисляем выражение с помощью C-x C-e. Ooops:

wrong-number-of-arguments (url callback &optional cbargs)


Судя по C-h f url-retrieve нужен ещё и callback. Поскольку просто
pop-to-buffer написать не получится, а ещё одну функцию городить лень,
воткнём lambda-выражение

(url-retrieve "http://ya.ru" (lambda (x) (pop-to-buffer (current-buffer))))

Поздравляю — скорее всего у вас сейчас открылся исходный код ya.ru, и
половина дела сделана. Если не открылся — смотрите ошибки и
спрашивайте в чём дело у соседей-лисповодов

Оформляем код



Emacs должен знать что ему потребуется для работы нашей библиотеки и
что, вообще, наша библиотека предоставляет, поэтому первые 3 строки
будут банальны:

(require 'url)
(require 'xml)
(provide 'godville)


Теперь, когда кто-то сделает load-library godville, emacs сначала
убедится что url и xml есть в наличии.

Следующим шагом будет создание функции, которая будет забирать XML с
сервера. url выглядит как http://godville.net/gods/api/username.xml,
значит нам надо добавить переменную с именем бога. Во время разработки
проще это сделать с помощью setq:
(setq godville-username "Godville")

И, собственно, сама функция:

(defun godville-get-info ()
    "Connect to server, get XML and create list with data"

    (interactive)
    ;; Get XML with hero data and go to parser
    (if (not godville-username)
    	(setq godville-username (read-from-minibuffer "Enter user name: ")))
	(godville-parse-xml (url-retrieve
		       (concat "http://godville.net/gods/api/" godville-username ".xml")
		       (lambda (x) ()
		       )))
)


Собственно, создание функции происходит как в большинстве других
языков:

(defun function-name (params)
;; function body
)

Единственное к чему надо привыкнуть, это к тому что не приходится
заводить множество промежуточных переменных — результат одной функции
передаётся вышестоящей, та ещё выше, и т.д. И ещё одно — всегда
оставляйте комментарий к функции. всегда. Это сэкономит время как
вам, так и тем кто будет смотреть код в последущем.

В нашем случае (if) сначала проверяет наличие переменной
godville-username, и если та не задана, просит ввести её в
минибуфере. После того как имя введено вычисляется список
(godville-parse-xml (url-retrieve...)) Так как callback из
url-retrieve нам ни к чему (точнее, я туда повесил обработку 404, но
сейчас она будет лишней) туда вставляем пустое лямбда-выражение, а
результат url-retrieve передаём функции godville-parse-xml

url-retrieve возвращает новый буффер с данными, которые он
получил. Следовательно у godville-parse-xml будет как минимум один
параметр — имя буфера с нашим XML:

(defun godville-parse-xml (buffer)
...
)


Пройдясь grep по xml.el нашлись xml-parse-file и xml-parse-region. Для
xml-parse-file нам необходимо получить буфер, сохранить его,
пропарсить, удалить файл… не подходит. Будем использовать
xml-parse-region. Теперь наша функция будет выглядеть примерно так:

(defun godville-parse-xml (buffer)
    "Parse xml and kill temp buffer"
    (switch-to-buffer buffer)
    (setq tmp-list (xml-parse-region (point-min) (point-max)))
    (kill-buffer buffer))


Пользуясь C-h f и читая документацию скоро вы поймёте, что эта функция
проста как 2 копейки: переключились в буфер, распарсили xml, запихали
результат в переменную и прибили теперь уже ненужный буфер.

В принципе, на этом работа библиотек url и xml заканчивается — файл мы
получили, составили нормальный список из xml-файла, остальное дело
техники — проходимся по списку, красиво рисуем нужные данные,
выкидываем ненужные, и выкладываем код на суд общественности.

Кастомизация



Опуская нудный код именно этой задачи, нельзя пропустить возможности
настройки. Все вы наверняка использовали M-x customize или M-x
customize-group, значит и нам можно сделать не хуже.

Для этого используются функции defgroup, defcustom, defface и т.д..

(defgroup godville nil "Godville status viewer"

    :group 'applications)

(не забывайте для каждой знакомой и незнакомой функции делать C-h f — освоение emacs lisp пойдёт гораздо быстрее. В данном случае можете
посмотреть откуда взялся nil в defgroup)

(defcustom godville-user nil
    "God name"
    :type 'string
    :group 'godville)

(defcustom godville-timer 1800
    "Update interval"

    :type 'integer
    :group 'godville)


Финал



Собственно, вот так, оформив "шапку" с require/provide и дописывая код
маленькими шагами можно превращать свой редактор в маленьую и уютную
операционную систему, из которой совсем не обязательно
выходить. Главное, не бояться делать C-x C-e, читать ошибки и
исправлять их.

Статья о умелом использовании Emacs моего коллеги, желающего стать хабраГражданином, и наблюдать НЛО. Пожалуйста инвайт на d.klykvin@гмайл.ком
Теги:
Хабы:
+23
Комментарии 13
Комментарии Комментарии 13

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн
PG Bootcamp 2024
Дата 16 апреля
Время 09:30 – 21:00
Место
Минск Онлайн
EvaConf 2024
Дата 16 апреля
Время 11:00 – 16:00
Место
Москва Онлайн