From 9397e56f7424b87f0b52be1235b25a56002661f1 Mon Sep 17 00:00:00 2001 From: Martin Rudalics Date: Sat, 11 Jun 2011 11:50:37 +0200 Subject: [PATCH] Move/add window-buffer-related functions to window.el. * buffer.c: New Lisp objects Qbuffer_list_update_hook and Qclone_number. Remove external declaration of Qdelete_window. (Fbuffer_list): Rewrite doc-string. Minor restructuring of code. (Fget_buffer_create, Fmake_indirect_buffer, Frename_buffer): Run Qbuffer_list_update_hook if allowed. (Fother_buffer): Rewrite doc-string. Major rewrite for new buffer list implementation. (other_buffer_safely): New function. (Fkill_buffer): Replace call to replace_buffer_in_all_windows by calls to replace_buffer_in_windows and replace_buffer_in_windows_safely. Run Qbuffer_list_update_hook if allowed. (record_buffer): Inhibit quitting and rewrite using quittable functions. Run Qbuffer_list_update_hook if allowed. (Frecord_buffer, Funrecord_buffer): New functions. (switch_to_buffer_1, Fswitch_to_buffer): Remove. Move switch-to-buffer to window.el. (bury-buffer): Move to window.el. (Vbuffer_list_update_hook): New variable. * lisp.h (other_buffer_safely): Add prototype in buffer.c section. * window.h (resize_frame_windows): Move up in code. (Fwindow_frame): Remove EXFUN. (replace_buffer_in_all_windows): Remove prototype. (replace_buffer_in_windows_safely): Add prototype. * window.c: Declare Qdelete_window static again. Move down declaration of select_count. (Fnext_window, Fprevious_window): Rewrite doc-strings. (Fother_window): Move to window.el. (window_loop): Remove DELETE_BUFFER_WINDOWS and UNSHOW_BUFFER cases. Add REPLACE_BUFFER_IN_WINDOWS_SAFELY case. (Fdelete_windows_on, Freplace_buffer_in_windows): Move to window.el. (replace_buffer_in_windows): Implement by calling Qreplace_buffer_in_windows. (replace_buffer_in_all_windows): Remove with some functionality moved into replace_buffer_in_windows_safely. (replace_buffer_in_windows_safely): New function. (select_window_norecord, select_frame_norecord): Move in front of run_window_configuration_change_hook. Remove now obsolete declarations. (Fset_window_buffer): Rewrite doc-string. Call Qrecord_window_buffer. (keys_of_window): Move binding for other-window to window.el. * loadup.el (top-level): Load window before files for the sake of replace-buffer-in-windows. * files.el (read-buffer-to-switch) (switch-to-buffer-other-window) (switch-to-buffer-other-frame, display-buffer-other-frame): Move to window.el. * simple.el (get-next-valid-buffer, last-buffer, next-buffer) (previous-buffer): Move to window.el. * bindings.el (unbury-buffer): Move to window.el. * window.el (delete-other-windows-vertically): Move after definition of delete-other-windows. (other-window, delete-windows-on, replace-buffer-in-windows): Move here from window.c. (record-window-buffer, unrecord-window-buffer) (set-window-buffer-start-and-point, switch-to-prev-buffer) (switch-to-next-buffer): New functions. (get-next-valid-buffer, last-buffer, next-buffer): Move here from simple.el. Call switch-to-next-buffer. (previous-buffer): Move here from simple.el. Call switch-to-prev-buffer. (bury-buffer): Move here from buffer.c. Switch to previous buffer when window cannot be deleted. (unbury-buffer): Move here from bindings.el. (ctl-x-map): Move binding for other-window from window.c to here. (read-buffer-to-switch, switch-to-buffer-other-window) (switch-to-buffer-other-frame): Move here from files.el. (normalize-buffer-to-switch-to): New functions. (switch-to-buffer): Move here from buffer.c. Use read-buffer-to-switch and normalize-buffer-to-switch-to. --- lisp/ChangeLog | 37 +++ lisp/bindings.el | 5 - lisp/files.el | 94 ------ lisp/loadup.el | 2 +- lisp/simple.el | 54 ---- lisp/window.el | 768 +++++++++++++++++++++++++++++++++++++++++++++-- src/ChangeLog | 51 ++++ src/buffer.c | 457 ++++++++++++---------------- src/lisp.h | 1 + src/window.c | 396 +++++++++--------------- src/window.h | 6 +- 11 files changed, 1164 insertions(+), 707 deletions(-) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 1ac216dfbe2..611531330cc 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,40 @@ +2011-06-10 Martin Rudalics + + * loadup.el (top-level): Load window before files for the sake + of replace-buffer-in-windows. + + * files.el (read-buffer-to-switch) + (switch-to-buffer-other-window) + (switch-to-buffer-other-frame, display-buffer-other-frame): Move + to window.el. + + * simple.el (get-next-valid-buffer, last-buffer, next-buffer) + (previous-buffer): Move to window.el. + + * bindings.el (unbury-buffer): Move to window.el. + + * window.el (delete-other-windows-vertically): Move after + definition of delete-other-windows. + (other-window, delete-windows-on, replace-buffer-in-windows): + Move here from window.c. + (record-window-buffer, unrecord-window-buffer) + (set-window-buffer-start-and-point, switch-to-prev-buffer) + (switch-to-next-buffer): New functions. + (get-next-valid-buffer, last-buffer, next-buffer): Move here + from simple.el. Call switch-to-next-buffer. + (previous-buffer): Move here from simple.el. Call + switch-to-prev-buffer. + (bury-buffer): Move here from buffer.c. Switch to previous + buffer when window cannot be deleted. + (unbury-buffer): Move here from bindings.el. + (ctl-x-map): Move binding for other-window from window.c to + here. + (read-buffer-to-switch, switch-to-buffer-other-window) + (switch-to-buffer-other-frame): Move here from files.el. + (normalize-buffer-to-switch-to): New functions. + (switch-to-buffer): Move here from buffer.c. Use + read-buffer-to-switch and normalize-buffer-to-switch-to. + 2011-06-10 Martin Rudalics * window.el (window-min-height, window-min-width): Move here diff --git a/lisp/bindings.el b/lisp/bindings.el index ffc797966b8..a7b729a1ba3 100644 --- a/lisp/bindings.el +++ b/lisp/bindings.el @@ -454,11 +454,6 @@ Major modes that edit things other than ordinary files may change this (put 'mode-line-buffer-identification 'risky-local-variable t) (make-variable-buffer-local 'mode-line-buffer-identification) -(defun unbury-buffer () "\ -Switch to the last buffer in the buffer list." - (interactive) - (switch-to-buffer (last-buffer))) - (defun mode-line-unbury-buffer (event) "\ Call `unbury-buffer' in this window." (interactive "e") diff --git a/lisp/files.el b/lisp/files.el index 317153dc9bf..aafc6f92906 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -1288,100 +1288,6 @@ return value, which may be passed as the REQUIRE-MATCH arg to 'confirm) (t nil))) -(defun read-buffer-to-switch (prompt) - "Read the name of a buffer to switch to and return as a string. -It is intended for `switch-to-buffer' family of commands since they -need to omit the name of current buffer from the list of completions -and default values." - (let ((rbts-completion-table (internal-complete-buffer-except))) - (minibuffer-with-setup-hook - (lambda () - (setq minibuffer-completion-table rbts-completion-table) - ;; Since rbts-completion-table is built dynamically, we - ;; can't just add it to the default value of - ;; icomplete-with-completion-tables, so we add it - ;; here manually. - (if (and (boundp 'icomplete-with-completion-tables) - (listp icomplete-with-completion-tables)) - (set (make-local-variable 'icomplete-with-completion-tables) - (cons rbts-completion-table - icomplete-with-completion-tables)))) - (read-buffer prompt (other-buffer (current-buffer)) - (confirm-nonexistent-file-or-buffer))))) - -(defun switch-to-buffer-other-window (buffer-or-name &optional norecord) - "Select the buffer specified by BUFFER-OR-NAME in another window. -BUFFER-OR-NAME may be a buffer, a string \(a buffer name), or -nil. Return the buffer switched to. - -If called interactively, prompt for the buffer name using the -minibuffer. The variable `confirm-nonexistent-file-or-buffer' -determines whether to request confirmation before creating a new -buffer. - -If BUFFER-OR-NAME is a string and does not identify an existing -buffer, create a new buffer with that name. If BUFFER-OR-NAME is -nil, switch to the buffer returned by `other-buffer'. - -Optional second argument NORECORD non-nil means do not put this -buffer at the front of the list of recently selected ones. - -This uses the function `display-buffer' as a subroutine; see its -documentation for additional customization information." - (interactive - (list (read-buffer-to-switch "Switch to buffer in other window: "))) - (let ((pop-up-windows t) - same-window-buffer-names same-window-regexps) - (pop-to-buffer buffer-or-name t norecord))) - -(defun switch-to-buffer-other-frame (buffer-or-name &optional norecord) - "Switch to buffer BUFFER-OR-NAME in another frame. -BUFFER-OR-NAME may be a buffer, a string \(a buffer name), or -nil. Return the buffer switched to. - -If called interactively, prompt for the buffer name using the -minibuffer. The variable `confirm-nonexistent-file-or-buffer' -determines whether to request confirmation before creating a new -buffer. - -If BUFFER-OR-NAME is a string and does not identify an existing -buffer, create a new buffer with that name. If BUFFER-OR-NAME is -nil, switch to the buffer returned by `other-buffer'. - -Optional second arg NORECORD non-nil means do not put this -buffer at the front of the list of recently selected ones. - -This uses the function `display-buffer' as a subroutine; see its -documentation for additional customization information." - (interactive - (list (read-buffer-to-switch "Switch to buffer in other frame: "))) - (let ((pop-up-frames t) - same-window-buffer-names same-window-regexps) - (pop-to-buffer buffer-or-name t norecord))) - -(defun display-buffer-other-frame (buffer) - "Display buffer BUFFER in another frame. -This uses the function `display-buffer' as a subroutine; see -its documentation for additional customization information." - (interactive "BDisplay buffer in other frame: ") - (let ((pop-up-frames t) - same-window-buffer-names same-window-regexps - ;;(old-window (selected-window)) - new-window) - (setq new-window (display-buffer buffer t)) - ;; This may have been here in order to prevent the new frame from hiding - ;; the old frame. But it does more harm than good. - ;; Maybe we should call `raise-window' on the old-frame instead? --Stef - ;;(lower-frame (window-frame new-window)) - - ;; This may have been here in order to make sure the old-frame gets the - ;; focus. But not only can it cause an annoying flicker, with some - ;; window-managers it just makes the window invisible, with no easy - ;; way to recover it. --Stef - ;;(make-frame-invisible (window-frame old-window)) - ;;(make-frame-visible (window-frame old-window)) - )) - (defmacro minibuffer-with-setup-hook (fun &rest body) "Temporarily add FUN to `minibuffer-setup-hook' while executing BODY. BODY should use the minibuffer at most once. diff --git a/lisp/loadup.el b/lisp/loadup.el index 7e80ff97cff..4c677523689 100644 --- a/lisp/loadup.el +++ b/lisp/loadup.el @@ -95,6 +95,7 @@ (load "env") (load "format") (load "bindings") +(load "window") ; Needed here for `replace-buffer-in-windows'. (setq load-source-file-function 'load-with-code-conversion) (load "files") @@ -156,7 +157,6 @@ (load "language/cham") (load "indent") -(load "window") (load "frame") (load "term/tty-colors") (load "font-core") diff --git a/lisp/simple.el b/lisp/simple.el index 76269c9ef9a..b36cf2ec3ec 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -52,60 +52,6 @@ wait this many seconds after Emacs becomes idle before doing an update." (defgroup paren-matching nil "Highlight (un)matching of parens and expressions." :group 'matching) - -(defun get-next-valid-buffer (list &optional buffer visible-ok frame) - "Search LIST for a valid buffer to display in FRAME. -Return nil when all buffers in LIST are undesirable for display, -otherwise return the first suitable buffer in LIST. - -Buffers not visible in windows are preferred to visible buffers, -unless VISIBLE-OK is non-nil. -If the optional argument FRAME is nil, it defaults to the selected frame. -If BUFFER is non-nil, ignore occurrences of that buffer in LIST." - ;; This logic is more or less copied from other-buffer. - (setq frame (or frame (selected-frame))) - (let ((pred (frame-parameter frame 'buffer-predicate)) - found buf) - (while (and (not found) list) - (setq buf (car list)) - (if (and (not (eq buffer buf)) - (buffer-live-p buf) - (or (null pred) (funcall pred buf)) - (not (eq (aref (buffer-name buf) 0) ?\s)) - (or visible-ok (null (get-buffer-window buf 'visible)))) - (setq found buf) - (setq list (cdr list)))) - (car list))) - -(defun last-buffer (&optional buffer visible-ok frame) - "Return the last buffer in FRAME's buffer list. -If BUFFER is the last buffer, return the preceding buffer instead. -Buffers not visible in windows are preferred to visible buffers, -unless optional argument VISIBLE-OK is non-nil. -Optional third argument FRAME nil or omitted means use the -selected frame's buffer list. -If no such buffer exists, return the buffer `*scratch*', creating -it if necessary." - (setq frame (or frame (selected-frame))) - (or (get-next-valid-buffer (nreverse (buffer-list frame)) - buffer visible-ok frame) - (get-buffer "*scratch*") - (let ((scratch (get-buffer-create "*scratch*"))) - (set-buffer-major-mode scratch) - scratch))) - -(defun next-buffer () - "Switch to the next buffer in cyclic order." - (interactive) - (let ((buffer (current-buffer))) - (switch-to-buffer (other-buffer buffer t)) - (bury-buffer buffer))) - -(defun previous-buffer () - "Switch to the previous buffer in cyclic order." - (interactive) - (switch-to-buffer (last-buffer (current-buffer) t))) - ;;; next-error support framework diff --git a/lisp/window.el b/lisp/window.el index a88e56be83c..2811baf706d 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -2078,6 +2078,74 @@ WINDOW can be any window and defaults to the selected window." "Return non-nil if WINDOW is the root window of its frame." (eq window (frame-root-window window))) +(defun other-window (count &optional all-frames) + "Select another window in cyclic ordering of windows. +COUNT specifies the number of windows to skip, starting with the +selected window, before making the selection. If COUNT is +positive, skip COUNT windows forwards. If COUNT is negative, +skip -COUNT windows backwards. COUNT zero means do not skip any +window, so select the selected window. In an interactive call, +COUNT is the numeric prefix argument. Return nil. + +If the `other-window' parameter of WINDOW is a function and +`ignore-window-parameters' is nil, call that function with the +arguments COUNT and ALL-FRAMES. + +This function does not select a window whose `no-other-window' +window parameter is non-nil. + +This function uses `next-window' for finding the window to +select. The argument ALL-FRAMES has the same meaning as in +`next-window', but the MINIBUF argument of `next-window' is +always effectively nil." + (interactive "p") + (let* ((window (selected-window)) + (function (and (not ignore-window-parameters) + (window-parameter window 'other-window))) + old-window old-count) + (if (functionp function) + (funcall function count all-frames) + ;; `next-window' and `previous-window' may return a window we are + ;; not allowed to select. Hence we need an exit strategy in case + ;; all windows are non-selectable. + (catch 'exit + (while (> count 0) + (setq window (next-window window nil all-frames)) + (cond + ((eq window old-window) + (when (= count old-count) + ;; Keep out of infinite loops. When COUNT has not changed + ;; since we last looked at `window' we're probably in one. + (throw 'exit nil))) + ((window-parameter window 'no-other-window) + (unless old-window + ;; The first non-selectable window `next-window' got us: + ;; Remember it and the current value of COUNT. + (setq old-window window) + (setq old-count count))) + (t + (setq count (1- count))))) + (while (< count 0) + (setq window (previous-window window nil all-frames)) + (cond + ((eq window old-window) + (when (= count old-count) + ;; Keep out of infinite loops. When COUNT has not changed + ;; since we last looked at `window' we're probably in one. + (throw 'exit nil))) + ((window-parameter window 'no-other-window) + (unless old-window + ;; The first non-selectable window `previous-window' got + ;; us: Remember it and the current value of COUNT. + (setq old-window window) + (setq old-count count))) + (t + (setq count (1+ count))))) + + (select-window window) + ;; Always return nil. + nil)))) + ;; This should probably return non-nil when the selected window is part ;; of an atomic window whose root is the frame's root window. (defun one-window-p (&optional nomini all-frames) @@ -2285,6 +2353,518 @@ window signal an error." (window-check frame)) ;; Always return nil. nil))) + +(defun delete-other-windows-vertically (&optional window) + "Delete the windows in the same column with WINDOW, but not WINDOW itself. +This may be a useful alternative binding for \\[delete-other-windows] + if you often split windows horizontally." + (interactive) + (let* ((window (or window (selected-window))) + (edges (window-edges window)) + (w window) delenda) + (while (not (eq (setq w (next-window w 1)) window)) + (let ((e (window-edges w))) + (when (and (= (car e) (car edges)) + (= (caddr e) (caddr edges))) + (push w delenda)))) + (mapc 'delete-window delenda))) + +;;; Windows and buffers. + +;; `prev-buffers' and `next-buffers' are two reserved window slots used +;; for (1) determining which buffer to show in the window when its +;; buffer shall be buried or killed and (2) which buffer to show for +;; `switch-to-prev-buffer' and `switch-to-next-buffer'. + +;; `prev-buffers' consists of +;; triples. The entries on this list are ordered by the time their +;; buffer has been removed from the window, the most recently removed +;; buffer's entry being first. The window-start and window-point +;; components are `window-start' and `window-point' at the time the +;; buffer was removed from the window which implies that the entry must +;; be added when `set-window-buffer' removes the buffer from the window. + +;; `next-buffers' is the list of buffers that have been replaced +;; recently by `switch-to-prev-buffer'. These buffers are the least +;; preferred candidates of `switch-to-prev-buffer' and the preferred +;; candidates of `switch-to-next-buffer' to switch to. This list is +;; reset to nil by any action changing the window's buffer with the +;; exception of `switch-to-prev-buffer' and `switch-to-next-buffer'. +;; `switch-to-prev-buffer' pushes the buffer it just replaced on it, +;; `switch-to-next-buffer' pops the last pushed buffer from it. + +;; Both `prev-buffers' and `next-buffers' may reference killed buffers +;; if such a buffer was killed while the window was hidden within a +;; window configuration. Such killed buffers get removed whenever +;; `switch-to-prev-buffer' or `switch-to-next-buffer' encounter them. + +;; The following function is called by `set-window-buffer' _before_ it +;; replaces the buffer of the argument window with the new buffer. +(defun record-window-buffer (&optional window) + "Record WINDOW's buffer. +WINDOW must be a live window and defaults to the selected one." + (let* ((window (normalize-live-window window)) + (buffer (window-buffer window)) + (entry (assq buffer (window-prev-buffers window)))) + ;; Reset WINDOW's next buffers. If needed, they are resurrected by + ;; `switch-to-prev-buffer' and `switch-to-next-buffer'. + (set-window-next-buffers window nil) + + (when entry + ;; Remove all entries for BUFFER from WINDOW's previous buffers. + (set-window-prev-buffers + window (assq-delete-all buffer (window-prev-buffers window)))) + + ;; Don't record insignificant buffers. + (unless (eq (aref (buffer-name buffer) 0) ?\s) + ;; Add an entry for buffer to WINDOW's previous buffers. + (with-current-buffer buffer + (let ((start (window-start window)) + (point (window-point window))) + (setq entry + (cons buffer + (if entry + ;; We have an entry, update marker positions. + (list (set-marker (nth 1 entry) start) + (set-marker (nth 2 entry) point)) + ;; Make new markers. + (list (copy-marker start) + (copy-marker point))))) + + (set-window-prev-buffers + window (cons entry (window-prev-buffers window)))))))) + +(defun unrecord-window-buffer (&optional window buffer) + "Unrecord BUFFER in WINDOW. +WINDOW must be a live window and defaults to the selected one. +BUFFER must be a live buffer and defaults to the buffer of +WINDOW." + (let* ((window (normalize-live-window window)) + (buffer (or buffer (window-buffer window)))) + (set-window-prev-buffers + window (assq-delete-all buffer (window-prev-buffers window))) + (set-window-next-buffers + window (delq buffer (window-next-buffers window))))) + +(defun set-window-buffer-start-and-point (window buffer &optional start point) + "Set WINDOW's buffer to BUFFER. +Optional argument START non-nil means set WINDOW's start position +to START. Optional argument POINT non-nil means set WINDOW's +point to POINT. If WINDOW is selected this also sets BUFFER's +`point' to POINT. If WINDOW is selected and the buffer it showed +before was current this also makes BUFFER the current buffer." + (let ((selected (eq window (selected-window))) + (current (eq (window-buffer window) (current-buffer)))) + (set-window-buffer window buffer) + (when (and selected current) + (set-buffer buffer)) + (when start + (set-window-start window start)) + (when point + (if selected + (with-current-buffer buffer + (goto-char point)) + (set-window-point window point))))) + +(defun switch-to-prev-buffer (&optional window bury-or-kill) + "In WINDOW switch to previous buffer. +WINDOW must be a live window and defaults to the selected one. + +Optional argument BURY-OR-KILL non-nil means the buffer currently +shown in WINDOW is about to be buried or killed and consequently +shall not be switched to in future invocations of this command." + (interactive) + (let* ((window (normalize-live-window window)) + (old-buffer (window-buffer window)) + ;; Save this since it's destroyed by `set-window-buffer'. + (next-buffers (window-next-buffers window)) + entry new-buffer killed-buffers deletable visible) + (cond + ;; When BURY-OR-KILL is non-nil, there's no previous buffer for + ;; this window, and we can delete the window (or the frame) do + ;; that. + ((and bury-or-kill + (or (not (window-prev-buffers window)) + (and (eq (caar (window-prev-buffers window)) old-buffer) + (not (cdr (car (window-prev-buffers window)))))) + (setq deletable (window-deletable-p window))) + (if (eq deletable 'frame) + (delete-frame (window-frame window)) + (delete-window window))) + ((window-dedicated-p window) + (error "Window %s is dedicated to buffer %s" window old-buffer))) + + (unless deletable + (catch 'found + ;; Scan WINDOW's previous buffers first, skipping entries of next + ;; buffers. + (dolist (entry (window-prev-buffers window)) + (when (and (setq new-buffer (car entry)) + (or (buffer-live-p new-buffer) + (not (setq killed-buffers + (cons new-buffer killed-buffers)))) + (not (eq new-buffer old-buffer)) + (or bury-or-kill + (not (memq new-buffer next-buffers)))) + (set-window-buffer-start-and-point + window new-buffer (nth 1 entry) (nth 2 entry)) + (throw 'found t))) + ;; Scan reverted buffer list of WINDOW's frame next, skipping + ;; entries of next buffers. Note that when we bury or kill a + ;; buffer we don't reverse the global buffer list to avoid showing + ;; a buried buffer instead. Otherwise, we must reverse the global + ;; buffer list in order to make sure that switching to the + ;; previous/next buffer traverse it in opposite directions. + (dolist (buffer (if bury-or-kill + (buffer-list (window-frame window)) + (nreverse (buffer-list (window-frame window))))) + (when (and (buffer-live-p buffer) + (not (eq buffer old-buffer)) + (not (eq (aref (buffer-name buffer) 0) ?\s)) + (or bury-or-kill (not (memq buffer next-buffers)))) + (if (get-buffer-window buffer) + ;; Try to avoid showing a buffer visible in some other window. + (setq visible buffer) + (setq new-buffer buffer) + (set-window-buffer-start-and-point window new-buffer) + (throw 'found t)))) + (unless bury-or-kill + ;; Scan reverted next buffers last (must not use nreverse + ;; here!). + (dolist (buffer (reverse next-buffers)) + ;; Actually, buffer _must_ be live here since otherwise it + ;; would have been caught in the scan of previous buffers. + (when (and (or (buffer-live-p buffer) + (not (setq killed-buffers + (cons buffer killed-buffers)))) + (not (eq buffer old-buffer)) + (setq entry (assq buffer (window-prev-buffers window)))) + (setq new-buffer buffer) + (set-window-buffer-start-and-point + window new-buffer (nth 1 entry) (nth 2 entry)) + (throw 'found t)))) + + ;; Show a buffer visible in another window. + (when visible + (setq new-buffer visible) + (set-window-buffer-start-and-point window new-buffer))) + + (if bury-or-kill + ;; Remove `old-buffer' from WINDOW's previous and (restored list + ;; of) next buffers. + (progn + (set-window-prev-buffers + window (assq-delete-all old-buffer (window-prev-buffers window))) + (set-window-next-buffers window (delq old-buffer next-buffers))) + ;; Move `old-buffer' to head of WINDOW's restored list of next + ;; buffers. + (set-window-next-buffers + window (cons old-buffer (delq old-buffer next-buffers))))) + + ;; Remove killed buffers from WINDOW's previous and next buffers. + (when killed-buffers + (dolist (buffer killed-buffers) + (set-window-prev-buffers + window (assq-delete-all buffer (window-prev-buffers window))) + (set-window-next-buffers + window (delq buffer (window-next-buffers window))))) + + ;; Return new-buffer. + new-buffer)) + +(defun switch-to-next-buffer (&optional window) + "In WINDOW switch to next buffer. +WINDOW must be a live window and defaults to the selected one." + (interactive) + (let* ((window (normalize-live-window window)) + (old-buffer (window-buffer window)) + (next-buffers (window-next-buffers window)) + new-buffer entry killed-buffers visible) + (when (window-dedicated-p window) + (error "Window %s is dedicated to buffer %s" window old-buffer)) + + (catch 'found + ;; Scan WINDOW's next buffers first. + (dolist (buffer next-buffers) + (when (and (or (buffer-live-p buffer) + (not (setq killed-buffers + (cons buffer killed-buffers)))) + (not (eq buffer old-buffer)) + (setq entry (assq buffer (window-prev-buffers window)))) + (setq new-buffer buffer) + (set-window-buffer-start-and-point + window new-buffer (nth 1 entry) (nth 2 entry)) + (throw 'found t))) + ;; Scan the buffer list of WINDOW's frame next, skipping previous + ;; buffers entries. + (dolist (buffer (buffer-list (window-frame window))) + (when (and (buffer-live-p buffer) (not (eq buffer old-buffer)) + (not (eq (aref (buffer-name buffer) 0) ?\s)) + (not (assq buffer (window-prev-buffers window)))) + (if (get-buffer-window buffer) + ;; Try to avoid showing a buffer visible in some other window. + (setq visible buffer) + (setq new-buffer buffer) + (set-window-buffer-start-and-point window new-buffer) + (throw 'found t)))) + ;; Scan WINDOW's reverted previous buffers last (must not use + ;; nreverse here!) + (dolist (entry (reverse (window-prev-buffers window))) + (when (and (setq new-buffer (car entry)) + (or (buffer-live-p new-buffer) + (not (setq killed-buffers + (cons new-buffer killed-buffers)))) + (not (eq new-buffer old-buffer))) + (set-window-buffer-start-and-point + window new-buffer (nth 1 entry) (nth 2 entry)) + (throw 'found t))) + + ;; Show a buffer visible in another window. + (when visible + (setq new-buffer visible) + (set-window-buffer-start-and-point window new-buffer))) + + ;; Remove `new-buffer' from and restore WINDOW's next buffers. + (set-window-next-buffers window (delq new-buffer next-buffers)) + + ;; Remove killed buffers from WINDOW's previous and next buffers. + (when killed-buffers + (dolist (buffer killed-buffers) + (set-window-prev-buffers + window (assq-delete-all buffer (window-prev-buffers window))) + (set-window-next-buffers + window (delq buffer (window-next-buffers window))))) + + ;; Return new-buffer. + new-buffer)) + +(defun get-next-valid-buffer (list &optional buffer visible-ok frame) + "Search LIST for a valid buffer to display in FRAME. +Return nil when all buffers in LIST are undesirable for display, +otherwise return the first suitable buffer in LIST. + +Buffers not visible in windows are preferred to visible buffers, +unless VISIBLE-OK is non-nil. +If the optional argument FRAME is nil, it defaults to the selected frame. +If BUFFER is non-nil, ignore occurrences of that buffer in LIST." + ;; This logic is more or less copied from other-buffer. + (setq frame (or frame (selected-frame))) + (let ((pred (frame-parameter frame 'buffer-predicate)) + found buf) + (while (and (not found) list) + (setq buf (car list)) + (if (and (not (eq buffer buf)) + (buffer-live-p buf) + (or (null pred) (funcall pred buf)) + (not (eq (aref (buffer-name buf) 0) ?\s)) + (or visible-ok (null (get-buffer-window buf 'visible)))) + (setq found buf) + (setq list (cdr list)))) + (car list))) + +(defun last-buffer (&optional buffer visible-ok frame) + "Return the last buffer in FRAME's buffer list. +If BUFFER is the last buffer, return the preceding buffer +instead. Buffers not visible in windows are preferred to visible +buffers, unless optional argument VISIBLE-OK is non-nil. +Optional third argument FRAME nil or omitted means use the +selected frame's buffer list. If no such buffer exists, return +the buffer `*scratch*', creating it if necessary." + (setq frame (or frame (selected-frame))) + (or (get-next-valid-buffer (nreverse (buffer-list frame)) + buffer visible-ok frame) + (get-buffer "*scratch*") + (let ((scratch (get-buffer-create "*scratch*"))) + (set-buffer-major-mode scratch) + scratch))) + +(defun bury-buffer (&optional buffer-or-name) + "Put BUFFER-OR-NAME at the end of the list of all buffers. +There it is the least likely candidate for `other-buffer' to +return; thus, the least likely buffer for \\[switch-to-buffer] to +select by default. + +You can specify a buffer name as BUFFER-OR-NAME, or an actual +buffer object. If BUFFER-OR-NAME is nil or omitted, bury the +current buffer. Also, if BUFFER-OR-NAME is nil or omitted, +remove the current buffer from the selected window if it is +displayed there." + (interactive) + (let* ((buffer (normalize-live-buffer buffer-or-name))) + ;; If `buffer-or-name' is not on the selected frame we unrecord it + ;; although it's not "here" (call it a feature). + (unrecord-buffer buffer) + ;; Handle case where `buffer-or-name' is nil and the current buffer + ;; is shown in the selected window. + (cond + ((or buffer-or-name (not (eq buffer (window-buffer))))) + ((not (window-dedicated-p)) + (switch-to-prev-buffer nil 'bury)) + ((frame-root-window-p (selected-window)) + (iconify-frame (window-frame (selected-window)))) + ((window-deletable-p) + (delete-window))) + ;; Always return nil. + nil)) + +(defun unbury-buffer () + "Switch to the last buffer in the buffer list." + (interactive) + (switch-to-buffer (last-buffer))) + +(defun next-buffer () + "In selected window switch to next buffer." + (interactive) + (switch-to-next-buffer)) + +(defun previous-buffer () + "In selected window switch to previous buffer." + (interactive) + (switch-to-prev-buffer)) + +(defun delete-windows-on (&optional buffer-or-name frame) + "Delete all windows showing BUFFER-OR-NAME. +BUFFER-OR-NAME may be a buffer or the name of an existing buffer +and defaults to the current buffer. + +The following non-nil values of the optional argument FRAME +have special meanings: + +- t means consider all windows on the selected frame only. + +- `visible' means consider all windows on all visible frames on + the current terminal. + +- 0 (the number zero) means consider all windows on all visible + and iconified frames on the current terminal. + +- A frame means consider all windows on that frame only. + +Any other value of FRAME means consider all windows on all +frames. + +When a window showing BUFFER-OR-NAME is dedicated and the only +window of its frame, that frame is deleted when there are other +frames left." + (interactive "BDelete windows on (buffer):\nP") + (let ((buffer (normalize-live-buffer buffer-or-name)) + ;; Handle the "inverted" meaning of the FRAME argument wrt other + ;; `window-list-1' based function. + (all-frames (cond ((not frame) t) ((eq frame t) nil) (t frame)))) + (dolist (window (window-list-1 nil nil all-frames)) + (if (eq (window-buffer window) buffer) + (let ((deletable (window-deletable-p window))) + (cond + ((eq deletable 'frame) + ;; Delete frame. + (delete-frame (window-frame window))) + (deletable + ;; Delete window only. + (delete-window window)) + (t + ;; In window switch to previous buffer. + (set-window-dedicated-p window nil) + (switch-to-prev-buffer window 'bury)))) + ;; If a window doesn't show BUFFER, unrecord BUFFER in it. + (unrecord-window-buffer window buffer))))) + +(defun replace-buffer-in-windows (&optional buffer-or-name) + "Replace BUFFER-OR-NAME with some other buffer in all windows showing it. +BUFFER-OR-NAME may be a buffer or the name of an existing buffer +and defaults to the current buffer. + +When a window showing BUFFER-OR-NAME is either dedicated, or the +window has no previous buffer, that window is deleted. If that +window is the only window on its frame, the frame is deleted too +when there are other frames left. If there are no other frames +left, some other buffer is displayed in that window. + +This function removes the buffer denoted by BUFFER-OR-NAME from +all window-local buffer lists." + (let ((buffer (normalize-live-buffer buffer-or-name))) + (dolist (window (window-list-1 nil nil t)) + (if (eq (window-buffer window) buffer) + (let ((deletable (window-deletable-p window))) + (cond + ((eq deletable 'frame) + ;; Delete frame. + (delete-frame (window-frame window))) + ((and (window-dedicated-p window) deletable) + ;; Delete window. + (delete-window window)) + (t + ;; Switch to another buffer in window. + (set-window-dedicated-p window nil) + (switch-to-prev-buffer window 'kill)))) + ;; Unrecord BUFFER in WINDOW. + (unrecord-window-buffer window buffer))))) + +(defun quit-restore-window (&optional window kill) + "Quit WINDOW in some way. +WINDOW must be a live window and defaults to the selected window. +Return nil. + +According to information stored in WINDOW's `quit-restore' window +parameter either \(1) delete WINDOW and its frame, \(2) delete +WINDOW, \(3) restore the buffer previously displayed in WINDOW, +or \(4) make WINDOW display some other buffer than the present +one. If non-nil, reset `quit-restore' parameter to nil. + +Optional argument KILL non-nil means in addition kill WINDOW's +buffer. If KILL is nil, put WINDOW's buffer at the end of the +buffer list. Interactively, KILL is the prefix argument." + (interactive "i\nP") + (setq window (normalize-live-window window)) + (let ((buffer (window-buffer window)) + (quit-restore (window-parameter window 'quit-restore)) + deletable resize) + (cond + ((and (or (and (memq (car-safe quit-restore) '(new-window new-frame)) + ;; Check that WINDOW's buffer is still the same. + (eq (window-buffer window) (nth 1 quit-restore))) + (window-dedicated-p window)) + (setq deletable (window-deletable-p window))) + ;; WINDOW can be deleted. + (unrecord-buffer buffer) + (if (eq deletable 'frame) + ;; WINDOW's frame can be deleted. + (delete-frame (window-frame window)) + ;; Just delete WINDOW. + (delete-window window)) + ;; If the previously selected window is still alive, select it. + (when (window-live-p (nth 2 quit-restore)) + (select-window (nth 2 quit-restore)))) + ((and (buffer-live-p (nth 0 quit-restore)) + ;; The buffer currently shown in WINDOW must still be the + ;; buffer shown when its `quit-restore' parameter was created + ;; in the first place. + (eq (window-buffer window) (nth 3 quit-restore))) + (setq resize (with-current-buffer buffer temp-buffer-resize-mode)) + ;; Unrecord buffer. + (unrecord-buffer buffer) + (unrecord-window-buffer window buffer) + ;; Display buffer stored in the quit-restore parameter. + (set-window-dedicated-p window nil) + (set-window-buffer window (nth 0 quit-restore)) + (set-window-start window (nth 1 quit-restore)) + (set-window-point window (nth 2 quit-restore)) + (when (and resize (/= (nth 4 quit-restore) (window-total-size window))) + (resize-window + window (- (nth 4 quit-restore) (window-total-size window)))) + ;; Reset the quit-restore parameter. + (set-window-parameter window 'quit-restore nil) + (when (window-live-p (nth 5 quit-restore)) + (select-window (nth 5 quit-restore)))) + (t + ;; Otherwise, show another buffer in WINDOW and reset the + ;; quit-restore parameter. + (set-window-parameter window 'quit-restore nil) + (unrecord-buffer buffer) + (switch-to-prev-buffer window 'bury-or-kill))) + + ;; Kill WINDOW's old-buffer if requested + (when kill (kill-buffer buffer)) + nil)) ;;; Splitting windows. (defsubst window-split-min-size (&optional horizontal) @@ -2569,22 +3149,14 @@ window." (<= (window-start new-window) old-point) (set-window-point new-window old-point) (select-window new-window))) - (split-window-save-restore-data new-window old-window))) + ;; Always copy quit-restore parameter in interactive use. + (let ((quit-restore (window-parameter old-window 'quit-restore))) + (when quit-restore + (set-window-parameter new-window 'quit-restore quit-restore))) + new-window)) (defalias 'split-window-vertically 'split-window-above-each-other) -;; This is to avoid compiler warnings. -(defvar view-return-to-alist) - -(defun split-window-save-restore-data (new-window old-window) - (with-current-buffer (window-buffer) - (when view-mode - (let ((old-info (assq old-window view-return-to-alist))) - (when old-info - (push (cons new-window (cons (car (cdr old-info)) t)) - view-return-to-alist)))) - new-window)) - (defun split-window-side-by-side (&optional size) "Split selected window into two windows side by side. The selected window becomes the left one and gets SIZE columns. @@ -2603,7 +3175,12 @@ The selected window remains selected. Return the new window." (when (and size (< size 0) (< (- size) window-min-width)) ;; `split-window' would not signal an error here. (error "Size of new window too small")) - (split-window-save-restore-data (split-window nil size t) old-window))) + (setq new-window (split-window nil size t)) + ;; Always copy quit-restore parameter in interactive use. + (let ((quit-restore (window-parameter old-window 'quit-restore))) + (when quit-restore + (set-window-parameter new-window 'quit-restore quit-restore))) + new-window)) (defalias 'split-window-horizontally 'split-window-side-by-side) @@ -3545,6 +4122,29 @@ consider all visible or iconified frames on the current terminal." (window--even-window-heights window-to-use) (window--display-buffer-2 buffer window-to-use))))) +(defun display-buffer-other-frame (buffer) + "Display buffer BUFFER in another frame. +This uses the function `display-buffer' as a subroutine; see +its documentation for additional customization information." + (interactive "BDisplay buffer in other frame: ") + (let ((pop-up-frames t) + same-window-buffer-names same-window-regexps + ;;(old-window (selected-window)) + new-window) + (setq new-window (display-buffer buffer t)) + ;; This may have been here in order to prevent the new frame from hiding + ;; the old frame. But it does more harm than good. + ;; Maybe we should call `raise-window' on the old-frame instead? --Stef + ;;(lower-frame (window-frame new-window)) + + ;; This may have been here in order to make sure the old-frame gets the + ;; focus. But not only can it cause an annoying flicker, with some + ;; window-managers it just makes the window invisible, with no easy + ;; way to recover it. --Stef + ;;(make-frame-invisible (window-frame old-window)) + ;;(make-frame-visible (window-frame old-window)) + )) + (defun pop-to-buffer (buffer-or-name &optional other-window norecord) "Select buffer BUFFER-OR-NAME in some window, preferably a different one. BUFFER-OR-NAME may be a buffer, a string \(a buffer name), or @@ -3587,6 +4187,130 @@ at the front of the list of recently selected ones." ;; input focus and is risen. (select-frame-set-input-focus new-frame)) buffer)) + +(defun read-buffer-to-switch (prompt) + "Read the name of a buffer to switch to, prompting with PROMPT. +Return the neame of the buffer as a string. + +This function is intended for the `switch-to-buffer' family of +commands since these need to omit the name of the current buffer +from the list of completions and default values." + (let ((rbts-completion-table (internal-complete-buffer-except))) + (minibuffer-with-setup-hook + (lambda () + (setq minibuffer-completion-table rbts-completion-table) + ;; Since rbts-completion-table is built dynamically, we + ;; can't just add it to the default value of + ;; icomplete-with-completion-tables, so we add it + ;; here manually. + (if (and (boundp 'icomplete-with-completion-tables) + (listp icomplete-with-completion-tables)) + (set (make-local-variable 'icomplete-with-completion-tables) + (cons rbts-completion-table + icomplete-with-completion-tables)))) + (read-buffer prompt (other-buffer (current-buffer)) + (confirm-nonexistent-file-or-buffer))))) + +(defun normalize-buffer-to-switch-to (buffer-or-name) + "Normalize BUFFER-OR-NAME argument of buffer switching functions. +If BUFFER-OR-NAME is nil, return the buffer returned by +`other-buffer'. Else, if a buffer specified by BUFFER-OR-NAME +exists, return that buffer. If no such buffer exists, create a +buffer with the name BUFFER-OR-NAME and return that buffer." + (if buffer-or-name + (or (get-buffer buffer-or-name) + (let ((buffer (get-buffer-create buffer-or-name))) + (set-buffer-major-mode buffer) + buffer)) + (other-buffer))) + +(defun switch-to-buffer (buffer-or-name &optional norecord) + "Switch to buffer BUFFER-OR-NAME in the selected window. +If called interactively, prompt for the buffer name using the +minibuffer. The variable `confirm-nonexistent-file-or-buffer' +determines whether to request confirmation before creating a new +buffer. + +BUFFER-OR-NAME may be a buffer, a string \(a buffer name), or +nil. If BUFFER-OR-NAME is a string that does not identify an +existing buffer, create a buffer with that name. If +BUFFER-OR-NAME is nil, switch to the buffer returned by +`other-buffer'. + +Optional argument NORECORD non-nil means do not put the buffer +specified by BUFFER-OR-NAME at the front of the buffer list and +do not make the window displaying it the most recently selected +one. Return the buffer switched to. + +This function is intended for interactive use only. Lisp +functions should call `pop-to-buffer-same-window' instead." + (interactive + (list (read-buffer-to-switch "Switch to buffer: "))) + (let ((buffer (normalize-buffer-to-switch-to buffer-or-name))) + (if (and (or (window-minibuffer-p) (eq (window-dedicated-p) t)) + (not (eq buffer (window-buffer)))) + ;; Cannot switch to another buffer in a minibuffer or strongly + ;; dedicated window that does not show the buffer already. Call + ;; `pop-to-buffer' instead. + (pop-to-buffer buffer nil norecord) + (unless (eq buffer (window-buffer)) + ;; I'm not sure why we should NOT call `set-window-buffer' here, + ;; but let's keep things as they are (otherwise we could always + ;; call `pop-to-buffer-same-window' here). + (set-window-buffer nil buffer)) + (unless norecord + (select-window (selected-window))) + (set-buffer buffer)))) + +(defun switch-to-buffer-other-window (buffer-or-name &optional norecord) + "Select the buffer specified by BUFFER-OR-NAME in another window. +BUFFER-OR-NAME may be a buffer, a string \(a buffer name), or +nil. Return the buffer switched to. + +If called interactively, prompt for the buffer name using the +minibuffer. The variable `confirm-nonexistent-file-or-buffer' +determines whether to request confirmation before creating a new +buffer. + +If BUFFER-OR-NAME is a string and does not identify an existing +buffer, create a new buffer with that name. If BUFFER-OR-NAME is +nil, switch to the buffer returned by `other-buffer'. + +Optional second argument NORECORD non-nil means do not put this +buffer at the front of the list of recently selected ones. + +This uses the function `display-buffer' as a subroutine; see its +documentation for additional customization information." + (interactive + (list (read-buffer-to-switch "Switch to buffer in other window: "))) + (let ((pop-up-windows t) + same-window-buffer-names same-window-regexps) + (pop-to-buffer buffer-or-name t norecord))) + +(defun switch-to-buffer-other-frame (buffer-or-name &optional norecord) + "Switch to buffer BUFFER-OR-NAME in another frame. +BUFFER-OR-NAME may be a buffer, a string \(a buffer name), or +nil. Return the buffer switched to. + +If called interactively, prompt for the buffer name using the +minibuffer. The variable `confirm-nonexistent-file-or-buffer' +determines whether to request confirmation before creating a new +buffer. + +If BUFFER-OR-NAME is a string and does not identify an existing +buffer, create a new buffer with that name. If BUFFER-OR-NAME is +nil, switch to the buffer returned by `other-buffer'. + +Optional second arg NORECORD non-nil means do not put this +buffer at the front of the list of recently selected ones. + +This uses the function `display-buffer' as a subroutine; see its +documentation for additional customization information." + (interactive + (list (read-buffer-to-switch "Switch to buffer in other frame: "))) + (let ((pop-up-frames t) + same-window-buffer-names same-window-regexps) + (pop-to-buffer buffer-or-name t norecord))) (defun set-window-text-height (window height) "Set the height in lines of the text display area of WINDOW to HEIGHT. @@ -4258,21 +4982,6 @@ active. This function is run by `mouse-autoselect-window-timer'." (run-hooks 'mouse-leave-buffer-hook)) (select-window window)))) -(defun delete-other-windows-vertically (&optional window) - "Delete the windows in the same column with WINDOW, but not WINDOW itself. -This may be a useful alternative binding for \\[delete-other-windows] - if you often split windows horizontally." - (interactive) - (let* ((window (or window (selected-window))) - (edges (window-edges window)) - (w window) delenda) - (while (not (eq (setq w (next-window w 1)) window)) - (let ((e (window-edges w))) - (when (and (= (car e) (car edges)) - (= (caddr e) (caddr edges))) - (push w delenda)))) - (mapc 'delete-window delenda))) - (defun truncated-partial-width-window-p (&optional window) "Return non-nil if lines in WINDOW are specifically truncated due to its width. WINDOW defaults to the selected window. @@ -4293,6 +5002,7 @@ Otherwise, consult the value of `truncate-partial-width-windows' (define-key ctl-x-map "1" 'delete-other-windows) (define-key ctl-x-map "2" 'split-window-above-each-other) (define-key ctl-x-map "3" 'split-window-side-by-side) +(define-key ctl-x-map "o" 'other-window) (define-key ctl-x-map "^" 'enlarge-window) (define-key ctl-x-map "}" 'enlarge-window-horizontally) (define-key ctl-x-map "{" 'shrink-window-horizontally) diff --git a/src/ChangeLog b/src/ChangeLog index ed3ea6dfbb3..b53eea56a0d 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,54 @@ +2011-06-11 Martin Rudalics + + * buffer.c: New Lisp objects Qbuffer_list_update_hook and + Qclone_number. Remove external declaration of Qdelete_window. + (Fbuffer_list): Rewrite doc-string. Minor restructuring of + code. + (Fget_buffer_create, Fmake_indirect_buffer, Frename_buffer): Run + Qbuffer_list_update_hook if allowed. + (Fother_buffer): Rewrite doc-string. Major rewrite for new + buffer list implementation. + (other_buffer_safely): New function. + (Fkill_buffer): Replace call to replace_buffer_in_all_windows by + calls to replace_buffer_in_windows and + replace_buffer_in_windows_safely. Run Qbuffer_list_update_hook + if allowed. + (record_buffer): Inhibit quitting and rewrite using quittable + functions. Run Qbuffer_list_update_hook if allowed. + (Frecord_buffer, Funrecord_buffer): New functions. + (switch_to_buffer_1, Fswitch_to_buffer): Remove. Move + switch-to-buffer to window.el. + (bury-buffer): Move to window.el. + (Vbuffer_list_update_hook): New variable. + + * lisp.h (other_buffer_safely): Add prototype in buffer.c + section. + + * window.h (resize_frame_windows): Move up in code. + (Fwindow_frame): Remove EXFUN. + (replace_buffer_in_all_windows): Remove prototype. + (replace_buffer_in_windows_safely): Add prototype. + + * window.c: Declare Qdelete_window static again. Move down + declaration of select_count. + (Fnext_window, Fprevious_window): Rewrite doc-strings. + (Fother_window): Move to window.el. + (window_loop): Remove DELETE_BUFFER_WINDOWS and UNSHOW_BUFFER + cases. Add REPLACE_BUFFER_IN_WINDOWS_SAFELY case. + (Fdelete_windows_on, Freplace_buffer_in_windows): Move to + window.el. + (replace_buffer_in_windows): Implement by calling + Qreplace_buffer_in_windows. + (replace_buffer_in_all_windows): Remove with some functionality + moved into replace_buffer_in_windows_safely. + (replace_buffer_in_windows_safely): New function. + (select_window_norecord, select_frame_norecord): Move in front + of run_window_configuration_change_hook. Remove now obsolete + declarations. + (Fset_window_buffer): Rewrite doc-string. Call + Qrecord_window_buffer. + (keys_of_window): Move binding for other-window to window.el. + 2011-06-11 Chong Yidong * dispextern.h (struct image): Replace data member, whose int_val diff --git a/src/buffer.c b/src/buffer.c index 0862de9baf7..bd059cdbe14 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -131,13 +131,15 @@ static Lisp_Object Qprotected_field; static Lisp_Object QSFundamental; /* A string "Fundamental" */ static Lisp_Object Qkill_buffer_hook; +static Lisp_Object Qbuffer_list_update_hook; static Lisp_Object Qget_file_buffer; static Lisp_Object Qoverlayp; Lisp_Object Qpriority, Qbefore_string, Qafter_string; -static Lisp_Object Qevaporate; + +static Lisp_Object Qclone_number, Qevaporate; Lisp_Object Qmodification_hooks; Lisp_Object Qinsert_in_front_hooks; @@ -171,9 +173,9 @@ Value is nil if OBJECT is not a buffer or if it has been killed. */) DEFUN ("buffer-list", Fbuffer_list, Sbuffer_list, 0, 1, 0, doc: /* Return a list of all existing live buffers. -If the optional arg FRAME is a frame, we return the buffer list -in the proper order for that frame: the buffers in FRAME's `buffer-list' -frame parameter come first, followed by the rest of the buffers. */) +If the optional arg FRAME is a frame, we return the buffer list in the +proper order for that frame: the buffers show in FRAME come first, +followed by the rest of the buffers. */) (Lisp_Object frame) { Lisp_Object general; @@ -185,9 +187,9 @@ frame parameter come first, followed by the rest of the buffers. */) Lisp_Object args[3]; CHECK_FRAME (frame); - framelist = Fcopy_sequence (XFRAME (frame)->buffer_list); - prevlist = Fnreverse (Fcopy_sequence (XFRAME (frame)->buried_buffer_list)); + prevlist = Fnreverse (Fcopy_sequence + (XFRAME (frame)->buried_buffer_list)); /* Remove from GENERAL any buffer that duplicates one in FRAMELIST or PREVLIST. */ @@ -209,8 +211,8 @@ frame parameter come first, followed by the rest of the buffers. */) args[2] = prevlist; return Fnconc (3, args); } - - return general; + else + return general; } /* Like Fassoc, but use Fstring_equal to compare @@ -384,6 +386,9 @@ even if it is dead. The return value is never nil. */) /* Put this in the alist of all live buffers. */ XSETBUFFER (buffer, b); Vbuffer_alist = nconc2 (Vbuffer_alist, Fcons (Fcons (name, buffer), Qnil)); + /* And run buffer-list-update-hook. */ + if (!NILP (Vrun_hooks)) + call1 (Vrun_hooks, Qbuffer_list_update_hook); /* An error in calling the function here (should someone redefine it) can lead to infinite regress until you run out of stack. rms @@ -659,6 +664,10 @@ CLONE nil means the indirect buffer's state is reset to default values. */) set_buffer_internal_1 (old_b); } + /* Run buffer-list-update-hook. */ + if (!NILP (Vrun_hooks)) + call1 (Vrun_hooks, Qbuffer_list_update_hook); + return buf; } @@ -1262,81 +1271,119 @@ This does not change the name of the visited file (if any). */) if (NILP (BVAR (current_buffer, filename)) && !NILP (BVAR (current_buffer, auto_save_file_name))) call0 (intern ("rename-auto-save-file")); + + /* Run buffer-list-update-hook. */ + if (!NILP (Vrun_hooks)) + call1 (Vrun_hooks, Qbuffer_list_update_hook); + /* Refetch since that last call may have done GC. */ return BVAR (current_buffer, name); } DEFUN ("other-buffer", Fother_buffer, Sother_buffer, 0, 3, 0, doc: /* Return most recently selected buffer other than BUFFER. -Buffers not visible in windows are preferred to visible buffers, -unless optional second argument VISIBLE-OK is non-nil. -If the optional third argument FRAME is non-nil, use that frame's -buffer list instead of the selected frame's buffer list. -If no other buffer exists, the buffer `*scratch*' is returned. -If BUFFER is omitted or nil, some interesting buffer is returned. */) +Buffers not visible in windows are preferred to visible buffers, unless +optional second argument VISIBLE-OK is non-nil. Ignore the argument +BUFFER unless it denotes a live buffer. If the optional third argument +FRAME is non-nil, use that frame's buffer list instead of the selected +frame's buffer list. + +The buffer is found by scanning the selected or specified frame's buffer +list first, followed by the list of all buffers. If no other buffer +exists, return the buffer `*scratch*' (creating it if necessary). */) (register Lisp_Object buffer, Lisp_Object visible_ok, Lisp_Object frame) { - register Lisp_Object tail, buf, notsogood, tem, pred, add_ons; - notsogood = Qnil; + Lisp_Object Fset_buffer_major_mode (Lisp_Object buffer); + Lisp_Object tail, buf, pred; + Lisp_Object notsogood = Qnil; if (NILP (frame)) frame = selected_frame; CHECK_FRAME (frame); - tail = Vbuffer_alist; pred = frame_buffer_predicate (frame); - - /* Consider buffers that have been seen in the selected frame - before other buffers. */ - - tem = frame_buffer_list (frame); - add_ons = Qnil; - while (CONSP (tem)) + /* Consider buffers that have been seen in the frame first. */ + tail = XFRAME (frame)->buffer_list; + for (; CONSP (tail); tail = XCDR (tail)) { - if (BUFFERP (XCAR (tem))) - add_ons = Fcons (Fcons (Qnil, XCAR (tem)), add_ons); - tem = XCDR (tem); + buf = XCAR (tail); + if (BUFFERP (buf) && !EQ (buf, buffer) + && !NILP (BVAR (XBUFFER (buf), name)) + && (SREF (BVAR (XBUFFER (buf), name), 0) != ' ') + /* If the frame has a buffer_predicate, disregard buffers that + don't fit the predicate. */ + && (NILP (pred) || !NILP (call1 (pred, buf)))) + { + if (!NILP (visible_ok) + || NILP (Fget_buffer_window (buf, Qvisible))) + return buf; + else if (NILP (notsogood)) + notsogood = buf; + } } - tail = nconc2 (Fnreverse (add_ons), tail); + /* Consider alist of all buffers next. */ + tail = Vbuffer_alist; for (; CONSP (tail); tail = XCDR (tail)) { buf = Fcdr (XCAR (tail)); - if (EQ (buf, buffer)) - continue; - if (NILP (buf)) - continue; - if (NILP (BVAR (XBUFFER (buf), name))) - continue; - if (SREF (BVAR (XBUFFER (buf), name), 0) == ' ') - continue; - /* If the selected frame has a buffer_predicate, - disregard buffers that don't fit the predicate. */ - if (!NILP (pred)) + if (BUFFERP (buf) && !EQ (buf, buffer) + && !NILP (BVAR (XBUFFER (buf), name)) + && (SREF (BVAR (XBUFFER (buf), name), 0) != ' ') + /* If the frame has a buffer_predicate, disregard buffers that + don't fit the predicate. */ + && (NILP (pred) || !NILP (call1 (pred, buf)))) { - tem = call1 (pred, buf); - if (NILP (tem)) - continue; + if (!NILP (visible_ok) + || NILP (Fget_buffer_window (buf, Qvisible))) + return buf; + else if (NILP (notsogood)) + notsogood = buf; } - - if (NILP (visible_ok)) - tem = Fget_buffer_window (buf, Qvisible); - else - tem = Qnil; - if (NILP (tem)) - return buf; - if (NILP (notsogood)) - notsogood = buf; } + if (!NILP (notsogood)) return notsogood; + else + { + buf = Fget_buffer (build_string ("*scratch*")); + if (NILP (buf)) + { + buf = Fget_buffer_create (build_string ("*scratch*")); + Fset_buffer_major_mode (buf); + } + return buf; + } +} + +/* The following function is a safe variant of Fother_buffer: It doesn't + pay attention to any frame-local buffer lists, doesn't care about + visibility of buffers, and doesn't evaluate any frame predicates. */ + +Lisp_Object +other_buffer_safely (Lisp_Object buffer) +{ + Lisp_Object Fset_buffer_major_mode (Lisp_Object buffer); + Lisp_Object tail, buf; + + tail = Vbuffer_alist; + for (; CONSP (tail); tail = XCDR (tail)) + { + buf = Fcdr (XCAR (tail)); + if (BUFFERP (buf) && !EQ (buf, buffer) + && !NILP (BVAR (XBUFFER (buf), name)) + && (SREF (BVAR (XBUFFER (buf), name), 0) != ' ')) + return buf; + } + buf = Fget_buffer (build_string ("*scratch*")); if (NILP (buf)) { buf = Fget_buffer_create (build_string ("*scratch*")); Fset_buffer_major_mode (buf); } + return buf; } @@ -1509,13 +1556,20 @@ with SIGHUP. */) if (NILP (BVAR (b, name))) return Qnil; + /* These may run Lisp code and into infinite loops (if someone + insisted on circular lists) so allow quitting here. */ + replace_buffer_in_windows (buffer); + frames_discard_buffer (buffer); + clear_charpos_cache (b); tem = Vinhibit_quit; Vinhibit_quit = Qt; - replace_buffer_in_all_windows (buffer); + /* Remove the buffer from the list of all buffers. */ Vbuffer_alist = Fdelq (Frassq (buffer, Vbuffer_alist), Vbuffer_alist); - frames_discard_buffer (buffer); + /* If replace_buffer_in_windows didn't do its job correctly fix that + now. */ + replace_buffer_in_windows_safely (buffer); Vinhibit_quit = tem; /* Delete any auto-save file, if we saved it in this session. @@ -1589,83 +1643,103 @@ with SIGHUP. */) UNBLOCK_INPUT; BVAR (b, undo_list) = Qnil; + /* Run buffer-list-update-hook. */ + if (!NILP (Vrun_hooks)) + call1 (Vrun_hooks, Qbuffer_list_update_hook); + return Qt; } -/* Move the assoc for buffer BUF to the front of buffer-alist. Since - we do this each time BUF is selected visibly, the more recently - selected buffers are always closer to the front of the list. This - means that other_buffer is more likely to choose a relevant buffer. */ +/* Move association for BUFFER to the front of buffer (a)lists. Since + we do this each time BUFFER is selected visibly, the more recently + selected buffers are always closer to the front of those lists. This + means that other_buffer is more likely to choose a relevant buffer. + + Note that this moves BUFFER to the front of the buffer lists of the + selected frame even if BUFFER is not shown there. If BUFFER is not + shown in the selected frame, consider the present behavior a feature. + `select-window' gets this right since it shows BUFFER in the selected + window when calling us. */ void -record_buffer (Lisp_Object buf) +record_buffer (Lisp_Object buffer) { - register Lisp_Object list, prev; - Lisp_Object frame; - frame = selected_frame; + Lisp_Object aelt, link, tem; + register struct frame *f = XFRAME (selected_frame); + register struct window *w = XWINDOW (FRAME_SELECTED_WINDOW (f)); - prev = Qnil; - for (list = Vbuffer_alist; CONSP (list); list = XCDR (list)) - { - if (EQ (XCDR (XCAR (list)), buf)) - break; - prev = list; - } + CHECK_BUFFER (buffer); - /* Effectively do Vbuffer_alist = Fdelq (list, Vbuffer_alist); - we cannot use Fdelq itself here because it allows quitting. */ + /* Update Vbuffer_alist (we know that it has an entry for BUFFER). + Don't allow quitting since this might leave the buffer list in an + inconsistent state. */ + tem = Vinhibit_quit; + Vinhibit_quit = Qt; + aelt = Frassq (buffer, Vbuffer_alist); + link = Fmemq (aelt, Vbuffer_alist); + Vbuffer_alist = Fdelq (aelt, Vbuffer_alist); + XSETCDR (link, Vbuffer_alist); + Vbuffer_alist = link; + Vinhibit_quit = tem; - if (NILP (prev)) - Vbuffer_alist = XCDR (Vbuffer_alist); - else - XSETCDR (prev, XCDR (XCDR (prev))); + /* Update buffer list of selected frame. */ + f->buffer_list = Fcons (buffer, Fdelq (buffer, f->buffer_list)); + f->buried_buffer_list = Fdelq (buffer, f->buried_buffer_list); - XSETCDR (list, Vbuffer_alist); - Vbuffer_alist = list; + /* Run buffer-list-update-hook. */ + if (!NILP (Vrun_hooks)) + call1 (Vrun_hooks, Qbuffer_list_update_hook); +} - /* Effectively do a delq on buried_buffer_list. */ +DEFUN ("record-buffer", Frecord_buffer, Srecord_buffer, 1, 1, 0, + doc: /* Move BUFFER to the front of the buffer list. +Return BUFFER. */) + (Lisp_Object buffer) +{ + CHECK_BUFFER (buffer); - prev = Qnil; - for (list = XFRAME (frame)->buried_buffer_list; CONSP (list); - list = XCDR (list)) - { - if (EQ (XCAR (list), buf)) - { - if (NILP (prev)) - XFRAME (frame)->buried_buffer_list = XCDR (list); - else - XSETCDR (prev, XCDR (XCDR (prev))); - break; - } - prev = list; - } + record_buffer (buffer); - /* Now move this buffer to the front of frame_buffer_list also. */ + return buffer; +} - prev = Qnil; - for (list = frame_buffer_list (frame); CONSP (list); - list = XCDR (list)) - { - if (EQ (XCAR (list), buf)) - break; - prev = list; - } + /* Move BUFFER to the end of the buffer (a)lists. Do nothing if the + buffer is killed. For the selected frame's buffer list this moves + BUFFER to its end even if it was never shown in that frame. If + this happens we have a feature, hence `unrecord-buffer' should be + called only when BUFFER was shown in the selected frame. */ - /* Effectively do delq. */ +DEFUN ("unrecord-buffer", Funrecord_buffer, Sunrecord_buffer, 1, 1, 0, + doc: /* Move BUFFER to the end of the buffer list. +Return BUFFER. */) + (Lisp_Object buffer) +{ + Lisp_Object aelt, link, tem; + register struct frame *f = XFRAME (selected_frame); - if (CONSP (list)) - { - if (NILP (prev)) - set_frame_buffer_list (frame, - XCDR (frame_buffer_list (frame))); - else - XSETCDR (prev, XCDR (XCDR (prev))); + CHECK_BUFFER (buffer); - XSETCDR (list, frame_buffer_list (frame)); - set_frame_buffer_list (frame, list); - } - else - set_frame_buffer_list (frame, Fcons (buf, frame_buffer_list (frame))); + /* Update Vbuffer_alist (we know that it has an entry for BUFFER). + Don't allow quitting since this might leave the buffer list in an + inconsistent state. */ + tem = Vinhibit_quit; + Vinhibit_quit = Qt; + aelt = Frassq (buffer, Vbuffer_alist); + link = Fmemq (aelt, Vbuffer_alist); + Vbuffer_alist = Fdelq (aelt, Vbuffer_alist); + XSETCDR (link, Qnil); + Vbuffer_alist = nconc2 (Vbuffer_alist, link); + Vinhibit_quit = tem; + + /* Update buffer lists of selected frame. */ + f->buffer_list = Fdelq (buffer, f->buffer_list); + f->buried_buffer_list = Fcons (buffer, Fdelq (buffer, f->buried_buffer_list)); + + /* Run buffer-list-update-hook. */ + if (!NILP (Vrun_hooks)) + call1 (Vrun_hooks, Qbuffer_list_update_hook); + + return buffer; } DEFUN ("set-buffer-major-mode", Fset_buffer_major_mode, Sset_buffer_major_mode, 1, 1, 0, @@ -1708,86 +1782,6 @@ the current buffer's major mode. */) return unbind_to (count, Qnil); } -/* Switch to buffer BUFFER in the selected window. - If NORECORD is non-nil, don't call record_buffer. */ - -static Lisp_Object -switch_to_buffer_1 (Lisp_Object buffer_or_name, Lisp_Object norecord) -{ - register Lisp_Object buffer; - - if (NILP (buffer_or_name)) - buffer = Fother_buffer (Fcurrent_buffer (), Qnil, Qnil); - else - { - buffer = Fget_buffer (buffer_or_name); - if (NILP (buffer)) - { - buffer = Fget_buffer_create (buffer_or_name); - Fset_buffer_major_mode (buffer); - } - } - Fset_buffer (buffer); - if (NILP (norecord)) - record_buffer (buffer); - - Fset_window_buffer (EQ (selected_window, minibuf_window) - ? Fnext_window (minibuf_window, Qnil, Qnil) - : selected_window, - buffer, Qnil); - - return buffer; -} - -DEFUN ("switch-to-buffer", Fswitch_to_buffer, Sswitch_to_buffer, 1, 2, - "(list (read-buffer-to-switch \"Switch to buffer: \"))", - doc: /* Make BUFFER-OR-NAME current and display it in selected window. -BUFFER-OR-NAME may be a buffer, a string \(a buffer name), or -nil. Return the buffer switched to. - -If BUFFER-OR-NAME is a string and does not identify an existing -buffer, create a new buffer with that name. Interactively, if -`confirm-nonexistent-file-or-buffer' is non-nil, request -confirmation before creating a new buffer. If BUFFER-OR-NAME is -nil, switch to buffer returned by `other-buffer'. - -Optional second arg NORECORD non-nil means do not put this buffer -at the front of the list of recently selected ones. This -function returns the buffer it switched to as a Lisp object. - -If the selected window is the minibuffer window or dedicated to -its buffer, use `pop-to-buffer' for displaying the buffer. - -WARNING: This is NOT the way to work on another buffer temporarily -within a Lisp program! Use `set-buffer' instead. That avoids -messing with the window-buffer correspondences. */) - (Lisp_Object buffer_or_name, Lisp_Object norecord) -{ - if (EQ (buffer_or_name, Fwindow_buffer (selected_window))) - { - /* Basically a NOP. Avoid signalling an error in the case where - the selected window is dedicated, or a minibuffer. */ - - /* But do put this buffer at the front of the buffer list, unless - that has been inhibited. Note that even if BUFFER-OR-NAME is - at the front of the main buffer-list already, we still want to - move it to the front of the frame's buffer list. */ - if (NILP (norecord)) - record_buffer (buffer_or_name); - return Fset_buffer (buffer_or_name); - } - else if (EQ (minibuf_window, selected_window) - /* If `dedicated' is neither nil nor t, it means it's - dedicatedness can be overridden by an explicit request - such as a call to switch-to-buffer. */ - || EQ (Fwindow_dedicated_p (selected_window), Qt)) - /* We can't use the selected window so let `pop-to-buffer' try some - other window. */ - return call3 (intern ("pop-to-buffer"), buffer_or_name, Qnil, norecord); - else - return switch_to_buffer_1 (buffer_or_name, norecord); -} - DEFUN ("current-buffer", Fcurrent_buffer, Scurrent_buffer, 0, 0, 0, doc: /* Return the current buffer as a Lisp object. */) (void) @@ -1937,70 +1931,6 @@ DEFUN ("barf-if-buffer-read-only", Fbarf_if_buffer_read_only, xsignal1 (Qbuffer_read_only, Fcurrent_buffer ()); return Qnil; } - -extern Lisp_Object Qdelete_window; - -DEFUN ("bury-buffer", Fbury_buffer, Sbury_buffer, 0, 1, "", - doc: /* Put BUFFER-OR-NAME at the end of the list of all buffers. -There it is the least likely candidate for `other-buffer' to return; -thus, the least likely buffer for \\[switch-to-buffer] to select by -default. - -The argument may be a buffer name or an actual buffer object. If -BUFFER-OR-NAME is nil or omitted, bury the current buffer and remove it -from the selected window if it is displayed there. If the selected -window is dedicated to its buffer, delete that window if there are other -windows on the same frame. If the selected window is the only window on -its frame, iconify that frame. */) - (register Lisp_Object buffer_or_name) -{ - Lisp_Object buffer; - - /* Figure out what buffer we're going to bury. */ - if (NILP (buffer_or_name)) - { - Lisp_Object tem; - XSETBUFFER (buffer, current_buffer); - - tem = Fwindow_buffer (selected_window); - /* If we're burying the current buffer, unshow it. */ - if (EQ (buffer, tem)) - { - if (NILP (Fwindow_dedicated_p (selected_window))) - Fswitch_to_buffer (Fother_buffer (buffer, Qnil, Qnil), Qnil); - else if (NILP (XWINDOW (selected_window)->parent)) - Ficonify_frame (Fwindow_frame (selected_window)); - else - call1 (Qdelete_window, selected_window); - } - } - else - { - buffer = Fget_buffer (buffer_or_name); - if (NILP (buffer)) - nsberror (buffer_or_name); - } - - /* Move buffer to the end of the buffer list. Do nothing if the - buffer is killed. */ - if (!NILP (BVAR (XBUFFER (buffer), name))) - { - Lisp_Object aelt, list; - - aelt = Frassq (buffer, Vbuffer_alist); - list = Fmemq (aelt, Vbuffer_alist); - Vbuffer_alist = Fdelq (aelt, Vbuffer_alist); - XSETCDR (list, Qnil); - Vbuffer_alist = nconc2 (Vbuffer_alist, list); - - XFRAME (selected_frame)->buffer_list - = Fdelq (buffer, XFRAME (selected_frame)->buffer_list); - XFRAME (selected_frame)->buried_buffer_list - = Fcons (buffer, Fdelq (buffer, XFRAME (selected_frame)->buried_buffer_list)); - } - - return Qnil; -} DEFUN ("erase-buffer", Ferase_buffer, Serase_buffer, 0, 0, "*", doc: /* Delete the entire contents of the current buffer. @@ -6101,6 +6031,15 @@ The function `kill-all-local-variables' runs this before doing anything else. * Qchange_major_mode_hook = intern_c_string ("change-major-mode-hook"); staticpro (&Qchange_major_mode_hook); + DEFVAR_LISP ("buffer-list-update-hook", Vbuffer_list_update_hook, + doc: /* Hook run when the buffer list changes. +Functions running this hook are `get-buffer-create', +`make-indirect-buffer', `rename-buffer', `kill-buffer', +`record-buffer' and `unrecord-buffer'. */); + Vbuffer_list_update_hook = Qnil; + Qbuffer_list_update_hook = intern_c_string ("buffer-list-update-hook"); + staticpro (&Qbuffer_list_update_hook); + defsubr (&Sbuffer_live_p); defsubr (&Sbuffer_list); defsubr (&Sget_buffer); @@ -6122,12 +6061,12 @@ The function `kill-all-local-variables' runs this before doing anything else. * defsubr (&Sother_buffer); defsubr (&Sbuffer_enable_undo); defsubr (&Skill_buffer); + defsubr (&Srecord_buffer); + defsubr (&Sunrecord_buffer); defsubr (&Sset_buffer_major_mode); - defsubr (&Sswitch_to_buffer); defsubr (&Scurrent_buffer); defsubr (&Sset_buffer); defsubr (&Sbarf_if_buffer_read_only); - defsubr (&Sbury_buffer); defsubr (&Serase_buffer); defsubr (&Sbuffer_swap_text); defsubr (&Sset_buffer_multibyte); diff --git a/src/lisp.h b/src/lisp.h index c5f810a0746..ae9296b4944 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3011,6 +3011,7 @@ extern Lisp_Object set_buffer_if_live (Lisp_Object); EXFUN (Fbarf_if_buffer_read_only, 0); EXFUN (Fcurrent_buffer, 0); EXFUN (Fother_buffer, 3); +extern Lisp_Object other_buffer_safely (Lisp_Object); EXFUN (Foverlay_get, 2); EXFUN (Fbuffer_modified_p, 1); EXFUN (Fset_buffer_modified_p, 1); diff --git a/src/window.c b/src/window.c index 7a026b3a1c7..959c1c31aa2 100644 --- a/src/window.c +++ b/src/window.c @@ -50,9 +50,9 @@ along with GNU Emacs. If not, see . */ #include "nsterm.h" #endif -Lisp_Object Qwindowp, Qwindow_live_p, Qdelete_window; +Lisp_Object Qwindowp, Qwindow_live_p; static Lisp_Object Qwindow_configuration_p, Qrecord_window_buffer; -static Lisp_Object Qwindow_deletable_p, Qdisplay_buffer; +static Lisp_Object Qwindow_deletable_p, Qdelete_window, Qdisplay_buffer; static Lisp_Object Qreplace_buffer_in_windows, Qget_mru_window; static Lisp_Object Qresize_root_window, Qresize_root_window_vertically; static Lisp_Object Qscroll_up, Qscroll_down, Qscroll_command; @@ -310,6 +310,8 @@ selected windows appears and to which many commands apply. */) return selected_window; } +int window_select_count; + /* If select_window is called with inhibit_point_swap non-zero it will not store point of the old selected window's buffer back into that window's pointm slot. This is needed by Fset_window_configuration to @@ -2210,35 +2212,32 @@ next_window (Lisp_Object window, Lisp_Object minibuf, Lisp_Object all_frames, in DEFUN ("next-window", Fnext_window, Snext_window, 0, 3, 0, doc: /* Return window following WINDOW in cyclic ordering of windows. -WINDOW defaults to the selected window. The optional arguments -MINIBUF and ALL-FRAMES specify the set of windows to consider. +WINDOW must be a live window and defaults to the selected one. The +optional arguments MINIBUF and ALL-FRAMES specify the set of windows to +consider. -MINIBUF t means consider the minibuffer window even if the -minibuffer is not active. MINIBUF nil or omitted means consider -the minibuffer window only if the minibuffer is active. Any -other value means do not consider the minibuffer window even if -the minibuffer is active. +MINIBUF nil or omitted means consider the minibuffer window only if the +minibuffer is active. MINIBUF t means consider the minibuffer window +even if the minibuffer is not active. Any other value means do not +consider the minibuffer window even if the minibuffer is active. -Several frames may share a single minibuffer; if the minibuffer -is active, all windows on all frames that share that minibuffer -are considered too. Therefore, if you are using a separate -minibuffer frame and the minibuffer is active and MINIBUF says it -counts, `next-window' considers the windows in the frame from -which you entered the minibuffer, as well as the minibuffer -window. +ALL-FRAMES nil or omitted means consider all windows on WINDOW's frame, +plus the minibuffer window if specified by the MINIBUF argument. If the +minibuffer counts, consider all windows on all frames that share that +minibuffer too. The following non-nil values of ALL-FRAMES have special +meanings: + +- t means consider all windows on all existing frames. + +- `visible' means consider all windows on all visible frames. + +- 0 (the number zero) means consider all windows on all visible and + iconified frames. + +- A frame means consider all windows on that frame only. -ALL-FRAMES nil or omitted means consider all windows on WINDOW's - frame, plus the minibuffer window if specified by the MINIBUF - argument, see above. If the minibuffer counts, consider all - windows on all frames that share that minibuffer too. -ALL-FRAMES t means consider all windows on all existing frames. -ALL-FRAMES `visible' means consider all windows on all visible - frames on the current terminal. -ALL-FRAMES 0 means consider all windows on all visible and - iconified frames on the current terminal. -ALL-FRAMES a frame means consider all windows on that frame only. Anything else means consider all windows on WINDOW's frame and no - others. +others. If you use consistent values for MINIBUF and ALL-FRAMES, you can use `next-window' to iterate through the entire cycle of acceptable @@ -2252,9 +2251,32 @@ windows, eventually ending up back at the window you started with. DEFUN ("previous-window", Fprevious_window, Sprevious_window, 0, 3, 0, doc: /* Return window preceding WINDOW in cyclic ordering of windows. -WINDOW defaults to the selected window. The optional arguments -MINIBUF and ALL-FRAMES specify the set of windows to consider. -For the precise meaning of these arguments see `next-window'. +WINDOW must be a live window and defaults to the selected one. The +optional arguments MINIBUF and ALL-FRAMES specify the set of windows to +consider. + +MINIBUF nil or omitted means consider the minibuffer window only if the +minibuffer is active. MINIBUF t means consider the minibuffer window +even if the minibuffer is not active. Any other value means do not +consider the minibuffer window even if the minibuffer is active. + +ALL-FRAMES nil or omitted means consider all windows on WINDOW's frame, +plus the minibuffer window if specified by the MINIBUF argument. If the +minibuffer counts, consider all windows on all frames that share that +minibuffer too. The following non-nil values of ALL-FRAMES have special +meanings: + +- t means consider all windows on all existing frames. + +- `visible' means consider all windows on all visible frames. + +- 0 (the number zero) means consider all windows on all visible and + iconified frames. + +- A frame means consider all windows on that frame only. + +Anything else means consider all windows on WINDOW's frame and no +others. If you use consistent values for MINIBUF and ALL-FRAMES, you can use `previous-window' to iterate through the entire cycle of @@ -2267,37 +2289,6 @@ reverse order. */) } -DEFUN ("other-window", Fother_window, Sother_window, 1, 2, "p", - doc: /* Select another window in cyclic ordering of windows. -COUNT specifies the number of windows to skip, starting with the -selected window, before making the selection. If COUNT is -positive, skip COUNT windows forwards. If COUNT is negative, -skip -COUNT windows backwards. COUNT zero means do not skip any -window, so select the selected window. In an interactive call, -COUNT is the numeric prefix argument. Return nil. - -This function uses `next-window' for finding the window to select. -The argument ALL-FRAMES has the same meaning as in `next-window', -but the MINIBUF argument of `next-window' is always effectively -nil. */) - (Lisp_Object count, Lisp_Object all_frames) -{ - Lisp_Object window; - int i; - - CHECK_NUMBER (count); - window = selected_window; - - for (i = XINT (count); i > 0; --i) - window = Fnext_window (window, Qnil, all_frames); - for (; i < 0; ++i) - window = Fprevious_window (window, Qnil, all_frames); - - Fselect_window (window, Qnil); - return Qnil; -} - - /* Return a list of windows in cyclic ordering. Arguments are like for `next-window'. */ @@ -2397,10 +2388,9 @@ be listed first but no error is signalled. */) enum window_loop { WINDOW_LOOP_UNUSED, - GET_BUFFER_WINDOW, /* Arg is buffer */ - DELETE_BUFFER_WINDOWS, /* Arg is buffer */ - UNSHOW_BUFFER, /* Arg is buffer */ - REDISPLAY_BUFFER_WINDOWS, /* Arg is buffer */ + GET_BUFFER_WINDOW, /* Arg is buffer */ + REPLACE_BUFFER_IN_WINDOWS_SAFELY, /* Arg is buffer */ + REDISPLAY_BUFFER_WINDOWS, /* Arg is buffer */ CHECK_ALL_WINDOWS }; @@ -2408,6 +2398,7 @@ static Lisp_Object window_loop (enum window_loop type, Lisp_Object obj, int mini, Lisp_Object frames) { Lisp_Object window, windows, best_window, frame_arg; + int frame_best_window_flag = 0; struct frame *f; struct gcpro gcpro1; @@ -2457,110 +2448,51 @@ window_loop (enum window_loop type, Lisp_Object obj, int mini, Lisp_Object frame is visible, since Fwindow_list skips non-visible frames if that is desired, under the control of frame_arg. */ if (!MINI_WINDOW_P (w) - /* For UNSHOW_BUFFER, we must always consider all windows. */ - || type == UNSHOW_BUFFER + /* For REPLACE_BUFFER_IN_WINDOWS_SAFELY, we must always + consider all windows. */ + || type == REPLACE_BUFFER_IN_WINDOWS_SAFELY || (mini && minibuf_level > 0)) switch (type) { case GET_BUFFER_WINDOW: if (EQ (w->buffer, obj) - /* Don't find any minibuffer window - except the one that is currently in use. */ - && (MINI_WINDOW_P (w) - ? EQ (window, minibuf_window) - : 1)) + /* Don't find any minibuffer window except the one that + is currently in use. */ + && (MINI_WINDOW_P (w) ? EQ (window, minibuf_window) : 1)) { - if (NILP (best_window)) - best_window = window; - else if (EQ (window, selected_window)) - /* Prefer to return selected-window. */ + if (EQ (window, selected_window)) + /* Preferably return the selected window. */ RETURN_UNGCPRO (window); - else if (EQ (Fwindow_frame (window), selected_frame)) - /* Prefer windows on the current frame. */ + else if (EQ (XWINDOW (window)->frame, selected_frame) + && !frame_best_window_flag) + /* Prefer windows on the current frame (but don't + choose another one if we have one already). */ + { + best_window = window; + frame_best_window_flag = 1; + } + else if (NILP (best_window)) best_window = window; } break; - case DELETE_BUFFER_WINDOWS: + case REPLACE_BUFFER_IN_WINDOWS_SAFELY: + /* We could simply check whether the buffer shown by window + is live, and show another buffer in case it isn't. */ if (EQ (w->buffer, obj)) { - struct frame *fr = XFRAME (WINDOW_FRAME (w)); - - /* If this window is dedicated, and in a frame of its own, - kill the frame. */ - if (EQ (window, FRAME_ROOT_WINDOW (fr)) - && !NILP (w->dedicated) - && other_visible_frames (fr)) - { - /* Skip the other windows on this frame. - There might be one, the minibuffer! */ - while (CONSP (XCDR (windows)) - && EQ (XWINDOW (XCAR (windows))->frame, - XWINDOW (XCAR (XCDR (windows)))->frame)) - windows = XCDR (windows); - - /* Now we can safely delete the frame. */ - delete_frame (w->frame, Qnil); - } - else if (NILP (w->parent)) - { - /* If we're deleting the buffer displayed in the - only window on the frame, find a new buffer to - display there. */ - Lisp_Object buffer; - buffer = Fother_buffer (obj, Qnil, w->frame); - /* Reset dedicated state of window. */ - w->dedicated = Qnil; - Fset_window_buffer (window, buffer, Qnil); - if (EQ (window, selected_window)) - Fset_buffer (w->buffer); - } - else - call1 (Qdelete_window, window); - } - break; - - case UNSHOW_BUFFER: - if (EQ (w->buffer, obj)) - { - Lisp_Object buffer; - struct frame *fr = XFRAME (w->frame); - - /* Find another buffer to show in this window. */ - buffer = Fother_buffer (obj, Qnil, w->frame); - - /* If this window is dedicated, and in a frame of its own, - kill the frame. */ - if (EQ (window, FRAME_ROOT_WINDOW (fr)) - && !NILP (w->dedicated) - && other_visible_frames (fr)) - { - /* Skip the other windows on this frame. - There might be one, the minibuffer! */ - while (CONSP (XCDR (windows)) - && EQ (XWINDOW (XCAR (windows))->frame, - XWINDOW (XCAR (XCDR (windows)))->frame)) - windows = XCDR (windows); - - /* Now we can safely delete the frame. */ - delete_frame (w->frame, Qnil); - } - else if (!NILP (w->dedicated) && !NILP (w->parent)) - { - Lisp_Object window_to_delete; - XSETWINDOW (window_to_delete, w); - /* If this window is dedicated and not the only window - in its frame, then kill it. */ - call1 (Qdelete_window, window_to_delete); - } - else - { - /* Otherwise show a different buffer in the window. */ - w->dedicated = Qnil; - Fset_window_buffer (window, buffer, Qnil); - if (EQ (window, selected_window)) - Fset_buffer (w->buffer); - } + /* Undedicate WINDOW. */ + w->dedicated = Qnil; + /* Make WINDOW show the buffer returned by + other_buffer_safely, don't run any hooks. */ + set_window_buffer + (window, other_buffer_safely (w->buffer), 0, 0); + /* If WINDOW is the selected window, make its buffer + current. But do so only if the window shows the + current buffer (Bug#6454). */ + if (EQ (window, selected_window) + && XBUFFER (w->buffer) == current_buffer) + Fset_buffer (w->buffer); } break; @@ -2875,85 +2807,26 @@ window-start value is reasonable when this function is called. */) } -DEFUN ("delete-windows-on", Fdelete_windows_on, Sdelete_windows_on, - 0, 2, "bDelete windows on (buffer): ", - doc: /* Delete all windows showing BUFFER-OR-NAME. -BUFFER-OR-NAME may be a buffer or the name of an existing buffer and -defaults to the current buffer. - -Optional second argument FRAME controls which frames are affected. -If optional argument FRAME is `visible', search all visible frames. -If FRAME is 0, search all visible and iconified frames. -If FRAME is nil, search all frames. -If FRAME is t, search only the selected frame. -If FRAME is a frame, search only that frame. -When a window showing BUFFER-OR-NAME is dedicated and the only window of -its frame, that frame is deleted when there are other frames left. */) - (Lisp_Object buffer_or_name, Lisp_Object frame) +void +replace_buffer_in_windows (Lisp_Object buffer) { - Lisp_Object buffer; - - /* FRAME uses t and nil to mean the opposite of what window_loop - expects. */ - if (NILP (frame)) - frame = Qt; - else if (EQ (frame, Qt)) - frame = Qnil; - - if (NILP (buffer_or_name)) - buffer = Fcurrent_buffer (); - else - { - buffer = Fget_buffer (buffer_or_name); - CHECK_BUFFER (buffer); - } - - window_loop (DELETE_BUFFER_WINDOWS, buffer, 0, frame); - - return Qnil; + call1 (Qreplace_buffer_in_windows, buffer); } -DEFUN ("replace-buffer-in-windows", Freplace_buffer_in_windows, - Sreplace_buffer_in_windows, - 0, 1, "bReplace buffer in windows: ", - doc: /* Replace BUFFER-OR-NAME with some other buffer in all windows showing it. -BUFFER-OR-NAME may be a buffer or the name of an existing buffer and -defaults to the current buffer. -When a window showing BUFFER-OR-NAME is dedicated that window is -deleted. If that window is the only window on its frame, that frame is -deleted too when there are other frames left. If there are no other -frames left, some other buffer is displayed in that window. */) - (Lisp_Object buffer_or_name) -{ - Lisp_Object buffer; - - if (NILP (buffer_or_name)) - buffer = Fcurrent_buffer (); - else - { - buffer = Fget_buffer (buffer_or_name); - CHECK_BUFFER (buffer); - } - - window_loop (UNSHOW_BUFFER, buffer, 0, Qt); - - return Qnil; -} - -/* Replace BUFFER with some other buffer in all windows - of all frames, even those on other keyboards. */ +/* Safely replace BUFFER with some other buffer in all windows of all + frames, even those on other keyboards. */ void -replace_buffer_in_all_windows (Lisp_Object buffer) +replace_buffer_in_windows_safely (Lisp_Object buffer) { Lisp_Object tail, frame; - /* A single call to window_loop won't do the job - because it only considers frames on the current keyboard. - So loop manually over frames, and handle each one. */ + /* A single call to window_loop won't do the job because it only + considers frames on the current keyboard. So loop manually over + frames, and handle each one. */ FOR_EACH_FRAME (tail, frame) - window_loop (UNSHOW_BUFFER, buffer, 1, frame); + window_loop (REPLACE_BUFFER_IN_WINDOWS_SAFELY, buffer, 1, frame); } /* If *ROWS or *COLS are too small a size for FRAME, set them to the @@ -3014,8 +2887,6 @@ adjust_window_margins (struct window *w) return 1; } -int window_select_count; - static Lisp_Object Fset_window_margins (Lisp_Object, Lisp_Object, Lisp_Object); static Lisp_Object Fset_window_fringes (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object); @@ -3023,6 +2894,8 @@ static Lisp_Object Fset_window_scroll_bars (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object); static Lisp_Object Fset_window_vscroll (Lisp_Object, Lisp_Object, Lisp_Object); +/* The following three routines are needed for running a window's + configuration change hook. */ static void run_funs (Lisp_Object funs) { @@ -3031,8 +2904,19 @@ run_funs (Lisp_Object funs) call0 (XCAR (funs)); } -static Lisp_Object select_window_norecord (Lisp_Object window); -static Lisp_Object select_frame_norecord (Lisp_Object frame); +static Lisp_Object +select_window_norecord (Lisp_Object window) +{ + return WINDOW_LIVE_P (window) + ? Fselect_window (window, Qt) : selected_window; +} + +static Lisp_Object +select_frame_norecord (Lisp_Object frame) +{ + return FRAME_LIVE_P (XFRAME (frame)) + ? Fselect_frame (frame, Qt) : selected_frame; +} void run_window_configuration_change_hook (struct frame *f) @@ -3209,11 +3093,13 @@ WINDOW can be any window and defaults to the selected one. */) DEFUN ("set-window-buffer", Fset_window_buffer, Sset_window_buffer, 2, 3, 0, doc: /* Make WINDOW display BUFFER-OR-NAME as its contents. -WINDOW defaults to the selected window. BUFFER-OR-NAME must be a buffer -or the name of an existing buffer. Optional third argument KEEP-MARGINS -non-nil means that WINDOW's current display margins, fringe widths, and -scroll bar settings are preserved; the default is to reset these from -the local settings for BUFFER-OR-NAME or the frame defaults. Return nil. +WINDOW has to be a live window and defaults to the selected one. +BUFFER-OR-NAME must be a buffer or the name of an existing buffer. + +Optional third argument KEEP-MARGINS non-nil means that WINDOW's current +display margins, fringe widths, and scroll bar settings are preserved; +the default is to reset these from the local settings for BUFFER-OR-NAME +or the frame defaults. Return nil. This function throws an error when WINDOW is strongly dedicated to its buffer (that is `window-dedicated-p' returns t for WINDOW) and does not @@ -3238,33 +3124,27 @@ This function runs `window-scroll-functions' before running else if (!EQ (tem, Qt)) /* w->buffer is t when the window is first being set up. */ { - if (EQ (tem, buffer)) - return Qnil; - else if (EQ (w->dedicated, Qt)) - error ("Window is dedicated to `%s'", SDATA (BVAR (XBUFFER (tem), name))); - else - w->dedicated = Qnil; + if (!EQ (tem, buffer)) + { + if (EQ (w->dedicated, Qt)) + /* WINDOW is strongly dedicated to its buffer, signal an + error. */ + error ("Window is dedicated to `%s'", SDATA (BVAR (XBUFFER (tem), name))); + else + /* WINDOW is weakly dedicated to its buffer, reset + dedicatedness. */ + w->dedicated = Qnil; + + call1 (Qrecord_window_buffer, window); + } unshow_buffer (w); } set_window_buffer (window, buffer, 1, !NILP (keep_margins)); + return Qnil; } - -static Lisp_Object -select_window_norecord (Lisp_Object window) -{ - return WINDOW_LIVE_P (window) - ? Fselect_window (window, Qt) : selected_window; -} - -static Lisp_Object -select_frame_norecord (Lisp_Object frame) -{ - return FRAME_LIVE_P (XFRAME (frame)) - ? Fselect_frame (frame, Qt) : selected_frame; -} static Lisp_Object display_buffer (Lisp_Object buffer, Lisp_Object not_this_window_p, Lisp_Object override_frame) @@ -3799,7 +3679,7 @@ resize_frame_windows (struct frame *f, int size, int horflag) DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, 4, 4, 0, doc: /* Split window OLD. Second argument TOTAL-SIZE specifies the number of lines or columns of the -new window. In any case TOTAL-SIZE must be a positive integer +new window. In any case TOTAL-SIZE must be a positive integer. Third argument SIDE nil (or `below') specifies that the new window shall be located below WINDOW. SIDE `above' means the new window shall be @@ -4280,7 +4160,6 @@ DEFUN ("resize-mini-window-internal", Fresize_mini_window_internal, Sresize_mini else error ("Failed to resize minibuffer window"); } - /* Mark window cursors off for all windows in the window tree rooted at W by setting their phys_cursor_on_p flag to zero. Called from xterm.c, e.g. when a frame is cleared and thereby all cursors on @@ -5337,7 +5216,6 @@ and redisplay normally--don't erase and redraw the frame. */) return Qnil; } - DEFUN ("window-text-height", Fwindow_text_height, Swindow_text_height, 0, 1, 0, doc: /* Return the height in lines of the text display area of WINDOW. @@ -6865,10 +6743,7 @@ function `window-nest' and altered by the function `set-window-nest'. */); defsubr (&Sset_window_display_table); defsubr (&Snext_window); defsubr (&Sprevious_window); - defsubr (&Sother_window); defsubr (&Sget_buffer_window); - defsubr (&Sdelete_windows_on); - defsubr (&Sreplace_buffer_in_windows); defsubr (&Sdelete_other_windows_internal); defsubr (&Sdelete_window_internal); defsubr (&Sresize_mini_window_internal); @@ -6917,7 +6792,6 @@ function `window-nest' and altered by the function `set-window-nest'. */); void keys_of_window (void) { - initial_define_key (control_x_map, 'o', "other-window"); initial_define_key (control_x_map, '<', "scroll-left"); initial_define_key (control_x_map, '>', "scroll-right"); diff --git a/src/window.h b/src/window.h index 1ebc99055a5..52fecbf2e56 100644 --- a/src/window.h +++ b/src/window.h @@ -827,6 +827,7 @@ extern Lisp_Object make_window (void); extern Lisp_Object window_from_coordinates (struct frame *, int, int, enum window_part *, int); EXFUN (Fwindow_dedicated_p, 1); +extern void resize_frame_windows (struct frame *, int, int); extern void delete_all_subwindows (Lisp_Object); extern void freeze_window_starts (struct frame *, int); extern void grow_mini_window (struct window *, int); @@ -902,7 +903,6 @@ extern Lisp_Object Qwindowp, Qwindow_live_p; extern Lisp_Object Vwindow_list; EXFUN (Fwindow_buffer, 1); -EXFUN (Fwindow_frame, 1); EXFUN (Fget_buffer_window, 2); EXFUN (Fwindow_minibuffer_p, 1); EXFUN (Fselected_window, 0); @@ -918,12 +918,10 @@ extern int window_internal_height (struct window *); extern int window_body_cols (struct window *w); EXFUN (Frecenter, 1); extern void temp_output_buffer_show (Lisp_Object); -extern void replace_buffer_in_all_windows (Lisp_Object); +extern void replace_buffer_in_windows_safely (Lisp_Object); extern void init_window_once (void); extern void init_window (void); extern void syms_of_window (void); extern void keys_of_window (void); -extern void resize_frame_windows (struct frame *, int, int); - #endif /* not WINDOW_H_INCLUDED */