Improving emacs-jabber

Start


There was already an overview article on jabber.el - jabber-client for emacs. Having decided to try this client after pidgin, I stumbled upon the absence of such familiar things as input history or formatted messages. Unfortunately, emacs-jabber is not developing as fast as we would like. Fortunately, the emacs configuration options are almost endless, so adding the right one is easy. In this article I will tell how I implemented the input history. If this topic is of interest to the public, in the future I will describe the sending of formatted messages (html) and some other goodies.

Reservation
Everything described was done for myself. I use prelude , so some functions may be from third-party libraries and may not be present in pure emax. I also use only chats, for conferences the specified code will need to be corrected.


Input history


A very handy thing in pidgin is the input history. By ctrl + up you can go back along the history of sent messages, and by ctrl + down you can go forward. Add this functionality to emacs-jabber. We will need three variables: a list of entered phrases, the current position in this list and the last text entered but not yet sent.
(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)

When sending a message, add it to the list:
(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)

Important: my-jabber-input-history-position does not indicate the last element of the story, but behind it (numbering from scratch). This is logical, because we have not yet gone on the list.
Function to go back through the list:
(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)))))

It's simple, the only thing worth paying attention to is saving the current text entered. If the user changes his mind to use the story, we can restore what he managed to enter.
Function to go forward in the list:
(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))))))

Here the logic is trickier. If we are at the last item in the list (len - 1), then you must insert the previously saved user input. But the position is still increasing, so that the next time the second condition works.
We hang these functions on convenient button combinations:
(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)

Done. Now we have the same functionality as in pidgin + notifications about the beginning and end of the list + history without duplicate messages (thanks to add-to-list).
For lovers of ido-mode, this function may come in handy:
(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)))

Since this is ido, the search in the list works as you type (even fuzzy if the ido-enable-flex-matching variable is set) and iterates over the options by Cs / Cr.

the end


The text was prepared in emacs using org-mode and exported to a docbook (C-c Ce D).

References


Lispokod

Source code of this XSLT article

for transformation of pre-book XML into a format understandable to Habr.

Also popular now: