28

Does anyone know how if something like this Vim Relative Line Numbers exists for emacs? I use vimpulse, and man, that would be super handy to have! I know some lisp, so if it doesn't, I could try to make my own, if I got a point in the right direction.

Update: Thanks to the correct response, I came up with this, that will show 1 for the current line, and -1 for the previous line, for combining with vimpulse yanks and deletes.

Thanks a ton to all who helped! I know it is not exactly what Vim does, but what good is the Relative line numbers in vim that start at zero?? Silly 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)
phils
  • 71,335
  • 11
  • 153
  • 198
Steve
  • 1,596
  • 1
  • 10
  • 21
  • Like Steve said, he uses vimpulse in Emacs. http://www.emacswiki.org/emacs/Vimpulse – phils Jul 29 '11 at 16:52
  • @phils - ah, thanks, missed that bit of the post. – jtahlborn Jul 29 '11 at 17:50
  • Steve, I've taken the liberty of updating your version with a bug fix to use the correct face for the linum-format function output. – phils Mar 13 '12 at 13:09
  • BTW: Regarding such a "relative line number" feature, how should it behave when the buffer is displayed in several windows: should the "other windows" show line numbers relative to their respective points, or to the point in the currently selected window (and if so, what should happen if none of the buffer's windows is the selected window)? – Stefan Sep 29 '15 at 13:45

6 Answers6

39

In Emacs 26.1, there's a built-in line number mode (display-line-numbers-mode). Enable it and set (setq display-line-numbers 'relative) to use relative line numbers.

Aethanyc
  • 671
  • 1
  • 6
  • 4
13

(2012-03-16: line numbers are now right-aligned, and displayed in the correct face.)

The problem here is that by the time a custom linum-format function is called, point has already been moved (by linum-update-window) to the line in question, so we can no longer use it to establish the difference between the two lines; it would just print a zero for every line.

There is a linum-before-numbering-hook, but this is run after point has been moved to the start of the buffer, so that's not useful for our purpose.

The following code solves the problem by using advice for linum-update to store the current line number, so that it will be available to the custom linum-format function.

To right-align the numbers I initially used a hard-coded format string of %3d on the basis that a single window showing more than 100 lines of code was not terribly likely. If you're a fan of follow-mode, however (or simply have multiple windows on the same buffer), that circumstance becomes exceedingly likely; so the code now calculates the number of columns required dynamically. The use of linum-before-numbering-hook makes this more efficient than the approach taken by the default dynamic linum format.

Note that if you comment out the add-hook, the faster non-dynamic approach is used.

(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)
phils
  • 71,335
  • 11
  • 153
  • 198
  • Two questions. How can I have the negative line numbers without the `-` prefix? And how can I avoid the line numbers getting the same face settings at the line they represent? For example, line numbers for commented out lines get the same color applied as the comments themselves. – jimeh Mar 13 '12 at 12:06
  • 1
    To remove the minus sign, just call `abs` to get the absolute value of the number. i.e.: `(number-to-string (abs (- line-number my-linum-current-line-number)))` – phils Mar 13 '12 at 12:32
  • For question two, my code was missing a trick: when `linum-format` is a function, the return value is not automatically propertized with the linum face. I've updated the code above to take care of that. You can then `M-x customize-group RET linum RET` if you wish to modify the face further. – phils Mar 13 '12 at 13:01
  • Thanks, works great :)... I just have one last question, how do you right-align or left-pad the line numbers? For example, the line-numbers look left-aligned here: http://cl.ly/2J1i1T2B0Q0N13181g24 – jimeh Mar 14 '12 at 15:15
  • Thanks again :D... I did add a `abs` call though, as I find it just looks weird having negative numbers. And looks to me like `follow-mode` would only be an issue if you can get 1000 or more lines on the screen at once. But then that might be due to me removing the `-` character... lol – jimeh Mar 20 '12 at 00:15
  • I've taken this code and written relative-linum.el on EmacsWiki so others can use it. Check it out: http://www.emacswiki.org/emacs/relative-linum.el . I hope that's OK :) – Wilfred Hughes Jul 11 '12 at 13:30
  • Sure (if you add " by phils" after the stackoverflow URL, that would be fine for attribution), except I can only suggest removing the things you've added to it: your `relative-linum-jump` is basically identical to the `forward-line` function it calls, and so doesn't really serve a purpose; and you're clobbering a binding that you shouldn't be using. – phils Jul 11 '12 at 14:27
  • The solution works, unfortunately it also causes a lot of annoying "flashing" (slow redraws, I guess) when moving the cursor at least of Emacs24 + Windows. Ultimately this is something that should probably be implemented in the core. – juanjux Sep 12 '14 at 07:51
  • 1
    juanjux: Try running `emacs -Q` and see if you still have that issue after loading this code. At the time I wrote it, this was actually *more* efficient than the default linum (but I think that's been sorted out in the interim). In any case, I'd be surprised if you were having issues with this code that you weren't having with un-modified linum? Try byte-compiling the code, if you hadn't already done so. You might also investigate the `nlinum` package, which was written by an Emacs maintainer to be a more efficient replacement for linum. – phils Sep 12 '14 at 08:07
  • I do not know whether it will work for your answer, but I have obtained great speed increases in a custom minor mode by using: `(save-excursion (goto-char (point-max)) (format-mode-line "%l"))` instead of `count-lines`, which causes slow-downs in large buffers. http://emacs.stackexchange.com/a/3822/2287 You have a few occurrences in the answer as it stands now -- i.e., `line-number-at-pos` relies upon `count-lines` and you have `count-lines` separately as well. – lawlist Sep 28 '15 at 23:08
11

I just came across Scott Jaderholm's code for this, and remembered seeing this question, so I decided to post a link to the relevant lines in his .emacs.

Update: If you're using MELPA (and you should be!), just M-x package-install RET linum-relative.

sanityinc
  • 15,002
  • 2
  • 49
  • 43
  • 1
    The most complete and up-to-date library providing this functionality actually appears to be [linum-relative](http://www.emacswiki.org/emacs/linum-relative.el), for which [Melpa](http://melpa.milkbox.net/) provides a snapshot ELPA package. – sanityinc Dec 10 '12 at 19:57
  • @sanityinc linum-relative doesn't install from melpa for me with package.el or el-get. How did you get it to work? – Sam Hasler Feb 06 '13 at 17:30
  • @Sam Installing `linum-relative` from Melpa works fine for me. What do you mean by "doesn't install"? You get an error, or you don't see it in your package lists? – sanityinc Feb 07 '13 at 10:29
  • Same redraws problems on emacs24 + windows than phils' code. – juanjux Sep 12 '14 at 08:27
  • PS: but works perfectly on linux GUI & console, the problem seems to be Windows Emacs only. – juanjux Sep 12 '14 at 18:28
  • `linum-mode` runs **very, very, and very** slowly for my org files that are slightly larger. `display-line-number-mode` seems to solve this problem. – Student Dec 08 '19 at 18:23
5

On a related note, if you're only looking to move to a specific line, ace-jump-mode provides a command ace-jump-line-mode that lets you jump to a specific line visible on screen. It uses letters rather than numbers for lines, however:

ace-jump-mode screenshot

Wilfred Hughes
  • 29,846
  • 15
  • 139
  • 192
4

Look at M-x linum-mode and the linum-format variable.

linum-format is a variable defined in `linum.el'.
Its value is dynamic

Documentation:

Format used to display line numbers.
Either a format string like "%7d", `dynamic' to adapt the width as needed, or a function that is called with a line number as its argument and should evaluate to a string to be shown on that line.
See also `linum-before-numbering-hook'.

phils
  • 71,335
  • 11
  • 153
  • 198
aartist
  • 3,145
  • 3
  • 33
  • 31
  • Ah, I didn't see that before, that I can just give it a function to create the string shown on the line... good call, I might try making my own this weekend. – Steve Jul 29 '11 at 17:50
3

emacs 28, this worked for me

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

might be helpful

Earlier I was using "linum-relative" package, which was conflicting with "diff-hl": line numbers were not visible in version controlled diff regions, native line numbers are working fine!.

Javed
  • 5,904
  • 4
  • 46
  • 71