branch: master commit 7a65bfa4553b2143f9793897aadf9574f51a4c64 Author: Oleh Krehel <ohwoeo...@gmail.com> Commit: Oleh Krehel <ohwoeo...@gmail.com>
Initial import --- README.md | 47 +++++++++++++++ ace-window.el | 182 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 229 insertions(+), 0 deletions(-) diff --git a/README.md b/README.md new file mode 100644 index 0000000..47ad2b9 --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# ace-window + +**GNU Emacs package for selecting a window to switch to** + +## What and why + +I'm sure you're aware of `other-window` command. While it's great for +two windows, it quickly loses it's value when there are more windows: +you need to call it many times, and since it's not easily predictable, +you have to check each time if you're in the window that you wanted. + +Another approach is to use `windmove-left`, `windmove-up` etc. These +are fast and predictable. Their disadvantage is that they need 4 key +bindings. The default ones are shift+arrows, which are hard to reach. + +This package aims to take the speed and predictability of `windmove` +and pack it into a single key binding, similar to `other-window`. + +## Setup + +Just assign `ace-window` to a short key binding, as switching windows +is a common task. I suggest **M-p** as it's short and not bound in the +default Emacs. + +## Usage + +When there are two windows, `ace-window` will call `other-window`. If +there are more, each window will have its first character highlighted. +Pressing that character will switch to that window. Note that unlike +`ace-jump-mode`, the point position will not be changed: it's the same +behavior as that of `other-window`. + +The windows are ordered top-down, left-to-right. This means that if +you remember your window layouts, you can switch windows without even +looking at the leading char. For instance, the top left window will +always be `1`. + +## Customization + +Aside from binding `ace-window`, maybe you'd like to customize +`aw-keys` - the sequence of leading characters for each window: + + (global-set-key (kbd "M-p") 'ace-window) + (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)) + +`aw-keys` are 0-9 by default, which is reasonable, but in the setup +above, the keys are on the home row. diff --git a/ace-window.el b/ace-window.el new file mode 100644 index 0000000..69a11b0 --- /dev/null +++ b/ace-window.el @@ -0,0 +1,182 @@ +;;; ace-window.el --- Quickly switch windows using `ace-jump-mode'. -*- lexical-binding: t -*- + +;; Copyright (C) 2014 Oleh Krehel + +;; Author: Oleh Krehel <ohwoeo...@gmail.com> +;; URL: https://github.com/abo-abo/ace-window +;; Version: 0.1.0 +;; Package-Requires: ((ace-jump-mode "2.0")) +;; Keywords: cursor, window, location + +;; This file is not part of GNU Emacs + +;; This file 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, or (at your option) +;; any later version. + +;; This program 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. + +;; For a full copy of the GNU General Public License +;; see <http://www.gnu.org/licenses/>. + +;;; Commentary: +;; +;; This package uses `ace-jump-mode' machinery to switch between +;; windows. +;; +;; The main function, `ace-window' is meant to replace `other-window'. +;; If fact, when there are only two windows present, `other-window' is +;; called. If there are more, each window will have its first +;; character highlighted. Pressing that character will switch to that +;; window. Note that unlike `ace-jump-mode', the point position will +;; not be changed: only current window focus changes. +;; +;; To setup this package, just add to your ~.emacs: +;; +;; (global-set-key (kbd "M-p") 'ace-window) +;; +;; replacing "M-p" with an appropriate shortcut. +;; +;; Depending on your window usage patterns, you might want to set +;; +;; (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)) +;; +;; This way they're all on the home row, although the intuitive +;; ordering is lost. + +;;; Code: +(require 'ace-jump-mode) + +(defgroup ace-window nil + "Quickly switch current window." + :group 'convenience + :prefix "aw-") + +(defcustom aw-keys '(?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9) + "Keys for selecting window.") + +;;;###autoload +(defun ace-window () + "Use variable `ace-jump-mode' to switch windows." + (interactive) + (let ((visual-area-list + (sort (ace-jump-list-visual-area) + 'aw-visual-area<))) + (cl-case (length visual-area-list) + (0) + (1) + (2 + (other-window 1)) + (t + (let ((candidate-list + (mapcar (lambda (va) + (make-aj-position + :offset (window-start (aj-visual-area-window va)) + :visual-area va)) + visual-area-list))) + ;; make indirect buffer for those windows that show the same buffer + (setq ace-jump-recover-visual-area-list + (ace-jump-mode-make-indirect-buffer visual-area-list)) + ;; create background for each visual area + (if ace-jump-mode-gray-background + (setq ace-jump-background-overlay-list + (loop for va in visual-area-list + collect (let* ((w (aj-visual-area-window va)) + (b (aj-visual-area-buffer va)) + (ol (make-overlay (window-start w) + (window-end w) + b))) + (overlay-put ol 'face 'ace-jump-face-background) + ol)))) + ;; construct search tree and populate overlay into tree + (setq ace-jump-search-tree + (ace-jump-tree-breadth-first-construct + (length candidate-list) + (length aw-keys))) + (ace-jump-populate-overlay-to-search-tree ace-jump-search-tree + candidate-list) + (ace-jump-update-overlay-in-search-tree ace-jump-search-tree + aw-keys) + (setq ace-jump-mode " Ace - Window") + (force-mode-line-update) + ;; override the local key map + (setq overriding-local-map + (let ((map (make-keymap))) + (dolist (key-code aw-keys) + (define-key map (make-string 1 key-code) 'aw-move-window)) + (define-key map [t] 'ace-jump-done) + map)) + (add-hook 'mouse-leave-buffer-hook 'ace-jump-done) + (add-hook 'kbd-macro-termination-hook 'ace-jump-done)))))) + +(defun aw-move-window () + "Switch window based on user input." + (interactive) + (let* ((index (let ((ret (position (aref (this-command-keys) 0) + aw-keys))) + (if ret ret (length aw-keys)))) + (node (nth index (cdr ace-jump-search-tree)))) + (cond + ;; we do not find key in search tree. This can happen, for + ;; example, when there is only three selections in screen + ;; (totally five move-keys), but user press the forth move key + ((null node) + (message "No such position candidate.") + (ace-jump-done)) + ;; this is a branch node, which means there need further + ;; selection + ((eq (car node) 'branch) + (let ((old-tree ace-jump-search-tree)) + ;; we use sub tree in next move, create a new root node + ;; whose child is the sub tree nodes + (setq ace-jump-search-tree (cons 'branch (cdr node))) + (ace-jump-update-overlay-in-search-tree ace-jump-search-tree + aw-keys) + ;; this is important, we need remove the subtree first before + ;; do delete, we set the child nodes to nil + (setf (cdr node) nil) + (ace-jump-delete-overlay-in-search-tree old-tree))) + ;; if the node is leaf node, this is the final one + ((eq (car node) 'leaf) + ;; need to save aj data, as `ace-jump-done' will clean it + (let ((aj-data (overlay-get (cdr node) 'aj-data))) + (ace-jump-done) + (ace-jump-push-mark) + (run-hooks 'ace-jump-mode-before-jump-hook) + (aw-switch-to-window aj-data)) + (run-hooks 'ace-jump-mode-end-hook)) + (t + (ace-jump-done) + (error "[AceJump] Internal error: tree node type is invalid"))))) + +(defun aw-visual-area< (va1 va2) + "Return true if visual area VA1 is less than VA2. +This is determined by their respective window coordinates. +Windows are numbered top down, left to right." + (let ((e1 (window-edges (aj-visual-area-window va1))) + (e2 (window-edges (aj-visual-area-window va2)))) + (cond ((< (car e1) (car e2)) + t) + ((> (car e1) (car e2)) + nil) + ((< (cadr e1) (cadr e2)) + t)))) + +(defun aw-switch-to-window (position) + "Switch to window of `aj-position' structure POSITION." + (let ((frame (aj-position-frame position)) + (window (aj-position-window position))) + (if (and (frame-live-p frame) + (not (eq frame (selected-frame)))) + (select-frame-set-input-focus (window-frame window))) + (if (and (window-live-p window) + (not (eq window (selected-window)))) + (select-window window)))) + +(provide 'ace-window) + +;;; ace-window.el ends here