branch: master commit 2c1b58ac309eab66ce190159641308fb2e2efac3 Author: Stefan Monnier <monn...@iro.umontreal.ca> Commit: Stefan Monnier <monn...@iro.umontreal.ca>
* other-frame-window.el: Disable more carefully; add transient map Only require 24.4. (ofw--just-set, ofw-transient-map): New vars. (ofw--set-prefix): Use them. Rename from ofw-add-to-overriding. Update callers. Let the C-u prefix pass through. (ofw-delete-from-overriding): Prefer nil to (nil . nil) in display-buffer-overriding-action. (ofw-switch-to-buffer-advice): Better preserve the default behavior. (ofw--suspend-and-restore): Rename from ofw-temp-window-advice. Take advantage of dynamic scoping. (other-frame-window-mode-map): Rename from ofw-map. (ofw--reset-prefix): New function. (other-frame-window-mode): Use it to disable the prefix after the next command. Advise read-from-minibuffer rather than temp-buffer-window-show. (ofw-dwim--frame-p): New function. (ofw-dwim-delete-this, ofw-dwim-one, ofw-dwim-open-other) (ofw-dwim-select-other): New commands. --- packages/other-frame-window/other-frame-window.el | 199 +++++++++++++++------ 1 files changed, 146 insertions(+), 53 deletions(-) diff --git a/packages/other-frame-window/other-frame-window.el b/packages/other-frame-window/other-frame-window.el index 7324125..52d22a8 100755 --- a/packages/other-frame-window/other-frame-window.el +++ b/packages/other-frame-window/other-frame-window.el @@ -4,10 +4,9 @@ ;; ;; Author: Stephen Leake <stephen_le...@member.fsf.org> ;; Maintainer: Stephen Leake <stephen_le...@member.fsf.org> -;; Keywords: frame -;; window +;; Keywords: frame window ;; Version: 1.0.0 -;; package-requires: ((emacs "25.0")) +;; Package-Requires: ((emacs "24.4")) ;; ;; This file is part of GNU Emacs. ;; @@ -53,80 +52,126 @@ ;; This adds advice to switch-to-buffer; eventually Emacs could ;; reimplement switch-to-buffer to do the same. +;;;; Todo: + +;; - Make the `C-x 7' prefix appear in the echo area. +;; - `C-x 7 C-h' should display the transient map. +;; - `C-x 7 C-u foo' should pass both prefixes to `foo'. + ;;; Code: -(defun ofw-add-to-overriding (func) - "Add FUNC to 'display-buffer-overriding-action' action list." +(defvar ofw--just-set nil + "Non-nil if we just set the prefix in the previous command.") + +(defvar ofw-transient-map + (let ((map (make-sparse-keymap))) + ;; FIXME: This is basically the union of the default the C-x 4 and C-x 5 + ;; keymaps in Emacs-25. + (define-key map [?\C-f] #'find-file) + (define-key map [?\C-o] #'display-buffer) + (define-key map [?.] + (if (fboundp 'xref-find-definitions) ;Emacs≥25. + 'xref-find-definitions 'find-tag)) + (define-key map [?0] #'ofw-dwim-delete-this) + (define-key map [?1] #'ofw-dwim-one) + (define-key map [?2] #'ofw-dwim-open-other) + (define-key map [?a] #'add-change-log-entry) + (define-key map [?b] #'switch-to-buffer) + (define-key map [?c] #'clone-indirect-buffer) + (define-key map [?d] #'dired) + (define-key map [?f] #'find-file) + (define-key map [?m] #'compose-mail) + (define-key map [?o] #'ofw-dwim-select-other) + (define-key map [?r] #'find-file-read-only) + map) + "Keymap used for one command right after setting the prefix.") + +(defun ofw--set-prefix (func) + "Add ofw prefix function FUNC." (let ((functions (car display-buffer-overriding-action)) (attrs (cdr display-buffer-overriding-action))) (push func functions) - (setq display-buffer-overriding-action (cons functions attrs)))) + (setq display-buffer-overriding-action (cons functions attrs)) + ;; Make sure the next pre-command-hook doesn't immediately set + ;; display-buffer-overriding-action back to nil. + (setq ofw--just-set t) + ;; C-u C-x 7 foo should pass C-u to foo, not to C-x 7, so + ;; pass the normal prefix to the next command. + ;; FIXME: This should be done by all prefix commands and for all kinds of + ;; prefixes, so that C-x 7 C-u foo works as well! + (setq prefix-arg current-prefix-arg) + (set-transient-map ofw-transient-map))) (defun ofw-delete-from-overriding () - "Delete 'ofw-display-buffer-other-window' and -'ofw-display-buffer-other-frame' from -'display-buffer-overriding-action' action list, if present." - (let ((functions (car display-buffer-overriding-action)) - (attrs (cdr display-buffer-overriding-action))) - (setq functions (delq 'ofw-display-buffer-other-frame (delq 'ofw-display-buffer-other-window functions))) - (setq display-buffer-overriding-action (cons functions attrs)))) + "Remove ourselves from 'display-buffer-overriding-action' action list, if present." + (let ((functions (car display-buffer-overriding-action)) + (attrs (cdr display-buffer-overriding-action))) + (setq functions (delq #'ofw-display-buffer-other-frame + (delq #'ofw-display-buffer-other-window functions))) + (setq display-buffer-overriding-action + (when (or functions attrs) (cons functions attrs))))) (defun ofw-other-window () "Set `display-buffer-overriding-action' to indicate other window." (interactive) - (ofw-add-to-overriding 'ofw-display-buffer-other-window)) + (ofw--set-prefix #'ofw-display-buffer-other-window)) (defun ofw-other-frame () "Set `display-buffer-overriding-action' to indicate other frame." (interactive) - (ofw-add-to-overriding 'ofw-display-buffer-other-frame)) + (ofw--set-prefix #'ofw-display-buffer-other-frame)) (defun ofw-display-buffer-other-window (buffer alist) "Show BUFFER in another window in the current frame, creating new window if needed and allowed. If successful, return window; else return nil. Intended for 'display-buffer-overriding-action'." - ;; Reset for next display-buffer call. FIXME: C-g, and next command - ;; failure, should also reset this + ;; Reset for next display-buffer call. Normally, this is taken care of by + ;; ofw--reset-prefix, but we do it here just in case. + ;; FIXME: Why be careful in ofw-delete-from-overriding and careless here? (setq display-buffer-overriding-action nil) ;; We can't use display-buffer-use-some-window here, because ;; that unconditionally allows another frame. (or (display-buffer-use-some-frame buffer - (append (list (cons 'frame-predicate (lambda (frame) (eq frame (selected-frame)))) + (append (list (cons 'frame-predicate + (lambda (frame) (eq frame (selected-frame)))) '(inhibit-same-window . t)) alist)) (display-buffer-pop-up-window buffer alist))) (defun ofw-display-buffer-other-frame (buffer alist) - "Show BUFFER in another frame, creating a new frame -if needed. + "Show BUFFER in another frame, creating a new frame if needed. If successful, return window; else return nil. Intended for 'display-buffer-overriding-action'." ;; Reset for next display-buffer call. + ;; FIXME: Why be careful in ofw-delete-from-overriding and careless here? (setq display-buffer-overriding-action nil) (or (display-buffer-use-some-frame buffer alist) (display-buffer-pop-up-frame buffer alist))) ;; FIXME: use defadvice for Emacs 24.3 -(defun ofw-switch-to-buffer-advice (_orig-fun buffer &optional norecord _force-same-window) - "Change switch-to-buffer to call pop-to-buffer. -This allows switch-to-buffer to respect 'ofw-other-window', -'ofw-other-frame'." - (pop-to-buffer buffer (list 'display-buffer-same-window) norecord)) +(defun ofw-switch-to-buffer-advice (orig-fun buffer + &optional norecord force-same-window) + "Change `switch-to-buffer' to call `pop-to-buffer'. +This allows `switch-to-buffer' to respect `ofw-other-window', +`ofw-other-frame'." + (if display-buffer-overriding-action + (pop-to-buffer buffer (list #'display-buffer-same-window) norecord) + (funcall orig-fun buffer norecord force-same-window))) ;; FIXME: use defadvice for Emacs 24.3 -(defun ofw-temp-window-advice (orig-func buffer &optional action) - "Delete any ofw actions from 'display-buffer-overriding-action', -call ORIG-FUNC, restore ofw actions to 'display-buffer-overriding-action'." - (let ((temp display-buffer-overriding-action) - result) +(defun ofw--suspend-and-restore (orig-func &rest args) + "Call ORIG-FUNC without any ofw actions on 'display-buffer-overriding-action'." + (let ((display-buffer-overriding-action display-buffer-overriding-action)) + ;; FIXME: ofw-delete-from-overriding operates destructively, so the + ;; subsequent "restore" step only works if our ofw actions were all at the + ;; very beginning display-buffer-overriding-action (in which case `delq' + ;; happens not to be destructive). (ofw-delete-from-overriding) - (setq result (funcall orig-func buffer action)) - (setq display-buffer-overriding-action temp) - result)) + (apply orig-func args))) (defun ofw-move-to-other-window () "Move current buffer to another window in same frame. @@ -153,27 +198,36 @@ Point stays in moved buffer." '((reusable-frames . visible))) ))) -(defvar ofw-map +(defvar other-frame-window-mode-map (let ((map (make-sparse-keymap))) - (define-key map "\C-x7" 'ofw-other-window) - (define-key map "\C-x9" 'ofw-other-frame) - (define-key map "\C-xW" 'ofw-move-to-other-window) - (define-key map "\C-xF" 'ofw-move-to-other-frame) - map - ) "Local keymap used for other-frame-window minor mode.") + (define-key map "\C-x7" #'ofw-other-window) + (define-key map "\C-x9" #'ofw-other-frame) + (define-key map "\C-xW" #'ofw-move-to-other-window) + (define-key map "\C-xF" #'ofw-move-to-other-frame) + map) + "Local keymap used for other-frame-window minor mode.") + +(defun ofw--reset-prefix () + (if ofw--just-set + (setq ofw--just-set nil) + (ofw-delete-from-overriding))) (define-minor-mode other-frame-window-mode "Minor mode for other frame/window buffer placement. Enable mode if ARG is positive." - :init-value nil + ;; FIXME: I think the mode-line is too crowded to accommodate such + ;; global-and-permanent minor-modes. :lighter " ofw" ;; mode line - :keymap ofw-map :global t + (remove-hook 'pre-command-hook #'ofw--reset-prefix) + (if other-frame-window-mode ;; enable (progn - ;; We assume Emacs code calls pop-to-buffer when there is a good + (add-hook 'pre-command-hook #'ofw--reset-prefix) + + ;; We assume Emacs code calls pop-to-buffer when there is a good ;; reason to put the buffer in another window, so we don't mess ;; with the default actions, except to allow ;; display-buffer-reuse-window to use a window in another frame; @@ -185,20 +239,23 @@ Enable mode if ARG is positive." (setq display-buffer-base-action (cons functions attrs))) ;; Change switch-to-buffer to use display-buffer - (if (fboundp 'advice-add) - ;; Emacs 25 + (if (fboundp 'advice-add) ;Emacs≥24.4 (advice-add 'switch-to-buffer :around #'ofw-switch-to-buffer-advice) - ;; Emacs 24 - (ad-activate 'switch-to-buffer)) + ;; FIXME: `ad-activate' affects all pieces of advice of that + ;; function, which is not what we want! + ;; (ad-activate 'switch-to-buffer) + ) ;; Completing-read <tab> pops up a buffer listing completions; ;; that should not respect or consume - ;; ofw-frame-window-prefix-arg. We advise - ;; temp-buffer-window-show-advice (used by completing-read) to - ;; handle additional similar cases. + ;; ofw-frame-window-prefix-arg. (if (fboundp 'advice-add) - (advice-add 'temp-buffer-window-show :around #'ofw-temp-window-advice) - (ad-activate 'temp-buffer-window-show)) + (advice-add 'read-from-minibuffer + :around #'ofw--suspend-and-restore) + ;; FIXME: `ad-activate' affects all pieces of advice of that + ;; function, which is not what we want! + ;; (ad-activate 'read-from-minibuffer) + ) ) ;; else disable @@ -208,7 +265,7 @@ Enable mode if ARG is positive." (setq display-buffer-base-action (cons functions attrs))) (advice-remove 'switch-to-buffer #'ofw-switch-to-buffer-advice) - (advice-remove 'temp-buffer-window-show #'ofw-temp-buffer-window-show-advice) + (advice-remove 'read-from-minibuffer #'ofw--suspend-and-restore) )) (unless (fboundp 'display-buffer-use-some-frame) @@ -250,5 +307,41 @@ that allows the selected frame)." )) ) +;; Some of the commands on the transient keymap don't actually *display* +;; in another window/frame but instead do something either at the level +;; of windows or frames. I call those "ofw-dwim-*". + +(defun ofw-dwim--frame-p () + "Return non-nil if the prefix is for \"other-frame\" rather than window." + ;; FIXME: Comparing functions is ugly/hackish! + (memq #'ofw-display-buffer-other-frame + (car display-buffer-overriding-action))) + +(defun ofw-dwim-delete-this () + "Delete this frame or window." + (interactive) + (call-interactively + (if (ofw-dwim--frame-p) #'delete-frame #'kill-buffer-and-window))) + +(defun ofw-dwim-one () + "Delete all other frames or windows." + (interactive) + (call-interactively + (if (ofw-dwim--frame-p) #'delete-other-frames #'delete-other-windows))) + +(defun ofw-dwim-open-other () + "Show current buffer in other frame or window." + (interactive) + (if (ofw-dwim--frame-p) + ;; FIXME: This is the old C-x 5 2 behavior, but maybe it should just use + ;; display-buffer instead! + (call-interactively #'make-frame-command) + (display-buffer (current-buffer)))) + +(defun ofw-dwim-select-other () + "Select other frame or window." + (interactive) + (call-interactively (if (ofw-dwim--frame-p) #'other-frame #'other-window))) + (provide 'other-frame-window) ;;; other-frame-window.el ends here