这都是开放的,随时欢迎提交新补丁
最新版的EAF已经包括这个功能啊,谢谢补丁。
我是 evil 用户,但不用 space/doom emacs,没有一个专门的 leader-key
比如在 normal 下 ,我把 SPC 全局绑定为打开 buffer-list , 逗号是弹出一个最常用的功能的 hydra ,还有如 q,s 都有对应不同的全局函数。由于 eaf 的按键已经很接近 vim 了,所以对 eaf-mode 我是直接关闭 evil 的(这和进入 eaf 默认用 evil-emacs state 效果差不多,所以也不去 require eaf-evil),但我还是想在 eaf 下用 SPC 来切 buffer,用逗号弹出一个全局的 hydra 等等。 如果用
(eaf-bind-key comma-hydra/body "," eaf-browser-keybinding)
的设置方式,会使得在 text focus 状态下没法输入逗号。
我当前临时处理方案是写个函数判断是否处于 focus,如下
(defun eaf-insert-or-comma ()
(interactive)
(if (eaf-call-sync "execute_function" eaf--buffer-id "is_focus")
(eaf-py-proxy-insert_or_zoom_in) ;; any insert_or_* command
(comma-hydra/body)))
(eaf-bind-key eaf-insert-or-comma "," eaf-browser-keybinding)
由于我有好几个这样的按键,每个都得这么绑定,重复代码比较多,另外执行到这些按键时每次执行都要给 python 端通信,确认是否 “is_focus", 导致在 text focus 下输入 SPC 逗号时有一点点延迟。
不知道你有没有这种需求,使得可以在 eaf 下更方便地对非控制键设置 emacs 内部的命令?
或者 @manateelazycat 能不能考虑在 emacs 这边同步一个类似 eaf–is_focus 的变量,不用每次和 python 通信?
尝试写一个补丁吧,实在看不懂再问我,先自己锻炼一下。
好的,感谢,有空尝试一下
今天看了 eaf 代码,参照着 caret 的部分,在 core/webengine.py 里的以下两个函数加了一个同步 focus 状态的操作,发现几行代码代码就行了,这个接口封装地真方便,python emacs 一体了 @manateelazycat
@interactive(insert_or_do=True)
def focus_input(self):
''' input in focus.'''
if self.focus_input_js == None:
self.focus_input_js = self.read_js_content("focus_input.js")
self.eval_js(self.focus_input_js)
eval_in_emacs('eaf--sync-text-focus-state', ["'t"])
@interactive
def clear_focus(self):
''' Clear the focus.'''
if self.clear_focus_js == None:
self.clear_focus_js = self.read_js_content("clear_focus.js")
self.eval_js(self.clear_focus_js)
eval_in_emacs('eaf--sync-text-focus-state', ["'nil"])
eaf–sync-text-focus-state 定义在 app/browser/eaf-browser.el 里
(defcustom eaf-browser-text-focus-state nil
"Indicates whether text is being focus"
:type 'boolean)
(defun eaf--sync-text-focus-state (text-focus-status)
"Toggle text focus state given TEXT-FOCUS-STATUS."
(setq eaf-browser-text-focus-state text-focus-status))
这样 emacs 这边就有一个判断是否 text-focus 状态的变量, 绑定按键用以下方式后,focus 时按逗号之类的就不卡了
(defun eaf-insert-or-comma ()
(interactive)
(if eaf-browser-text-focus-state
(eaf-py-proxy-insert_or_zoom_in)
(comma-hydra/body)))
(eaf-bind-key eaf-insert-or-comma "," eaf-browser-keybinding)
有了一个状态标识之后, eaf mode 下本身就相当于有 vim 的 normal 和 insert 状态,可以类似地加一些 hook或 advice 来判断状态变化,例如 参考 根据evil和内置输入法的状态调整modeline的背景色 里根据 eaf 是否 focus 来改变 modeline 颜色
(以上 clear focus 显示有点问题,但主要展示 modeline 变化)
现在新的折腾需求是想把 text-input 内容也同步到某个变量或者隐藏的 buffer 里, 这样用 rime 或 pyim 的一些 predicate 功能就能统一,不用打开 edit buffer, 不知道可不可行。
另外,我用 doom-modeline,一般是用左下角那个小圆点来表示状态, @seagle0128 请教这个怎么根据某个变量值来切换颜色?
状态同步的代码发个补丁吧,我想办法融合到EAF里面去,感谢。
看这里
(defsubst doom-modeline--evil ()
"The current evil state. Requires `evil-mode' to be enabled."
(when (bound-and-true-p evil-local-mode)
(doom-modeline--modal-icon
(let ((tag (evil-state-property evil-state :tag t)))
(if (stringp tag) tag (funcall tag)))
(cond
((evil-normal-state-p) 'doom-modeline-evil-normal-state)
((evil-emacs-state-p) 'doom-modeline-evil-emacs-state)
((evil-insert-state-p) 'doom-modeline-evil-insert-state)
((evil-motion-state-p) 'doom-modeline-evil-motion-state)
((evil-visual-state-p) 'doom-modeline-evil-visual-state)
((evil-operator-state-p) 'doom-modeline-evil-operator-state)
((evil-replace-state-p) 'doom-modeline-evil-replace-state)
(t 'doom-modeline-evil-normal-state))
(evil-state-property evil-state :name t))))
好的,eaf 和 eaf-browser 我都发 PR 了
感谢答复,我先了解一下
我看了一下补丁, 两个补丁都是有问题的,正确方法如下:
同步更新浏览器的 focus 状态到 elisp 端
在 emacs-application-framework/webengine.py at 6ca09592cc269c36ce24f86710845fee0ffcf831 · emacs-eaf/emacs-application-framework · GitHub 这个函数里面, 添加 is_focus 判断, 然后调用 elisp 函数保存状态到 Emacs 端。
eventFilter 这个Qt函数的含义是, 任何事件都会通过这个函数过滤, 包括鼠标和键盘事件, 这样可以保证不漏任何事件, 你现在的补丁只是在调用 focus_input 或者 clear_focus 的时候才生效, 如果用户点击一下鼠标, 你现在的补丁就不会生效。
Elisp 同步 focus 状态
因为EAF支持浏览器多标签, eaf-browser-text-focus-state-p 需要用 defvar-local 来定义, 同时需要用 setq-local 来设置, 这样才能保证不同标签的 JavaScript focus 状态是隔离的,你现在的补丁会导致多个标签的 focus 状态相互影响。 为了保证区分不同标签的 focus 状态, python 端要传递 self.buffer_id 给 Elisp 函数, Elisp 函数接到 Python 消息后, 需要用 macro eaf-for-each-eaf-buffer
来找到 buffer-id 对应的 EAF buffer, 具体参考 emacs-application-framework/eaf.el at 6ca09592cc269c36ce24f86710845fee0ffcf831 · emacs-eaf/emacs-application-framework · GitHub 的实现。
你已经理解了 EAF Python<->Elisp 双向通信的原理, 但是逻辑还不够严密, 继续调整补丁吧, 加油!
我目前用 eaf-browser 主要还是在一个 buffer 下浏览页面,还在从 chrome+surfingkeys 慢慢转过来, 在 emacs 也几乎不用鼠标,确实都没想到这些问题 ,感谢这么详细的说明,近期有空我再改一波。
你要的这个功能我已经完成了, 可以看看具体的实现, Elisp这边同步的变量是 eaf-buffer-input-focus , 只要检查 eaf-buffer-input-focus 就知道 EAF 浏览器是否处于 JavaScript 元素的输入状态了。
谢谢!我 pull 最新的提交后试了一下,发现eaf epc 很容易断掉,emacs -Q 还是有这问题:
deferred error : (error "Process EPC Server 2 <127.0.0.1:35400> not running")
怀疑是 event 过滤时调用 self.buffer.is_focus() 太频繁了(这个函数应该还得调用 js 代码?),大量按键、鼠标操作都会触发同步,测试时删掉这行确实就正常了。
感觉在键盘鼠标事件层面来过滤工作量比较大,大部分时候这些事件是不会触发 focus 状态变化的,不知道 focus 变化是否有一个统一的函数接口(类似 emacs 里打开文件的操作底层基本都调用 find-file),在这个接口加 hook来检查可能更好,事件只有真正触发该接口时再同步状态
qt层面肯定有focus状态变化的信号,但是网页内部的输入焦点只能靠js才能算出来,js一般就是检测input,textarea等标签的状态,没有固定的focus状态信号。
现在改成页面加载完或者用户手动调用按键命令的时候会更新。
更完备的需要 jump marker 或者按Tab键的时候也要一并处理一下。
这样就不会太消耗平常的性能。
我更新了代码,尝试发现在正常模式下用 i 进入文本输入框后,需要连续输入两个以上字符之后状态才会同步到 focus 状态,同时从文本输入框退出后也要先按一两个其他按键才能同步到 focus 状态。我不太清楚哪些会触发页面加载,但鼠标点击和滚动似乎是不会触发。
以我个人的体会,会在 emacs 里用 evil 操作浏览器,那基本上:
-
95% 场景下是键盘操作
在前几层楼你提到说鼠标事件也应该同步前,我都没想到 eaf-browser 里还可以用鼠标,我之前也没用过(可能某些场景下无意识地滑过滚轮,但基本想不起来),你提到这个之后我最近测试代码才用上几次鼠标
-
基本只按 i 进入 text focus, 按 esc 退出
因此着重优化的就是 focus_input 和 clear_focus ,这两个操作不要去调用 self.is_focus(), 因为我看它还得调用 js 获得所有文本来检查,很多时候可能跟不上按键速度。 所以建议加上以下两句
@interactive(insert_or_do=True) def focus_input(self): ''' input in focus.''' if self.focus_input_js == None: self.focus_input_js = self.read_js_content("focus_input.js") self.eval_js(self.focus_input_js) eval_in_emacs('eaf-update-focus-state', [self.buffer_id, "'t"]) @interactive def clear_focus(self): ''' Clear the focus.''' if self.clear_focus_js == None: self.clear_focus_js = self.read_js_content("clear_focus.js") self.eval_js(self.clear_focus_js) eval_in_emacs('eaf-update-focus-state', [self.buffer_id, "'nil"])
-
剩下 5% 的情形
这里面可能最重要的是鼠标点击,因为输入文本的时候一般就是鼠标点上去,然后输入,所以MouseButtonRelease 就可以(这种情况可以在按键失效,或者放松状态下单手操作之类的时候备用):
if event.type() == QEvent.Type.MouseButtonRelease: eval_in_emacs("eaf-update-focus-state", [self.buffer_id, self.buffer.is_focus()])
其他滑动滚轮、按键的事件都不太需要跟踪,因为还可以用类似前文讨论过的,加上一个状态标识,比如 modeline 颜色,文字等,可以根据标识来判断在哪个状态,如果状态不对,只是多按一个 esc 或者 i 的成本(但更多情况就是习惯性按 esc 了 )。实在要的话,加上一个两三秒间隔 timer 来同步也行。
以上配置个人用的还是比较顺畅。总结来就是:
- buffer 之间状态隔离
- 对最常用的 clear_focus, focus_input, 鼠标 release 进行状态同步,并且只有鼠标点击需要真正让 js 检查是否 focus
再次感谢大佬,我知道你不用 evil,但这么忙还来一起折腾这个特性,十分感谢并希望理解:pray:t2:
你再发个补丁吧,我看了没问题来合并。
好的,刚发 PR 了
我不用doom和spacemacs,但是我用leader,我尝试了官网require eaf-evil的方法行不通。不知道前辈们有没有方法可以设置在eaf-mode下使用原来evil下的一套leader打头的快捷键