Xref: utzoo comp.emacs:5247 gnu.emacs:425 Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!unmvax!ncar!husc6!bbn!bbn.com!mesard From: mesard@bbn.com (Wayne Mesard) Newsgroups: comp.emacs,gnu.emacs Subject: Re: line numbers in emacs Keywords: GNU emacs editing Message-ID: <35659@bbn.COM> Date: 6 Feb 89 19:21:44 GMT References: <22@euteal.UUCP> Sender: news@bbn.COM Reply-To: mesard@labs-n.bbn.com (Wayne Mesard) Organization: Bolt Beranek and Newman Inc., Cambridge MA Lines: 232 In article <22@euteal.UUCP> michel@euteal.UUCP (Michel Berkelaar) writes: > >I'm looking for an emacs minor mode which will add line numbers to all lines >and still allow editing, scrolling, saving (without those numbers), >etc to go on. It seems so basic that someone must have worked on it. One would think so, but Control-X L seems adequate for most people's needs. >If there are any hints, please post, or mail to: I have what may be an answer. Well, two answers. And since I'm in M-x verbose-mode today. You're gonna have to wade through a bunch of prose to get to them. Some time ago, I wrote commands to stuff line numbers into the current region or buffer. This solution has the benefits of simplicity: ------------LINE-NUMBERS-TAKE-1--------- (defun line-number-region (beg end &optional relative) "Insert line number labels at the beginning of every line in the region. Each label will be the same length so that it's trivial to write a function or keyboard macro to delete them at a later time. With optional [third] argument, RELATIVE, start counting from 1 no matter where we are in the buffer. Otherwise, begin counting from the starting line number. " (interactive "r\nP") (save-excursion (let ((num (if relative 1 (1+ (count-lines (point-min) beg)))) (digitc (length (int-to-string (count-lines (if relative beg (point-min)) end)))) ) (goto-char end) (end-of-line) (setq end (point-marker)) (goto-char beg) (beginning-of-line) (while (< (point) end) (insert (substring "0000000" 0 (- digitc (length (int-to-string num)))) (int-to-string num) ?:) (setq num (1+ num)) (forward-line 1) ))) ) (defun line-number-buffer () "Put line numbers at the start of every line in the buffer." (interactive) (line-number-region (point-min) (point-max) t) ) ------------LINE-NUMBERS-TAKE-1--------- This is basically useless if you want the line numbers there while you're editing. When I saw your message, it renewed my frustration at the fact that there's no way to write a minor mode in GNU elisp. The basic mechanism needed to get line numbers displayed is to have an interrupt facility (or something) periodically call an elisp function which checks to see if the window has been scrolled recently, and does the right thing if it has. My first thought was that it would be interesting to create a new keymap, bind every key to the same function, and have that function do its minor-mode-type-thing and then call the function which is normally bound to that key: (let ((cmd (aref *old-map* (this-command-keys)))) ... (call-interactively cmd)))) Well, that is interesting, except it doesn't work so good. (Left as an exercise to the reader.) My second thought was, lacking an explicit interrupt or on-redisplay-hook type of thing, I could abuse the procedure facility (used in emacs/lisp/time.el) to do the equivalent. All that's needed is a program which will periodically dump a character onto its output. loadst(1) which comes with GNU Emacs and BSD's yes(1) both fit this bill. But I used the "peep" shellscript below: ------peep--------Put this on your PATH------------ while [ 1 ] do echo -n x sleep $1 done ------peep-------- start-process will invoke this program, and everytime it prints something out (i.e. approxiamtely every LINE-NUM-UPDATE seconds), the process-filter will get called. Put "peep" in your PATH somewhere, then the following will maintain a line number buffer to the left of the buffer you are editing. The biggest disadvantage with this method is that Emacs can't do smart scrolling with horizontally split windows (at least not on vt100s). This can be a pain at 1200 baud. I'd be interested in any comments on this code. ------------LINE-NUMBERS-TAKE-2--------- ;; display-line-numbers: Show line-numbers for an Emacs buffer. ;; Copyright (C) 1989 Wayne Mesard ;; ;; This file is not officially part of GNU EMACS. But it is being ;; distributed under the terms of the GNU Emacs General Public License. ;; The copyright notice and this notice must be preserved in all copies. ;; Call M-x display-line-nums to show 'em. ;; M-x hide-line-nums to make 'em go away. ;; Or bind these guys to keys in your .emacs file. ;; [N.B. I haven't tested this rigorously. It may be buggy.] ;; I will get around to these things eventually. Other suggestions ;; are welcome. Mail to Mesard@BBN.COM. -wsm ;; To do: [wsm060289] ;; o Support for line numbers in multiple windows is not adequate. ;; Things like this should be possible and fool proof: ;; ;; +---------------------+ ;; | 1| GNU, which stands| ;; | 2|for Gnu's not Unix| ;; | 3|is the name for | ;; | 4|the complete ... | ;; +---------------------+ ;; |20|I canna change the| ;; |21|laws o physics | ;; |22|Cap'n! | ;; +---------------------+ ;; ;; o Ctrl-X 1 hides the line number window, but doesn't kill ;; The line-num process. It should. (Have the filter function ;; check for the case when the line num window isn't on screen.) ;; o Ctrl-X o should not go to the num buf. ;; o Similarly, The *Help* buffer should NOT be allowed to come ;; up in the line number window! ;; o Some function documentation would be nice. (defvar *win-min-width* 6) (defvar *line-num-update* 1) (defvar line-num-process nil) (defvar *line-num-buf* nil) (defun display-line-nums () (interactive) (if (> window-min-width *win-min-width*) (setq window-min-width *win-min-width*)) (split-window-horizontally *win-min-width*) (switch-to-buffer " Line-Numbers" t) (setq *line-num-buf* (current-buffer)) (other-window 1) (if (not (and line-num-process (eq (process-status line-num-process) 'run))) (progn (setq line-num-process (start-process "line-nums" nil "peep" (int-to-string *line-num-update*))) (set-process-sentinel line-num-process 'line-num-sentinel) (set-process-filter line-num-process 'update-when-scrolled))) (process-kill-without-query line-num-process) ) (defun line-num-sentinel (ignore reason) (hide-line-nums) (message "The line number process died unexpectedly: %s." reason)) (defun hide-line-nums () (interactive) (if (and line-num-process (eq (process-status line-num-process) 'run)) (progn (set-process-sentinel line-num-process nil) (delete-process line-num-process))) (let ((first-window (current-buffer)) (first-time t)) (while (or (not (eq first-window (current-buffer))) first-time) (setq first-time nil) (if (eq *line-num-buf* (current-buffer)) (delete-window) (other-window 1))) )) (defvar prev-top -99) (defun update-when-scrolled (ignore ignore) "When window has been scrolled, do something." (save-excursion (move-to-window-line 0) (if (and (not (eq (current-buffer) *line-num-buf*)) (/= (point) prev-top)) (let* ((cur-line-num (count-lines (point-min) (point))) (i (+ cur-line-num (window-height)))) (setq prev-top (point)) (other-window -1) (if (eq (current-buffer) *line-num-buf*) (progn (newline (goto-line i)) (while (> i cur-line-num) (setq i (1- i)) (forward-line -1) (if (looking-at "$") (progn (if (< i 10) (insert " ") (if (< i 100) (insert ?\040))) (insert (int-to-string i)))) ) (move-to-window-line 0) (scroll-up (- i (count-lines (point-min) (point)))) (move-to-window-line 0) )) (other-window 1)) ))) ------------LINE-NUMBERS-TAKE-2--------- -- void Wayne_Mesard(); Mesard@BBN.COM Edith Keillor died for your sins. BBN, Cambridge, MA