Cloel: 用Clojure扩展Emacs

可以。但我们这里讨论的是设计一套用 clojure 写插件系统,就好像在搭网线,网线搭好后访问什么网页都可以,系统搭建好后想写什么插件就写什么插件,到那时在讨论要不要写一套 LSP 才更好。

waypipe 怎么样

今天已经实现了基本框架:

  1. Elisp到Clojure的异步调用方法
  2. Clojure到Elisp的异步消息发送
  3. Clojure到Elisp的异步方法调用
  4. Clojure到Elisp的同步函数执行
  5. Clojure到Elisp的同步变量读取

这5个接口基本上就是Clojure扩展Emacs的全部核心函数。

接下来的工作:

  1. Clojure接到的Elisp请求全部封装到线程中,保证Clojure的处理函数不会卡住
  2. Elisp这端支持多App, 不同的App可以启动不同的Clojure进程, 这样一个框架可以支持多个应用的独立启动、停止和重启
  3. Clojure核心框架代码封装成一个 Clojure 库, 这样新的扩展应用就写写Clojure的业务代码就好了, 不用拷贝代码

从实际的体验看, clj -M 来启动 Clojure 的启动速度很快, 执行速度也很快,关键是两边都是Lisp风格的代码, 不认真品, 都区分不出来两边的代码。

9 个赞

method :eval 和 method :async-eval 的格式怎么不一样。为何 :async-eval 就可以有一个额外参数 :func 指定函数名称,eval 就只能是列表?

改成 apply/funcall 能减少岐义

已经修改了, 等我周末再折腾折腾

Clojure 有很多种异步的方案,卡的问题感觉都不是问题,可能是哪里打开方式不对。

对接现有的 nrepl 或 socket repl 还是会更好一些,最大的好处就是可以直接对接上 clj, cljs, babashka 之类的,后端选择就很多。

特别是 babashka 很适合这个场景。

  • 100ms 以内的启动时间
  • clojure 完全一样的语法
  • 自己有常用库的 pod ,基本上覆盖了这个实用场景
  • 本身是二进制发布的
  • 同样用了 vthread 支持异步(内置的 core.async 实现)

我感觉用 clojure 比 babashka 的优势在这个场景下只有一个,就是 clojure 可以用 libpython-clj 去无缝的调用 python 里面的函数。libpython-clj 虽然名称朴素了点,但其实是个很给力的东西。就看怎么取舍了。

3 个赞

我目前还是想构建了 python-bridge 一样的开发环境。

如果平常调试Elisp可以用 ielm, Clojure 调试用 Cider

Clojure 更像做成一个多进程TCP通讯的方式, 编译运行, 而不是解释执行, 我感觉解释执行容易把东西搞废。

clojure 还有 js版的

下面三点都搞完了:

  1. Clojure接到的Elisp请求全部封装到线程中,保证Clojure的处理函数不会卡住
  2. Elisp这端支持多App, 不同的App可以启动不同的Clojure进程, 这样一个框架可以支持多个应用的独立启动、停止和重启
  3. Clojure核心框架代码封装成一个 Clojure 库, 这样新的扩展应用就写写Clojure的业务代码就好了, 不用拷贝代码、

晚上试着写一个基于 cloel 框架的App出来。

4 个赞

Demo和文档都整完了, 欢迎大家试用, 一起用 Clojure 开发 Emacs 插件。

1 个赞

Cloel 吸取了 EAF 设计的经验, 基于 Cloel 开发Clojure插件是非常松散的, 只需要提供应用名称和应用Clojure文件的路径即可。

Clojure的开发体感很好, JVM生态很全, 而且从我实际体验看, 启动速度飞快, 大家习惯了, 我觉得基本上可以像 Lua 对 NeoVim 的作用一样大。

(defn ^:export elisp-eval-async [func & args]
  (let [id (generate-call-id)]
    (send-to-client {:type :call :id id :method :eval-async :func func :args args})
    (future
      (let [result (apply elisp-call :eval-async func args)]
        result))))

猫大你看这是不是不对,怎么先 async-eval 了一次又在 future 中 async-eval 了第二次,两次的协议还不一样。另外 el 那边也没有 handle 第一次 call 的协议啊!

等我重构哈,zsbd

我早上把Clojure和Elisp代码都重构了一下, 清理了很多开发过程中的调试代码。

1 个赞

clojure(script)用了3,4年了。 开发体验相比于大部分主流语言真的是好很多(更不用说elisp)

欢迎大佬一起玩呀

架构已经搭建好了,欢迎大家贡献卡emacs的地方,我以后周末有空就去一个一个用clojure加速

3 个赞

今天开发了基于 cloel 的第一个插件 reorder-file

这个插件的作用很简单, Emacs传递一个Buffer的内容给 Clojure, Clojure 利用多线程代码自动分析文本的内容, 重新改变序号后, 把新的内容传递给Emacs进行更新, 具体的效果可以看这个补丁的内容 Reorder todo.md · manateelazycat/lsp-bridge@09e7f32 · GitHub

因为整个文件分析的代码都在外部 Clojure 进程的子线程执行的, 这样遇到超大文件的时候, 可以让 Clojure 自己去慢慢分析, 等分析完了以后再问用户是否要替换。

如果原来用Elisp实现也是可以的, 但是如果一旦文件非常大以后, Elisp就会卡住Emacs, 这时候用户啥也不能做。

用 cloel 开发这个插件很简单, 代码只有70行就轻松实现了, 而且Clojure的开发体验也是Lisp风格, 全程和Elisp的风格是一样的, 开发心流很一致。

1 个赞

学着做了一个生成随机 quote 的插件:

2 个赞