Search code examples
emacsline-numbersvimpulse

Relative Line Numbers In Emacs


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)

Solution

  • (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)