mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-12-27 10:54:40 +00:00
1195 lines
37 KiB
EmacsLisp
1195 lines
37 KiB
EmacsLisp
;;; 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>
|
||
;; Keywords: extensions
|
||
;; location: http://www.cogs.susx.ac.uk/users/stephene/emacs
|
||
|
||
;; This file is part of GNU Emacs.
|
||
|
||
;; GNU Emacs 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.
|
||
|
||
;; GNU Emacs 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.
|
||
|
||
;;; Installation:
|
||
|
||
;; To get the functions in this package bound to keys, do
|
||
;; (iswitchb-default-keybindings)
|
||
|
||
;;; 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:
|
||
|
||
;;; 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-buffer'.
|
||
Possible values:
|
||
`samewindow' Show new buffer in same window
|
||
`otherwindow' Show new buffer in another window (same frame)
|
||
`display' Display buffer in another window without switching to it
|
||
`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', `display', `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-buffer' completion feedback.
|
||
Should only be set to t by iswitchb functions, so that it doesn't
|
||
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-buffer'.")
|
||
|
||
(defvar iswitchb-history nil
|
||
"History of buffers selected using `iswitchb-buffer'.")
|
||
|
||
(defvar iswitchb-exit nil
|
||
"Flag to monitor how `iswitchb-buffer' 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-buffer'."
|
||
(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 empty, 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, maybe create a new one."
|
||
(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.
|
||
Currently visible buffers 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 'display)
|
||
(display-buffer 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)
|
||
)))
|
||
|
||
;;;###autoload
|
||
(defun iswitchb-default-keybindings ()
|
||
"Set up default keybindings for `iswitchb-buffer'.
|
||
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 "4" 'iswitchb-display-buffer)
|
||
(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 `\\[describe-function] iswitchb'."
|
||
(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 `\\[describe-function] iswitchb'."
|
||
(interactive)
|
||
(setq iswitchb-method 'otherwindow)
|
||
(iswitchb-entry))
|
||
|
||
|
||
|
||
;;;###autoload
|
||
(defun iswitchb-display-buffer ()
|
||
"Display a buffer in another window but don't select it.
|
||
The buffer name is selected interactively by typing a substring.
|
||
For details of keybindings, do `\\[describe-function] iswitchb'."
|
||
(interactive)
|
||
(setq iswitchb-method 'display)
|
||
(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 `\\[describe-function] iswitchb'."
|
||
(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 a list 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-buffer'.
|
||
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-buffer'."
|
||
(iswitchb-tidy))
|
||
|
||
|
||
(defun iswitchb-post-command ()
|
||
"Run after command in `iswitchb-buffer'."
|
||
(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
|
||
(memq this-command
|
||
'(iswitchb-buffer
|
||
iswitchb-buffer-other-frame
|
||
iswitchb-display-buffer
|
||
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
|