自从有了 orgmode 之后,就经常用它来写一些代码片段,快速验证想法。但是对于 elisp 似乎没有隔离,使得当前的配置常常受到污染。
后来找到 ob-async 这个包,可以用它启动一个异步的 Emacs 进程来执行代码(当然,它不仅仅用于 elisp)。但是用在 elip 上感觉比用在其它地方体验略显糟糕:因为这个异步执行过程很慢。另外还有个缺点是,不能指定运行特定版本的 Emacs。
然后我参考 async.el(把代码进行 base64 编码,然后传递给命令行)写了第一版的 with-emacs (gist:test-with-emacs-v1.el),基本满足了我的需求:
第一版用了好一段时间了,但始终有一点点不爽的是,这个版本是返回的是标准输入输出,所以当我即想打印 debug 信息,又想获得正常返回值的时候,就有点冲突了。
所以有了第二版(最上面的链接)。在第二版里,所有 (message ...)
的输出会重新定向到当前主 Emacs 的 *Messages*
,同时再把最末尾表达式的结果作为返回值。使用起来更向普通的函数。
除了一般使用之外,例如〈如何确定某个函数是哪一版本的Emacs引入的?〉这样的问题也容易解决了,或者给〈用 Emacs Web Server 写的 Emacs Lisp Docstring Server〉增加获得指定版本的 doc string 的功能。
第二版目前仍然只有一个 `emacs-with` 函数,打算以后扩展成可以指定加载第三方包/配置:
(with-emacs "/path/to/emacs"
:dir "/path/to/pkg-dir/"
:package "/path/to/pkg-file"
(do-something)
...)
增加对 org-babel 的支持,这点有待商榷,直接代码块里写 (with-emacs ...)
比增加一个 :with-emacs
头部参数似乎更容易理解。
EDIT: 1. with-emacs
增加了 with-emacs-define-partially-applied
宏; 2. 发布了新的包 ob-with-emacs
。(详见 15#楼)
EDIT2: 增加了 with-emacs-server
宏 和 :timeout
参数。(详见 18#楼)
5 个赞
我直接用了 async.el
:
(other-emacs-eval '(fboundp 'define-advice) "emacs-24.5")
;; => nil
(other-emacs-eval '(fboundp 'define-advice) "emacs-25.1")
;; => t
4 个赞
没想到你这早就有类似的包了 。
不过还是有点区别。你这个包接口规划比较细致、有针对性,with-emas
则像其它各种 with-
开头的宏一样使用,可以直接在里面 (message ...)
,也可以用来获得返回值。
也许应该反过来,把这个 repl 作为 with-emacs
的基础。
MELPA 和 GNU ELPA 总共有大概 4500 个包,Visual Studio Code 有 12088 个(光主题就有 2000 多个),大家还是可以多多写包,分享出来:
感觉melpa的审核过于严格,不仅lint要过,管理员还会提出自己的设计建议。
建议说明管理员不好当,必须去阅读和理解別人的设计,还要有一定的品味,能迅速发现设计当中不酷的地方。
建议当然是出于好意,但它是否必要,有待商榷。
一旦审核通过,就放任包的更新了,这恐怕是个大大的漏洞。
审核确实很严格,这是好事,至少包的质量比以前好了。当然,关于设计方面的建议确实有待商榷。也就是说,开发者必须说服管理员这么做是很cool的,包是很有价值的,而且没有其他问题,这样才能上线。
但是,上线后就随便你改了。以后更新是没有任何审核的,完全靠开发人员的职业素质了。这里漏洞是相当大的。
cireu
12
Emacs Lisp缺乏能用的静态分析工具,如果有,或许可以大大减轻管理员持续审核的压力
有lint啊,条件还挺苛刻。必须没有error和warning才能提交审核。
cireu
14
我说的是带上类型推导和类型标注的静态分析检查。现在不过要byte-compile clean还有一些格式上的检查(trailing whitespace,non lisp-case symbol,etc.)
1. with-emacs
增加了 with-emacs-define-partially-applied
宏
用这个宏来固定部分参数,免去每次都填写长参数的烦恼,例如:
(with-emacs-define-partially-applied
(t nil t)
(24.3 \"/path/to/emacs-24.3\")
(24.4-t \"/path/to/emacs-24.4\" t))
将生成以下宏定义,每个宏的参数列表都有些不同,因为有些参数已经固化了:
(with-emacs-t &rest BODY &key PATH)
(with-emacs-24.3 &rest BODY &key LEXICAL)
(with-emacs-24.4-t &rest BODY)
使用时分别等效于:
(with-emacs :lexical t ...)
(with-emacs :path "/path/to/emacs-24.3" ...)
(with-emacs :path "/path/to/emacs-24.4" :lexical t ...)
2. 发布了新的包 ob-with-emacs
虽说可以直接在代码块中用 with-emacs
,但由于 with-emacs
没有区分 print 和 message 输出,全部都显示在 *Message*
中了:
#+BEGIN_SRC emacs-lisp :results output
(with-emacs
(print emacs-version))
#+END_SRC
#+RESULTS:
: print 没有输出
这点与 orgmode 默认行为是不同的。
所以还是要一个 ob-with-emacs
,处理一下 print 输出结果。其实也正是之前写 with-emacs
的初衷:用来隔离 orgmode 中的 elisp block,防止污染当前 Emacs。
使用方法:
在 block 头部加上参数 :with-emacs "/path/to/{version}/emacs"
(path 可选)
#+BEGIN_SRC emacs-lisp :results output :with-emacs
(print emacs-version)
#+END_SRC
如果定义了 partially applied 函数 (详见 with-emacs-define-partially-applied
文档),
#+BEGIN_SRC emacs-lisp :results output :with-emacs-24.3
(print emacs-version)
#+END_SRC
1 个赞
毕竟人手不足,持续的审查是很不现实的。第一次的审查过了基本说明作者至少是靠谱的,知道自己在干啥,之后应该也可以信任。
对的,基本靠信任。我指的漏洞是,如果后续由其他开发人员维护可能就就会为所欲为。如果后续由随机抽查也会好很多。最好的方式是每次提交版本都有自动化的检查,这样能避免很多问题。
1. 增加 with-emacs-server
宏(有一段时间了,感谢 @xuchunyang 的建议)
如果对运行环境不是很严苛,不必每次都启动一个全新的进程,那么用 server 可以节省不少时间:
(with-emacs-server "foo"
:ensure t ;; 可选参数,确保 foo 存在,不存在则就地创建
(1+ 1))
2. 给 with-emacs-server
增加超时退出机制(昨天刚想到)
这样就可以任意使用,不必担心"僵尸"进程了:
(with-emacs-server "foo"
:ensure t
:timeout 100 ;; 空闲 100 分钟自动退出
(1+ 1))
或者设置默认退出时间:
(setq with-emacs-server-timeout 100)
(with-emacs-server "foo"
:ensure t
(1+ 1))
但仍然可以继续使用 :timeout
参数来覆盖默认值,比如当临时需要不同的超时时间或禁止超时退出:
(with-emacs-server "foo"
:ensure t
:timeout nil
(1+ 1))
EDIT: 可以考虑把 :ensure t
也设成可默认
2 个赞