使用 Emacs frame 实现 Peek definition
在许多 IDE 中,都有 peek definition 这个功能,它在不离开当前 buffer 的情况下,在弹出框中打开定义(函数、类、符号等)。
大多数情况下,跳到一个定义,然后跳回来就足够了,所以为什么要离开当前 buffer 去重新加载一个全新的 buffer,然后再回去重新加载原 buffer 呢?这让人分心。
peek definition 的一个例子如下所示:
我在 Emacs 中找过替代方案,但显然,所有涉及弹出框的解决方案都很慢而且缺乏必要的功能。弹出框显示文本的空间有限,而且没有字体锁定 (font locking)。你甚至不能搜索,或者即使可以搜索,也及其困难和缓慢。
最近,在一次跳转代码定义的过程中,我遗漏了跳转链起始的原 buffer 的踪迹,例如我从 file1.c 开始,然后跳转到 file2.c, file3.h, file4.c,如此继续,直到我忘记我是从 file1.c 开始的 (file1.c 可能很长,很难记住它的名称)。
这让我想在另一个独立的 frame/buffer 中执行整个代码跳跃过程,而不要打乱我的当前 buffer。
我尝试改进流程如下:
- 使用不同的 buffer:最初,我创建另一个 buffer,并从那里开始。然而,通常情况下,这个另一个 buffer 也是一个有用的 buffer。
- 使用不同的 workspace:我使用 eyebrowse ,这个包可以创建一个全新的工作空间,并将之前的窗口配置保存为另一个工作空间。事实上,你可以轻松地在不同的窗口配置之间切换。这个解决方案很快就变得很麻烦,因为我必须记住哪个工作空间用于浏览代码,更不用说为我还因为其他目的创建了 5 个其他工作空间。这很快成为管理工作空间的负担。
就在这时,我突然想起了 frame。没错,Emacs frame!它非常适合这个用例。我可以在这个 frame 中做任何我想做的事,当完成时,只需用 C-x 5 0
关闭它即可。通过使用 frame,我的工作空间(使用 eyebrowse) 的数量保持在一个可管理的数量内。
再等等,如果 Emacs frame 足够小,那它不是和弹出框一样么?而且还保留了 buffer 的每个特性(语法高亮、代码跳跃等).更何况,制作一个 frame 比其他弹出式解决方案要轻量级得多。
现在,在 Emacs 中实现 peek definition 就是简单地自动化这些步骤:
- 找到光标所在符号起始位置的绝对位置,以像素为单位。
- 创建一个新的不可见框架,其中包含当前 buffer。
- 将新 frame 定位在符号开始的正下方。
- 跳转到光标所在的符号定义处。
- 让 frame 可见。
下面是一个 peek definition 的弹出框:
在本例中,我使用 rtag-find-symbol-at-point
函数进行跳转。但是你可以用任何一个函数来查找定义,只要它会跳转到对应 buffer 就行。
最后,代码如下:
(defun rtags-peek-definition ()
"Peek at definition at point using rtags."
(interactive)
(let ((func (lambda ()
(rtags-find-symbol-at-point)
(rtags-location-stack-forward))))
(rtags-start-process-unless-running)
(make-peek-frame func)))
(defun make-peek-frame (find-definition-function &rest args)
"Make a new frame for peeking definition"
(when (or (not (rtags-called-interactively-p)) (rtags-sandbox-id-matches))
(let (summary
doc-frame
x y
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 1. Find the absolute position of the current beginning of the symbol at point, ;;
;; in pixels. ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(abs-pixel-pos (save-excursion
(beginning-of-thing 'symbol)
(window-absolute-pixel-position))))
(setq x (car abs-pixel-pos))
;; (setq y (cdr abs-pixel-pos))
(setq y (+ (cdr abs-pixel-pos) (frame-char-height)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 2. Create a new invisible frame, with the current buffer in it. ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(setq doc-frame (make-frame '((minibuffer . nil)
(name . "*RTags Peek*")
(width . 80)
(visibility . nil)
(height . 15))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 3. Position the new frame right under the beginning of the symbol at point. ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(set-frame-position doc-frame x y)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 4. Jump to the symbol at point. ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(with-selected-frame doc-frame
(apply find-definition-function args)
(read-only-mode)
(when semantic-stickyfunc-mode (semantic-stickyfunc-mode -1))
(recenter-top-bottom 0))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 5. Make frame visible again ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(make-frame-visible doc-frame))))
然后,将新命令绑定到一个快捷键,并尝试一下:
(global-set-key (kbd "C-c p") 'rtags-peek-definition)
要关闭 peek 帧,只需使用 C-x 5 0
(运行删除 frame 命令)。你可以将之绑定到 l 另一个快捷键让关闭 frame 更容易,例如 f12 键。
我使用 Emacs 越多,就越开始意识到 frame 是多么有用。特别是在 Emacs 26 之后,有一个选项可以让从 OS 任务栏中删除框架,让你不能使用 Alt+Tab 切换到 Emacs 中创建的任何子框架中去。
有了这个特性,您可以创建许多 Emacs frame,而不会造成 Alt+tab 混乱。也许是时候拥抱 frame 了。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: 唯快不破的 TDD
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论