突出显示Emacs Lisp中的非局部变量

时间:2013-05-15 15:13:28

标签: emacs elisp

在js2-mode中,全局变量会自动为我突出显示:

enter image description here

如何在Emacs lisp中执行相同的操作?我希望能够在以下内容中突出显示flymake-log-levelbarr

(defun foo ()
  (let (bar baz)
    (setq baz flymake-log-level) ;; flymake-log-level isn't locally bound
    (setq barr (1+ flymake-log-level)))) ;; misspelled bar

3 个答案:

答案 0 :(得分:4)

可以通过安装flycheck来利用字节编译器。 Flycheck是flymake的替代品,并且支持Elisp。

对于第一个示例,它不会帮助您,但它会在第二个示例中出现(假设存在必要的require):

(require 'flymake)

(defun foo ()
  (let (bar baz)
    (setq baz flymake-log-level) ;; no complaints here
    (setq barr (1+ flymake-log-level)))) ;; assignment to free variable `barr'

还有hl-defined.el,这是一种次要模式,几乎完全符合问题所描述的内容。安装它,然后在emacs-lisp-mode缓冲区中运行hdefd-highlight-mode。然后,您可以运行命令hdefd-cycle,直到您只显示尚未定义的变量。这给出了类似的东西:

hl-defined screenshot

(这不是完美的,hl-defined不承认fn是一个参数而不是一个自由变量,它将函数list与这里使用的参数混淆。但是,它是对问题中描述的用例非常有帮助。)

最后,一些软件包包括突出显示它们定义的功能。例如,dash.el为其函数提供突出显示,以及它在回指宏中使用的变量名称(即突出显示it)。

;; Enable syntax highlighting of dash functions
(eval-after-load "dash" '(dash-enable-font-lock))

答案 1 :(得分:2)

我想说这是相当多的工作......

最好的方法是使用font-lock-mode并添加新规则。通常,规则包含匹配某些内容的正则表达式,但是,使用函数执行此操作也是合法的。然后,此函数可以搜索标识符,并且对于每个标识符,它可以通过检查变量是否出现在参数列表中,letdolist或类似构造中来检查它是否在本地绑定。 / p>

类似于cwarn mode的包的一个示例,它突出显示(除此之外)表达式内的赋值,用于类C语言。

答案 2 :(得分:2)

我认为甚至不可能准确地突出显示变量绑定范围,或者至少在没有实际涉及字节编译器(或其部分)的情况下,或者重新实现Emacs Lisp语义的部分内容。

关键问题是宏。这些在Emacs Lisp中不卫生。因此任何 宏可以引入任意本地绑定。事实上,许多宏都这样做,例如来自标准库的dolistcondition-casepcase,或来自dash.el的照应列表处理函数,以命名受欢迎的第3个党的图书馆..

使用这些宏,仅从语法上下文确定变量作用域变得不可能。请看以下示例:

(condition-case err
    (--each my-fancy-list
      (my-fancy-function it nil t))
  (error (message "Error %S happened: %s" (car err) (cadr err))))

在不知道condition-case--each的情况下,errit是否在本地绑定?如果是这样,它们绑定的子表达式,例如,是err绑定在所有子表达式中,还是只绑定到处理程序表单(后者是这种情况)?

要确定此类情况下的变量范围,您需要维护宏的详尽白名单及其绑定属性,或者需要扩展宏以动态确定其绑定属性(例如,在中查找let扩展的身体)。

这两种方法在实施时都相当多,并且有缺点。宏定义的白名单几乎是自然不完整,不正确和过时的(只需查看pcase的复杂绑定语义),而扩展宏需要存在宏定义,但情况并非总是如此,例如您正在使用前面提到的dash.el编辑Emacs Lisp,而实际上并未安装此库。

尽管如此,扩展宏可能是最好的努力,甚至更好,你不需要自己实现它。 Emacs Lisp字节编译器已经这样做了,并且它警告了对自由变量的引用,并且还启用了关于未使用的词法变量的词法绑定。所以,字节编译你的文件!

充其量,避免在正在运行的Emacs中调用byte-compile-file,而是在新的Emacs实例中编写Makefile字节编译,以获得干净的环境:

SRCS = foo.el
OBJECTS = $(SRCS:.el=.elc)

.PHONY: compile
compile : $(OBJECTS)

%.elc : %.el
    $(EMACS) -Q --batch -f batch-byte-compile $<

在更复杂的库中,使用-L标志来设置适当的Emacs load-path用于编译。