deno-bridge

恩,已经注意到这个项目了。相当有启发性。我准备参照这个思路做一个类似的英文断句工具(分割英文长难句),辅助一下我看英文小说。调研中。

BTW,使用机器学习相关的工具,多半只能用python了。学习 emacs python rpc 中。

提了一个 PR,希望这个插件模拟原生的 kill-wordbackward-kill-word 函数,这样比较方便 remap。

嗯嗯,已合并。

很好用, 特别是删除中文文章的时候。

反馈两个bug:

  1. 向左删除的时候,删除到行首就不能再继续往上一行删除了。
  2. 向右删除的时候,遇到逗号就删除不动了, 不是所有逗号都删除不动, 哈哈哈哈。

@stephanoskomnenos

删除,应该也可以和移动一样。判断一下字符,中文则按照分词处理。英文则使用原生的 kill-word。

然后行头,行尾,空格,标点可能需要特殊处理。

这应该是 deno-jieba 的问题,某些地方的英文逗号会被吞掉,在 token 里找不到。 image

原生的 forward-word 在遇到英文符号后面紧跟中文的时候会直接跳到下一个空格,而不是中文第一个词结束。

话说我刚刚在 vscode 里写了一个类似的,用的是 node binding 的 jieba-wasm。我没有根据中英文决定分词方式,而是完全交给结巴分词,这里没有出现类似的问题。所以这个 bug 应该在上游。

vscode-jieba

@manateelazycat

更新了一下 kill-word 和 backward-kill-word 的逻辑:

  1. 如果当前字符为标点符号或者空格,递归删除标点空格以及最近的一个word。
  2. 如果是单宽度字符(认为是英文)则使用原生的kill-word
  3. 如果是中文使用 jieba-kill-word

示例,在最右的逗号backward-kill-word 删除: 你好小明同学,,,,-> 你好小明

1 个赞

那估计是 deno-jieba 实现的有问题。

restart后,会弹出当前进程,看有没有正常启动

另外可以试试终端中直接去执行对应命令,例如我是用straight来接入的

deno run -A --unstable /Users/xxx/.emacs.d/.local/straight/build-28.2/deno-bridge-jieba/deno-bridge-jieba.ts deno-bridge-jieba 63613 6361

看能否正常运行

另外刚启动的时候,需要下载一些依赖,多等一会看看

1 个赞

谢谢,找到原因了,主要是我用的doom-emacs 配置,添加包的时候没有把 ts 文件放进来,所以默认的编译后的包目录下只有el文件,修改后可以了

(package! deno-bridege-jieba :recipe (:host github :repo "ginqi7/deno-bridge-jieba" :files ("*.el" "*.ts" )))
1 个赞

@nailuogGG @driftcrow 二位帮了大忙!谢谢! 我这边可以正常用了,附上配置:

(use-package websocket)
(use-package deno-bridge
  :straight (deno-bridge
    :type git
    :host github
    :repo "manateelazycat/deno-bridge"))
(use-package deno-bridge-jieba
  :straight (deno-bridge-jieba
    :type git
    :host github
    :repo "ginqi7/deno-bridge-jieba"
    :files ("*.el" "*.ts")))

1 个赞

@manateelazycat 麻烦问一下。

看了一下 deno-bridge 的源码。我发现在 elisp 端和 ts 端,都启动了一个 websocket server,然后都有各自的 websocket client 去连对方的 server。

感觉是不是没必要。建立好 websocket 连接之后应该就可以双向通信了。不需要两个Server 吧。

例如:在 deno 创建了 websocket server,在 elisp 建立连接

(websocket-open
 "ws://127.0.0.1:61028"
 :on-open
 (lambda (ws)
   (message "Connected")
   (websocket-send-text ws "Hello, WebSocket!"))
 :on-message
 (lambda (ws frame)
   (message "Receive: %s" (websocket-frame-text frame))
   )
 :on-close
 (lambda (ws)
   (message "Closed")))

两端都可以互相通信了。

是有什么特殊的考虑吗?

EAF和lsp-bridge 都是基于Python-EPC 去实现 Elisp <-> Python 互相通讯, 在Python-EPC 的实现是, 每边的通道是单向的, 所有两边都要有 server 和 client 的实现。

WebSocket确实是双向的, 一边建立通道以后两边都可以通讯, 但是我对TypeScript 的了解没有 Python 深入, 我不太清楚, 如果一个方向的通道很忙的时候, 是否会造成 WebSocket 对向通道堵塞?

所以当初保险起见, 还是在两边都实现 server client , 保证两侧通讯是独立的, 再加上本地进程间通讯开销忽略不计, 不像共有云服务设计时要谨慎打开不必要的长链接, 所以最终用了两个WebSocket.

如果最终检测发现, 一个 WebSocket 通信就够了, 确实可以删除另外一个 WebSocket, 我现在也没有想到删除一个 WebSocket 会有啥意外的情况, 哈哈哈哈。

1 个赞

最新有一个需求,需要获取浏览器的 cookie 来操作 chrome,发现 js 好像没有什么好的方法获取本地浏览器的cookie,而 python 有一个对应的包。

因此尝试用 python-epc 去实现插件。感觉使用起来没用 deno-bridge 方便:

  • 由于 epc 太老旧。新版的 Emacs 并不能直接使用,需要修改epcs
  • 每次写新的包,都需要在 emacs 和 python 端都启动server 通信,还需要手动注册哪些方法需要进行 rpc 调用。
  • 使用、管理没有 deno-bridge 简洁。

因此考虑把 deno-bridge 移植 python 版本。

看了一下 deno-bridge 的代码之后觉得,可以实现一个统一的 websocket-bridge:

  • 只在 emacs 里启动一个 websocket server
  • 外部程序,无论是python 或是 deno,或是任何支持 websocket 的应用都行,每一个插件就启动一个websocket client 建立连接。通信即可。

PS:其实需求上我只有python的需求。在网络上搜索某功能的实现,python 的还是比js 的多一些(大概后端程序员实现脚本还是更喜欢用python。)。

python epc 建议从 EAF 从fork出来用,python-epc有很多不用的功能,比如用ctables列举进程。

deno-bridge不如python-epc的一点是,它可以直接用sexp格式和elisp通讯,但是websocket目前还只能是字符串,复杂结构怎么双向传递还没有研究。

除非deno有一个很好用的库,并且性能更好,各种解析库生态还是不如python,JavaScript生态优势在网络库方面。

emacs -Q 测试,打开文件,insert-translated-name-insert, 输入你好, 报这种错是为啥呀

emacs 28.2, deno 1.27.0

Debugger entered--Lisp error: (websocket-closed #s(websocket-frame :opcode text :payload "[\"data\",[\" \344\275\240\345\245\275 \",\"comment\",\"init.el\",\"94b2f16b9..." :length nil :completep t))
  signal(websocket-closed (#s(websocket-frame :opcode text :payload "[\"data\",[\" \344\275\240\345\245\275 \",\"comment\",\"init.el\",\"94b2f16b9..." :length nil :completep t)))
  (if (websocket-openp websocket) nil (signal 'websocket-closed (list frame)))
  websocket-send(nil #s(websocket-frame :opcode text :payload "[\"data\",[\" \344\275\240\345\245\275 \",\"comment\",\"init.el\",\"94b2f16b9..." :length nil :completep t))
  websocket-send-text(nil "[\"data\",[\" 你好 \",\"comment\",\"init.el\",\"94b2f16b99178...")
  (if (member app-name deno-bridge-app-list) (websocket-send-text (symbol-value (intern-soft (format "deno-bridge-client-%s" app-name))) (json-encode (list "data" func-args))) (message "[DenoBridge] Application %s has exited." app-name))
  deno-bridge-call("insert-translated-name" " 你好 " "comment" "init.el" "94b2f16b9917859209156d0fa81937d0")
  insert-translated-name-retrieve-translation(" 你好 " "comment" "94b2f16b9917859209156d0fa81937d0")
  (let ((placeholder (insert-translated-name-generate-uuid))) (if (boundp 'insert-translated-name-placeholder-hash) nil (set (make-local-variable 'insert-translated-name-placeholder-hash) (make-hash-table :test 'equal))) (puthash placeholder (point) insert-translated-name-placeholder-hash) (insert-translated-name-retrieve-translation word style placeholder))
  (if (string-equal word "") (message "Nothing input, cancel translate.") (let ((placeholder (insert-translated-name-generate-uuid))) (if (boundp 'insert-translated-name-placeholder-hash) nil (set (make-local-variable 'insert-translated-name-placeholder-hash) (make-hash-table :test 'equal))) (puthash placeholder (point) insert-translated-name-placeholder-hash) (insert-translated-name-retrieve-translation word style placeholder)))
  insert-translated-name-query-translation(" 你好 " "comment")
  (let ((word (buffer-substring-no-properties translate-start translate-end))) (kill-region translate-start translate-end) (insert-translated-name-query-translation word insert-translated-name-active-style) (insert-translated-name-inactive nil))
  (cond ((string-equal (buffer-substring-no-properties start end) " ") (let ((word (buffer-substring-no-properties translate-start translate-end))) (kill-region translate-start translate-end) (insert-translated-name-query-translation word insert-translated-name-active-style) (insert-translated-name-inactive nil))) (t (move-overlay insert-translated-name-active-overlay translate-start translate-end)))
  (let ((translate-start insert-translated-name-active-point) (translate-end (point))) (cond ((string-equal (buffer-substring-no-properties start end) " ") (let ((word (buffer-substring-no-properties translate-start translate-end))) (kill-region translate-start translate-end) (insert-translated-name-query-translation word insert-translated-name-active-style) (insert-translated-name-inactive nil))) (t (move-overlay insert-translated-name-active-overlay translate-start translate-end))))
  (if insert-translated-name-active-point (let ((translate-start insert-translated-name-active-point) (translate-end (point))) (cond ((string-equal (buffer-substring-no-properties start end) " ") (let ((word (buffer-substring-no-properties translate-start translate-end))) (kill-region translate-start translate-end) (insert-translated-name-query-translation word insert-translated-name-active-style) (insert-translated-name-inactive nil))) (t (move-overlay insert-translated-name-active-overlay translate-start translate-end)))))
  (progn (if insert-translated-name-active-point (let ((translate-start insert-translated-name-active-point) (translate-end (point))) (cond ((string-equal (buffer-substring-no-properties start end) " ") (let ((word ...)) (kill-region translate-start translate-end) (insert-translated-name-query-translation word insert-translated-name-active-style) (insert-translated-name-inactive nil))) (t (move-overlay insert-translated-name-active-overlay translate-start translate-end))))))
  (if (and (boundp 'insert-translated-name-active-point)) (progn (if insert-translated-name-active-point (let ((translate-start insert-translated-name-active-point) (translate-end (point))) (cond ((string-equal (buffer-substring-no-properties start end) " ") (let (...) (kill-region translate-start translate-end) (insert-translated-name-query-translation word insert-translated-name-active-style) (insert-translated-name-inactive nil))) (t (move-overlay insert-translated-name-active-overlay translate-start translate-end)))))))
  insert-translated-name-monitor-after-change(144 145 0)
  self-insert-command(1 32)
  funcall-interactively(self-insert-command 1 32)
  command-execute(self-insert-command)

安装 crow-translate

安装完 crow-translate 还报同样的错

还有什么其他需要设置的么。

我用crow translate gui时,lingva 能正常使用,bing提示 “错误:无法在 web 版本中找到 Bing 凭据。“ 不知道会不会影响 insert-translated-name-insert