From: nj104@cus.cam.ac.uk (Neil Jerram)
Newsgroups: gnu.emacs.help,gnu.emacs.sources
Subject: Tracking Line Numbers in Emacs
Date: 21 Dec 92 17:12:01 GMT
Distribution: gnu
Organization: U of Cambridge, England

There have recently been postings in gnu.emacs.help about

(i) Tracking the line number in Emacs' buffers, and
(ii) Automatic horizontal scrolling of Emacs windows.

The posted solution to (i) involved redefining every Emacs command
which could move the cursor up or down, while the solution to (ii)
used an external process to nudge Emacs every now and then and cause
it to check the horizontal position of the cursor.

Clearly it would be possible to apply the solution method of (ii) to
the problem of (i) (and vice versa, if anyone wanted to...).  Here a
piece of Emacs Lisp which does just that.  Its disadvantage is that
there is a delay (half a second on average) before the correct line
number is shown. Its advantages are (a) that the solution is far less
cumbersome than the redefining-functions approach; (b) the previous
solution will fail if any new Lisp function which moves the cursor
vertically is loaded and executed, whereas the new solution will still
work correctly.

Neil Jerram.

;---------------cut here-----------------------------------
; linenumbers.el
;
; A mix'n'match of linenumbers.el by Ajay Shekhawat
;              and hscroll.el by Wayne Mesard
;
; OR...
;
; ``If it's acceptable to have 1 second polling for horizontal scroll
;   checking, then why not do line numbers the easy way ?''
;
; Mished and mashed by Neil Jerram <nj104@cus.cam.ac.uk>,
; Monday 21 December 1992.
;
;;; This program is free software; you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 1, or (at your option)
;;; any later version.
;;;
;;; This program is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;;
;;; The GNU General Public License is available by anonymouse ftp from
;;; prep.ai.mit.edu in pub/gnu/COPYING.  Alternately, you can write to
;;; the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
;;; USA.

(provide 'linenumbers)

; ----------------
; Public variables
; ----------------

(defvar linenumber-poll-period "1"
  "*Interval between checking the current line number (in seconds).
If nil, it will test continuously (but this is not recommended, 
since it will slow down your machine and annoy Emacs).")

(defvar linenumber-mode nil 
  "Whether linenumber-mode is enabled for the current buffer.
Ordinarily set indirectly (via \\[linenumber-mode]).  However,
   (setq-default linenumber-mode t)
will put buffers in Linenumber mode by default.
Automatically becomes local when set.")

; ----------------
; Public functions
; ----------------

(defun linenumber-mode (&optional onoff)
  "Toggle Line# mode in the current buffer.
With arg, turn Line# mode on if arg is positive, off otherwise."
  (interactive "P")
  (if (null linenumber-process)
      (progn
	(make-variable-buffer-local 'linenumber-mode)
	;; So that the last line in this func will do the right thing
	;; when default value is t and this is the first buffer.
	(setq linenumber-mode nil)
	(setq linenumber-process 
	      (start-process "linenumber" nil
			     (concat exec-directory "wakeup")
			     (or linenumber-poll-period
				 "0")))
	(set-process-sentinel linenumber-process 'linenumber-sentinel)
	(set-process-filter linenumber-process 'linenumber-filter)
	(process-kill-without-query linenumber-process)))
  (setq linenumber-mode (if onoff
			    (> (if (numberp onoff) onoff
				 (prefix-numeric-value onoff))
			       0)
			  (not linenumber-mode)))
  (setq mode-line-buffer-identification
	(if linenumber-mode
	    '("Emacs: %1b" " l." line-number)
	  '("Emacs: %1b"))))


(defun linenumber-shutdown ()
  "Disable Line# mode in all buffers, and terminate the Line# subprocess.
This command is an \"emergency switch\" for use if the subprocess starts
hogging up too many system resources."
  (interactive)
  (if (eq 'run (process-status linenumber-process))
      (kill-process linenumber-process))
  (setq linenumber-process nil))

; ---------
; Internals
; ---------

(make-variable-buffer-local 'line-number)

(defvar linenumber-process nil)

(defun which-line ()
  "RETURN the current line number (in the buffer) of point."
  (save-restriction
    (widen)
    (save-excursion
      (beginning-of-line)
      (concat (1+ (count-lines 1 (point)))))))

(defun linenumber-filter (ignore ignore)
  ;; Don't even bother if we're not in the mode.
  (if linenumber-mode
      (setq line-number (which-line))))


(defun linenumber-sentinel (ignore reason)
  (linenumber-shutdown)
  (error "Whoa: the Line# process died unexpectedly: %s." reason))

; ---
; End
; ---

;---------------cut here-----------------------------------