4

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

在此处输入图像描述

我怎样才能在 Emacs lisp 中做同样的事情?我希望能够突出显示flymake-log-level以下barr内容:

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

3 回答 3

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,这是一种次要模式,几乎完全符合问题中的描述。安装它,然后hdefd-highlight-mode在 emacs-lisp-mode 缓冲区中运行。然后,您可以运行该命令hdefd-cycle,直到只显示尚未定义的变量。这给出了类似的东西:

hl定义的截图

(这并不完美,hl-defined 无法识别fn参数不是自由变量,并且会将函数list与此处使用的参数混淆。不过,它对于问题中描述的用例非常有帮助。)

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

;; Enable syntax highlighting of dash functions
(eval-after-load "dash" '(dash-enable-font-lock))
于 2013-05-17T13:41:55.477 回答
2

我会说这是一项相当多的工作......

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

一个类似的包的例子是cwarn mode,它突出显示(除其他外)表达式中的赋值,用于类 C 语言。

于 2013-05-16T08:08:37.733 回答
2

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

关键问题是宏。这些在 Emacs Lisp 中并不卫生。因此,任何宏 都可以引入任意本地绑定。事实上,许多宏都是这样做的,例如dolist,来自标准库的宏,或者来自 dash.el 的照应列表处理函数condition-case命名一个流行的 3rd 方库。pcase

使用这些宏,不可能仅从句法上下文中确定变量范围。举个例子:

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

在不知道condition-caseand的情况下, and--each是本地绑定的吗?如果是这样,它们绑定在哪些子表达式中,例如绑定在所有子表达式中,还是仅绑定处理程序形式(后者是这种情况)?erriterr

要在这种情况下确定变量范围,您需要维护一个详尽的宏白名单及其绑定属性,或者您需要扩展宏以动态确定它们的绑定属性(例如,let在扩展的正文中查找)。

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

尽管如此,扩展宏可能是最好的努力,甚至更好的是,您不需要自己实现它。Emacs Lisp 字节编译器已经做到了这一点,它警告对自由变量的引用,并在启用词法绑定的情况下也对未使用的词法变量发出警告。所以,字节编译你的文件!

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

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

.PHONY: compile
compile : $(OBJECTS)

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

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

于 2013-05-17T08:13:13.777 回答