aboutsummaryrefslogtreecommitdiffstats
path: root/elpa/switch-window-20210808.742/switch-window.el
diff options
context:
space:
mode:
Diffstat (limited to 'elpa/switch-window-20210808.742/switch-window.el')
-rw-r--r--elpa/switch-window-20210808.742/switch-window.el1017
1 files changed, 1017 insertions, 0 deletions
diff --git a/elpa/switch-window-20210808.742/switch-window.el b/elpa/switch-window-20210808.742/switch-window.el
new file mode 100644
index 0000000..e21a0ea
--- /dev/null
+++ b/elpa/switch-window-20210808.742/switch-window.el
@@ -0,0 +1,1017 @@
+;;; switch-window.el --- A *visual* way to switch window -*- lexical-binding: t -*-
+;;
+;; Copyright (C) 2010-2017 Dimitri Fontaine
+;; 2016-2017 Feng Shu
+;;
+;; Author: Dimitri Fontaine <dim@tapoueh.org>
+;; Feng Shu <tumashu@163.com>
+;; URL: https://github.com/dimitri/switch-window
+;; http://tapoueh.org/emacs/switch-window.html
+;; Git-URL: https://github.com/dimitri/switch-window.git
+;; Version: 1.6.1
+;; Created: 2010-04-30
+;; Keywords: convenience
+;; Licence: WTFPL, grab your copy here: http://sam.zoy.org/wtfpl/
+;; Package-Requires: ((emacs "24"))
+;;
+;; This file is NOT part of GNU Emacs.
+;;
+;;; Commentary:
+;;
+;; * What is switch-window :README:
+;; switch-window is an Emacs window switch tool, which offer a
+;; *visual* way to choose a window to switch to, delete, split
+;; or other operations.
+;;
+;; [[./snapshots/switch-window.png]]
+;;
+;; ** Installation
+;;
+;; 1. Config melpa source, please read: http://melpa.org/#/getting-started
+;; 2. M-x package-install RET switch-window RET
+;;
+;; Note: User can install switch-window with [[http://github.com/dimitri/el-get][El-Get]] too.
+;;
+;; ** Configure and Usage
+;;
+;; #+BEGIN_EXAMPLE
+;; (require 'switch-window)
+;; (global-set-key (kbd "C-x o") 'switch-window)
+;; (global-set-key (kbd "C-x 1") 'switch-window-then-maximize)
+;; (global-set-key (kbd "C-x 2") 'switch-window-then-split-below)
+;; (global-set-key (kbd "C-x 3") 'switch-window-then-split-right)
+;; (global-set-key (kbd "C-x 0") 'switch-window-then-delete)
+;;
+;; (global-set-key (kbd "C-x 4 d") 'switch-window-then-dired)
+;; (global-set-key (kbd "C-x 4 f") 'switch-window-then-find-file)
+;; (global-set-key (kbd "C-x 4 m") 'switch-window-then-compose-mail)
+;; (global-set-key (kbd "C-x 4 r") 'switch-window-then-find-file-read-only)
+;;
+;; (global-set-key (kbd "C-x 4 C-f") 'switch-window-then-find-file)
+;; (global-set-key (kbd "C-x 4 C-o") 'switch-window-then-display-buffer)
+;;
+;; (global-set-key (kbd "C-x 4 0") 'switch-window-then-kill-buffer)
+;; #+END_EXAMPLE
+;;
+;; When switch-window is enabled, user can use the below five keys:
+;;
+;; | key | command description |
+;; |-----+-----------------------|
+;; | "i" | Move the border up |
+;; | "k" | Move the border down |
+;; | "j" | Move the border left |
+;; | "l" | Move the border right |
+;; | "b" | Balance windows |
+;; |"SPC"| Resume auto-resize |
+;;
+;; If you want to customize this feature, please see variable:
+;; `switch-window-extra-map'.
+;;
+;; Note: if you use auto-resize window feature, you *must* know
+;; that when you execute above window operate commands, auto-resize
+;; feature will be disabled temporarily, you should use above "SPC"
+;; key to resume.
+;;
+;; ** Tips
+;;
+;; *** I want to select a window with "a-z" instead of "1-9".
+;; #+BEGIN_EXAMPLE
+;; (setq switch-window-shortcut-style 'qwerty)
+;; #+END_EXAMPLE
+;;
+;; Note: user can arrange qwerty shortcuts by variable
+;; `switch-window-qwerty-shortcuts'.
+;;
+;; *** I want to let window to show bigger label.
+;; The face of label is switch-window-label, user can change it :height
+;; with custiomize-face
+;;
+;; *** I want to *hide* window label when window's number < 3
+;; #+BEGIN_EXAMPLE
+;; (setq switch-window-threshold 2)
+;; #+END_EXAMPLE
+;;
+;; *** I want to select minibuffer with label "z".
+;; #+BEGIN_EXAMPLE
+;; (setq switch-window-minibuffer-shortcut ?z)
+;; #+END_EXAMPLE
+;;
+;; *** I want to auto resize a window when switch to it
+;; #+BEGIN_EXAMPLE
+;; (setq switch-window-auto-resize-window t)
+;; (setq switch-window-default-window-size 0.8) ;80% of frame size
+;; (switch-window-mouse-mode) ;auto resize when switch window with mouse
+;; #+END_EXAMPLE
+;;
+;; Advanced usage:
+;; #+BEGIN_EXAMPLE
+;; (setq switch-window-auto-resize-window
+;; (lambda ()
+;; (equal (buffer-name) "*scratch*"))) ;when return t, run auto switch
+;; (setq switch-window-default-window-size '(0.8 . 0.6)) ;80% width and 60% height of frame
+;; #+END_EXAMPLE
+;;
+;; By the way, you can use package [[https://github.com/roman/golden-ratio.el][golden-ratio]] also.
+;;
+;; *** Switch-window seem to conflict with Exwm, how to do?
+;; By default, switch-window get user's input with the help
+;; of function `read-event', this approach does not work well
+;; with [[https://github.com/ch11ng/exwm][Exwm]] (Emacs X window manager),
+;; user should set the below variable and use minibuffer
+;; to get input instead:
+;;
+;; #+BEGIN_EXAMPLE
+;; (setq switch-window-input-style 'minibuffer)
+;; #+END_EXAMPLE
+;;
+;; Note: if you use minibuffer to get input, the feature about
+;; `switch-window-minibuffer-shortcut' will not work well.
+;;
+;; *** I use text terminal, but I want *bigger* label.
+;; The only choice is using asciiart, which *draw* a bigger label
+;; with *small* ascii char.
+;;
+;; #+BEGIN_EXAMPLE
+;; (setq switch-window-shortcut-appearance 'asciiart)
+;; #+END_EXAMPLE
+;;
+;; [[./snapshots/switch-window-3.png]]
+;;
+;; *** I want to use image or icon as label.
+;; 1. Prepare your label images, rename them to:
+;; 1.png ... 9.png, a.png ... z.png.
+;;
+;; You can use other image types supported by
+;; Emacs, please see: `image-types'.
+;; 2. Put all above images to directory:
+;; `switch-window-image-directory'.
+;; 3. Set variable: `switch-window-shortcut-appearance'
+;; #+BEGIN_EXAMPLE
+;; (setq switch-window-shortcut-appearance 'image)
+;; #+END_EXAMPLE
+;;
+;; [[./snapshots/switch-window-2.png]]
+;;
+;; *** `switch-window-shortcut-appearance' can't satisfy my need. how to do?
+;; All you should do is hacking you own label buffer function,
+;; for example: my-switch-window-label-buffer-function, and set
+;; the below variable:
+;;
+;; #+BEGIN_EXAMPLE
+;; (setq switch-window-label-buffer-function
+;; 'my-switch-window-label-buffer-function)
+;; #+END_EXAMPLE
+;;
+;; *** Have any other similar package exist?
+;; - [[https://github.com/abo-abo/ace-window][ace-window]]
+;;
+;; ** Changelog
+;;
+;; *** 1.6.0 - 2018-06-06
+;; 1. Add switch-window-label face to control the appearance of label.
+;; 2. Remove `switch-window-increase', use switch-window-label face instead.
+;; 3. Show orig text with label: see `switch-window-background'
+;; 4. Switch between frames: see `switch-window-multiple-frames'
+;; 5. [incompatible] `switch-window-label-buffer-function''s arguments have changed,
+;; user should update when use it.
+;;
+;; *** 1.5.0 - 2017-04-29
+;; - Implement commands:
+;; 1. switch-window-then-maximize
+;; 2. switch-window-then-delete
+;; 3. switch-window-then-split-below
+;; 4. switch-window-then-split-right
+;; 5. switch-window-then-split-horizontally
+;; 6. switch-window-then-split-vertically
+;; 7. switch-window-then-swap-buffer
+;; - Let switch-window work well with Exwm (Emacs X window manager).
+;; - User can customize switch-window label's appearance.
+;;
+;; *** 1.0.0 - 2015-01-14
+;; - Please fixme.
+;;
+;; *** 0.11 - 2013-09-14
+;; - restore point to end-of-buffer for windows where it was the case after
+;; switching, fixing an anoying bug.
+;;
+;; *** 0.10 - 2011-06-19
+;; - implement M-x delete-other-window (thanks developernotes on github)
+;;
+;; *** 0.9 - 2010-11-11 - emacs22 called, it wants some support
+;; - implement a propertize based hack to support emacs22
+;;
+;; *** 0.8 - 2010-09-13 - 999
+;; - Suport more than 9 windows (with a single key to type)
+;; - Use quail-keyboard-layout to choose single key labels for windows
+;;
+;; *** 0.7 - 2010-08-23 - window-dedicated-p
+;; - temporarily unset the window dedicated flag for displaying the
+;; numbers, patch from René Kyllingstad <Rene@Kyllingstad.com>
+;; - fix timeout and RET handling wrt to not changing window selection
+;;
+;; *** 0.6 - 2010-08-12 - *Minibuf-1*
+;; - add support for selecting the minibuffer when it's active
+;; - some try at a better horizontal centering
+;; - assorted cleanup
+;;
+;; *** 0.5 - 2010-08-08 - Polishing
+;; - dim:switch-window-increase is now a maximum value
+
+;;; Code:
+;; * Switch-window's code
+
+(require 'cl-lib)
+(require 'quail)
+(require 'switch-window-asciiart)
+(require 'switch-window-mvborder)
+
+(defgroup switch-window nil
+ "switch-window customization group"
+ :group 'convenience)
+
+(defcustom switch-window-background nil
+ "When t, `switch-window' will dim out all buffers temporarily when used."
+ :type 'boolean
+ :group 'switch-window)
+
+(defcustom switch-window-timeout 5
+ "After this many seconds, cancel the window switching."
+ :type 'integer
+ :group 'switch-window)
+
+(defcustom switch-window-threshold 2
+ "Only active ‘switch-window’ after this many windows open."
+ :type 'integer
+ :group 'switch-window)
+
+(defcustom switch-window-relative nil
+ "Control the ordering of windows, when true this depends on current-window."
+ :type 'boolean
+ :group 'switch-window)
+
+(defcustom switch-window-shortcut-style 'quail
+ "Use either keyboard layout or alphabet shortcut style."
+ :type '(choice (const :tag "Alphabet" 'alphabet)
+ (const :tag "Keyboard Layout" 'quail)
+ (const :tag "Qwerty Homekeys Layout" 'qwerty))
+ :group 'switch-window)
+
+(defcustom switch-window-qwerty-shortcuts
+ '("a" "s" "d" "f" "j" "k" "l" ";" "g" "h"
+ "q" "w" "e" "r" "t" "y" "u" "i" "p"
+ "z" "x" "c" "v" "b" "n" "m")
+ "The list of characters used when ‘switch-window-shortcut-style’ is 'qwerty'."
+ :type 'list
+ :group 'switch-window)
+
+(defcustom switch-window-shortcut-appearance 'text
+ "Switch-window shortcut's appearance."
+ :type '(choice (const :tag "Show shortcut with text" 'text)
+ (const :tag "Show shortcut with Ascii art." 'asciiart)
+ (const :tag "Show shortcut with image." 'image))
+ :group 'switch-window)
+
+(defcustom switch-window-image-directory (locate-user-emacs-file "switch-window/image")
+ "Switch-window image directory.
+If `switch-window-shortcut-appearance' set to 'image, image file
+will be found in this directory."
+ :type 'directory
+ :group 'switch-window)
+
+(defcustom switch-window-label-buffer-function
+ 'switch-window--create-label-buffer
+ "Switch-window's label buffer function.
+This function is used to prepare a temp buffer to diplay
+a window's label string, two optional arguments:
+1. window Label string will be showed in this window.
+2. buffer Label string will be inserted into this buffer.
+3. label The window's shortcut string."
+ :type 'function
+ :group 'switch-window)
+
+(defcustom switch-window-input-style 'minibuffer
+ "Use `read-event' or `read-from-minibuffer' to get user's input."
+ :type '(choice (const :tag "Get input by read-event" 'read-event)
+ (const :tag "Get input from minibuffer" 'minibuffer))
+ :group 'switch-window)
+
+(defcustom switch-window-minibuffer-shortcut nil
+ "Whether to customize the minibuffer shortcut.
+Default to no customisation (nil), which will make the minibuffer
+take whatever the last short is. If a character is specified
+it will always use that key for the minibuffer shortcut.
+
+Note: this feature only works when the value
+of `switch-window-input-style' is 'default ."
+ :type '(choice (const :tag "Off" nil)
+ (character "m"))
+ :group 'switch-window)
+
+(defcustom switch-window-auto-resize-window nil
+ "Auto resize window's size when switch to a window.
+1. If its value is t, auto resize the selected window.
+2. If its value is a function without arguments,
+ when the returned value is non-nil, auto resize
+ the selected window."
+ :type '(choice boolean function)
+ :group 'switch-window)
+
+(defcustom switch-window-default-window-size 0.7
+ "The default auto resize window's size.
+1. If its value is nil, disable auto resize feature.
+2. If its value is a number (0<x<1), resize selected window to fraction of frame size.
+3. If its value is a number (0<x<1) cons, resize selected window to
+ car% of frame width and cdr% of frame height."
+ :type '(choice (const :tag "Off" nil)
+ (float :tag "Fraction of frame size")
+ (cons
+ :tag "Fractions of frame width and height"
+ (float :tag "Fraction of frame width")
+ (float :tag "Fraction of frame height")))
+ :group 'switch-window)
+
+(defcustom switch-window-finish-hook nil
+ "A hook, run when `switch-window--then' is finishd.
+Its hook function have no arguments."
+ :group 'switch-window
+ :type 'hook)
+
+(defcustom switch-window-preferred 'default
+ "Prefer default commands or helm/ivy style commands."
+ :type '(choice (const :tag "Emacs default" 'default)
+ (const :tag "Helm" 'helm)
+ (const :tag "Ivy or Counsel" 'ivy)
+ (const :tag "Ido" 'ido))
+ :group 'switch-window)
+
+(defvar switch-window-preferred-alist
+ '((helm
+ (find-file . helm-find-files)
+ (switch-to-buffer . helm-mini))
+ (ivy
+ (find-file . counsel-find-file)
+ (switch-to-buffer . ivy-switch-buffer))
+ (ido
+ (find-file . ido-find-file)
+ (switch-to-buffer . ido-switch-buffer)
+ (dired . ido-dired)))
+ "The settings of `switch-window-preferred'.")
+
+(defvar switch-window-extra-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "i") 'switch-window-mvborder-up)
+ (define-key map (kbd "k") 'switch-window-mvborder-down)
+ (define-key map (kbd "j") 'switch-window-mvborder-left)
+ (define-key map (kbd "l") 'switch-window-mvborder-right)
+ (define-key map (kbd "b") 'balance-windows)
+ (define-key map (kbd "SPC") 'switch-window-resume-auto-resize-window)
+ map)
+ "Extra keymap for ‘switch-window’ input.
+Note: at the moment, it cannot bind commands, which will
+increase or decrease window's number, for example:
+`split-window-below' `split-window-right' `maximize'.")
+
+(defcustom switch-window-configuration-change-hook-inhibit nil
+ "Whether inhibit `window-configuration-change-hook' during ‘switch-window’."
+ :type 'boolean
+ :group 'switch-window)
+
+(defvar switch-window--temp-disable-auto-resize nil
+ "Disable auto resize window feature temporarily.")
+
+;; Fix warn when compile switch-window with emacs-no-x
+(defvar image-types)
+
+(defcustom switch-window-multiple-frames nil
+ "When non-nil, run `switch-window' across multiple frames."
+ :type 'boolean
+ :group 'switch-window)
+
+(defcustom switch-window-frame-list-function
+ 'visible-frame-list
+ "Function to get a list of frames.
+
+This function is used when `switch-window-multiple-frames' is non-nil."
+ :type 'function
+ :group 'switch-window)
+
+(defface switch-window-label
+ '((t (:inherit font-lock-builtin-face :height 3.0)))
+ "Face used by switch-window's key.")
+
+(defface switch-window-background
+ '((t (:foreground "gray40")))
+ "Face for switch-window background.")
+
+(defun switch-window--other-window-or-frame ()
+ "If `switch-window-multiple-frames' is set cycle through all visible
+windows from all frames. Call `other-window' otherwise."
+ (if switch-window-multiple-frames
+ (switch-window--select-window (next-window nil nil 'visible))
+ (other-window 1)))
+
+(defun switch-window--select-window (window)
+ "Switch to the window WINDOW. Select WINDOW's frame respecting
+`focus-follows-mouse' and `mouse-autoselect-window'."
+ (let ((new-frame (window-frame window))
+ (old-frame (selected-frame)))
+ (when (window-live-p window)
+ (select-window window)
+ (if (not (eq new-frame old-frame))
+ (select-frame-set-input-focus new-frame)))))
+
+(defun switch-window--list-keyboard-keys ()
+ "Return a list of current keyboard layout keys."
+ (cl-loop with layout = (split-string quail-keyboard-layout "")
+ for row from 1 to 4
+ nconc (cl-loop for col from 1 to 10
+ collect (nth (+ 1 (* 2 col) (* 30 row)) layout))))
+
+(defun switch-window--list-keys ()
+ "Return a list of keys to use depending on `switch-window-shortcut-style'."
+ (cl-remove-if
+ #'(lambda (key)
+ (or (and switch-window-minibuffer-shortcut
+ (equal key (char-to-string switch-window-minibuffer-shortcut)))
+ (lookup-key switch-window-extra-map key)))
+ (cond ((eq switch-window-shortcut-style 'qwerty)
+ switch-window-qwerty-shortcuts)
+ ((eq switch-window-shortcut-style 'alphabet)
+ (cl-loop for i from 0 to 25
+ collect (byte-to-string (+ (string-to-char "a") i))))
+ (t (switch-window--list-keyboard-keys)))))
+
+(defun switch-window--enumerate ()
+ "Return a list of one-letter strings to label current windows."
+ (cl-loop for w in (switch-window--list)
+ for x in (switch-window--list-keys)
+ collect (if (and switch-window-minibuffer-shortcut
+ (minibuffer-window-active-p w))
+ (char-to-string switch-window-minibuffer-shortcut)
+ x)))
+
+(defun switch-window--label (num)
+ "Return the label to use for a given window NUM."
+ (nth (- num 1) (switch-window--enumerate)))
+
+(defun switch-window--list (&optional from-current-window)
+ "List windows for current frame.
+It will start at top left unless FROM-CURRENT-WINDOW is not nil"
+ (let ((relative (or from-current-window
+ switch-window-relative))
+ (frames (if (bound-and-true-p switch-window-multiple-frames)
+ (funcall switch-window-frame-list-function)
+ (list (selected-frame)))))
+ (cl-loop for frm in (if relative
+ (cons (selected-frame)
+ (cl-remove (selected-frame) frames))
+ (cl-sort frames
+ 'switch-window--compare-frame-positions))
+ append (window-list frm nil
+ (unless (and relative
+ (equal frm (selected-frame)))
+ (frame-first-window frm))))))
+
+(defun switch-window--compare-frame-positions (frm1 frm2)
+ "Compare positions between two frames FRM1 and FRM2."
+ (cl-destructuring-bind
+ ((x1 . y1) (x2 . y2))
+ (list (frame-position frm1) (frame-position frm2))
+ (cond
+ ((< x1 x2) t)
+ ((> x1 x2) nil)
+ ((< y1 y2) t)
+ (t nil))))
+
+(defun switch-window--display-number (win num)
+ "Prepare a temp buffer to diplay NUM in the window WIN while choosing."
+ (let* ((label (switch-window--label num))
+ (buffer (get-buffer-create
+ (format " *%s: %s*"
+ label (buffer-name (window-buffer win)))))
+ (background (switch-window--window-substring win)))
+ (funcall switch-window-label-buffer-function win buffer label background)
+ (set-window-buffer win buffer switch-window-background)
+ buffer))
+
+(defun switch-window--window-substring (window)
+ "Get the buffer substring of window."
+ (let ((height (window-height)))
+ (with-current-buffer (window-buffer window)
+ (save-excursion
+ (let ((b (line-beginning-position))
+ (c (line-end-position))
+ (a (progn
+ (forward-line (* (/ height 2) -1))
+ (point)))
+ (d (progn
+ (forward-line height)
+ (point))))
+ (concat (propertize
+ (buffer-substring-no-properties a b)
+ 'face 'switch-window-background)
+ (buffer-substring b c)
+ (propertize
+ (buffer-substring-no-properties c d)
+ 'face 'switch-window-background)))))))
+
+(defun switch-window--create-label-buffer (&optional _window buffer label background)
+ "The default LABEL BUFFER create funcion."
+ (with-current-buffer buffer
+ (setq truncate-lines t)
+ (when switch-window-background
+ (insert background)
+ (goto-char (point-min)))
+ (cond
+ ((eq switch-window-shortcut-appearance 'asciiart)
+ (setq line-spacing nil)
+ (let* ((lines (split-string
+ (replace-regexp-in-string
+ "^\n" ""
+ (nth (cl-position
+ label
+ (remove "" (split-string "123456789abcdefjhijklmnopqrstuvwxyz" ""))
+ :test #'equal)
+ switch-window-asciiart))
+ "\n"))
+ (num (apply #'max (mapcar #'length lines))))
+ ;; Deal with "let: End of buffer" problem.
+ ;; Maybe not beautiful, but simple :-)
+ (goto-char (point-max))
+ (dotimes (_ 20)
+ (insert (make-string num ? ))
+ (insert "\n"))
+ (goto-char (point-min))
+ (dolist (line lines)
+ (delete-char num)
+ (insert (format (concat "%-" (number-to-string num) "s") line))
+ (insert " ")
+ (forward-line 1))))
+ ((eq switch-window-shortcut-appearance 'text)
+ (insert (propertize label 'face 'switch-window-label)))
+ ((eq switch-window-shortcut-appearance 'image)
+ (let ((types (cl-copy-seq image-types))
+ file)
+ (while types
+ (let* ((type (pop types))
+ (file1 (format "%s%s.%S"
+ (file-name-as-directory switch-window-image-directory)
+ label type)))
+ (when (file-exists-p file1)
+ (setq file file1)
+ (setq types nil))))
+ (if (and file (display-images-p))
+ (insert-image-file (expand-file-name file))
+ (insert (propertize label 'face 'switch-window-label))))))
+ (insert " ")
+ (goto-char (point-min))
+ (setq-local buffer-read-only t)
+ (setq-local show-trailing-whitespace nil)
+ buffer))
+
+(defun switch-window--jump-to-window (index)
+ "Jump to the window depend on INDEX."
+ (cl-loop for c from 1
+ for win in (switch-window--list)
+ until (= c index)
+ finally (switch-window--select-window win)))
+
+(defun switch-window--list-eobp ()
+ "Return a list of all the windows where `eobp' is currently true.
+so that we can restore that important property (think
+ auto scrolling) after switching."
+ (cl-loop for win in (switch-window--list)
+ when (with-current-buffer (window-buffer win) (eobp))
+ collect win))
+
+(defun switch-window--restore-eobp (eobp-window-list)
+ "For each window in EOBP-WINDOW-LIST move the point to end of buffer."
+ (cl-loop for win in eobp-window-list
+ do (let ((buffer (window-buffer win)))
+ (when buffer
+ (with-current-buffer buffer
+ (goto-char (point-max)))))))
+
+;;;###autoload
+(defun switch-window-then-delete ()
+ "Display an overlay in each window showing a unique key.
+In the mean time, user will be asked to choose the window deleted."
+ (interactive)
+ (switch-window--then
+ "Delete window: "
+ #'delete-window
+ #'delete-window t))
+
+(defalias 'delete-other-window 'switch-window-then-delete)
+(make-obsolete 'delete-other-window 'switch-window-then-delete
+ "switch-window version 0.2")
+
+;;;###autoload
+(defun switch-window-then-maximize ()
+ "Display an overlay in each window showing a unique key.
+In the mean time, ask user which window to maximize"
+ (interactive)
+ (switch-window--then
+ "Maximize window: "
+ #'delete-other-windows
+ #'delete-other-windows t))
+
+;;;###autoload
+(defun switch-window ()
+ "Display an overlay in each window showing a unique key.
+In the mean time, ask user for the window where move to"
+ (interactive)
+ (switch-window--then
+ "Move to window: "
+ #'switch-window--other-window-or-frame))
+
+;;;###autoload
+(defun switch-window-then-split-horizontally (arg)
+ "Select a window then split it horizontally.
+Argument ARG ."
+ (interactive "P")
+ (switch-window--then
+ "Horiz-split window: "
+ #'split-window-horizontally
+ #'split-window-horizontally arg 1))
+
+;;;###autoload
+(defun switch-window-then-split-vertically (arg)
+ "Select a window then split it vertically.
+Argument ARG ."
+ (interactive "P")
+ (switch-window--then
+ "Verti-split window: "
+ #'split-window-vertically
+ #'split-window-vertically arg 1))
+
+;;;###autoload
+(defun switch-window-then-split-below (arg)
+ "Select a window then split it with split-window-below's mode.
+TODO: Argument ARG."
+ (interactive "P")
+ (switch-window--then
+ "Below-split window: "
+ #'split-window-below
+ #'split-window-below arg 1))
+
+;;;###autoload
+(defun switch-window-then-split-right (arg)
+ "Select a window then split it with split-window-right's mode.
+TODO: Argument ARG ."
+ (interactive "P")
+ (switch-window--then
+ "Right-split window: "
+ #'split-window-right
+ #'split-window-right arg 1))
+
+;;;###autoload
+(defun switch-window-then-swap-buffer (&optional keep-focus)
+ "Swap the current window's buffer with a selected window's buffer.
+
+Move the focus on the newly selected window unless KEEP-FOCUS is
+non-nil (aka keep the focus on the current window).
+
+When a window is strongly dedicated to its buffer, this function
+won't take effect, and no buffers will be switched."
+ (interactive "P")
+ (let ((buffer1 (window-buffer))
+ (window1 (get-buffer-window))
+ buffer2 window2)
+ (if (window-dedicated-p window1)
+ (message "The current window has a dedicated buffer: `%s'" (buffer-name buffer1))
+ (switch-window)
+ (setq buffer2 (current-buffer))
+ (setq window2 (get-buffer-window))
+ (if (window-dedicated-p window2)
+ (progn
+ (select-window window1)
+ (message "The selected window has a dedicated buffer: `%s'" (buffer-name buffer2)))
+ (set-window-buffer window2 buffer1 t)
+ (set-window-buffer window1 buffer2 t)
+ (if keep-focus
+ (switch-window--select-window window1))))))
+
+;;;###autoload
+(defun switch-window-then-find-file ()
+ "Select a window, then find a file in it.
+
+Designed to replace `find-file-other-window'."
+ (interactive)
+ (switch-window--then-other-window
+ "Find file in window: "
+ #'find-file))
+
+;;;###autoload
+(defun switch-window-then-find-file-read-only ()
+ "Select a window, then find a file in it, read-only.
+
+Designed to replace `find-file-read-only-other-window'."
+ (interactive)
+ (switch-window--then-other-window
+ "Find file read-only in window: "
+ #'find-file-read-only))
+
+;;;###autoload
+(defun switch-window-then-display-buffer ()
+ "Select a window, display a buffer in it, then return.
+
+Designed to replace `display-buffer'."
+ (interactive)
+ (let ((original-window (selected-window)))
+ (switch-window--then-other-window
+ "Show buffer in window: "
+ #'switch-to-buffer)
+ (switch-window--select-window original-window)))
+
+;;;###autoload
+(defun switch-window-then-kill-buffer ()
+ "Select a window, then kill its buffer, then close it.
+
+Designed to replace `kill-buffer-and-window'."
+ (interactive)
+ (switch-window--then-other-window
+ "Window to kill: "
+ #'kill-buffer-and-window))
+
+;;;###autoload
+(defun switch-window-then-dired ()
+ "Select a window, then dired in it.
+
+Designed to replace `dired-other-window'."
+ (interactive)
+ (switch-window--then-other-window
+ "Dired in window: "
+ #'dired))
+
+;;;###autoload
+(defun switch-window-then-compose-mail ()
+ "Select a window, then start composing mail in it.
+
+Designed to replace `compose-mail-other-window'."
+ (interactive)
+ (switch-window--then-other-window
+ "Compose mail in window: "
+ #'compose-mail))
+
+(defun switch-window--get-preferred-function (function)
+ "Get the preferred FUNCTION based on `switch-window-preferred'."
+ (or (cdr (assq function
+ (cdr (assq switch-window-preferred
+ switch-window-preferred-alist))))
+ function))
+
+(defun switch-window--then-other-window (prompt function)
+ "PROMPT a question and let use select or create a window to run FUNCTION."
+ (let ((f (switch-window--get-preferred-function function)))
+ (switch-window--then
+ prompt
+ (lambda ()
+ (select-window
+ (if (one-window-p)
+ (split-window-right)
+ (next-window)))
+ (call-interactively f))
+ (lambda () (call-interactively f))
+ nil
+ 2)))
+
+(defun switch-window--then (prompt function1 &optional function2
+ return-original-window threshold)
+ "Prompt a PROMPT, let user switch to a window to do something.
+
+If the number of opened window is less than THRESHOLD,
+call FUNCTION1 in current window, otherwise, switch to
+the window assocated with the typed key, then call FUNCTION2.
+
+1. FUNCTION1 and FUNCTION2 are functions with no arguments.
+2. When RETURN-ORIGINAL-WINDOW is t, switch to original window
+ after FUNCTION2 is called.
+3. When THRESHOLD is not a number, use the value of
+ ‘switch-window-threshold’ instead."
+ (if (<= (length (switch-window--list))
+ (if (numberp threshold)
+ threshold
+ switch-window-threshold))
+ (when (functionp function1)
+ (funcall function1))
+ (let ((orig-window (selected-window))
+ (index (switch-window--prompt prompt))
+ (eobps (switch-window--list-eobp)))
+ (switch-window--jump-to-window index)
+ (when (functionp function2)
+ (funcall function2))
+ (when (and return-original-window
+ (window-live-p orig-window))
+ (switch-window--select-window orig-window))
+ (switch-window--restore-eobp eobps)))
+ (switch-window--auto-resize-window)
+ (run-hooks 'switch-window-finish-hook))
+
+(defun switch-window--get-input (prompt-message minibuffer-num eobps)
+ "Get user's input with the help of `read-event'.
+Arguments: PROMPT-MESSAGE MINIBUFFER-NUM EOBPS."
+ (let (key)
+ (while (not key)
+ (let ((input (event-basic-type
+ (read-event
+ (if minibuffer-num
+ (format "Move to window [minibuffer is %s]: "
+ (if switch-window-minibuffer-shortcut
+ (char-to-string switch-window-minibuffer-shortcut)
+ (switch-window--label minibuffer-num)))
+ prompt-message)
+ nil switch-window-timeout))))
+ (if (or (null input) (eq input 'return))
+ (progn
+ (switch-window--restore-eobp eobps)
+ (keyboard-quit)) ; timeout or RET
+ (unless (symbolp input)
+ (let* ((wchars (mapcar 'string-to-char
+ (switch-window--enumerate)))
+ (pos (cl-position input wchars))
+ (extra-function
+ (lookup-key switch-window-extra-map
+ (char-to-string input))))
+ (cond
+ (extra-function
+ (call-interactively extra-function)
+ ;; Commands in `switch-window-extra-map' mainly are window-resize commands.
+ ;; If we use these commands, theirs effects should not be override by
+ ;; auto-resize feature.
+ (unless (eq extra-function 'switch-window-resume-auto-resize-window)
+ (setq switch-window--temp-disable-auto-resize t)))
+ (pos (setq key (1+ pos)))
+ (t (switch-window--restore-eobp eobps)
+ (keyboard-quit))))))))
+ key))
+
+(defun switch-window--get-minibuffer-input (prompt-message minibuffer-num eobps)
+ "Get user's input with the help of `read-from-minibuffer'.
+Arguments: PROMPT-MESSAGE MINIBUFFER-NUM EOBPS."
+ (let (key)
+ (while (not key)
+ (let ((input (read-from-minibuffer
+ (if minibuffer-num
+ (format "Move to window [minibuffer is %s]: "
+ (if switch-window-minibuffer-shortcut
+ (char-to-string switch-window-minibuffer-shortcut)
+ (switch-window--label minibuffer-num)))
+ prompt-message)
+ nil
+ (let ((map (copy-keymap minibuffer-local-map))
+ (i ?\ ))
+ (while (< i 127)
+ (define-key map (char-to-string i)
+ (lambda ()
+ (interactive)
+ (self-insert-command 1)
+ (exit-minibuffer)))
+ (setq i (1+ i)))
+ map))))
+ (if (< (length input) 1)
+ (switch-window--restore-eobp eobps)
+ (let ((pos (cl-position input (switch-window--enumerate)
+ :test #'equal))
+ (extra-function
+ (lookup-key switch-window-extra-map input)))
+ (cond
+ (extra-function
+ (call-interactively extra-function)
+ ;; Commands in `switch-window-extra-map' mainly are window resize commands.
+ ;; If we use these commands, theirs effects should not be override by
+ ;; auto-resize feature.
+ (unless (eq extra-function 'switch-window-resume-auto-resize-window)
+ (setq switch-window--temp-disable-auto-resize t)))
+ (pos (setq key (1+ pos)))
+ (t (switch-window--restore-eobp eobps)))))))
+ key))
+
+(defun switch-window--prompt (prompt-message)
+ "Display an overlay in each window showing a unique key.
+In the mean time, prompt PROMPT-MESSAGE and let user select
+a window"
+ (let ((window-configuration-change-hook
+ (unless switch-window-configuration-change-hook-inhibit
+ window-configuration-change-hook))
+ (original-cursor (default-value 'cursor-type))
+ (eobps (switch-window--list-eobp))
+ (minibuffer-num nil)
+ (num 1)
+ key label-buffers
+ window-buffers window-margins window-points dedicated-windows)
+
+ ;; arrange so that C-g will get back to previous window configuration
+ (unwind-protect
+ (progn
+ ;; hide cursor during window selection process
+ (setq-default cursor-type nil)
+ ;; save window's buffer, point and dedicate state.
+ ;; then display label buffers in all window.
+ (dolist (win (switch-window--list))
+ (push (cons win (window-buffer win)) window-buffers)
+ (push (cons win (window-margins win)) window-margins)
+ (push (cons win (window-point win)) window-points)
+ (when (window-dedicated-p win)
+ (push (cons win (window-dedicated-p win)) dedicated-windows)
+ (set-window-dedicated-p win nil))
+ (if (minibuffer-window-active-p win)
+ (setq minibuffer-num num)
+ (push (switch-window--display-number win num) label-buffers))
+ (setq num (1+ num)))
+ ;; get user's input
+ (cond ((eq switch-window-input-style 'read-event)
+ (setq key (switch-window--get-input
+ prompt-message minibuffer-num eobps)))
+ ((member switch-window-input-style '(default minibuffer))
+ (setq key (switch-window--get-minibuffer-input
+ prompt-message minibuffer-num eobps)))))
+ ;; clean input-method-previous-message
+ (setq input-method-previous-message nil)
+ ;; restore original cursor
+ (setq-default cursor-type original-cursor)
+ ;; Restore window's buffer, point and dedicate state.
+ (dolist (w window-buffers)
+ (set-window-buffer (car w) (cdr w) t))
+ ;; Restore window's margins.
+ (dolist (w window-margins)
+ (set-window-margins (car w) (cadr w) (cddr w)))
+ (dolist (w window-points)
+ (set-window-point (car w) (cdr w)))
+ (dolist (w dedicated-windows)
+ (set-window-dedicated-p (car w) (cdr w)))
+
+ ;; remove all label-buffers, useless now.
+ ;; it's important to remove temporary label buffers after restoring original buffers
+ ;; because for protected windows it will also kill the window via `replace-buffer-in-windows''`
+ ;; according to https://www.gnu.org/software/emacs/manual/html_node/elisp/Killing-Buffers.html
+ ;; for protected window a temporary lable buffer will be the only it displays
+ ;; For example, it will kill Treemacs buffer and it's window
+ ;; thus failing to restore it if you run the killing loop before.
+ (mapc 'kill-buffer label-buffers))
+ key))
+
+(define-minor-mode switch-window-mouse-mode
+ "Enable auto resize window when switch window with mouse."
+ :global t
+ :require 'switch-window
+ (if switch-window-mouse-mode
+ (add-hook 'mouse-leave-buffer-hook
+ #'switch-window--mouse-auto-resize-window)
+ (remove-hook 'mouse-leave-buffer-hook
+ #'switch-window--mouse-auto-resize-window)))
+
+(defun switch-window--mouse-auto-resize-window ()
+ "Auto resize window when switch window with mouse."
+ (run-at-time 0.1 nil #'switch-window--auto-resize-window))
+
+(defun switch-window-resume-auto-resize-window ()
+ "Resume auto resize window feature.
+It is temporarily disabled by commands in
+`switch-window-extra-map'."
+ (interactive)
+ (setq switch-window--temp-disable-auto-resize nil))
+
+(defun switch-window--auto-resize-window ()
+ "Auto resize window's size when switch to window."
+ (when (and (not switch-window--temp-disable-auto-resize)
+ (if (functionp switch-window-auto-resize-window)
+ (funcall switch-window-auto-resize-window)
+ switch-window-auto-resize-window)
+ (not (window-minibuffer-p))
+ (not (one-window-p)))
+ (let ((arg switch-window-default-window-size)
+ n1 n2)
+ (cond ((and (numberp arg) (< 0 arg 1))
+ (setq n1 arg)
+ (setq n2 arg))
+ ((and (consp arg)
+ (numberp (car arg))
+ (numberp (cdr arg))
+ (< 0 (car arg) 1)
+ (< 0 (cdr arg) 1))
+ (setq n1 (car arg))
+ (setq n2 (cdr arg)))
+ (t (setq n1 nil)
+ (setq n2 nil)
+ (message "The value of `switch-window-default-window-size' is invalid.")))
+ (when (and n1 n2)
+ (with-selected-window (selected-window)
+ (let ((ncol (floor (- (* (frame-width) n1)
+ (window-width))))
+ (nrow (floor (- (* (frame-height) n2)
+ (window-height)))))
+ (when (window-resizable-p (selected-window) nrow)
+ (enlarge-window nrow))
+ (when (window-resizable-p (selected-window) ncol t)
+ (enlarge-window ncol t)))))))
+ (when (and switch-window-auto-resize-window
+ switch-window--temp-disable-auto-resize)
+ (message
+ (substitute-command-keys
+ "Switch-window: resume auto-resize with `\\[switch-window-resume-auto-resize-window]'"))
+ (message "")))
+
+
+(provide 'switch-window)
+;;; switch-window.el ends here