在spacemacs生成micro-state的同时为每个命令生成对应的执行并进入micro-state的函数

博客原文地址: http://liu233w.github.io/blog/2016/09/02/multiple-micro-state/

2 个赞

只要贴个链接就可以了,不要把同一个内容复制粘贴过来。不然的话,你和读者会面临两个版本的内容、两个讨论环境等等,比如这里的 TOC 并不能跳转。

不错。我也是试了下。但是不需要接受函数的话,可以直接用 bind-key.el 的 bind-keys

(defmacro jerry-bind-keys (map &rest bindings)
  (declare (indent 1))
  (let ((forms
         (mapcar (lambda (binding)
                   `(define-key ,map ,(car binding) #',(cdr binding)))
                 bindings)))
    `(progn ,@forms)))

(jerry-bind-keys global-map
  ("\M-1" . emacs-version)
  ("\M-2" . emacs-init-time))
     ==> (progn
           (define-key global-map "\261"
             (function emacs-version))
           (define-key global-map "\262"
             (function emacs-init-time)))

对,重定义一个命令会有用,但是参数列表可能会有问题。

哈,根据 bytecode 的话,参数表也能获得。

动态环境下,参数表能直接获得

(byte-compile (lambda (a b &optional c &rest r)))
     => #[(a b &optional c &rest r) "\300\207" [nil] 1]

静态环境下,能获得参数表的个数(也就是:必要参数个数,可选参数个数和是否含 &rest

(setq lexical-binding t)

(byte-compile (lambda (a b &optional c &rest r)))
     => #[898 "\300\207" [nil] 5 "

(fn A B &optional C &rest R)"]

把参数描述 898 改成 16 进制:

(format "%x" 898)
     => "382"

再对应这个表

0       6   7   8       14 15
xxxx, xxx,  x,  xxxx, xxx, x
^       ^   ^   ^       ^
---------   -   ---------
   min    &rest     max

可以发现,最多有 3 个参数,最少有 3 个参数,含有 &rest,所以可以推断出函数的参数必然形如:(var1 var2 &optional var3 &rest rest)


Emacs Bytecode 的文档:info ‘(elisp) Byte-Code Objects’ 或 Byte-Code Objects (GNU Emacs Lisp Reference Manual)

emacs的describe-function产生的帮助说明里面是有函数的所有参数的。我感觉可以研究这个来找到办法。

我刚刚发现了一个很奇怪的特性,如果使用函数documentation来返回函数的文档的话,在文档的末尾会有函数的声明。比如:

(documentation 'describe-function)

=>

"Display the full documentation of FUNCTION (a symbol).

(fn FUNCTION)"

而那个函数内部的文档底下是没有这个东西的。 但是如果我自己定义了一个函数,就没有这种东西。 documentation是一个C语言函数,看不了elisp代码。我也不知道这是什么原理了。

EDIT

我刚才生成了一个词法作用域的函数试了一下。 好像只有在词法作用域之下编译之后的函数才会在document的最后生成函数的声明。 如果是动态作用域就只能从byte-compile里面得到声明了。

这时候 documentation 就不行了。用 Emacs 25 的 elisp-get-fnsym-args-string 应该动态、静态都可以:

(elisp-get-fnsym-args-string 'next-line)
     => #("next-line: (&optional ARG TRY-VSCROLL)" 0 9 (face font-lock-function-name-face))

用 Byte code 的话,静态作用域下定义的函数的名字信息就没有了,这是为什么 lexical-binding 快的原因之一 [1]。我刚刚写了个 arglist-form,就用的是 Byte code。

https://github.com/xuchunyang/learn/commit/12540e9560c7339a3838cc44caef113f88c03421

(arglist-form #'emacs-version)
     => (&optional here)

(setq lexical-binding t)

(arglist-form '(lambda (_a _b &optional _c &rest _r)))
     => (a b &optional c &rest others)

  1. Emacs Byte-code Internals: Emacs Byte-code Internals
1 个赞

我觉得可以直接使用read来将byte-compile的返回值变成列表,比如:

(setf lexical-binding t)
(defun test1 (arg1 arg2 arg3 &optional arg4))

(read
 (replace-regexp-in-string
  "^.*\n\\|]$" "" (format "%s" (byte-compile 'test1))))

=>

(fn ARG1 ARG2 ARG3 &optional ARG4)

这样就可以用函数原来的参数名了

还有一个问题,貌似describe-function没有使用byte-compile,但是它是怎么获得函数的参数的呢?

是个办法,

看起来是:

(documentation #'next-line t)

我没发现参数表有什么作用,知道大概怎么回事就完了,具体细节也无所谓。与之相比,Emacs 的 Byte-code 应该更有趣:可以设计一门语言,解释或编译成 Emacs byte-code,然后就能用那种语言扩展 Emacs 了,不一定有什么实际价值,但是一定会非常有趣。

我找到了

(help-function-arglist 'test1)
=>
(arg1 arg2 arg3 &optional arg4)

_(:зゝ∠)_


这个函数里面用了一个indirect-function,如果函数没有编译的话,可以直接返回函数的完整定义,否则会返回一个编译之后的函数对象(就像byte-compile的返回值一样)


EDIT: 我是不是绕了个弯啊,command-execute可以直接执行interactive,那我生成的函数就不需要原来的参数了啊_(:зゝ∠)_

1 个赞