用Python扩展Emacs已经非常爽了, lsp-bridge和EAF都通过外部进程和多线程RPC互调用极大的增强了Emacs的能力和性能。
最近准备开发一个Clojure + Elisp的编程框架,实现类似 python-bridge 的效果:当Elisp处理性能不够或者分析大量数据会产生GC时,通过RPC来调用Clojure函数来加速Emacs。
用Clojure的好处是, 不论是写Emacs这边的逻辑还是写外部加速代码都是Lisp风格的, 体感一样,这样贡献者会相对多一点。
当初实现EAF的动力是想在Emacs中实现多媒体界面,实现lsp-bridge的动力是现有的lsp client性能实在是太拉垮, 写代码卡一卡的没感觉。
这段时间卖公司的硬件实在太忙了, 想给自己下班找点有趣的事情做,调节一下心情, 大家有没有想增强Emacs的地方?
如果是我感兴趣的地方, 我会造一把新的 Clojure 的编程框架(“锤子”), 欢迎大家贡献“钉子”, 哈哈哈哈。
21 个赞
有一个算不上增强的点: 之前论坛里有一个 emacs-rainbow-fart, 如果能实现 Clojure 和 Emacs 之间的通信的话, 可以借助 Overtone 做合成器, 可以实现类似 mikutap 这样 “声音污染” 效果的给每个键都绑定一个触发音, (更好玩的效果估计是可以捏一个状态机来转换).
配合 holo-layer 的光标特效估计会很好玩.
又: 想问问大佬 RPC 和类似于 SLY (或者 cider) 这样的方式有那些不同呢?
2 个赞
rpc主要可以实现两边进程代码自动化,就像lsp-bridge一样。
sly目前我感觉更像解释环境。
对接上 nrepl 就能打通大部分生态,clj, cljs, bb 都可用。
但是不知道可以用来做什么的样子。
2 个赞
Emacs 可以在远端运行server,然后在浏览器打开界面,登陆就好了。我用Rstudio server或者Python 的jupyter notebook,它们可以完全部署在server端,然后网页访问,提供了近乎一致的GUI界面。不知道Emacs是否可以这样做,从而在terminal下图片预览等无法完成事情,可以在web端做到。
1 个赞
其实我感觉只需要clojure的多线程和库生态支持,所有耗时的emacs插件代理給clojure来开发
2 个赞
它这个要在emacs端起一个httpd的server,感觉不需要呀
1 个赞
感觉大佬说的其实是 Clojure 配合 Web 来实现一套新的 Emacs 前端, 类似 NeoVim 做的事情。
然后 Clojure 这个 Web Server 来和 Emacs Server 通讯, 让 Emacs 主要对接Elisp插件。
1 个赞
不敢在猫哥面前当大佬。 我猜测应该是这样的,不管是Clojure 还是Python 来实现。当然这样的代价,就是在web端,我们网页打开的界面,基本丢失了我们Emacs配置的快捷键设计,大部分情况下需要鼠标操作了,但用Emacs在Web端的一个主要目的不是为了一直生活在Web端,只是提供一个GUI界面来观测程序中产生的图片或者pdf文件等。
2 个赞
yqu212
14
我和 @DogLooksGood 的想法一致。如果要用clojure扩展emacs,那最好对接nrepl。clojure作为lisp语言,相对于其他主流开发语言最大的特色恐怕就是它的repl了,整个开发流程都和repl密不可分。有了nrepl整个生态就进来了,clojure、java、python、js的的库都可以用上,并且可以用lisp的方式来写。另外,有了repl开发插件时的难度会降低很多,在eaf里面用其他语言开发插件现在debug还是比较麻烦的。
我用过一段时间clomacs,基本上能满足我的需求,就是性能上感觉有问题,搞不好就与httpd的通信方式相关。
1 个赞
Clojure启动速度太慢了,如果用CL应该启动速度上会好很多。SLIME里面已经有一个简单的RPC客户端(SWANK),SLY作为SLIME的分支应该也有一个类似的。
1 个赞
sly 的话有一个 slynk:eval-in-emacs
(需要开启 sly-enable-evaluate-in-emacs
为 t
), 可以做 lisp 反向调用 emacs. 一个例子就是可以让 lisp 给 emacs 发送一张图片然后显示在 mrepl 里面:
;; common lisp
(defun insert-image (path)
(let ((path (uiop:native-namestring (truename path))))
(slynk:eval-in-emacs
`(sly-insert-image ,path)
t)))
;; elisp
(defun sly-insert-image (path)
(with-current-buffer (sly-mrepl--buffer-name sly-default-connection)
(save-excursion
(goto-char sly-mrepl--output-mark)
(insert-before-markers "\n\n")
(previous-line)
(insert-image (create-image path)))))
效果是如下所示: (如果做得好的话估计还能像 CLIM 一样)
这种可以做成类似于 Mathematica 的 Dynamic 的效果:
- Emacs 前端只需要做显示图片, 或者显示一个渲染的 HTML 或者 QT 组件之类的, 绑定一个可计算的实例的 uuid
- 后端在每一次更新的时候都去找那个 uuid 对应的渲染过程然后去重新渲染对象
- 然后做一个类似
sly-clear-mrepl
的函数去清空释放不需要的绘制对象, 防止像 Mathematica Notebook 那样绘制的元素多了把 Notebook 给卡死.
(感觉还是 holo-layer 来做这种事情会更爽… )
cl 的话缺点还是缺少一些辅助框架吧, 如果像 Python 一样有一堆实用的库可以调的话估计会轻松很多.
3 个赞
主要是自己写了一些自用的代码, 不太方便迁移到 Python 里面, (其实实在不行也可以多个语言混着调用, 哪个语言某个功能做得比较好就用那个语言去实现特定的功能).
比如调用 Mathematica 的符号运算 (Mathematica 没有一个很好的宏展开, 所以用 Lisp 糊了一个翻译器给 Mathematica):
# shell create MMA server
wolframscript -code "socket = SocketConnect[\"tcp://127.0.0.1:2333\", \"ZMQ_REP\"];
While[True,
expr = ByteArrayToString[SocketReadMessage[socket]];
res = Check[ToExpression[expr], $Failed];
WriteString[socket, ToString[FullForm[res]]];
]"
然后 Lisp 的简单调用形式如下:
(defun to-wolfram-name-form (symbol &optional (first-upcase nil))
(let ((name (str:join "" (str:split "-" (format nil "~:(~a~)" symbol)))))
(cond (first-upcase name)
(t
(str:concat (str:downcase (str:s-nth 0 name))
(subseq name 1))))))
(defun lisp-expr-to-wolfram-lang (expr)
(cond ((atom expr)
(etypecase expr
(keyword (to-wolfram-name-form expr))
(symbol (to-wolfram-name-form expr))
(integer (format nil "~d" expr))
(float (format nil "~f" expr))
(string (format nil "~s" expr))))
(t
(let ((fn (car expr))
(arg (cdr expr)))
(format nil "~a[~{~a~^, ~}]"
(case fn
(+ "Plus")
(- "Plus[#[[1]],Minus[Total@#[[2;;-1]]]]&@List")
(* "Times")
(/ "Div")
(expt "Power")
(otherwise (to-wolfram-name-form fn t)))
(mapcar #'lisp-expr-to-wolfram-lang arg))))))
(defmacro wolfram-eval (&body body)
`(send-to-wolfram
(format nil "~{~a~^;~}" (mapcar #'lisp-expr-to-wolfram-lang ',body))))
效果就是前面在 mrepl 中插入绘图的那个效果:
(wolfram-eval
(export "~/Buff/test.png"
(plot (sin x) (list x -1 1))))
做好宏展开和一些绑定的话基本上根本感觉不到是在写别的语言. 类似的工作应该也有 py4cl (call python from common lisp). 不过缺点就是可能性能或者安全性上会有一些问题? 但是个人自用应该是完全没有问题的.
1 个赞
emacs 调用 lisp 的话还有两个 sly-eval
和 sly-eval-async
(这个就不会卡 Emacs), 目前我是把 emacs 当成一个 common lisp 的前端, 启动的时候连接到后台的 lisp server 上, Clojure 不清楚能不能这样做, 如果可以的话应该就不会有前面说的 Clojure 启动速度的问题.
1 个赞
我感觉从现代库的生态来讲, Python生态最好, Clojure在Web计算的生态更好? 而 Common Lisp 除了计算能力强以外, 现代一点的库生态比较差?
个人理解, 可能不对
1 个赞