Shorter Directory Names in Emacs Ibuffer and Mode Line

I use Emacs and Z shell exclusively, and have been frustrated at times about the features in Z shell that are not in Emacs, one being the shortened directory names. These are used for navigating (changing directories and displayed in the prompt), and are especially useful with long/deep directory names.

In Java projects, this is beneficial, because the hierarchy tends to be so deep. For example, with the path:

   /home/me/proj/com/mycompany/projectx/trunk/src/main/java/com/mycompany/util/FooUtil.java

In Z shell the directory can be given a short name with:

   hash -d projectx=/home/me/proj/com/mycompany/projectx/trunk

So the file can be referred as:

   less ~projectx/src/main/java/com/mycompany/util/FooUtil.java

And this can be done more extensively than that, such as:

   hash -d pxutil=~projectx/src/main/java/com/mycompany/util

And used as:

   less ~pxutil/FooUtil.java

That also makes for a shorter directory name displayed in the prompt, such as this for DiffJ:

    (~diffj)-[master]-(0)%

Which without Zsh hashes would be:

    (/home/jpace/proj/org/incava/diffj)-[master]-(0)%

So I’ve become used to that in my shell, but have been frustrated with my editor, which shows the full path in the default ibuffer display, running out to over 100 columns:

    TokenList.java   1699 /home/jpace/proj/org/incava/diffj/src/main/java/org/incava/diffj/code/TokenList.java

It also seemed redundant that in the above, TokenList.java is displayed twice, so the filename-and-process column could be replaced with only the directory name.

I looked around for examples about changing the columns in ibuffer, but found few examples, mainly the same code repeated on various sites online, variations of this from http://www.emacswiki.org/emacs/IbufferMode:

;; Use human readable Size column instead of original one
(define-ibuffer-column size-h
  (:name "Size" :inline t)
  (cond
   ((> (buffer-size) 1000000) (format "%7.3fM" (/ (buffer-size) 1000000.0)))
   ((> (buffer-size) 1000) (format "%7.3fk" (/ (buffer-size) 1000.0)))
   (t (format "%8d" (buffer-size)))))

That was a starting point, but I couldn’t find anything more about revising the filename-and-process column, which shows the above long path.

The following code adds a ‘dirname’ format to ibuffer to shorten the filename and show only the directory:

(defvar jep:filename-subs
  '(("/home/jpace" . "~")
    (".*/Projects/com/mycompany/is/" . "~is/")
    ("/home/jpace/proj/org/incava/" . "~incava/")
    ("/$" . "")))

(define-ibuffer-column dirname
  (:name "Directory"
	 :inline nil)
  (if (buffer-file-name buffer)
      (str-replace-all (file-name-directory (buffer-file-name buffer)) jep:filename-subs)
    (or dired-directory
	"")))

(setq ibuffer-formats
      '((mark modified read-only " "
	      (name 30 30 :left :elide)
	      " "
	      (size 9 -1 :right)
	      " " dirname)
	(mark modified read-only " "
	      (name 30 30 :left :elide)
	      " "
	      (size 9 -1 :right)
	      " " filename-and-process)
	(mark " "
	      (name 30 30 :left :elide)
	      " " filename-and-process)))

Now my ibuffer looks like:

[ diffj ]
    TokenList.java    1699 ~incava/diffj/src/main/java/org/incava/diffj/code
    build.gradle       892 ~incava/diffj
    etc....

Much better.

Coincidentally, I had the same complaint about the modeline, which defaults to the same display as ibuffer, with the full path of the file. So I tweaked the modeline code to do the same:

(defvar jep:modeline-subs
  '(("/home/jpace/" . "~/")
    (".*/Projects/com/mycompany/is/" . "~is/")
    ("/proj/org/incava/" . "~incava/")
    ("/$" . "")
    ))

(defun jep:modeline-dir-abbrev ()
  (str-replace-all default-directory jep:modeline-subs))

(setq default-mode-line-format
      (list ""
            'mode-line-modified
            "%25b--"
            " ["
            '(:eval (jep:modeline-dir-abbrev))
            "] "
            "%[("
            'mode-name
            "%n"
            'mode-line-process
            ")%]--"
             "L%l--"
             "C%c--"
            '(-3 . "%P")
            "-%-"))

Note that the modeline and ibuffer code above uses this function, which I put in ~/.emacs.d/lisp/str.el:

(defun str-replace-all (str pats)
  (if (null pats)
      str
    (let* ((pat (car pats))
	   (lhs (car pat))
	   (rhs (cdr pat)))
      (replace-regexp-in-string lhs rhs (str-replace-all str (cdr pats))))))

So there is my contribution. Here it is in action:

emacs_dirname

And it’s included with my Emacs configuration on GitHub.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s