Feeds:
Posts
Comments

Archive for the ‘Emacs’ Category

I am a student of the design your data structures first school of programming1. My first proper language was C and in that language pretty much all you had was typedef struct and a handful of primitive types.

Despite the fact that I now spend most of my time programming with dynamic languages (emacs lisp and perl), I still believe it is important to think about your types before you start programming. Eh, but what’s that? Dynamic languages don’t have types? Au contraire mon ami. Not having static type-checking is not the same as not having types.

So, enough handwaving, what about a real example?

Extensible Vectors

In C++, one of the data structures I use most is the STL vector. But in emacs lisp, the vector is a very different animal as it doesn’t automatically extend. If I want an extensible vector type (an evector), I have to implement it myself (or find a library containing one, but that wouldn’t make a good example).

So, first of all, I need to know what pieces of information each evector will need. There will be the vector itself, plus the current end of the vector and the current size before we need to resize it. If we want to use fibonacci to get the next size of vector we need another slot for the fibonacci counter. So each evector will look like this:

['evector [...] <end position> <fib counter>]

The constructor is generally called make-<typename> so the implementation (assuming we want a vector that starts with 8 slots) is:

(defun make-evector ()
  (vector 'evector (make-vector 8 nil) 0 5))

We also need a predicate for checking if a variable is of type evector.

(defun evectorp (object)
  (eq (aref object 0) 'evector))

What does the push-back operation look like?

(defun evector-push-back (object elem)
  (let* ((vec (aref object 1))
         (pos (aref object 2))
         (len (length vec)))
    ;; increase the size of the vector if necessary
    (when (<= len pos)
      (let* ((new-size (+ len (aref object 3)))
             (new-vec (make-vector new-size nil))
             (i 0))
        ;; copy the original vector into the new one
        (while (< i len)
          (aset new-vec i (aref vec i))
          (incf i))
        (setq vec new-vec)
        (setf (aref object 1) vec)
        (setf (aref object 3) len)))
    ;; set the vector element and update end position
    (aset vec pos elem)
    (incf (aref object 2))))

Hmmm… referring to the member variables by position looks error prone and inflexible. What can we do about that?

If you’re thinking accessors, you’re on the right lines. Fortunately, because of setf, you only need a getter.

(edit: fixed, thanks Jisang)

(defsubst evector-data (object) (aref object 1))
(defsubst evector-end-position (object aref object 2))
(defsubst evector-fib-var (object aref object 3))

defstruct

But even better, there is a macro that does defines your constructor, predicate and accessors for you, defstruct.

(defstruct evector
  (data (make-vector 8 nil))
  (end-pos 0)
  (fib-var 5))

Yet another advantage of using defstruct, is that the error messages are clearer than with my basic accessors.

(evector-fib-var 'x)

[basic accessor]
Debugger entered--Lisp error: (wrong-number-of-arguments (lambda (object aref object 3)) 1)

[defstruct accessor]
Debugger entered--Lisp error: (error "evector-fib-var accessing a non-evector")
  signal(error ("evector-fib-var accessing a non-evector"))
  error("evector-fib-var accessing a non-evector")
...

edit: apologies to those who arrived from ironman perl. This isn’t a perl related post so I hadn’t tagged it as such. I didn’t realise that posts that mention perl anywhere get picked up.


1. Although it might not come across in my examples :)

Read Full Post »

Directory Aliases is one of my favourite emacs packages, well, out of the ones I have written at least. It provides a way to go from an alias to a directory location using ido and dired respectively. On Linux, I can use shell aliases and the emacs daemon but for my sins, a lot of my time is spent developing on Windows and there it is invaluable. As I find the various Windows shells are lacking it is nice to an alternative interface to the OS – emacs.

alias realias='$EDITOR ~/.aliases; source ~/.aliases'

Inspired by chromatic’s post mentioning realias (attributed to Damian Conway) I added a couple of features to directory aliases. The first is the ability to open a regular file in addition to the directory. The second is the ability to reload the aliases so you have access to them immediately after adding them.

Future plans include a facility to add an alias with a keystroke while visiting a file (maybe I should be using bookmarks instead… but I like ido too much).

(require 'ido)
(require 'dired)

(defconst *home* "c:/home/jared")
(defconst *packages* "c:/packages")

(defconst *file:dir-aliases* (concat *elisp-dir* "dir-aliases.el"))

(defsubst home (path) (concat *home* path))
(defsubst packages (path) (concat *packages* path))

The preamble has some convenience functions for referring to commonly used areas.

(defconst *file-aliases*
  (list (cons "dir-aliases" *file:dir-aliases*)
        ...))

(defconst *dired-aliases*
  (list (cons "project" (home "/project"))
        (cons "curious" (home "/websites/curious"))
        (cons "plack" (home "/plack-tests"))
        (cons "site-lisp" (packages "/emacs/site-lisp"))
        ...))

I main separate alists for file aliases and dired aliases.

(defun my-open-alias (aliases fn &optional alias)
  (interactive)
  (unless alias
    (setq alias
          (ido-completing-read "Alias: "
                               (mapcar (lambda (e) (car e)) aliases)
                               nil t)))
  (if (and (stringp alias) (> (length alias) 0 ))
      (let ((pair (assoc alias aliases)))
        (if pair
            (funcall fn (cdr pair))
          (error "Invalid alias %s" alias)))
    (error "Invalid alias %s" alias)))

The main my-open-alias function reads in the alias with ido and then applies the passed in function.

(defun file-open-alias (&optional alias)
  (interactive)
  (my-open-alias *file-aliases* #'find-file alias))

(defun dired-open-alias (&optional alias)
  (interactive)
  (my-open-alias *dired-aliases* #'dired alias))

(defun reload-aliases ()
  (interactive)
  (load *file:dir-aliases*))

More convenience functions using #'find-file and #'dired and the alias reloader I mentioned earlier.

(defvar f2-prefix-map nil)
(setq f2-prefix-map (make-sparse-keymap))

(global-set-key [f2] f2-prefix-map)
(global-set-key (kbd " d") 'dired-open-alias)
(global-set-key (kbd " f") 'file-open-alias)

(provide 'dir-aliases)

Maybe dir-aliases isn’t the best name anymore.

Read Full Post »

Sorting Records With Emacs

I use sort-lines fairly frequently to keep my #includes, uses or variable names in order. Occasionally though, I need to sort something slightly more complicated and I end up writing a little perl script. I found myself thinking it must be equally easy in emacs.

For example, I say I have the following data which I want to sort by the second field which is in position 12 and 13.

Edward Wood 9   01/07/2005
Ed Smith    25  06/12/2004
James Brown 18  01/07/2005
Jon James   13  05/15/2007

Now, emacs does have a sort-columns function which does all sorts of fancy things including checking if we are using Unix and shelling out to sort if possible, but it doesn’t seem to be able to do a numerical comparison. However, in sort.el it does have a general purpose sorting function called sort-subr.

(defun sort-subr (reverse nextrecfun endrecfun
                          &optional startkeyfun endkeyfun predicate)

sort-lines itself is a good start for my custom function as it has all the code for handling regions, reversal and skipping between records (lines) that we need.

(defun sort-lines (reverse beg end)
  (interactive "P\nr")
  (save-excursion
    (save-restriction
      (narrow-to-region beg end)
      (goto-char (point-min))
      (let ((inhibit-field-text-motion t))
        (sort-subr reverse 'forward-line 'end-of-line)))))

Getting the startkeyfun and endkeyfun correct was fairly straight forward but I wasn’t entirely sure what information was passed to the predicate.

(defun sort-on-field (reverse beg end)
  (interactive "P\nr")
  (save-excursion
    (save-restriction
      (narrow-to-region beg end)
      (goto-char (point-min))
      (let ((inhibit-field-text-motion t))
        (sort-subr reverse 'forward-line 'end-of-line
                   (lambda () (forward-char 12))
                   (lambda () (forward-char 2))
                   (lambda (a b)
                     (message (format "[%s] [%s]" a b))))))))

[(703 . 705)] [(676 . 678)]
[(757 . 759)] [(730 . 732)]
[(757 . 759)] [(703 . 705)]
[(730 . 732)] [(703 . 705)]

The final working version:

(defun sort-on-field (reverse beg end)
  (interactive "P\nr")
  (save-excursion
    (save-restriction
      (narrow-to-region beg end)
      (goto-char (point-min))
      (let ((inhibit-field-text-motion t))
        (sort-subr reverse 'forward-line 'end-of-line
                   (lambda () (forward-char 12))
                   (lambda () (forward-char 2))
                   (lambda (a b)
                     (let ((s1 (buffer-substring (car a) (cdr a)))
                           (s2 (buffer-substring (car b) (cdr b))))
                       (< (string-to-number s1) (string-to-number s2)))))))))

Providing a nice way of asking for the start and end location of the sorting field is left as an exercise for the reader.

Read Full Post »

I don’t create many lisp macros but it is still a huge advantage having them. I’m a big user of macros that other people have created. For example, by default emacs lisp does not support keyword parameters. Thanks to the magic of macros they have been added in the cl.el common lisp package.

(require 'cl)

(defun* example (&rest args
                 &key (a 1) (b 2))
  (format "%s %s %s" args a b))

(example) works as expected and returns "nil 1 2"

(example :a 10) is pretty good too although you may be surprised that keywords are not removed from the &rest args "(:a 10) 10 2"

However, (example 1) returns the following surprise (error "Keyword argument 1 not one of (:a :b)").

If you are using &rest and &key together, you can fix this with &allow-other-keys.

(defun* example (&rest args
                 &key (a 1) (b 2)
                 &allow-other-keys)
  (format "%s %s %s" args a b))

Okay, so much for pithy examples. How about something practical?

(defun* remote-path (&rest path
                     &key (user "jared") (server "someserver")
                     &allow-other-keys)
  (concat "ftp:" user "@" server ":"
          (mapconcat #'identity path "/")))

(remote-path :user "uat-user" "/tmp" "test-dir") throws this error (wrong-type-argument sequencep :user). This is because if you use &rest arguments and &key arguments together, the keyword arguments are also passed through into &rest.

I couldn’t find an obvious function to remove keyword parameters so I’ve written my own remove-keyword-params.

(defun remove-keyword-params (seq)
  (if (null seq) nil
    (let ((head (car seq))
          (tail (cdr seq)))
      (if (keywordp head) (remove-keyword-params (cdr tail))
        (cons head (remove-keyword-params tail))))))

(defun* remote-path (&rest path
                     &key (user "jared") (server "someserver")
                     &allow-other-keys)
  (concat "ftp:" user "@" server ":"
          (mapconcat #'identity (remove-keyword-params path) "/")))

Now it works correctly.

(remote-path :user "uat-user" "/tmp" "test-dir")
;; "ftp:uat-user@someserver:/tmp/test-dir"

So what do I want this for? For my directory aliases of course.

(defconst *dired-dirs*
  (list (cons "dev" (remote-path "~/dev"))
        (cons "dev:system"
              (remote-path :user "dev-user" "~/system"))
        (cons "uat:system"
              (remote-path :user "dev-user" "~/system"))))

And surprise surprise, because emacs is architected so well, it all works remotely through tramp completely transparently.

Read Full Post »

Does anyone else use tramp? It is a mode that allows emacs to transparently edit remote files. For example, you have a file called afile.txt on a server called someserver.

/ftp:jared@someserver:~/afile.txt

Then behind the scenes emacs will ftp the the file to and from the server every time you save.

What Is Happening In Ange-ftp?

Recently tramp has been letting me down. When I try and access a remote-file it fails to login to the ftp process. Sad.

The ftp login as handled by ange-ftp. I finally tracked the problem down to this section.

(defun ange-ftp-get-process (host user)
      ;; ...
      (let ((pass (ange-ftp-quote-string
                   (ange-ftp-get-passwd host user)))

ange-ftp-get-process calls (ange-ftp-quote-string ...). Normally, this isn’t a problem. For example, on the Windows PC I am using at this precise moment (ange-ftp-quote-string "!\"£$%^&*()") expands to "!\"£$%^&*()". However, at work, I get:

"\\!\"£\\$\\%^\\&\\*()"

Could this be due to the customize EmacsW32 option? I’m not sure. Anyway, absent the underlying cause, my work-around is to (require 'ange-ftp) and then redefine the ange-ftp-get-process without the quoting of the password.

(require 'ange-ftp)

(defun ange-ftp-get-process (host user)
      ;; ...
      (let ((pass (ange-ftp-get-passwd host user))

Read Full Post »

I have a number of utility functions in a file called my-utils.el. The first one I wrote was add-path.

add-path

(defun add-path (&rest path)
  (let ((full-path ""))
    (dolist (var path)
      (setq full-path (concat full-path var)))
    (add-to-list 'load-path (expand-file-name full-path))))

This means that when I add a new third-party library (in its own directory), I can just write:

(add-path *elisp-dir* "muse-3.12")

I have a function called identity that returns whatever was passed to it.

(defun identity (e) e)

This is particularly useful in functions like mapconcat.

(defun add-path (&rest path)
  (add-to-list 'load-path (mapconcat #'identity path "")))

I like this function that I found on the internet a while ago (can’t find it again for attribution unfortunately).

insert-YYMMDD

(defun insert-YYYMMDD ()
  (interactive)
  (insert (format-time-string "%Y%m%d")))

remove-dupes

remove-dupes removes duplicate items that are next to each other in a list in the same way that uniq does. (I mentioned this previously in one of the enabling your users posts.)

(defun remove-dupes (list)
  (let (tmp-list head)
    (while list
      (setq head (pop list))
      (unless (equal head (car list))
        (push head tmp-list)))
    (reverse tmp-list)))

assoc-replace

Sometimes you will have an assocation list where you just want to replace one of the values.

(defun assoc-replace (seq values)
  "Replace an element within an association list where the cars match."
  (mapcar (lambda (elem)
            (let* ((key (car elem))
                   (val (assoc key values)))
              (if val (cadr val) elem))) seq))

And often, only the cdr needs to change so kv is a shortcut for that case. (kv 'a 'b) returns (a (a . b)).

(defsubst kv (k v)
  `(,k (,k . ,v)))

(These functions were mentioned in emacs association lists)

ext-mode-map

ext-mode-map allows me to easily setup a major mode for extensions that are inconsistently capitalized.

(defun file-extensions (l)
  (concat "\\.\\("
          (mapconcat
           (lambda (s)
             (mapconcat (lambda (c)
                          (let ((c (upcase (char-to-string c))))
                            (concat "[" c (downcase c) "]")))
                        (symbol-name s) ""))
           l "\\|")
          "\\)\\'"))

(defun ext-mode-map (extensions mode)
  (cons (file-extensions extensions) mode))

Then, for example, I can setup my perl extensions like this:

(add-to-list 'auto-mode-alist (ext-mode-map '(pl perl pm) 'cperl-mode))

(These functions were previously mentioned in autoloading an emacs major mode)

duplicate-current-line

I’m still using this function to duplicate the current line. It works unless you are on the final line which is very rare and you can work around that by opening a line below.

(defun duplicate-current-line ()
  (interactive)
  (beginning-of-line nil)
  (let ((b (point)))
    (end-of-line nil)
    (copy-region-as-kill b (point)))
  (beginning-of-line 2)
  (open-line 1)
  (yank)
  (back-to-indentation))

(This function was mentioned previously in A Simple Emacs Shortcut – Duplicate Line)

set-longlines-mode

I often copy text into buffer and want to read it nicely wrapped around. I’ll change this function to use visual-line-mode when all my emacs versions are upgraded to 23.

(defun set-longlines-mode ()
  (interactive)
  (text-mode)
  (longlines-mode 1))

count-words

Other people have talked about their own count-words functions in the past. I have my own ideas about what constitutes a word.

(defun count-words ()
  (interactive)
  (let ((words (count-matches "[-A-Za-z0-9][-A-Za-z0-9.]*"
                              (point-min) (point-max))))
    (message (format "There are %d words" words))))

regex-replace and string-repeat

I sometimes have a text transform that I want to apply globally to a buffer. Exercise for the reader, fix regex-replace to only apply to a region if one is selected.

(defun regex-replace (regex string)
  (goto-char (point-min))
  (while (re-search-forward regex nil t)
    (replace-match string)))

(defun string-repeat (str n)
  (let ((retval ""))
    (dotimes (i n)
      (setq retval (concat retval str)))
    retval))

Read Full Post »

Now that I have customised my emacs extensively, the default configuration is quite uncomfortable for me to use. I have a file called my-defaults.el which is the bare minimum I need to make using emacs a pleasant experience. If I have to sit down at your emacs session, I will probably need to cut and paste these into a temp buffer and call M-x eval-region.

I’ve mentioned some of these modifications before.

I always always use ido and uniquify. Ido makes it so nice for finding files and switching buffers, I now find the default behaviour surprising and sometimes even catch myself waiting for the options to appear.

(require 'ido)
(require 'uniquify)

flex-matching is a given of course, and I don’t like being prompted unnecessarily for new buffers – I’m always creating them.

(ido-mode t)
(setq ido-enable-flex-matching t)

(setq ido-create-new-buffer 'always)

The way emacs deals with identically named files by default is poor, but it is great that it is so easy to fix.

(setq uniquify-buffer-name-style 'reverse)
(setq uniquify-separator "|")
(setq uniquify-after-kill-buffer-p t)
(setq uniquify-ignore-buffers-re "^\\*")

Since emacs23, fonts now look great. This is from my windows config. Other folks have written about beautifying emacs for other OSes. I summarised those posts here.

(set-default-font
 "-outline-Consolas-normal-r-normal-normal-14-97-96-96-c-*-iso8859-1")

It should be obvious what most of these do. The most important ones are setting yes-or-no-p to accept y or n rather than forcing me to type yes<RETURN> and removing the toolbar. Actually no, scratch that, these are all important. (Emacs23 has some of these set by default).

(global-font-lock-mode 1)
(setq inhibit-splash-screen t)
(setq font-lock-maximum-decoration 3)
(setq use-file-dialog nil)
(setq use-dialog-box nil)
(fset 'yes-or-no-p 'y-or-n-p)
(tool-bar-mode -1)
(show-paren-mode 1)
(transient-mark-mode t)
(setq case-fold-search t)
(blink-cursor-mode 0)

It took me ages to figure out how to prevent emacs converting a bunch of spaces into tabs. And of course, the scrollbar should always be on the right.

(custom-set-variables
 '(scroll-bar-mode 'right)
 '(indent-tabs-mode nil))

Where am I?

(line-number-mode 1)
(column-number-mode 1)

Keep emacs backup files in one place. This is from my windows config again.

(push '("." . "c:/home/jared/.emacs-backups") backup-directory-alist)

And make it so that when I copy a region, that gets sent to the OS clipboard.

(setq x-select-enable-clipboard t)
(setq interprogram-paste-function 'x-cut-buffer-or-selection-value)

Read Full Post »

« Newer Posts - Older Posts »

Follow

Get every new post delivered to your Inbox.