大数据

Emacs上下文引起的Elisp宏展开及编译问题

问题

在开发leanote-emacs这个包的时候,我定义了一个spaceline的segement,代码如下:

(defun leanote-spaceline-status ()
  "Install spaceline status, need spaceline 2.x version."
  (interactive)
  (require 'spaceline)
  (spaceline-define-segment leanote-status-seg
    "show the leanote status"
    (when leanote-mode
      (powerline-raw
       (s-trim (leanote-status)))))
  (spaceline-spacemacs-theme 'leanote-status-seg)
  (spaceline-compile))

其中spaceline-define-segment是在spaceline中定义的一个宏。我发现,如果我将leanote的安装放在spacemacs的启动安装包里时。当emacs启动完成后,我执行M-x leanote-spaceline-status打开leanote的modeline的status时会报如下错误:

(invalid-function spaceline-define-segment)

我感到很困惑,为什么说是invalid-function无效的函数?可它明明是一个宏呀!

定位

不过,令我感觉奇怪的是,当我的emacs启动完成后(注:我emacs是安装了spaceline这个包的),我再手工安装leanote-emacs时不会报这个错。我将两次安装时leanote.el编译后的leanote.elc文件打开看,发现它们不一样,如下:

报错时leanote.elc文件

正确的结果

原因

emacs是有上下文的(如一些变量及包),当编译leanote.el这个文件的时候,上下文不一样,最后导致的编译结果会不一样。第一次将leanote放在spacemacs启动安装里时,因为这个时候spaceline这个feature还没有载入到emacs的上下文中。因此,编译leanote.el文件时,编译器并不知道spaceline-define-segment它是一个宏,因此,它就不会对其进行宏展开,只认为它是一个普通的函数。第二次,当我手工进行安装的时候,这个时候上下文中已经存在spaceline这个包,也就是spaceline-define-segment这个宏的声明已经在emacs的上下文中。所以,编译器会将这个宏进行展开,而不会将它视为一个函数对待。