Статья о умелом использовании Emacs моего коллеги, желающего стать хабраГражданином, и наблюдать НЛО. Пожалуйста инвайт на d.klykvin@гмайл.ком
Однажды я открыл для себя игру Годвилль, где герой живёт сам по себе,
и подпинывать его надо когда самому будет не лень. Но, спустя n-ное
количество недель, мне надоело открывать браузер чтобы посмотреть
самочувствие моего героя, и я решил написать расширение для emacs,
которое будет само лезть в сеть и выводить интересующую меня
информацию.
Наверняка эта статья будет неинтересна гуру emacs, но для первых шагов
может пригодиться
Имеем:
— GNU Emacs
— xml со стороны сервера
— базовые знания elisp
— google
— исходники других модулей
Для начала надо посмотреть какой инструментарий у нас уже
есть. Во-первых, нам понадобится забирать файл с сервера, во-вторых,
парсить его.
Быстрый поиск по /usr/share/emacs/ (узнать откуда у вас
emacs подгружает файлы можно с помощью M-x v load-path) выдал что
загрузкой ссылок занимается некий url, а парсингом — xml так что делаем
Теперь можно создавать буфер, где и будет храниться наш код (я
поместил его в ~/.emacs.d/godville.el )
Обычно я ищу функции тремя способами — C-h a (apropos) листая
исходный код или делая C-h f function-name.
в данном случае поиск по коду оказался лучше, потому что команда "grep
defun url.el" выдала мне все функции которые есть в этом файле
(напоминаю что пакет url не ограничивается одним файлом — просто мне
нужен был именно он)
Огромное преимущество emacs в том, что это lisp-машина, и в любом
месте редактора можно выполнить кусок кода. В grep проскочила
информация о url-retrieve — сразу её и посмотрим.
Прямо в начале файла пишем
и вычисляем выражение с помощью C-x C-e. Ooops:
Судя по C-h f url-retrieve нужен ещё и callback. Поскольку просто
pop-to-buffer написать не получится, а ещё одну функцию городить лень,
воткнём lambda-выражение
Поздравляю — скорее всего у вас сейчас открылся исходный код ya.ru, и
половина дела сделана. Если не открылся — смотрите ошибки и
спрашивайте в чём дело у соседей-лисповодов
Emacs должен знать что ему потребуется для работы нашей библиотеки и
что, вообще, наша библиотека предоставляет, поэтому первые 3 строки
будут банальны:
Теперь, когда кто-то сделает load-library godville, emacs сначала
убедится что url и xml есть в наличии.
Следующим шагом будет создание функции, которая будет забирать XML с
сервера. url выглядит как http://godville.net/gods/api/username.xml,
значит нам надо добавить переменную с именем бога. Во время разработки
проще это сделать с помощью setq:
(setq godville-username "Godville")
И, собственно, сама функция:
Собственно, создание функции происходит как в большинстве других
языков:
Единственное к чему надо привыкнуть, это к тому что не приходится
заводить множество промежуточных переменных — результат одной функции
передаётся вышестоящей, та ещё выше, и т.д. И ещё одно — всегда
оставляйте комментарий к функции. всегда. Это сэкономит время как
вам, так и тем кто будет смотреть код в последущем.
В нашем случае (if) сначала проверяет наличие переменной
godville-username, и если та не задана, просит ввести её в
минибуфере. После того как имя введено вычисляется список
(godville-parse-xml (url-retrieve...)) Так как callback из
url-retrieve нам ни к чему (точнее, я туда повесил обработку 404, но
сейчас она будет лишней) туда вставляем пустое лямбда-выражение, а
результат url-retrieve передаём функции godville-parse-xml
url-retrieve возвращает новый буффер с данными, которые он
получил. Следовательно у godville-parse-xml будет как минимум один
параметр — имя буфера с нашим XML:
Пройдясь grep по xml.el нашлись xml-parse-file и xml-parse-region. Для
xml-parse-file нам необходимо получить буфер, сохранить его,
пропарсить, удалить файл… не подходит. Будем использовать
xml-parse-region. Теперь наша функция будет выглядеть примерно так:
Пользуясь C-h f и читая документацию скоро вы поймёте, что эта функция
проста как 2 копейки: переключились в буфер, распарсили xml, запихали
результат в переменную и прибили теперь уже ненужный буфер.
В принципе, на этом работа библиотек url и xml заканчивается — файл мы
получили, составили нормальный список из xml-файла, остальное дело
техники — проходимся по списку, красиво рисуем нужные данные,
выкидываем ненужные, и выкладываем код на суд общественности.
Опуская нудный код именно этой задачи, нельзя пропустить возможности
настройки. Все вы наверняка использовали M-x customize или M-x
customize-group, значит и нам можно сделать не хуже.
Для этого используются функции defgroup, defcustom, defface и т.д..
(не забывайте для каждой знакомой и незнакомой функции делать C-h f — освоение emacs lisp пойдёт гораздо быстрее. В данном случае можете
посмотреть откуда взялся nil в defgroup)
Собственно, вот так, оформив "шапку" с require/provide и дописывая код
маленькими шагами можно превращать свой редактор в маленьую и уютную
операционную систему, из которой совсем не обязательно
выходить. Главное, не бояться делать C-x C-e, читать ошибки и
исправлять их.
Статья о умелом использовании Emacs моего коллеги, желающего стать хабраГражданином, и наблюдать НЛО. Пожалуйста инвайт на d.klykvin@гмайл.ком
Однажды я открыл для себя игру Годвилль, где герой живёт сам по себе,
и подпинывать его надо когда самому будет не лень. Но, спустя n-ное
количество недель, мне надоело открывать браузер чтобы посмотреть
самочувствие моего героя, и я решил написать расширение для emacs,
которое будет само лезть в сеть и выводить интересующую меня
информацию.
Наверняка эта статья будет неинтересна гуру emacs, но для первых шагов
может пригодиться
Имеем:
— GNU Emacs
— xml со стороны сервера
— базовые знания elisp
— исходники других модулей
Потрошим внутренности
Для начала надо посмотреть какой инструментарий у нас уже
есть. Во-первых, нам понадобится забирать файл с сервера, во-вторых,
парсить его.
Быстрый поиск по /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@гмайл.ком