使用.dir-locals.el为特定文件设置模式



我为导航GHC配置文件创建了一个小的次要模式,我希望它能在haskell项目中的.prof文件中自动生效。我很高兴使用.dir-locals.el,我记得不久前我找到了一个相关的功能,但现在似乎找不到了。

有很多不同的方法可以实现这一点。这里有三个:

  1. 文件局部变量
  2. 目录本地变量
  3. 标准模式处理

它们中的每一个都有一些优点和缺点,所以你可以决定哪一个最适合你的需求。

文件本地变量

Emacs提供了一种机制,允许您使用特殊语法直接在文件中指定某些变量的值。当访问这样的文件时,Emacs将解析第一行,并将指定的变量设置为给定的值。

当然,次要模式不是一个变量。但是文件本地机制提供了一种通过使用CCD_ 3作为"文件"的名称来执行任意代码的方法;变量";。在实践中,它看起来是这样的。有两种方法可以指定文件本地变量,即配置次要模式的加载。

专用第一行

您可以将以下行作为.prof文件的第一行:

-*- eval: (my-prof-minor-mode) -*-

在这里,您可以看到保留的eval关键字的使用,它允许执行任意代码——在我们的代码中,该代码只是对次要模式的调用(当然,该示例使用占位符,您必须输入实际次要模式函数的名称)。

局部变量列表

如果你不喜欢弄乱文件的开头,你也可以在文件的末尾(或末尾)包含eval声明,但语法略有不同:

## Local Variables:
## eval: (my-prof-minor-mode)

您注意到##在这里的使用:这可能因文件而异,因为它应该只是该类型文件中常用的标准注释语法。不幸的是,我无法确定.prof文件使用的注释语法(如果有的话)。

然而,文件局部变量方法的主要缺点是:实际上不鼓励用户将其用于次要模式。其原理是,次要模式通常是特定用户在编辑时的个人偏好的表达。在共享环境中,这些首选项可能因用户而异,因此在文件本身中修复它们将是有问题的。

此外,由于其他原因,扰乱文件内容可能会有问题,例如,如果您不能在不扰乱文件预期语法的情况下轻松地做到这一点。例如,如果在添加了上面两个选项中的一个之后,Haskell探查器再也不能正确读取.prof文件,那么整个练习毫无意义。

有关文件本地变量的更多信息,请参阅此处、此处和此处。

目录本地变量

目录本地变量使您可以选择在访问某个目录中的文件时设置某些变量值,而不是直接干扰有问题的文件。而且,就像以前一样,该机制支持使用特殊的eval〃;变量";执行任意代码,包括调用次要模式。

为了使用此方法,您需要在与.prof文件相同的目录中创建一个名为.dir-locals.el的文件。该文件的内容如下:

(fundamental-mode . ((eval . (my-prof-minor-mode)))))

该条目以主模式说明符开始:后面的所有内容都只应用于缓冲区的主模式为"的文件;基本模式";。如果您已将Emacs配置为将不同的主模式应用于.prof文件,则必须相应地更改该规范。

然而,一个更大的问题是,上面的规范会为该目录中与给定主模式匹配的所有文件打开次要模式。然而,通过添加文件名测试:,修复.dir-locals.el文件以做正确的事情并不困难

((fundamental-mode . ((eval . (when (string-match "\.prof$" (buffer-file-name))
(my-prof-minor-mode))))))

关于目录局部变量的更多信息可以在这里、这里和这里找到。

标准模式处理

让我们一步一步来完成这个过程。

对于某些文件类型,打开主模式的标准方法是通过auto-mode-alist:

(setq auto-mode-alist (append '(("\.prof$" . my-prof-major-mode))
auto-mode-alist))  

对于某些主要模式,打开次要模式的标准方法是通过主要模式挂钩进行注册:

(add-hook 'text-mode-hook 'my-prof-minor-mode)

现在的问题是:您是否编辑不在Haskell项目中的.prof文件,并且不希望打开次要模式?如果没有,那么以上机制可能已经足够了。让我们暂时假设情况是这样的。

编辑.prof文件时,您使用哪种主要模式?如果是fundamental-mode,您可以考虑将次要模式转换为主要模式,并使用auto-mode-alist为所有.prof文件打开它,如上面的第一个代码示例所示。

如果是其他模式,比如text-mode(或其他任何模式),那么您可以使用第二个代码示例来激活次要模式。但是,只有当主模式专门用于.prof文件时,这才真正有用。对于text-mode来说,情况肯定不是这样。但是,按照上面的方法,通过主模式挂钩激活次要模式,将为所有文本文件打开它,而不仅仅是.prof文件。我们可以通过一个小的助手功能来解决这个问题:

(add-hook 'text-mode-hook (lambda ()
(when (string-match "\.prof$" (or (buffer-file-name) (buffer-name)))
(my-prof-minor-mode))))

这样,每当缓冲区的主模式设置为text-mode并且相关文件以.prof结束时,就会打开次要模式。如果您已经阅读了上面关于目录局部变量的部分,它可能看起来有点熟悉。要确保它也能使用与文件无关的缓冲区,就稍微复杂一些。

现在,让我们来看看这样的情况:您有各种各样的.prof文件,但您只想为那些驻留在Haskell项目中的文件打开次要模式。好吧,接下来我们来看另一个辅助函数,这一次它告诉我们一个文件是否在Haskell项目中:

(defun file-inside-haskell-project-p (filename)
"This function returns t if the file with the given `filename`
is located inside a Haskell project, otherwise nil."
(when filename
(locate-dominating-file (file-name-directory (expand-file-name filename))
(lambda (dir) (directory-files dir nil "\.cabal$")))))

(对不起,我对Haskell一无所知,所以我真的不知道如何检查一个文件是否在Haskell项目中。这个函数的作用如下:给定一个文件,它检查同一目录中是否有.cabal文件。如果没有,它检查父目录和grand-parent目录,等等,一直到文件系统的根目录如果找到,则函数返回包含的目录,否则返回nil。您可能需要用更好的函数来替换此函数。)

这个测试函数现在可以在上述eval0表达式中使用,以获得所需的结果:

(add-hook 'text-mode-hook (lambda ()
(let ((buffer-file-name (buffer-file-name)))
(when (and (string-match "\.prof$" (or buffer-file-name ""))
(file-inside-haskell-project-p buffer-file-name))
(my-prof-minor-mode)))))

请注意,与前面讨论的方法不同,这个方法不适用于与文件无关的缓冲区。不过,原因很清楚:如果我们只想将次要模式应用于驻留在Haskell项目目录中的文件,那么与任何文件都不关联的缓冲区就不符合条件。

最新更新