From: nj104@cus.cam.ac.uk (Neil Jerram)
Newsgroups: gnu.emacs.sources
Subject: Improved version of linenumbers.el
Date: 6 Jan 93 14:37:18 GMT
Distribution: gnu
Organization: U of Cambridge, England

Apologies for the linenumbers.el which I posted just before Christmas
and which contained some laughably fraudulent claims in its source
code (viz. the bit about (setq-default linenumber-mode t)...).

Here is a much improved version which does what it claims to do.  The
format of line number display is now customisable and *NB* most
variables and internal functions have been renamed to begin with "ln-"
rather than "linenumber-".

I'm afraid that `enjoy' is a transitive verb this side of the pond :-)

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.
;
; Wednesday 6 January 1993:
; * Modified considerably so that the claim about
;      (setq-default ln-mode t)
;   is true.
; * Added a new public variable ln-format to allow customisation of
;   the line number display.
; * Renamed most variables and internal functions so they begin
;   with "ln-" rather than "linenumber-".
; * Improved mode-line modification so as to interfere minimally with
;   the representation before linenumbers are switched on.
;   (Try linenumbers in *info* for an example of this.)
; * Improved linenumbers-shutdown so that it switches off linenumbering
;   in all exisiting buffers.  Otherwise you are left with a lingering
;   but meaningless number in your mode-line.
; * Generally tidied up, split isolated bits of functionality
;   (like starting the external process) into their own functions, etc.

;;; 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 ln-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 ln-mode nil 
  "*Whether ln-mode is enabled for the current buffer.
Ordinarily set indirectly (via \\[linenumbers]).  However,
   (setq-default ln-mode t)
will put buffers in linenumbers mode by default.
Automatically becomes local when set.")

(defvar ln-format "l.%d"
  "*A format string describing the presentation of the line number.
The \"%d\" should be included somewhere in this string: it stands for 
the line number itself.")

; As an example of alternative formats, you might write
;  (setq ln-format "-= line %d =-")
; in your `.emacs' file after the instruction to load in the lisp code.
; The code as it stands does not allow the format to be different in
; different buffers, but you can achieve this if you want it by adding
;  (make-variable-buffer-local 'ln-format)
; in your `.emacs' file.
;   Hard cases might want to use %o or %x instead of %d.
;   Changes to ln-format will not carry into other buffers until they
; become active (even 'though they may be visible in another window).
; This could be remedied, by making ln-filter cycle through all existing
; buffers every time it gets a signal from "Wakeup!", but I don't
; really think it's worth it.

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

(defun linenumbers (&optional onoff)
  "Toggle linenumbers mode in the current buffer.
With arg, turn linenumbers mode on if arg is positive, off otherwise."
  (interactive "P")
  (setq ln-mode (if onoff
		    (> (prefix-numeric-value onoff) 0)
		  (not ln-mode)))
  (or ln-mode
      (not ln-usual-mode-line)
      (setq mode-line-buffer-identification ln-usual-mode-line
	    ln-usual-mode-line nil))
  (and ln-mode
       (null ln-process)
       (ln-start-process)))

(defun linenumbers-shutdown ()
  "Disable linenumbers mode in all buffers, and terminate the linenumbers subprocess.
This command is an \"emergency switch\" for use if the subprocess starts
hogging up too many system resources."
  (interactive)
  (if (processp ln-process)
      (progn
	(set-process-filter ln-process nil)
	(set-process-sentinel ln-process nil)
	(if (eq 'run (process-status ln-process)) 
	    (kill-process ln-process))))
  (setq ln-process nil)
  (let* ((buffers (buffer-list))
	 (i (length buffers)))
    (while (>= (setq i (1- i)) 0)
      (set-buffer (nth i buffers))
      (linenumbers -1))))

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

(defvar ln-usual-mode-line nil
  "A buffer-specific variable to store the usual mode line style
for each buffer.  When linenumbering is switched off the mode line
should revert to its default style.")

(defvar ln-string nil
  "String printed in the mode line describing the current line number.")

(defvar ln-process nil
  "Variable holding the linenumbers process object.")

(defvar ln-initialised nil
  "A variable which is t once local variables have been set up.")

(defun ln-initialise ()
  "Sets up local variables and their initial values."
  (make-variable-buffer-local 'ln-mode)
  (make-variable-buffer-local 'ln-usual-mode-line)
  (make-variable-buffer-local 'ln-string)
  (setq-default ln-usual-mode-line nil)
  (setq ln-initialised t))

(defun ln-start-process ()
  "Starts the \"wakeup\" process for linenumbers mode."
  (setq ln-process (start-process "linenumbers" nil
				  (concat exec-directory "wakeup")
				  (or ln-poll-period "0")))
  (set-process-sentinel ln-process 'ln-sentinel)
  (set-process-filter ln-process 'ln-filter)
  (process-kill-without-query ln-process))

(defun ln-modified-buffer-id ()
  "Returns the modified form of mode-line-buffer-identification, q.v."
  (cond
   ((listp mode-line-buffer-identification)
    (append mode-line-buffer-identification '(ln-string)))
   ((stringp mode-line-buffer-identification)
    (if (string-match " $" mode-line-buffer-identification)
	(append (list mode-line-buffer-identification) '(ln-string))
      (append (list mode-line-buffer-identification " ") '(ln-string))))
   (t
    (append (list mode-line-buffer-identification) '(ln-string)))))

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

(defun ln-filter (ignore ignore)
  ;; Don't even bother if we're not in the mode.
  (if ln-mode
      (progn
	(or ln-initialised (ln-initialise))
	(setq ln-string (format ln-format (which-line)))
	(or ln-usual-mode-line
	    (setq ln-usual-mode-line
		  mode-line-buffer-identification
		  mode-line-buffer-identification
		  (ln-modified-buffer-id))))))

(defun ln-sentinel (ignore reason)
  (linenumbers-shutdown)
  (error "Whoa: the linenumbers process died unexpectedly: %s." reason))

; --------------------------
; Start the external process
; --------------------------

(or ln-process
    (ln-start-process))

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