使用 Emacs frame 实现 Peek definition

发布于 2024-10-09 20:20:00 字数 4329 浏览 1 评论 0

在许多 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 就是简单地自动化这些步骤:

  1. 找到光标所在符号起始位置的绝对位置,以像素为单位。
  2. 创建一个新的不可见框架,其中包含当前 buffer。
  3. 将新 frame 定位在符号开始的正下方。
  4. 跳转到光标所在的符号定义处。
  5. 让 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

哆啦不做梦

暂无简介

0 文章
0 评论
22 人气
更多

推荐作者

风暴

文章 0 评论 0

乐玩

文章 0 评论 0

英雄似剑

文章 0 评论 0

秋风の叶未落

文章 0 评论 0

luoshaoja

文章 0 评论 0

吴彦文

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文