用Rust扩展Emacs能力

学习一门语言,最好的方式就是在实践中学习使用。自接触Rust语言一来,我一直在寻找使用这门语言的机会。

前几个月,我接触到了 ubolonto 的 GitHub - ubolonton/emacs-module-rs: Rust binding and tools for Emacs's dynamic modules ,这个crate让用Rust开发Emacs dynamic module成为可能。

于是在与Emacs联手大战Rust编译器后,我整理出了这篇文章,希望可以破除一部分Emacs hacker对dynamic module “望而生畏” 的心态,同时对不了解Rust和emacs-module-rs的Hacker一份中文的简易教程。

TOC(被discourse裁剪,请点击大图查看完整列表):

文章地址:用Rust扩展Emacs功能 | NIL


写这样的一篇文章需要耗费很多时间和精力来踩坑以及和想玩游戏的欲望斗智斗勇,如果你们觉得这篇文章对你有帮助,欢迎移步 我 | NIL 进行捐赠。

注意:虽然我是管理员,但是捐赠的金额为本人所有,与论坛无关

32 个赞

还发现有 Haskell、OCaml、Nim、Go 的对 emacs-module.[c,h] 的绑定,是不是能跟 C 双向 FFI 的都行?(比如 Rust 既能调用 C 函数,从 C 中也能用 Rust?)

1 个赞

是的,只要能输出C格式动态库的,都可以写module。某种意义上vim用户“可以用很多语言写插件”的所谓很吹爆的点我看来就是伪命题。

  1. 只要能导出C ABI的动态链接库,Emacs就可以通过 emacs-module.h 这块利用
  2. Emacs一直都可以基于文本RPC的来做到不同语言互操作,小到jieba,大到LSP,EAF,都是成功的例子
  3. 就算其他语言再用得怎么爽,最后还是要面对内置的 Emacs Lisp/VimL – 最后一里路的胶水代码总要写吧!
2 个赞

这篇文章写的太好了。

2 个赞

像 Racket、Common Lisp、Python 行不行?它们好像跟 Emacs Lisp 类似都需要一个 Virtual Machine,所以不行?

对,他们需要独立的VM来支持语言运行,嵌入Emacs会很麻烦(只能通过dynamic module把VM实现嵌入Emacs,然后执行这些代码)

某些Scheme(Yes,Chicken)可以直接编译出C来,这种比较有戏

还有的,把别的语言编译到Elisp(如 GitHub - quasilyte/goism: Not a fan of Emacs Lisp? Hack Emacs in Go! ),运行时还是要用Elisp VM,这种炫技成分大,实际作用不大。

GNU Guile 好像也行,手册里有介绍怎么从 C 使用 Guile:

咦,这样可行的话,用 Guile 扩展 Emacs 这个目的不已经实现了吗?

估计不是这个目标吧,如果只是简单的扩展的话,当初 GuileEmacs 可是更进一步呢,而且 GuixSD 上也有了勉强可以跑的 GuileEmacs 版本呢

guilemace 是打算替换内置的 elisp vm, 目的不太一样

guilemacs基本上是挂了

对啊,感觉挺可惜的,我还挺想看看 guilemacs 换上 guile 3 是什么效果呢,之前还看到有人连着用几个月的 guilemacs ,唉。

我看emacs devel上讨论,可能emacs核心维护者有点质疑guile的维护能力,怕它撂挑子

2 个赞

倒是 gccemacs 我感觉合并到master的可能性不小

恩,感觉最近 native-comp 的开发还挺活跃的,体验也不错。

我发现即使使用了

emacs::plugin_is_GPL_compatible!();

emacs还是会报

Error in private config: config.el, (module-not-gpl-compatible /home/firstlove/projects/greeting/greeting.so)

emacs version:GNU Emacs 28.0.50

这种情况不确定是不是Emacs开发版的问题,建议给出确切的提交名,方便我测试。或者你先用26.3自测

emacs26.3(manjaro)在使用emacs -q后依然会出现同样的错误:

Debugger entered--Lisp error: (module-not-gpl-compatible "/home/firstlove/projects/greeting/greeting.so")
  require(greeting)
  eval((require (quote greeting)) nil)
  elisp--eval-last-sexp(nil)
  eval-last-sexp(nil)
  funcall-interactively(eval-last-sexp nil)
  call-interactively(eval-last-sexp nil nil)
  command-execute(eval-last-sexp)

目前还没遇到这样蹊跷的问题,我只知道 plugin_is_GPL_compatible 宏展开成

#[no_mangle]
#[allow(non_upper_case_globals)]
pub static plugin_is_GPL_compatible: ::std::os::raw::c_int = 0;

你可能需要开一个issue给emacs-module-rs的作者

直接在c文件里面加上

int plugin_is_GPL_compatible;

不行吗?

哈哈,是rust,看成c了。

写的太好了,省了好多精力 :+1:

1 个赞