aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard M. Stallman1997-08-27 04:13:52 +0000
committerRichard M. Stallman1997-08-27 04:13:52 +0000
commit1ffece3a395a51200dab005bc63e1eaca22854f2 (patch)
treefa7fbc95e546af0c9691e332deaec1254efcf4ff
parent3a295f7b0d200b6d9c54684d031a63532fc05451 (diff)
downloademacs-1ffece3a395a51200dab005bc63e1eaca22854f2.tar.gz
emacs-1ffece3a395a51200dab005bc63e1eaca22854f2.zip
Many changes by Ivar Rummelhoff.
-rw-r--r--lisp/winner.el374
1 files changed, 140 insertions, 234 deletions
diff --git a/lisp/winner.el b/lisp/winner.el
index ff09b3a3fc9..ad6ae4399ed 100644
--- a/lisp/winner.el
+++ b/lisp/winner.el
@@ -1,11 +1,11 @@
1;;; winner.el --- Restore window configuration or change buffer 1;;; winner.el --- Restore window configuration (or switch buffer)
2 2
3;; Copyright (C) 1997 Free Software Foundation. Inc. 3;; Copyright (C) 1997 Free Software Foundation. Inc.
4 4
5;; Author: Ivar Rummelhoff <ivarr@ifi.uio.no> 5;; Author: Ivar Rummelhoff <ivarr@ifi.uio.no>
6;; Maintainer: Ivar Rummelhoff <ivarr@ifi.uio.no> 6;; Maintainer: Ivar Rummelhoff <ivarr@ifi.uio.no>
7;; Created: 27 Feb 1997 7;; Created: 27 Feb 1997
8;; Keywords: extensions,windows 8;; Keywords: extensions, windows
9 9
10;; This file is part of GNU Emacs. 10;; This file is part of GNU Emacs.
11 11
@@ -25,58 +25,34 @@
25;; Boston, MA 02111-1307, USA. 25;; Boston, MA 02111-1307, USA.
26 26
27;;; Commentary: 27;;; Commentary:
28
29;; Winner mode is a global minor mode that when turned on records
30;; changes in window configuration. This way the changes can be
31;; "undone" using the function `winner-undo'. By default this one is
32;; bound to the key sequence ctrl-x left. If you change your mind
33;; (while undoing), you can press ctrl-x right (calling
34;; `winner-redo'). Unlike the normal undo, you may have to skip
35;; through several identical window configurations in order to find
36;; the one you want. This is a bug due to some techical limitations
37;; in Emacs and can maybe be fixed in the future.
28;; 38;;
29;; winner.el provides a minor mode (`winner-mode') that does 39;; In addition to this I have added `winner-switch' which is a program
30;; essentially two things: 40;; that switches to other buffers without disturbing Winner mode. If
31;; 41;; you bind this command to a key sequence, you may step through all
32;; 1) It keeps track of changing window configurations, so that 42;; your buffers (except the ones mentioned in `winner-skip-buffers' or
33;; when you wish to go back to a previous view, all you have 43;; matched by `winner-skip-regexps'). With a numeric prefix argument
34;; to do is to press C-left a couple of times. 44;; skip several buffers at a time.
35;;
36;; 2) It lets you switch to other buffers by pressing C-right.
37;;
38;; To use Winner mode, put this line in your .emacs file:
39;;
40;; (add-hook 'after-init-hook (lambda () (winner-mode 1)))
41
42;; Details:
43;;
44;; 1. You may of course decide to use other bindings than those
45;; mentioned above. Just set these variables in your .emacs:
46;;
47;; `winner-prev-event'
48;; `winner-next-event'
49;;
50;; 2. When you have found the view of your choice
51;; (using your favourite keys), you may press ctrl-space
52;; (`winner-max-event') to `delete-other-windows'.
53;;
54;; 3. Winner now keeps one configuration stack for each frame.
55;;
56;;
57;;
58;; Yours sincerely, Ivar Rummelhoff
59;;
60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
61 45
62;;; Code: 46;;; Code:
63 47
48(eval-when-compile (require 'cl))
49(require 'ring)
64 50
51(defvar winner-dont-bind-my-keys nil
52 "If non-nil: Do not use `winner-mode-map' in Winner mode.")
65 53
66;;;; Variables you may want to change 54(defvar winner-ring-size 100
67 55 "Maximum number of stored window configurations per frame.")
68(defvar winner-prev-event 'C-left
69 "Winner mode binds this event to the command `winner-previous'.")
70
71(defvar winner-next-event 'C-right
72 "Winner mode binds this event to the command `winner-next'.")
73
74(defvar winner-max-event 67108896 ; CTRL-space
75 "Event for deleting other windows
76after having selected a view with Winner.
77
78The normal functions of this event will also be performed.
79In the default case (CTRL-SPACE) the mark will be set.")
80 56
81(defvar winner-skip-buffers 57(defvar winner-skip-buffers
82 '("*Messages*", 58 '("*Messages*",
@@ -84,7 +60,7 @@ In the default case (CTRL-SPACE) the mark will be set.")
84 ".newsrc-dribble", 60 ".newsrc-dribble",
85 "*Completions*", 61 "*Completions*",
86 "*Buffer list*") 62 "*Buffer list*")
87 "Exclude these buffer names from any \(Winner mode\) list of buffers.") 63 "Exclude these buffer names from any \(Winner switch\) list of buffers.")
88 64
89(defvar winner-skip-regexps '("^ ") 65(defvar winner-skip-regexps '("^ ")
90 "Winner excludes buffers with names matching any of these regexps. 66 "Winner excludes buffers with names matching any of these regexps.
@@ -93,31 +69,43 @@ They are not included in any Winner mode list of buffers.
93By default `winner-skip-regexps' is set to \(\"^ \"\), 69By default `winner-skip-regexps' is set to \(\"^ \"\),
94which excludes \"invisible buffers\".") 70which excludes \"invisible buffers\".")
95 71
96 72(defvar winner-ring-alist nil)
97(defvar winner-limit 50
98 "Winner will save no more than 2 * `winner-limit' window configurations.
99\(.. and no less than `winner-limit'.\)")
100 73
101(defvar winner-mode-hook nil 74(defsubst winner-ring (frame)
102 "Functions to run whenever Winner mode is turned on.") 75 (or (cdr (assq frame winner-ring-alist))
76 (progn
77 (push (cons frame (make-ring winner-ring-size))
78 winner-ring-alist)
79 (cdar winner-ring-alist))))
103 80
104(defvar winner-mode-leave-hook nil 81(defvar winner-modified-list nil)
105 "Functions to run whenever Winner mode is turned off.")
106 82
107(defvar winner-dont-bind-my-keys nil 83(defun winner-change-fun ()
108 "If non-nil: Do not use `winner-mode-map' in Winner mode.") 84 (pushnew (selected-frame) winner-modified-list))
109 85
86(defun winner-save-new-configurations ()
87 (while winner-modified-list
88 (ring-insert
89 (winner-ring (car winner-modified-list))
90 (current-window-configuration (pop winner-modified-list)))))
110 91
92(defun winner-set (conf)
93 (set-window-configuration conf)
94 (if (eq (selected-window) (minibuffer-window))
95 (other-window 1)))
111 96
112;;;; Winner mode
113 97
114(eval-when-compile (require 'cl)) 98;;; Winner mode (a minor mode)
115 99
100(defvar winner-mode-hook nil
101 "Functions to run whenever Winner mode is turned on.")
116 102
117(defvar winner-mode nil) ; For the modeline. 103(defvar winner-mode-leave-hook nil
104 "Functions to run whenever Winner mode is turned off.")
105
106(defvar winner-mode nil) ; mode variable
118(defvar winner-mode-map nil "Keymap for Winner mode.") 107(defvar winner-mode-map nil "Keymap for Winner mode.")
119 108
120;;;###autoload
121(defun winner-mode (&optional arg) 109(defun winner-mode (&optional arg)
122 "Toggle Winner mode. 110 "Toggle Winner mode.
123With arg, turn Winner mode on if and only if arg is positive." 111With arg, turn Winner mode on if and only if arg is positive."
@@ -125,91 +113,70 @@ With arg, turn Winner mode on if and only if arg is positive."
125 (let ((on-p (if arg (> (prefix-numeric-value arg) 0) 113 (let ((on-p (if arg (> (prefix-numeric-value arg) 0)
126 (not winner-mode)))) 114 (not winner-mode))))
127 (cond 115 (cond
128 (on-p (let ((winner-frames-changed (frame-list))) 116 ;; Turn mode on
129 (winner-do-save)) ; Save current configurations 117 (on-p
130 (add-hook 'window-configuration-change-hook 'winner-save-configuration) 118 (setq winner-mode t)
131 (setq winner-mode t) 119 (add-hook 'window-configuration-change-hook 'winner-change-fun)
132 (run-hooks 'winner-mode-hook)) 120 (add-hook 'post-command-hook 'winner-save-new-configurations)
133 (t (remove-hook 'window-configuration-change-hook 'winner-save-configuration) 121 (setq winner-modified-list (frame-list))
134 (when winner-mode 122 (winner-save-new-configurations)
135 (setq winner-mode nil) 123 (run-hooks 'winner-mode-hook))
136 (run-hooks 'winner-mode-leave-hook)))) 124 ;; Turn mode off
125 (winner-mode
126 (setq winner-mode nil)
127 (run-hooks 'winner-mode-leave-hook)))
137 (force-mode-line-update))) 128 (force-mode-line-update)))
138 129
139 130;; Inspired by undo (simple.el)
140;; List of frames which have changed 131(defun winner-undo (arg)
141(defvar winner-frames-changed nil) 132 "Switch back to an earlier window configuration saved by Winner mode.
142 133In other words, \"undo\" changes in window configuration."
143;; Time to save the window configuration. 134 (interactive "p")
144(defun winner-save-configuration () 135 (cond
145 (push (selected-frame) winner-frames-changed) 136 ((not winner-mode) (error "Winner mode is turned off"))
146 (add-hook 'post-command-hook 'winner-do-save)) 137 ((eq (selected-window) (minibuffer-window))
147 138 (error "No winner undo from minibuffer."))
148 139 (t (setq this-command t)
149(defun winner-do-save () 140 (if (eq last-command 'winner-undo)
150 (let ((current (selected-frame))) 141 ;; This was no new window configuration after all.
151 (unwind-protect 142 (ring-remove winner-pending-undo-ring 0)
152 (do ((frames winner-frames-changed (cdr frames))) 143 (setq winner-pending-undo-ring (winner-ring (selected-frame)))
153 ((null frames)) 144 (setq winner-undo-counter 0))
154 (unless (memq (car frames) (cdr frames)) 145 (winner-undo-more (or arg 1))
155 ;; Process each frame once. 146 (message "Winner undo (%d)!" winner-undo-counter)
156 (select-frame (car frames)) 147 (setq this-command 'winner-undo))))
157 (winner-push (current-window-configuration) (car frames)))) 148
158 (setq winner-frames-changed nil) 149(defvar winner-pending-undo-ring nil)
159 (select-frame current) 150
160 (remove-hook 'post-command-hook 'winner-do-save)))) 151(defvar winner-undo-counter nil)
161 152
162 153(defun winner-undo-more (count)
163 154 "Undo N window configuration changes beyond what was already undone.
164 155Call `winner-undo-start' to get ready to undo recent changes,
165 156then call `winner-undo-more' one or more times to undo them."
166;;;; Configuration stacks (one for each frame) 157 (let ((len (ring-length winner-pending-undo-ring)))
167 158 (incf winner-undo-counter count)
168 159 (if (>= winner-undo-counter len)
169(defvar winner-stacks nil) ; ------ " ------ 160 (error "No further window configuration undo information")
170 161 (winner-set
171;; This works around a bug in defstruct. 162 (ring-ref winner-pending-undo-ring
172(defvar custom-print-functions nil) 163 winner-undo-counter)))))
173 164
174;; A stack of window configurations with some additional information. 165(defun winner-redo ()
175(defstruct (winner-stack 166 "Restore a more recent window configuration saved by Winner mode."
176 (:constructor winner-stack-new 167 (interactive)
177 (config &aux 168 (cond
178 (data (list config)) 169 ((eq last-command 'winner-undo)
179 (place data)))) 170 (ring-remove winner-pending-undo-ring 0)
180 data place (count 1)) 171 (winner-set
181 172 (ring-remove winner-pending-undo-ring 0))
182 173 (or (eq (selected-window) (minibuffer-window))
183;; Return the stack of this frame 174 (message "Winner undid undo!")))
184(defun winner-stack (frame) 175 (t (error "Previous command was not a winner-undo"))))
185 (let ((stack (cdr (assq frame winner-stacks)))) 176
186 (if stack (winner-stack-data stack) 177;;; Winner switch
187 ;; Else make new stack 178
188 (letf (((selected-frame) frame)) 179(defun winner-switch-buffer-list ()
189 (let ((config (current-window-configuration)))
190 (push (cons frame (winner-stack-new config))
191 winner-stacks)
192 (list config))))))
193
194;; Push this window configuration on the right stack,
195;; but make sure the stack doesn't get too large etc...
196(defun winner-push (config frame)
197 (let ((this (cdr (assq frame winner-stacks))))
198 (if (not this) (push (cons frame (winner-stack-new config))
199 winner-stacks)
200 (push config (winner-stack-data this))
201 (when (> (incf (winner-stack-count this)) winner-limit)
202 ;; No more than 2*winner-limit configs
203 (setcdr (winner-stack-place this) nil)
204 (setf (winner-stack-place this)
205 (winner-stack-data this))
206 (setf (winner-stack-count this) 1)))))
207
208;;;; Selecting a window configuration
209
210;; Return list of names of other buffers, excluding the current buffer
211;; and buffers specified by the user.
212(defun winner-other-buffers ()
213 (loop for buf in (buffer-list) 180 (loop for buf in (buffer-list)
214 for name = (buffer-name buf) 181 for name = (buffer-name buf)
215 unless (or (eq (current-buffer) buf) 182 unless (or (eq (current-buffer) buf)
@@ -218,99 +185,38 @@ With arg, turn Winner mode on if and only if arg is positive."
218 if (string-match regexp name) return t 185 if (string-match regexp name) return t
219 finally return nil)) 186 finally return nil))
220 collect name)) 187 collect name))
188
189(defvar winner-switch-list nil)
190
191(defun winner-switch (count)
192 "Step through your buffers without disturbing `winner-mode'.
193`winner-switch' does not consider buffers mentioned in the list
194`winner-skip-buffers' or matched by `winner-skip-regexps'."
195 (interactive "p")
196 (decf count)
197 (setq this-command t)
198 (cond
199 ((eq last-command 'winner-switch)
200 (if winner-mode (ring-remove (winner-ring (selected-frame)) 0))
201 (bury-buffer (current-buffer))
202 (mapcar 'bury-buffer winner-switch-list))
203 (t (setq winner-switch-list (winner-switch-buffer-list))))
204 (setq winner-switch-list (nthcdr count winner-switch-list))
205 (or winner-switch-list
206 (setq winner-switch-list (winner-switch-buffer-list))
207 (error "No more buffers"))
208 (switch-to-buffer (pop winner-switch-list))
209 (message (concat "Winner: [%s] "
210 (mapconcat 'identity winner-switch-list " "))
211 (buffer-name))
212 (setq this-command 'winner-switch))
221 213
222(defun winner-select (&optional arg)
223 "Change to previous or new window configuration.
224With arg start at position 1 if arg is positive, and
225at -1 if arg is negative; else start at position 0.
226\(For Winner to record changes in window configurations,
227Winner mode must be turned on.\)"
228 (interactive "P")
229
230 (setq arg
231 (cond
232 ((not arg) nil)
233 ((> (prefix-numeric-value arg) 0) winner-next-event)
234 ((< (prefix-numeric-value arg) 0) winner-prev-event)
235 (t nil)))
236 (if arg (push arg unread-command-events))
237
238 (let ((stack (winner-stack (selected-frame)))
239 (store nil)
240 (buffers (winner-other-buffers))
241 (passed nil)
242 (config (current-window-configuration))
243 (pos 0) event)
244 ;; `stack' and `store' are stacks of window configuration while
245 ;; `buffers' and `passed' are stacks of buffer names.
246
247 (condition-case nil
248
249 (loop
250 (setq event (read-event))
251 (cond
252
253 ((eq event winner-prev-event)
254 (cond (passed (push (pop passed) buffers)(decf pos))
255 ((cdr stack)(push (pop stack) store) (decf pos))
256 (t (setq stack (append (nreverse store) stack))
257 (setq store nil)
258 (setq pos 0))))
259
260 ((eq event winner-next-event)
261 (cond (store (push (pop store) stack) (incf pos))
262 (buffers (push (pop buffers) passed) (incf pos))
263 (t (setq buffers (nreverse passed))
264 (setq passed nil)
265 (setq pos 0))))
266
267 ((eq event winner-max-event)
268 ;; Delete other windows and leave.
269 (delete-other-windows)
270 ;; Let this change be saved.
271 (setq pos -1)
272 ;; Perform other actions of this event.
273 (push event unread-command-events)
274 (return))
275 (t (push event unread-command-events) (return)))
276
277 (cond
278 ;; Display
279 (passed (set-window-buffer (selected-window) (car passed))
280 (message (concat "Winner\(%d\): [%s] "
281 (mapconcat 'identity buffers " "))
282 pos (car passed)))
283
284 (t (set-window-configuration (car stack))
285 (if (window-minibuffer-p (selected-window))
286 (other-window 1))
287 (message "Winner\(%d\)" pos))))
288
289 (quit (set-window-configuration config)
290 (setq pos 0)))
291 (if (zerop pos)
292 ;; Do not record these changes.
293 (remove-hook 'post-command-hook 'winner-do-save)
294 ;; Else update the buffer list and make sure that the displayed
295 ;; buffer is the same as the current buffer.
296 (switch-to-buffer (window-buffer)))))
297
298(defun winner-previous ()
299 "Change to previous window configuration."
300 (interactive)
301 (winner-select -1))
302
303(defun winner-next ()
304 "Change to new window configuration."
305 (interactive)
306 (winner-select 1))
307
308;;;; To be evaluated when the package is loaded: 214;;;; To be evaluated when the package is loaded:
309 215
310(unless winner-mode-map 216(unless winner-mode-map
311 (setq winner-mode-map (make-sparse-keymap)) 217 (setq winner-mode-map (make-sparse-keymap))
312 (define-key winner-mode-map (vector winner-prev-event) 'winner-previous) 218 (define-key winner-mode-map [?\C-x left] 'winner-undo)
313 (define-key winner-mode-map (vector winner-next-event) 'winner-next)) 219 (define-key winner-mode-map [?\C-x right] 'winner-redo))
314 220
315(unless (or (assq 'winner-mode minor-mode-map-alist) 221(unless (or (assq 'winner-mode minor-mode-map-alist)
316 winner-dont-bind-my-keys) 222 winner-dont-bind-my-keys)