So I asked this question about directory shortcuts in a shell. The idea is I'd like to be able to open a file in /home/Documents/foo, say, by typing C-x C-f ~foo/file.

So, is there a way to emulate zsh's "named directories" in the emacs buffer?

Incidentally, the shortcut definitions I want to use are already in my .zshrc, if there's a solution that reads them directly, that gets bonus points.

  • Perhaps this isn't a unix SE question and I should try my luck on SU? – Seamus Sep 6 '10 at 20:17
  • this question isn't unix-specific (even down to the zsh part: there's a zsh port for Windows, or at least for Cygwin), so it should be on SU. But, well, it's here now, so I answered it here. – Gilles 'SO- stop being evil' Sep 6 '10 at 22:16

The Emacs component that's responsible for expanding ~ in file names is expand-file-name. Unfortunately, it's written in C, and deep inside its bowels is code that assumes that what comes after ~ is a user name. Fortunately, Emacs has a generic way of adding a wrapper around functions, so you can do what you want if you don't mind repeating some of the logic in the built-in function.

Here's some completely untested code that should get you going. Look up “Advising Emacs Lisp Functions” in the Emacs Lisp manual for more information; the basic idea is that defadvice adds some code to run before the actual code of expand-file-name. Please signal the mistakes I've inevitably made in comments (whether you know how to fix them or not).

(defvar expand-file-name-custom-tilde-alist
  '(("foo" . "/home/Documents/foo")))

(defadvice expand-file-name (before expand-file-name-custom-tilde
                             (name &optional default-directory)
                             activate compile)
  "User-defined expansions for ~NAME in file names."
    (when (string-match "\\`\\(\\(.*/\\)?~\\([^:/]+\\)\\)/" name)
      (let ((replacement (assoc (match-string 3 name) expand-file-name-custom-tilde-alist)))
        (when replacement
          (setq name (replace-match (cdr replacement) t t name 1)))))))

I'll leave parsing the shortcuts in .zshrc to fill expand-file-name-custom-tilde-alist (or whatever technique you choose to keep the aliases in synch) as an exercise.

| improve this answer | |
  • My exercise solution: (defun my/add-to-tilde-alist (hash) (let* ((tilde:dir (split-string hash "=")) (tilde (car tilde:dir)) (dir (cadr tilde:dir))) (push (cons tilde dir) expand-file-name-custom-tilde-alist))) (mapc #'my/add-to-tilde-alist (split-string (with-output-to-string (call-process "zsh" nil standard-output nil "-ic" "hash -d")) "\n" t)) – Michaël Jul 25 '19 at 22:30

Simply use $foo instead of ~foo in the minibuffer. Emacs will treat foo as the name of an environment variable and use its value.

| improve this answer | |
  • What a killer hidden feature! (remark: export the variable for them to be available) – AdrieanKhisbe Feb 15 '14 at 23:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.