From: dodd@mycenae.cchem.berkeley.edu (Lawrence R. Dodd)
Newsgroups: gnu.emacs.sources
Subject: update for mode-line.el (v 2.50)
Date: 19 Dec 92 14:40:20
Organization: Dept of Chemical Engineering, Polytechnic Univ, NY, USA
Distribution: gnu
NNTP-Posting-Host: mycenae.cchem.berkeley.edu
Summary: v 2.50 of mode-line.el, more robust
Keywords: mode line, abbreviated file paths


Hello,

  Here is the latest release of mode-line.el (version 2.50). This version
  fixes a couple of bugs that make mode-line.el much more robust than any of
  the earlier versions.  Also added is a toggle feature to scroll through
  various mode line displays (bound to C-c C-t).

                     share and enjoy,
                            Larry
                            dodd@roebling.poly.edu
                   
P.S. Happy holidays.

<file: ~/lisp/mode-line.el>
........................... cut along dotted line ...........................
;; mode-line.el - code for including abbreviated file paths in mode line

(defconst mode-line-version (substring "$Revision: 2.50 $" 11 -2)
  "$Id: mode-line.el,v 2.50 1992/12/19 22:30:50 dodd Stable $")

;; Copyright (C) 1992, 1993 Free Software Foundation, Inc.
;;
;; 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.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; if not, write to the Free Software
;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

;; LCD Archive Entry:
;; mode-line|Lawrence R. Dodd|dodd@roebling.poly.edu|
;; code for including abbreviated file paths in mode line|
;; $Date: 1992/12/19 22:30:50 $|$Revision: 2.50 $|~/????/mode-line.el.Z|

;;; MAINTAINER OF mode-line.el:
;;;  
;;; Lawrence R. Dodd
;;; dodd@roebling.poly.edu
;;; 
;;; Please send bug reports, comments, suggestions, and any smart remarks
;;; concerning this code to the above e-mail address. Please be sure to
;;; mention the value of the variable `mode-line-version' *or* simply type
;;; `M-x mode-line-submit-report' and let mode-line.el do it for you.

;;; CONTRIBUTORS TO mode-line.el:
;;;
;;; Lawrence R. Dodd
;;; dodd@roebling.poly.edu
;;;
;;; Robert McLay 
;;; mclay@cfdlab.ae.utexas.edu
;;; (for much beta-testing)
;;;
;;; Crys Rides (a.k.a., James C. Ghering) 
;;; crys@cave.tcp.com
;;; (for suggesting and testing of view-mode support)

;;; VERSION:
;;;
;;; $LastEditDate: "Sat Dec 19 17:30:30 1992"$
;;; $Id: mode-line.el,v 2.50 1992/12/19 22:30:50 dodd Stable $
;;; $Revision: 2.50 $ 

;;; HISTORY:
;;; 
;;; Derived from prettymodeln.el.  That file was checked in as version 2.1 of
;;; mode-line.el.  This is a cleaned, debugged, and more robust version of
;;; that original code containing with more features and more documentation. I
;;; would have named this prettymodeln++.el but that is too many letters and I
;;; besides I hack Fortran.
;;;
;;; AUTHOR OF prettymodeln.el: 
;;; 
;;; Andy Gaynor (a.k.a., Silver)
;;; gaynor@paul.rutgers.edu ...!rutgers!paul.rutgers.edu!gaynor
;;; 
;;;                               _   /|  Splthlt...
;;;                    Ahckthph!  \`o_@'
;;;                                 (_)
;;;                                  U   Ptooey!
;;;
;;; Created: 13 Sep 87 18:34:59 GMT

;;; INSTALLATION/USAGE:
;;;
;;;   o  save as mode-line.el in the GNU emacs load-path
;;;   o  stick this in your ~/.emacs:
;;;
;;;                (require 'mode-line) 
;;;   
;;;   o  use C-c C-t to scroll through different mode lines manually
;;;      (with argument it will scroll through automatically)

;;; ADVANCED USAGE:
;;;
;;;   o  same as above but also stick something _like_ the following inside 
;;;      your ~/.emacs:
;;; 
;;;   (setq file-name-abbreviation-alist
;;;         (list 
;;;          (cons  (concat "^" (expand-file-name "~") 
;;;                         "/" "special/")  "special:")
;;;          (cons  (concat "^" (expand-file-name "~") "/")  "~/")
;;;          '("^/dodd@roebling.poly.edu:/home/dodd/" . "Roebling:")
;;;          '("^/joe@\\([a-zA-Z0-9.]*\\).\\(edu\\|gov\\):/home/joe/" . "\\1:")
;;;          '("^.*/\\([^/]*/\\)" . "\\1")))  
;;; 
;;; The explanation of above is as follows. If I am editing a file called
;;; `filename' this list of associations will be attempted in this order: if
;;; the full path to `filename' is
;;; 
;;; (1) `/myhomedirectory/special/filename' display as `special:filename' 
;;;      (this is useful for much used sub-sub-directories)
;;; (2) `/myhomedirectory/filename' display as `~/filename' 
;;;      (this eliminates those long paths to your home directory)
;;; (3) `/user@machine.edu:/anything/filename' display as `Machine:filename' 
;;;     (this is _extremely_ useful with ange-ftp)
;;; (4) `/user@regexp.edu:/anything/filename' display as `regexp:filename' 
;;;     (this is the same as above but attempts to use a regular expression)
;;; (5) `/snafu/barfoo/filename' display as `barfoo:filename' 
;;;     (this shows just the current directory and is done for any path that
;;;     does not match one of the above)

;;; SEE ALSO: 
;;; 
;;; For more information on the filename associations list, after
;;; loading, do `M-x describe-variable file-name-abbreviation-alist'

;;; MOTIVATION:
;;; 
;;; Buffer names in the mode line are not very informative. When files with
;;; the same name are being visited in different directories the mode line
;;; shows names like "Makefile," "Makefile<2>," "Makefile<3>," and so on.  The
;;; zeroth order correction is to use the file name and directory in the mode
;;; line.  However, long file names with full directory paths (for example
;;; /u2/luser/foobar/bletch/src/0.1/foobar/Makefile) in the mode line are a
;;; pain in the ass.  They suck up the whole mode line, and are a strain on
;;; the eyes to differentiate. We would like to display things like
;;; "foobar/Makefile," "barfoo/Makefile," "conserve/Makefile," and so on in
;;; the mode line.
;;;
;;; You will find here a mode line formatting scheme that is fairly nice.  It
;;; displays the buffer name if the buffer is not associated with a file.
;;; Otherwise, it displays the file name, but only after abbreviating it as
;;; per a list of abbreviations that you provide.

;;; LOGIC: 
;;; 
;;; Set up the mode line by making mode-line-buffer-identification local to
;;; every buffer.  Various hooks will abbreviate the buffer-file-name to
;;; something a little easier to read.
;;; 
;;;   `buffer-file-name' == the original file name   
;;;   `file-name-abbreviation-alist' == list of abbreviations
;;;   `abbreviate-mode-line-buffer-identification' == what hooks call      
;;;   `string-replace-regexp-in-alist' == means of abbreviation

;;; KNOWN BUG WITH SUGGESTED PATCH:
;;; 
;;;   Sebastian Kremer's Tree Dired (v 6.0, available via anonymous ftp from
;;;   ftp.uni-koeln.de[134.95.80.1]:/pub/gnu/emacs/diredall.tar.Z) is an
;;;   improvement over the distribution dired for reasons too numerous to
;;;   mention. One major improvement is that Kremer's Dired correctly renames
;;;   any buffers visiting a file that is renamed using dired-mode.
;;; 
;;;   Unfortunately, it uses `set-visited-file-name' in the function
;;;   `dired-rename-file', which does not use `write-hooks'. The result being
;;;   that while the buffer is renamed, the mode line is not updated properly
;;;   after a dired-do-move (key r).  The patch is to force a call to
;;;   `abbreviate-mode-line-buffer-identification' after the call to
;;;   `set-visited-file-name' in the function `dired-rename-file':
;;;
;;;      (defun dired-rename-file (from to ok-flag)
;;;                                ...
;;;                        [material not shown]
;;;                                ...
;;;              (let ((modflag (buffer-modified-p)))
;;;                (set-visited-file-name to)   ; kills write-file-hooks
;;;     +          ;; for mode-line.el
;;;     +          (and (memq 'abbreviate-mode-line-buffer-identification
;;;     +                     write-file-hooks)
;;;     +                 (abbreviate-mode-line-buffer-identification)) 
;;;                (set-buffer-modified-p modflag))))
;;;                                ...
;;;                        [material not shown]
;;;                                ...

;;; We need to load the default `view' package (be it view.el or new-view.el)
;;; so that `view-hook' will be defined when we append to it below.

(require 'view)

;;; We need to use kill-fix.el, by Joe Wells (jbw@cs.bu.edu), in order to
;;; preserve some mode-line's variables when the major mode of a buffer is
;;; changed. The next section contains a copy of kill-fix.el.  The copyright
;;; notice at the top of this file should be considered as a replacement for
;;; the one found in kill-fix.el.

;;; Note that instead of this page, the statement "(require 'kill-fix)" could
;;; be used if the the user already has a copy of kill-fix.el. That code is
;;; available via anonymous ftp to archive.cis.ohio-state.edu [128.146.8.52]
;;; in /pub/gnu/emacs/elisp-archive/as-is/kill-fix.el.Z.

;; -----------
;; kill-fix.el - enhancement to kill-all-local-variables
;; Author: Joe Wells (jbw@cs.bu.edu)
;; Date: 18 Jan 89 22:16:15 GMT

;; save the original subr function definition of kill-all-local-variables
(or (fboundp 'original-kill-all-local-variables)
    (fset 'original-kill-all-local-variables
          (symbol-function 'kill-all-local-variables)))

(defun kill-all-local-variables ()
  "Eliminate all the buffer-local variable values of the current
buffer.  This buffer will then see the default values of all
variables.  NOTE: This function has been modified to ignore
buffer-local variables whose preserved property is non-nil."
  (let ((oldvars (buffer-local-variables)))
    (original-kill-all-local-variables)
    (while oldvars
      (let ((var (car (car oldvars))))
        (cond ((get var 'preserved)
               (make-local-variable var)
               (set var (cdr (car oldvars))))))
      (setq oldvars (cdr oldvars)))))

(provide 'kill-fix)
;; -----------

;;; GENERAL DISPLAY STUFF:

;;; This makes the mode line display the day, date, time of day, and average
;;; number of processes. The increment for time update is 30 seconds, also
;;; `Mail' appears if there is any unread mail.  Users may wish to comment
;;; this stuff out.

(setq display-time-interval 30)
(setq display-time-day-and-date t)
(display-time)

;;; Customize mode-line-format and its constituents. 

;;; Make sure you use mode-line-buffer-identification to identify the buffer
;;; in your mode-line-format.  This variable must be buffer-local (if it is
;;; not already).

;;; Note that mode-line-buffer-identification must be used to identify the
;;; buffer.  mode-line-modified is retained because it is in emacs' own
;;; default mode-line-format, and emacs might do some clever tricks with it.

(make-variable-buffer-local 'mode-line-modified)
(setq-default mode-line-modified '("%*%*-"))

(make-variable-buffer-local 'mode-line-buffer-identification)
(setq-default mode-line-buffer-identification '("%b"))

;; create a new buffer-local variable to keep track of the current state of 
;; the mode line for use by mode-line-toggle-display.

(make-variable-buffer-local 'mode-line-state)

(defvar mode-line-state "buffer-name"
  "A buffer-local variable to keep track of the current state of the mode line
for use by mode-line-toggle-display.")

(setq-default mode-line-state "buffer-name")

;; KLUDGE

;; If the user changes the major mode of a buffer the variables
;; `mode-line-buffer-identification' and `mode-line-state' are set to their
;; default values. We use Joe Wells' kill-fix.el to preserve these variables
;; when the major mode of a buffer is changed.

(put 'mode-line-buffer-identification 'preserved t)
(put 'mode-line-state 'preserved t)

;;; now the define the organization of the mode-line-format

(setq-default mode-line-format
  '("--"
    mode-line-modified
    " "
    mode-line-buffer-identification
    " %[("
    (-10 . mode-name) ; truncate mode name to 10 chars, it got too long - LRD
    minor-mode-alist
    "%n"
    mode-line-process
    " "
    (-3 . "%p") ; make string at most 3 chars: `Top', `Bot', or `nn%' - LRD
    ")%] "
    global-mode-string
    " %-"))

;;; A big thankyou to Robert McLay (mclay@cfdlab.ae.utexas.edu) for help with
;;; the following - LRD.

;;; Form home directory with a leading `^' and trailing `/' so if your home
;;; directory is /home/machine/user-name then home-dir is
;;; `^/home/machine/user-name/' (without the quotes) The leading `^' is need
;;; to match the leading end of the string.

;;; (originally was not a user option because it was missing the `*' - LRD)

(defvar file-name-abbreviation-alist
      (list 
       (cons  (concat "^" (expand-file-name "~") "/")  "~/"))

  "*Alist of embedded filename patterns versus the corresponding desired
abbreviations. Each element is of the form (<regexp> . <to-string>).

The package mode-line.el goes down this alist looking for matches to regular
expression <regexp> in the full pathname of the file and replaces it with
<to-string>.  This is then repeated for all <regexp> in the list. This fact
can be exploited in forming the regular expressions. However, since the
searching and replacing is done top-down, special cases should be put at the
head of the list.

Examples:

  If the user often plays with the files in /u2/luser/foobar/bletch.
  What the user may want to do is replace leading instances of this
  path with just `bletch.'  To do this stick the association into
  the alist

     (\"^/u2/luser/foobar/bletch\" . \"bletch\")

  Another good association is to display only the last directory in
  the path if no other special case applies. This is done with the
  following association

     (\"^.*/\\([^/]*/\\)\" . \"\\1\")

  Finally, one can also abbreviate those long filenames that result
  when using ange-ftp

     (\"^/emily@roebling.poly.edu:/home/emily/\" . \"Roebling:\")

  The default entry for this variable removes the home directory
  path and replaces it with ~/

     (cons  (concat \"^\" (expand-file-name \"~\") \"/\")  \"~/\")")

;;;; DEFUNS

;;; the function that makes the substitutions - this is the work-horse

(defun string-replace-regexp-in-alist (string replacement-alist)

   "Given a string STRING, replace *each* instance of <regexp> (cars of elements
in REPLACEMENT-ALIST) with <to-string> (cdrs of elements in REPLACEMENT-ALIST)
and return the new string. The above is different from simply replacing the
first match in the alist and then leaving. This is why a temporary buffer is
used."

   (save-excursion
       
     (let 

         ;; VARLIST - we need to generate a unique name for temporary buffer
         ;; (originally just used `!@#$%^&*' which, believe or not, might not be
         ;; unique - LRD)

         ((temp-buffer (get-buffer-create (make-temp-name "!@#$%^&*")))
          (temp-alist replacement-alist) ; don't mess with incoming alist
          (new-string)) ; this is the value to be returned 

       ;; create temporary buffer
       (set-buffer temp-buffer)
       
       ;; insert incoming string (name of filename with full path name)
       (insert string)
       
       ;; we want to make sure the temporary buffer is killed
       (unwind-protect
           
           ;; BODY
           (progn
             
             ;; walk down `temp-alist', removing as we go, until it is empty
             (while temp-alist
               
               ;; go to beginning of temporary buffer
               (goto-char (point-min))
               
               ;; search the temporary buffer for every occurrence of the
               ;; regular expression stored in `(car (car temp-alist))' and
               ;; replace it with the one stored in `(cdr (car temp-alist))'
               ;; (code originally used replace-regexp - LRD)
               
               (while (re-search-forward (car (car temp-alist)) nil t)
                 (replace-match (cdr (car temp-alist))))
               
               ;; decrement temp-alist and restart while-loop
               (setq temp-alist (cdr temp-alist)))
             
             ;; set return string to what remains in the temporary buffer
             (setq new-string (buffer-string)))
         
         ;; CLEAN UP - no matter what happens, remove the temporary buffer
         (kill-buffer temp-buffer))
     
     ;; return value of converted string
     new-string)))

;;; function that creates the abbreviated identification and is called by the
;;; various hooks (originally returned non-nil values - LRD)

(defun abbreviate-mode-line-buffer-identification ()

  "Abbreviates mode-line-buffer-identification locally using the function
string-replace-regexp-in-alist and the alist file-name-abbreviation-alist.
This function will return nil always. This is needed for view-mode since it
will call this function even if it is not visiting a file and its return value
needs to be predictable (as opposed to garbage). A nil return is also needed
for the write-file-hooks."

  (if buffer-file-name
      (progn
        (setq mode-line-buffer-identification
              (list
               (string-replace-regexp-in-alist buffer-file-name
                                               file-name-abbreviation-alist)))
        (setq mode-line-state "abbrev-file-name"))
    (setq mode-line-state "buffer-name"))
  ;; always return nil
  nil)

;;;; HOOKS

;;; Add abbreviate-mode-line-buffer-identification to find-file-hooks,
;;; write-file-hooks, and view-hook but only if it has not been added already
;;; (originally overwrote find-file-hooks - LRD).

(or (memq 'abbreviate-mode-line-buffer-identification find-file-hooks)
    (setq find-file-hooks
          (append find-file-hooks
                  '(abbreviate-mode-line-buffer-identification))))

(or (memq 'abbreviate-mode-line-buffer-identification view-hook)
    (setq view-hook
          (append view-hook
                  '(abbreviate-mode-line-buffer-identification))))

(or (memq 'abbreviate-mode-line-buffer-identification write-file-hooks)
    (setq write-file-hooks 
          (append write-file-hooks
                  '(abbreviate-mode-line-buffer-identification))))

;;;; TOGGLE 

(define-key global-map "\C-c\C-t" 'mode-line-toggle-display)

(defun mode-line-toggle-display (arg)

  "Cycles the buffer descriptor currently being displayed in modeline. If
filename is currently displayed as abbreviated, then display with full path.
If full path is currently displayed, then display just the buffer name. If the
buffer name is currently displayed, then display the abbreviated filename.
With argument will scroll through displays automatically."

  (interactive "P")

  (if buffer-file-name

      (if arg

          ;; scroll display
          (progn
            (mode-line-toggle-display nil)
            (sit-for 1)
            (mode-line-toggle-display nil)
            (sit-for 1)
            (mode-line-toggle-display nil))

        ;; change display 
        (progn
          (if (string= mode-line-state "abbrev-file-name")
              (progn (setq mode-line-buffer-identification (buffer-file-name))
                     (setq mode-line-state "full-file-name"))
            (if (string= mode-line-state "full-file-name")
                (progn (setq mode-line-buffer-identification '("Emacs: %b"))
                       (setq mode-line-state "buffer-name"))
              (progn (abbreviate-mode-line-buffer-identification)
                     (setq mode-line-state "abbrev-file-name"))))
        
        ;; force redisplay of mode line
        (set-buffer-modified-p (buffer-modified-p))))))

;;;; BUG REPORTS

;;; this section is provided for reports.
;;; adopted from Barry A. Warsaw's c++-mode.el

(defvar mode-line-mailer 'mail
  "*Mail package to use to generate report mail buffer.")

(defconst mode-line-help-address "dodd@roebling.poly.edu"
  "Address accepting submission of reports on mode-line.el.")

(defconst mode-line-maintainer "Larry"
  "First name of person accepting submission of reports on mode-line.el.")

(defconst mode-line-file "mode-line.el"
  "Name of file containing emacs lisp code.")

(defun mode-line-submit-report ()
  "Submit via mail a report using the mailer in mode-line-mailer, filename in
mode-line-file, to address in mode-line-help-address."
  (interactive)
  (funcall mode-line-mailer)
  (insert mode-line-help-address)
  (if (re-search-forward "^subject:[ \t]+" (point-max) 'move)
      (insert "Report on " mode-line-file " " mode-line-version))
  (if (not (re-search-forward mail-header-separator (point-max) 'move))
      (progn (goto-char (point-max))
             (insert "\n" mail-header-separator "\n")
             (goto-char (point-max)))
    (forward-line 1))
  (set-mark (point)) ;user should see mark change
  (insert "\n\n---------\n")
  (insert (emacs-version) "\n")
  (insert "code: " mode-line-file ",v " mode-line-version)
  (insert "\n\n")
  (insert "current value of file-name-abbreviation-alist:\n\n")
  (insert (prin1-to-string file-name-abbreviation-alist))
  (exchange-point-and-mark)
  (insert "\n" mode-line-maintainer ",\n\n")
  (message "please write the message"))

;;;; provide the package

(provide 'mode-line)
........................... cut along dotted line ...........................
<end file: ~/lisp/mode-line.el>