Улучшаем emacs-jabber

от автора

Начало

На хабре уже была обзорная статья по jabber.el — jabber-клиенту для emacs. Решив попробовать этот клиент после pidgin, я наткнулся на отсутствие таких привычных уже вещей, как история ввода или форматированные сообщения. К сожалению, emacs-jabber не развивается так быстро, как хотелось бы. К счастью, возможности emacs по настройке практически безграничны, поэтому добавить нужное несложно. В этой статье я расскажу, как реализовал историю ввода. Если эта тема заинтересует общественность, в дальнейшем опишу отправку форматированных сообщений (html) и некоторые другие плюшки.

Оговорка

Всё описанное делалось для себя. Я используюprelude, поэтому некоторые функции могут быть из сторонних библиотек и отсутствовать в чистом емаксе. Также я пользуюсь только чатами, для конференций указанный код нужно будет подправить.

История ввода

Очень удобная штука в pidgin — история ввода. По ctrl+up можно идти назад по истории отправленных сообщений, а по ctrl+down вперёд. Добавим этот функционал в emacs-jabber. Нам понадобятся три переменные: список введённых фраз, текущая позиция в этом списке и последний введённый, но ещё не отправленный текст.

(defvar my-jabber-input-history '() "Variable that holds input history") (make-variable-buffer-local 'my-jabber-input-history)  (defvar my-jabber-input-history-position 0 "Current position in input history") (make-variable-buffer-local 'my-jabber-input-history-position)  (defvar my-jabber-input-history-current nil "Current input value") (make-variable-buffer-local 'my-jabber-input-history-current) 

При отправке сообщения добавляем его в список:

(defun my-jabber-input-history-hook (body id)   (add-to-list 'my-jabber-input-history body t)   (setq my-jabber-input-history-position (length my-jabber-input-history))) (add-hook 'jabber-chat-send-hooks 'my-jabber-input-history-hook) 

Важно: my-jabber-input-history-position указывает не на последний элемент истории, а за него (нумерация с нуля). Это логично, ведь мы ещё не ходили по списку.
Функция для прохода назад по списку:

(defun my-jabber-previous-input ()   (interactive)   (let (current-input (pos my-jabber-input-history-position) (len (length my-jabber-input-history)))     (if (= pos 0)         (message "%s" "No previous input")       (setq current-input (delete-and-extract-region jabber-point-insert (point-max)))       (when (= pos len) ; running first time, save current input           (setq my-jabber-input-history-current current-input))       (decf my-jabber-input-history-position)       (insert (nth my-jabber-input-history-position my-jabber-input-history))))) 

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

(defun my-jabber-next-input ()   (interactive)   (let ((pos my-jabber-input-history-position) (len (length my-jabber-input-history)))     (cond      ((= pos (1- len)) ; pointing at the last element, insert saved input        (incf my-jabber-input-history-position)        (delete-region jabber-point-insert (point-max))        (insert my-jabber-input-history-current)        (setq my-jabber-input-history-current nil))       ((= pos len)                              ; pointing beyound last element, notify user        (message "%s" "No next input"))       (t                                ; insert next history item        (incf my-jabber-input-history-position)        (delete-region jabber-point-insert (point-max))        (insert (nth my-jabber-input-history-position my-jabber-input-history)))))) 

Здесь логика хитрее. Если мы на последнем элементе в списке (len — 1), то вставлять надо предварительно сохранённый пользовательский ввод. Но позицию по-прежнему увеличиваем, чтоб в следующий раз сработало второе условие.
Вешаем эти функции на удобные кнопкосочетания:

(define-key jabber-chat-mode-map (kbd "M-p") 'my-jabber-previous-input) (define-key jabber-chat-mode-map (kbd "M-n") 'my-jabber-next-input) 

Готово. Теперь у нас есть тот же функционал, что и в pidgin + извещения о достижении начала и конца списка + история без дублирующихся сообщений (благодаря add-to-list).
Любителям ido-mode может пригодиться такая функция:

(defun my-jabber-input-history-choose ()   (interactive)   (let ((choice (ido-completing-read "Select history item: " (reverse my-jabber-input-history))))     (delete-region jabber-point-insert (point-max))     (insert choice))) 

Поскольку это ido, то работает поиск в списке по мере ввода текста (даже нечёткий, если установлена переменная ido-enable-flex-matching) и перебор вариантов по C-s/C-r.

Конец

Текст подготовлен в emacs с использованием org-mode и экспорта в docbook (С-c C-e D).

Ссылки

Лиспокод

Исходник этой статьи

XSLT для преобразования докбукового XML в понятный хабру формат.

ссылка на оригинал статьи http://habrahabr.ru/post/164573/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *