3

I want a "default directory" which is constant across all contexts (buffers, packages, etc). That is, when working on multiple files, opening a new terminal, doing anything which requires a starting directory, I want that starting directory to always be the same.

How can I set a global default directory without having to write an arbitrary number of wrapper functions?


To illustrate the problem, suppose my project has the following structure:

.
└───my-project
    │   bar.py
    │   foo.py
    │
    └───tests
            test_bar.py
            test_foo.py

If I'm working on test_foo.py and I do find-file (C-x C-f) to open foo.py, the prompt will start at my-project/tests. I have to go up a level. Now suppose I'm editing foo.py and I want to look at test_bar.py. When I do find-file within the foo.py buffer, the prompt will be at my-project. I need to navigate to tests. Similar things happen when using eshell and other applications which have a default directory.

According to this, this, and this, the starting directory is handled by default-directory. They all indicate that to change the starting directory, you need to do something like,

(setq default-directory "/my/default/path")

or

(cd "/my/default/path")

The trouble is, default-directory is buffer local. You need to constantly re-assign default-directory.

In the case of find-file, I overcome this by creating a wrapper which changes the directory to the default before calling find-file. I then reassign the keybinding:

(defun my-set-global-default-directory (new-default-directory)
  "Set my-global-default-directory to NEW-DEFAULT-DIRECTORY."
  (interactive "DNew global default directory: ")
  (setq my-global-default-directory new-default-directory))

(defun my-find-file ()
  (interactive)
  (cd my-global-default-directory)
  (call-interactively 'find-file))

(global-set-key (kbd "C-x C-f") 'my-find-file)

This works for find-file, but for this to work universally, I would need to manually wrap any function that uses default-directory!

  • I am not very clear about your question, for example, what do your mean by default-buffer? Besides, you really should let-bind default-directory, such as, (let ((default-directory "~/.emacs.d/")) (call-interactively #'find-file)), instead of modifying default-directory via setq or cd. In my opinion, default-directory should not be changed by user. – xuchunyang Sep 15 '18 at 19:40
  • Whoa! default-buffer was a typo. Was supposed to be default-directory. I have corrected the question. – Lorem Ipsum Sep 17 '18 at 12:16
  • 1
    default-directory isn't "reset" according to the buffer, but rather it is a buffer-local value, so it has an independent value in every buffer. FWIW I am awfully doubtful that what you're asking for is a good idea -- it goes against the expectations for the variable, and as such I strongly suspect (but am not certain) that you would encounter undesirable side-effects. Can you elaborate on the specific things which you find problematic with the default behaviour? – phils Sep 17 '18 at 14:33
  • 1
    Maybe explaining the context will help. Say I'm working on a project and am utilizing three buffers: my program, the test for the program, and a terminal. The program and test files live in different directories. I find that if I do something related to directories, like finding a file or opening a new terminal instance, the directory prompt depends on where I'm calling the function from. This means that I'm constantly having to "cd ..". I want to set a global default directory so that whenever I do some directory related operation, I start out there. – Lorem Ipsum Sep 17 '18 at 16:00
  • How many different functions do you need? It sounds like my-find-file, my-write-file, and maybe a few others might be all you need? If that's the case, your approach might be safer than trying to truly change the value of default-directory globally – Tyler Sep 17 '18 at 18:38
2

The code below uses a new feature in 26.1 called variable-watcher. It allows a function to observe changes in some variable's value. But more or less the same could be achieved by using hooks and/or advices covering all standard functions which are changing the directory (e.g. find-file-hook).

There may be unforeseen consequences to this.

;; -*- lexical-binding: t -*-

(defun run-with-hook (hook fn &optional append local)
  "Like `add-hook', but runs only once."
  (declare (indent 1))
  (let* ((fname (make-symbol "run-with-hook"))
         (fn `(lambda (&rest args)
                (unwind-protect
                    (apply (function ,fn) args)
                  (remove-hook ',hook ',fname ,local)))))

    (fset fname fn)
    (add-hook hook fname append local)))

(defvar global-default-directory (expand-file-name "~"))

(defun global-default-directory-watcher (_symbol _newval op where)
  (when (and (eq op 'set)
             (buffer-live-p where))
    (run-with-hook 'post-command-hook
      (lambda ()
        (when (buffer-live-p where)
          (with-current-buffer where
            (cd global-default-directory)))))))

(define-minor-mode global-default-directory-mode
  "Use a global `default-directory' value in every buffer."
  :global t
  (cond
   (global-default-directory-mode
    (setq-default default-directory global-default-directory)
    (dolist (buffer (buffer-list))
      (with-current-buffer buffer
        (when (local-variable-p 'default-directory)
          (cd global-default-directory))))
    (add-variable-watcher 'default-directory
                          #'global-default-directory-watcher))
   (t
    (remove-variable-watcher 'default-directory
                             #'global-default-directory-watcher))))
  • Thank you! However, it will take me a little bit to digest this... – Lorem Ipsum Sep 17 '18 at 20:12
0

I am using this setting in my ~/.emacs.d/init.el file to set $FOO as my default directory:

(add-hook 'find-file-hook
          (lambda() (setq default-directory (substitute-in-file-name "$FOO/"))))
  • Thank you, I appreciate your contribution. Unfortunately, it doesn't answer the question. It also has several interesting...side-effects(?). If this is what you're currently using, you might try the my-find-file I describe in the question. – Lorem Ipsum Nov 14 '18 at 22:29

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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