Emacs中的相对行号

29
有没有类似于Vim 相对行号的东西适用于 emacs?我使用 vimpulse,如果有这个功能会非常方便!我懂一些 Lisp,所以如果没有这个功能,我可以尝试自己制作,只要给我指点方向即可。

更新: 感谢正确的回答,我想出了这个代码,它将在与 vimpulse 配合时显示当前行的 1 和上一行的 -1。

谢谢所有帮助过我的人!我知道这不完全是 Vim 所做的,但是 Vim 从零开始的相对行号有什么用呢?愚蠢的 Vim。
(defvar my-linum-current-line-number 0)

(setq linum-format 'my-linum-relative-line-numbers)

(defun my-linum-relative-line-numbers (line-number)
  (let ((test2 (1+ (- line-number my-linum-current-line-number))))
    (propertize
     (number-to-string (cond ((<= test2 0) (1- test2))
                             ((> test2 0) test2)))
     'face 'linum)))

(defadvice linum-update (around my-linum-update)
  (let ((my-linum-current-line-number (line-number-at-pos)))
    ad-do-it))
(ad-activate 'linum-update)

就像Steve所说的那样,他在Emacs中使用vimpulse。http://www.emacswiki.org/emacs/Vimpulse - phils
@phils - 啊,谢谢,我漏掉了帖子的那一部分。 - jtahlborn
Steve,我已经擅自更新了你的版本,并修复了一个错误,以便在linum-format函数输出中使用正确的面孔。 - phils
顺便提一下:“相对行号”这个功能,当缓冲区在多个窗口中显示时,它应该如何表现:是显示相对于各自点的行号,还是相对于当前选定窗口的点(如果是这样,那么如果没有一个缓冲区的窗口是选定窗口,会发生什么)? - Stefan
6个回答

41
在Emacs 26.1版本中,有一个内置的行号模式(display-line-numbers-mode)。启用它并设置(setq display-line-numbers 'relative)可使用相对行号。

9
对于我来说,(setq display-line-numbers-type 'relative) 在全局范围内启用了它。(Emacs 26.3) - loki
@loki display-line-numbers-type 已经完美运行。 - rox
2
还应该添加(global-display-line-numbers-mode)以自动启用所有缓冲区的行号。 - jdhao

13

(2012-03-16:行号现在已经右对齐,并以正确的字体显示。)

问题出在自定义的linum-format函数被调用时,point已经被linum-update-window移动到了相应的行,所以我们不能再使用它来确定两行之间的差异;否则每一行都会输出0。

虽然有linum-before-numbering-hook,但它是在将point移动到缓冲区开头之后才运行,因此对于我们的目的没有什么用。

以下代码通过为linum-update添加advice来解决该问题,它可以存储当前行号,以便自定义的linum-format函数可以使用。

为了右对齐数字,我最初使用了硬编码格式字符串%3d,因为单个窗口显示超过100行代码的可能性不大。但是,如果您喜欢follow-mode,或者在同一个缓冲区内有多个窗口,那么这种情况就变得极其可能;因此,现在的代码会动态计算所需的列数。使用linum-before-numbering-hook使得其比默认的dynamic linum格式更加高效。

请注意,如果您注释掉add-hook,则会使用更快的非动态方法。

(defvar my-linum-format-string "%3d")

(add-hook 'linum-before-numbering-hook 'my-linum-get-format-string)

(defun my-linum-get-format-string ()
  (let* ((width (1+ (length (number-to-string
                             (count-lines (point-min) (point-max))))))
         (format (concat "%" (number-to-string width) "d")))
    (setq my-linum-format-string format)))

(defvar my-linum-current-line-number 0)

(setq linum-format 'my-linum-relative-line-numbers)

(defun my-linum-relative-line-numbers (line-number)
  (let ((offset (- line-number my-linum-current-line-number)))
    (propertize (format my-linum-format-string offset) 'face 'linum)))

(defadvice linum-update (around my-linum-update)
  (let ((my-linum-current-line-number (line-number-at-pos)))
    ad-do-it))
(ad-activate 'linum-update)

1
要去掉负号,只需调用 abs 函数获取数字的绝对值。例如:(number-to-string (abs (- line-number my-linum-current-line-number))) - phils
我已经使用这段代码编写了relative-linum.el在EmacsWiki上,以便其他人可以使用它。请查看:http://www.emacswiki.org/emacs/relative-linum.el 。我希望这样是可以的 :) - Wilfred Hughes
当然可以(如果你在stackoverflow的URL后面加上“by phils”,这样就可以进行归属),除此之外,我建议您删除您添加的内容:您的relative-linum-jump与它调用的forward-line函数基本相同,因此没有什么作用。而且您正在破坏一个不应该使用的绑定。 - phils
1
juanjux:尝试运行 emacs -Q,加载此代码后查看是否仍然存在该问题。在我编写此代码时,它实际上比默认的linum*更有效率(但我认为这在期间已得到解决)。无论如何,如果你使用这段代码出现问题,而在未修改的linum中没有问题,那我会感到惊讶?如果还没有这样做,请尝试对代码进行字节编译。您还可以调查由Emacs维护者编写的“nlinum”包,它是linum的更高效替代品。 - phils
我不知道这个方法是否适用于你的问题,但是我在自定义次要模式中使用 (save-excursion (goto-char (point-max)) (format-mode-line "%l")) 取代 count-lines,在大型缓冲区中可以获得很大的速度提升。http://emacs.stackexchange.com/a/3822/2287 目前答案中有几个实例 -- 即 line-number-at-pos 依赖于 count-lines,而你也单独使用了 count-lines - lawlist
显示剩余5条评论

11

1
提供此功能的最完整和最新的库似乎实际上是linum-relative,其中Melpa提供了一个快照ELPA包。 - sanityinc
@sanityinc,使用package.el或el-get在我的电脑上无法从melpa安装linum-relative。你是如何让它运行的? - Sam Hasler
@Sam 从 Melpa 安装 linum-relative 对我来说很顺利。你所说的“无法安装”是什么意思?你遇到了错误,还是在包列表中没有看到它? - sanityinc
在emacs24 + windows上,与phils的代码一样,存在相同的重绘问题。 - juanjux
PS:但在Linux GUI和控制台上运行得非常完美,问题似乎只出现在Windows Emacs上。 - juanjux
“linum-mode” 对于我的稍微大一点的 org 文件运行非常、非常和非常慢。display-line-number-mode 似乎解决了这个问题。 - Student

5

相关的是,如果你只想移动到特定的一行,ace-jump-mode 提供了一个命令 ace-jump-line-mode,它可以让你跳转到屏幕上指定的一行。但是,它使用字母而不是数字来表示行:

ace-jump-mode 截图


4

请查看M-x linum-modelinum-format变量。

linum-format是在`linum.el'中定义的一个变量。
它的值是动态的。

文档:

用于显示行号的格式。
可以是一个格式字符串,如"%7d"、`dynamic'以根据需要调整宽度或者是一个作为其参数调用行号并应该评估为要显示在该行上的字符串的函数。
另请参阅`linum-before-numbering-hook'。


啊,我之前没注意到,我可以给它一个函数来创建显示在行上的字符串...很好的想法,我可能会尝试在这个周末自己做一个。 - Steve

4

Emacs 28,这对我有用

(global-display-line-numbers-mode 1)
(setq display-line-numbers-type 'relative)

可能有帮助

之前我使用了"linum-relative"包,但它与"diff-hl"发生冲突:在版本控制的差异区域中无法看到行号,原生行号正常工作!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接