From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Stephen Leake Newsgroups: gmane.emacs.devel Subject: other-frame, other-window prefix keys Date: Sat, 08 Aug 2015 02:24:03 -0500 Message-ID: <86vbcq2qgc.fsf@stephe-leake.org> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: ger.gmane.org 1439018703 14123 80.91.229.3 (8 Aug 2015 07:25:03 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Sat, 8 Aug 2015 07:25:03 +0000 (UTC) To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Sat Aug 08 09:24:52 2015 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1ZNyUt-0004UC-I0 for ged-emacs-devel@m.gmane.org; Sat, 08 Aug 2015 09:24:51 +0200 Original-Received: from localhost ([::1]:52130 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZNyUs-0001KR-OW for ged-emacs-devel@m.gmane.org; Sat, 08 Aug 2015 03:24:50 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:50231) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZNyUo-0001KJ-9g for emacs-devel@gnu.org; Sat, 08 Aug 2015 03:24:47 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZNyUl-00072e-34 for emacs-devel@gnu.org; Sat, 08 Aug 2015 03:24:46 -0400 Original-Received: from gproxy4-pub.mail.unifiedlayer.com ([69.89.23.142]:34362) by eggs.gnu.org with smtp (Exim 4.71) (envelope-from ) id 1ZNyUk-00072Q-P8 for emacs-devel@gnu.org; Sat, 08 Aug 2015 03:24:43 -0400 Original-Received: (qmail 21004 invoked by uid 0); 8 Aug 2015 07:24:40 -0000 Original-Received: from unknown (HELO CMOut01) (10.0.90.82) by gproxy4.mail.unifiedlayer.com with SMTP; 8 Aug 2015 07:24:40 -0000 Original-Received: from host114.hostmonster.com ([74.220.207.114]) by CMOut01 with id 27QW1r00i2UdiVW017QZYG; Sat, 08 Aug 2015 01:24:39 -0600 X-Authority-Analysis: v=2.1 cv=NJxGpSKg c=1 sm=1 tr=0 a=CQdxDb2CKd3SRg4I0/XZPQ==:117 a=CQdxDb2CKd3SRg4I0/XZPQ==:17 a=DsvgjBjRAAAA:8 a=f5113yIGAAAA:8 a=9i_RQKNPAAAA:8 a=y7kgw_RnJtkA:10 a=hEr_IkYJT6EA:10 a=x_XPkuGwIRMA:10 a=uRRa74qj2VoA:10 a=mDV3o1hIAAAA:8 a=9GwMAvmE_TXJubp_j8UA:9 a=JfEA1gzarvUA:10 a=FdUU1bqgqC1yEpQdEPcA:9 Original-Received: from [76.218.37.33] (port=55805 helo=TAKVER2) by host114.hostmonster.com with esmtpa (Exim 4.84) (envelope-from ) id 1ZNyUZ-0003tW-ES for emacs-devel@gnu.org; Sat, 08 Aug 2015 01:24:31 -0600 User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.5 (windows-nt) X-Identified-User: {2442:host114.hostmonster.com:stephele:stephe-leake.org} {sentby:smtp auth 76.218.37.33 authed with stephen_leake@stephe-leake.org} X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 69.89.23.142 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:188596 Archived-At: --=-=-= Content-Type: text/plain I finally made time to work on universal other-frame and other-window prefix keys. This was discussed about 2 years ago here: https://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00075.html. It turned out to be easier than I anticipated. Attached is a minor mode that implements this. It advises switch-to-buffer and temp-buffer-window-show-advice, and binds C-x 7 and C-x 8 such that: C-x 7 causes a buffer displayed by to appear in another window in the same frame; a window is created if necessary. C-x 8 causes a buffer displayed by to appear in another frame; a frame is created if necessary. This avoids the need to define -other-window and -other-frame variants of commands that switch to a new buffer; it adds those variants in a generic way. It also binds: C-x W move current buffer to another window in the same frame C-x F move current buffer to another frame These make it easy to rearrange the buffer placement, and to recover from forgetting to type C-x 7 or 8. It works nicely, and I'm already getting dependent on it. I used C-x 7, C-x 8 because they are currently unbound. That's a bit awkward to type, so in my personal setup I bound M-m, M-M, which are close to M-. , which I use a lot, so this is very convenient. Advising temp-buffer-window-show-advice addresses the issue of how temporary prompt buffers are displayed. There is one issue (see the FIXMEs); if the command is aborted for any reason, the prefix (set in ofw-frame-window-prefix-arg) needs to be reset. I haven't looked into doing that yet. If post-command-hook is run even if a command is aborted, that might be a good place to reset this. I'll have to try it. I also haven't finished porting this to Emacs 24.3. I'd be happy to put this in Gnu ELPA (in part so it is available for Emacs 24.3), but there was some discussion in the original thread about having something like this in core. The advice on switch-to-buffer simply redefines the function; it is probably possible to do that more carefully, to preserve more of the current semantics. -- -- Stephe --=-=-= Content-Type: application/emacs-lisp Content-Disposition: attachment; filename=other-frame-window.el Content-Transfer-Encoding: quoted-printable ;;; minor mode to enable global prefix keys for other frame/window buffer p= lacement -*- lexical-binding: t -*- ;; ;; Copyright (C) 2015 Free Software Foundation, Inc. ;; ;; Author: Stephen Leake ;; Maintainer: Stephen Leake ;; Keywords: frame ;; window ;; Version: 1.0.0 ;; package-requires: ((emacs "24.3")) ;; ;; This file is part of GNU Emacs. ;; ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see . ;; ;;; Usage: ;; ;; Enable the minor mode with: ;; ;; M-x other-frame-window-mode ;; ;; or, in your ~/.emacs: ;; ;; (other-frame-window-mode t) ;; ;; C-x 7 causes a buffer displayed by to appear in ;; another window in the same frame; a window is created if necessary. ;; ;; C-x 8 causes a buffer displayed by to appear in ;; another frame; a frame is created if necessary. ;;; Design: ;; ;; This uses C-x 7, 8 prefix because those keys are undefined in core ;; Emacs. It could eventually switch to 4, 5, since those are ;; currently used for -other-window, -other-frame bindings. ;; ;; (info "(emacs) Pop Up Window") (info "(emacs) Creating Frames") ;; ;; This adds advice to switch-to-buffer; eventually Emacs could ;; reimplement switch-to-buffer to do the same. ;;; Code: ;; Universal frame/window prefix; value put in ofw-frame-window-prefix-arg ;; must be globally unique, and must match what ;; ofw-display-buffer-other-window-or-frame expects. (defvar ofw-frame-window-prefix-arg nil "Universal frame/window prefix; see `ofw-display-buffer-other-window-or-f= rame'.") (defconst ofw-other-window 7) (defconst ofw-other-frame 8) (defun ofw-other-window-argument () "Set `ofw-frame-window-prefix-arg' to indicate other window. See `display-buffer-other-window-or-frame'." (interactive) (setq ofw-frame-window-prefix-arg ofw-other-window)) (defun ofw-other-frame-argument () "Set `ofw-frame-window-prefix-arg' to indicate other frame. See `display-buffer-other-window-or-frame'." (interactive) (setq ofw-frame-window-prefix-arg ofw-other-frame)) (defun ofw-display-buffer-other-window-or-frame (buffer alist) "Depending on `ofw-frame-window-prefix-arg', show BUFFER in another windo= w or frame. For display-buffer alist. If ofw-frame-window-prefix-arg is: ofw-other-window: show buffer in another window in current frame, creating new window if needed. ofw-other-frame: show buffer in another frame, creating new frame if needed. other: return nil." (let ((arg ofw-frame-window-prefix-arg)) (setq ofw-frame-window-prefix-arg nil) ;; for next call. FIXME: C-g sho= uld also reset this (or (cond ((equal arg ofw-other-window) ;; other window, create if needed ;; 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 (select= ed-frame)))) '(inhibit-same-window . t)) alist)) (display-buffer-pop-up-window buffer alist))) ((equal arg ofw-other-frame) ;; other frame, create if needed (or (display-buffer-use-some-frame buffer alist) (display-buffer-pop-up-frame buffer alist))) (t nil) ;; fall thru to other actions )))) ;; FIXME: use defadvice for Emacs 24.3 (defun ofw-switch-to-buffer-advice (_orig-fun buffer &optional norecord _f= orce-same-window) "Change switch-to-buffer to call pop-to-buffer. This allows switch-to-buffer to respect `ofw-frame-window-prefix-arg' when `ofw-display-buffer-other-window-or-frame' is in `display-buffer-overriding-action'." (pop-to-buffer buffer (list 'display-buffer-same-window) norecord)) ;; FIXME: use defadvice for Emacs 24.3 (defun ofw-temp-buffer-window-show-advice (orig-fun buffer &optional action) (let ((ofw-frame-window-prefix-arg nil)) (funcall orig-fun buffer action))) (defun ofw-move-to-other-window () "Move current buffer to another window in same frame. Point stays in moved buffer." (interactive) (let ((buffer (current-buffer))) (switch-to-prev-buffer nil 'bury) (pop-to-buffer buffer (cons '(display-buffer-use-some-frame display-buffer-pop-up-window) (list (cons 'frame-predicate (lambda (frame) (eq frame (selected-frame)= ))) '(inhibit-same-window . t))) ))) (defun ofw-move-to-other-frame () "Move current buffer to a window in another frame. Point stays in moved buffer." (interactive) (let ((buffer (current-buffer))) (switch-to-prev-buffer nil 'bury) (pop-to-buffer buffer (cons '(display-buffer-use-some-frame display-buffer-pop-up-frame) '((reusable-frames . visible))) ))) (defvar ofw-map (let ((map (make-sparse-keymap))) (define-key map "\C-x7" 'ofw-other-window-argument) (define-key map "\C-x8" 'ofw-other-frame-argument) (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-minor-mode other-frame-window-mode "Minor mode for other frame/window buffer placement. Enable mode if ARG is positive." :init-value nil :lighter " ofw" ;; mode line :keymap ofw-map :global t (if other-frame-window-mode ;; enable (progn ;; Always handle ofw-frame-window-prefix-arg; add ;; 'ofw-display-buffer-other-window-or-frame to ;; display-buffer-overriding-action functions. (let ((functions (car display-buffer-overriding-action)) (attrs (cdr display-buffer-overriding-action))) (push 'ofw-display-buffer-other-window-or-frame functions) (setq display-buffer-overriding-action (cons functions attrs))) ;; 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; ;; add (reusable-frames . visible) to display-buffer-base-action ;; attributes alist. (let ((functions (car display-buffer-base-action)) (attrs (cdr display-buffer-base-action))) (push '(reusable-frames . visible) attrs) (setq display-buffer-base-action (cons functions attrs))) ;; Change switch-to-buffer to use display-buffer (if (fboundp 'advice-add) ;; Emacs 25 (advice-add 'switch-to-buffer :around #'ofw-switch-to-buffer-advice) ;; Emacs 24 (ad-activate 'switch-to-buffer)) ;; Completing-read 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. (if (fboundp 'advice-add) (advice-add 'temp-buffer-window-show :around #'ofw-temp-buffer-window-= show-advice) (ad-activate 'temp-buffer-window-show)) ;; FIXME: if command aborts, we need to reset ofw-frame-window-prefix-arg = to nil ) ;; else disable (let ((functions (car display-buffer-overriding-action)) (attrs (cdr display-buffer-overriding-action))) (setq functions (delq 'ofw-display-buffer-other-window-or-frame funct= ions)) (setq display-buffer-overriding-action (cons functions attrs))) (let ((functions (car display-buffer-base-action)) (attrs (cdr display-buffer-base-action))) (setq attrs (delq '(reusable-frames . visible) attrs)) (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-a= dvice) )) (unless (fboundp 'display-buffer-use-some-frame) ;; in Emacs 25; define here for Emacs 24.3 (defun display-buffer-use-some-frame (buffer alist) "Display BUFFER in an existing frame that meets a predicate \(by default any frame other than the current frame). If successful, return the window used; otherwise return nil. If ALIST has a non-nil `inhibit-switch-frame' entry, avoid raising the frame. If ALIST has a non-nil `frame-predicate' entry, its value is a function taking one argument (a frame), returning non-nil if the frame is a candidate; this function replaces the default predicate. If ALIST has a non-nil `inhibit-same-window' entry, avoid using the currently selected window (only useful with a frame-predicate that allows the selected frame)." (let* ((predicate (or (cdr (assq 'frame-predicate alist)) (lambda (frame) (and (not (eq frame (selected-frame))) (not (window-dedicated-p (or (get-lru-window frame) (frame-first-window frame))))) ))) (frame (car (filtered-frame-list predicate))) (window (and frame (get-lru-window frame nil (cdr (assq 'inhibit-s= ame-window alist)))))) (when window (prog1 (window--display-buffer buffer window 'frame alist display-buffer-mark-dedicated) (unless (cdr (assq 'inhibit-switch-frame alist)) (window--maybe-raise-frame frame)))) )) ) (provide 'other-frame-window) ;; end of file --=-=-=--