EMACS-DOCUMENT

=============>随便,谢谢

使用Emacs frame实现Peek definition

在许多IDE中,都有peek definition这个功能,它在不离开当前buffer的情况下,在弹出框中打开定义(函数、类、符号等)。 大多数情况下,跳到一个定义,然后跳回来就足够了,所以为什么要离开当前buffer去重新加载一个全新的buffer,然后再回去重新加载原buffer呢?这让人分心。

peek definition的一个例子如下所示: peek-definition-vs.png (来源:微软)。

我在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的弹出框:

peek-definition-emacs.gif

在本例中,我使用 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了。