简体   繁体   English

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

[英]Highlight non-local variables in Emacs Lisp

In js2-mode, global variables are automatically highlighted for me: 在js2-mode中,全局变量会自动为我突出显示:

在此输入图像描述

How can I do the same in Emacs lisp? 我怎么能在Emacs lisp中做同样的事情? I'd like to be able to highlight flymake-log-level and barr in the following: 我希望能够突出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

It's possible to take advantage of the byte-compiler by installing flycheck . 通过安装flycheck可以利用字节编译器。 Flycheck is an alternative to flymake and has support for Elisp. Flycheck是flymake的替代品,并且支持Elisp。

It won't help you for the first example, but it will for the second (assuming the necessary require is present): 它对第一个例子没有帮助,但它会在第二个例子中出现(假设存在必要的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'

There's also hl-defined.el , a minor mode that does almost exactly what is described in the question. 还有hl-defined.el ,这是一种次要模式,几乎完全按照问题中的描述进行操作。 Install it, then run hdefd-highlight-mode in an emacs-lisp-mode buffer. 安装它,然后在emacs-lisp-mode缓冲区中运行hdefd-highlight-mode You can then run the command hdefd-cycle until you are only showing variables that aren't already defined. 然后,您可以运行命令hdefd-cycle直到您只显示尚未定义的变量。 This gives something like: 这给出了类似的东西:

hl定义的截图

(This isn't perfect, hl-defined doesn't recognise that fn is a parameter not a free variable, and it confuses the function list with the parameter used here. Still, it's very helpful for the use case described in the question.) (这不是完美的,hl-defined不能识别fn是一个参数而不是一个自由变量,它会将函数list与这里使用的参数混淆。但是,它对问题中描述的用例非常有帮助。 )

Finally, some packages include highlighting for the functions they define. 最后,一些软件包包括突出显示它们定义的功能。 For example, dash.el provides highlighting for its functions, plus the variable names it uses in anaphoric macros (ie it highlights it ). 例如, dash.el为其函数提供突出显示,以及它在回指宏中使用的变量名称(即突出显示it )。

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

I would say that this is quite a bit of work... 我会说这是相当多的工作......

The best way is to use font-lock-mode and add a new rule. 最好的方法是使用font-lock-mode并添加新规则。 Normally, rules contains a regexp to match something, however, it is also legal to use a function to do this. 通常,规则包含匹配某些内容的正则表达式,但是,使用函数执行此操作也是合法的。 This function could then search for identifiers, and for each identifier it finds it could check if it's bound locally by checking if the variable occurs in the parameter list, in a let , dolist or similar construct. 然后,该函数可以搜索标识符,并且对于每个标识符,它发现它可以通过检查变量是否出现在参数列表中,在letdolist或类似构造中来检查它是否在本地绑定。

An example of a package that similar things is cwarn mode , which highlight (among else) assignments inside expressions, for C-like languages. 类似事物的包的示例是cwarn模式 ,其突出显示( 除此之外 )表达式内的分配,用于类C语言。

I presume that it is even impossible to accurately highlight variable binding scopes, or at least not without actually involving the byte compiler (or parts thereof), or re-implementing parts of Emacs Lisp' semantics. 我认为甚至不可能准确地突出显示变量绑定范围,或者至少没有实际涉及字节编译器(或其部分),或者重新实现Emacs Lisp语义的部分。

The key problem are macros. 关键问题是宏。 These are not hygienic in Emacs Lisp. 这些在Emacs Lisp中不卫生。 Thus any macro can introduce arbitrary local bindings . 因此, 任何宏可以将任意本地绑定 In fact, many macros do so, for instance dolist , condition-case and pcase from the standard library, or the anaphoric list processing functions from dash.el , to name a popular 3rd party library.. 实际上,许多宏都是这样做的,例如来自标准库的dolistcondition-casepcase ,或者来自dash.el的照应列表处理函数,以命名一个受欢迎的第三方库。

With these macros, it becomes impossible to determine the variable scoping from the syntactic context only. 有了这些宏,则无法确定仅从语法方面的变量范围。 Take the following example: 请看以下示例:

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

Without knowing about condition-case and --each , are err and it locally bound? 不知道condition-case和 - --each ,是errit在本地绑定? If so, in which sub-expressions are they bound, eg is err bound in all sub-expressions, or just the handler form only (the latter is the case)? 如果是这样,它们绑定哪些子表达式,例如在所有子表达式中是err绑定,还是仅仅是处理程序表单(后者是这种情况)?

To determine variable scope in such cases, you either need to maintain an exhaustive whitelist of macros along with their binding properties, or you need to expand macros to determine their binding properties dynamically (eg look for let in the expanded body). 要在这种情况下确定变量范围,您需要维护宏的详尽白名单及其绑定属性,或者需要扩展宏以动态确定其绑定属性(例如,在扩展的主体中查找let )。

Both of these approaches amount to a lot of work when implemented, and have shortcomings. 这两种方法在实施时都相当于很多工作,并且有缺点。 A whitelist of macro definitions is almost naturally incomplete, incorrect and outdated (just look at the complex binding semantics of pcase ), while expanding macros requires the macro definition to be present, which is not always the case, for instance if you are editing Emacs Lisp using the aforementioned dash.el, without having this library installed actually. 宏定义的白名单几乎自然是不完整的,不正确的和过时的(只是看看pcase的复杂绑定语义),而扩展宏需要存在宏定义,但情况并非总是如此,例如,如果您正在编辑Emacs Lisp使用前面提到的dash.el,实际上没有安装这个库。

Still, expanding macros is probably the best effort, and even better, you don't need to implement it your own. 尽管如此,扩展宏可能是最好的努力,甚至更好,你不需要自己实现它。 The Emacs Lisp byte compiler already does this, and it warns about references to free variables, and with lexical binding enabled also about unused lexical variables. Emacs Lisp字节编译器已经这样做了,并且它警告了对自由变量的引用,并且还启用了关于未使用的词法变量的词法绑定。 So, byte compile your files! 所以, 字节编译你的文件!

At best, avoid calling byte-compile-file from within your running Emacs, and instead write a Makefile to byte compile in a fresh Emacs instance, to have a clean environment: 最好避免在运行的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 $<

In more complicated libraries, use the -L flag to Emacs to set up a proper load-path for compilation. 在更复杂的库中,使用-L标志来为Emacs设置适当的load-path以进行编译。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM