Chris R
Chris R

Reputation: 17926

How can I access the path to the current directory in an emacs directory variable file?

According to the Emacs documentation, Directory Variables apply to all files below a directory that contains an .dir-locals.el file.

How can I, in that file, set a variable to the full path that contains the file? For example:

((nil . ((indent-tabs-mode . t)
          (my-project-path **THIS_DIRECTORY**))))

Upvotes: 27

Views: 10597

Answers (7)

slu
slu

Reputation: 1325

I think (file-name-directory (or load-file-name buffer-file-name)) should give you the directory path.

See Link

Edit: Except it won't, because any eval expressions are evaluated in the context of the buffer whose variables are being hacked.

Upvotes: 4

Apteryx
Apteryx

Reputation: 6371

I've found the locate-dominating-file procedure, which comes out-of-the-box with Emacs, useful to retreive the current directory of a known file. The example below sets the guix-directory variable to the topmost directory of the project containing a .dir-locals.el file.

((nil . ((eval . (setq guix-directory
                       (locate-dominating-file default-directory
                                               ".dir-locals.el"))))))

It's not a safe .dir-locals.el setting, due to relying on eval, but it gets the job done.

Upvotes: 4

nberth
nberth

Reputation: 571

I asked myself the same question and found no solution on the web, so I think this answer may help. Actually, it turns out we can reuse dir-locals-find-file to get the directory containing the .dir-locals.el file. So here's what I found for, e.g, setting up an aspell personal dictionary dedicated to a whole directory:

((nil . ((eval . (setq ispell-personal-dictionary
                       (expand-file-name
                        ".aspell_words"
                        (file-name-directory
                         (let ((d (dir-locals-find-file ".")))
                           (if (stringp d) d (car d))))))))))

Also, it seems entries are evaluated in the order they are specified, so the following code should work:

((nil . ((eval . (set (make-local-variable 'my-project-path)
                      (file-name-directory
                       (let ((d (dir-locals-find-file ".")))
                         (if (stringp d) d (car d))))))
         (eval . (message "Project directory set to `%s'." my-project-path)))))

Emacs will complain about unsafe local variables (due to the eval construct), yet one can still permanently mark it safe.

Update: Since Emacs ≥ 26.3 (and maybe older versions as well), it appears that one needs to use (dir-locals-find-file "./") instead of (dir-locals-find-file ".").

Upvotes: 14

Steve Broberg
Steve Broberg

Reputation: 4394

In my case, I wanted to locate a file that was relative to my current working directory for my repository, and the .dir-locals.el file was checked into the root, so a file "local" to the .dir-locals.el was also a file "local" to the project root.

pajato0's answer above worked for some cases, but it was also breaking other modes (like magit). I got around the issue by using the projectile package's projectile-project-root function to find my the base path for me:

((nil . ((eval . (setq cmake-ide-build-dir
                   (concat (projectile-project-root) "/build-make"))
         ))))

Upvotes: 4

Junwei WANG
Junwei WANG

Reputation: 239

If you are working on *nix, you might get the work directory by the following elisp code,

(defun get-working-directory ()
    (getenv "PWD))

Btw, I have to mentioned that, (shell-command "pwd") will result in the directory where file (which is corresponding to the buffer you are currently editing).

Upvotes: 0

phils
phils

Reputation: 73345

hack-local-variables is the main function for processing all local variables, and it calls hack-dir-local-variables to deal with the .dir-locals.el file (or a dir local class variable, if you're not using that file).

The code for establishing the directory is not isolated in its own function, so we'll have to copy it out into a new function (this from GNU Emacs 24.0.95.1):

(defun my-dir-locals-dir ()
  "Return the directory local variables directory.
Code taken from `hack-dir-local-variables'."
  (let ((variables-file (dir-locals-find-file (or (buffer-file-name) default-directory)))
        (dir-name nil))
    (cond
     ((stringp variables-file)
      (setq dir-name (file-name-directory variables-file)))
     ((consp variables-file)
      (setq dir-name (nth 0 variables-file))))
    dir-name))

Upvotes: 1

pajato0
pajato0

Reputation: 3676

In case it still matters, to the OP or some other, I would suggest you create a function to generate the .dir-locals.el file. Then one could write something like:

(let ((path default-directory)
      file)
  (setq file (format "%s/.dir-locals.el" path))
  (with-temp-buffer
    (insert (format "((nil . ((indent-tabs-mode . t)
          (my-project-path \"%s\"))))" path))
    (when (file-writable-p file)
      (write-region (point-min)
                    (point-max)
                    file))))

to be executed within the project home directory.

Upvotes: 2

Related Questions