changeset 17616:a64126a1f870

Initial revision
author Richard M. Stallman <rms@gnu.org>
date Fri, 02 May 1997 00:22:11 +0000
parents 32f90c43d1d2
children 4851316697c1
files lisp/iswitchb.el
diffstat 1 files changed, 1206 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/iswitchb.el	Fri May 02 00:22:11 1997 +0000
@@ -0,0 +1,1206 @@
+;;; ISWITCHB.EL --- switch between buffers using substrings
+
+;; Copyright (C) 1996, 1997  Free Software Foundation, Inc.
+
+;; Author: Stephen Eglen <stephene@cogs.susx.ac.uk>
+;; Maintainer: Stephen Eglen <stephene@cogs.susx.ac.uk>
+;; Created: 15 Dec 1996
+;; $Revision: 1.26 $
+;; Keywords: extensions
+;; location: http://www.cogs.susx.ac.uk/users/stephene/emacs
+
+ 
+;; 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 2, 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 GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;; LCD Archive Entry:
+;; iswitchb|Stephen Eglen|<stephene@cogs.susx.ac.uk>
+;; |switch between buffers using substrings
+;; |$Date: 1997/03/23 18:10:48 $|$Revision: 1.26 $|~/packages/iswitchb.el
+
+
+;;; Installation:
+
+;; To load the package, do
+;; (require 'iswitchb)
+;; To get the functions in this package bound to keys, do
+;; (iswitchb-default-keybindings)
+;; 
+;; Has been tested on Emacs 19.34 and XEmacs 19.14.  I think it needs
+;; at least Emacs 19.29 to run.
+
+;;; Commentary:
+
+;; As you type in a substring, the list of buffers currently matching
+;; the substring are displayed as you type.  The list is ordered so
+;; that the most recent buffers visited come at the start of the list.
+;; The buffer at the start of the list will be the one visited when
+;; you press return.  By typing more of the substring, the list is
+;; narrowed down so that gradually the buffer you want will be at the
+;; top of the list.  Alternatively, you can use C-s an C-r to rotate
+;; buffer names in the list until the one you want is at the top of
+;; the list.  Completion is also available so that you can see what is
+;; common to all of the matching buffers as you type.
+
+;; This code is similar to a couple of other packages.  Michael R Cook
+;; <mcook@cognex.com wrote a similar buffer switching package, but
+;; does exact matching rather than substring matching on buffer names.
+;; I also modified a couple of functions from icomplete.el to provide
+;; the completion feedback in the minibuffer.
+
+;;; Example 
+
+;;If I have two buffers called "123456" and "123", with "123456" the
+;;most recent, when I use iswitchb, I first of all get presented with
+;;the default buffer (xxx) to switch to:
+;;
+;;       iswitch  {default xxx} 
+;;
+;; If I then press 2:
+;;       iswitch 2[3]{123456,123}
+;;
+;; The list in {} are the matching buffers, most recent first (buffers
+;; visible in the current frame are put at the end of the list by
+;; default).  At any time I can select the item at the head of the
+;; list by pressing RET.  I can also bring the put the first element
+;; at the end of the list by pressing C-s, or put the last element at
+;; the head of the list by pressing C-r.  The item in [] indicates
+;; what can be added to my input by pressing TAB.  In this case, I
+;; will get "3" added to my input.  So, press TAB: 
+;;	 iswitch 23{123456,123}
+;;
+;; At this point, I still have two matching buffers.
+;; If I want the first buffer in the list, I simply press RET.  If I
+;; wanted the second in the list, I could press C-s to move it to the
+;; top of the list and then RET to select it.
+;;
+;;However, If I type 4, I only have one match left:
+;;       iswitch 234[123456] [Matched]
+;;
+;;Since there is only one matching buffer left, it is given in [] and we
+;;see the text [Matched] afterwards.  I can now press TAB or RET to go
+;;to that buffer.
+;;
+;; If however, I now type "a":
+;;       iswitch 234a [No match]
+;; There are no matching buffers.  If I press RET or TAB, I can be
+;; prompted to create a new buffer called "234a".
+;;
+;; Of course, where this function comes in really useful is when you
+;; can specify the buffer using only a few keystrokes.  In the above
+;; example, the quickest way to get to the "123456" buffer would be
+;; just to type 4 and then RET (assuming there isnt any newer buffer
+;; with 4 in its name).
+
+;; To see a full list of all matching buffers in a separate buffer,
+;; hit ? or press TAB when there are no further completions to the
+;; substring.
+
+;;
+;;  See the doc string of iswitchb for full keybindings and features.
+;;  (describe-function 'iswitchb)
+
+;;; Customisation
+
+;; See the User Variables section below for easy ways to change the
+;; functionality of the program.
+;; To modify the keybindings, use the hook provided.  For example:
+;;(add-hook 'iswitchb-define-mode-map-hook
+;;	  'iswitchb-my-keys)
+;;
+;;(defun iswitchb-my-keys ()
+;;  "Add my keybings for iswitchb."
+;;  (define-key iswitchb-mode-map " " 'iswitchb-next-match)
+;;  )
+;;
+;; Seeing all the matching buffers.
+;; If you have many matching buffers, they may not all fit onto one
+;; line of the minibuffer.  In this case, you should use rsz-mini
+;; (resize-minibuffer-mode).  You can also limit iswitchb so that it
+;; only shows a certain number of lines -- see the documentation for
+;; `iswitchb-minibuffer-setup-hook'.
+
+
+;; Changing the list of buffers.
+
+;; By default, the list of current buffers is most recent first,
+;; oldest last, with the exception that the buffers visible in the
+;; current frame are put at the end of the list.  A hook exists to
+;; allow other functions to order the list.  For example, if you add:
+;;
+;; (add-hook 'iswitchb-make-buflist-hook 'iswitchb-summaries-to-end)
+;;
+;; then all buffers matching "Summary" are moved to the end of the
+;; list.  (I find this handy for keeping the INBOX Summary and so on
+;; out of the way.)  It also moves buffers matching "output\*$" to the
+;; end of the list (these are created by AUC TeX when compiling.)
+;; Other functions could be made available which alter the list of
+;; matching buffers (either deleting or rearranging elements.)
+
+;; Font-Lock
+
+;; If you have font-lock loaded, the first matching buffer is
+;; highlighted.  To switch this off, set (setq iswitchb-use-fonts nil)
+;; I don't use font-lock that much, so I've hardcoded the faces.  If
+;; this is too harsh, let me know.  Colouring of the matching buffer
+;; name was suggested by Carsten Dominik (dominik@strw.leidenuniv.nl)
+
+;;; Comparison with iswitch-buffer
+
+;; This package is a rewrite of iswitch-buffer, using the minibuffer
+;; rather than the echo area.  The advantages of using the minibuffer
+;; are several:
+;; o minibuffer has more powerful editing facilities
+;; o doesnt interfere with other packages that use the echo area
+;; o *Messages* buffer doesnt get filled up with all of the messages that
+;;   go to the modeline
+;; o cursor is in the minibuffer, which somehow looks right.
+;; o minibuffer can be resized dynamically to show all the possible matching
+;;   buffers rather than just the first line's worth (using rsz-mini).
+;;
+;; Disadvantages:
+;; o cant change the prompt to indicate status of searching (eg whether
+;;   regexp searching is currently on).
+
+
+;;; Acknowledgements
+
+;; Thanks to Jari Aalto <jari.aalto@poboxes.com> for help with the
+;; first version of this package, iswitch-buffer.  Thanks also to many
+;; others for testing earlier versions.
+
+;;; Code:
+
+(defconst iswitchb-version (substring "$Revision: 1.26 $" 11 -2)
+  "$Id: iswitchb.el,v 1.26 1997/03/23 18:10:48 stephene Exp $
+
+Report bugs to: Stephen Eglen <stephene@cogs.susx.ac.uk>")
+
+
+;;; User Variables
+;;
+;; These are some things you might want to change.
+
+(defvar iswitchb-case case-fold-search
+  "*Non-nil if searching of buffer names should ignore case.")
+
+(defvar iswitchb-buffer-ignore
+  '("^ ")
+  "*List of regexps or functions matching buffer names to ignore.  For
+example, traditional behavior is not to list buffers whose names begin
+with a space, for which the regexp is \"^ \".  See the source file for
+example functions that filter buffernames.")
+
+;;; Examples for setting the value of iswitchb-buffer-ignore
+;(defun -c-mode (name)
+;  "Ignore all c mode buffers -- example function for iswitchb."
+;  (save-excursion
+;    (set-buffer name)
+;    (string-match "^C$" mode-name)))
+
+;(setq iswitchb-buffer-ignore '("^ " ignore-c-mode))
+;(setq iswitchb-buffer-ignore '("^ " "\\.c$" "\\.h$"))
+
+
+(defvar iswitchb-default-method  'always-frame
+    "*How to switch to new buffer when using iswitchb.
+Possible values:
+`samewindow'	Show new buffer in same window
+`otherwindow'	Show new buffer in another window (same frame)
+`otherframe'	Show new buffer in another frame
+`maybe-frame'	If a buffer is visible in another frame, prompt to ask if you
+		you want to see the buffer in the same window of the current
+  		frame or in the other frame.
+`always-frame'   If a buffer is visible in another frame, raise that
+		frame.  Otherwise, visit the buffer in the same window.")
+
+(defvar iswitchb-regexp nil
+  "*Non-nil means that iswitchb will do regexp matching.  Value can be
+toggled within iswitchb.")
+
+(defvar iswitchb-newbuffer t
+  "*Non-nil means create new buffer if no buffer matches substring.
+See also `iswitchb-prompt-newbuffer'.")
+
+(defvar iswitchb-prompt-newbuffer t
+  "*Non-nil means prompt user to confirm before creating new buffer.
+See also `iswitchb-newbuffer'.")
+
+(defvar iswitchb-define-mode-map-hook  nil
+  "*Hook to define keys in `iswitchb-mode-map' for extra keybindings.")
+
+
+(defvar iswitchb-use-fonts t
+  "*Non-nil means use fonts for showing first match.")
+
+(defvar iswitchb-make-buflist-hook  nil
+  "*Hook to run when list of matching buffers is created.")
+
+
+(defvar iswitchb-method nil
+  "*Stores the method for viewing the selected buffer.  Its value is
+one of `samewindow', `otherwindow', `otherframe', `maybe-frame' or
+`always-frame'.  See `iswitchb-default-method' for details of
+values.")
+
+(defvar iswitchb-all-frames 'visible
+  "*Argument to pass to `walk-windows' when finding visible buffers.
+See documentation of `walk-windows' for useful values.")
+
+;;; THINGS TO DO / BUGS
+
+;; In Xemacs, the default buffer is not shown the first time you enter
+; the minibuffer, but if you type a char and then delete a char, the
+; default appears.  The first time we enter the minibuffer in XEmacs,
+; the default msg is not displayed, presumably because the hook is not
+; being called.  I have put in a temporary hack therefore at the
+; bottom of this file.  
+;
+; There is also a problem with the backspace key in XEmacs, so I have
+; bound it to the normal backward-delete-char.
+
+;; iswitch-buffer features Not yet implemented:
+;  C-f Quit iswitch and drop into find-file
+
+
+;; Do we need the variable iswitchb-use-mycompletion?
+
+
+;;; Internal Variables
+(defvar iswitchb-minibuffer-setup-hook nil
+  "*Iswitchb-specific customization of minibuffer setup.
+
+This hook is run during minibuffer setup iff iswitchb will be active.
+It is intended for use in customizing iswitchb for interoperation
+with other packages.  For instance:
+
+  \(add-hook 'iswitchb-minibuffer-setup-hook 
+	    \(function
+	     \(lambda ()
+	       \(make-local-variable 'resize-minibuffer-window-max-height)
+	       \(setq resize-minibuffer-window-max-height 3))))
+
+will constrain rsz-mini to a maximum minibuffer height of 3 lines when
+iswitchb is running.  Copied from icomplete-minibuffer-setup-hook")
+
+(defvar iswitchb-eoinput 1
+  "Point where minibuffer input ends and completion info begins.
+Copied from icomplete-eoinput.")
+(make-variable-buffer-local 'iswitchb-eoinput)
+
+
+(defvar iswitchb-buflist nil
+  "Stores the current list of buffers that will be searched through.
+The list is ordered, so that the most recent buffers come first,
+although by default, the buffers visible in the current frame are put
+at the end of the list.  Created by `iswitchb-make-buflist'.")
+
+;; todo -- is this necessary?
+
+(defvar iswitchb-use-mycompletion nil
+  "Non-nil means use iswitchb completion feedback.  Should only be set
+to t by iswitchb functions, so that it doesnt interfere with other
+minibuffer usage.")
+
+(defvar iswitchb-change-word-sub nil 
+  "Private variable used by `iswitchb-word-matching-substring'.")
+
+
+(defvar iswitchb-common-match-string  nil
+  "Stores the string that is common to all matching buffers.")
+
+
+(defvar iswitchb-rescan nil
+  "Non-nil means we need to regenerate the list of matching buffers.")
+
+(defvar iswitchb-text nil
+  "Stores the users string as it is typed in.")
+
+(defvar iswitchb-matches nil
+  "List of buffers currenly matching `iswitchb-text'.")
+
+(defvar iswitchb-default-buffer nil
+  "Default buffer to switch to.")
+
+(defvar iswitchb-mode-map nil
+  "Keymap for iswitchb.")
+
+(defvar  iswitchb-history nil
+  "History of buffers selected using iswitchb.")
+
+(defvar iswitchb-exit nil 
+  "Flag to monitor how iswitchb exits.  If equal to `takeprompt', we
+use the prompt as the buffer name to be selected.")
+
+(defvar iswitchb-buffer-ignore-orig nil
+  "Stores original value of `iswitchb-buffer-ignore'.")
+
+(defvar iswitchb-xemacs  (string-match "XEmacs" (emacs-version))
+  "Non-nil if we are running XEmacs.  Otherwise, assume we are running Emacs.")
+
+
+;;; FUNCTIONS
+
+
+;;; ISWITCHB KEYMAP 
+(defun iswitchb-define-mode-map ()
+  "Set up the keymap for iswitchb."
+  (interactive)
+  (let (map)
+    ;; generated every time so that it can inheret new functions.
+    ;;(or iswitchb-mode-map
+
+    (setq map (copy-keymap minibuffer-local-map))
+    (define-key map "?" 'iswitchb-completion-help)
+    (define-key map "\C-s" 'iswitchb-next-match)
+    (define-key map "\C-r" 'iswitchb-prev-match)
+    (define-key map "\t" 'iswitchb-complete)
+    (define-key map "\C-j" 'iswitchb-select-buffer-text)
+    (define-key map "\C-t" 'iswitchb-toggle-regexp)
+    ;;(define-key map "\C-a" 'iswitchb-toggle-ignore)
+    (define-key map "\C-c" 'iswitchb-toggle-case)
+    (setq iswitchb-mode-map map)
+    (run-hooks 'iswitchb-define-mode-map-hook)
+    ))
+  
+
+
+;;; MAIN FUNCTION
+(defun iswitchb ()
+  "Switch to buffer matching a substring.
+As you type in a string, all of the buffers matching the string are
+displayed.  When you have found the buffer you want, it can then be
+selected.  As you type, most keys have their normal keybindings,
+except for the following:
+\\<iswitchb-mode-map>
+
+RET Select the buffer at the front of the list of matches.  If the
+list is emptty, possibly prompt to create new buffer.
+
+\\[iswitchb-select-buffer-text] Select the current prompt as the buffer.
+If no buffer is found, prompt for a new one.
+
+\\[iswitchb-next-match] Put the first element at the end of the list.
+\\[iswitchb-prev-match] Put the last element at the start of the list.
+\\[iswitchb-complete] Complete a common suffix to the current string that 
+matches all buffers.  If there is only one match, select that buffer.
+If there is no common suffix, show a list of all matching buffers
+in a separate window.
+\\[iswitchb-toggle-regexp] Toggle rexep searching.
+\\[iswitchb-toggle-case] Toggle case-sensitive searching of buffer names.
+\\[iswitchb-completion-help] Show list of matching buffers in separate window.
+"
+  ;;\\[iswitchb-toggle-ignore] Toggle ignoring certain buffers (see \
+  ;;`iswitchb-buffer-ignore')
+  	
+  (let
+      (
+       prompt
+       buf-sel
+       iswitchb-final-text
+       (minibuffer-confirm-incomplete nil) ;XEmacs todo: prevent `;confirm'
+       (icomplete-mode nil) ;; prevent icomplete starting up
+       (minibuffer-local-completion-map minibuffer-local-completion-map)
+       ;; can only use fonts if they have been bound.
+       (iswitchb-use-fonts (and iswitchb-use-fonts
+				(boundp 'font-lock-comment-face)
+				(boundp 'font-lock-function-name-face)))
+       )
+    
+    (iswitchb-define-mode-map)
+    (setq minibuffer-local-completion-map iswitchb-mode-map)
+    
+    (setq iswitchb-exit nil)
+    (setq iswitchb-rescan t)
+    (setq iswitchb-text "")
+    (setq iswitchb-matches nil)
+    ;;(setq iswitchb-default-buffer (buffer-name (other-buffer)))
+    (setq prompt (format "iswitch "))
+    (iswitchb-make-buflist)
+    (setq iswitchb-default-buffer (format "%s" (car iswitchb-buflist)))
+    
+    ;; highlight the default.
+    (if iswitchb-use-fonts
+	(put-text-property 0 (length iswitchb-default-buffer)
+			   'face 
+			   'font-lock-function-name-face
+			   iswitchb-default-buffer))
+
+    ;; prompt the user for the buffer name
+    (setq iswitchb-final-text (completing-read prompt
+					       ;;nil
+					       '(("dummy".1))
+					       ;;("2".2)  ("3".3))
+					       nil nil
+					       nil;init string
+					       'iswitchb-history))
+    ;;(message "chosen text %s" iswitchb-final-text)
+    ;; Choose the buffer name: either the text typed in, or the head
+    ;; of the list of matches
+    (if (or 
+	 (eq iswitchb-exit 'takeprompt)
+	 (null iswitchb-matches))
+	(setq buf-sel iswitchb-final-text)
+      ;; else take head of list
+      (setq buf-sel (car iswitchb-matches)))
+    
+    ;; Or possibly choose the default buffer
+    (if  (equal iswitchb-final-text "")
+	(setq buf-sel iswitchb-default-buffer))
+    
+    ;; View the buffer
+    (message "go to buf %s" buf-sel)
+    
+    (if (get-buffer buf-sel)
+	;; buffer exists, so view it and then exit
+	(iswitchb-visit-buffer buf-sel)
+      ;; else buffer doesnt exist
+      (iswitchb-possible-new-buffer buf-sel))
+    
+    ))
+
+
+;;; COMPLETION CODE
+
+(defun iswitchb-set-common-completion  ()
+  "Find common completion of `iswitchb-text' in `iswitchb-matches'.  The
+result is stored in `iswitchb-common-match-string'."
+
+  (let* (val)
+    (setq  iswitchb-common-match-string nil)
+    (if (and iswitchb-matches
+             (stringp iswitchb-text)
+             (> (length iswitchb-text) 0))
+        (if (setq val (iswitchb-find-common-substring
+                       iswitchb-matches iswitchb-text))
+            (setq iswitchb-common-match-string val)))
+    val
+    ))
+
+
+(defun iswitchb-complete ()
+  "Try and complete the current pattern amongst the buffer names."
+  (interactive)
+  (let (res)
+    (cond ((not  iswitchb-matches)
+
+	   ;; todo
+	   ;;(message "No buffer completions.")
+	   ;;(sit-for 0.3)
+	   (iswitchb-completion-help)
+	   )
+	  
+	  ((eq 1 (length iswitchb-matches))
+	   ;; only one choice, so select it.
+	   (exit-minibuffer))
+	  
+	  (t
+	   ;; else there could be some completions
+	   
+	   (setq res (iswitchb-find-common-substring
+		      iswitchb-matches iswitchb-text))
+	   (if (and (not (memq res '(t nil)))
+		    (not (eq res iswitchb-text)))
+	       ;; found something to complete, so put it in the minibuff.
+	       (progn
+		 (setq iswitchb-rescan nil)
+		 (delete-region (point-min) (point))
+		 (insert  res))
+	     ;; else nothing to complete
+	     (iswitchb-completion-help)
+	     )
+	   )
+	  )))
+
+
+
+;;; TOGGLE FUNCTIONS
+
+(defun iswitchb-toggle-case ()
+  "Toggle the value of `iswitchb-case'."
+  (interactive)
+  (setq iswitchb-case (not iswitchb-case))
+  ;; ask for list to be regenerated.
+  (setq iswitchb-rescan t)
+  )
+
+(defun iswitchb-toggle-regexp ()
+  "Toggle the value of `iswitchb-regexp'."
+  (interactive)
+  (setq iswitchb-regexp (not iswitchb-regexp))
+  ;; ask for list to be regenerated.
+  (setq iswitchb-rescan t)
+  )
+
+
+(defun iswitchb-toggle-ignore ()
+  "Toggle ignoring buffers specified with `iswitchb-buffer-ignore'."
+  (interactive)
+  (if iswitchb-buffer-ignore
+      (progn
+        (setq iswitchb-buffer-ignore-orig iswitchb-buffer-ignore)
+        (setq iswitchb-buffer-ignore nil)
+        )
+    ;; else
+    (setq iswitchb-buffer-ignore iswitchb-buffer-ignore-orig)
+    )
+  ;; ask for list to be regenerated.
+  (setq iswitchb-rescan t)
+  )
+
+
+(defun iswitchb-select-buffer-text ()
+  "Select the buffer named by the prompt.  If no buffer exactly
+matching the prompt exists, a new one is possibly created."
+  (interactive)
+  (setq iswitchb-exit 'takeprompt)
+  (exit-minibuffer))
+
+
+(defun iswitchb-next-match () 
+  "Put first element of `iswitchb-matches' at the end of the list."
+  (interactive)
+  (let ((tmp  (car iswitchb-matches)))
+    (setq iswitchb-matches (cdr iswitchb-matches))
+    (setq iswitchb-matches (append iswitchb-matches (list tmp)))
+    (setq iswitchb-rescan nil)
+    ))
+
+(defun iswitchb-prev-match () 
+  "Put last element of `iswitchb-matches' at the front of the list."
+  (interactive)
+  (setq iswitchb-matches (iswitchb-rotate-list iswitchb-matches))
+  (setq iswitchb-rescan nil)
+  )
+
+
+
+
+;;; CREATE LIST OF ALL CURRENT BUFFERS
+
+(defun iswitchb-make-buflist ()
+  "Set `iswitchb-buflist' to the current list of buffers.  Buffers
+that are currently visible are put at the end of the list."
+
+  (setq iswitchb-buflist 
+	(let (buflist
+	      iswitchb-current-buffers)
+	  (setq iswitchb-current-buffers (iswitchb-get-buffers-in-frames))
+	  (setq buflist (mapcar 'buffer-name (buffer-list)))
+	  (setq buflist (delq nil 
+			      (mapcar
+			       '(lambda (x)
+				  (if (not (iswitchb-ignore-buffername-p x))
+				      x))
+			       buflist)))
+	  (mapcar 'iswitchb-to-end iswitchb-current-buffers)
+
+	  (run-hooks 'iswitchb-make-buflist-hook)
+	  buflist)))
+
+
+(defun iswitchb-to-end (elem)
+  "Move ELEM to the end of BUFLIST."
+  (setq buflist (delq elem buflist))
+  ;;(message "removing %s" elem)
+  (setq buflist (append buflist (list elem))))
+		    
+
+
+
+(defun iswitchb-get-buffers-in-frames (&optional current)
+
+  "Return the list of buffers that are visible in the current frame.
+If optional argument `current' is given, restrict searching to the
+current frame, rather than all frames, regardless of value of
+`iswitchb-all-frames'."
+
+  (let ((iswitchb-bufs-in-frame nil))
+    
+    (walk-windows 'iswitchb-get-bufname nil
+		  (if current 
+		      nil
+		    iswitchb-all-frames))
+    iswitchb-bufs-in-frame))
+
+
+(defun iswitchb-get-bufname (win)
+  "Used by `iswitchb-get-buffers-in-frames' to walk through all windows."
+  (setq iswitchb-bufs-in-frame
+	(cons (buffer-name (window-buffer win))
+	      iswitchb-bufs-in-frame)))
+
+
+;;; FIND MATCHING BUFFERS
+
+(defun iswitchb-set-matches ()
+  "Set `iswitchb-matches' to the list of buffers matching prompt."
+
+  (if iswitchb-rescan
+      (setq iswitchb-matches
+	    (let* ((buflist iswitchb-buflist)
+		   )
+	      (if (> (length iswitchb-text) 0)
+		  (iswitchb-get-matched-buffers iswitchb-text iswitchb-regexp
+						buflist)
+		;; else no text, no matches
+		nil)))))
+
+(defun iswitchb-get-matched-buffers
+  (regexp &optional string-format buffer-list)
+  "Return matched buffers.  If STRING-FORMAT is non-nil, consider
+REGEXP as string.  BUFFER-LIST can be list of buffers or list of
+strings."
+  
+  (let* ((case-fold-search  iswitchb-case)
+	 ;; need reverse since we are building up list backwards
+	 (list              (reverse buffer-list))
+         (do-string          (stringp (car list)))
+         name
+         ret
+         )
+    (mapcar
+     (function
+      (lambda (x)
+
+        (if do-string
+            (setq name x)               ;We already have the name
+          (setq name (buffer-name x)))
+
+        (cond
+         ((and (or (and string-format (string-match regexp name))
+                   (and (null string-format)
+                        (string-match (regexp-quote regexp) name)))
+	       
+               ;; todo (not (iswitchb-ignore-buffername-p name))
+	       )
+          (setq ret (cons name ret))
+          ))))
+     list)
+    ret
+    ))
+
+
+
+
+(defun iswitchb-ignore-buffername-p (bufname)
+  "Return t if the buffer BUFNAME should be ignored."
+  (let ((data       (match-data))
+        (re-list    iswitchb-buffer-ignore)
+        ignorep
+        nextstr
+        )
+    (while re-list
+      (setq nextstr (car re-list))
+      (cond
+       ((stringp nextstr)
+        (if (string-match nextstr bufname)
+            (progn
+              (setq ignorep t)
+              (setq re-list nil))))
+       ((fboundp nextstr)
+        (if (funcall nextstr bufname)
+            (progn
+              (setq ignorep t)
+              (setq re-list nil))
+          ))
+       )
+      (setq re-list (cdr re-list)))
+    (store-match-data data)
+
+    ;; return the result
+    ignorep)
+  )
+
+
+
+(defun iswitchb-word-matching-substring (word)
+  "Return part of WORD before 1st match to `iswitchb-change-word-sub'.
+If `iswitchb-change-word-sub' cannot be found in WORD, return nil."
+  (let ((case-fold-search iswitchb-case)) 
+    (let ((m (string-match iswitchb-change-word-sub word)))
+      (if m
+          (substring word m)
+        ;; else no match
+        nil))))
+
+
+
+
+
+
+(defun iswitchb-find-common-substring (lis subs)
+  "Return common string following SUBS in each element of LIS."
+  (let (res
+        alist
+        iswitchb-change-word-sub
+        )
+    (setq iswitchb-change-word-sub
+          (if iswitchb-regexp
+              subs
+            (regexp-quote subs)))
+    (setq res (mapcar 'iswitchb-word-matching-substring lis))
+    (setq res (delq nil res)) ;; remove any nil elements (shouldnt happen)
+    (setq alist (mapcar 'iswitchb-makealist res)) ;; could use an  OBARRAY
+
+    ;; try-completion returns t if there is an exact match.
+    (let ((completion-ignore-case iswitchb-case))
+
+    (try-completion subs alist)   
+    )))
+
+
+(defun iswitchb-makealist (res)
+  "Return dotted pair (RES . 1)."
+  (cons res 1))
+
+;; from Wayne Mesard <wmesard@esd.sgi.com>
+(defun iswitchb-rotate-list (lis)
+  "Destructively removes the last element from LIS.
+Return the modified list with the last element prepended to it."
+  (if (<= (length lis) 1)
+      lis
+    (let ((las lis)
+          (prev lis))
+      (while (consp (cdr las))
+        (setq prev las
+              las (cdr las)))
+      (setcdr prev nil)
+      (cons (car las) lis))
+    ))
+
+
+(defun iswitchb-completion-help ()
+  "Show possible completions in a *Buffer Completions* buffer."
+  ;; we could allow this buffer to be used to select match, but I think
+  ;; choose-completion-string will need redifining, so it just inserts
+  ;; choice with out any previous input.  
+  (interactive)
+  (let ((completion-setup-hook nil)	;disable fancy highlight/selection.
+	)
+    (with-output-to-temp-buffer "*Buffer Completions*"
+      (if iswitchb-xemacs 
+	  
+	  ;; XEmacs extents are put on by default, doesn't seem to be
+	  ;; any way of switching them off.
+	  (display-completion-list (if iswitchb-matches
+				       iswitchb-matches
+				     iswitchb-buflist)
+				   :help-string "iswitchb "
+				   :activate-callback 
+				   '(lambda (x y z) 
+				      (message "doesnt work yet, sorry!")))
+	;; else running Emacs
+	(display-completion-list (if iswitchb-matches
+				     iswitchb-matches
+				   iswitchb-buflist))
+	))))
+
+;;; VISIT CHOSEN BUFFER
+(defun iswitchb-visit-buffer (buffer)
+  "Visit buffer named BUFFER according to `iswitchb-method'."
+  (let* (win  newframe)
+    (cond
+     ((eq iswitchb-method 'samewindow)
+      (switch-to-buffer buffer))
+
+     ((memq iswitchb-method '(always-frame maybe-frame))
+      (cond
+       ((and (setq win (iswitchb-window-buffer-p buffer))
+	     (or (eq iswitchb-method 'always-frame)
+		 (y-or-n-p "Jump to frame? ")))
+	(setq newframe (window-frame win))
+	(raise-frame newframe)
+	(select-frame newframe)
+	(select-window win)
+	(if (not iswitchb-xemacs)
+	    ;; reposition mouse to make frame active.  not needed in XEmacs
+	    ;; This line came from the other-frame defun in Emacs.
+	    (set-mouse-position (selected-frame) (1- (frame-width)) 0))
+	)
+       (t
+	;;  No buffer in other frames...
+	(switch-to-buffer buffer)
+	)))
+
+
+
+     ((eq iswitchb-method 'otherwindow)
+      (switch-to-buffer-other-window buffer))
+
+     ((eq iswitchb-method 'otherframe)
+      (progn
+	(switch-to-buffer-other-frame buffer)
+	(if (not iswitchb-xemacs)
+	    (set-mouse-position (selected-frame) (1- (frame-width)) 0))
+	)
+      ) )))
+
+(defun iswitchb-possible-new-buffer (buf)
+  "Possibly create and visit a new buffer called BUF."
+
+  (let ((newbufcreated))
+    (if (and iswitchb-newbuffer
+	     (or
+	      (not iswitchb-prompt-newbuffer)
+	      
+	      (and iswitchb-prompt-newbuffer
+		   (y-or-n-p
+		    (format
+		     "No buffer matching `%s', create one? "
+		     buf)))))
+	;; then create a new buffer
+	(progn
+	  (setq newbufcreated (get-buffer-create buf))
+	  (if (fboundp 'set-buffer-major-mode)
+	      (set-buffer-major-mode newbufcreated))
+	  (iswitchb-visit-buffer newbufcreated))
+      ;; else wont create new buffer
+      (message (format "no buffer matching `%s'" buf))
+      )))
+
+(defun iswitchb-window-buffer-p  (buffer)
+  "Return window pointer if BUFFER is visible in another frame.  If
+BUFFER is visible in the current frame, return nil."
+
+  (interactive)
+
+  (let ((blist (iswitchb-get-buffers-in-frames 'current)))
+    ;;If the buffer is visible in current frame, return nil
+    (if (memq buffer blist)
+	nil
+      ;;  maybe in other frame...
+      (get-buffer-window buffer 'visible)
+      )))
+
+;;; KEYBINDINGS AND TOP LEVEL FUNCTIONS.
+(defun iswitchb-default-keybindings ()
+  "Set up default keybindings for iswitchb.  
+Call this function to override the normal bindings."
+  (interactive)
+  (global-set-key "b" 'iswitchb-buffer)
+  (global-set-key "4b" 'iswitchb-buffer-other-window)
+  (global-set-key "5b" 'iswitchb-buffer-other-frame))
+
+
+
+;;;###autoload
+(defun iswitchb-buffer ()
+  "Switch to another buffer.
+
+The buffer name is selected interactively by typing a substring.  The
+buffer is displayed according to `iswitchb-default-method' -- the
+default is to show it in the same window, unless it is already visible
+in another frame.  For details of keybindings, do `C-h f
+iswitchb-mode'."
+
+  (interactive)
+  (setq iswitchb-method iswitchb-default-method)
+  (iswitchb-entry))
+
+
+;;;###autoload
+(defun iswitchb-buffer-other-window ()
+  "Switch to another buffer and show it in another window.
+The buffer name is selected interactively by typing a substring.
+For details of keybindings, do `C-h f iswitchb-mode'."
+  (interactive)
+  (setq iswitchb-method 'otherwindow)
+  (iswitchb-entry))
+
+
+
+;;;###autoload
+(defun iswitchb-buffer-other-frame ()
+  "Switch to another buffer and show it in another frame.
+The buffer name is selected interactively by typing a substring.
+For details of keybindings, do `C-h f iswitchb-mode'."
+  (interactive)
+  (setq iswitchb-method 'otherframe)
+  (iswitchb-entry))
+
+
+
+(defun iswitchb-entry ()
+  "Simply fall into iswitchb -- the main function."
+  (iswitchb))
+
+
+
+
+
+;;; XEMACS HACK FOR SHOWING DEFAULT BUFFER
+
+;; The first time we enter the minibuffer, Emacs puts up the default
+;; buffer to switch to, but XEmacs doesnt -- presumably there is a
+;; subtle difference in the two, either in icomplete or somewhere
+;; else.  The default is shown for both whenever we delete all of our
+;; text though, indicating its just a problem the first time we enter
+;; the function.  To solve this, we use another entry hook for emacs
+;; to show the default the first time we enter the minibuffer.
+
+(defun iswitchb-init-Xemacs-trick ()
+  "Display default buffer when first entering minibuffer.  This is a
+hack for XEmacs, and should really be handled by iswitchb-exhibit."
+  (if (iswitchb-entryfn-p)
+      (progn
+	(iswitchb-show-default-buffer)
+	(goto-char (point-min)))))
+
+;; add this hook for Xemacs only.
+(if iswitchb-xemacs
+    (add-hook 'iswitchb-minibuffer-setup-hook 
+	      'iswitchb-init-Xemacs-trick))
+
+
+;;; XEMACS / BACKSPACE key
+;; For some reason, if the backspace key is pressed in xemacs, the
+;; line gets confused, so I've added a simple key definition to make
+;; backspace act like the normal delete key.  
+
+(defun iswitchb-xemacs-backspacekey ()
+  "Bind backspace to `backward-delete-char'."
+  (define-key iswitchb-mode-map '[backspace] 'backward-delete-char)
+  (define-key iswitchb-mode-map '[(meta backspace)] 'backward-kill-word)
+  )
+
+
+(if iswitchb-xemacs
+    (add-hook 'iswitchb-define-mode-map-hook 
+	      'iswitchb-xemacs-backspacekey))
+
+
+
+;;; ICOMPLETE TYPE CODE
+
+(defun iswitchb-exhibit ()
+  "Find matching buffers and display them in the minibuffer.
+Copied from `icomplete-exhibit' with two changes:
+1. It prints a default buffer name when there is no text yet entered.
+2. It calls my completion routine rather than the standard completion."
+
+  (if iswitchb-use-mycompletion
+      (let ((contents (buffer-substring (point-min)(point-max)))
+	    (buffer-undo-list t))
+	(save-excursion
+	  (goto-char (point-max))
+                                        ; Register the end of input, so we
+                                        ; know where the extra stuff
+                                        ; (match-status info) begins:
+	  (if (not (boundp 'iswitchb-eoinput))
+	      ;; In case it got wiped out by major mode business:
+	      (make-local-variable 'iswitchb-eoinput))
+	  (setq iswitchb-eoinput (point))
+	  ;; Update the list of matches
+	  (setq iswitchb-text contents)
+	  (iswitchb-set-matches)
+	  (setq iswitchb-rescan t)
+	  (iswitchb-set-common-completion)
+
+	  ;; Insert the match-status information:
+	  (if (> (point-max) 1)
+	      (insert-string
+	       (iswitchb-completions 
+		contents
+		minibuffer-completion-table
+		minibuffer-completion-predicate
+		(not minibuffer-completion-confirm)))
+	    ;; else put in default
+	    (iswitchb-show-default-buffer))
+	  ))))
+
+(defun iswitchb-show-default-buffer ()
+  "Insert the default buffer to switch to."
+  ;; insert done this way to preserve any text-propertes.
+  (insert (concat "  {"  iswitchb-default-buffer  "} [Default]")))
+
+(defun iswitchb-completions
+  (name candidates predicate require-match)
+  "Return the string that is displayed after the user's text.
+Modified from `icomplete-completions'."
+  
+  (let ((comps iswitchb-matches)
+                                        ; "-determined" - only one candidate
+        (open-bracket-determined (if require-match "(" "["))
+        (close-bracket-determined (if require-match ")" "]"))
+                                        ;"-prospects" - more than one candidate
+        (open-bracket-prospects "{")
+        (close-bracket-prospects "}")
+	first
+        )
+
+    (if (and iswitchb-use-fonts  comps)
+	(progn
+	  (setq first (car comps))
+	  (setq first (format "%s" first))
+	  (put-text-property 0 (length first) 'face
+			     (if (eq (length comps) 1) 
+				 'font-lock-comment-face
+			       'font-lock-function-name-face)
+			     first) 
+	  (setq comps  (cons first (cdr comps)))
+	  ))
+
+    (cond ((null comps) (format " %sNo match%s"
+				open-bracket-determined
+				close-bracket-determined))
+
+	  ((null (cdr comps))		;one match
+	   (concat (if (and (> (length (car comps))
+			       (length name)))
+		       (concat open-bracket-determined
+			       ;; when there is one match, show the 
+			       ;; matching buffer name in full
+			       (car comps)
+			       close-bracket-determined)
+		     "")
+		   (if (not iswitchb-use-fonts) " [Matched]")
+		   ))
+	  (t				;multiple matches
+	   (let* (
+		  ;;(most (try-completion name candidates predicate))
+		  (most nil)
+		  (most-len (length most))
+		  most-is-exact
+		  first
+		  (alternatives
+		   (apply
+		    (function concat)
+		    (cdr (apply
+			  (function nconc)
+			  (mapcar '(lambda (com)
+				     (if (= (length com) most-len)
+					 ;; Most is one exact match,
+					 ;; note that and leave out
+					 ;; for later indication:
+					 (progn
+					   (setq most-is-exact t)
+					   ())
+				       (list ","
+					     (substring com
+							most-len))))
+				  comps))))))
+
+	     (concat
+
+	      ;; put in common completion item -- what you get by
+	      ;; pressing tab
+	      (if (> (length iswitchb-common-match-string) (length name))
+		  (concat open-bracket-determined
+			  (substring iswitchb-common-match-string 
+				     (length name))
+			  close-bracket-determined)
+		)
+	      ;; end of partial matches...
+
+	      ;; think this bit can be ignored.
+	      (and (> most-len (length name))
+		   (concat open-bracket-determined
+			   (substring most (length name))
+			   close-bracket-determined))
+	      
+	      ;; list all alternatives
+	      open-bracket-prospects
+	      (if most-is-exact
+		  (concat "," alternatives)
+		alternatives)
+	      close-bracket-prospects)))
+	  )))
+
+(defun iswitchb-minibuffer-setup ()
+  "Set up minibuffer for iswitchb.  Copied from
+`icomplete-minibuffer-setup-hook'."
+  (if (iswitchb-entryfn-p)
+      (progn
+
+	(make-local-variable 'iswitchb-use-mycompletion)
+	(setq iswitchb-use-mycompletion t)
+	(make-local-hook 'pre-command-hook)
+	(add-hook 'pre-command-hook
+		  'iswitchb-pre-command
+		  nil t)
+	(make-local-hook 'post-command-hook)
+	(add-hook 'post-command-hook
+		  'iswitchb-post-command
+		  nil t)
+	
+	(run-hooks 'iswitchb-minibuffer-setup-hook) 
+	)
+    ))
+
+
+(defun iswitchb-pre-command ()
+  "Run before command in iswitchb."
+  (iswitchb-tidy))
+
+
+(defun iswitchb-post-command ()
+  "Run after command in iswitchb."
+  (iswitchb-exhibit)
+  )
+
+
+
+(defun iswitchb-tidy ()
+  "Remove completions display, if any, prior to new user input.
+Copied from `icomplete-tidy'."
+
+  (if (and (boundp 'iswitchb-eoinput)
+	   iswitchb-eoinput)
+      
+      (if (> iswitchb-eoinput (point-max))
+	  ;; Oops, got rug pulled out from under us - reinit:
+	  (setq iswitchb-eoinput (point-max))
+	(let ((buffer-undo-list buffer-undo-list )) ; prevent entry
+	  (delete-region iswitchb-eoinput (point-max))))
+    
+    ;; Reestablish the local variable 'cause minibuffer-setup is weird:
+    (make-local-variable 'iswitchb-eoinput)
+    (setq iswitchb-eoinput 1)))
+
+
+(defun iswitchb-entryfn-p ()
+  "Return non-nil if `this-command' shows we are using iswitchb-buffer."
+  (and (symbolp this-command)		; ignore lambda functions
+       (member (symbol-name this-command)
+	       '("iswitchb-buffer"
+		 "iswitchb-buffer-other-frame"
+		 "iswitchb-buffer-other-window"))))
+
+(defun iswitchb-summaries-to-end ()
+  "Move the summaries to the end of the list.  This is an example
+function which can be hooked on to `iswitchb-make-buflist-hook'.
+Any buffer matching the regexps `Summary' or `output\*$'are put to
+the end of the list."
+
+  (let ((summaries (delq nil (mapcar 
+			      '(lambda (x) 
+				 (if (or 
+				      (string-match "Summary" x)
+				      (string-match "output\\*$" x))
+				     x))
+			      buflist)
+			      )))
+    
+    (mapcar 'iswitchb-to-end summaries)))
+
+
+
+;;; HOOKS
+(add-hook 'minibuffer-setup-hook 'iswitchb-minibuffer-setup)
+
+(provide 'iswitchb)
+
+;;; ISWITCHB.EL ends here