├── .gitignore ├── .gitattributes ├── BUGS ├── vimpulse.el ├── LICENSE ├── INSTALL ├── Acknowledgements ├── vimpulse-ex.el ├── CONTRIBUTE ├── README ├── Makefile ├── Header ├── vimpulse-paren-matching.el ├── vimpulse-test.el ├── NEWS ├── TODO ├── vimpulse-dependencies.el ├── vimpulse-compatibility.el ├── vimpulse-modal.el ├── vimpulse-text-object-system.el ├── vimpulse-misc-keybindings.el └── vimpulse-utils.el /.gitignore: -------------------------------------------------------------------------------- 1 | *.elc 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.el diff=lisp 2 | -------------------------------------------------------------------------------- /BUGS: -------------------------------------------------------------------------------- 1 | ;;; Bugs: 2 | 3 | ;; (We would appreciate it very much if you report bugs.) 4 | ;; 5 | ;; Known bugs: 6 | ;; 7 | ;; - Undo has problems in XEmacs. 8 | 9 | -------------------------------------------------------------------------------- /vimpulse.el: -------------------------------------------------------------------------------- 1 | (require 'vimpulse-dependencies) 2 | (require 'vimpulse-viper-function-redefinitions) 3 | (require 'vimpulse-utils) 4 | (require 'vimpulse-modal) 5 | (require 'vimpulse-ex) 6 | (require 'vimpulse-paren-matching) 7 | (require 'vimpulse-visual-mode) 8 | (require 'vimpulse-operator) 9 | (require 'vimpulse-text-object-system) 10 | (require 'vimpulse-misc-keybindings) 11 | (require 'vimpulse-compatibility) 12 | (provide 'vimpulse) 13 | 14 | ;;; vimpulse.el ends here 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ;;; License: 2 | 3 | ;; This program is free software; you can redistribute it and/or 4 | ;; modify it under the terms of the GNU General Public License as 5 | ;; published by the Free Software Foundation; either version 2 of the 6 | ;; License, or any later version. 7 | ;; 8 | ;; This program is distributed in the hope that it will be useful, but 9 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | ;; General Public License for more details. 12 | ;; 13 | ;; You should have received a copy of the GNU General Public License 14 | ;; along with this program; if not, write to the Free Software 15 | ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 16 | ;; 02111-1307, USA. 17 | 18 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | ;;; Installation: 2 | 3 | ;; If you checked out from Git but still want the single-file version, run 4 | ;; `make big' to produce `vimpulse.el' in a `bigd' directory. Then: 5 | ;; 6 | ;; 1. Put vimpulse.el somewhere in your `load-path'. 7 | ;; 8 | ;; 2. Add the following to your init file: 9 | ;; 10 | ;; (require 'vimpulse) 11 | ;; 12 | ;; If you use Windows, see 13 | ;; http://www.gnu.org/software/emacs/windows/faq3.html 14 | ;; 15 | ;; The rest is optional: 16 | ;; 17 | ;; 3. For linear undo/redo and undo branches, install 18 | ;; undo-tree.el: http://www.emacswiki.org/emacs/UndoTree 19 | ;; 20 | ;; Just place it in `load-path', and Vimpulse will load it 21 | ;; automatically. On XEmacs, you can use redo.el instead. 22 | ;; 23 | ;; Vimpulse automatically enables Viper. You can temporarily disable 24 | ;; Viper (and Vimpulse) with the "C-z" key. 25 | 26 | -------------------------------------------------------------------------------- /Acknowledgements: -------------------------------------------------------------------------------- 1 | ;;; Acknowledgements: 2 | 3 | ;; Special thanks to Brad Beveridge, the original author of Vimpulse. 4 | ;; 5 | ;; Thanks to: 6 | ;; 7 | ;; cppjavaperl 8 | ;; Fabian Brännström 9 | ;; Frank Fischer 10 | ;; John 11 | ;; John J Foerch 12 | ;; José Alfredo Romero L. 13 | ;; Mieszko 14 | ;; rhinoryan 15 | ;; Rick Sladkey, author of rect-mark.el 16 | ;; Ryoichi Kanetaka 17 | ;; Samuel Padgett 18 | ;; Štěpán Němec 19 | ;; Stephen Bach 20 | ;; Stian S. 21 | ;; Tim Harper 22 | ;; Toby Cubitt 23 | ;; Wang Xin 24 | ;; Weihua Jiang 25 | ;; 26 | ;; and all the other people who have sent in bug reports and feedback. 27 | ;; Also, thanks to Michael Kifer and Viper's contributors. 28 | ;; 29 | ;; We love patches. Would you like to see your name here? 30 | ;; Please send code and/or documentation patches to the maintainer. 31 | ;; Ideas, comments, and test results are appreciated too. 32 | 33 | -------------------------------------------------------------------------------- /vimpulse-ex.el: -------------------------------------------------------------------------------- 1 | ;;;; Ex commands 2 | 3 | (require 'vimpulse-dependencies) ; ex-token-alist, v-want-quit-like-Vim 4 | 5 | (defvar vimpulse-extra-ex-commands 6 | '(("b" "buffer") 7 | ("bdelete" (vimpulse-kill-current-buffer)) 8 | ("bnext" "next") 9 | ("clo" "close") 10 | ("close" (delete-window)) 11 | ("on" "only") 12 | ("only" (delete-other-windows)) 13 | ("split" (split-window)) 14 | ("syntax" (global-font-lock-mode)) 15 | ;; Emacs and Vim use inverted naming conventions for splits 16 | ("vsplit" (split-window-horizontally))) 17 | "Extra Ex commands, added to `ex-token-alist' when Vimpulse loads.") 18 | 19 | (when vimpulse-want-quit-like-Vim 20 | (add-to-list 'vimpulse-extra-ex-commands 21 | '("quit" (save-buffers-kill-emacs)))) 22 | 23 | (defun vimpulse-kill-current-buffer () 24 | "Kill the current buffer." 25 | (interactive) 26 | ;; if the buffer was initiated by emacsclient, call `server-edit' 27 | ;; from server.el to avoid "Buffer still has clients" message 28 | (if (and (boundp 'server-buffer-clients) 29 | (fboundp 'server-edit) 30 | server-buffer-clients) 31 | (server-edit) 32 | (kill-buffer nil))) 33 | 34 | (dolist (entry vimpulse-extra-ex-commands) 35 | (setq ex-token-alist 36 | (delete (assoc (car entry) ex-token-alist) ex-token-alist)) 37 | (push entry ex-token-alist)) 38 | 39 | (provide 'vimpulse-ex) 40 | -------------------------------------------------------------------------------- /CONTRIBUTE: -------------------------------------------------------------------------------- 1 | So you want to contribute? That's great, thank you! Here are some tips that 2 | will help you make us cry with delight at the very sight of your patch: 3 | 4 | Patch format 5 | ------------ 6 | - It's best if the patches are directly processable by Git (such as when using 7 | `git-format-patch', `git-send-email' or the ---8<--- scissors trick). 8 | - A nice way to have Git produce better diff hunk headers when working with 9 | Lisp code is to add this to your config: 10 | 11 | [diff "lisp"] 12 | xfuncname = "^\\(.*$" 13 | 14 | With this and an appropriate diff attribute definition (provided by the 15 | .gitattributes file in the repository), it will be immediately visible 16 | which top level Lisp form a particular hunk pertains to. 17 | 18 | Commit messages 19 | --------------- 20 | - The first line is a commit summary, usually a single sentence in imperative 21 | short enough to fit in a mail subject line. It starts with a capital letter, 22 | but does not end with a period. 23 | - After that and a blank line comes the commit message with a free form 24 | explanation (optional for trivial patches). 25 | 26 | General coding guidelines 27 | ------------------------- 28 | - Keep it inside 80 columns when possible. 29 | - Avoid trailing whitespace (Git can help you with that). 30 | - Avoid tabs (set `indent-tabs-mode' to nil). 31 | - If you're making bigger changes, it's a good idea to try byte-compiling 32 | Vimpulse even though you normally don't (the compiler sometimes catches 33 | issues not obvious when running uncompiled). 34 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ;;; Usage: 2 | 3 | ;; To use Visual mode, press "v" in vi (command) mode. Then use the 4 | ;; motion commands to expand the selection. Press "d" to delete, "c" 5 | ;; to change, "r" to replace, or "y" to copy. You can use "p" to 6 | ;; paste. For Line selection, press "V" instead of "v"; then you can 7 | ;; copy and paste whole lines. For Block selection, press "C-v"; now 8 | ;; you can copy and paste the selected rectangle. In Block selection, 9 | ;; you may use "I" or "A" to insert or append text before or after the 10 | ;; selection on each line. 11 | ;; 12 | ;; Other features: 13 | ;; 14 | ;; Vimpulse supports text objects: "daw", "daW", "das", "dap", "dab", 15 | ;; "daB", "da(", "da[", "da{", "da<", "da"", "da'", as well as "diw", 16 | ;; "diW", "dis", etc. To change an object: "caw", "cas", etc. To yank 17 | ;; it: "yaw", "yas", etc. To select it: "vaw", "vas", etc. 18 | ;; 19 | ;; The extended documentation is still in its early stages, but you 20 | ;; can view drafts at: http://gitorious.org/vimpulse/pages/Home 21 | ;; 22 | ;; The documentation that comes with Vim -- which is online at 23 | ;; http://vimdoc.sf.net/ -- may also be helpful. 24 | ;; 25 | ;; Tips: 26 | ;; 27 | ;; - Vimpulse makes "C-r" run Redo in command mode, but you can 28 | ;; still get reverse isearch by pressing "C-s" and then "C-r". 29 | ;; 30 | ;; - To change the color of search, add something like the following 31 | ;; to .emacs: 32 | ;; 33 | ;; (set-face-foreground isearch nil) 34 | ;; (set-face-background isearch "lightgoldenrod2") 35 | ;; 36 | ;; - To change the color of Visual mode (`zmacs-region' in XEmacs): 37 | ;; 38 | ;; (set-face-background 'region "blue") 39 | ;; 40 | ;; For more tips, see: http://gitorious.org/vimpulse/pages/Tips 41 | 42 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL = /bin/sh 2 | 3 | .SUFFIXES: # no implicit rules 4 | 5 | EMACS = emacs 6 | BATCHFLAGS = -batch -Q 7 | LOADFLAGS = -eval "(add-to-list 'load-path \".\")" 8 | BYTECOMPCMD = $(EMACS) $(BATCHFLAGS) $(LOADFLAGS) -f batch-byte-compile 9 | 10 | ELFILES = vimpulse-compatibility.el vimpulse-dependencies.el vimpulse.el \ 11 | vimpulse-ex.el vimpulse-misc-keybindings.el vimpulse-modal.el \ 12 | vimpulse-operator.el vimpulse-paren-matching.el vimpulse-test.el \ 13 | vimpulse-text-object-system.el vimpulse-utils.el \ 14 | vimpulse-viper-function-redefinitions.el vimpulse-visual-mode.el 15 | 16 | BIGSRC = Header \ 17 | INSTALL \ 18 | README \ 19 | NEWS \ 20 | Acknowledgements \ 21 | BUGS \ 22 | TODO \ 23 | LICENSE \ 24 | vimpulse-dependencies.el \ 25 | vimpulse-viper-function-redefinitions.el \ 26 | vimpulse-utils.el \ 27 | vimpulse-modal.el \ 28 | vimpulse-ex.el \ 29 | vimpulse-paren-matching.el \ 30 | vimpulse-visual-mode.el \ 31 | vimpulse-operator.el \ 32 | vimpulse-text-object-system.el \ 33 | vimpulse-misc-keybindings.el \ 34 | vimpulse-compatibility.el \ 35 | vimpulse.el 36 | 37 | BIGDIR = bigd 38 | 39 | .PHONY: all 40 | all: elisp 41 | 42 | .PHONY: elisp 43 | elisp: $(ELFILES) 44 | $(BYTECOMPCMD) $(ELFILES) 45 | 46 | .PHONY: big 47 | big: $(BIGSRC) 48 | test -d $(BIGDIR) || mkdir $(BIGDIR) 49 | sed "/(\(provide\|require\|declare-function\) \('\|\)vimpulse-/d" $(BIGSRC) > $(BIGDIR)/vimpulse.el 50 | 51 | .SUFFIXES: .el .elc 52 | %.elc: %.el 53 | $(BYTECOMPCMD) $< 54 | 55 | .PHONY: test 56 | test: 57 | $(EMACS) -Q $(LOADFLAGS) -l vimpulse-test.el -f test-interactive-suite 58 | 59 | .PHONY: test-big 60 | test-big: 61 | cd $(BIGDIR) && $(EMACS) -Q $(LOADFLAGS) -l ../vimpulse-test.el -f test-interactive-suite 62 | 63 | emacs: clean 64 | $(EMACS) -Q -L . --eval "(require 'vimpulse)" & 65 | 66 | .PHONY: clean 67 | clean: 68 | rm -f *.elc 69 | rm -rf $(BIGDIR) 70 | -------------------------------------------------------------------------------- /Header: -------------------------------------------------------------------------------- 1 | ;;; vimpulse.el --- emulates Vim's most useful features -*- coding: utf-8 -*- 2 | 3 | ;; Copyright (C) 2007 Brad Beveridge 4 | ;; Copyright (C) 2007, 2009 Alessandro Piras 5 | ;; Copyright (C) 2008 Frank Fischer 6 | ;; Copyright (C) 2009 Jason Spiro 7 | ;; Copyright (C) 2010 Vegard Øye 8 | ;; Copyright (C) 2010 Štěpán Němec 9 | ;; 10 | ;; Author: Brad Beveridge et al. 11 | ;; Maintainer: Vegard Øye 12 | ;; Please send bug reports to the mailing list (see below). 13 | ;; Created: 23 Aug 2007 14 | ;; Version: 0.5+git 15 | ;; Keywords: emulations, viper 16 | ;; Human-Keywords: vim, visual-mode, rsi, ergonomics, emacs pinky 17 | ;; URL: http://www.emacswiki.org/emacs/vimpulse.el 18 | ;; Gitorious project: http://gitorious.org/vimpulse 19 | ;; For the latest development version, clone the repo with: 20 | ;; git clone git://gitorious.org/vimpulse/vimpulse.git 21 | ;; Mailing list: 22 | ;; Subscribe: http://tinyurl.com/implementations-list 23 | ;; Newsgroup: nntp://news.gmane.org/gmane.emacs.vim-emulation 24 | ;; Archives: http://dir.gmane.org/gmane.emacs.vim-emulation 25 | ;; You don't have to subscribe. We usually reply within a few 26 | ;; days and CC our replies back to you. 27 | ;; Related: viper.el, viper-in-more-modes.el 28 | ;; 29 | ;; Thanks to our old maintainers: 30 | ;; Alessandro Piras 31 | ;; Jason Spiro 32 | ;; We'll miss you as maintainers :) 33 | ;; 34 | ;; This file is not part of GNU Emacs. 35 | 36 | ;;; Commentary: 37 | 38 | ;; Vimpulse emulates Vim's most popular features, like Visual mode 39 | ;; and text objects. Vimpulse is a set of modifications to Viper, the 40 | ;; standard library that emulates vi. Vimpulse is not a minor mode; 41 | ;; as soon as it is loaded, Viper will start working in a more 42 | ;; Vim-like way. 43 | ;; 44 | ;; Vimpulse is under active development. It works quite well with 45 | ;; GNU Emacs 22.3 and 23.2, as well as XEmacs 21.4.22. Patches and 46 | ;; feature requests are welcome (see also the file CONTRIBUTE in the 47 | ;; repository). 48 | 49 | -------------------------------------------------------------------------------- /vimpulse-paren-matching.el: -------------------------------------------------------------------------------- 1 | ;;;; Paren matching 2 | 3 | ;; When highlighting matching parentheses, Emacs matches the closing 4 | ;; parenthesis before the cursor, instead of under it (like in Vim). 5 | ;; This code provides an alternate parenthesis matching function 6 | ;; used when Viper is in vi (command) mode, so that the parenthesis 7 | ;; under the cursor is matched. This makes it possible to visually 8 | ;; inspect a closing parenthesis at the end of the line. 9 | ;; 10 | ;; In Insert mode, Emacs' scheme is deemed best and kept as is. 11 | ;; 12 | ;; This code is LOADED BY DEFAULT. 13 | ;; To avoid loading it, set `vimpulse-enhanced-paren-matching' to nil 14 | ;; in your .emacs before loading Vimpulse. 15 | 16 | (require 'vimpulse-dependencies) ; vimpulse-setq etc. 17 | 18 | (declare-function vimpulse-delete-overlay "vimpulse-utils" (overlay)) 19 | 20 | (defvar show-paren-delay) 21 | (defvar vimpulse-paren-overlay-open nil 22 | "Overlay used to highlight the opening paren.") 23 | 24 | (defvar vimpulse-paren-overlay-close nil 25 | "Overlay used to highlight the closing paren.") 26 | 27 | ;; Load and enable paren.el if available. 28 | (unless (featurep 'paren) 29 | (condition-case nil 30 | (require 'paren) 31 | (error nil))) 32 | (and (fboundp 'show-paren-mode) 33 | (not (vimpulse-custom-value-p 'show-paren-mode)) 34 | ;; fast paren-matching 35 | (vimpulse-setq show-paren-delay 0) 36 | (show-paren-mode 1)) 37 | 38 | (defun vimpulse-paren-open-p (&optional pos) 39 | "Return t if the character at point (or POS) is an opening paren." 40 | (setq pos (or pos (point))) 41 | (let ((class (syntax-after pos))) 42 | (when class 43 | (setq class (syntax-class class)) 44 | (= class 4)))) 45 | 46 | (defun vimpulse-paren-close-p (&optional pos) 47 | "Return t if the character at point (or POS) is an closing paren." 48 | (setq pos (or pos (point))) 49 | (let ((class (syntax-after pos))) 50 | (when class 51 | (setq class (syntax-class class)) 52 | (= class 5)))) 53 | 54 | (defun vimpulse-paren-match (&optional pos) 55 | "Return the position of possible matching paren at point (or POS). 56 | If not a paren, return `not-a-paren'. If not found, return nil." 57 | (setq pos (or pos (point))) 58 | (condition-case nil 59 | (cond 60 | ((vimpulse-paren-open-p pos) 61 | (1- (scan-sexps pos 1))) 62 | ((vimpulse-paren-close-p pos) 63 | (scan-sexps (1+ pos) -1)) 64 | (t 65 | 'not-a-paren)) 66 | (error nil))) 67 | 68 | (defun vimpulse-paren-match-p (pos1 pos2) 69 | "Return t if POS1 and POS2 are matching characters. 70 | Checks the characters at position POS1 and POS2 and returns t 71 | if they are matching characters (in a paren-match meaning), 72 | nil otherwise." 73 | (let ((class1 (car (syntax-after pos1))) 74 | (match1 (cdr (syntax-after pos1))) 75 | (class2 (car (syntax-after pos2))) 76 | (match2 (cdr (syntax-after pos2)))) 77 | (or (eq match1 (char-after pos2)) 78 | (eq match2 (char-after pos1)) 79 | (eq match1 match2)))) 80 | 81 | (defun vimpulse-paren-highlight (face &optional pos) 82 | "Highlight the paren at point (or POS) with FACE." 83 | (setq pos (or pos (point))) 84 | (let ((ovl (if (vimpulse-paren-open-p pos) 85 | vimpulse-paren-overlay-open 86 | vimpulse-paren-overlay-close))) 87 | (viper-overlay-put ovl 'face face) 88 | (viper-move-overlay ovl pos (1+ pos)))) 89 | 90 | ;; FIXME: this description sucks 91 | (defun vimpulse-paren-highlight-pair (&optional pos) 92 | "Highlight paren pair. 93 | Highlights the paren at point (or POS) and eventual matching 94 | or mismatched paren." 95 | (setq pos (or pos (point))) 96 | (let ((match (vimpulse-paren-match pos))) 97 | (cond 98 | ((not match) 99 | (vimpulse-paren-highlight 'show-paren-mismatch pos)) 100 | ((eq match 'not-a-paren) 101 | (vimpulse-delete-overlay vimpulse-paren-overlay-open) 102 | (vimpulse-delete-overlay vimpulse-paren-overlay-close)) 103 | ((/= pos (vimpulse-paren-match match)) 104 | (vimpulse-paren-highlight 'show-paren-mismatch pos)) 105 | ((vimpulse-paren-match-p pos match) 106 | (vimpulse-paren-highlight 'show-paren-match pos) 107 | (vimpulse-paren-highlight 'show-paren-match match)) 108 | (t 109 | (vimpulse-paren-highlight 'show-paren-mismatch pos) 110 | (vimpulse-paren-highlight 'show-paren-mismatch match))))) 111 | 112 | (defadvice show-paren-function (around vimpulse-paren activate) 113 | "Use custom highlighting if `vimpulse-enhanced-paren-matching' is t." 114 | ;; define overlays if they don't exist 115 | (cond 116 | (vimpulse-enhanced-paren-matching 117 | (unless (viper-overlay-live-p vimpulse-paren-overlay-open) 118 | (setq vimpulse-paren-overlay-open 119 | (viper-make-overlay (point) (point) nil t nil) 120 | vimpulse-paren-overlay-close 121 | (viper-make-overlay (point) (point) nil t nil)) 122 | (vimpulse-delete-overlay vimpulse-paren-overlay-open) 123 | (vimpulse-delete-overlay vimpulse-paren-overlay-close)) 124 | (cond 125 | ;; Viper not in Insert, Replace or Emacs state 126 | ((and (not (eq viper-current-state 'insert-state)) 127 | (not (eq viper-current-state 'replace-state)) 128 | (not (eq viper-current-state 'emacs-state)) 129 | show-paren-mode viper-mode) 130 | ;; safely delete the overlays used by `show-paren-function' 131 | ;; and call our custom function instead 132 | (and (viper-overlay-live-p show-paren-overlay) 133 | (vimpulse-delete-overlay show-paren-overlay)) 134 | (and (viper-overlay-live-p show-paren-overlay-1) 135 | (vimpulse-delete-overlay show-paren-overlay-1)) 136 | (vimpulse-paren-highlight-pair)) 137 | ;; Viper in Insert mode 138 | (t 139 | ;; delete the overlays used by our custom function 140 | (vimpulse-delete-overlay vimpulse-paren-overlay-open) 141 | (vimpulse-delete-overlay vimpulse-paren-overlay-close) 142 | ad-do-it))) 143 | (t 144 | ad-do-it))) 145 | 146 | (provide 'vimpulse-paren-matching) 147 | -------------------------------------------------------------------------------- /vimpulse-test.el: -------------------------------------------------------------------------------- 1 | ;; vimpulse-test.el --- unit tests for Vimpulse -*- coding: utf-8 -*- 2 | 3 | ;; This file is for developers. It runs a couple of unit tests on 4 | ;; Vimpulse. To load it, add this line to .emacs: 5 | ;; 6 | ;; (require 'vimpulse-test) 7 | ;; 8 | ;; This file is NOT part of Vimpulse proper. 9 | 10 | (require 'test-framework) 11 | 12 | (defun vimpulse-test-buffer (body) 13 | "Execute BODY in a temporary buffer. 14 | The buffer contains the familiar *scratch* message, 15 | with point at position 1 and in vi (command) state." 16 | (let ((kill-ring kill-ring) 17 | (kill-ring-yank-pointer kill-ring-yank-pointer) 18 | x-select-enable-clipboard 19 | message-log-max) 20 | (with-temp-buffer 21 | (save-window-excursion 22 | (switch-to-buffer-other-window (current-buffer)) 23 | (buffer-enable-undo) 24 | (save-excursion 25 | (insert ";; This buffer is for notes you don't want to save, \ 26 | and for Lisp evaluation.\n;; If you want to create a file, visit \ 27 | that file with C-x C-f,\n;; then enter the text in that file's own \ 28 | buffer.\n")) 29 | (viper-mode) 30 | (funcall body))))) 31 | 32 | (defsuite test-utils-suite 33 | "Test suite for vimpulse-utils.el. 34 | This line is not included in the report." 35 | :run t 36 | :setup ((require 'vimpulse)) 37 | (test-augment-keymap 38 | "Test `vimpulse-augment-keymap'." 39 | (let (augment-alist map) 40 | (setq map (make-sparse-keymap)) 41 | (define-key map "a" 'foo) 42 | (define-key map "b" 'bar) 43 | (define-key map "c" 'baz) 44 | (setq augment-alist 45 | '(([?a] . wibble) 46 | ([?b] . wobble) 47 | ([?c] . wubble) 48 | ([?d] . flob))) 49 | (vimpulse-augment-keymap map augment-alist) 50 | (assert-eq 51 | "Augment keymap carefully." 52 | (lookup-key map "a") 'foo 53 | (lookup-key map "b") 'bar 54 | (lookup-key map "c") 'baz 55 | (lookup-key map "d") 'flob) 56 | (vimpulse-augment-keymap map augment-alist t) 57 | (assert-eq 58 | "Augment keymap forcefully." 59 | (lookup-key map "a") 'wibble 60 | (lookup-key map "b") 'wobble 61 | (lookup-key map "c") 'wubble 62 | (lookup-key map "d") 'flob))) 63 | (test-truncate 64 | "Test `vimpulse-truncate'." 65 | (assert-equal 66 | "Positive numbers." 67 | (vimpulse-truncate [a b c] 0) [] 68 | (vimpulse-truncate [a b c] 1) [a] 69 | (vimpulse-truncate [a b c] 2) [a b] 70 | (vimpulse-truncate [a b c] 3) [a b c] 71 | (vimpulse-truncate [a b c] 4) [a b c]) 72 | (assert-equal 73 | "Negative numbers." 74 | (vimpulse-truncate [a b c] -1) [a b] 75 | (vimpulse-truncate [a b c] -2) [a] 76 | (vimpulse-truncate [a b c] -3) [] 77 | (vimpulse-truncate [a b c] -4) []) 78 | (assert-equal 79 | "Limit cases." 80 | (vimpulse-truncate [] 0) [] 81 | (vimpulse-truncate [] 3) [])) 82 | (test-filter-list 83 | "Test `vimpulse-filter-list'." 84 | (let* ((foo '(a nil b nil c)) 85 | (bar (cdr foo)) 86 | (baz (cdr bar))) 87 | (assert-equal 88 | "Filter whole list." 89 | (vimpulse-filter-list foo 'null) '(a b c)) 90 | (assert-equal 91 | "Filter first element." 92 | (vimpulse-filter-list foo 'null bar) '(a nil b nil c)) 93 | (assert-equal 94 | "Filter first and second element." 95 | (vimpulse-filter-list foo 'null baz) '(a b nil c))))) 96 | 97 | ;; These tests are largely interactive (and heavy), so don't run them 98 | ;; automatically; add (test-interactive-suite) to .emacs and/or run 99 | ;; `M-x test-interactive-suite' manually. 100 | (defsuite test-interactive-suite 101 | "Interactive test suite for Vimpulse." 102 | :setup ((require 'vimpulse)) 103 | :fixture vimpulse-test-buffer 104 | (test-visual-delete-word 105 | "Visually delete a word." 106 | (execute-kbd-macro "wved") 107 | (assert-string= 108 | (buffer-substring 1 47) 109 | ";; buffer is for notes you don't want to save")) 110 | 111 | (test-visual-delete-line 112 | "Visually delete a line." 113 | (execute-kbd-macro "Vd") 114 | (assert-string= 115 | (buffer-string) 116 | ";; If you want to create a file, visit that file with C-x C-f, 117 | ;; then enter the text in that file's own buffer.\n")) 118 | 119 | (test-visual-delete-block 120 | "Visually delete `;; ' prefix." 121 | (execute-kbd-macro "\C-vjjlld") 122 | (assert-string= 123 | (buffer-string) 124 | "This buffer is for notes you don't want to save, and for \ 125 | Lisp evaluation. 126 | If you want to create a file, visit that file with C-x C-f, 127 | then enter the text in that file's own buffer.\n")) 128 | 129 | (test-visual-delete-block-with-counts 130 | "Visually delete `;; ' prefix, using counts." 131 | (execute-kbd-macro "\C-v2l2jd") 132 | (assert-string= 133 | (buffer-string) 134 | "This buffer is for notes you don't want to save, and for \ 135 | Lisp evaluation. 136 | If you want to create a file, visit that file with C-x C-f, 137 | then enter the text in that file's own buffer.\n")) 138 | 139 | (test-end-of-Word 140 | "Test E (`vimpulse-end-of-Word')." 141 | (execute-kbd-macro "wr+d0yEP") 142 | (assert-string= 143 | "Yank and put +his before point." 144 | (buffer-substring 1 52) 145 | "+his+his buffer is for notes you don't want to save") 146 | (execute-kbd-macro "EEEwcwfoo") 147 | (assert-string= 148 | "Move to \"for\" and change to \"foo\"." 149 | (buffer-substring 1 52) 150 | "+his+his buffer is foo notes you don't want to save") 151 | (execute-kbd-macro "$Ewcw`foo'") 152 | (assert-string= 153 | "Move to next line and change \"If\" to \"`foo'\"." 154 | (buffer-substring 79 113) 155 | ";; `foo' you want to create a file") 156 | (execute-kbd-macro "By2EP") 157 | (assert-string= 158 | "Yank two words and put before point." 159 | (buffer-substring 79 122) 160 | ";; `foo' you`foo' you want to create a file") 161 | (execute-kbd-macro "EEEEEwcw(bar)") 162 | (assert-string= 163 | "Change a single-letter word." 164 | (buffer-substring 97 126) 165 | "you want to create (bar) file") 166 | (execute-kbd-macro "bldiW") 167 | (assert-string= 168 | "Delete inner Word." 169 | (buffer-substring 97 121) 170 | "you want to create file")) 171 | 172 | (test-visual-replace 173 | "Replace with Visual selection." 174 | (execute-kbd-macro "wvjra") 175 | (assert-string= 176 | "Visually replace parts of two lines." 177 | (buffer-string) 178 | ";; aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ 179 | aaaaaaaaaaaaaaaaaaaaa 180 | aaaaf you want to create a file, visit that file with C-x C-f, 181 | ;; then enter the text in that file's own buffer.\n") 182 | (execute-kbd-macro "jjvral.l.") 183 | (assert-string= 184 | "Replace a character and repeat for subsequent characters." 185 | (buffer-substring 141 191) 186 | ";; aaan enter the text in that file's own buffer.\n") 187 | (execute-kbd-macro "$ra") 188 | (assert-string= 189 | "Replace at end of line." 190 | (buffer-substring 141 191) 191 | ";; aaan enter the text in that file's own buffera\n")) 192 | 193 | (test-change-undo 194 | "Change a word and undo." 195 | (let ((vimpulse-want-change-undo t)) 196 | (execute-kbd-macro "wcwfoou") 197 | (assert-string= 198 | (buffer-substring 1 51) 199 | ";; This buffer is for notes you don't want to save")))) 200 | 201 | (provide 'vimpulse-test) 202 | 203 | ;;; vimpulse-test.el ends here 204 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | ;;; News: 2 | 3 | ;; Version 0.5 [2010-09-12] 4 | ;; [vegard_oye at hotmail.com:] 5 | ;; - [NEW] "/" and "?" use isearch. Matches are highlighted while 6 | ;; typing; also, isearch shortcuts apply, like "M-c" to toggle 7 | ;; case-sensitivity. Type "C-h k /" for more details. 8 | ;; - [NEW] :undolist or :ul shows the undo history as a tree. This 9 | ;; uses undo-tree.el, which replaces redo.el for undo/redo 10 | ;; (see installation instructions). 11 | ;; - [NEW] Keybinding functions: `vimpulse-global-set-key', 12 | ;; `vimpulse-local-set-key', `vimpulse-define-key'. State bindings 13 | ;; can now be assigned to both minor and major Emacs modes, 14 | ;; which is useful for writing extensions. 15 | ;; - [NEW] Keys: "gi", "g0", "g$", "]P", "]p". 16 | ;; - [NEW] Lower-case marks are buffer-local -- thanks, Štěpán Němec. 17 | ;; - [NEW] If `viper-auto-indent' is t, then "RET" extends the 18 | ;; comment prefix to the next line (with `comment-indent-new-line' 19 | ;; from newcomment.el). 20 | ;; - [NEW] "C-w" has its own prefix map, `vimpulse-window-map'. 21 | ;; - [FIX] Replace mode's appearance is now more similar to Vim's. 22 | ;; - [FIX] Byte compilation errors. 23 | ;; - [FIX] Various bugs submitted to the mailing list -- 24 | ;; thanks, everyone. 25 | ;; - Operator commands are now defined with the 26 | ;; `vimpulse-define-operator' macro. 27 | ;; - To pacify the compiler, all variables are initially defined 28 | ;; in one place. 29 | ;; - For readability and consistency, "Yoda conditions" are 30 | ;; universally banned: e.g., (= var 0), not (= 0 var). 31 | ;; - "Change Log" is renamed to "News". 32 | ;; 33 | ;; Version 0.4 [2010-04-26] 34 | ;; [vegard_oye at hotmail.com:] 35 | ;; - [NEW] Operator-Pending mode: the cursor's appearance 36 | ;; changes temporarily after "y", "d", "c", etc. 37 | ;; - [NEW] Motion type system: one can change how a motion is 38 | ;; "interpreted" with "v", "V" and "C-v". For example, "dvj" will 39 | ;; delete a characterwise range (the default is linewise). 40 | ;; - [NEW] Keys: "gq", "gu", "gU", "g~", "g?". 41 | ;; - [NEW] Keybinding functions: `vimpulse-omap' and 42 | ;; `vimpulse-omap-local'. 43 | ;; - [FIX] Vimpulse's text objects handle whitespace 44 | ;; more like Vim's. 45 | ;; - [FIX] Various bugs submitted to the mailing list -- 46 | ;; thanks, everyone. 47 | ;; - The code for applying an "operator" like "d" to a "motion" 48 | ;; like "w" is completely rewritten. Operators are simple 49 | ;; to define (with `vimpulse-range'), and can be applied 50 | ;; to regular Emacs movement commands as well. 51 | ;; - The text objects have been redefined in terms of the new 52 | ;; framework. They are implemented as selection commands; 53 | ;; see the `vimpulse-define-text-object' macro for details. 54 | ;; - The code for adding Viper states is generalized. 55 | ;; Both Visual mode and Operator-Pending mode are 56 | ;; defined with the `vimpulse-define-state' macro. 57 | ;; - The comments use a more conventional format: ;;;; for major 58 | ;; headings (one per file), ;;; for subsections (within each file), 59 | ;; ;; for individual pieces of code and ; for trailing comments. 60 | ;; This is easier to maintain and complies with section D.7 of 61 | ;; the GNU Emacs Lisp Reference Manual. 62 | ;; 63 | ;; Version 0.3.1 [2010-03-09] 64 | ;; [vegard_oye at hotmail.com:] 65 | ;; - [NEW] Emacs-compatible Visual selection. 66 | ;; It is now a Viper state proper, with a user map 67 | ;; and a major mode extension map. 68 | ;; [NEW] Visual keys: "u", "U", "~", ">", "<", "J", "O", "gv" -- 69 | ;; thanks, Frank Fischer. 70 | ;; - [NEW] Movement keys: "C-o", "C-i", "C-w hjkl", "gb", "gd", "+", "_". 71 | ;; - [NEW] Keybinding functions: `vimpulse-map', 72 | ;; `vimpulse-imap' and `vimpulse-vmap'. 73 | ;; - [NEW] Backspace in Replace mode restores text. 74 | ;; - [NEW] Basic vi navigation in help buffers. 75 | ;; - [NEW] Vimpulse has its own customization group. 76 | ;; - [FIX] Improved text objects support, including Visual mode. 77 | ;; - [FIX] Various bugs listed at EmacsWiki or submitted to the 78 | ;; mailing list or bug tracker -- thanks. 79 | ;; - All Vimpulse bindings are now in `viper-vi-basic-map', 80 | ;; leaving `viper-vi-global-user-map' for the user. 81 | ;; The same is true of Visual mode. 82 | ;; - Easier installation. rect-mark.el is no longer needed, 83 | ;; nor is cl.el. 84 | ;; - All tabs are replaced by spaces. 85 | ;; - The file encoding is UTF-8. 86 | ;; [laynor at gmail.com:] 87 | ;; - Added some small fixes, and promoted the experimental stuff to 88 | ;; stable, as it seems to work well and not loading it caused 89 | ;; problems. 90 | ;; 91 | ;; Version 0.3.0 [2009-07-03] 92 | ;; [laynor at gmail.com:] 93 | ;; - [NEW] Register support on text object commands. 94 | ;; - [NEW] Issuing ":" in Visual mode has a behavior closer 95 | ;; to Vim's. 96 | ;; [jasonspiro3 at gmail.com:] 97 | ;; - [FIX] The Enter key now does what it should do -- insert a 98 | ;; newline -- even when longlines-mode is on. 99 | ;; - Comment changes. 100 | ;; 101 | ;; Version 0.2.6.9 [2009-06-24] 102 | ;; [laynor at gmail.com:] 103 | ;; - [FIX & NEW] Text objects support fixed and integrated with Viper. 104 | ;; Now count works (e.g., you can do "3caw" and it works correctly), 105 | ;; and it's possible to repeat the commands with ".". 106 | ;; 107 | ;; Version 0.2.6.8 [2009-06-22] 108 | ;; [laynor at gmail.com:] 109 | ;; - [NEW] Text object support: paren blocks, sentences, word, Words, 110 | ;; quoted expressions, paragraphs. Delete and change commands. 111 | ;; Example commands: "diw", "ci(", "das", etc. 112 | ;; - [FIX] It's now possible to exit Visual mode by pressing 113 | ;; "ESC" or "^[". 114 | ;; 115 | ;; Version 0.2.6.7 116 | ;; [jasonspiro3 at gmail.com:] 117 | ;; - No code changes. 118 | ;; - Fixed up "thanks" section below to mention Mieszko 119 | ;; 's full name. He wrote a small patch 120 | ;; which was included long ago. I must have forgotten to include it 121 | ;; in the changelog. 122 | ;; 123 | ;; Version 0.2.6.6 124 | ;; [laynor at gmail.com:] 125 | ;; - Fixed pasting in Visual mode, works like in Vim now 126 | ;; (experimental, see point 6 of installation instructions). 127 | ;; 128 | ;; Version 0.2.6.5 129 | ;; [laynor at gmail.com:] 130 | ;; - Fixed some major suckage with the change command. Still alpha, 131 | ;; comments welcome. To use it, see the installation instructions, 132 | ;; point 6 (it's still experimental). 133 | ;; - Cleaned namespace, hope there are no hidden bugs. 134 | ;; - Fixed loading on Emacs snapshot. 135 | ;; 136 | ;; Version 0.2.6.4 137 | ;; [laynor at gmail.com:] 138 | ;; - This can probably be considered a major release. 139 | ;; - [FIX & NEW] Rewritten Visual mode, "v" and "V" variants (no 140 | ;; changes to Visual Block still). It does not use the region like 141 | ;; before: highlighting is done through overlays, and the region is 142 | ;; set inside the command code before calling the Viper commands. 143 | ;; "=" in Visual mode calls `vimpulse-visual-indent-command'. The 144 | ;; Visual mode (apart from Block mode) looks and feels like Vim. 145 | ;; - [NEW] Enhanced paren-matching. Moving the cursor on a closing 146 | ;; paren in Normal mode now highlights the opening paren. 147 | ;; - [NEW] Pressing "RET" in Insert mode automatically indents 148 | ;; the new line. 149 | ;; - [NEW] "^[" works. 150 | ;; - [FIX] "a" leaves the cursor in the same location as it was 151 | ;; before (it advanced the cursor 1 character before -- 152 | ;; `viper-exit-insert-state's fault). 153 | ;; - [FIX] "cW" doesn't suck anymore at the end of a line. 154 | ;; 155 | ;; Version 0.2.6.3: 156 | ;; [frank.fischer at s2001.tu-chemnitz.de:] 157 | ;; - Support more Visual Block mode features: insert, append, delete, 158 | ;; yank, change. 159 | ;; - Change some Vimpulse and Viper functions to handle Block mode 160 | ;; properly. 161 | ;; - Update documentation to reflect Visual Block mode. 162 | ;; - The "=" key in Visual mode calls `indent-region'. 163 | ;; 164 | ;; Version 0.2.6.2: 165 | ;; [jasonspiro3 at gmail.com:] 166 | ;; - Improved XEmacs compatibility. 167 | ;; - Small documentation improvements. 168 | ;; 169 | ;; Version 0.2.6.1: 170 | ;; [jasonspiro3 at gmail.com:] 171 | ;; - Removed duplicate definition of `vimpulse-detect-mark-deactivate' 172 | ;; and duplicate `add-hook' call to add the hook. I must have added 173 | ;; the extra copies by accident when doing my last big merge; now 174 | ;; they are gone. 175 | ;; 176 | ;; Version 0.2.6.0: 177 | ;; [jasonspiro3 at gmail.com:] 178 | ;; - Merged a patch for the function that powers "*" and "#". Based on 179 | ;; Ryoichi's patch and a cleaned-up version of Weihua's patch -- 180 | ;; thanks. Now "*" and "#" will search for entire symbol at point, 181 | ;; including underscores, not just word at point. 182 | ;; - TODO addition. 183 | ;; 184 | ;; Version 0.2.5.1: 185 | ;; [jasonspiro3 at gmail.com:] 186 | ;; - Redefined viper-adjust-undo to do nothing. This way, in Insert 187 | ;; mode, typing then moving the cursor then typing more counts as 188 | ;; two separately undoable actions instead of one. Thanks to Weihua 189 | ;; JIANG and to max_ from IRC #emacs for the idea. 190 | ;; - Small extra TODO. 191 | ;; 192 | ;; Version 0.2.5.0: 193 | ;; [jasonspiro3 at gmail.com:] 194 | ;; I've ignored my local changes for too long. Here they are: 195 | ;; - Added keybindings from a Usenet post by Samuel Padgett. 196 | ;; - Made change ("cw", etc.) commands work more like Vim (my code). 197 | ;; - I removed (setq ex-cycle-other-window nil); although it is very 198 | ;; useful, it merely works around a problem with Viper. I plan to 199 | ;; discuss it with the Viper maintainer instead. 200 | ;; - Other changes and bugfixes from various people. 201 | ;; 202 | ;; Version 0.2.0.3: 203 | ;; [jasonspiro3 at gmail.com:] 204 | ;; - Added Brad's `viper-jump-to-tag-at-point'. 205 | ;; 206 | ;; Version 0.2.0.2: 207 | ;; [jasonspiro3 at gmail.com:] 208 | ;; - Small "C-w" keys and doc fixes. 209 | ;; 210 | ;; Version 0.2.0.1: 211 | ;; [cppjavaperl:] 212 | ;; - Added support for Visual Block mode (i.e., rectangle selection). 213 | ;; - Made "C-p" look for matches PRIOR to the cursor and added "C-n" 214 | ;; binding to look for matches BEFORE the cursor. This works more 215 | ;; like Vim does. 216 | ;; [jasonspiro3 at gmail.com:] 217 | ;; - Since Vimpulse has no website, I added a prominent pointer at 218 | ;; the top to the installation instructions. 219 | ;; 220 | ;; Version 0.2.0.0: Brad merged in several changes, including: 221 | ;; - Exit Visual mode when the mark deactivates. 222 | ;; - Changed the window manipulation to be global. 223 | ;; - Added "gf" (goto file at point). 224 | ;; - Added "\C-]" and "\C-t", tag jump & pop. 225 | ;; - Added a helper function for defining keys. 226 | ;; - Commented out `show-paren-function', what is it meant to do? 227 | ;; 228 | ;; Version 0.1.0.1: No code changes. Small documentation changes, 229 | ;; including updates on moving-left bug. 230 | ;; 231 | ;; Version 0.1: Initial release. 232 | 233 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | ;;; Development plans: 2 | 3 | ;; The design plan for Vimpulse has been to only emulate features that 4 | ;; are in Vim. This is a neat guideline, and Vimpulse should certainly 5 | ;; strive to be neat. But it overlooks the fact that Vim is almost as 6 | ;; extensible an editor as Emacs. 7 | ;; 8 | ;; Extensibility requires transparency. Much of Vimpulse's code is at 9 | ;; present convoluted and difficult to understand. This is partly due 10 | ;; to Vimpulse's inconsistent dependence upon Viper. Disentangling 11 | ;; Vimpulse from Viper would go a long way to improve matters. If 12 | ;; nothing else, a bug in Vimpulse is infinitely preferable to one 13 | ;; outside it -- the former can, after all, be fixed. 14 | ;; 15 | ;; Another priority is for Vimpulse to integrate well with standard 16 | ;; Emacs functionality. This is not always possible, and various 17 | ;; "Vimpulse specifics" get invented as a result. The less Vimpulse 18 | ;; requires, and the less it invents, the better. If there must be 19 | ;; red tape, it should be justified in some way -- preferably by 20 | ;; a tangible, major feature. 21 | ;; 22 | ;; Refactoring is likely to introduce bugs and should happen on a 23 | ;; separate "development" branch. Whenever that branch matures, it 24 | ;; can be merged into the "master" branch, where bugfixes and minor 25 | ;; improvements go in the meantime. 26 | 27 | ;;; Development goals: 28 | 29 | ;; - Clean up Visual mode's code. There are some good ideas in there, 30 | ;; but they are lost in a tangle of "just-so" rules. In particular, 31 | ;; the concept of a "range" encompasses more than expansion and 32 | ;; contraction: ranges can be measured (a line range of 2 lines), 33 | ;; described ("Deleted 2 lines") and recreated another place in the 34 | ;; buffer (select 2 lines about point). A `vimpulse-define-range' 35 | ;; macro would let one define all these properties at once, and then 36 | ;; the three Visual submodes could just point to the relevant range 37 | ;; type: char -> inclusive, line -> line, block -> block. (The range 38 | ;; type might even be customizable; for instance, char -> exclusive 39 | ;; would give Emacs-like character selection.) 40 | ;; 41 | ;; - Remove Vimpulse's dependency on Viper. 42 | ;; 43 | ;; - Rewrite the motions. Since motions like "j" and "k" have a 44 | ;; motion type of "line" -- i.e., they behave linewise in 45 | ;; Operator-Pending mode -- a `vimpulse-define-motion' macro 46 | ;; should be used. (However, its docstring should stress that 47 | ;; any movement command is a valid motion, and point to a 48 | ;; function for changing the motion type.) 49 | ;; 50 | ;; - Although the standard motions don't need it, more 51 | ;; facilities for letting a motion control the operator 52 | ;; calling it could be useful with regard to extensions. 53 | ;; Presently, a motion may set `vimpulse-inhibit-operator' 54 | ;; to t to disable the operator altogether. A special 55 | ;; function call could allow the motion to execute the 56 | ;; operator code inside itself, Viper-like, so that the 57 | ;; motion could execute code after the operator code. 58 | ;; 59 | ;; - Rewrite the copy/paste system. The right way to implement 60 | ;; line pasting is not to investigate whether the text ends with 61 | ;; a newline, but to tag it with an appropriate `yank-handler' 62 | ;; property; similarly for block pasting. (XEmacs does not 63 | ;; support `yank-handler' natively, but awareness of this could 64 | ;; be built into the paste commands.) "M-y" (`yank-pop') may 65 | ;; need to be advised with regard to cursor position. 66 | ;; 67 | ;; - Rewrite the repeat system. The current system is text-based, 68 | ;; like Vim's: the text between the entry point and the exit 69 | ;; point is repeated. A simpler and more straightforward 70 | ;; implementation would be to remember the actual keystrokes -- 71 | ;; in effect turning every insertion into a keyboard macro. 72 | ;; (Thus, for example, it is no longer necessary to "measure" 73 | ;; and "recreate" Visual selections; the keystrokes do the job.) 74 | ;; However, there are some pitfalls to this approach: certain 75 | ;; keys, such as "M-/" (`dabbrev-expand'), may behave 76 | ;; differently in other places in the buffer. Thus, the repeat 77 | ;; history may need to record some commands as "insertions" 78 | ;; rather than keystrokes. 79 | ;; 80 | ;; - Rewrite the state system. This is already halfway done. What 81 | ;; remains is to define vi (command) state, Insert state and 82 | ;; Replace state (and Emacs state?) in terms of 83 | ;; `vimpulse-define-state', and add code for enabling vi state 84 | ;; in all buffers. 85 | ;; 86 | ;; - `vimpulse-define-state' should provide an option for 87 | ;; specifying the cursor. Actually, two options: one for the 88 | ;; general case and one when running in the terminal. The 89 | ;; user could then associate different cursor colors with 90 | ;; different states, since Emacs is unable to change the 91 | ;; cursor shape in the terminal. 92 | ;; 93 | ;; - Furthermore, `vimpulse-define-state' could be simplified 94 | ;; considerably. :diehard-mode? :kbd-map? 95 | ;; :global-modifier-local-user-map? In no way are all of 96 | ;; these necessary. Since `vimpulse-define-key' creates 97 | ;; extra maps as needed, ideally each state needs only 98 | ;; correspond to one keymap. 99 | ;; 100 | ;; - Each state should have an entry hook and an exit hook. 101 | ;; For example, while the `dabbrev-expand' thing that Viper 102 | ;; does when one hits ESC is horrible in the hardcoded case, 103 | ;; it could very well go into a hook as a user 104 | ;; customization. 105 | ;; 106 | ;; - Rewrite Ex. This is probably the last thing that needs to be 107 | ;; done, as it is quite independent from the other parts of 108 | ;; Viper. Vimpulse's Ex should integrate with Emacs: all that 109 | ;; can be done with "M-x" and "M-:" should be possible with ":". 110 | ;; For example, one could type ":sort-lines" just as well as 111 | ;; "M-x sort-lines", or ":(setq foo bar)" instead of 112 | ;; "M-: (setq foo bar)". This would help unburden the overloaded 113 | ;; ESC key in terminal mode. 114 | ;; 115 | ;; - Throw away old code. If there is an immediate payoff to 116 | ;; refactoring, this is it. Most of the stuff in 117 | ;; vimpulse-compatibility.el can be purged by making such buffers 118 | ;; come up in a "Vim light" mode. Additional adjustments can be made 119 | ;; on a per-mode basis with `vimpulse-define-key'. 120 | 121 | ;;; Undecided development questions: 122 | 123 | ;; These are mostly outdated. 124 | ;; 125 | ;; - Make sure I have added all stuff in Brad's Viper additions and 126 | ;; from my collection, then start documenting already. Once there 127 | ;; are even the simplest of docs (a nice keymap), people will have a 128 | ;; far easier time using Vimpulse and so I bet more will contribute. 129 | ;; 130 | ;; - Folding. This should be implemented as a separate Lisp library 131 | ;; usable for even non-Viper users. Which foldmethods to do first? 132 | ;; I personally only use foldmethod=marker, and even that rarely. 133 | ;; 134 | ;; - i_C-(I forgot the letter) should do (copy-from-above-command 1) 135 | ;; from misc.el. 136 | ;; 137 | ;; - Add :set spell / :set nospell that uses flyspell-mode. 138 | ;; 139 | ;; - Add support for tabs.el, a tabs mode that works sensibly (get it 140 | ;; from Emacs Lisp List). 141 | ;; - Minimum needed: :tabedit, :tabnext, :tabprevious. 142 | ;; - Since I'm emulating Vim, emulate its tab pages feature. So a 143 | ;; tab page should be able to hold one or more buffers. 144 | ;; 145 | ;; - Add Customize option to let users stop "C-r" from being Redo? 146 | ;; 147 | ;; - Copy more features from Brad's work in darcs and from vimpact 148 | ;; into Vimpulse. 149 | ;; 150 | ;; - Doc: look in Google chat log, find description of one-char-off 151 | ;; bug, see if it applies to this or to the not-yet-released 152 | ;; viper-x, and if to this, mention under Bugs. 153 | ;; 154 | ;; - Doc: list all new keys (and maybe all differences from Viper) in 155 | ;; Usage section. 156 | ;; 157 | ;; - Doc: describe all new keys in Usage section; can look at Vim 158 | ;; manual for ideas. 159 | ;; 160 | ;; - Modify how tramp works so it also automatically handles URLs 161 | ;; typed in the netrw syntax, e.g., http:// etc. But first ask tramp 162 | ;; upstream if they could please make those changes themselves. 163 | ;; 164 | ;; - Improve "CTRL-O" for jumping back in the jumplist and "CTRL-I" for 165 | ;; jumping forwards (for undoing one "CTRL-O"). The global mark ring 166 | ;; is not what I want. I wonder if Emacs' tags functionality allows 167 | ;; a jumplist. I wonder if Viper does tags like nvi does. 168 | ;; - Try code.google.com/p/ejumplist/source/browse/trunk/jumplist.el 169 | ;; 170 | ;; - On my PC (I run Ubuntu), if you start plain Vim and then press 171 | ;; "CTRL-O" many times, it starts opening recently opened files. Is 172 | ;; that useful? Should Vimpulse have persistent jump table 173 | ;; functionality like that, and if so, should it use recentf or 174 | ;; Vim's .viminfo file or some tag functionality in Emacs? How will 175 | ;; it interact with the fact that in Emacs, it's not traditional to 176 | ;; suddenly close files without warning? 177 | ;; 178 | ;; - Make sentence movement work like in Vim. I wonder if this can be 179 | ;; done by setting Viper options. 180 | ;; - In Vim, according to :help sentence, end of sentence is: 181 | ;; - ".", "?", or "!" 182 | ;; - then (optionally) one or more """, "'", ")", and "]" 183 | ;; characters 184 | ;; - then a newline, space, or tab. 185 | ;; - A paragraph or section boundary is also a sentence 186 | ;; boundary, but I bet Viper handles that, and if it doesn't, 187 | ;; it should. 188 | ;; - A paragraph begins after each truly empty line (no 189 | ;; whitespace chars on it) or after certain col-1 nroff 190 | ;; macros. A sentence begins after a form feed (^L), or 191 | ;; certain nroff macros, in column 1. 192 | ;; - The characters "{" and "}" sometimes affect paragraph 193 | ;; definitions. See :help paragraph. 194 | ;; - In Viper, on the other hand, I bet sentences are like in vi, 195 | ;; where tabs aren't whitespace, and you need at least two spaces 196 | ;; after the punctuation mark. 197 | ;; 198 | ;; - E-mail ridip and ask him for his Vimpulse 199 | ;; contribs and his DVORAK stuff. 200 | ;; 201 | ;; - E-mail to Tromey for upload into ELPA? We'd have to redo this 202 | ;; when a new major version comes out. Or maybe we should just 203 | ;; contribute some auto-ELPA-management code. By the way, should we 204 | ;; move Vimpulse into CVS somewhere? 205 | ;; 206 | ;; - Maybe merge all feature requests that anyone has ever sent into a 207 | ;; "Feature requests" section here. 208 | ;; 209 | ;; - In Vimpulse, like in real Vim, "C-r" only does Redo in vi (command) 210 | ;; mode; in Insert mode it does something else. (In Vimpulse that 211 | ;; "something else" is reverse isearch.) Should it do reverse 212 | ;; isearch in Insert mode too? 213 | ;; 214 | ;; - I want to allow buffer-switching without using the "C-x" key, since 215 | ;; "C-x b RET" an extremely large amount of times per day is 216 | ;; uncomfortable for my right pinky, which presses RET. There's 217 | ;; already :b, which seems to just invoke `switch-to-buffer'. Is this 218 | ;; right? Is it bad if I make Vimpulse emulate set autowrite=on 219 | ;; then write new multi-buffer code? What should the code's user 220 | ;; interface be like? I really should switch back to Vim for a day, 221 | ;; learn more about how it deals with multiple buffers at once (and 222 | ;; maybe also with tab pages) and emulate whatever of Vim's is most 223 | ;; convenient. What do you think of all the above? 224 | ;; - Update: IIRC, :set hidden lets you switch buffers w/o saving 225 | ;; - Update from Sebastien Rocca Serra: :set wildmenu plus 226 | ;; tab-completion makes :b very pleasant to use when you have 227 | ;; 50+ buffers open. Wildmenu is almost like iswitchb or ido. 228 | ;; - I wonder how well that stuff works with just a few buffers open. 229 | ;; 230 | ;; - Simulate Vim's set virtualedit=onemore option to make "C-x C-e" 231 | ;; possible without first advancing to next line? 232 | ;; 233 | ;; - Would it be bad to edit users' .viminfo files without asking 234 | ;; permission, or should some variable have to be customized on to do 235 | ;; such a thing? 236 | ;; 237 | ;; - Is there any need to implement Vim's new 238 | ;; [count]dk-can-go-past-top-of-file-without-error functionality (to 239 | ;; me, no need) or any related functionality? 240 | ;; 241 | ;; - What to do about XEmacs? It doesn't ship with woman. I wonder 242 | ;; if woman is in some XEmacs package? 243 | 244 | -------------------------------------------------------------------------------- /vimpulse-dependencies.el: -------------------------------------------------------------------------------- 1 | ;;; Code: 2 | 3 | ;;; Version 4 | (defconst vimpulse-version "0.5+git" 5 | "The current version of Vimpulse") 6 | 7 | (defun vimpulse-version () 8 | (interactive) 9 | (message "Vimpulse version is %s" vimpulse-version)) 10 | 11 | ;; load Viper 12 | (defvar viper-mode t) 13 | (defvar viper-inhibit-startup-message t) 14 | (defvar viper-expert-level 5) 15 | (defvar viper-want-ctl-h-help t) 16 | (defvar viper-search-wrap-around t) 17 | (require 'viper) 18 | 19 | ;; load undo-tree.el if available, with redo.el as fall-back 20 | (unless (featurep 'undo-tree) 21 | (condition-case nil 22 | (require 'undo-tree) 23 | (error (condition-case nil 24 | (require 'redo) 25 | (error nil))))) 26 | (and (fboundp 'global-undo-tree-mode) 27 | (global-undo-tree-mode 1)) 28 | 29 | ;; load goto-chg.el if available 30 | (condition-case nil 31 | (require 'goto-chg) 32 | (error nil)) 33 | 34 | ;;; Customization group for Vimpulse 35 | 36 | (defgroup vimpulse nil 37 | "Vim emulation within Emacs." 38 | :group 'emulations 39 | :link '(custom-group-link "viper") 40 | :prefix 'vimpulse-) 41 | 42 | (defcustom vimpulse-want-change-state nil 43 | "Whether commands like \"cw\" invoke Replace state, vi-like. 44 | The default is to delete the text and enter Insert state, 45 | like in Vim." 46 | :group 'vimpulse 47 | :type 'boolean) 48 | 49 | (defcustom vimpulse-want-change-undo t 50 | "Whether commands like \"cw\" are undone in a single step. 51 | On by default." 52 | :group 'vimpulse 53 | :type 'boolean) 54 | 55 | (defcustom vimpulse-want-C-u-like-Vim nil 56 | "Whether C-u scrolls like in Vim, off by default." 57 | :group 'vimpulse 58 | :type 'boolean) 59 | 60 | (defcustom vimpulse-want-C-i-like-Vim t 61 | "Whether C-i jumps forward like in Vim, on by default." 62 | :group 'vimpulse 63 | :type 'boolean) 64 | 65 | (defcustom vimpulse-want-quit-like-Vim t 66 | "Whether :q quits the editor like in Vim, on by default." 67 | :group 'vimpulse 68 | :type 'boolean) 69 | 70 | (defcustom vimpulse-want-Viper-checkout nil 71 | "Whether Viper handles versioning, off by default." 72 | :group 'vimpulse 73 | :type 'boolean) 74 | 75 | (defcustom vimpulse-enhanced-paren-matching t 76 | "Enhanced matching of parentheses, on by default." 77 | :group 'vimpulse 78 | :type 'boolean) 79 | 80 | (defcustom vimpulse-want-operator-pending-cursor t 81 | "Whether the cursor changes in Operator-Pending mode, on by default." 82 | :group 'vimpulse 83 | :type 'boolean) 84 | 85 | (defcustom vimpulse-visual-block-untabify nil 86 | "Whether Block mode may change tabs to spaces for fine movement. 87 | Off by default." 88 | :type 'boolean 89 | :group 'vimpulse-visual) 90 | 91 | (defcustom vimpulse-want-vi-keys-in-apropos t 92 | "Whether to use vi keys in Apropos mode, on by default." 93 | :group 'vimpulse 94 | :type 'boolean) 95 | 96 | (defcustom vimpulse-want-vi-keys-in-buffmenu t 97 | "Whether to use vi keys in Buffer menu, on by default." 98 | :group 'vimpulse 99 | :type 'boolean) 100 | 101 | (defcustom vimpulse-want-vi-keys-in-dired t 102 | "Whether to use vi keys in Dired mode, on by default." 103 | :group 'vimpulse 104 | :type 'boolean) 105 | 106 | (defcustom vimpulse-want-vi-keys-in-Info t 107 | "Whether to use vi keys in Info mode, on by default." 108 | :group 'vimpulse 109 | :type 'boolean) 110 | 111 | (defcustom vimpulse-want-vi-keys-in-help t 112 | "Whether to use vi keys in Help mode, on by default." 113 | :group 'vimpulse 114 | :type 'boolean) 115 | 116 | (defcustom vimpulse-fold-level 0 117 | "Default fold level." 118 | :type 'integer 119 | :group 'vimpulse) 120 | 121 | ;; the secrets discovered from untold diggings among 122 | ;; the ruins of Customize code 123 | (defun vimpulse-custom-value-p (symbol) 124 | "Non-nil if SYMBOL has a customized value." 125 | (or (get symbol 'customized-value) 126 | (get symbol 'customized-face) 127 | (get symbol 'saved-value))) 128 | 129 | (defmacro vimpulse-setq-custom (sym val &rest body) 130 | "Set the customized value of SYM to VAL." 131 | `(progn 132 | (prog1 (setq ,sym ,val) ; return VAL 133 | (when (get ',sym 'custom-autoload) 134 | (custom-load-symbol ',sym)) 135 | (put ',sym 'customized-value (list (custom-quote ,val)))) 136 | ,(when body 137 | `(vimpulse-setq-custom ,@body)))) 138 | 139 | (defmacro vimpulse-setq-custom-default (symbol value &rest body) 140 | "Set the customized default value of SYMBOL to VALUE." 141 | `(progn 142 | (prog1 ,value ; return VALUE 143 | (when (get ',symbol 'custom-autoload) 144 | (custom-load-symbol ',symbol)) 145 | (put ',symbol 'standard-value (list (custom-quote ,value)))) 146 | ,(when body 147 | `(vimpulse-setq-custom-default ,@body)))) 148 | 149 | (defmacro vimpulse-setq (sym val &rest body) 150 | "Set SYM to VAL, defaults included, unless SYM is customized. 151 | SYM is unquoted. Returns VAL." 152 | `(progn 153 | (cond 154 | ;; customized value: just set custom standard value 155 | ((vimpulse-custom-value-p ',sym) 156 | (vimpulse-setq-custom-default ,sym ,val)) 157 | ;; customized variable: set custom and regular values 158 | ((custom-variable-p ',sym) 159 | (vimpulse-setq-custom-default ,sym ,val) 160 | (vimpulse-setq-custom ,sym ,val) 161 | (setq-default ,sym ,val) 162 | (setq ,sym ,val)) 163 | ;; regular variable; set default and local values 164 | (t 165 | (setq-default ,sym ,val) 166 | (setq ,sym ,val))) 167 | ,@(when body 168 | `((vimpulse-setq ,@body))))) 169 | 170 | ;;; Declare and/or initialize variables 171 | 172 | (defvar isearch-forward) 173 | (defvar isearch-lazy-highlight-end) 174 | (defvar isearch-lazy-highlight-last-string) 175 | (defvar isearch-lazy-highlight-start) 176 | (defvar isearch-lazy-highlight-wrapped) 177 | (defvar isearch-regexp) 178 | (defvar isearch-string) 179 | (defvar killed-rectangle nil) ; rect.el 180 | (defvar undo-tree-visualizer-map) 181 | (defvar woman-use-own-frame) 182 | (defvar woman-use-topic-at-point) 183 | 184 | (defvar ex-token-alist) ; viper-ex.el 185 | 186 | (defvar vimpulse-viper-movement-cmds 187 | '(viper-backward-Word viper-backward-char viper-backward-paragraph 188 | viper-backward-sentence viper-backward-word 189 | viper-beginning-of-line viper-command-argument 190 | viper-digit-argument viper-end-of-Word viper-end-of-word 191 | viper-exec-mapped-kbd-macro viper-find-char-backward 192 | viper-find-char-forward viper-forward-Word viper-forward-char 193 | viper-forward-paragraph viper-forward-sentence viper-forward-word 194 | viper-goto-char-backward viper-goto-char-forward viper-goto-eol 195 | viper-goto-line viper-line-to-bottom viper-line-to-middle 196 | viper-line-to-top viper-next-line viper-previous-line 197 | viper-scroll-down-one viper-scroll-down viper-scroll-up 198 | viper-scroll-up-one viper-window-bottom viper-window-middle 199 | viper-window-top vimpulse-end-of-previous-word 200 | vimpulse-goto-first-line vimpulse-goto-definition 201 | vimpulse-goto-line vimpulse-search-backward-for-symbol-at-point 202 | vimpulse-search-forward-for-symbol-at-point vimpulse-jump-backward 203 | vimpulse-jump-forward vimpulse-visual-toggle-char 204 | vimpulse-visual-toggle-line vimpulse-visual-toggle-block) 205 | "List of Viper/Vimpulse movement commands.") 206 | 207 | (defvar vimpulse-core-movement-cmds 208 | '(viper-backward-char 209 | viper-next-line 210 | viper-previous-line 211 | viper-forward-char 212 | viper-ex) 213 | "List of Viper \"core\" movement commands. 214 | These should be present in every mode, to avoid confusion.") 215 | 216 | (defvar vimpulse-mark-list nil 217 | "List of mark positions to jump to with `vimpulse-jump-forward'. 218 | They are stored as markers, the current position first: 219 | 220 | (car vimpulse-mark-list) = current position (last popped) 221 | (cdr vimpulse-mark-list) = future positions (previously popped) 222 | (cadr vimpulse-mark-list) = next position (to jump to) 223 | 224 | In other words, a sort of \"reverse mark ring\": marks that are 225 | popped off the mark ring, are collected here.") 226 | 227 | (viper-deflocalvar vimpulse-local-marks-alist nil 228 | "Association list of local marks. 229 | Entries have the form (CHAR (FILE . POS)), where POS is a marker 230 | or a character position.") 231 | 232 | (defvar vimpulse-global-marks-alist nil 233 | "Association list of global marks. 234 | Entries have the form (CHAR (FILE . POS)), where POS is a marker 235 | or a character position.") 236 | 237 | (viper-deflocalvar vimpulse-replace-alist nil 238 | "Alist of characters overwritten in Replace mode. 239 | Used by `vimpulse-replace-backspace' to restore text. 240 | The format is (POS . CHAR).") 241 | 242 | (viper-deflocalvar vimpulse-exit-point nil 243 | "Like `viper-insert-point', but when exiting Insert mode.") 244 | 245 | (defvar vimpulse-operator-remap-map (make-sparse-keymap) 246 | "FIXME.") 247 | 248 | (defvar vimpulse-operator-remap-alist nil 249 | "Association list of command remappings in Operator-Pending mode.") 250 | 251 | (defvar vimpulse-this-operator nil 252 | "Current operator. 253 | In general, motions and operators are orthogonal, with some exceptions: 254 | \"cw\" and \"dw\" work on slightly different ranges, for example. 255 | Motions can check this variable if they need to know what 256 | operator receives their range. See also `vimpulse-this-motion'.") 257 | 258 | (defvar vimpulse-this-motion nil 259 | "Current motion. 260 | In general, motions and operators are orthogonal, with some exceptions: 261 | \"cc\" may indent the current line while \"cw\" may not, for example. 262 | Operators may check this variable if they need to know what 263 | motion produced the current range. See also `vimpulse-this-operator'.") 264 | 265 | (defvar vimpulse-this-count nil 266 | "Current count (operator count times motion count).") 267 | 268 | (defvar vimpulse-this-motion-type nil 269 | "Current motion type. 270 | May be `block', `line', `inclusive', `exclusive' or nil.") 271 | 272 | (defvar vimpulse-last-motion-type nil 273 | "Last repeated range type. 274 | May be `block', `line', `inclusive', `exclusive' or nil.") 275 | 276 | (defvar vimpulse-last-operator nil 277 | "Last repeated operator. 278 | Used by `vimpulse-operator-repeat'.") 279 | 280 | (defvar vimpulse-last-motion nil 281 | "Last repeated motion. 282 | Used by `vimpulse-operator-repeat'.") 283 | 284 | (defvar vimpulse-block-orientation 'left 285 | "Orientation of single-column blocks. 286 | `left' if beg-col < end-col; `right' if beg-col > end-col.") 287 | 288 | (defvar vimpulse-block-func nil 289 | "Function to use for repeated block insertion.") 290 | 291 | (defvar vimpulse-movement-cmds 292 | '(backward-char backward-list backward-paragraph backward-sentence 293 | backward-sexp backward-up-list backward-word beginning-of-buffer 294 | beginning-of-defun beginning-of-line beginning-of-visual-line 295 | cua-cancel digit-argument down-list end-of-buffer end-of-defun 296 | end-of-line end-of-visual-line exchange-point-and-mark 297 | forward-char forward-list forward-paragraph forward-sentence 298 | forward-sexp forward-word keyboard-quit mouse-drag-region 299 | mouse-save-then-kill mouse-set-point mouse-set-region 300 | move-beginning-of-line move-end-of-line next-line previous-line 301 | scroll-down scroll-up undo universal-argument up-list 302 | vimpulse-end-of-previous-word vimpulse-goto-definition 303 | vimpulse-goto-first-line vimpulse-goto-line vimpulse-goto-mark 304 | vimpulse-goto-mark-and-skip-white vimpulse-visual-block-rotate 305 | vimpulse-visual-exchange-corners vimpulse-visual-forward-char 306 | vimpulse-visual-goto-eol vimpulse-visual-reselect 307 | vimpulse-visual-restore vimpulse-visual-toggle-block 308 | vimpulse-visual-toggle-char vimpulse-visual-toggle-line 309 | viper-backward-Word viper-backward-char viper-backward-paragraph 310 | viper-backward-sentence viper-backward-word 311 | viper-beginning-of-line viper-digit-argument viper-end-of-Word 312 | viper-end-of-word viper-exec-mapped-kbd-macro 313 | viper-find-char-backward viper-find-char-forward 314 | viper-forward-Word viper-forward-char viper-forward-paragraph 315 | viper-forward-sentence viper-forward-word viper-goto-char-backward 316 | viper-goto-char-forward viper-goto-eol viper-goto-line 317 | viper-goto-mark viper-goto-mark-and-skip-white viper-insert 318 | viper-intercept-ESC-key viper-line-to-bottom viper-line-to-middle 319 | viper-line-to-top viper-next-line viper-paren-match 320 | viper-previous-line viper-repeat-find viper-repeat-find-opposite 321 | viper-scroll-screen viper-scroll-screen-back viper-search-Next 322 | viper-search-backward viper-search-forward viper-search-next 323 | viper-window-bottom viper-window-middle viper-window-top) 324 | "List of commands that move point. 325 | If listed here, the region is not expanded to the 326 | Visual selection before the command is executed.") 327 | 328 | (defvar vimpulse-newline-cmds 329 | '(cua-copy-region cua-cut-region cua-delete-region delete-region 330 | exchange-point-and-mark execute-extended-command kill-region 331 | kill-ring-save vimpulse-Put-and-indent vimpulse-put-and-indent 332 | vimpulse-visual-exchange-corners viper-Put-back viper-put-back) 333 | "Non-operator commands needing trailing newline in Visual Line mode. 334 | In most cases, it's more useful not to include this newline in 335 | the region acted on.") 336 | 337 | (defvar vimpulse-search-prompt nil 338 | "String to use for vi-like searching.") 339 | 340 | (defvar vimpulse-auxiliary-modes nil 341 | "List of Emacs modes with state bindings. 342 | The topmost modes have the highest priority.") 343 | 344 | (defvar vimpulse-auxiliary-modes-alist 345 | '((vi-state . viper-vi-auxiliary-modes) 346 | (insert-state . viper-insert-auxiliary-modes) 347 | (replace-state . viper-replace-auxiliary-modes) 348 | (emacs-state . viper-emacs-auxiliary-modes))) 349 | 350 | (defvar viper-vi-auxiliary-modes nil) 351 | (defvar viper-insert-auxiliary-modes nil) 352 | (defvar viper-replace-auxiliary-modes nil) 353 | (defvar viper-emacs-auxiliary-modes nil) 354 | 355 | ;;; Carefully set Viper/woman variables 356 | 357 | (defun vimpulse-configure-variables () 358 | "Set various variables, unless customized." 359 | ;; can backspace past start of insert/line 360 | (vimpulse-setq viper-ex-style-editing nil) 361 | ;; don't create new frame for manpages 362 | (vimpulse-setq woman-use-own-frame nil) 363 | ;; don't prompt upon K key (manpage display) 364 | (vimpulse-setq woman-use-topic-at-point t) 365 | ;; no start-up message 366 | (vimpulse-setq viper-inhibit-startup-message t) 367 | ;; Viper expert level 5 368 | (vimpulse-setq viper-expert-level 5) 369 | ;; make cursor color consistent 370 | (vimpulse-setq viper-insert-state-cursor-color 371 | viper-vi-state-cursor-color) 372 | ;; cursor moves backwards when exiting Insert state 373 | (vimpulse-setq viper-ESC-moves-cursor-back t) 374 | ;; not in Vim: C-h is indispensable in Emacs 375 | (vimpulse-setq viper-want-ctl-h-help t) 376 | ;; refresh Viper settings 377 | (viper-change-state-to-vi)) 378 | 379 | (if (and (boundp 'after-init-time) after-init-time) 380 | (vimpulse-configure-variables) 381 | (add-hook 'after-init-hook 'vimpulse-configure-variables)) 382 | 383 | (provide 'vimpulse-dependencies) 384 | -------------------------------------------------------------------------------- /vimpulse-compatibility.el: -------------------------------------------------------------------------------- 1 | ;;;; This code integrates Viper with the outside world 2 | 3 | (require 'vimpulse-utils) 4 | (require 'vimpulse-viper-function-redefinitions) 5 | 6 | ;;; undo-tree.el 7 | 8 | (when (and (boundp 'undo-tree-visualizer-map) 9 | (fboundp 'undo-tree-visualizer-quit)) 10 | 11 | (defadvice undo-tree-visualize (after vimpulse activate) 12 | "Enable Viper." 13 | (viper-mode)) 14 | 15 | (defun vimpulse-undo-quit () 16 | "Quit the undo-tree visualizer and delete window." 17 | (interactive) 18 | (let ((w (selected-window))) 19 | (undo-tree-visualizer-quit) 20 | (when (eq (selected-window) w) 21 | (delete-window)))) 22 | 23 | (define-key undo-tree-visualizer-map [remap viper-backward-char] 'undo-tree-visualize-switch-branch-left) 24 | (define-key undo-tree-visualizer-map [remap viper-forward-char] 'undo-tree-visualize-switch-branch-right) 25 | (define-key undo-tree-visualizer-map [remap viper-next-line] 'undo-tree-visualize-redo) 26 | (define-key undo-tree-visualizer-map [remap viper-previous-line] 'undo-tree-visualize-undo) 27 | (define-key undo-tree-visualizer-map [remap undo-tree-visualizer-scroll-left] 'viper-scroll-up) 28 | (define-key undo-tree-visualizer-map [remap undo-tree-visualizer-scroll-left] 'viper-scroll-up-one) 29 | (define-key undo-tree-visualizer-map [remap undo-tree-visualizer-scroll-right] 'viper-scroll-down) 30 | (define-key undo-tree-visualizer-map [remap undo-tree-visualizer-scroll-right] 'viper-scroll-down-one) 31 | (define-key undo-tree-visualizer-map [remap viper-intercept-ESC-key] 'vimpulse-undo-quit) 32 | (define-key undo-tree-visualizer-map [remap viper-nil] 'vimpulse-undo-quit) 33 | (define-key undo-tree-visualizer-map [remap undo-tree-visualizer-quit] 'vimpulse-undo-quit) 34 | (define-key undo-tree-visualizer-map [remap viper-next-line-at-bol] 'vimpulse-undo-quit) 35 | 36 | (add-to-list 'viper-vi-state-mode-list 'undo-tree-visualizer-mode) 37 | 38 | (add-to-list 'ex-token-alist '("undolist" (undo-tree-visualize))) 39 | (add-to-list 'ex-token-alist '("ulist" (undo-tree-visualize)))) 40 | 41 | ;;; Isearch 42 | 43 | (defcustom vimpulse-incremental-search t 44 | "Use isearch for / and ?, on by default." 45 | :type 'boolean 46 | :group 'vimpulse) 47 | 48 | (defcustom vimpulse-flash-delay 2 49 | "Number of seconds to flash search matches." 50 | :type 'integer 51 | :group 'vimpulse) 52 | 53 | (defvar vimpulse-flash-timer nil 54 | "Timer for flashing search results.") 55 | 56 | (defadvice isearch-message-prefix (around vimpulse-search activate) 57 | "Use vi prefix if appropriate." 58 | (if vimpulse-search-prompt 59 | (setq ad-return-value vimpulse-search-prompt) 60 | ad-do-it)) 61 | 62 | (defadvice isearch-delete-char (around vimpulse-search activate) 63 | "Exit search if no search string." 64 | (if (and vimpulse-search-prompt 65 | (string= isearch-string "")) 66 | (isearch-exit) 67 | ad-do-it)) 68 | 69 | (defvar viper-re-search) 70 | (defvar viper-s-forward) 71 | (defvar viper-s-string) 72 | 73 | (defadvice isearch-update-ring (after vimpulse-search activate) 74 | "Update `viper-s-string'." 75 | (when (eq viper-re-search regexp) 76 | (setq viper-s-string string))) 77 | 78 | (defadvice isearch-lazy-highlight-search (around vimpulse-search activate) 79 | "Deactivate `viper-search-wrap-around'." 80 | (let (viper-search-wrap-around) 81 | ad-do-it)) 82 | 83 | (defadvice viper-search (after vimpulse-search activate) 84 | "Update isearch history." 85 | (isearch-update-ring string viper-re-search)) 86 | 87 | ;; if `viper-search-wrap-around' is t, we want the search to wrap 88 | (defun vimpulse-search-fun-function () 89 | "Return a wrapping search function. 90 | Based on `viper-re-search' and `viper-s-forward'." 91 | `(lambda (regexp &optional bound noerror count) 92 | (let ((orig (point)) 93 | (search-fun (if isearch-regexp 94 | (if isearch-forward 95 | 're-search-forward 96 | 're-search-backward) 97 | (if isearch-forward 98 | 'search-forward 99 | 'search-backward))) 100 | retval) 101 | (setq retval (funcall search-fun regexp bound t count)) 102 | (when (and (not retval) viper-search-wrap-around) 103 | (goto-char (if isearch-forward (point-min) (point-max))) 104 | (setq retval (funcall search-fun regexp bound t count)) 105 | (unless retval 106 | (goto-char orig))) 107 | retval))) 108 | 109 | (defun vimpulse-search-backward (arg) 110 | "Search backward for user-entered text. 111 | Searches for regular expression if `viper-re-search' is t." 112 | (interactive "P") 113 | (let ((vimpulse-search-prompt "?") 114 | (lazy-highlight-initial-delay 0) 115 | (orig (point)) 116 | (isearch-mode-map isearch-mode-map) 117 | (isearch-search-fun-function 'vimpulse-search-fun-function) 118 | (oldmsg (current-message)) 119 | message-log-max 120 | search-nonincremental-instead) 121 | (vimpulse-vi-remap 'viper-intercept-ESC-key 122 | 'isearch-exit 123 | isearch-mode-map) 124 | (setq viper-s-forward nil) 125 | (isearch-backward viper-re-search) 126 | (when (and (eq orig (point)) 127 | (not (string= isearch-string ""))) 128 | (isearch-repeat-backward) 129 | (isearch-exit)) 130 | (if oldmsg (message "%s" oldmsg) 131 | (message nil)) 132 | (unless (string= isearch-string "") 133 | (vimpulse-flash-search-pattern t)) 134 | (setq vimpulse-this-motion 'viper-search-next))) 135 | 136 | (put 'vimpulse-search-backward 'function-documentation 137 | (format "Search backward for user-entered text. 138 | Searches for regular expression if `viper-re-search' is t. 139 | 140 | %s" (if (and (fboundp 'isearch-forward) 141 | (documentation 'isearch-forward)) 142 | (format "Below is the documentation string for `isearch-forward', 143 | which lists available keys: 144 | 145 | %s" (documentation 'isearch-forward))))) 146 | 147 | (defun vimpulse-search-forward (arg) 148 | "Search forward for user-entered text. 149 | Searches for regular expression if `viper-re-search' is t." 150 | (interactive "P") 151 | (let ((vimpulse-search-prompt "/") 152 | (orig (point)) 153 | (isearch-mode-map isearch-mode-map) 154 | (isearch-search-fun-function 'vimpulse-search-fun-function) 155 | (oldmsg (current-message)) 156 | message-log-max 157 | search-nonincremental-instead) 158 | (vimpulse-vi-remap 'viper-intercept-ESC-key 159 | 'isearch-exit 160 | isearch-mode-map) 161 | (setq viper-s-forward t) 162 | (isearch-forward viper-re-search) 163 | (and isearch-other-end (goto-char isearch-other-end)) 164 | (when (and (eq orig (point)) 165 | (not (string= isearch-string ""))) 166 | (isearch-repeat-forward) 167 | (isearch-exit)) 168 | (and isearch-other-end (goto-char isearch-other-end)) 169 | (if oldmsg (message "%s" oldmsg) 170 | (message nil)) 171 | (unless (string= isearch-string "") 172 | (vimpulse-flash-search-pattern t)) 173 | (setq vimpulse-this-motion 'viper-search-next))) 174 | 175 | (put 'vimpulse-search-forward 'function-documentation 176 | (format "Search forward for user-entered text. 177 | Searches for regular expression if `viper-re-search' is t. 178 | 179 | %s" (if (and (fboundp 'isearch-forward) 180 | (documentation 'isearch-forward)) 181 | (format "Below is the documentation string for `isearch-forward', 182 | which lists available keys: 183 | 184 | %s" (documentation 'isearch-forward))))) 185 | 186 | (defun vimpulse-flash-search-pattern (&optional only-current) 187 | "Flash search matches for duration of `vimpulse-flash-delay'." 188 | (let ((lazy-highlight-initial-delay 0) 189 | (isearch-search-fun-function 'vimpulse-search-fun-function) 190 | (isearch-case-fold-search case-fold-search) 191 | (disable (lambda (&optional arg) (vimpulse-flash-hook t)))) 192 | (when vimpulse-flash-timer 193 | (if (fboundp 'disable-timeout) 194 | (disable-timeout vimpulse-flash-timer) 195 | (cancel-timer vimpulse-flash-timer))) 196 | (when (viper-has-face-support-p) 197 | (isearch-highlight (match-beginning 0) (match-end 0)) 198 | (unless only-current 199 | (setq isearch-string viper-s-string 200 | isearch-forward viper-s-forward 201 | isearch-regexp viper-re-search 202 | isearch-lazy-highlight-wrapped nil 203 | isearch-lazy-highlight-start (point) 204 | isearch-lazy-highlight-end (point)) 205 | (and (fboundp 'isearch-lazy-highlight-new-loop) 206 | (isearch-lazy-highlight-new-loop)) 207 | (unless (and (boundp 'isearch-lazy-highlight-overlays) 208 | isearch-lazy-highlight-overlays) 209 | (and (fboundp 'isearch-lazy-highlight-update) 210 | (isearch-lazy-highlight-update)))) 211 | (add-hook 'pre-command-hook 'vimpulse-flash-hook) 212 | (setq vimpulse-flash-timer 213 | (if (fboundp 'run-at-time) 214 | (add-timeout vimpulse-flash-delay disable nil) 215 | (run-at-time vimpulse-flash-delay nil disable)))))) 216 | 217 | (defun vimpulse-flash-hook (&optional force) 218 | "Disable hightlighting if `this-command' is not search. 219 | Disable anyway if FORCE is t." 220 | (when (or force 221 | ;; to avoid flicker, don't disable highlighting if the 222 | ;; next command is also a search command 223 | (not (memq this-command 224 | '(viper-exec-mapped-kbd-macro 225 | viper-search 226 | viper-search-backward 227 | viper-search-forward 228 | viper-search-next 229 | viper-search-Next 230 | vimpulse-search-backward 231 | vimpulse-search-forward 232 | vimpulse-search-backward-for-symbol-at-point 233 | vimpulse-search-forward-for-symbol-at-point)))) 234 | (isearch-dehighlight) 235 | (setq isearch-lazy-highlight-last-string nil) 236 | (and (fboundp 'isearch-highlight-all-cleanup) 237 | (isearch-highlight-all-cleanup)) 238 | (and (fboundp 'lazy-highlight-cleanup) 239 | (lazy-highlight-cleanup t)) 240 | (when vimpulse-flash-timer 241 | (cancel-timer vimpulse-flash-timer))) 242 | (remove-hook 'pre-command-hook 'vimpulse-flash-hook)) 243 | 244 | (when vimpulse-incremental-search 245 | (defvaralias 'viper-case-fold-search 'case-fold-search) 246 | (defalias 'viper-search-backward 'vimpulse-search-backward) 247 | (defalias 'viper-search-forward 'vimpulse-search-forward) 248 | (defalias 'viper-flash-search-pattern 'vimpulse-flash-search-pattern)) 249 | 250 | ;;; Add vi navigation to help buffers 251 | 252 | ;; Apropos 253 | (eval-after-load 'apropos 254 | '(when vimpulse-want-vi-keys-in-apropos 255 | (add-to-list 'viper-vi-state-mode-list 'apropos-mode) 256 | (let ((map (copy-keymap apropos-mode-map))) 257 | (vimpulse-add-core-movement-cmds map) 258 | (vimpulse-inhibit-destructive-cmds map) 259 | (viper-modify-major-mode 'apropos-mode 'vi-state map)))) 260 | 261 | ;; Buffer-menu 262 | (eval-after-load "buff-menu" 263 | '(when vimpulse-want-vi-keys-in-buffmenu 264 | (setq viper-emacs-state-mode-list 265 | (delq 'Buffer-menu-mode viper-emacs-state-mode-list)) 266 | (add-to-list 'viper-vi-state-mode-list 'Buffer-menu-mode) 267 | (let ((map (copy-keymap Buffer-menu-mode-map))) 268 | (vimpulse-add-core-movement-cmds map) 269 | (vimpulse-inhibit-destructive-cmds map) 270 | (viper-modify-major-mode 'Buffer-menu-mode 'vi-state map)))) 271 | 272 | ;; Info 273 | (eval-after-load 'info 274 | '(when vimpulse-want-vi-keys-in-Info 275 | (setq viper-emacs-state-mode-list 276 | (delq 'Info-mode viper-emacs-state-mode-list)) 277 | (add-to-list 'viper-vi-state-mode-list 'Info-mode) 278 | (let ((map (copy-keymap Info-mode-map))) 279 | (vimpulse-add-core-movement-cmds map) 280 | (vimpulse-inhibit-destructive-cmds map) 281 | (define-key map "\C-t" 'Info-history-back) ; l 282 | (define-key map "\C-o" 'Info-history-back) 283 | (define-key map (kbd "\M-h") 'Info-help) ; h 284 | (define-key map " " 'Info-scroll-up) 285 | (define-key map "\C-]" 'Info-follow-nearest-node) 286 | (define-key map [backspace] 'Info-scroll-down) 287 | (viper-modify-major-mode 'Info-mode 'vi-state map)))) 288 | 289 | ;; Help 290 | (eval-after-load 'help-mode 291 | '(when vimpulse-want-vi-keys-in-help 292 | (setq viper-emacs-state-mode-list 293 | (delq 'help-mode viper-emacs-state-mode-list)) 294 | (add-to-list 'viper-vi-state-mode-list 'help-mode) 295 | (let ((map (copy-keymap help-mode-map))) 296 | (vimpulse-add-core-movement-cmds map) 297 | (vimpulse-inhibit-destructive-cmds map) 298 | (define-key map "q" 'View-quit) 299 | (viper-modify-major-mode 'help-mode 'vi-state map)))) 300 | 301 | ;; Slime 302 | (eval-after-load 'slime 303 | '(defadvice slime-popup-buffer-mode (after vimpulse activate) 304 | (when slime-popup-buffer-mode 305 | (viper-add-local-keys 306 | 'vi-state '(([?q] . slime-popup-buffer-quit-function)))))) 307 | 308 | ;;; Edebug 309 | 310 | (eval-after-load 'edebug 311 | '(progn 312 | (define-key edebug-mode-map [remap viper-forward-char] 'edebug-step-mode) 313 | (define-key edebug-mode-map [remap viper-search-next] 'edebug-next-mode) 314 | (define-key edebug-mode-map [remap vimpulse-goto-first-line] 'edebug-go-mode) 315 | (define-key edebug-mode-map [remap viper-goto-line] 'edebug-Go-nonstop-mode) 316 | (define-key edebug-mode-map [remap viper-goto-char-forward] 'edebug-trace-mode) 317 | (define-key edebug-mode-map [remap viper-goto-char-backward] 'edebug-Trace-fast-mode) 318 | (define-key edebug-mode-map [remap vimpulse-change] 'edebug-continue-mode) 319 | (define-key edebug-mode-map [remap viper-change-to-eol] 'edebug-Continue-fast-mode) 320 | (define-key edebug-mode-map [remap viper-find-char-forward] 'edebug-forward-sexp) 321 | (define-key edebug-mode-map [remap viper-backward-char] 'edebug-goto-here) 322 | (define-key edebug-mode-map [remap viper-Insert] 'edebug-instrument-callee) 323 | (define-key edebug-mode-map [remap viper-insert] 'edebug-step-in) 324 | (define-key edebug-mode-map [remap viper-open-line] 'edebug-step-out) 325 | (define-key edebug-mode-map [remap viper-nil] 'top-level) 326 | (define-key edebug-mode-map [remap viper-query-replace] 'edebug-top-level-nonstop) 327 | (define-key edebug-mode-map [remap viper-append] 'abort-recursive-edit) 328 | (define-key edebug-mode-map [remap viper-substitute-line] 'edebug-stop) 329 | (define-key edebug-mode-map [remap viper-backward-word] 'edebug-set-breakpoint) 330 | (define-key edebug-mode-map [remap undo-tree-undo] 'edebug-unset-breakpoint) 331 | (define-key edebug-mode-map [remap viper-backward-Word] 'edebug-next-breakpoint) 332 | (define-key edebug-mode-map [remap viper-delete-char] 'edebug-set-conditional-breakpoint) 333 | (define-key edebug-mode-map [remap viper-delete-backward-char] 'edebug-set-global-break-condition) 334 | (define-key edebug-mode-map [remap vimpulse-replace] 'edebug-previous-result) 335 | (define-key edebug-mode-map [remap viper-end-of-word] 'edebug-eval-expression) 336 | (define-key edebug-mode-map [remap viper-end-of-Word] 'edebug-visit-eval-list) 337 | (define-key edebug-mode-map [remap vimpulse-visual-toggle-char] 'edebug-view-outside) 338 | (define-key edebug-mode-map [remap viper-put-back] 'edebug-bounce-point) 339 | (define-key edebug-mode-map [remap viper-Put-back] 'edebug-view-outside) 340 | (define-key edebug-mode-map [remap viper-forward-Word] 'edebug-toggle-save-windows) 341 | (define-key edebug-mode-map [remap vimpulse-search-backward] 'edebug-help) 342 | (define-key edebug-mode-map [remap vimpulse-delete] 'edebug-backtrace) 343 | (define-key edebug-mode-map [remap viper-previous-line-at-bol] 'negative-argument) 344 | (define-key edebug-mode-map [remap vimpulse-indent] 'edebug-temp-display-freq-count))) 345 | 346 | ;;; ElDoc 347 | 348 | (eval-after-load 'eldoc 349 | '(apply 'eldoc-add-command 350 | (append vimpulse-viper-movement-cmds 351 | vimpulse-core-movement-cmds))) 352 | 353 | ;;; Folding 354 | 355 | (eval-after-load 'hideshow 356 | '(progn 357 | (defun vimpulse-za () 358 | (interactive) 359 | (hs-toggle-hiding) 360 | (hs-hide-level vimpulse-fold-level)) 361 | (defun vimpulse-hs-setup () 362 | (define-key viper-vi-basic-map "za" 'vimpulse-za) 363 | (define-key viper-vi-basic-map "zm" 'hs-hide-all) 364 | (define-key viper-vi-basic-map "zr" 'hs-show-all) 365 | (define-key viper-vi-basic-map "zo" 'hs-show-block) 366 | (define-key viper-vi-basic-map "zc" 'hs-hide-block)) 367 | (add-hook 'hs-minor-mode-hook 'vimpulse-hs-setup))) 368 | 369 | (provide 'vimpulse-compatibility) 370 | -------------------------------------------------------------------------------- /vimpulse-modal.el: -------------------------------------------------------------------------------- 1 | ;;;; Modal keybinding functions 2 | 3 | ;; Functions for making state bindings (modal bindings). 4 | ;; 5 | ;; Global state bindings (seen in all buffers): 6 | ;; `vimpulse-global-set-key', `vimpulse-map', `vimpulse-imap', 7 | ;; `vimpulse-vmap', `vimpulse-omap' 8 | ;; Buffer-local state bindings (seen only in the current buffer): 9 | ;; `vimpulse-local-set-key', `vimpulse-map-local', 10 | ;; `vimpulse-imap-local', `vimpulse-vmap-local', 11 | ;; `vimpulse-omap-local' 12 | ;; State bindings for Emacs modes (minor and major): 13 | ;; `vimpulse-define-key' 14 | ;; 15 | ;; `vimpulse-map' etc. mimic Vim's :map, :imap, :vmap and :omap 16 | ;; commands; `vimpulse-map-local' etc. are similar, but buffer-local. 17 | ;; 18 | ;; `vimpulse-define-key' associates state bindings with an Emacs mode, 19 | ;; and is useful for grouping bindings: 20 | ;; 21 | ;; (define-minor-mode foo-mode) 22 | ;; (vimpulse-define-key 'foo-mode 'vi-state "a" 'bar) 23 | ;; (vimpulse-define-key 'foo-mode 'visual-state "b" 'baz) 24 | ;; 25 | ;; These bindings are only seen in buffers where foo-mode is enabled. 26 | ;; You can also augment existing modes, of course. 27 | ;; 28 | ;; Vimpulse provides "careful" bindings, which are bindings that can 29 | ;; be safely stacked on top of pre-existing bindings. They can be made 30 | ;; with `vimpulse-make-careful-binding' (see its docstring for more 31 | ;; details), or by passing a non-nil value for the CAREFUL argument of 32 | ;; `vimpulse-global-set-key', `vimpulse-local-set-key' or 33 | ;; `vimpulse-define-key'. `vimpulse-map' etc. are always careful. 34 | 35 | (require 'vimpulse-viper-function-redefinitions) 36 | (eval-when-compile (require 'vimpulse-utils)) ; v-{with-state,strip-prefix,truncate} 37 | 38 | ;;; Variables 39 | (defvar vimpulse-last-command-event nil 40 | "Value for overwriting `last-command-event'. 41 | Used by `vimpulse-careful-pre-hook'.") 42 | 43 | (defvar vimpulse-careful-alist nil 44 | "Key bindings for which `vimpulse-careful-pre-hook' is active. 45 | That is, `last-command-event' and `read-char' work differently 46 | for these bindings. The format is (KEY-VECTOR . COMMAND).") 47 | 48 | (defvar vimpulse-careful-map (make-sparse-keymap) 49 | "Keymap of bindings overwritten by `vimpulse-map' et al.") 50 | 51 | ;;; Advice 52 | 53 | ;; For XEmacs, construct a wrap-around advice of the current command 54 | ;; shadowing the read-only command loop variables with a 55 | ;; `let' binding. 56 | (defmacro vimpulse-advice-command (command) 57 | "Define an around advice for COMMAND to shadow `last-command-event'. 58 | XEmacs does not allow us to change its command loop variables 59 | directly, but shadowing them with a `let' binding works." 60 | `(defadvice ,command (around vimpulse-careful activate) 61 | "Shadow `last-command-event' with a `let' binding." 62 | (cond 63 | (vimpulse-last-command-event 64 | (let* ((last-command-event 65 | (character-to-event vimpulse-last-command-event)) 66 | (last-command-char vimpulse-last-command-event) 67 | (last-input-event last-command-event) 68 | (last-input-char last-command-char)) 69 | ad-do-it)) 70 | (t 71 | ad-do-it)))) 72 | 73 | ;;; General functions 74 | 75 | (defun vimpulse-careful-check (key-sequence) 76 | "Return t if KEY-SEQUENCE defaults to `this-command', 77 | but only for bindings listed in `vimpulse-careful-alist'." 78 | (let ((temp-sequence (vimpulse-strip-prefix key-sequence))) 79 | (setq temp-sequence (vimpulse-truncate temp-sequence -1)) 80 | (and this-command ; may be nil 81 | (not (key-binding key-sequence)) ; only default bindings 82 | (eq (cdr (assoc temp-sequence vimpulse-careful-alist)) 83 | this-command)))) 84 | 85 | (defun vimpulse-careful-remove (key-vector &optional recursive) 86 | "Delete entry with KEY-VECTOR from `vimpulse-careful-alist'. 87 | If RECURSIVE is non-nil, also delete entries whose key-vectors 88 | start with KEY-VECTOR." 89 | (if recursive 90 | (dolist (entry vimpulse-careful-alist) 91 | (when (equal (vimpulse-truncate (car entry) 92 | (length key-vector)) 93 | key-vector) 94 | (setq vimpulse-careful-alist 95 | (delq entry vimpulse-careful-alist)))) 96 | (setq vimpulse-careful-alist 97 | (assq-delete-all key-vector vimpulse-careful-alist)))) 98 | 99 | (defun vimpulse-xemacs-def-binding 100 | (keymap key def &optional careful-binding define-func) 101 | "Make a default binding in XEmacs. If CAREFUL-BINDING is 102 | non-nil, advice DEF by means of `vimpulse-advice-command'." 103 | (let ((temp-sequence (vconcat key)) 104 | (submap (lookup-key keymap key))) 105 | (unless define-func (setq define-func 'define-key)) 106 | (and careful-binding (commandp def) 107 | (eval `(vimpulse-advice-command ,def))) 108 | (and (> (length temp-sequence) 1) 109 | (eq (aref temp-sequence (1- (length temp-sequence))) t) 110 | (setq temp-sequence (vimpulse-truncate temp-sequence -1))) 111 | ;; from http://tracker.xemacs.org/XEmacs/its/msg2021 112 | (unless (keymapp submap) 113 | (setq submap (make-sparse-keymap))) 114 | (when (fboundp 'set-keymap-default-binding) 115 | (set-keymap-default-binding submap def)) 116 | (funcall define-func keymap temp-sequence submap))) 117 | 118 | (defun vimpulse-default-binding 119 | (keymap key def &optional careful-binding define-func) 120 | "Make a default binding in GNU Emacs or XEmacs, 121 | whichever is appropriate. If CAREFUL-BINDING is non-nil, 122 | the binding is listed in `vimpulse-careful-alist'." 123 | (let ((temp-sequence (vconcat key))) 124 | (unless define-func (setq define-func 'define-key)) 125 | (cond 126 | ((featurep 'xemacs) 127 | (vimpulse-xemacs-def-binding 128 | keymap temp-sequence def careful-binding define-func)) 129 | (t 130 | (unless (eq (aref temp-sequence (1- (length temp-sequence))) t) 131 | (setq temp-sequence (vconcat temp-sequence [t]))) 132 | (funcall define-func keymap temp-sequence def))) 133 | (when careful-binding 134 | (add-to-list 'vimpulse-careful-alist 135 | (cons (vimpulse-truncate temp-sequence -1) def))) 136 | def)) 137 | 138 | ;;; Hook run before each command 139 | 140 | ;; If the current command is a default key binding made by 141 | ;; `vimpulse-make-careful-binding', we need to unread the last input 142 | ;; events and change some command loop variables to give the command 143 | ;; the impression of its "old" binding. 144 | (defun vimpulse-careful-pre-hook () 145 | "Update `vimpulse-last-command-event' and `unread-command-events'. 146 | If the current key-sequence defaults to a shorter key-sequence, 147 | the difference is stored in these two variables, to be passed on 148 | via the `last-command-event' variable and the `read-char' 149 | functions, respectively." 150 | (setq vimpulse-last-command-event nil) 151 | (let ((key-sequence (vconcat (this-command-keys)))) 152 | ;; if XEmacs, get rid of the event object type 153 | (when (featurep 'xemacs) 154 | (setq key-sequence (events-to-keys key-sequence))) 155 | (while (and (> (length key-sequence) 1) 156 | (vimpulse-careful-check key-sequence)) 157 | ;; unread last event 158 | (setq vimpulse-last-command-event 159 | (elt key-sequence (1- (length key-sequence)))) 160 | (when (featurep 'xemacs) 161 | (setq vimpulse-last-command-event 162 | (character-to-event vimpulse-last-command-event))) 163 | (add-to-list 'unread-command-events vimpulse-last-command-event) 164 | ;; change command loop variables 165 | (setq vimpulse-last-command-event 166 | (elt key-sequence (1- (1- (length key-sequence))))) 167 | (unless (featurep 'xemacs) ; if XEmacs, do this with advice 168 | (with-no-warnings 169 | (setq last-command-event vimpulse-last-command-event 170 | last-command-char vimpulse-last-command-event 171 | last-input-event vimpulse-last-command-event 172 | last-input-char vimpulse-last-command-event))) 173 | (setq key-sequence 174 | (vimpulse-truncate key-sequence -1))))) 175 | 176 | ;;; Hook run after each command 177 | 178 | ;; this merely ensures `vimpulse-last-command-event' is reset 179 | (defun vimpulse-careful-post-hook () 180 | "Erase `vimpulse-last-command-event'." 181 | (setq vimpulse-last-command-event nil)) 182 | 183 | (add-hook 'pre-command-hook 'vimpulse-careful-pre-hook) 184 | (add-hook 'post-command-hook 'vimpulse-careful-post-hook) 185 | 186 | ;;; Modal binding functions 187 | 188 | (defun vimpulse-make-careful-binding 189 | (keymap key def &optional dont-list define-func) 190 | "Carefully bind KEY to DEF in KEYMAP. 191 | \"Carefully\" means that if a subset of the key sequence is already 192 | bound, a default binding is made so that the new binding won't 193 | overwrite the old. E.g., if we want to carefully bind \"A B C\" to 194 | `foo', and \"A B\" is already bound to `bar', the end result is 195 | 196 | \"A B C\" => `foo' 197 | \"A B \" => `bar' 198 | 199 | Thus, \"A B C\" runs `foo', while \"A B\" + any key other than \"C\" 200 | defaults to `bar'. To remove a careful binding, bind it to nil. 201 | 202 | The default binding gets listed in `vimpulse-careful-alist', so 203 | that, with regard to command loop variables, it appears exactly 204 | the same as the binding it replaced. In the example above, `bar' 205 | will see a `last-command-event' of \"B\", and will automatically 206 | obtain the last key by calling `read-char'. To make a regular 207 | default binding, use DONT-LIST. DEFINE-FUNC specifies a function 208 | to be used in place of `define-key'. 209 | 210 | NOTE: If the original binding \"A B\" is not stored in KEYMAP, 211 | but in some other map which is active only in a certain 212 | state (say, Insert mode), this function can detect that binding 213 | only if called in the same state. The functions `vimpulse-map', 214 | `vimpulse-imap' and `vimpulse-vmap' take care of this." 215 | (let (key-vector temp-sequence current-binding previous-binding) 216 | (setq define-func (or define-func 'define-key)) 217 | (setq key-vector (if (stringp key) (read-kbd-macro key t) key)) 218 | (when (equal key-vector []) 219 | (setq key-vector (vconcat key))) 220 | (cond 221 | ;; nil unbinds the key-sequence 222 | ((null def) 223 | (funcall define-func keymap key-vector def) 224 | (while (and (> (length key-vector) 1) 225 | (not (lookup-key keymap key-vector))) 226 | (vimpulse-careful-remove key-vector t) 227 | (setq key-vector (vimpulse-truncate key-vector -1)))) 228 | ;; `undefined' also unbinds, but less forcefully 229 | ((eq def 'undefined) 230 | (if (keymapp (lookup-key keymap key-vector)) 231 | (vimpulse-default-binding keymap key-vector nil t define-func) 232 | (funcall define-func keymap key-vector def)) 233 | (vimpulse-careful-remove key-vector)) 234 | ;; regular binding: convert previous bindings to default bindings 235 | (t 236 | (dotimes (i (1- (length key-vector))) 237 | ;; For each subset of KEY-VECTOR (stored in `temp-sequence'), 238 | ;; check the binding (stored in `current-binding'); if it 239 | ;; isn't bound, use `previous-binding'. 240 | (setq temp-sequence (vimpulse-truncate key-vector (1+ i))) 241 | (setq current-binding (lookup-key keymap temp-sequence t)) 242 | (when (or (numberp current-binding) (not current-binding)) 243 | (setq current-binding 244 | (or (key-binding temp-sequence t) previous-binding))) 245 | (setq previous-binding current-binding) 246 | ;; If `current-binding' is a keymap, do nothing, since our 247 | ;; careful binding can happily exist as part of that keymap. 248 | ;; However, if `current-binding' is a command, we need to make 249 | ;; room for the careful binding by creating a default binding. 250 | (unless (keymapp current-binding) 251 | (setq temp-sequence (vconcat temp-sequence [t])) 252 | (setq current-binding (lookup-key keymap temp-sequence t)) 253 | (when (or (numberp current-binding) (not current-binding)) 254 | (setq current-binding 255 | (or (key-binding temp-sequence t) previous-binding)) 256 | (define-key keymap 257 | (vimpulse-truncate temp-sequence -1) nil) 258 | (vimpulse-default-binding 259 | keymap temp-sequence current-binding 260 | (not dont-list) define-func)) 261 | (setq previous-binding current-binding))) 262 | ;; Defaults are taken care of; we may now bind the key. 263 | ;; If a longer binding starting with KEY-VECTOR exists, 264 | ;; make a default binding so it's not overwritten. 265 | (if (keymapp (lookup-key keymap key-vector)) 266 | (vimpulse-default-binding 267 | keymap key-vector def (not dont-list) define-func) 268 | (funcall define-func keymap key def)))))) 269 | 270 | (define-minor-mode vimpulse-careful-minor-mode 271 | "Minor mode of bindings overwritten by `vimpulse-map' et al." 272 | :keymap vimpulse-careful-map 273 | (dolist (entry vimpulse-careful-alist) 274 | (unless (lookup-key vimpulse-careful-map (car entry)) 275 | (define-key vimpulse-careful-map (car entry) (cdr entry)))) 276 | (when vimpulse-careful-minor-mode 277 | (viper-normalize-minor-mode-map-alist))) 278 | 279 | (add-to-list 'vimpulse-state-maps-alist 280 | (cons 'vimpulse-careful-minor-mode 'vimpulse-careful-map)) 281 | 282 | (defun vimpulse-define-key (mode state key def &optional careful) 283 | "Modally bind KEY to DEF in STATE for MODE. 284 | MODE is an Emacs mode (minor or major), while STATE is one of 285 | `vi-state', `insert-state', `visual-state' or `operator-state'. 286 | For example: 287 | 288 | (vimpulse-define-key 'text-mode 'vi-state \"a\" 'foo) 289 | (vimpulse-define-key 'visual-line-mode 'visual-state \"b\" 'bar) 290 | 291 | If CAREFUL is non-nil, make a careful binding with 292 | `vimpulse-make-careful-binding'." 293 | (let* ((entry (cdr (assq state vimpulse-auxiliary-modes-alist))) 294 | (aux (cdr (assq mode (symbol-value entry)))) 295 | (map (eval (cdr (assq aux vimpulse-state-maps-alist))))) 296 | ;; if no auxiliary mode exists, create one 297 | (unless (keymapp map) 298 | (setq aux (intern (format "vimpulse-%s-%s" state mode)) 299 | map (intern (format "vimpulse-%s-%s-map" state mode))) 300 | (eval `(viper-deflocalvar ,aux nil 301 | ,(format "Auxiliary %s mode for `%s'." state mode))) 302 | (eval `(defvar ,map (make-sparse-keymap) 303 | ,(format "Auxiliary %s keymap for `%s'." state mode))) 304 | (when (fboundp mode) 305 | (eval `(defadvice ,mode (after vimpulse-modal activate) 306 | (when viper-mode 307 | (viper-normalize-minor-mode-map-alist) 308 | (viper-set-mode-vars-for viper-current-state))))) 309 | (add-to-list 'vimpulse-state-maps-alist (cons aux map) t) 310 | (add-to-list entry (cons mode aux) t) 311 | (add-to-list 'vimpulse-auxiliary-modes mode) 312 | (vimpulse-normalize-auxiliary-modes) 313 | (setq map (eval map))) 314 | ;; define key 315 | (if careful 316 | (vimpulse-with-state state 317 | (vimpulse-make-careful-binding map key def)) 318 | (define-key map key def)))) 319 | 320 | ;; This modifies the major mode extension keymap, i.e., it's 321 | ;; a reusable front-end to `viper-modify-major-mode'. 322 | ;; (By itself, `viper-modify-major-mode' discards the previous keymap.) 323 | ;; Don't use this; use `vimpulse-define-key' instead. 324 | (defun vimpulse-define-major-key (mode state key def &optional careful) 325 | "Modally bind KEY to DEF in STATE for major mode MODE. 326 | STATE is one of `vi-state', `insert-state', `visual-state' or 327 | `operator-state'. If CAREFUL is non-nil, make a careful binding 328 | with `vimpulse-make-careful-binding'." 329 | (let ((modifier-map (vimpulse-modifier-map state mode))) 330 | (if careful 331 | (vimpulse-with-state state 332 | (vimpulse-make-careful-binding modifier-map key def)) 333 | (define-key modifier-map key def)) 334 | (viper-modify-major-mode mode state modifier-map) 335 | def)) 336 | 337 | (defalias 'vimpulse-define-minor-key 'vimpulse-define-key) 338 | 339 | (defun vimpulse-global-set-key (state key def &optional careful) 340 | "Modally bind KEY to DEF in STATE. 341 | STATE is one of `vi-state', `insert-state', `visual-state' or `operator-state'. 342 | If CAREFUL is non-nil, don't overwrite previous bindings." 343 | (let* ((map (cdr (assq state vimpulse-state-vars-alist))) 344 | (global-user-map (eval (cdr (assq 'global-user-map map))))) 345 | (if careful 346 | (vimpulse-with-state state 347 | (vimpulse-make-careful-binding global-user-map key def)) 348 | (define-key global-user-map key def)))) 349 | 350 | (defun vimpulse-local-set-key (state key def) 351 | "Modally bind KEY to DEF in STATE, locally. 352 | STATE is one of `vi-state', `insert-state', `visual-state' or `operator-state'." 353 | (viper-add-local-keys state `((,key . ,def))) 354 | def) 355 | 356 | (defun vimpulse-map-state (state key def &optional modes) 357 | "Modally bind KEY to DEF in STATE. 358 | Don't use this function directly; see `vimpulse-map', 359 | `vimpulse-imap', `vimpulse-vmap' and `vimpulse-omap' instead." 360 | (let* ((map (cdr (assq state vimpulse-state-vars-alist))) 361 | (basic-map (eval (cdr (assq 'basic-map map))))) 362 | (if modes 363 | (dolist (mode modes) 364 | (if (eq mode t) 365 | (vimpulse-global-set-key state key def t) 366 | (vimpulse-define-major-key mode state key def t))) 367 | (vimpulse-with-state state 368 | (vimpulse-make-careful-binding basic-map key def))))) 369 | 370 | (defalias 'vimpulse-map-state-local 'vimpulse-local-set-key) 371 | 372 | (defun vimpulse-map (key def &rest modes) 373 | "Modally bind KEY to DEF in vi (command) state. 374 | The syntax is the same as that of `global-set-key', e.g., 375 | 376 | (vimpulse-map \"abc\" 'abc-command) 377 | 378 | The optional MODES argument specifies which major modes the 379 | binding is seen in: 380 | 381 | (vimpulse-map \"abc\" 'abc-command 'lisp-mode 'text-mode) 382 | 383 | Otherwise, the binding is universal, but has lower priority. 384 | Pass t to MODES to create an universal binding with presedence 385 | over mode-specific bindings." 386 | (vimpulse-map-state 'vi-state key def modes)) 387 | 388 | (defun vimpulse-imap (key def &rest modes) 389 | "Modally bind KEY to DEF in Insert state. 390 | The syntax is the same as that of `global-set-key', e.g., 391 | 392 | (vimpulse-imap \"abc\" 'abc-command) 393 | 394 | The optional MODES argument specifies which major modes the 395 | binding is seen in: 396 | 397 | (vimpulse-imap \"abc\" 'abc-command 'lisp-mode 'text-mode) 398 | 399 | Otherwise, the binding is universal, but has lower priority. 400 | Pass t to MODES to create an universal binding with presedence 401 | over mode-specific bindings." 402 | (vimpulse-map-state 'insert-state key def modes)) 403 | 404 | (defun vimpulse-vmap (key def &rest modes) 405 | "Modally bind KEY to DEF in the Visual state. 406 | The syntax is the same as that of `global-set-key', e.g., 407 | 408 | (vimpulse-vmap \"abc\" 'abc-command) 409 | 410 | The optional MODES argument specifies which major modes the 411 | binding is seen in: 412 | 413 | (vimpulse-vmap \"abc\" 'abc-command 'lisp-mode 'text-mode) 414 | 415 | Otherwise, the binding is universal, but has lower priority. 416 | Pass t to MODES to create an universal binding with presedence 417 | over mode-specific bindings." 418 | (vimpulse-map-state 'visual-state key def modes)) 419 | 420 | (defun vimpulse-omap (key def &rest modes) 421 | "Modally bind KEY to DEF in the Operator-Pending state. 422 | The syntax is the same as that of `global-set-key', e.g., 423 | 424 | (vimpulse-omap \"abc\" 'abc-command) 425 | 426 | The optional MODES argument specifies which major modes the 427 | binding is seen in: 428 | 429 | (vimpulse-omap \"abc\" 'abc-command 'lisp-mode 'text-mode) 430 | 431 | Otherwise, the binding is universal, but has lower priority. 432 | Pass t to MODES to create an universal binding with presedence 433 | over mode-specific bindings." 434 | (vimpulse-map-state 'operator-state key def modes)) 435 | 436 | (defun vimpulse-map! (key def &rest modes) 437 | "Bind KEY to DEF in vi (command) state and the Visual state. 438 | To bind in Insert state, use `vimpulse-imap'." 439 | (vimpulse-map key def modes) 440 | (vimpulse-vmap key def modes)) 441 | 442 | (defun vimpulse-map-local (key def) 443 | "Make a buffer-local binding of KEY to DEF in vi (command) state. 444 | The syntax is the same as that of `local-set-key', e.g., 445 | 446 | (vimpulse-map-local \"abc\" 'abc-command) 447 | 448 | You would typically use this in a mode hook. To make a global 449 | binding, use `vimpulse-map'." 450 | (vimpulse-map-state-local 'vi-state key def)) 451 | 452 | (defun vimpulse-imap-local (key def) 453 | "Make a buffer-local binding of KEY to DEF in Insert state. 454 | The syntax is the same as that of `local-set-key', e.g., 455 | 456 | (vimpulse-imap-local \"abc\" 'abc-command) 457 | 458 | You would typically use this in a mode hook. To make a global 459 | binding, use `vimpulse-imap'." 460 | (vimpulse-map-state-local 'insert-state key def)) 461 | 462 | (defun vimpulse-vmap-local (key def) 463 | "Make a buffer-local binding of KEY to DEF in the Visual state. 464 | The syntax is the same as that of `local-set-key', e.g., 465 | 466 | (vimpulse-vmap-local \"abc\" 'abc-command) 467 | 468 | You would typically use this in a mode hook. To make a global 469 | binding, use `vimpulse-vmap'." 470 | (vimpulse-map-state-local 'visual-state key def)) 471 | 472 | (defun vimpulse-omap-local (key def) 473 | "Make a buffer-local binding of KEY to DEF in the Operator-Pending state. 474 | The syntax is the same as that of `local-set-key', e.g., 475 | 476 | (vimpulse-omap-local \"abc\" 'abc-command) 477 | 478 | You would typically use this in a mode hook. To make a global 479 | binding, use `vimpulse-omap'." 480 | (vimpulse-map-state-local 'visual-state key def)) 481 | 482 | (provide 'vimpulse-modal) 483 | -------------------------------------------------------------------------------- /vimpulse-text-object-system.el: -------------------------------------------------------------------------------- 1 | ;;;; Text objects support 2 | 3 | ;; The following code implements support for text objects and commands 4 | ;; like diw, daw, ciw, caw. Currently, the most common objects are 5 | ;; supported: 6 | ;; 7 | ;; - bracket-blocks: b B { [ ( < > ) ] } 8 | ;; - sentences: s 9 | ;; - paragraphs: p 10 | ;; - quoted expressions: " and ' 11 | ;; - words: w and W 12 | ;; 13 | ;; Vimpulse's text objects are fairly close to Vim's, and are based on 14 | ;; Viper's movement commands. More objects are easily added with 15 | ;; `vimpulse-define-text-object'. 16 | 17 | (eval-when-compile (require 'vimpulse-utils)) ; vimpulse-unquote 18 | (require 'vimpulse-visual-mode) ; v-v-{activate,expand-region,select} 19 | 20 | (declare-function vimpulse-calculate-motion-range "vimpulse-operator" (count motion &optional type refresh)) 21 | 22 | (defvar vimpulse-operator-basic-map) ; defined programmatically by `v-define-state' 23 | 24 | (defmacro vimpulse-define-text-object (object args &rest body) 25 | "Define a text object OBJECT. 26 | ARGS is the argument list, which must contain at least one argument: 27 | the count. It is followed by an optional docstring and optional 28 | keywords: 29 | 30 | :keys KEYS A key or a list of keys to bind the command to. 31 | :map MAP Keymap to bind :keys in, defaults to 32 | `vimpulse-operator-basic-map'. 33 | :type TYPE The object's motion type. 34 | 35 | The keywords are followed by the object's body, which must return 36 | a pure range (BEG END) or a motion range (TYPE BEG END). Thus, 37 | a simple example may look somewhat like: 38 | 39 | (vimpulse-define-text-object test (arg) 40 | \"Test object.\" 41 | :keys \"t\" 42 | (list 'exclusive (point) (+ arg (point)))) 43 | 44 | Here, the count is stored in ARG. Note that the body must be able 45 | to handle a negative value, which specifies reverse direction." 46 | (declare (indent defun) 47 | (debug (&define name lambda-list 48 | [&optional stringp] 49 | [&rest keywordp sexp] 50 | def-body))) 51 | (let ((map 'vimpulse-operator-basic-map) 52 | count doc keys keyword type) 53 | ;; collect COUNT argument 54 | (setq args (or args (list 'arg)) 55 | count (car args)) 56 | ;; collect docstring, if any 57 | (when (stringp (car body)) 58 | (setq doc (car body) 59 | body (cdr body))) 60 | ;; collect keywords 61 | (while (keywordp (setq keyword (car body))) 62 | (setq body (cdr body)) 63 | (cond 64 | ((eq keyword :keys) 65 | (setq keys (vimpulse-unquote (pop body)))) 66 | ((eq keyword :map) 67 | (setq map (vimpulse-unquote (pop body)))) 68 | ((eq keyword :type) 69 | (setq type (vimpulse-unquote (pop body)))) 70 | (t 71 | (pop body)))) 72 | (unless (listp keys) 73 | (setq keys (list keys))) 74 | (when type 75 | (setq type `(',type))) 76 | ;; macro expansion: define key bindings, set motion type 77 | ;; and define command 78 | `(progn 79 | (dolist (key ',keys) 80 | (define-key ,map key ',object)) 81 | ,(when type 82 | `(put ',object 'motion-type ,@type)) 83 | (defun ,object ,args 84 | ,doc 85 | (interactive "p") 86 | (let ((,count (if (numberp ,count) ,count 1)) 87 | range) 88 | (cond 89 | ((region-active-p) 90 | (when (< (point) (mark t)) 91 | (setq ,count (- ,count))) 92 | (when (memq vimpulse-visual-mode '(line block)) 93 | (vimpulse-visual-activate 'char)) 94 | (when (and vimpulse-visual-mode 95 | (not vimpulse-visual-region-expanded)) 96 | (vimpulse-visual-expand-region)) 97 | (setq range (progn ,@body)) 98 | (unless (vimpulse-mark-range range t ,@type) 99 | ;; Are we stuck (unchanged region)? 100 | ;; Move forward and try again. 101 | (viper-forward-char-carefully (if (< ,count 0) -1 1)) 102 | (setq range (progn ,@body)) 103 | (vimpulse-mark-range range t ,@type))) 104 | (t 105 | (setq range (progn ,@body)) 106 | (vimpulse-mark-range range nil ,@type)))))))) 107 | 108 | (defun vimpulse-mark-range (range &optional widen type) 109 | "Mark RANGE, which has the form (BEG END) or (TYPE BEG END). 110 | If WIDEN is non-nil, expands existing region. If the TYPE 111 | argument is specified, it overrides the type of RANGE." 112 | (let* ((type (or type (vimpulse-motion-type range))) 113 | (range (vimpulse-motion-range range)) 114 | (beg (vimpulse-range-beginning range)) 115 | (end (vimpulse-range-end range))) 116 | (cond 117 | ((eq type 'exclusive) 118 | (vimpulse-visual-select beg end widen)) 119 | (t 120 | (when vimpulse-visual-mode 121 | (unless (memq type '(line block)) 122 | (setq type 'char)) 123 | (unless (eq type vimpulse-visual-mode) 124 | (vimpulse-visual-activate type))) 125 | (vimpulse-visual-select beg end widen))))) 126 | 127 | ;;; Text object range functions 128 | 129 | ;; word-like expressions (words, sentences, paragraphs) 130 | (defun vimpulse-object-range 131 | (count backward-func forward-func &optional type) 132 | "Return a text object range (TYPE BEG END). 133 | BACKWARD-FUNC moves point to the object's beginning, 134 | FORWARD-FUNC moves to its end. Schematically, 135 | 136 | \(vimpulse-object-range ) 137 | 138 | COUNT is the number of objects. If positive, go forwards and 139 | then backwards; if negative, go backwards and then forwards. 140 | 141 | The type of the object (`exclusive', `inclusive' or `line') 142 | may be specified with TYPE. Otherwise, the type is inferred 143 | from the motion types of BACKWARD-FUNC and FORWARD-FUNC." 144 | (let ((types '(exclusive inclusive line block)) 145 | beg end forward-range backward-range 146 | viper-com-point 147 | vimpulse-visual-vars-alist 148 | vimpulse-this-motion 149 | vimpulse-this-motion-type) 150 | (save-excursion 151 | (setq count (or (if (eq count 0) 1 count) 1)) 152 | (if (< count 0) 153 | (setq backward-range 154 | (vimpulse-calculate-motion-range 155 | (abs count) backward-func type t) 156 | forward-range 157 | (vimpulse-calculate-motion-range 158 | (abs count) forward-func type t)) 159 | (setq forward-range 160 | (vimpulse-calculate-motion-range 161 | (abs count) forward-func type t) 162 | backward-range 163 | (vimpulse-calculate-motion-range 164 | (abs count) backward-func type t))) 165 | (setq beg (vimpulse-range-beginning backward-range) 166 | end (vimpulse-range-end forward-range)) 167 | (unless type 168 | (setq type 'exclusive) 169 | (dolist (elt types) 170 | (when (or (eq elt (vimpulse-motion-type backward-range)) 171 | (eq elt (vimpulse-motion-type forward-range))) 172 | (setq type elt)))) 173 | (list type beg end)))) 174 | 175 | (defun vimpulse-an-object-range 176 | (count backward-func forward-func &optional include-newlines regexp) 177 | "Return a text object range (BEG END) with whitespace. 178 | Unless INCLUDE-NEWLINES is t, whitespace inclusion is restricted 179 | to the line(s) the object is on. REGEXP is a regular expression 180 | for matching whitespace; the default is \"[ \\f\\t\\n\\r\\v]+\". 181 | See `vimpulse-object-range' for more details." 182 | (let (range beg end line-beg line-end mark-active-p) 183 | (save-excursion 184 | (setq count (or (if (eq count 0) 1 count) 1)) 185 | (setq regexp (or regexp "[ \f\t\n\r\v]+")) 186 | (setq range (vimpulse-motion-range 187 | (vimpulse-object-range 188 | count backward-func forward-func))) 189 | ;; let `end' be the boundary furthest from point, 190 | ;; based on the direction we are going 191 | (if (< count 0) 192 | (setq beg (cadr range) 193 | end (car range)) 194 | (setq beg (car range) 195 | end (cadr range))) 196 | ;; if INCLUDE-NEWLINES is nil, never move past 197 | ;; the line boundaries of the text object 198 | (unless include-newlines 199 | (setq line-beg (line-beginning-position) 200 | line-end (line-end-position)) 201 | (when (> (* count beg) 202 | (max (* count line-beg) (* count line-end))) 203 | (setq count (- count)) 204 | (setq range (vimpulse-motion-range 205 | (vimpulse-object-range 206 | count backward-func forward-func))) 207 | (if (< count 0) 208 | (setq beg (cadr range) 209 | end (car range)) 210 | (setq beg (car range) 211 | end (cadr range)))) 212 | (setq line-beg (save-excursion 213 | (goto-char (min beg end)) 214 | (line-beginning-position)) 215 | line-end (save-excursion 216 | (goto-char (max beg end)) 217 | (line-end-position)))) 218 | ;; Generally only include whitespace at one side (but see below). 219 | ;; If we are before the object, include leading whitespace; 220 | ;; if we are inside the object, include trailing whitespace. 221 | ;; If trailing whitespace inclusion fails, include leading. 222 | (setq count (if (< count 0) -1 1)) 223 | (when (or (< (* count (point)) (* count beg)) 224 | (eq end (setq end (save-excursion 225 | (goto-char end) 226 | (vimpulse-skip-regexp 227 | regexp count line-beg line-end))))) 228 | (setq beg (save-excursion 229 | (goto-char beg) 230 | (if (and (not include-newlines) 231 | (looking-back "^[ \t]*")) 232 | beg 233 | (vimpulse-skip-regexp 234 | regexp (- count) line-beg line-end)))) 235 | ;; Before/after adjustment for whole lines: if the object is 236 | ;; followed by a blank line, include that as trailing 237 | ;; whitespace and subtract a line from the leading whitespace. 238 | (when include-newlines 239 | (goto-char end) 240 | (forward-line count) 241 | (when (looking-at "[ \t]*$") 242 | (setq end (line-beginning-position)) 243 | (goto-char beg) 244 | (when (looking-at "[ \t]*$") 245 | (forward-line count) 246 | (setq beg (line-beginning-position)))))) 247 | ;; return the range 248 | (list (min beg end) (max beg end))))) 249 | 250 | (defun vimpulse-inner-object-range 251 | (count backward-func forward-func) 252 | "Return a text object range (BEG END) including point. 253 | If point is outside the object, it is included in the range. 254 | To include whitespace, use `vimpulse-an-object-range'. 255 | See `vimpulse-object-range' for more details." 256 | (let (range beg end line-beg line-end) 257 | (setq count (or (if (eq count 0) 1 count) 1)) 258 | (setq range (vimpulse-motion-range 259 | (vimpulse-object-range 260 | count backward-func forward-func))) 261 | (setq beg (car range) 262 | end (cadr range)) 263 | (setq line-beg (line-beginning-position) 264 | line-end (line-end-position)) 265 | (when (> (min (* count beg) (* count end)) 266 | (max (* count line-beg) (* count line-end))) 267 | (setq count (- count)) 268 | (setq range (vimpulse-motion-range 269 | (vimpulse-object-range 270 | count backward-func forward-func)) 271 | beg (car range) 272 | end (cadr range))) 273 | ;; return the range, including point 274 | (list (min beg (point)) (max end (point))))) 275 | 276 | ;; parenthetical expressions 277 | (defun vimpulse-paren-range (count &optional open close include-parentheses) 278 | "Return a parenthetical expression range (BEG END). 279 | The type of parentheses may be specified with OPEN and CLOSE, 280 | which must be characters. INCLUDE-PARENTHESES specifies 281 | whether to include the parentheses in the range." 282 | (let ((open-regexp (if open (regexp-quote (string open)) "")) 283 | (close-regexp (if close (regexp-quote (string close)) "")) 284 | (count (if (eq count 0) 1 (abs count))) 285 | (beg (point)) (end (point)) 286 | line-beg line-end) 287 | (save-excursion 288 | (with-syntax-table (copy-syntax-table (syntax-table)) 289 | (when (and open close) 290 | (modify-syntax-entry open (format "(%c" close)) 291 | (modify-syntax-entry close (format ")%c" open))) 292 | (when (and open (looking-at open-regexp)) 293 | (forward-char)) 294 | ;; find opening and closing paren with Emacs' S-exp facilities 295 | (while (progn 296 | (vimpulse-backward-up-list 1) 297 | (not (when (looking-at open-regexp) 298 | (when (save-excursion 299 | (forward-sexp) 300 | (when (looking-back close-regexp) 301 | (setq end (point)))) 302 | (if (>= count 0) 303 | (setq beg (point)) 304 | (setq count (1- count)) nil)))))) 305 | (if include-parentheses 306 | (list beg end) 307 | (setq beg (prog1 (min (1+ beg) end) 308 | (setq end (max (1- end) beg)))) 309 | (if (<= (count-lines beg end) 1) 310 | (list beg end) 311 | ;; multi-line inner range: select whole lines 312 | (goto-char beg) 313 | (when (looking-at "[ \f\t\n\r\v]*$") 314 | (forward-line) 315 | ;; Include indentation? 316 | (if (and viper-auto-indent 317 | (not (eq vimpulse-this-operator 318 | 'vimpulse-delete))) 319 | (back-to-indentation) 320 | (beginning-of-line)) 321 | (setq beg (point))) 322 | (goto-char end) 323 | (when (and (looking-back "^[ \f\t\n\r\v]*") 324 | (not (eq vimpulse-this-operator 325 | 'vimpulse-delete))) 326 | (setq end (line-end-position 0)) 327 | (goto-char end)) 328 | (list (min beg end) (max beg end)))))))) 329 | 330 | ;; quoted expressions 331 | (defun vimpulse-quote-range (count &optional quote include-quotes) 332 | "Return a quoted expression range (BEG END). 333 | QUOTE is a quote character (default ?\\\"). INCLUDE-QUOTES 334 | specifies whether to include the quote marks in the range." 335 | (let ((beg (point)) (end (point)) 336 | regexp) 337 | (save-excursion 338 | (setq count (if (eq count 0) 1 (abs count))) 339 | (setq quote (or quote ?\")) 340 | (setq quote (if (characterp quote) 341 | (regexp-quote (string quote)) "") 342 | regexp (concat "\\([^\\\\]\\|^\\)" quote)) 343 | (when (and (not (string= quote "")) 344 | (looking-at quote)) 345 | (forward-char)) 346 | ;; search forward for a closing quote 347 | (while (and (> count 0) 348 | (re-search-forward regexp nil t)) 349 | (setq count (1- count)) 350 | (setq end (point)) 351 | ;; find the matching opening quote 352 | (condition-case nil 353 | (progn 354 | (setq beg (scan-sexps end -1)) 355 | ;; Emacs' S-exp logic doesn't work in text mode 356 | (save-excursion 357 | (goto-char beg) 358 | (unless (looking-at quote) 359 | (re-search-backward regexp) 360 | (unless (looking-at quote) 361 | (forward-char)) 362 | (setq beg (point))))) 363 | ;; Finding the opening quote failed. Maybe we're already at 364 | ;; the opening quote and should look for the closing instead? 365 | (error (condition-case nil 366 | (progn 367 | (viper-backward-char-carefully) 368 | (setq beg (point)) 369 | (setq end (scan-sexps beg 1)) 370 | (unless (looking-back quote) 371 | (re-search-forward regexp) 372 | (unless (looking-back quote) 373 | (backward-char)) 374 | (setq end (point)))) 375 | (error (setq end beg)))))) 376 | (if include-quotes 377 | (list beg end) 378 | (list (min (1+ beg) end) (max (1- end) beg)))))) 379 | 380 | ;;; Text object definitions 381 | 382 | (vimpulse-define-text-object vimpulse-line (arg) 383 | "Select ARG lines." 384 | :type 'line 385 | (setq arg (1- arg)) 386 | (vimpulse-line-range 387 | (point) 388 | (save-excursion 389 | (when (> arg 0) 390 | (viper-next-line-carefully arg)) 391 | (point)))) 392 | 393 | (vimpulse-define-text-object vimpulse-a-word (arg) 394 | "Select a word." 395 | :keys "aw" 396 | (vimpulse-an-object-range 397 | arg 398 | (lambda (arg) 399 | (vimpulse-limit (line-beginning-position) (line-end-position) 400 | (viper-backward-word (cons arg ?r)))) 401 | (lambda (arg) 402 | (vimpulse-limit (line-beginning-position) (line-end-position) 403 | (viper-end-of-word (cons arg ?r)))))) 404 | 405 | (vimpulse-define-text-object vimpulse-inner-word (arg) 406 | "Select inner word." 407 | :keys "iw" 408 | (vimpulse-inner-object-range 409 | arg 410 | (lambda (arg) 411 | (vimpulse-limit (line-beginning-position) (line-end-position) 412 | (viper-backward-word (cons arg ?r)))) 413 | (lambda (arg) 414 | (vimpulse-limit (line-beginning-position) (line-end-position) 415 | (backward-char) 416 | (viper-end-of-word (cons arg ?r)))))) 417 | 418 | (vimpulse-define-text-object vimpulse-a-Word (arg) 419 | "Select a Word." 420 | :keys "aW" 421 | (vimpulse-an-object-range 422 | arg 423 | (lambda (arg) 424 | (vimpulse-limit (line-beginning-position) (line-end-position) 425 | (viper-backward-Word (cons arg ?r)))) 426 | (lambda (arg) 427 | (vimpulse-limit (line-beginning-position) (line-end-position) 428 | (viper-end-of-Word (cons arg ?r)))))) 429 | 430 | (vimpulse-define-text-object vimpulse-inner-Word (arg) 431 | "Select inner Word." 432 | :keys "iW" 433 | (vimpulse-inner-object-range 434 | arg 435 | (lambda (arg) 436 | (vimpulse-limit (line-beginning-position) (line-end-position) 437 | (viper-backward-Word (cons arg ?r)))) 438 | (lambda (arg) 439 | (vimpulse-limit (line-beginning-position) (line-end-position) 440 | (viper-end-of-Word (cons arg ?r)))))) 441 | 442 | (vimpulse-define-text-object vimpulse-a-sentence (arg) 443 | "Select a sentence." 444 | :keys "as" 445 | (vimpulse-an-object-range 446 | arg 447 | (lambda (arg) 448 | (viper-backward-sentence arg) 449 | (vimpulse-skip-regexp "[ \f\t\n\r\v]+" 1)) 450 | (lambda (arg) 451 | (viper-forward-sentence arg) 452 | (vimpulse-skip-regexp "[ \f\t\n\r\v]+" -1)))) 453 | 454 | (vimpulse-define-text-object vimpulse-inner-sentence (arg) 455 | "Select inner sentence." 456 | :keys "is" 457 | (vimpulse-inner-object-range 458 | arg 459 | (lambda (arg) 460 | (viper-backward-sentence arg) 461 | (vimpulse-skip-regexp "[ \f\t\n\r\v]+" 1)) 462 | (lambda (arg) 463 | (viper-forward-sentence arg) 464 | (vimpulse-skip-regexp "[ \f\t\n\r\v]+" -1)))) 465 | 466 | (vimpulse-define-text-object vimpulse-a-paragraph (arg) 467 | "Select a paragraph." 468 | :keys "ap" 469 | (vimpulse-an-object-range 470 | arg 471 | (lambda (arg) 472 | (vimpulse-skip-regexp "[ \f\t\n\r\v]+" -1) 473 | (viper-backward-paragraph arg) 474 | (vimpulse-skip-regexp "[ \f\t\n\r\v]+" 1)) 475 | (lambda (arg) 476 | (vimpulse-skip-regexp "[ \f\t\n\r\v]+" 1) 477 | (viper-forward-paragraph arg) 478 | (vimpulse-skip-regexp "[ \f\t\n\r\v]+" -1)) t)) 479 | 480 | (vimpulse-define-text-object vimpulse-inner-paragraph (arg) 481 | "Select inner paragraph." 482 | :keys "ip" 483 | (vimpulse-inner-object-range 484 | arg 485 | (lambda (arg) 486 | (vimpulse-skip-regexp "[ \f\t\n\r\v]+" -1) 487 | (viper-backward-paragraph arg) 488 | (vimpulse-skip-regexp "[ \f\t\n\r\v]+" 1)) 489 | (lambda (arg) 490 | (vimpulse-skip-regexp "[ \f\t\n\r\v]+" 1) 491 | (viper-forward-paragraph arg) 492 | (vimpulse-skip-regexp "[ \f\t\n\r\v]+" -1)))) 493 | 494 | (vimpulse-define-text-object vimpulse-a-paren (arg) 495 | "Select a parenthesis." 496 | :keys '("ab" "a(" "a)") 497 | (vimpulse-paren-range arg ?\( ?\) t)) 498 | 499 | (vimpulse-define-text-object vimpulse-inner-paren (arg) 500 | "Select inner parenthesis." 501 | :keys '("ib" "i(" "i)") 502 | (vimpulse-paren-range arg ?\( ?\))) 503 | 504 | (vimpulse-define-text-object vimpulse-a-bracket (arg) 505 | "Select a square bracket." 506 | :keys '("a[" "a]") 507 | (vimpulse-paren-range arg ?\[ ?\] t)) 508 | 509 | (vimpulse-define-text-object vimpulse-inner-bracket (arg) 510 | "Select inner square bracket." 511 | :keys '("i[" "i]") 512 | (vimpulse-paren-range arg ?\[ ?\])) 513 | 514 | (vimpulse-define-text-object vimpulse-a-curly (arg) 515 | "Select a curly bracket (\"brace\")." 516 | :keys '("aB" "a{" "a}") 517 | (vimpulse-paren-range arg ?{ ?} t)) 518 | 519 | (vimpulse-define-text-object vimpulse-inner-curly (arg) 520 | "Select inner curly bracket (\"brace\")." 521 | :keys '("iB" "i{" "i}") 522 | (vimpulse-paren-range arg ?{ ?})) 523 | 524 | (vimpulse-define-text-object vimpulse-an-angle (arg) 525 | "Select an angle bracket." 526 | :keys '("a<" "a>") 527 | (vimpulse-paren-range arg ?< ?> t)) 528 | 529 | (vimpulse-define-text-object vimpulse-inner-angle (arg) 530 | "Select inner angle bracket." 531 | :keys '("i<" "i>") 532 | (vimpulse-paren-range arg ?< ?>)) 533 | 534 | (vimpulse-define-text-object vimpulse-a-single-quote (arg) 535 | "Select a single-quoted expression." 536 | :keys "a'" 537 | (vimpulse-quote-range arg ?' t)) 538 | 539 | (vimpulse-define-text-object vimpulse-inner-single-quote (arg) 540 | "Select inner single-quoted expression." 541 | :keys "i'" 542 | (vimpulse-quote-range arg ?')) 543 | 544 | (vimpulse-define-text-object vimpulse-a-double-quote (arg) 545 | "Select a double-quoted expression." 546 | :keys "a\"" 547 | (vimpulse-quote-range arg ?\" t)) 548 | 549 | (vimpulse-define-text-object vimpulse-inner-double-quote (arg) 550 | "Select inner double-quoted expression." 551 | :keys "i\"" 552 | (vimpulse-quote-range arg ?\")) 553 | 554 | (provide 'vimpulse-text-object-system) 555 | -------------------------------------------------------------------------------- /vimpulse-misc-keybindings.el: -------------------------------------------------------------------------------- 1 | ;;;; Keybindings 2 | 3 | (require 'vimpulse-dependencies) 4 | (require 'vimpulse-modal) 5 | (require 'vimpulse-visual-mode) ; vimpulse-apply-on-block, vimpulse-visual-mode 6 | 7 | (declare-function vimpulse-range "vimpulse-operator" (&optional no-repeat dont-move-point whole-lines keep-visual custom-motion)) 8 | (declare-function vimpulse-set-replace-cursor-type "vimpulse-viper-function-redefinitions" nil) 9 | 10 | ;;; C-u 11 | 12 | (unless vimpulse-want-C-u-like-Vim 13 | (define-key viper-vi-basic-map "\C-u" 'universal-argument)) 14 | 15 | ;;; vi (command) mode keys 16 | 17 | (define-key viper-vi-basic-map "c" 'vimpulse-change) 18 | (define-key viper-vi-basic-map "d" 'vimpulse-delete) 19 | (define-key viper-vi-basic-map "g" nil) ; delete `viper-nil' binding 20 | (define-key viper-vi-basic-map "g?" 'vimpulse-rot13) 21 | (define-key viper-vi-basic-map "gU" 'vimpulse-upcase) 22 | (define-key viper-vi-basic-map "gb" 'vimpulse-end-of-previous-word) 23 | (define-key viper-vi-basic-map "gd" 'vimpulse-goto-definition) 24 | (define-key viper-vi-basic-map "gf" 'find-file-at-point) 25 | (define-key viper-vi-basic-map "gg" 'vimpulse-goto-first-line) 26 | (define-key viper-vi-basic-map "gh" 'backward-char) 27 | (define-key viper-vi-basic-map "ga" 'what-cursor-position) 28 | (define-key viper-vi-basic-map "gi" 'vimpulse-resume-insert) 29 | (define-key viper-vi-basic-map "gj" 'next-line) 30 | (define-key viper-vi-basic-map "gk" 'previous-line) 31 | (define-key viper-vi-basic-map "gl" 'forward-char) 32 | (define-key viper-vi-basic-map "gq" 'vimpulse-fill) 33 | (define-key viper-vi-basic-map "gu" 'vimpulse-downcase) 34 | (define-key viper-vi-basic-map "gw" 'vimpulse-fill) 35 | (define-key viper-vi-basic-map "g~" 'vimpulse-invert-case) 36 | (define-key viper-vi-basic-map "g0" 'vimpulse-beginning-of-visual-line) 37 | (define-key viper-vi-basic-map "g$" 'vimpulse-end-of-visual-line) 38 | (define-key viper-vi-basic-map "gJ" 'vimpulse-Join) 39 | (define-key viper-vi-basic-map "J" 'vimpulse-join) 40 | (define-key viper-vi-basic-map "K" 'woman) 41 | (define-key viper-vi-basic-map "m" 'vimpulse-mark-point) 42 | (define-key viper-vi-basic-map "`" 'vimpulse-goto-mark) 43 | (define-key viper-vi-basic-map "'" 'vimpulse-goto-mark-and-skip-white) 44 | (define-key viper-vi-basic-map "r" 'vimpulse-replace) 45 | (define-key viper-vi-basic-map "y" 'vimpulse-yank) 46 | (define-key viper-vi-basic-map "zb" 'viper-line-to-bottom) 47 | (define-key viper-vi-basic-map "zh" 'scroll-right) 48 | (define-key viper-vi-basic-map "zl" 'scroll-left) 49 | (define-key viper-vi-basic-map "zt" 'viper-line-to-top) 50 | (define-key viper-vi-basic-map "zz" 'viper-line-to-middle) 51 | (define-key viper-vi-basic-map "\C-]" 'vimpulse-jump-to-tag-at-point) 52 | (define-key viper-vi-basic-map "\C-t" 'pop-tag-mark) 53 | (define-key viper-vi-basic-map "=" 'vimpulse-indent) 54 | (define-key viper-vi-basic-map "_" 'viper-next-line-at-bol) 55 | (define-key viper-vi-basic-map "#" 'vimpulse-search-backward-for-symbol-at-point) 56 | (define-key viper-vi-basic-map "*" 'vimpulse-search-forward-for-symbol-at-point) 57 | (define-key viper-vi-basic-map "<" 'vimpulse-shift-left) 58 | (define-key viper-vi-basic-map ">" 'vimpulse-shift-right) 59 | (define-key viper-vi-basic-map "~" 'vimpulse-invert-char) 60 | (define-key viper-vi-basic-map "\"" 'vimpulse-read-register) 61 | (define-key viper-vi-basic-map "/" 'vimpulse-search-forward) 62 | (define-key viper-vi-basic-map "?" 'vimpulse-search-backward) 63 | (define-key viper-vi-kbd-map "/" nil) 64 | 65 | (vimpulse-map "]P" 'vimpulse-Put-and-indent) 66 | (vimpulse-map "]p" 'vimpulse-put-and-indent) 67 | 68 | ;; go to last change 69 | (when (fboundp 'goto-last-change) 70 | (define-key viper-vi-basic-map "g;" 'goto-last-change)) 71 | (when (fboundp 'goto-last-change-reverse) 72 | (define-key viper-vi-basic-map "g," 'goto-last-change-reverse)) 73 | 74 | ;; Visual bindings 75 | (define-key viper-vi-basic-map "v" 'vimpulse-visual-toggle-char) 76 | (define-key viper-vi-basic-map "V" 'vimpulse-visual-toggle-line) 77 | (define-key viper-vi-basic-map "\C-v" 'vimpulse-visual-toggle-block) 78 | (define-key viper-vi-basic-map "gv" 'vimpulse-visual-restore) 79 | 80 | ;; map undo and redo 81 | (define-key viper-vi-basic-map "u" 'undo) 82 | (cond 83 | ((fboundp 'undo-tree-redo) 84 | (define-key viper-vi-basic-map "\C-r" 'undo-tree-redo)) 85 | ((fboundp 'redo) 86 | (define-key viper-vi-basic-map "\C-r" 'redo))) 87 | 88 | ;; window manipulation 89 | (defvar vimpulse-window-map 90 | (let ((map (make-sparse-keymap))) 91 | (define-key map "\C-w" 'other-window) 92 | (define-key map "w" 'other-window) 93 | (define-key map "o" 'delete-other-windows) 94 | (define-key map "c" 'delete-window) 95 | (define-key map "s" 'split-window-vertically) 96 | (define-key map "v" 'split-window-horizontally) 97 | (define-key map "=" 'balance-windows) 98 | (when (fboundp 'windmove-left) 99 | (define-key map "h" 'windmove-left) 100 | (define-key map "j" 'windmove-down) 101 | (define-key map "k" 'windmove-up) 102 | (define-key map "l" 'windmove-right)) 103 | map) 104 | "Keymap for window-related commands. 105 | Equivalent to Vim's C-w prefix.") 106 | 107 | (define-key viper-vi-basic-map "\C-w" vimpulse-window-map) 108 | 109 | ;;; Insert mode keys 110 | 111 | ;; Vim-like completion keys 112 | (define-key viper-insert-basic-map "\C-p" 'vimpulse-abbrev-expand-before) 113 | (define-key viper-insert-basic-map "\C-n" 'vimpulse-abbrev-expand-after) 114 | (define-key viper-insert-basic-map "\C-x\C-p" 'vimpulse-expand-line) 115 | (define-key viper-insert-basic-map "\C-x\C-n" 'vimpulse-expand-line) 116 | (define-key viper-insert-basic-map [delete] 'delete-char) ; key 117 | ;; make ^[ work 118 | (define-key viper-insert-basic-map (kbd "ESC") 'viper-exit-insert-state) 119 | ;; paste 120 | (define-key viper-insert-basic-map "\C-r" 'vimpulse-paste-in-insert) 121 | ;; temporarily escape to vi state 122 | (define-key viper-insert-basic-map "\C-o" 'viper-escape-to-vi) 123 | 124 | ;;; " 125 | 126 | (defun vimpulse-read-register (&optional register command) 127 | "Use COMMAND with REGISTER. 128 | If called interactively, read REGISTER and COMMAND from keyboard." 129 | (interactive) 130 | (setq register (or register (read-char))) 131 | (when (viper-valid-register register) 132 | (setq command (or command (key-binding (read-key-sequence nil)))) 133 | (when (commandp command) 134 | (let ((this-command command) 135 | (viper-use-register register)) 136 | (call-interactively command))))) 137 | 138 | ;;; g0, g$ 139 | 140 | (defun vimpulse-beginning-of-visual-line (arg) 141 | "Go to beginning of `visual-line-mode' line." 142 | (interactive "p") 143 | (if (and (boundp 'visual-line-mode) visual-line-mode) 144 | (beginning-of-visual-line arg) 145 | ;; `move-beginning-of-line' instead of `beginning-of-line' 146 | ;; handles longlines-mode properly 147 | (move-beginning-of-line arg))) 148 | 149 | (defun vimpulse-end-of-visual-line (arg) 150 | "Go to end of `visual-line-mode' line." 151 | (interactive "p") 152 | (if (and (boundp 'visual-line-mode) visual-line-mode) 153 | (end-of-visual-line arg) 154 | ;; `move-end-of-line' instead of `end-of-line' 155 | ;; handles longlines-mode properly 156 | (move-end-of-line arg)) 157 | (unless (bolp) 158 | (backward-char))) 159 | 160 | ;;; gg 161 | 162 | (defun vimpulse-goto-first-line (arg) 163 | "Go to ARGth line (first line by default)." 164 | (interactive "P") 165 | (let ((val (viper-P-val arg)) 166 | (com (viper-getCom arg))) 167 | (when (eq com ?c) (setq com ?C)) 168 | (viper-move-marker-locally 'viper-com-point (point)) 169 | (viper-deactivate-mark) 170 | (push-mark nil t) 171 | (cond 172 | ((null val) 173 | (goto-char (point-min))) 174 | (t 175 | (viper-goto-line val))) 176 | (when com 177 | (viper-execute-com 'vimpulse-goto-line val com)))) 178 | 179 | ;;; gb 180 | 181 | (defun vimpulse-beginning-of-Word-p () 182 | (and (looking-at "[^[:space:]]") 183 | (or (bobp) 184 | (save-excursion 185 | (backward-char) 186 | (looking-at "[[:space:]]"))))) 187 | 188 | (defun vimpulse-end-of-previous-word (arg) 189 | "Move point to end of previous word." 190 | (interactive "P") 191 | (viper-leave-region-active) 192 | (let ((val (viper-p-val arg)) 193 | (com (viper-getcom arg))) 194 | (when com 195 | (viper-move-marker-locally 'viper-com-point (point))) 196 | (when (and (looking-at "[^[:space:]]") 197 | (not (vimpulse-beginning-of-Word-p))) 198 | (viper-backward-Word 1)) 199 | (when (bolp) 200 | (skip-syntax-backward " >")) 201 | (viper-backward-Word val) 202 | (let (viper-com-point) 203 | (viper-end-of-Word '(1 . ?r))) 204 | (unless com 205 | (backward-char)) 206 | (when com 207 | (viper-execute-com 'viper-end-of-word val com)))) 208 | 209 | ;;; gd 210 | 211 | (defun vimpulse-goto-definition () 212 | "Go to definition or first occurrence of symbol under cursor." 213 | (interactive) 214 | (let ((str (vimpulse-search-string (point) 'symbol)) 215 | ientry ipos) 216 | (cond 217 | ((string= str "") 218 | (error "No string under cursor")) 219 | ;; if imenu is available, try it 220 | ((or (fboundp 'imenu--make-index-alist) 221 | (load "imenu" t)) 222 | (setq ientry 223 | (condition-case nil 224 | (and (fboundp 'imenu--make-index-alist) 225 | (imenu--make-index-alist)) 226 | (error nil))) 227 | (setq ientry (assoc str ientry)) 228 | (setq ipos (cdr ientry)) 229 | (unless (markerp ipos) 230 | (setq ipos (cadr ientry))) 231 | (cond 232 | ;; imenu found a position, so go there and 233 | ;; highlight the occurrence 234 | ((and (markerp ipos) 235 | (eq (marker-buffer ipos) (current-buffer))) 236 | (vimpulse-search-for-symbol nil ipos str)) 237 | ;; imenu failed, so just go to first occurrence in buffer 238 | (t 239 | (vimpulse-search-for-symbol nil (point-min))))) 240 | ;; no imenu, so just go to first occurrence in buffer 241 | (t 242 | (vimpulse-search-for-symbol nil (point-min)))))) 243 | 244 | (defun vimpulse-jump-to-tag-at-point () 245 | (interactive) 246 | (let ((tag (thing-at-point 'word))) 247 | (find-tag tag))) 248 | 249 | ;;; gi 250 | 251 | (defun vimpulse-resume-insert (arg) 252 | "Insert at previous insert position." 253 | (interactive "P") 254 | (when (markerp vimpulse-exit-point) 255 | (goto-char vimpulse-exit-point)) 256 | (viper-insert arg)) 257 | 258 | ;;; *, # 259 | 260 | (defun vimpulse-search-string (&optional pos thing backward regexp) 261 | "Find something to search for near POS or point. 262 | THING is a `thing-at-point', default `symbol'. 263 | BACKWARD, if t, specifies reverse direction. 264 | REGEXP, if t, means the string is `regexp-quote'd. 265 | Returns the empty string if nothing is found." 266 | (save-excursion 267 | (setq pos (or pos (point)) 268 | thing (or thing 'symbol)) 269 | (goto-char pos) 270 | (let ((str (thing-at-point thing))) 271 | ;; if there's nothing under point, go forwards 272 | ;; (or backwards) to find it 273 | (while (and (null str) 274 | (or (and backward (not (bobp))) 275 | (and (not backward) (not (eobp))))) 276 | (if backward (backward-char) (forward-char)) 277 | (setq str (thing-at-point thing))) 278 | (setq str (or str "")) 279 | ;; no text properties, thank you very much 280 | (set-text-properties 0 (length str) nil str) 281 | (when regexp 282 | (setq str (regexp-quote str))) 283 | str))) 284 | 285 | (defun vimpulse-search-for-symbol (&optional backward pos search) 286 | "Search forwards or backwards for the symbol under point. 287 | If BACKWARD is t, search in the reverse direction. 288 | SEARCH is a regular expression to use for searching instead of 289 | the symbol under point; it is wrapped in \"\\\\_<\" and \"\\\\_>\". 290 | POS specifies an alternative position to search from. Note that 291 | if POS is specified and at the beginning of a match, that match 292 | is highlighted rather than skipped past." 293 | (setq search (or search (vimpulse-search-string 294 | (point) 'symbol backward t))) 295 | (cond 296 | ((string= search "") 297 | (error "No string under cursor")) 298 | (t 299 | (setq viper-s-string (concat "\\_<" search "\\_>") 300 | viper-s-forward (not backward)) 301 | (cond 302 | (pos 303 | (unless (region-active-p) 304 | (push-mark nil t)) 305 | (goto-char pos) 306 | (cond 307 | ((looking-at search) 308 | (save-excursion 309 | (search-forward search)) 310 | (viper-flash-search-pattern)) 311 | (t 312 | (viper-search viper-s-string (not backward) 1) 313 | (unless (region-active-p) 314 | (pop-mark))))) 315 | (t 316 | (viper-search viper-s-string (not backward) 1)))))) 317 | 318 | (defun vimpulse-search-forward-for-symbol-at-point () 319 | (interactive) 320 | (cond 321 | ((and (stringp viper-s-string) 322 | (not (string= viper-s-string "")) 323 | (memq last-command 324 | '(vimpulse-search-backward-for-symbol-at-point 325 | vimpulse-search-forward-for-symbol-at-point))) 326 | (setq viper-s-forward t) 327 | (viper-search-next 1)) 328 | (t 329 | (vimpulse-search-for-symbol)))) 330 | 331 | (defun vimpulse-search-backward-for-symbol-at-point () 332 | (interactive) 333 | (cond 334 | ((and (stringp viper-s-string) 335 | (not (string= viper-s-string "")) 336 | (memq last-command 337 | '(vimpulse-search-backward-for-symbol-at-point 338 | vimpulse-search-forward-for-symbol-at-point))) 339 | (setq viper-s-forward nil) 340 | (viper-search-next 1)) 341 | (t 342 | (vimpulse-search-for-symbol t)))) 343 | 344 | ;;; Auto-indent 345 | 346 | (defun vimpulse-autoindent () 347 | "Auto Indentation, Vim-style." 348 | (interactive) 349 | (let ((col (current-indentation))) 350 | (if viper-preserve-indent 351 | (setq viper-preserve-indent nil) 352 | (setq viper-current-indent col)) 353 | ;; don't leave whitespace lines around 354 | (if (memq last-command 355 | '(viper-autoindent 356 | viper-open-line viper-Open-line 357 | viper-replace-state-exit-cmd)) 358 | (indent-to-left-margin)) 359 | (when viper-auto-indent 360 | (setq viper-cted t) 361 | (if (and viper-electric-mode 362 | (not (memq major-mode 363 | '(fundamental-mode 364 | text-mode 365 | paragraph-indent-text-mode)))) 366 | (if (fboundp 'comment-indent-new-line) 367 | (comment-indent-new-line) 368 | (newline-and-indent)) 369 | (newline) 370 | (indent-to col))))) 371 | 372 | (defun vimpulse-Put-and-indent (&optional arg) 373 | "Put before point/line and indent to current line. 374 | Doesn't indent with a prefix argument." 375 | (interactive "P") 376 | (viper-Put-back nil) 377 | (unless arg 378 | (indent-region (region-beginning) (region-end)))) 379 | 380 | (defun vimpulse-put-and-indent (&optional arg) 381 | "Put after point/line and indent to current line. 382 | Doesn't indent with a prefix argument." 383 | (interactive "P") 384 | (viper-put-back nil) 385 | (unless arg 386 | (indent-region (region-beginning) (region-end)))) 387 | 388 | (defalias 'viper-autoindent 'vimpulse-autoindent) 389 | 390 | ;;; C-o, C-i 391 | 392 | (defadvice set-mark (after vimpulse activate) 393 | "Clear `vimpulse-mark-list'." 394 | (mapc (lambda (marker) 395 | (set-marker marker nil)) 396 | vimpulse-mark-list) 397 | (setq vimpulse-mark-list nil)) 398 | 399 | (defadvice push-mark (after vimpulse activate) 400 | "Clear `vimpulse-mark-list'." 401 | (mapc (lambda (marker) 402 | (set-marker marker nil)) 403 | vimpulse-mark-list) 404 | (setq vimpulse-mark-list nil)) 405 | 406 | (defun vimpulse-jump-backward (arg) 407 | "Go to older position in jump list. 408 | \\To go the other way, \ 409 | press \\[vimpulse-jump-forward]." 410 | (interactive "p") 411 | (let ((current-pos (make-marker)) i) 412 | (unless vimpulse-mark-list 413 | (move-marker current-pos (point)) 414 | (add-to-list 'vimpulse-mark-list current-pos)) 415 | (dotimes (arg arg) 416 | (setq current-pos (make-marker)) 417 | ;; skip past duplicate entries in the mark ring 418 | (setq i (length mark-ring)) 419 | (while (progn (move-marker current-pos (point)) 420 | (let (vimpulse-mark-list) 421 | ;; protect `vimpulse-mark-list' 422 | (set-mark-command 0)) 423 | (setq i (1- i)) 424 | (and (= (point) current-pos) (> i 0)))) 425 | ;; Already there? 426 | (move-marker current-pos (point)) 427 | (unless (= (car vimpulse-mark-list) current-pos) 428 | (setq vimpulse-mark-list 429 | (cons current-pos vimpulse-mark-list)))))) 430 | 431 | (defun vimpulse-jump-forward (arg) 432 | "Go to newer position in jump list. 433 | \\To go the other way, \ 434 | press \\[vimpulse-jump-backward]." 435 | (interactive "p") 436 | (let (current-pos next-pos) 437 | (dotimes (arg arg) 438 | (setq current-pos (car vimpulse-mark-list) 439 | next-pos (cadr vimpulse-mark-list)) 440 | (when next-pos 441 | ;; protect `vimpulse-mark-list' 442 | (let (vimpulse-mark-list) 443 | (push-mark current-pos t nil)) 444 | (unless (eq (marker-buffer next-pos) (current-buffer)) 445 | (switch-to-buffer (marker-buffer next-pos))) 446 | (goto-char next-pos) 447 | (setq vimpulse-mark-list (cdr vimpulse-mark-list)))))) 448 | 449 | (when vimpulse-want-C-i-like-Vim 450 | (define-key viper-vi-basic-map "\C-i" 'vimpulse-jump-forward)) 451 | (define-key viper-vi-basic-map "\C-o" 'vimpulse-jump-backward) 452 | 453 | ;;; Replace backspace 454 | 455 | (defcustom vimpulse-backspace-restore t 456 | "Whether Backspace restores the original text in Replace mode. 457 | On by default." 458 | :group 'vimpulse 459 | :type 'boolean) 460 | 461 | (defun vimpulse-replace-pre-command () 462 | "Remember the character under point." 463 | (cond 464 | (viper-replace-minor-mode 465 | (unless (assq (point) vimpulse-replace-alist) 466 | (add-to-list 'vimpulse-replace-alist 467 | (cons (point) (char-after))))) 468 | ;; if not in Replace mode, remove itself 469 | (t 470 | (remove-hook 'pre-command-hook 'vimpulse-replace-pre-command)))) 471 | 472 | (add-hook 'viper-replace-state-hook 473 | (lambda () 474 | (setq vimpulse-replace-alist nil) 475 | (vimpulse-replace-pre-command) 476 | (add-hook 'pre-command-hook 477 | 'vimpulse-replace-pre-command))) 478 | 479 | (defun vimpulse-replace-backspace () 480 | "Restore character under cursor. 481 | If `vimpulse-backspace-restore' is nil, 482 | call `viper-del-backward-char-in-replace' instead." 483 | (interactive) 484 | (cond 485 | (vimpulse-backspace-restore 486 | (backward-char) 487 | (let ((oldchar (cdr (assq (point) vimpulse-replace-alist)))) 488 | (when oldchar 489 | (save-excursion 490 | (delete-char 1) 491 | (insert oldchar))))) 492 | (t 493 | (viper-del-backward-char-in-replace)))) 494 | 495 | (defadvice viper-adjust-keys-for (after vimpulse activate) 496 | "Map to `vimpulse-replace-backspace' in Replace mode." 497 | (define-key viper-replace-map [backspace] 'vimpulse-replace-backspace)) 498 | 499 | (defvar dabbrev--last-abbrev-location) 500 | (defvar dabbrev--last-abbreviation) 501 | (defvar dabbrev--last-direction) 502 | (defvar dabbrev--last-expansion) 503 | (defvar dabbrev--last-expansion-location) 504 | 505 | (defun vimpulse-abbrev-expand-before () 506 | "Expand to the nearest preceding word. 507 | Search forwards if a match isn't found." 508 | (interactive) 509 | (if (minibufferp) 510 | (minibuffer-complete) 511 | (dabbrev-expand nil))) 512 | 513 | ;; Getting dabbrev to search forwards first and then backwards 514 | ;; is tricky, because (dabbrev-expand -1) just fails when it 515 | ;; doesn't find a following match. 516 | (defun vimpulse-abbrev-expand-after () 517 | "Expand to the nearest following word. 518 | Search backwards if a match isn't found." 519 | (interactive) 520 | ;; back up global variables 521 | (let ((abbrev (and (boundp 'dabbrev--last-abbreviation) 522 | dabbrev--last-abbreviation)) 523 | (abbrev-loc (and (boundp 'dabbrev--last-abbrev-location) 524 | dabbrev--last-abbrev-location)) 525 | (expansion (and (boundp 'dabbrev--last-expansion) 526 | dabbrev--last-expansion)) 527 | (expansion-loc (and (boundp 'dabbrev--last-expansion-location) 528 | dabbrev--last-expansion-location))) 529 | ;; expand in same direction as previously, initially forward 530 | (if (minibufferp) 531 | (minibuffer-complete) 532 | (condition-case nil 533 | (if (eq last-command this-command) 534 | (dabbrev-expand nil) 535 | (setq dabbrev--last-direction -1) 536 | (dabbrev-expand -1)) 537 | ;; restore dabbrev variables if version < 23.2 538 | (error (progn 539 | (when (version< emacs-version "23.2") 540 | (setq dabbrev--last-abbreviation abbrev 541 | dabbrev--last-abbrev-location abbrev-loc 542 | dabbrev--last-expansion expansion 543 | dabbrev--last-expansion-location expansion-loc)) 544 | (setq dabbrev--last-direction 1) 545 | (dabbrev-expand nil) nil)))))) 546 | 547 | (defun vimpulse-expand-line (&optional arg) 548 | "Expand a whole line." 549 | (interactive "P") 550 | (let ((hippie-expand-try-functions-list 551 | '(try-expand-line 552 | try-expand-line-all-buffers))) 553 | (hippie-expand arg))) 554 | 555 | ;;; i_CTRL-Y, i_CTRL-E 556 | 557 | (defun vimpulse-copy-from-above (arg) 558 | "Copy characters from preceding non-blank line. 559 | The copied text is inserted before point. 560 | ARG is the number of lines to move backward." 561 | (interactive 562 | (cond 563 | ;; if a prefix argument was given, repeat it for subsequent calls 564 | ((and (null current-prefix-arg) 565 | (eq last-command 'vimpulse-copy-from-above)) 566 | (setq current-prefix-arg last-prefix-arg) 567 | (list (prefix-numeric-value current-prefix-arg))) 568 | (t 569 | (list (prefix-numeric-value current-prefix-arg))))) 570 | (insert (vimpulse-copy-chars-from-line 1 (- arg)))) 571 | 572 | (defun vimpulse-copy-from-below (arg) 573 | "Copy characters from following non-blank line. 574 | The copied text is inserted before point. 575 | ARG is the number of lines to move forward." 576 | (interactive 577 | (cond 578 | ((and (null current-prefix-arg) 579 | (eq last-command 'vimpulse-copy-from-below)) 580 | (setq current-prefix-arg last-prefix-arg) 581 | (list (prefix-numeric-value current-prefix-arg))) 582 | (t 583 | (list (prefix-numeric-value current-prefix-arg))))) 584 | (insert (vimpulse-copy-chars-from-line 1 arg))) 585 | 586 | ;; adapted from `copy-from-above-command' from misc.el 587 | (defun vimpulse-copy-chars-from-line (n num &optional col) 588 | "Return N characters from line NUM, starting at column COL. 589 | NUM is relative to the current line and can be negative. 590 | COL defaults to the current column." 591 | (interactive "p") 592 | (let ((col (or col (current-column))) prefix) 593 | (save-excursion 594 | (forward-line num) 595 | (when (looking-at "[[:space:]]*$") 596 | (if (< num 0) 597 | (skip-chars-backward " \t\n") 598 | (skip-chars-forward " \t\n"))) 599 | (beginning-of-line) 600 | (move-to-column col) 601 | ;; if the column winds up in middle of a tab, 602 | ;; return the appropriate number of spaces 603 | (when (< col (current-column)) 604 | (if (eq (preceding-char) ?\t) 605 | (let ((len (min n (- (current-column) col)))) 606 | (setq prefix (make-string len ?\s) 607 | n (- n len))) 608 | ;; if in middle of a control char, return the whole char 609 | (backward-char 1))) 610 | (concat prefix 611 | (buffer-substring (point) 612 | (min (line-end-position) 613 | (+ n (point)))))))) 614 | 615 | (define-key viper-insert-basic-map "\C-y" 'vimpulse-copy-from-above) 616 | (define-key viper-insert-basic-map "\C-e" 'vimpulse-copy-from-below) 617 | 618 | ;; paste in Insert state by pressing "C-r " 619 | (defun vimpulse-paste-in-insert (register) 620 | "Paste in Insert state from REGISTER." 621 | (interactive (list (read-char))) 622 | (if (viper-valid-register register) 623 | (setq viper-use-register register) 624 | (setq viper-use-register nil)) 625 | (viper-Put-back nil) 626 | (forward-char)) 627 | 628 | (provide 'vimpulse-misc-keybindings) 629 | -------------------------------------------------------------------------------- /vimpulse-utils.el: -------------------------------------------------------------------------------- 1 | ;;;; General utility code used by all of Vimpulse; 2 | ;;;; may be useful to the end user 3 | 4 | (require 'vimpulse-dependencies) 5 | 6 | ;; macro helper 7 | (eval-and-compile 8 | (defun vimpulse-unquote (exp) 9 | "Return EXP unquoted." 10 | (if (and (listp exp) 11 | (eq (car exp) 'quote)) 12 | (cadr exp) 13 | exp))) 14 | 15 | (defmacro vimpulse-called-interactively-p () 16 | (if (version< emacs-version "23.2") 17 | '(called-interactively-p) 18 | '(called-interactively-p 'any))) 19 | 20 | ;;; Autogenerated vi bindings 21 | 22 | (defun vimpulse-augment-keymap 23 | (map augment-alist &optional replace) 24 | "Augment MAP with bindings from AUGMENT-ALIST. 25 | If REPLACE is non-nil, bindings in MAP may be overwritten. 26 | AUGMENT-ALIST has the format ((KEY . DEF) ...), 27 | where KEY and DEF are passed to `define-key'." 28 | (let (key def num) 29 | (dolist (binding augment-alist) 30 | (setq key (car binding) 31 | def (cdr binding) 32 | num (lookup-key map key)) 33 | (cond 34 | (replace 35 | (when (numberp num) 36 | (define-key map (vimpulse-truncate key num) nil)) 37 | (define-key map key def)) 38 | (t 39 | (when (numberp num) 40 | (setq num (lookup-key map (vimpulse-truncate key num)))) 41 | (unless num 42 | (define-key map key def))))))) 43 | 44 | (defun vimpulse-add-vi-bindings (map cmds &optional replace filter) 45 | "Add vi bindings for CMDS to MAP. 46 | Add forcefully if REPLACE is t. Don't add keys matching FILTER, 47 | which is a list of key vectors." 48 | (let ((bindings (apply 'vimpulse-get-vi-bindings cmds))) 49 | (unless filter 50 | (when (and (boundp 'viper-want-ctl-h-help) 51 | viper-want-ctl-h-help) 52 | (add-to-list 'filter [?\C-h])) 53 | (unless (and (boundp 'vimpulse-want-C-u-like-Vim) 54 | vimpulse-want-C-u-like-Vim) 55 | (add-to-list 'filter [?\C-u]))) 56 | (dolist (key filter) 57 | (setq bindings (assq-delete-all key bindings))) 58 | (vimpulse-augment-keymap map bindings replace))) 59 | 60 | (defun vimpulse-get-bindings (cmd &rest maps) 61 | "Return assocation list of bindings for CMD in MAPS." 62 | (let (keys bindings) 63 | (setq maps (or maps '(nil))) 64 | (dolist (map maps bindings) 65 | (unless (keymapp map) 66 | (setq map (eval map))) 67 | (setq keys (where-is-internal cmd map)) 68 | (dolist (key keys) 69 | (unless (assq key bindings) 70 | (add-to-list 'bindings (cons key cmd) t)))))) 71 | 72 | (defun vimpulse-get-vi-bindings (&rest cmds) 73 | "Return assocation list of vi bindings for CMDS." 74 | (let (bindings) 75 | (dolist (cmd cmds bindings) 76 | (dolist (binding (apply 'vimpulse-get-bindings cmd 77 | '(viper-vi-intercept-map 78 | viper-vi-local-user-map 79 | viper-vi-global-user-map 80 | viper-vi-kbd-map 81 | viper-vi-diehard-map 82 | viper-vi-basic-map))) 83 | (unless (assq (car binding) bindings) 84 | (add-to-list 'bindings binding t)))))) 85 | 86 | (defun vimpulse-add-movement-cmds (map &optional replace) 87 | "Add Viper/Vimpulse movement commands to MAP. 88 | The commands are taken from `vimpulse-viper-movement-cmds' and looked 89 | up in vi keymaps. If REPLACE is non-nil, may overwrite bindings 90 | in MAP." 91 | (vimpulse-add-vi-bindings map vimpulse-viper-movement-cmds replace)) 92 | 93 | ;; the default for this function is to replace rather than augment, 94 | ;; as core navigation should be present everywhere 95 | (defun vimpulse-add-core-movement-cmds (map &optional augment) 96 | "Add \"core\" movement commands to MAP, forcefully. 97 | The commands are taken from `vimpulse-core-movement-cmds'. 98 | If AUGMENT is non-nil, don't overwrite bindings in MAP." 99 | (vimpulse-add-vi-bindings map 100 | vimpulse-core-movement-cmds 101 | (not augment))) 102 | 103 | (defun vimpulse-inhibit-cmds (map cmds &optional replace) 104 | "Remap CMDS to `viper-nil' in MAP. 105 | REPLACE is passed to `vimpulse-augment-keymap'." 106 | (vimpulse-augment-keymap 107 | map (mapcar (lambda (cmd) 108 | (cons `[remap ,cmd] 'viper-nil)) 109 | cmds) replace)) 110 | 111 | (defun vimpulse-inhibit-movement-cmds (map &optional replace) 112 | "Remap Viper movement commands to `viper-nil' in MAP. 113 | The commands are taken from `vimpulse-viper-movement-cmds'. 114 | If REPLACE is non-nil, may overwrite bindings in MAP." 115 | (vimpulse-inhibit-cmds map vimpulse-viper-movement-cmds replace)) 116 | 117 | (defun vimpulse-inhibit-other-movement-cmds (map &optional replace) 118 | "Remap non-core Viper movement commands to `viper-nil' in MAP. 119 | The commands are taken from `vimpulse-viper-movement-cmds'. 120 | If REPLACE is non-nil, may overwrite bindings in MAP." 121 | (let ((cmds vimpulse-viper-movement-cmds)) 122 | ;; remove core movement commands 123 | (dolist (cmd vimpulse-core-movement-cmds) 124 | (setq cmds (delq cmd cmds))) 125 | (vimpulse-inhibit-cmds map cmds replace))) 126 | 127 | (defun vimpulse-inhibit-destructive-cmds (map &optional replace) 128 | "Remap destructive Viper commands to `viper-nil' in MAP." 129 | (let ((cmds '(viper-Append 130 | viper-Insert 131 | viper-append 132 | viper-change-to-eol 133 | viper-command-argument 134 | viper-insert 135 | viper-kill-line 136 | viper-substitute 137 | viper-substitute-line 138 | vimpulse-change 139 | vimpulse-delete 140 | vimpulse-visual-append 141 | vimpulse-visual-insert))) 142 | (vimpulse-inhibit-cmds map cmds replace))) 143 | 144 | (defmacro vimpulse-remap (keymap from to) 145 | "Remap FROM to TO in KEYMAP. 146 | For XEmacs compatibility, KEYMAP should have a `remap-alist' 147 | property referring to a variable used for storing a \"remap 148 | association list\"." 149 | (if (featurep 'xemacs) 150 | `(let ((remap-alist (get ',keymap 'remap-alist)) 151 | (from ,from) (to ,to)) 152 | (when remap-alist 153 | (add-to-list remap-alist (cons from to)))) 154 | `(let ((keymap ,keymap) (from ,from) (to ,to)) 155 | (define-key keymap `[remap ,from] to)))) 156 | 157 | (defun vimpulse-vi-remap (from to &optional keymap) 158 | "Remap FROM to TO in vi (command) state. 159 | If KEYMAP is specified, take the keys that FROM is bound to 160 | in vi state and bind them to TO in KEYMAP." 161 | (if keymap 162 | (vimpulse-augment-keymap 163 | keymap 164 | (mapcar (lambda (binding) 165 | (cons (car binding) to)) 166 | (vimpulse-get-vi-bindings from))) 167 | (define-key viper-vi-basic-map `[remap ,from] to))) 168 | 169 | ;;; States 170 | 171 | (defmacro vimpulse-with-state (state &rest body) 172 | "Execute BODY with Viper state STATE, then restore previous state." 173 | (declare (indent defun) 174 | (debug t)) 175 | `(let ((new-viper-state ,state) 176 | (old-viper-state viper-current-state)) 177 | (unwind-protect 178 | (progn 179 | (viper-set-mode-vars-for new-viper-state) 180 | (let ((viper-current-state new-viper-state)) 181 | (viper-normalize-minor-mode-map-alist) 182 | ,@body)) 183 | (viper-set-mode-vars-for old-viper-state) 184 | (viper-normalize-minor-mode-map-alist)))) 185 | 186 | (when (fboundp 'font-lock-add-keywords) 187 | (font-lock-add-keywords 188 | 'emacs-lisp-mode 189 | '(("(\\(vimpulse-with-state\\)\\>" 1 font-lock-keyword-face)))) 190 | 191 | ;;; Vector tools 192 | 193 | (defun vimpulse-truncate (vector length &optional offset) 194 | "Return a copy of VECTOR truncated to LENGTH. 195 | If LENGTH is negative, skip last elements of VECTOR. 196 | If OFFSET is specified, skip first elements of VECTOR." 197 | ;; if LENGTH is too large, trim it 198 | (when (> length (length vector)) 199 | (setq length (length vector))) 200 | ;; if LENGTH is negative, convert it to the positive equivalent 201 | (when (< length 0) 202 | (setq length (max 0 (+ (length vector) length)))) 203 | (if offset 204 | (setq length (- length offset)) 205 | (setq offset 0)) 206 | (let ((result (make-vector length t))) 207 | (dotimes (idx length result) 208 | (aset result idx (aref vector (+ idx offset)))))) 209 | 210 | ;; This is useful for deriving a "standard" key-sequence from 211 | ;; `this-command-keys', to be looked up in `vimpulse-careful-alist'. 212 | (defun vimpulse-strip-prefix (key-sequence &optional string) 213 | "Strip any prefix argument keypresses from KEY-SEQUENCE. 214 | If STRING is t, output a string if possible. 215 | Otherwise output a vector." 216 | (let* ((offset 0) 217 | (temp-sequence (vconcat key-sequence)) 218 | (key (aref temp-sequence offset)) 219 | (length (length temp-sequence)) 220 | temp-string) 221 | ;; if XEmacs, get rid of the event object type 222 | (and (featurep 'xemacs) (eventp key) 223 | (setq key (event-to-character key nil t))) 224 | ;; any keys bound to `universal-argument', `digit-argument' or 225 | ;; `negative-argument' or bound in `universal-argument-map' 226 | ;; are considered prefix keys 227 | (while (and (or (memq (key-binding (vector key) t) 228 | '(universal-argument 229 | digit-argument 230 | negative-argument)) 231 | (lookup-key universal-argument-map 232 | (vector key))) 233 | (setq offset (1+ offset)) 234 | (< offset length)) 235 | (setq key (aref temp-sequence offset)) 236 | (and (featurep 'xemacs) (eventp key) 237 | (setq key (event-to-character key nil t)))) 238 | (if (and string 239 | ;; string conversion is impossible if the vector 240 | ;; contains a non-numerical element 241 | (not (memq nil (mapcar 'integerp (append temp-sequence nil))))) 242 | (concat (vimpulse-truncate temp-sequence length offset)) 243 | (vimpulse-truncate temp-sequence length offset)))) 244 | 245 | ;; GNU Emacs 22 lacks `characterp' 246 | (unless (fboundp 'characterp) 247 | (defalias 'characterp 'integerp)) 248 | 249 | ;;; Movement 250 | 251 | (defun vimpulse-move-to-column (column &optional dir force) 252 | "Move point to column COLUMN in the current line. 253 | Places point at left of the tab character (at the right 254 | if DIR is non-nil) and returns point. 255 | If `vimpulse-visual-block-untabify' is non-nil, then 256 | tabs are changed to spaces. (FORCE untabifies regardless.)" 257 | (interactive "p") 258 | (if (or vimpulse-visual-block-untabify force) 259 | (move-to-column column t) 260 | (move-to-column column) 261 | (when (or (not dir) (and (numberp dir) (< dir 1))) 262 | (when (> (current-column) column) 263 | (unless (bolp) 264 | (backward-char))))) 265 | (point)) 266 | 267 | (defmacro vimpulse-limit (start end &rest body) 268 | "Eval BODY, but limit point to buffer-positions START and END. 269 | Both may be nil. Returns position." 270 | (declare (indent 2) 271 | (debug t)) 272 | `(let ((start (or ,start (point-min))) 273 | (end (or ,end (point-max)))) 274 | (when (> start end) 275 | (setq start (prog1 end 276 | (setq end start)))) 277 | (save-restriction 278 | (narrow-to-region start end) 279 | ,@body 280 | (point)))) 281 | 282 | (defmacro vimpulse-skip (dir bounds &rest body) 283 | "Eval BODY, but limit point to BOUNDS in DIR direction. 284 | Returns position." 285 | (declare (indent 2) 286 | (debug t)) 287 | `(let ((dir ,dir) (bounds ,bounds) start end) 288 | (setq dir (if (and (numberp dir) (< dir 0)) -1 1)) 289 | (dolist (bound bounds) 290 | (unless (numberp bound) 291 | (setq bounds (delq bound bounds)))) 292 | (when bounds 293 | (if (< dir 0) 294 | (setq start (apply 'min bounds)) 295 | (setq end (apply 'max bounds)))) 296 | (vimpulse-limit start end ,@body))) 297 | 298 | (defun vimpulse-skip-regexp (regexp dir &rest bounds) 299 | "Move point in DIR direction based on REGEXP and BOUNDS. 300 | REGEXP is passed to `looking-at' or `looking-back'. 301 | If DIR is positive, move forwards to the end of the regexp match, 302 | but not beyond any buffer positions listed in BOUNDS. 303 | If DIR is negative, move backwards to the beginning of the match. 304 | Returns the new position." 305 | (setq dir (if (and (numberp dir) (< dir 0)) -1 1)) 306 | (setq regexp (or regexp "")) 307 | (vimpulse-skip dir bounds 308 | (if (< dir 0) 309 | (when (looking-back regexp nil t) 310 | (goto-char (match-beginning 0))) 311 | (when (looking-at regexp) 312 | (goto-char (match-end 0)))))) 313 | 314 | ;; XEmacs only has `looking-at' 315 | (unless (fboundp 'looking-back) 316 | (defun looking-back (regexp &optional limit greedy) 317 | "Return t if text before point matches regular expression REGEXP." 318 | (let ((start (point)) 319 | (pos 320 | (save-excursion 321 | (and (re-search-backward 322 | (concat "\\(?:" regexp "\\)\\=") limit t) 323 | (point))))) 324 | (if (and greedy pos) 325 | (save-restriction 326 | (narrow-to-region (point-min) start) 327 | (while (and (> pos (point-min)) 328 | (save-excursion 329 | (goto-char pos) 330 | (backward-char 1) 331 | (looking-at 332 | (concat "\\(?:" regexp "\\)\\'")))) 333 | (setq pos (1- pos))) 334 | (save-excursion 335 | (goto-char pos) 336 | (looking-at (concat "\\(?:" regexp "\\)\\'"))))) 337 | (not (null pos))))) 338 | 339 | (defun vimpulse-backward-up-list (&optional arg) 340 | "Like `backward-up-list', but breaks out of strings." 341 | (interactive "p") 342 | (let ((orig (point))) 343 | (setq arg (or arg 1)) 344 | (while (progn 345 | (condition-case 346 | nil (backward-up-list arg) 347 | (error nil)) 348 | (when (eq (point) orig) 349 | (backward-char) 350 | (setq orig (point))))))) 351 | 352 | ;;; Region 353 | 354 | ;; GNU Emacs 22 lacks `region-active-p' 355 | (unless (fboundp 'region-active-p) 356 | (defun region-active-p () 357 | (and transient-mark-mode mark-active))) 358 | 359 | (defun vimpulse-region-face () 360 | "Return face of region." 361 | (if (featurep 'xemacs) 'zmacs-region 'region)) 362 | 363 | (defun vimpulse-deactivate-region (&optional now) 364 | "Deactivate region, respecting Emacs version." 365 | (cond 366 | ((and (boundp 'cua-mode) cua-mode 367 | (fboundp 'cua--deactivate)) 368 | (cua--deactivate now)) 369 | ((featurep 'xemacs) 370 | (let ((zmacs-region-active-p t)) 371 | (zmacs-deactivate-region))) 372 | (now 373 | (setq mark-active nil)) 374 | (t 375 | (setq deactivate-mark t)))) 376 | 377 | (defun vimpulse-activate-region (&optional pos) 378 | "Activate mark if there is one. Otherwise set mark at point. 379 | If POS if specified, set mark at POS instead." 380 | (setq pos (or pos (mark t) (point))) 381 | (cond 382 | ((and (boundp 'cua-mode) cua-mode) 383 | (let ((opoint (point)) 384 | (oldmsg (current-message)) 385 | message-log-max 386 | cua-toggle-set-mark) 387 | (goto-char (or pos (mark t) (point))) 388 | (unwind-protect 389 | (and (fboundp 'cua-set-mark) 390 | (cua-set-mark)) 391 | (if oldmsg (message "%s" oldmsg) 392 | (message nil))) 393 | (goto-char opoint))) 394 | (t 395 | (let (this-command) 396 | (push-mark pos t t))))) 397 | 398 | (defun vimpulse-set-region (beg end &optional widen dir) 399 | "Set Emacs region to BEG and END. 400 | Preserves the order of point and mark, unless specified by DIR: 401 | a positive number means mark goes before or is equal to point, 402 | a negative number means point goes before mark. If WIDEN is 403 | non-nil, only modifies region if it does not already encompass 404 | BEG and END. Returns nil if region is unchanged." 405 | (cond 406 | (widen 407 | (vimpulse-set-region 408 | (min beg end (or (region-beginning) (point))) 409 | (max beg end (or (region-end) (point))) 410 | nil dir)) 411 | (t 412 | (unless (region-active-p) 413 | (vimpulse-activate-region)) 414 | (let* ((oldpoint (point)) 415 | (oldmark (or (mark t) oldpoint)) 416 | (newmark (min beg end)) 417 | (newpoint (max beg end))) 418 | (when (or (and (numberp dir) (< dir 0)) 419 | (and (not (numberp dir)) 420 | (< oldpoint oldmark))) 421 | (setq newpoint (prog1 newmark 422 | (setq newmark newpoint)))) 423 | (unless (or (and (numberp dir) 424 | (= (min newpoint newmark) 425 | (min oldpoint oldmark)) 426 | (= (max newpoint newmark) 427 | (max oldpoint oldmark))) 428 | (and (= newpoint oldpoint) 429 | (= newmark oldmark))) 430 | (set-mark newmark) 431 | (goto-char newpoint)))))) 432 | 433 | ;;; Overlays (extents in XEmacs) 434 | 435 | (eval-and-compile 436 | (cond 437 | ((featurep 'xemacs) ; XEmacs 438 | (defalias 'vimpulse-delete-overlay 'delete-extent) 439 | (defalias 'vimpulse-overlays-at 'extents-at)) 440 | (t ; GNU Emacs 441 | (defalias 'vimpulse-delete-overlay 'delete-overlay) 442 | (defalias 'vimpulse-overlays-at 'overlays-at)))) 443 | 444 | ;; `viper-make-overlay' doesn't handle FRONT-ADVANCE 445 | ;; and REAR-ADVANCE properly in XEmacs 446 | (defun vimpulse-make-overlay 447 | (beg end &optional buffer front-advance rear-advance) 448 | "Create a new overlay with range BEG to END in BUFFER. 449 | In XEmacs, create an extent." 450 | (cond 451 | ((featurep 'xemacs) 452 | (let ((extent (make-extent beg end buffer))) 453 | (set-extent-property extent 'start-open front-advance) 454 | (set-extent-property extent 'end-closed rear-advance) 455 | (set-extent-property extent 'detachable nil) 456 | extent)) 457 | (t 458 | (make-overlay beg end buffer front-advance rear-advance)))) 459 | 460 | (defun vimpulse-overlay-before-string (overlay string &optional face) 461 | "Set the `before-string' property of OVERLAY to STRING. 462 | In XEmacs, change the `begin-glyph' property." 463 | (cond 464 | ((featurep 'xemacs) 465 | (setq face (or face (get-text-property 0 'face string))) 466 | (when (and string (not (glyphp string))) 467 | (setq string (make-glyph string))) 468 | (when face 469 | (set-glyph-face string face)) 470 | (set-extent-begin-glyph overlay string)) 471 | (t 472 | (viper-overlay-put overlay 'before-string string)))) 473 | 474 | (defun vimpulse-overlay-after-string (overlay string &optional face) 475 | "Set the `after-string' property of OVERLAY to STRING. 476 | In XEmacs, change the `end-glyph' property." 477 | (cond 478 | ((featurep 'xemacs) 479 | (setq face (or face (get-text-property 0 'face string))) 480 | (when (and string (not (glyphp string))) 481 | (setq string (make-glyph string))) 482 | (when face 483 | (set-glyph-face string face)) 484 | (set-extent-end-glyph overlay string)) 485 | (t 486 | (viper-overlay-put overlay 'after-string string)))) 487 | 488 | ;;; Undo 489 | 490 | (viper-deflocalvar vimpulse-undo-list-pointer nil 491 | "Everything up to this mark is united in the undo-list.") 492 | 493 | (defun vimpulse-refresh-undo-step () 494 | "Refresh `buffer-undo-list' entries for current undo step. 495 | Undo boundaries until `vimpulse-undo-list-pointer' are removed 496 | to make the entries undoable as a single action. 497 | See `vimpulse-start-undo-step'." 498 | (setq buffer-undo-list 499 | (vimpulse-filter-list buffer-undo-list 'null 500 | vimpulse-undo-list-pointer))) 501 | 502 | (defun vimpulse-filter-list (list predicate &optional pointer) 503 | "Filter LIST for entries matching PREDICATE until POINTER. 504 | Returns a new list." 505 | (let ((rest list) elt result) 506 | (while (and rest (not (eq rest pointer))) 507 | (setq elt (car rest) 508 | rest (cdr rest)) 509 | (unless (funcall predicate elt) 510 | (setq result (append result (list elt))))) 511 | (append result rest))) 512 | 513 | (defun vimpulse-start-undo-step () 514 | "Start a single undo step. 515 | End the step with `vimpulse-end-undo-step'. 516 | All intermediate buffer modifications will be undoable as a 517 | single action." 518 | (when (listp buffer-undo-list) 519 | (unless (null (car buffer-undo-list)) 520 | (add-to-list 'buffer-undo-list nil)) 521 | (setq vimpulse-undo-list-pointer buffer-undo-list) 522 | ;; Continually refresh the undo entries for the step, 523 | ;; ensuring proper synchronization between `buffer-undo-list' 524 | ;; and `buffer-undo-tree' 525 | (add-hook 'post-command-hook 'vimpulse-refresh-undo-step nil t))) 526 | 527 | (defun vimpulse-end-undo-step () 528 | "End a single undo step. 529 | The step must have been started with `vimpulse-start-undo-step'. 530 | All intermediate buffer modifications will be undoable as a 531 | single action." 532 | (when (memq 'vimpulse-refresh-undo-step post-command-hook) 533 | (vimpulse-refresh-undo-step) 534 | (undo-boundary) 535 | (remove-hook 'post-command-hook 'vimpulse-refresh-undo-step t))) 536 | 537 | (add-hook 'viper-vi-state-hook 'vimpulse-end-undo-step) 538 | 539 | (defmacro vimpulse-single-undo (&rest body) 540 | "Execute BODY as a single undo step." 541 | (declare (indent 0) 542 | (debug t)) 543 | `(unwind-protect 544 | (progn 545 | (vimpulse-start-single-undo) 546 | ,@body) 547 | (vimpulse-end-single-undo))) 548 | 549 | (defun vimpulse-repeat-p () 550 | "Return non-nil if the current command is being repeated." 551 | (or (eq viper-intermediate-command 'viper-repeat) 552 | (eq this-command 'viper-repeat))) 553 | 554 | ;;; Motion type system 555 | 556 | (defun vimpulse-range-p (object) 557 | "Return t if OBJECT is a pure range (BEG END)." 558 | (and (listp object) 559 | (eq (length object) 2) 560 | (numberp (car object)) 561 | (numberp (cadr object)))) 562 | 563 | (defun vimpulse-motion-range-p (object) 564 | "Return t if OBJECT is a motion range (TYPE BEG END)." 565 | (and (listp object) 566 | (symbolp (car object)) 567 | (vimpulse-range-p (cdr object)))) 568 | 569 | (defun vimpulse-motion-range (object) 570 | "Return the range part of OBJECT." 571 | (cond 572 | ((vimpulse-motion-range-p object) 573 | (cdr object)) 574 | ((vimpulse-range-p object) 575 | object) 576 | (t 577 | (list (point) (point))))) 578 | 579 | (defun vimpulse-range-beginning (range) 580 | "Return the beginning of RANGE." 581 | (apply 'min (vimpulse-motion-range range))) 582 | 583 | (defun vimpulse-range-end (range) 584 | "Return the end of RANGE." 585 | (apply 'max (vimpulse-motion-range range))) 586 | 587 | (defun vimpulse-range-height (range &optional normalized) 588 | "Return height of RANGE. Normalize unless NORMALIZED. 589 | 590 | A block range has height and width, a line range 591 | has only height, and a character range has only width." 592 | (let* ((range (if normalized range 593 | (vimpulse-normalize-motion-range range))) 594 | (beg (vimpulse-range-beginning range)) 595 | (end (vimpulse-range-end range)) 596 | (type (vimpulse-motion-type range))) 597 | (cond 598 | ((eq type 'block) 599 | (count-lines beg 600 | (save-excursion 601 | (goto-char end) 602 | (if (and (bolp) (not (eobp))) 603 | (1+ end) 604 | end)))) 605 | ((eq type 'line) 606 | (count-lines beg end)) 607 | (t 608 | nil)))) 609 | 610 | (defun vimpulse-range-width (range &optional normalized) 611 | "Return width of RANGE. Normalize unless NORMALIZED. 612 | 613 | A block range has height and width, a line range 614 | has only height, and a character range has only width." 615 | (let* ((range (if normalized range 616 | (vimpulse-normalize-motion-range range))) 617 | (beg (vimpulse-range-beginning range)) 618 | (end (vimpulse-range-end range)) 619 | (type (vimpulse-motion-type range))) 620 | (cond 621 | ((eq type 'block) 622 | (abs (- (save-excursion 623 | (goto-char end) 624 | (current-column)) 625 | (save-excursion 626 | (goto-char beg) 627 | (current-column))))) 628 | ((eq type 'line) 629 | nil) 630 | (t 631 | (abs (- end beg)))))) 632 | 633 | (defun vimpulse-motion-type (object &optional raw) 634 | "Return motion type of OBJECT. 635 | The type is one of `exclusive', `inclusive', `line' and `block'. 636 | Defaults to `exclusive' unless RAW is specified." 637 | (let ((type (cond 638 | ((symbolp object) 639 | (get object 'motion-type)) 640 | ((vimpulse-motion-range-p object) 641 | (car object))))) 642 | (if raw 643 | type 644 | (or type 'exclusive)))) 645 | 646 | (defun vimpulse-make-motion-range (beg end &optional type normalize) 647 | "Return motion range (TYPE BEG END). 648 | If NORMALIZE is non-nil, normalize the range with 649 | `vimpulse-normalize-motion-range'." 650 | (let* ((range (list (min beg end) (max beg end))) 651 | (type (or type 'exclusive))) 652 | (if normalize 653 | (vimpulse-normalize-motion-range range type) 654 | (cons type range)))) 655 | 656 | ;; This implements section 1 of motion.txt (Vim Reference Manual) 657 | (defun vimpulse-normalize-motion-range (range &optional type) 658 | "Normalize the beginning and end of a motion range (TYPE FROM TO). 659 | Returns the normalized range. 660 | 661 | Usually, a motion range should be normalized only once, as 662 | information is lost in the process: an unnormalized motion range 663 | has the form (TYPE FROM TO), while a normalized motion range has 664 | the form (TYPE BEG END). 665 | 666 | See also `vimpulse-block-range', `vimpulse-line-range', 667 | `vimpulse-inclusive-range' and `vimpulse-exclusive-range'." 668 | (let* ((type (or type (vimpulse-motion-type range))) 669 | (range (vimpulse-motion-range range)) 670 | (from (car range)) 671 | (to (cadr range))) 672 | (cond 673 | ((memq type '(blockwise block)) 674 | (vimpulse-block-range from to)) 675 | ((memq type '(linewise line)) 676 | (vimpulse-line-range from to)) 677 | ((eq type 'inclusive) 678 | (vimpulse-inclusive-range from to)) 679 | (t 680 | (vimpulse-exclusive-range from to t))))) 681 | 682 | ;; Ranges returned by these functions have the form (TYPE BEG END) 683 | ;; where TYPE is one of `inclusive', `exclusive', `line' or `block' 684 | ;; and BEG and END are the buffer positions. 685 | (defun vimpulse-block-range (from to) 686 | "Return a blockwise motion range delimited by FROM and TO. 687 | Like `vimpulse-inclusive-range', but for rectangles: 688 | the last column is included." 689 | (let* ((beg (min from to)) 690 | (end (max from to)) 691 | (beg-col (save-excursion 692 | (goto-char beg) 693 | (current-column))) 694 | (end-col (save-excursion 695 | (goto-char end) 696 | (current-column)))) 697 | (save-excursion 698 | (cond 699 | ((= beg-col end-col) 700 | (goto-char end) 701 | (cond 702 | ((eolp) 703 | (goto-char beg) 704 | (if (eolp) 705 | (vimpulse-make-motion-range beg end 'block) 706 | (vimpulse-make-motion-range (1+ beg) end 'block))) 707 | ((eq vimpulse-block-orientation 'right) 708 | (vimpulse-make-motion-range (1+ beg) end 'block)) 709 | (t 710 | (vimpulse-make-motion-range beg (1+ end) 'block)))) 711 | ((< beg-col end-col) 712 | (goto-char end) 713 | (if (eolp) 714 | (vimpulse-make-motion-range beg end 'block) 715 | (vimpulse-make-motion-range beg (1+ end) 'block))) 716 | (t 717 | (goto-char beg) 718 | (if (eolp) 719 | (vimpulse-make-motion-range beg end 'block) 720 | (vimpulse-make-motion-range (1+ beg) end 'block))))))) 721 | 722 | (defun vimpulse-line-range (from to) 723 | "Return a linewise motion range delimited by FROM and TO." 724 | (let* ((beg (min from to)) 725 | (end (max from to))) 726 | (vimpulse-make-motion-range 727 | (save-excursion 728 | (goto-char beg) 729 | (line-beginning-position)) 730 | (save-excursion 731 | (goto-char end) 732 | (line-beginning-position 2)) 733 | 'line))) 734 | 735 | (defun vimpulse-inclusive-range (from to) 736 | "Return an inclusive motion range delimited by FROM and TO. 737 | That is, the last character is included." 738 | (let* ((beg (min from to)) 739 | (end (max from to))) 740 | (save-excursion 741 | (goto-char end) 742 | (unless (eobp) 743 | (setq end (1+ end))) 744 | (vimpulse-make-motion-range beg end 'inclusive)))) 745 | 746 | (defun vimpulse-exclusive-range (from to &optional normalize) 747 | "Return an exclusive motion range delimited by FROM and TO. 748 | However, if NORMALIZE is t and the end of the range is at the 749 | beginning of a line, a different type of range is returned: 750 | 751 | * If the start of the motion is at or before the first 752 | non-blank in the line, the motion becomes `line' (normalized). 753 | 754 | * Otherwise, the end of the motion is moved to the end of the 755 | previous line and the motion becomes `inclusive' (normalized)." 756 | (let* ((beg (min from to)) 757 | (end (max from to))) 758 | (save-excursion 759 | (cond 760 | ((and normalize 761 | (/= beg end) 762 | (progn 763 | (goto-char end) 764 | (bolp))) 765 | (viper-backward-char-carefully) 766 | (setq end (max beg (point))) 767 | (cond 768 | ((save-excursion 769 | (goto-char beg) 770 | (looking-back "^[ \f\t\v]*")) 771 | (vimpulse-make-motion-range beg end 'line t)) 772 | (t 773 | (vimpulse-make-motion-range beg end 'inclusive)))) 774 | (t 775 | (vimpulse-make-motion-range beg end 'exclusive)))))) 776 | 777 | (provide 'vimpulse-utils) 778 | --------------------------------------------------------------------------------