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

在Emacs Lisp中突出显示非局部变量,emacs,elisp,Emacs,Elisp,在js2模式下,全局变量会自动突出显示: 如何在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 我想说这是

在js2模式下,全局变量会自动突出显示:

如何在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

我想说这是一个相当多的工作

最好的方法是使用
字体锁定模式
并添加新规则。通常,规则包含一个regexp来匹配某些内容,但是,使用函数来匹配也是合法的。然后,该函数可以搜索标识符,对于它找到的每个标识符,它可以通过检查变量是否出现在参数列表中、
let
dolist
或类似结构中来检查它是否在本地绑定


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

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

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

使用这些宏,仅从语法上下文确定变量范围变得不可能。以以下为例:

(condition-case err
    (--each my-fancy-list
      (my-fancy-function it nil t))
  (error (message "Error %S happened: %s" (car err) (cadr err))))
在不知道
条件情况下
--每种
错误
是本地绑定的吗?如果是,它们绑定在哪些子表达式中,例如,在所有子表达式中是
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 $<
SRCS=foo.el
对象=$(SRCS:.el=.elc)
.伪造:编译
编译:$(对象)
%.elc:%.el
$(EMACS)-Q—批处理-f批处理字节编译$<
在更复杂的库中,使用Emacs的
-L
标志设置适当的
加载路径
进行编译。

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

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

还有一个小模式,它几乎完全符合问题中的描述。安装它,然后在emacs lisp模式缓冲区中运行hdefd高亮显示模式。然后可以运行命令
hdefd cycle
,直到只显示尚未定义的变量。这会产生如下结果:

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

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


值得注意的是,js2模式之所以可以做一些技巧,比如识别变量的作用域,是因为与其他语法突出显示模式不同,它实际上是。可靠地识别Elisp globals同样需要大量的代码…@dodgethesteamroller Emacs自然包括一个完整的Emacs Lisp解析器,它由
语法PPS
和friends公开。所以至少解析工作已经为您完成了。声明的全局变量(即通过
defvar
和朋友)也可以通过
special-variable-p
轻松识别。哦,我不知道
special-variable-p
。因此,当然可以突出显示声明的变量。非常好的观点,谢谢。我的Emacs配置实际上是以不同的方式给它上色(使用
font-lock variable face
),但我不确定是哪个更改导致了这种情况。这与上下文无关。看起来flycheck是这里最好的选择。@Wilfredhaughes此突出显示是自己安装的。感谢您使用我的项目:)不过有一点更正:flycheck不是Flymake的分支。最初它是基于API构建的,但现在它是一个独立的动态语法检查的无尘室实现。
(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'
;; Enable syntax highlighting of dash functions
(eval-after-load "dash" '(dash-enable-font-lock))