8

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.

2
  • Perhaps this isn't a unix SE question and I should try my luck on SU?
    – Seamus
    Sep 6, 2010 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. Sep 6, 2010 at 22:16

2 Answers 2

8

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."
  (save-match-data
    (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.

1
  • 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, 2019 at 22:30
4

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

1
  • What a killer hidden feature! (remark: export the variable for them to be available) Feb 15, 2014 at 23:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .