如何在emacs目录变量文件中访问当前目录的路径?

26
根据Emacs文档,目录变量适用于包含.dir-locals.el文件的目录下的所有文件。
在该文件中,如何将变量设置为包含文件的完整路径?例如:
((nil . ((indent-tabs-mode . t)
          (my-project-path **THIS_DIRECTORY**))))

4
你没有明确提到,但我想知道你是否对各种“项目”包中的任何一个感兴趣。例如,我刚刚开始使用 https://github.com/bbatsov/projectile;它可能可以做一些你想通过 directory-locals 实现的功能。 - offby1
7个回答

14

我曾经问过自己同样的问题,但在网上没有找到解决方案,所以我认为这个答案可能会有所帮助。实际上,我们可以重复使用dir-locals-find-file来获取包含.dir-locals.el文件的目录。因此,以下是我发现的设置针对整个目录的aspell个人词典的示例:

((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))))))))))

此外,似乎输入项按照指定的顺序进行评估,因此以下代码应该可行:

((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)))))

使用eval构造会导致Emacs抱怨不安全的本地变量,但仍然可以永久标记为安全。

更新:自 Emacs ≥ 26.3(也可能适用于早期版本),似乎需要使用 (dir-locals-find-file "./") 而不是 (dir-locals-find-file ".")


2
我的错:我的快速测试存在偏见,因为所有的.dir-locals.el都是相同的。所以确实,dir-locals-find-file总是返回Emacs遇到的第一个.dir-locals.el(最接近访问的文件)。我承认这种限制。 - nberth
2
好的,我现在已经查看了代码,结果发现任何情况下只有一个.dir-locals.el会被查询关于任何特定缓冲区。所以我认为手册需要澄清这一点... - SamB
2
换句话说:我的批评基于对.dir-locals.el的工作方式的误解。 - SamB
由于某些原因(我的elisp非常差),这个文件在其他模式下给我带来了问题(magit每隔一次调用就会“崩溃”,抱怨“my-project-path”不存在)。请参见下面的答案,另有一种方法。 - Steve Broberg
对于涉及ispell-personal-dictionary的示例,将其也设置为make-local-variable是否有意义呢?因为默认情况下它不是缓冲区本地的,所以现在的示例会改变全局绑定,影响到dir-locals目录之外的地方。 - undefined
显示剩余2条评论

4
在我的情况下,我想要找到一个相对于我的代码库当前工作目录的文件,而.dir-locals.el文件被检入了根目录,所以一个相对于.dir-locals.el的“local”文件也是项目根目录下的“local”文件。 pajato0上面的答案对某些情况有效,但它也会破坏其他模式(比如magit)。我通过使用projectile包的projectile-project-root函数来解决这个问题:
((nil . ((eval . (setq cmake-ide-build-dir
                   (concat (projectile-project-root) "/build-make"))
         ))))

4

我认为(file-name-directory (or load-file-name buffer-file-name))应该给出目录路径。

参见链接

编辑:但它不会,因为任何eval表达式都是在被篡改变量的缓冲区的上下文中评估的。


3
据我所知,Emacs实际上并不会评估.dir-locals中的表单,而只是读取它们,并将值分配给命名变量...且这些值必须是常数。我希望我是错的,但我怀疑没有办法做到这一点。 - offby1
@offby1:这种方法似乎比nberth的更有可能成功,至少... - SamB
@SamB 我很失望,显然我们中没有人真正尝试过这个;所以我刚刚试了一下。它不起作用 :-( ((nil . ((did-it-work (file-name-directory (or load-file-name buffer-file-name)))))) 不过,我得到了一个"unsafe-variable"警告。 - offby1
@offby1:你必须使用“eval”,但它不管用。哎呀。 - SamB

4

我发现Emacs自带的locate-dominating-file程序很有用,可以检索已知文件的当前目录。下面的示例将guix-directory变量设置为包含.dir-locals.el文件的项目的最顶层目录。

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

虽然该设置依赖于eval,不是一个安全的safe.dir-locals.el设置,但它可以完成任务。


如果祖先目录中存在.dir-locals.el文件,那么这将给出错误的答案,对吗?因此,即使代码在哪个文件中,如果/a/.dir-locals.el/a/b/.dir-locals.el都存在,则guix-directory将为/a - Troy Daniels

2

如果仍然有影响,我建议您创建一个函数来生成.dir-locals.el文件。然后可以编写类似以下内容的代码:

(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))))

应在项目主目录下执行。


1
嗯,这可能不是世界上最糟糕的想法,但仍然有很多需要改进的地方! - SamB
只要不移动目录就可以工作。例如,将项目特定设置提交到 git 存储库并有人在其他地方检查它。 - Steve Buzonas

1

hack-local-variables 是处理所有本地变量的主要函数,并调用 hack-dir-local-variables 处理 .dir-locals.el 文件(如果您没有使用该文件,则为目录本地类变量)。

建立目录的代码不是在其自己的函数中隔离的,因此我们将不得不将其复制到一个新函数中(这是从 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))

这段代码看起来是正确的,只是有点笨重,不太适合放在.dir-locals.el文件中... - SamB
该函数无疑会与需要信息的代码位于同一库中。您可能只需将其作为函数调用,而不是设置变量(尽管您可以将其与自动缓冲区本地变量配对,扩展函数以检查该变量是否已设置,并在存在值时返回现有值;仅在设置时执行处理以设置该值)。 - phils
图书馆?谁说过什么关于图书馆的事情了? - SamB
阅读“elisp文件”。如果没有任何需要该值的代码,知道它是什么意义不大。我假设代码存在,并且将存储在一个elisp文件中。 - phils

0
如果你正在 *nix 上工作,你可以通过以下 elisp 代码获取工作目录:
(defun get-working-directory ()
    (getenv "PWD))

顺便提一下,(shell-command "pwd") 将会返回当前正在编辑的缓冲区所对应的文件所在的目录。

1
英文没问题 :-) - DJJ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接