elisp的eval方法似乎不太好用

感谢 @xuchunyang 在我另一贴 elisp如何解析http响应头 的回复, 我用elisp解析了http响应头,并自定义了http响应头,自定义的内容大致如下:

Buffer: cms.org
Elisp: (next-line 3)(org-end-of-line)(org-cycle)

这里Buffer名字是因为emacs获取http响应结果并加载这一过程是异步的,要不响应当前操作;加Elisp代码是要在buffer中加载完org文本后触发执行。

这里就出现了需要将字符串转为elisp代码执行的问题,且必须是在指定的buffer中不能是其它地方,我用了网上找到的这一段代码:(eval (car (read-from-string (format "(progn %s)" string))))

这样可以解决问题,但是返回的org文本是表格形式,最后的 (org-cycle)将文本渲染成表格本来速度很快的,用上eval后就肉眼可见的卡那么一下。我感觉应该是eval这个方法有性能问题。

想问一下这个问题是否有办法解决,或者说emacs在指定buffer中执行服务端返回的elisp代码有什么好的形式没有,如果能做到像浏览器中执行服务端发回的js一样就在好不过了。 安全问题 就暂不考虑了

如果这个问题解决不了,我就不把elisp代码写在http响应头里了,换其它办法

一次 eval 还不至于产生肉眼可见的问题:

(benchmark-run 100000 (1+ 1))
;; => (0.0017700000000000007 0 0.0)
(benchmark-run 100000 (eval '(1+ 1)))
;; => (0.026227000000000004 0 0.0)
(benchmark-run 100000 (eval (car (read-from-string "(1+ 1)"))))
;; => (0.077767 0 0.0)

否则也不可能被如此大量使用:

$ rg '\(eval\s' ~/.emacs.d/elpa/ -g '*.el' | wc -l
371
$ rg '\(eval\s' ~/emacs-repo/lisp -g '*.el' | wc -l
672

应该检查一下你传给 eval 的语句是不是有问题,在执行 eval 的前后做了什么。

写了一段测试代码,最后发现你的看法是对的,一次eval还不至于产生肉眼可见的问题,产生卡顿应该是在执行eval的前后做了什么,但我却发现复现不了这个情况了,包含大量链接的org表格渲染时也没卡 :joy:

测试代码没找出问题,但写了就发在这里做个备份吧;要先创建一个temp.org的buffer,在运行elisp代码

(let ((url-request-method "POST")
	  (url-request-extra-headers `(("Content-Type" . "Content-type:application/json")))
      (url-request-data (json-serialize '(buffer "temp.org")) ) )
  (url-retrieve 
   "http://localhost/t/测试.php"
   (lambda (status)
     (forward-line 1);http状态码
     (let (org-buf (elisp "(next-line 3)(org-end-of-line)") body)
       (while (re-search-forward "^\\([^:]*\\): \\(.+\\)"
                                 url-http-end-of-headers t)
         (cond ((equal "Buffer" (match-string 1))
                (progn (setq org-buf (match-string 2))))
               ((equal "Elisp" (match-string 1))
                (progn (setq elisp (match-string 2)))) ))
       (delete-region (+ (point) 2)(point-min))
       (setq body (decode-coding-string (buffer-string) 'utf-8-auto-dos))
       (with-current-buffer org-buf
         (erase-buffer) (save-excursion (insert body))
         (eval (car (read-from-string (format "(progn %s)" elisp))))
         ;;(save-buffer)
         )
       ))))

服务器端的代码为:

header_remove();
header("Content-Type: text/org");
$json=json_decode(file_get_contents("php://input"),1);
header("Buffer: ".$json["buffer"]);
header("Elisp: (next-line 3)(org-end-of-line)(org-cycle)");
echo "|分类|数量|分类|数量|分类|数量\n";
for($i=0;$i<30;$i++){
  echo "|classify|".$i."|classify|".$i."|classify|".$i."\n";
}

动态执行代码,你可以写成函数,intern动态查找并执行。

emacs的表现力比web差的太远,org-roam已经开了个好头。

对笔记工具感觉文本编辑功能更重要,表现力其次。

html表现力强大,但编辑html时感觉关注点会不在笔记内容本身,会去想在另一个界面的展现效果是怎样的,怎么去完善,然后又加css、又加js.

而编辑org文本比编辑html舒服多了,不仅所见即所得还是当前页面立即得到,编辑与展现在同一个页面不需要分开(markdown也是分开的),关注点也能保持在笔记内容本身上

对markdown的编辑器不太熟悉,不知道是否有也能做到文本编辑与展现是在同一页面的

typora 应该就算吧?

不如直接在HTTP reponse发送数据,然后Elisp一边处理

比如数据格式

(:action scroll :data '())

然后Elisp这边先用read提取数据,然后根据action的值dispatch代码(通过pcase的模式匹配)

看了一下typora ,感觉这个就是,不喜欢markdown的文本编辑和预览页面分开,现在这个理由也没有了,markdown也是所见即所得了。

如果这个也能自定义链接以及链接中运行任意代码,那这个和org-mode几乎没有多少区别了。表现力上还比org更强大一些。

感觉仿佛看到了文本编辑器取代office的希望,RMS的愿望 也有了实现的可能