Initial darcs checkin, corresponds to v7 of the pre-darcs releases
Wed May 28 18:28:51 PDT 2008 larry@theclapp.org
* Initial darcs checkin, corresponds to v7 of the pre-darcs releases
diff -rN -u old-lw-vim-mode/bindings.lisp new-lw-vim-mode/bindings.lisp
--- old-lw-vim-mode/bindings.lisp 1969-12-31 16:00:00.000000000 -0800
+++ new-lw-vim-mode/bindings.lisp 2014-07-28 05:27:46.000000000 -0700
@@ -0,0 +1,279 @@
+(in-package :vim)
+
+(defun xlat-mode-abbrev (mode-abbrev)
+ (ecase mode-abbrev
+ (:c "Vim Command")
+ (:i "Vim Insert")
+ (:o "Vim Operator Pending")))
+
+(defmethod bind-vim ((name string) key (mode string))
+ (pushnew key *bound-vim-commands*)
+ ; (format t "~&~S bound to ~S in ~S mode~%" name key mode)
+ (bind-key name key :mode mode))
+(defmethod bind-vim ((name string) (keys list) (mode string))
+ (when (eql 'quote (car keys))
+ (error "Quoted list in bind-vim: ~S" keys))
+ (dolist (key keys)
+ (bind-vim name key mode)))
+(defmethod bind-vim ((name string) (keys string) (mode string))
+ (bind-vim name (make-array (list (length keys)) :initial-contents keys) mode))
+(defmethod bind-vim (name keys (mode symbol))
+ (bind-vim name keys (xlat-mode-abbrev mode)))
+(defmethod bind-vim (name keys (mode-list list))
+ (dolist (mode mode-list)
+ (bind-vim name keys mode)))
+
+(defun bind-vim-movement (name arg &key insert-too)
+ (bind-vim name arg '("Vim Command" "Vim Operator Pending"))
+ (when insert-too
+ (bind-vim name arg "Vim Insert")))
+(defun bind-vim-command (name arg)
+ (bind-vim name arg "Vim Command"))
+(defun bind-vim-insert (name arg)
+ (bind-vim name arg "Vim Insert"))
+(defun bind-vim-ci (name arg)
+ (bind-vim name arg '("Vim Command" "Vim Insert")))
+(defun bind-vim-table (table)
+ (mapcar (lambda (list)
+ (loop with command = (first list)
+ for (keys modes) on (rest list) by #'cddr do
+ (bind-vim command keys modes)))
+ table)
+ t ; return value not meaningful
+ )
+
+(defun add-return (&rest list)
+ (apply #'concatenate 'string (mapcar #'string (append list '(#\Return)))))
+
+;; Enter / Exit the modes
+(bind-key "All Vim" #\sh-c-escape :global :emacs)
+(bind-key "All Vim" #(#\c-\, #\c-\,) :global :emacs)
+
+;; Note to all: #\c-w is read as Ctrl-W, that is Ctrl-Shift-w. If you want
+;; Ctrl-w (lowercase-w) you need to say #\c-\w.
+(bind-vim-table
+ `(; control
+ ("Exit Vim Mode" (#\sh-c-escape #(#\c-\, #\c-\,)) :c)
+ ("Vim Insert Mode" #\i :c)
+ ("Vim Append" #\a :c)
+ ("Vim Append at EOL" #\A :c)
+ ("Vim Command Mode" (#\c-[ #\Escape) :i)
+ ("Illegal" (#\c-[ #\Escape) :c)
+
+ ; movement
+ ("Vim Previous Line" (#\k #\-) (:c :o)
+ (#\up #\c-\p) (:c :i :o))
+ ("Vim Previous Screen Line" "gk" (:c :o))
+ ("Vim Next Line" (#\j #\return) (:c :o)
+ (#\down #\c-\j #\c-\n) (:c :i :o))
+ ("Vim Next Screen Line" "gj" (:c :o))
+ ("Vim Backward Character" (#\h #\c-\h #\backspace) (:c :o)
+ #\left (:c :i :o))
+ ("Vim Forward Character" (#\l #\space) (:c :o)
+ #\right (:c :i :o))
+ ("Vim Forward Word" #\w (:c :o)
+ #\c-right (:c :i :o))
+ ("Vim Forward Word End" #\e (:c :o))
+ ("Vim Forward BIGWORD" #\W (:c :o))
+ ("Vim Forward BIGWORD End" #\E (:c :o))
+ ("Vim Backward Word" #\b (:c :o)
+ #\c-left (:c :i :o))
+ ("Vim Backward Word End" "ge" (:c :o))
+ ("Vim Backward BIGWORD" #\B (:c :o))
+ ("Vim Backward BIGWORD End" "gE" (:c :o))
+ ("Vim Goto Line or End of File" #\G (:c :o))
+ ("Vim Top of Window" #\H (:c :o))
+ ("Vim Bottom of Window" #\L (:c :o))
+ ("Vim Move to Window Line" #\M (:c :o))
+ ("Vim Goto Line or Top of Buffer" "gg" :c)
+ ("Vim Forward Form" #\) (:c :o))
+ ("Vim Backward Form" #\( (:c :o))
+ ("Vim Forward List" #\c-\) (:c :o))
+ ("Vim Backward List" #\c-\( (:c :o))
+ ("Vim Beginning of Line" ; for #\0 see Vim Beginning of Line or Collect Count
+ #\home (:c :i :o))
+ ("Vim End of Line" #\$ (:c :o)
+ #\end (:c :i :o))
+ ("Back to Indentation" #\^ (:c :o))
+
+ ("Beginning of Defun" "[\\" (:c :o))
+ ("End of Defun" "]\\" (:c :o))
+
+ ("Vim To Column N" #\| (:c :o))
+
+ ; scrolling
+ ("Scroll Window Down" #\c-\f (:c :o))
+ ("Scroll Window Up" #\c-\b (:c :o))
+ ("Vim Scroll Window Up" (#\c-\y #\sh-up) (:c :i :o))
+ ("Vim Scroll Window Down" (#\c-\e #\sh-down) (:c :i :o))
+ ("Vim Scroll Line to Top of Window" ("zt" #(#\z #\Return)) (:c :o))
+ ("Vim Scroll Line to Middle of Window" ("zz" "z.") (:c :o))
+ ("Vim Scroll Line to Bottom of Window" ("zb" "z-") (:c :o))
+
+ ; Repeating
+ ("Vim Repeat" #\. :c)
+ ("Vim Argument Digit" (#\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (:c :o))
+ ("Vim Beginning of Line or Collect Count" #\0 #| zero |# (:c :o))
+
+ ; changes
+ ("Vim Delete Next Character" #\x :c)
+ ("Vim Delete Previous Character" #\X :c)
+ ("Vim Replace Character" #\r :c)
+ ("Vim Append Text at End of Line" #\A :c)
+ ("Vim Insert Text at Beginning of Line" #\I :c)
+ ("Vim Delete Motion" #\d :c)
+ ("Vim Change Motion" #\c :c)
+ ("Kill Line" #\D :c)
+ ("Un-Kill" #\P :c)
+ ("Vim Move Over Whole Line" #\d :o) ; used for "dd"
+ ("Vim Move Over Inner Word" "iw" :o)
+ ("Vim Move Over A Word" "aw" :o)
+ ("Vim Move Over Inner BigWord" "iW" :o)
+ ("Vim Move Over A BigWord" "aW" :o)
+ ("Vim Open Line Down" #\o :c)
+ ("Vim Open Line Up" #\O :c)
+ ("Undo" #\u :c)
+
+ ("Lowercase Word" "~w" :c)
+ ("Lowercase Region" "vu" :c)
+ ("Uppercase Region" "vU" :c)
+
+ ("Indent Rigidly" ">>" :c)
+ ("Vim Join Lines" #\J :c)
+
+ ("Vim Insert Character Above Cursor" #\c-\y :i)
+ ("Vim Insert Character Below Cursor" #\c-\e :i)
+
+ ; buffer / window manipulation
+ ("New Buffer" ":n" :c)
+
+ ("New Window" #(#\c-\w #\n) :c)
+ ("Next Ordinary Window" #(#\c-\w #\j) :c)
+ ("Previous Window" #(#\c-\w #\k) :c)
+ ("Delete Window" #(#\c-\w #\c) :c)
+
+ ("List Buffers" ,(add-return ":ls") :c)
+ ("Circulate Buffers" ,(add-return ":bn") :c)
+ ("Select Buffer" ":b " :c)
+
+ ; search
+ ("ISearch Forward Regexp" #\/ (:c :o))
+ ("ISearch Backward Regexp" #\? (:c :o))
+ ("Vim Find Next" #\n (:c :o))
+ ("Vim Find Previous" #\N (:c :o))
+ ("List Matching Lines" ":gp" :c) ; not in Vim, exactly
+ ("Delete Matching Lines" ":gd" :c) ; not in Vim
+ ("Regexp Forward Search" #\c-\/ (:c :o))
+ ("Regexp Reverse Search" #\c-\? (:c :o))
+ ("Vim Find Char Right" #\f (:c :o))
+ ("Vim Find Char Left" #\F (:c :o))
+ ("Vim Find Till Char Right" #\t (:c :o))
+ ("Vim Find Till Char Left" #\T (:c :o))
+ ("Vim Repeat Last Find Char" #\; (:c :o))
+ ("Vim Repeat Last Find Char Opposite" #\, (:c :o))
+
+ ; File manipulation
+ ("Save File" ,(add-return ":w") :c)
+ ("Vim Save All Files" ,(add-return ":wa") :c)
+ ("Vim Save All Files And Exit" ,(add-return ":wqa") :c)
+ ("Write File" ":w " :c)
+ ("Wfind file" ":e " :c)
+ ("Vim Revert Buffer" ":e!" :c)
+ ("Insert File" ":r " :c)
+ ("Vim Goto File" "gf" :c)
+
+ ; tagging
+ ("Find Source" #\c-\] :c)
+ ("Continue Tags Search" ,(add-return ":tn") :c)
+ ("View Source Search" ,(add-return ":ts") :c)
+ ))
+
+;;; counts; may change later; for now just use Argument Digit
+; (bind-vim-command "Argument Digit" '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9))
+; (bind-vim-command "What Cursor Position" '(#(#\g #\Ctrl-\Meta-\g)))
+; (bind-vim-command "vim beginning of line or collect count" #\0)
+
+(defun restore-default-highlighting ()
+ ;; There may be a better way to do this. Like remembering all the
+ ;; highlighting we delete and replace, and restoring it. But this seems
+ ;; to work pretty well and pretty quickly, on the small files I've edited
+ ;; so far.
+ (font-lock-fontify-buffer-command nil))
+
+(defun highlight-search ()
+ (when editor::*last-search-string*
+ (restore-default-highlighting)
+ (let* ((buffer (current-buffer))
+ (start (buffers-start buffer))
+ (end (buffers-end buffer)))
+ ;; For some reason the highlighting doesn't show up unless I use this macro. ???
+ (with-hidden-font-lock-changes buffer
+ (with-point ((point start :temporary)
+ (form-start start :temporary))
+ (let ((pattern (make-fsa editor::*last-search-string*)))
+ (loop (let ((length (find-regexp-pattern point pattern t end)))
+ (unless length
+ (return))
+ (move-point form-start point)
+ (character-offset point length)
+ (font-lock-unfontify-region form-start point)
+ (font-lock-apply-highlight form-start point *font-lock-inverse-face*)))))
+ )
+ )))
+
+;;; Not Vim
+(bind-vim-table
+ '(("Evaluate Defun" ",x" :c)
+ ("Evaluate Defun In Listener" ",l" :c)))
+
+;;; Vim Insert mode
+; (bind-vim-insert "Indent New Line" #\Return)
+; (bind-vim-insert "New Line" #\Newline)
+; (bind-vim-insert "New Line" #\Return)
+
+;;; Larry's favorite mappings
+(bind-vim-table
+ '(("Vim Save All Files" #\c-F12 (:c :i))
+ ("List Buffers" #\F7 (:c :i))
+ ("Circulate Buffers" #\c-F10 :c)))
+
+;;; Ignore everything not defined
+
+(defun vim-command-char-p (char &optional (seq *bound-vim-commands*))
+ (find-if (lambda (item)
+ (or (eql char item)
+ (and (not (characterp item))
+ (not (keywordp item))
+ (vim-command-char-p char item))))
+ seq))
+
+(defmethod vim-ignore ((char character))
+ (unless (vim-command-char-p char)
+ ; (format t "Ignoring ~S~%" char)
+ (bind-key "Illegal" char :mode "Vim Command")))
+(defmethod vim-ignore ((char integer))
+ (vim-ignore (code-char char)))
+
+(loop for char across "abcdefghijklmnopqrstuvwxyz0123456789~`!@#$%^&*()_-+=[]{}\\|;':\",./<>?"
+ do (vim-ignore char)
+ if (both-case-p char)
+ do (vim-ignore (char-upcase char)))
+
+; Neither of these work; both expect Major Modes. :(
+; (pushnew "Vim Command" *default-new-buffer-modes*)
+; (pushnew "Vim Command" (variable-value "Default Modes"))
+
+#+nil
+(progn
+(defun foo (buffer enter)
+ (format t "buffer is ~S, enter is ~S~%" buffer enter))
+
+(add-hook 'foo 'vim-insert-mode-hook)
+(add-global-hook vim-insert-mode-hook 'foo)
+(remove-global-hook vim-insert-mode-hook 'foo)
+) ; progn
+
+(defadvice (editor::incremental-search-somehow after :after) (direction)
+ (declare (ignorable direction))
+ (when v+hlsearch
+ (highlight-search)))
diff -rN -u old-lw-vim-mode/changelog.mdwn new-lw-vim-mode/changelog.mdwn
--- old-lw-vim-mode/changelog.mdwn 1969-12-31 16:00:00.000000000 -0800
+++ new-lw-vim-mode/changelog.mdwn 2014-07-28 05:27:46.000000000 -0700
@@ -0,0 +1,106 @@
+v7, 5/19/2008
+
+General changes:
+
+* Works with LW 5.1. May not work any more with 5.0, haven't tried it.
+* Make Escape work like it should. As a necessary side effect, the use of
+ Escape for "Meta" changes to Ctrl-Escape, and the change is global to
+ the running image, whether a buffer is in a Vim mode or not. Ctrl-[ still
+ works, though.
+* Put all code in the :vim package instead of the :editor package. Did a
+ brute-force wrapper of most of the editor functions I use. Partly this
+ was to prepare for wrapping these commands for use of Kenny Tilton's
+ Cells library.
+* You can now `.` after an insert or append.
+* Counts on `.` work better now, maybe even correctly.
+* New macro: def-start-insert, used for `i`, `I`, `a`, and anything else that
+ starts an insert.
+
+Mappings changed:
+
+* Function formerly bound to `p` now bound to `P`, since it puts the cut
+ text in front of the current line.
+* `J` now joins with the *next* line instead of the *previous* line.
+
+Mappings added:
+
+* `Esc` in insert mode
+* `|` -- move to column
+* `P` -- Put cut text above the current line
+* `C-y`/`C-e`
+* `n`/`N` -- repeat most recent search, forward / backwards
+* `,l` -- evaluate code in Listener
+
+Not much to show for 2.5 months work, but on the other hand most of it was
+actually in the last few weeks. :)
+
+v6, 02/27/2008
+
+* Made `e` and `ge` "inclusive"
+* Separated `W` and `E` -- they used to map to the same command; now they
+ don't.
+* Renamed things like `Vim Forward WORD CAPS` to `Vim Forward BIGWORD`.
+* Added with-move macro; reimplemented many movement commands using it.
+
+Mappings changed
+
+* `W`/`E`
+
+Mappings Added
+
+* `E`/`gE`
+* `a`/`A`
+* `c{motion}`
+* `f`/`F`/`t`/`T`/`;`/`,`
+* `gf`
+
+v5, 12/29/2007
+
+* Forward and backward by vi-word and vi-WORD commands now work correctly.
+ These are commands w, W, b, B, e, ge. Hmm, looks like I missed E and gE.
+* Printing key bindings is commented out.
+
+Mappings changed:
+
+* `W`/`B`/`e`
+
+Mapping added:
+
+* `ge`
+
+v4, 12/14/2007
+
+* Add this changelog
+* Add check for quoted list in bind-vim
+* Reorganized/changed some bindings
+* Added most movement bindings to Operator Pending mode, to make
+ movement-pending commands work better.
+* Added commands and bindings for `zt`/`zz`/`zb` and `z<cr>`/`z.`/`z-`. The latter set
+ work just like the former set, i.e. they do not leave the cursor on the
+ first non-blank in the line.
+* Added commands and bindings for easy repeat counts; `12j` now works, you don't
+ have to say `c-u 12 j`.
+* Changed `:e!` to map to "Vim Revert Buffer", which resets the undo history
+ after it reads the file from disk, but it's still a bit wonky.
+* Kind of in the middle of adding an in-vim-command macro that takes care of
+ boilerplate start-vim-command / stop-vim-command stuff, so that when I add
+ new stuff that needs to happen at the start & end of a command, I only have
+ to change code in one place.
+* Added a setup-indent to def-vim-var-definer.
+* Added a new kind of vim-var, window vars; define via def-vim-window-var. I
+ suggest a `w-` prefix on all window vars, just like the `b-` prefix on all
+ buffer vars.
+* Added several hook variables, but don't actually use them yet.
+
+Commands / mappings added:
+
+* `-` (minus) to go up a line
+* `zt`/`zz`/`zb`, `z<cr>`/`z.`/`z-` (see note above)
+* repeat counts
+* `r<char>`
+* `gg`
+* `dd`
+
+Commands and mappings changed or fixed:
+
+* `d{movement}`
diff -rN -u old-lw-vim-mode/classes.lisp new-lw-vim-mode/classes.lisp
--- old-lw-vim-mode/classes.lisp 1969-12-31 16:00:00.000000000 -0800
+++ new-lw-vim-mode/classes.lisp 2014-07-28 05:27:46.000000000 -0700
@@ -0,0 +1,9 @@
+(in-package :vim)
+
+(defclass vim-var ()
+ ((name :initarg :name :accessor name-of)
+ (var-type :initarg :type :accessor var-type-of)
+ (values :initarg :values :accessor values-of)
+ (init-func :initarg :init-func :accessor init-func-of)
+ (doc :initarg :doc :accessor doc-of)))
+
diff -rN -u old-lw-vim-mode/commands.lisp new-lw-vim-mode/commands.lisp
--- old-lw-vim-mode/commands.lisp 1969-12-31 16:00:00.000000000 -0800
+++ new-lw-vim-mode/commands.lisp 2014-07-28 05:27:46.000000000 -0700
@@ -0,0 +1,318 @@
+(in-package :vim)
+
+;; Modes
+(defmode "Vim Command" :setup-function 'setup-vim-command-mode)
+(defmode "Vim Insert" :setup-function 'setup-vim-insert-mode)
+(defmode "Vim Operator Pending")
+
+(defvar *orig-meta-prefix-gesture-spec* editor::*meta-prefix-gesture-spec*)
+(defvar *orig-interrupt-keys* '(#\c-g))
+
+(defcommand "All Vim" (p)
+ "Put all buffers in Vim Command mode."
+ "Put all buffers in Vim Command mode."
+ (declare (ignore p))
+ (dolist (b editor::*buffer-list*)
+ (when (buffer-pathname b)
+ (use-buffer b
+ (vim-command-mode-command nil)))))
+
+(defcommand "Vim Command Mode" (p)
+ "Start Vim Command mode"
+ "Start Vim Command mode"
+ (declare (ignore p))
+ (when (vim-insert-mode-p)
+ ; Finish the repeat count on the i command itself, e.g. 3ix<esc> => xxx. This
+ ; doesn't work from inside copy-inserted-text. I think setf of buffer-minor-mode
+ ; locks the buffer, which I guess maybe makes sense.
+ (finish-repeated-insert))
+ (setf (buffer-minor-mode (current-buffer) "Vim Insert") nil)
+ (setf (buffer-minor-mode (current-buffer) "Vim Operator Pending") nil)
+ (setf (buffer-minor-mode (current-buffer) "Vim Command") t))
+
+(defcommand "Vim Mode" (p) "" ""
+ (vim-command-mode-command p))
+
+(def-start-insert "Vim Insert Mode" (p) "" ""
+ (declare (ignore p))
+ t)
+
+(defcommand "Exit Vim Mode" (p)
+ "Exit Vim Command Mode"
+ "Exit Vim Command Mode"
+ (declare (ignore p))
+ (dolist (mode '("Vim Command" "Vim Operator Pending" "Vim Insert"))
+ (setf (buffer-minor-mode (current-buffer) mode) nil))
+ (setf editor::*meta-prefix-gesture-spec* *orig-meta-prefix-gesture-spec*)
+ #+nil
+ (set-interrupt-keys *orig-interrupt-keys*))
+
+(def-start-insert "Vim Append" (p) "" ""
+ (declare (ignore p))
+ (forward-character-command 1))
+
+(def-start-insert "Vim Append at EOL" (p) "" ""
+ (end-of-line-command p))
+
+(defcommand "Vim Scroll Window Down" (p) "" ""
+ (scroll-window-down-command (or p 1)))
+
+(defcommand "Vim Scroll Window Up" (p) "" ""
+ (scroll-window-up-command (or p 1)))
+
+; Modified from Line to Top of Window from filecoms.lisp in the editor sources.
+(defcommand "Vim Scroll Line to Top of Window" (p) "" ""
+ (scroll-line-to-place p :top))
+
+(defcommand "Vim Scroll Line to Middle of Window" (p) "" ""
+ (scroll-line-to-place p :middle))
+
+(defcommand "Vim Scroll Line to Bottom of Window" (p) "" ""
+ (scroll-line-to-place p :bottom))
+
+(defcommand "Vim Repeat" (p) "" ""
+ (funcall *vim-last-action* p))
+
+(def-start-insert "Vim Append Text at End of Line" (p) "" ""
+ (declare (ignore p))
+ (end-of-line-command nil))
+
+(def-start-insert "Vim Insert Text at Beginning of Line" (p) "" ""
+ (declare (ignore p))
+ (back-to-indentation-command nil))
+
+(def-start-insert "Vim Open Line Down" (p) "" ""
+ (declare (ignore p))
+ (line-end (current-point))
+ (new-line-command nil))
+
+(def-start-insert "Vim Open Line Up" (p) "" ""
+ (declare (ignore p))
+ (line-start (current-point))
+ (open-line-command nil))
+
+(defcommand "Vim Save All Files" (p) "" ""
+ (declare (ignore p))
+ (save-all-files-command t))
+
+(defcommand "Vim Save All Files and Exit" (p) "" ""
+ (declare (ignore p))
+ (save-all-files-command t)
+ (lispworks-tools::confirm-quit-lispworks))
+
+;; This isn't really the right way (i.e. the Vim way) to do a
+;; setting like this. Oh well.
+#+nil
+(defcommand "Vim Toggle hlsearch" (p) "" ""
+ (setq v+hlsearch
+ (cond ((not p) (not v+hlsearch))
+ ((plusp p) t)
+ (t nil)))
+ (if v+hlsearch
+ (highlight-search)
+ (font-lock-fontify-buffer-command nil)))
+
+; (defcommand "Vim Jump To Match" (p) "Currently only works on ()" ""
+; (declare (ignore p))
+
+(def-vim-move "Vim Next Line" (p) :linewise :inclusive "" ""
+ (next-line-command p))
+(def-vim-move "Vim Next Screen Line" (p) nil :inclusive "" ""
+ (next-line-command p))
+(def-vim-move "Vim Previous Line" (p) :linewise :inclusive "" ""
+ (previous-line-command p))
+(def-vim-move "Vim Previous Screen Line" (p) nil :inclusive "" ""
+ (previous-line-command p))
+(def-vim-move "Vim Backward Character" (p) nil :exclusive "" ""
+ (backward-character-command p))
+(def-vim-move "Vim Forward Character" (p) nil :exclusive "" ""
+ (forward-character-command p))
+
+(def-vim-move "Vim Forward Word" (p) nil :exclusive "" ""
+ (let ((end nil))
+ (when (and (white-p (current-point))
+ (string= b-vim-movement-pending "Vim Change Motion"))
+ (setf end t
+ (exclusive) nil))
+ (vim-offset p :word t (current-point) :end end)))
+(def-vim-move "Vim Forward Word End" (p) nil :inclusive "" ""
+ (vim-offset p :word t (current-point) :end t))
+(def-vim-move "Vim Backward Word" (p) nil :exclusive "" ""
+ (vim-offset p :word nil (current-point)))
+(def-vim-move "Vim Backward Word End" (p) nil :inclusive "" ""
+ (vim-offset p :word nil (current-point) :end t))
+
+(def-vim-move "Vim Forward BIGWORD" (p) nil :exclusive "" ""
+ (let ((end nil))
+ (when (and (white-p (current-point))
+ (string= b-vim-movement-pending "Vim Change Motion"))
+ (setf end t
+ (exclusive) nil))
+ (vim-offset p :bigword t (current-point) :end end)))
+(def-vim-move "Vim Forward BIGWORD End" (p) nil :exclusive "" ""
+ (vim-offset p :bigword t (current-point) :end t))
+(def-vim-move "Vim Backward BIGWORD" (p) nil :inclusive "" ""
+ (vim-offset p :bigword nil (current-point)))
+(def-vim-move "Vim Backward BIGWORD End" (p) nil :inclusive "" ""
+ (vim-offset p :bigword nil (current-point) :end t))
+
+(def-vim-move "Vim Goto Line or End of File" (p) :linewise :inclusive "" ""
+ (if p
+ (goto-line-command p)
+ (end-of-buffer-command nil))
+ (back-to-indentation-command p))
+
+(def-vim-change "Vim Delete Next Character" (p) "" ""
+ (delete-next-character-command p))
+(def-vim-change "Vim Delete Previous Character" (p) "" ""
+ (delete-previous-character-command p))
+
+(def-vim-move "Vim Top of Window" (p) :linewise :inclusive "" ""
+ (top-of-window-command p))
+(def-vim-move "Vim Bottom of Window" (p) :linewise :inclusive "" ""
+ (bottom-of-window-command p))
+(def-vim-move "Vim Move to Window Line" (p) :linewise :inclusive "" ""
+ (move-to-window-line-command p))
+
+(def-vim-move "Vim Forward Form" (p) nil :exclusive "" ""
+ (forward-form-command p))
+(def-vim-move "Vim Backward Form" (p) nil :exclusive "" ""
+ (backward-form-command p))
+(def-vim-move "Vim Forward List" (p) nil :exclusive "" ""
+ (forward-list-command p))
+(def-vim-move "Vim Backward List" (p) nil :exclusive "" ""
+ (backward-list-command p))
+
+(def-vim-move "Vim Beginning of Line" (p) nil :exclusive "" ""
+ (beginning-of-line-command p))
+(def-vim-move "Vim End of Line" (p) nil :exclusive "" ""
+ (end-of-line-command p))
+(def-vim-move "Vim Goto Line or Top of Buffer" (p) :linewise :exclusive "" ""
+ (if p
+ (goto-line-command p)
+ (beginning-of-buffer-command p))
+ (back-to-indentation-command p))
+
+(def-vim-move "Vim Move Over Whole Line" (p) :linewise :exclusive "" ""
+ (line-start b-vim-point-before-movement)
+ (when (and p (> p 1))
+ (line-offset (current-point) (1- p)))
+ (line-end (current-point)))
+
+(def-vim-move "Vim Move Over Inner Word" (p) nil :inclusive "" ""
+ (move-over-word p :word t))
+
+(def-vim-move "Vim Move Over A Word" (p) nil :exclusive "" ""
+ (move-over-word p :word nil))
+
+(def-vim-move "Vim Move Over Inner BigWord" (p) nil :inclusive "" ""
+ (move-over-word p :bigword t))
+
+(def-vim-move "Vim Move Over A BigWord" (p) nil :exclusive "" ""
+ (move-over-word p :bigword nil))
+
+(defcommand "Vim Revert Buffer" (p) "" ""
+ (revert-buffer-command p)
+ (clear-undo-command p))
+
+(def-vim-movement-pending "Vim Delete Motion" (begin end) "" ""
+ (vim-delete-motion begin end))
+
+(def-vim-movement-pending "Vim Change Motion" (begin end) "" ""
+ (vim-delete-motion begin end)
+ (setf b-vim-movement-pending-ending-mode "Vim Insert"))
+
+(defcommand "Vim Argument Digit" (p) "" ""
+ (setf w-vim-collecting-count t)
+ (argument-digit-command p))
+
+(defcommand "Vim Beginning of Line or Collect Count" (p) "" ""
+ (if w-vim-collecting-count
+ (prompt-for-prefix 1 (* p 10))
+ (vim-beginning-of-line-command p)))
+
+(def-vim-change "Vim Replace Character" (p) "" ""
+ (let ((ch (vim-read-a-character)))
+ (with-point ((point (current-point))
+ (end (current-point)))
+ ;; Only proceed if there *are* p more characters in the buffer.
+ (if (character-offset end (or p 1))
+ (progn
+ (collect-undo (current-buffer)
+ (loop for n below (or p 1) do
+ (setf (next-character point) ch)
+ (character-offset point 1)))
+ (character-offset point -1))
+ (editor-error)))))
+
+;; FIXME: implement going to the p'th file in the path. First, need to
+;; implement path, though. :)
+(defcommand "Vim Goto File" (p) "" ""
+ (declare (ignorable p))
+ (let ((file (current-word :filename)))
+ ; (format t "file is ~A~%" file)
+ ; #+nil
+ (when file
+ (find-file-command nil (pathname file)))))
+
+(def-vim-move "Vim Find Char Right" (p) nil :inclusive "" ""
+ (vim-find-char t p :save t))
+
+(defcommand "Doesnt Work" (p)
+ (prompt-for-character* "Character: " :ignored)
+ (end-of-line-command p)
+ )
+
+(def-vim-move "Vim Find Char Left" (p) nil :exclusive "" ""
+ (vim-find-char nil p :save t))
+
+(def-vim-move "Vim Find Till Char Right" (p) nil :inclusive "" ""
+ (vim-find-char t p :leave-before t :save t))
+
+(def-vim-move "Vim Find Till Char Left" (p) nil :exclusive "" ""
+ (vim-find-char nil p :leave-before t :save t))
+
+(def-vim-move "Vim Repeat Last Find Char" (p) nil :exclusive "" ""
+ (vim-repeat-last-find p))
+
+(def-vim-move "Vim Repeat Last Find Char Opposite" (p) nil :exclusive "" ""
+ (vim-repeat-last-find p :invert t))
+
+(defun insert-character-at-offset (offset)
+ (with-point ((start (current-point))
+ (end (current-point)))
+ (when (line-offset start offset)
+ (move-point end start)
+ (character-offset end 1)
+ (insert-string (current-point)
+ (points-to-string start end)))))
+
+(defcommand "Vim Insert Character Above Cursor" (p) "" ""
+ (declare (ignore p))
+ (insert-character-at-offset -1))
+
+(defcommand "Vim Insert Character Below Cursor" (p) "" ""
+ (declare (ignore p))
+ (insert-character-at-offset 1))
+
+(def-vim-move "Vim To Column N" (p) nil :exclusive "" ""
+ (let ((point (current-point)))
+ (with-point ((end point))
+ (line-start point)
+ (line-end end)
+ (loop for n below (1- (or p 1))
+ while (point< point end)
+ do (character-offset point 1)))))
+
+(def-vim-change "Vim Join Lines" (p) "" ""
+ (collect-undo (current-buffer)
+ (dotimes (n (or p 1))
+ (delete-indentation-command 1))))
+
+(def-vim-move "Vim Find Next" (p) nil :exclusive "" ""
+ (dotimes (n (or p 1))
+ (regular-expression-search (current-point) editor::*last-search-string*)))
+
+(def-vim-move "Vim Find Previous" (p) nil :exclusive "" ""
+ (dotimes (n (or p 1))
+ (regular-expression-search (current-point) editor::*last-search-string* :forwardp nil)))
diff -rN -u old-lw-vim-mode/def-stuff.lisp new-lw-vim-mode/def-stuff.lisp
--- old-lw-vim-mode/def-stuff.lisp 1969-12-31 16:00:00.000000000 -0800
+++ new-lw-vim-mode/def-stuff.lisp 2014-07-28 05:27:46.000000000 -0700
@@ -0,0 +1,67 @@
+(in-package :vim)
+
+(editor:setup-indent 'def-vim-change 4)
+(defmacro def-vim-change (name lambda-list command-doc function-doc &body body)
+ (let ((p (car lambda-list))
+ (change (gensym))
+ (default-repeat (gensym)))
+ `(defcommand ,name ,lambda-list ,command-doc ,function-doc
+ (in-vim-command
+ (setf w-vim-collecting-count nil)
+ (let ((,default-repeat ,p))
+ (flet ((,change (,p)
+ (unless ,p (setf ,p ,default-repeat))
+ ,@body))
+ (setf *vim-last-action* #',change)
+ (,change nil)))))))
+
+(editor:setup-indent 'def-vim-move 6)
+(defmacro def-vim-move (name lambda-list linewise incl/excl command-doc function-doc
+ &body body)
+ (when (not (member incl/excl '(:inclusive :exclusive)))
+ (error "Invalid incl/excl flag ~A in def-vim-move ~A; should be ~A or ~A~%"
+ incl/excl name :inclusive :exclusive))
+ (let ((p (car lambda-list))
+ (move (gensym))
+ (default-repeat (gensym)))
+ `(defcommand ,name ,lambda-list ,command-doc ,function-doc
+ (in-vim-command
+ (setf w-vim-collecting-count nil)
+ (let ((,default-repeat
+ (when (or *vim-repeat-multiplier* ,p)
+ (* (or *vim-repeat-multiplier* 1)
+ (or ,p 1)))))
+ (flet ((,move (,p)
+ (unless ,p (setf ,p ,default-repeat))
+ (setf b-vim-linewise ,linewise
+ (exclusive) ,(eql incl/excl :exclusive)
+ b-vim-repeat-insertion-count 0)
+ (prog1 (progn ,@body)
+ ; FIXME: Need to call this when we move the cursor via the mouse, too.
+ (mark-start-insert))))
+ (if (vim-operator-pending-mode-p)
+ (finish-pending-motion #',move)
+ (,move nil))))))))
+
+(defmacro def-vim-movement-pending (name lambda-list command-doc function-doc &body body)
+ `(defcommand ,name (p) ,command-doc ,function-doc
+ (in-vim-command
+ (setf w-vim-collecting-count nil
+ *vim-repeat-multiplier* p
+ *vim-pending-action* (lambda ,lambda-list
+ ,@body)
+ b-vim-movement-pending-ending-mode "Vim Command"
+ b-vim-movement-pending ,name)
+ ; FIXME: (save-modes)
+ (setf (buffer-minor-mode (current-buffer) "Vim Command") nil)
+ (setf (buffer-minor-mode (current-buffer) "Vim Operator Pending") t))))
+
+(editor:setup-indent 'def-start-insert 4)
+(defmacro def-start-insert (name lambda-list command-doc function-doc &body body)
+ (let ((p (car lambda-list)))
+ `(defcommand ,name ,lambda-list ,command-doc ,function-doc
+ (setf b-vim-repeat-insertion-count (or ,p 1)
+ b-vim-repeat-insert-start (lambda () ,@body))
+ (funcall b-vim-repeat-insert-start)
+ (start-insert-mode))))
+
diff -rN -u old-lw-vim-mode/doc.mdwn new-lw-vim-mode/doc.mdwn
--- old-lw-vim-mode/doc.mdwn 1969-12-31 16:00:00.000000000 -0800
+++ new-lw-vim-mode/doc.mdwn 2014-07-28 05:27:46.000000000 -0700
@@ -0,0 +1,128 @@
+Browse / download code [here](http://theclapp.org/lw-vim-mode/src/).
+
+Important Caveats:
+
+* I guess I should mention that I use Edi Weitz's [Lispworks Add-Ons package](http://www.weitz.de/lw-add-ons). I suppose that might make a difference.
+* Lispworks normally allows Esc as a substitute for the Meta shift key. This package takes over Esc for use in the normal vi/Vim way (i.e. moving from Insert to Command mode). If you want Meta, use C-Esc. However, as a further caveat to *that*, if you previously used Esc-Esc for "Evaluate Expression", note that that was really Meta-Esc, and under Vim-mode you'd use C-Esc,Esc, not C-Esc,C-Esc.
+
+Most items have slightly different semantics than in Vim. Just as an example, many movement commands that are supposed to leave the cursor at the first non-blank character on the line, don't.
+
+Items marked with a leading (\*) have *significantly* different semantics than in Vim.
+
+Items marked with a (+) are not in Vim. (Obviously this is not to say that Vim can't do them, just that the mapping doesn't exist.)
+
+Some of these mappings are just my own favorites (like C-F12 to save all files), but they're in there, so I should document them.
+
+Installation:
+
+ (change-directory "path/to/files")
+ (load "vimmode")
+
+Changing modes:
+
+* Enter vi mode: `Shift-Ctrl-Esc`, `c-,`,`c-,` (ctrl-comma twice).
+ All other commands assume you're in vi mode
+* Exit vi mode: `Shift-Ctrl-Esc`, `c-, c-,`
+* Return to command mode (i.e from insert mode): `Esc`, `C-[`
+* See also the Making Changes section for various ways to enter Insert Mode
+
+Movement:
+
+* Previous line: `k Up C-p -` (minus)
+* Previous screen line: `gk`
+* Next line: `j Down C-j Return C-n`
+* Next screen line: `gj`
+* Backward character: `h Left C-h Backspace`
+* Forward character: `l` (lower-case-L) `Right Space`
+* Backward word/WORD: `b C-Left`, `B`
+* Forward word/WORD: `w C-Right`, `W`
+* Backward to end of word/WORD: `ge`, `gE`
+* Forward to end of word: `e`
+* Goto line or end of file: `G`
+* Goto line or top of file: `gg`
+* Top/Bottom/Middle of window: `H`/`L`/`M`
+* Backward/forward one Lisp form: `(`/`)`
+* Backward/forward one list: `C-(`/`C-)`
+* Goto beginning of line: `Home 0` (zero)
+* Goto end of line: `$ End`
+* Move to first non-blank on line: `^`
+* Move to beginning of defun: `[\`
+* Move to end of defun: `]\`
+* Move to column N: `|`
+* Search forward/backward for character: `f`/`F`
+* Search forward/backward till before character: `t`/`T`
+* Repeat last `f`/`F`/`t`/`T` search: `;`
+* Repeat last `f`/`F`/`t`/`T` search, in the opposite direction: `,`
+
+Scrolling:
+
+* Page down: `C-f`
+* Page up: `C-b`
+* Scroll window down by one line: `C-y S-Up`
+* Scroll window up by one line: `C-e S-Down`
+* Move current line to top/middle/bottom of window: `zt`/`zz`/`zb` or `z<CR>`/`z.`/`z-`
+
+Repeating:
+
+* Repeat last change: `.` (dot/period/full stop)
+* Repeat count: `<digits>`, e.g. 12j will move down 12 lines
+
+Making changes:
+
+* start insert before current character: `i`
+* start insert before first non-blank character in the line: `I`
+* start append after current character: `a`
+* append at end of line: `A`
+* exit insert, back to command mode: `Esc`, `Ctrl-[`
+* Delete next/previous character: `x/X`
+* (*)Delete motion: `d{motion}`
+* (*)Change motion: `c{motion}`
+* Delete line: `dd` (maybe should have a (*) in front)
+* (*)Delete to end of line: `D`
+* (*)Put recent deletion: `P`
+* Open line up/down: `O/o`
+* Lowercase word: `~w`
+* Lowercase/uppercase region: `vu/vU`
+* shift line right: `>>`
+* (*) Join with next line: `J`
+* Replace characters: `r<char>`
+* Insert characters from previous / next line: `C-y`/`C-e`
+
+Buffers/windows:
+
+* New buffer: `:n`
+* New window: `C-w`,`n` (that's C-w, then n)
+* Previous/next window: `C-w`,`k` / `C-w`,`j`
+* Close window: `C-w`,`c`
+* (*)List buffers: `:ls`,Return `F7`
+* (*)Select buffer: `:b`,Space
+* (*)Next buffer: `:bn`,Return `C-F10`
+
+Searching:
+
+* (*)search forward: `/`
+* (*)search backward: `?`
+* (*)repeat most recent search forward/backwards: `n`/`N`
+* List matching lines (sort of like :g/re/p in Vim): `:gp`
+* Delete matching lines (like :g/re/d in Vim): `:gd`
+
+Read/write files:
+
+* Save file: `:w`,Return
+* Save all files: `:wa`,Return `C-F12`
+* Save all files and exit: `:wqa`,Return
+* (*)Write file (to different filename): `:w`,Space
+* (*)Edit new file: :e,Space
+* Reload file from disk: `:e!`
+* Read file into buffer: `:r`,Space
+
+Tagging/finding source:
+
+* (*)Find source: `C-]`
+* (*)Find next tag: `:tn`,Return
+* (*)View tag list: `:ts`,Return
+
+Evaluate Lisp code:
+
+* (+)Evaluate current top-level-form: `,x`
+* (+)Evaluate current top-level form in listener: `,l`
diff -rN -u old-lw-vim-mode/functions.lisp new-lw-vim-mode/functions.lisp
--- old-lw-vim-mode/functions.lisp 1969-12-31 16:00:00.000000000 -0800
+++ new-lw-vim-mode/functions.lisp 2014-07-28 05:27:46.000000000 -0700
@@ -0,0 +1,369 @@
+(in-package :vim)
+
+(defun linewise ()
+ b-vim-linewise)
+
+(defun exclusive ()
+ b-vim-exclusive)
+
+(defun inclusive ()
+ (not b-vim-exclusive))
+
+(defun (setf linewise) (val)
+ (setf b-vim-linewise val))
+
+(defun (setf exclusive) (val)
+ (setf b-vim-exclusive val))
+
+(defun (setf inclusive) (val)
+ (setf b-vim-exclusive (not val)))
+
+(defun just-blanks (start end)
+ "Returns T if the characters from start to end are spaces."
+ (loop for ch across (points-to-string start end)
+ if (not (eql ch #\Space))
+ do (return nil)
+ finally (return t)))
+
+(defun blanks-before (point)
+ (with-point ((bol point :temporary))
+ (line-start bol)
+ (just-blanks bol point)))
+
+(defun blanks-after (point)
+ (with-point ((eol point :temporary))
+ (line-end eol)
+ (just-blanks point eol)))
+
+(defun vim-window-line ()
+ (with-point ((top (current-point)))
+ (save-excursion
+ (top-of-window-command nil)
+ (move-point top (current-point)))
+ (count-lines top (current-point))))
+
+(defun scroll-line-to-place (line-num place)
+ (when line-num
+ (goto-line-command line-num))
+ (refresh-screen-command (case place
+ (:top 0)
+ (:middle nil)
+ (:bottom -1))))
+
+(def-vim-char-attribute :whitespace '(#\Space #\Return #\Newline #\Tab))
+(def-vim-char-attribute :keyword
+ (loop for ch across "-_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ collect ch))
+(def-vim-char-attribute :filename
+ (loop for ch across "0123456789/\\.-_+,#$%{}[]:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!~="
+ collect ch))
+
+(defmethod vim-char-attribute ((type symbol) &optional (point (current-point)))
+ (member (next-character point) (gethash type b-vim-char-attributes)))
+(defmethod vim-char-attribute ((test function) &optional (point (current-point)))
+ (funcall test (next-character point)))
+(defmethod vim-char-attribute ((types list) &optional (point (current-point)))
+ (let* ((invert-p (eql (car types) :not))
+ (types (if invert-p (cdr types) types)))
+ (loop for type in types
+ if (vim-char-attribute type point)
+ return (not invert-p)
+ finally return invert-p)))
+
+(defun vim-find-attribute (forward attributes &optional (point (current-point)))
+ (with-point ((started-at point))
+ (let ((offset (if forward 1 -1)))
+ (loop while (and (not (vim-char-attribute attributes point))
+ (character-offset point offset)))
+ (and (point/= started-at point)
+ (vim-char-attribute attributes point)))))
+
+(defun xor (x y)
+ (or (and x (not y))
+ (and y (not x))))
+
+(defun white-p (point)
+ (vim-char-attribute :whitespace point))
+
+(defun skip-white (point forward)
+ (if (white-p point)
+ (vim-find-attribute forward '(:not :whitespace) point)
+ t))
+
+(defun skip-non-white (point forward)
+ (if (white-p point)
+ t
+ (vim-find-attribute forward :whitespace point)))
+
+;; FIXME: optimize this a bit so we're not creating several new functions
+;; every time we call with-move. Discussion: the only way to do that
+;; that I can see is change all the functions in the LABELS to macros.
+;; I think, for now, I won't worry about it.
+(defmacro with-move ((forward point &key (word-type :keyword)) &body body)
+ (rebinding (forward point word-type)
+ (with-unique-names (limit limit-test)
+ `(block with-move
+ (let ((,limit nil)
+ (,limit-test (if ,forward #'point<= #'point>=)))
+ (macrolet ((must (&body body)
+ `(let ((res (and ,@body)))
+ (if (and res (test-limit))
+ res
+ (return-from with-move nil)))))
+ (labels ((set-point (new-point)
+ (setf ,point new-point))
+ (set-direction (new-direction)
+ (setf ,forward new-direction))
+ (test-limit ()
+ (or (not ,limit)
+ (funcall ,limit-test ,point ,limit)))
+ (line-limit (point)
+ (if ,forward
+ (line-end point)
+ (line-start point)))
+ (set-limit (limit-point)
+ (setf ,limit limit-point))
+ (go-forward ()
+ (setf ,forward t))
+ (u-turn ()
+ (setf ,forward (not ,forward)))
+ (invert (list)
+ (if (eql (car list) :not)
+ (cdr list)
+ (cons :not list)))
+ (skip-current ()
+ (loop for attrib in '((:whitespace)
+ (,word-type)
+ (:not :whitespace ,word-type))
+ until (vim-char-attribute attrib ,point)
+ finally return (must (vim-find-attribute ,forward (invert attrib) ,point))))
+ (fix-endp (endp)
+ (if endp
+ (must (character-offset ,point (if ,forward -1 1)))
+ t))
+ (boundary (&optional endp)
+ (must (skip-current)
+ (fix-endp endp)))
+ (skip (&rest attribute)
+ (let ((inverted-attribute (invert attribute)))
+ (or (vim-char-attribute inverted-attribute ,point)
+ (must (vim-find-attribute ,forward inverted-attribute ,point)))))
+ (bump ()
+ (must (character-offset ,point (if ,forward 1 -1))))
+ (unbump ()
+ (must (character-offset ,point (if ,forward -1 1))))
+ (find (attribute &optional endp)
+ (skip (invert attribute))
+ (fix-endp endp))
+ (go-here ()
+ (move-point (current-point) ,point)))
+ ,@body)))))))
+
+(defgeneric vim-offset (n type forward point &key &allow-other-keys))
+(defmethod vim-offset (count (type (eql :word)) forward point &key end (word-type :keyword))
+ (setf count (or count 1))
+ (loop for n below count
+ while
+ ; This code highlights that e & b are inverses of each other, and
+ ; w and ge are inverses of each other. That is, e & b do the same
+ ; things in opposite directions; same for w and ge.
+ (with-move (forward point :word-type word-type)
+ (cond ((xor forward end)
+ (boundary)
+ (skip :whitespace))
+ (t (bump)
+ (skip :whitespace)
+ (boundary :end))))
+ finally return (= n count)))
+
+(defmethod vim-offset (count (type (eql :bigword)) forward point &key end)
+ (flet ((move ()
+ (with-move (forward point)
+ (cond ((xor forward end)
+ (skip :not :whitespace)
+ (skip :whitespace))
+ (t (bump)
+ (skip :whitespace)
+ (skip :not :whitespace)
+ (unbump))))))
+ (setf count (or count 1))
+ (loop for n below count
+ while (move)
+ finally return (= n count))))
+
+(defun current-word (&optional (word-type :keyword) (point (current-point)))
+ (unless (listp word-type)
+ (setf word-type (list word-type)))
+ (with-point ((start point)
+ (end point))
+ (with-move (t start)
+ (find word-type)
+ (u-turn)
+ (find (invert word-type) :end)
+ (move-point end start)
+ (go-forward)
+ (set-point end)
+ (find (invert word-type))
+ (points-to-string start end))))
+
+(defun move-over-word (p type end)
+ (vim-offset 1 type nil b-vim-point-before-movement)
+ (vim-offset (or p 1) type t (current-point) :end end))
+
+(defun vim-read-a-character ()
+ (gesture-to-simple-char
+ (prompt-for-character* "Character: " :ignored)))
+
+(defun vim-find-char (forward p &key ch leave-before save)
+ (let (update-window)
+ (unless ch (setf ch (vim-read-a-character)
+ update-window t))
+ (when save
+ (setf *vim-last-find-char* (list forward ch leave-before (exclusive))))
+ (setf p (or p 1))
+ (with-point ((point (current-point))
+ (limit (current-point)))
+ (with-move (forward point)
+ (set-limit (line-limit limit))
+ (bump)
+ (loop count (equal ch (next-character point)) into count
+ if (= count p)
+ return t
+ else do (bump))
+ (when leave-before (unbump))
+ (go-here)))
+ (when update-window
+ (update-buffer-window (current-window)))
+ ))
+
+(defun vim-repeat-last-find (p &key invert)
+ (if p
+ (destructuring-bind (forward ch leave-before exclusive) *vim-last-find-char*
+ (cond (invert
+ (setf (exclusive) (not exclusive))
+ (vim-find-char (not forward) p :ch ch :leave-before leave-before))
+ (t (setf (exclusive) exclusive)
+ (vim-find-char forward p :ch ch :leave-before leave-before))))
+ (editor-error "No character to find.")))
+
+(defun finish-pending-motion (move)
+ (let ((saved-vim-movement-pending b-vim-movement-pending))
+ (flet ((command (p)
+ (setf b-vim-movement-pending saved-vim-movement-pending)
+ (move-point b-vim-point-before-movement (current-point))
+ (funcall move p)
+ (unless (exclusive)
+ (character-offset (current-point) 1))
+ (funcall *vim-pending-action*
+ b-vim-point-before-movement
+ (current-point))))
+ (command nil)
+ (setf *vim-last-action* #'command
+ *vim-repeat-multiplier* nil
+ b-vim-movement-pending nil)
+ ; FIXME: (restore-modes)
+ (setf (buffer-minor-mode (current-buffer) "Vim Operator Pending") nil)
+ (setf (buffer-minor-mode (current-buffer) b-vim-movement-pending-ending-mode) t)
+ )))
+
+(defun mode-p (mode)
+ (let ((buffer (current-buffer)))
+ (and buffer
+ (find mode (buffer-mode-names buffer) :test #'string=))))
+
+(defun vim-command-mode-p ()
+ (mode-p "Vim Command"))
+(defun vim-operator-pending-mode-p ()
+ (mode-p "Vim Operator Pending"))
+(defun vim-insert-mode-p ()
+ (mode-p "Vim Insert"))
+
+(defun start-insert-mode ()
+ (setf (buffer-minor-mode (current-buffer) "Vim Insert") t)
+ (setf (buffer-minor-mode (current-buffer) "Vim Command") nil))
+
+; FIXME: Need to call this when we move the cursor via the mouse, too.
+(defun mark-start-insert ()
+ (when (vim-insert-mode-p)
+ (when (and b-vim-insert-start-point
+ (point-buffer b-vim-insert-start-point))
+ (delete-point b-vim-insert-start-point))
+ (setf b-vim-insert-start-point (copy-point (current-point) :insert-after))))
+
+(defun setup-vim-insert-mode (buffer)
+ (use-buffer buffer
+ (mark-start-insert)))
+
+; FIXME: The way I do repeats in the rest of the code is fairly clean.
+; The way I do it for repeated inserts is pretty gross by comparison.
+; I wanted to "make it work, then make it pretty." It's not pretty
+; yet. I think to make it pretty I'd need to invoke a recursive edit,
+; which could be "interesting", because I don't think the current code
+; is recursive-edit-proof. Not that making it recursive-edit- proof
+; would be bad or anything, it just isn't yet. I think. :)
+; FIXME: the undo for the inserted text is funky.
+(defun finish-repeated-insert ()
+ ; If somebody else modifies our buffer, especially if they delete
+ ; the text that b-vim-insert-start-point points to, then
+ ; b-vim-insert-start-point is deleted too, so we need to make sure
+ ; it's valid.
+ (when (point-buffer b-vim-insert-start-point)
+ (setf *vim-last-inserted-text* (points-to-string b-vim-insert-start-point (current-point)))
+ (let ((default-repeat b-vim-repeat-insertion-count))
+ ; Save the action so it can be repeated later by the . command.
+ (setf *vim-last-action*
+ (lambda (p)
+ (unless p (setf p default-repeat)) ; reminder: 0 is non-nil! :)
+ (when (> p 0)
+ (funcall b-vim-repeat-insert-start)
+ (insert-string (current-point)
+ (apply #'concatenate 'string
+ (loop for n below p
+ collect *vim-last-inserted-text*))))))
+ (funcall *vim-last-action* (1- b-vim-repeat-insertion-count)))))
+
+(defun setup-vim-command-mode (buffer)
+ (use-buffer buffer
+ ; I thought I needed these at one point, but now I don't think so. Leaving
+ ; for a while, just in case.
+ (setf ; *vim-pending-action* (constantly nil)
+ ; *vim-last-action* #'identity
+ *vim-repeat-multiplier* nil)
+ )
+ (setf editor::*meta-prefix-gesture-spec* (sys::make-gesture-spec
+ (char-code #\C-Escape)
+ sys:gesture-spec-control-bit))
+ ; fixme: can't seem to have the meta-prefix-gesture and the interrupt key be the same key
+ #+nil
+ (set-interrupt-keys '(#\escape)))
+
+; Note: Emacs's kill-region-command does not include the character at the "end" mark. I
+; think this just reflects a difference in approach. To Emacs a point lies *between*
+; two characters, so if you have 123^456^789 it deletes the "456". To Vim a point lies *on*
+; a character, so if you have
+; 123456789
+; ^ ^
+; an "exclusive" motion d2l deletes the "45".
+(defun vim-delete-motion (begin end)
+ ;; Make sure begin and end are distinct objects, different from each other
+ ;; and different from (current-point).
+ (with-point ((begin begin)
+ (end end))
+ ; (format t "~&starting Vim Delete Motion: begin is ~S, end is ~S~%" begin end)
+ (when (point< end begin)
+ (rotatef end begin))
+ ; (format t "begin is ~S, end is ~S~%" begin end)
+ (when (and (not (linewise))
+ (not (same-line-p begin end))
+ (blanks-before begin)
+ (blanks-after end))
+ (setf (linewise) t))
+ (when (linewise)
+ (line-start begin)
+ (line-end end)
+ (character-offset end 1))
+ ; (format t "begin is ~S, end is ~S~%" begin end)
+ (move-point (current-point) begin)
+ (set-current-mark end)
+ (kill-region-command nil)
+ (move-point (current-point) begin)))
diff -rN -u old-lw-vim-mode/macros.lisp new-lw-vim-mode/macros.lisp
--- old-lw-vim-mode/macros.lisp 1969-12-31 16:00:00.000000000 -0800
+++ new-lw-vim-mode/macros.lisp 2014-07-28 05:27:46.000000000 -0700
@@ -0,0 +1,44 @@
+(in-package :vim)
+
+; Copied from fontlock.lisp in the editor sources
+(defmacro with-hidden-font-lock-changes (buffer &body body)
+ (declare (ignorable buffer))
+ ; obviously this is wrong
+ `(progn ,@body)
+ #+nil
+ (rebinding (buffer)
+ (with-unique-names (modified writable)
+ `(let ((editor::*dont-undo* t)
+ (,modified (buffer-modified ,buffer))
+ (,writable (shiftf (buffer-writable ,buffer) t)))
+ (declare (special *dont-undo*))
+ (unwind-protect
+ (progn ,@body)
+ (unless ,modified
+ (setf (buffer-modified ,buffer) nil))
+ (setf (buffer-writable ,buffer) ,writable))))))
+
+(defmacro in-vim-command (&body body)
+ `(progn
+ (unless *vim-in-command*
+ (invoke-hook 'vim-before-top-level-command-hooks))
+ (invoke-hook 'vim-before-command-hooks)
+ (let ((was-in-command *vim-in-command*)
+ (*vim-in-command* t))
+ (unwind-protect
+ (progn ,@body)
+ (invoke-hook 'vim-after-command-hooks)
+ (unless was-in-command
+ (invoke-hook 'vim-after-top-level-command-hooks))))))
+
+(editor:setup-indent 'def-vim-char-attribute 1)
+(defmacro def-vim-char-attribute (type set)
+ `(setf (gethash ,type *vim-default-char-attributes*) ,set))
+
+
+(editor:setup-indent 'vim-point-after 0)
+(defmacro vim-point-after (&body body)
+ `(with-point ((point (current-point)))
+ ,@body
+ (prog1 (copy-point (current-point) :temporary)
+ (move-point (current-point) point))))
diff -rN -u old-lw-vim-mode/packages.lisp new-lw-vim-mode/packages.lisp
--- old-lw-vim-mode/packages.lisp 1969-12-31 16:00:00.000000000 -0800
+++ new-lw-vim-mode/packages.lisp 2014-07-28 05:27:46.000000000 -0700
@@ -0,0 +1,5 @@
+(in-package :cl-user)
+
+(defpackage #:vim
+ (:use :cl :lispworks
+ #+cells :cells))
diff -rN -u old-lw-vim-mode/vars.lisp new-lw-vim-mode/vars.lisp
--- old-lw-vim-mode/vars.lisp 1969-12-31 16:00:00.000000000 -0800
+++ new-lw-vim-mode/vars.lisp 2014-07-28 05:27:46.000000000 -0700
@@ -0,0 +1,28 @@
+(in-package :vim)
+
+(defparameter *vim-repeat-multiplier* nil
+ "The repeat multiplier, e.g. the 2 in 2d4j.")
+(defparameter *vim-pending-action* (constantly nil)
+ "Stores a closure to run after the next movement command. Used to implement things
+like dj.")
+(defparameter *vim-last-action* #'identity
+ "Stores a closure to run to repeat the previous command. Used to implement the . command.")
+(defparameter *vim-last-find-char* nil
+ "Stores direction and leave-before-ness of the last f/F/t/T command.")
+(defparameter *bound-vim-commands* nil)
+
+(defparameter *vim-vars* (make-hash-table :test #'equal)
+ "Holds the vim variables.")
+(defparameter *vim-var-selectors* (make-hash-table)
+ "Selector code for vim-vars.")
+(defvar *vim-in-command* nil
+ "T if currently running a Vim command.")
+(defvar *vim-last-inserted-text* nil
+ "A copy of the text most recently inserted.")
+
+(defvar *font-lock-inverse-face*
+ (editor:make-face 'font-lock-inverse-face
+ :if-exists t
+ :inverse-p t))
+
+(defvar *vim-default-char-attributes* (make-hash-table))
diff -rN -u old-lw-vim-mode/vim-vars.lisp new-lw-vim-mode/vim-vars.lisp
--- old-lw-vim-mode/vim-vars.lisp 1969-12-31 16:00:00.000000000 -0800
+++ new-lw-vim-mode/vim-vars.lisp 2014-07-28 05:27:46.000000000 -0700
@@ -0,0 +1,109 @@
+(in-package :vim)
+
+(defun vim-var-key (name type)
+ (cons name type))
+(defun vim-var-lookup (name type #| &optional create |#)
+ (let* ((key (vim-var-key name type))
+ (var (gethash key *vim-vars*)))
+ (cond (var var)
+ #+nil
+ (create (setf (gethash key *vim-vars*)
+ (make-instance 'vim-var :name name :type type)))
+ (t (error "vim-var-lookup: vim-var ~S of type ~S not found" name type)))))
+
+(defun vim-var-selector (type)
+ (multiple-value-bind (selector present) (gethash type *vim-var-selectors*)
+ (if present
+ (funcall selector)
+ (error "vim-var-selector: unknown selector: ~S" type))))
+
+(defun vim-var (name type)
+ (let ((var (vim-var-lookup name type)))
+ (if var
+ (let ((selector (vim-var-selector type)))
+ (multiple-value-bind (selected-val present)
+ (gethash selector (values-of var))
+ (if present
+ selected-val
+ (setf (gethash selector (values-of var))
+ (funcall (init-func-of var))))))
+ (error "vim-var: vim-var ~S of type ~S not found" name type))))
+
+(defun vim-var-set (name type value)
+ (setf (gethash (vim-var-selector type)
+ (values-of (vim-var-lookup name type)))
+ value))
+(defsetf vim-var vim-var-set)
+
+(defmacro def-vim-var-definer (definer-name type selector)
+ `(progn
+ (setf (gethash ,type *vim-var-selectors*) (lambda () ,selector))
+ (editor:setup-indent ',definer-name 2)
+ (defmacro ,definer-name (name init-code &optional (doc "Vim variable"))
+ `(progn
+ (setf (gethash (vim-var-key ',name ,,type) *vim-vars*)
+ (make-instance 'vim-var
+ :name ',name
+ :type ,,type
+ :values (make-hash-table)
+ :init-func (lambda () ,init-code)
+ :doc ,doc))
+ (eval-when (:compile-toplevel :load-toplevel :execute)
+ (define-symbol-macro ,name
+ (vim-var ',name ,,type)))))))
+
+(def-vim-var-definer def-vim-var :global t)
+(def-vim-var-definer def-vim-buffer-var :buffer (current-buffer))
+(def-vim-var-definer def-vim-window-var :window (current-window))
+
+(def-vim-buffer-var b-vim-point-before-movement (copy-point (current-point))
+ "The point before a movement command.")
+(def-vim-buffer-var b-vim-exclusive t
+ "Defines whether the current movement command is exclusive")
+(def-vim-buffer-var b-vim-linewise t
+ "Defines whether the current movement command is linewise.")
+(def-vim-buffer-var b-vim-movement-pending ""
+ "Stores the name of the pending command, e.g. for cw, stores \"Vim Change Motion\".")
+(def-vim-buffer-var b-vim-movement-pending-ending-mode "Vim Command"
+ "Stores the default mode to be in after ending a pending-motion command. Used to differentiate
+between dw and cw.")
+
+; These next three should probably be Window vars not Buffer vars, but
+; the interactions are weird. For now the answer is "don't do that
+; then".
+(def-vim-buffer-var b-vim-insert-start-point nil
+ "(current-point) when insert starts in a given buffer.")
+(def-vim-buffer-var b-vim-repeat-insertion-count 0
+ "Saved the 3 in 3ifoo<esc>.")
+(def-vim-buffer-var b-vim-repeat-insert-start (constantly t)
+ "When repeating an insert, you need to also repeat the I or a or A or whatever.
+This stores what to do.")
+
+(def-vim-var v+hlsearch t
+ "Are searches highlighted?")
+#+nil ; I don't use this just yet.
+(def-vim-var v+search-expr nil
+ "String to highlight")
+
+(def-vim-window-var w-vim-collecting-count nil
+ "True if we're collecting a count.
+We must know if we're collecting a count so we can process #\0 (zero) correctly.")
+
+(def-ed-var 'vim-before-top-level-command-hooks :global t nil
+ "Hooks called before starting a top-level Vim command.")
+(def-ed-var 'vim-after-top-level-command-hooks :global t nil
+ "Hooks called after ending a top-level Vim command.")
+(def-ed-var 'vim-before-command-hooks :global t nil
+ "Hooks called before starting any Vim command.")
+(def-ed-var 'vim-after-command-hooks :global t nil
+ "Hooks called after ending any Vim command.")
+
+(defun copy-hash-table (source)
+ (let ((dest (make-hash-table :test (hash-table-test source)
+ :size (hash-table-size source))))
+ (loop for key being the hash-keys of source using (hash-value value) do
+ (setf (gethash key dest) value))
+ dest))
+
+(def-vim-buffer-var b-vim-char-attributes
+ (copy-hash-table *vim-default-char-attributes*))
diff -rN -u old-lw-vim-mode/vimmode.lisp new-lw-vim-mode/vimmode.lisp
--- old-lw-vim-mode/vimmode.lisp 1969-12-31 16:00:00.000000000 -0800
+++ new-lw-vim-mode/vimmode.lisp 2014-07-28 05:27:46.000000000 -0700
@@ -0,0 +1,35 @@
+(in-package :cl-user)
+
+; To compile this in a stand-alone Lisp w/out loading the environment, use
+; lw -build =(print '(compile-file "vimmode")')
+; (The =() is zsh-specific though). You could also say
+; echo '(compile-file "vimmode")' > /tmp/build ; lw -build /tmp/build
+
+#+nil
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (asdf:oos 'asdf:load-op :cells))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (defmacro compile-load (file)
+ `(eval-when (:compile-toplevel :load-toplevel :execute)
+ (compile-file-if-needed ,file :load t))))
+
+(compile-load "packages")
+(compile-load "wrap-editor")
+(compile-load "vars")
+(compile-load "classes")
+(compile-load "vim-vars")
+(compile-load "def-stuff")
+(compile-load "macros")
+(compile-load "functions")
+(compile-load "commands")
+(compile-load "bindings")
+
+; Convenient scratchpad ...
+
+#+nil
+(bind-key "Vim Test" #(#\C-\, #\C-\.))
+
+#+nil
+(defcommand "Vim Test" (p) "" ""
+ (format t "'~A'~%" (current-word)))
diff -rN -u old-lw-vim-mode/wrap-editor.lisp new-lw-vim-mode/wrap-editor.lisp
--- old-lw-vim-mode/wrap-editor.lisp 1969-12-31 16:00:00.000000000 -0800
+++ new-lw-vim-mode/wrap-editor.lisp 2014-07-28 05:27:46.000000000 -0700
@@ -0,0 +1,106 @@
+(in-package :vim)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (defmacro wrap-editor (symbol)
+ (let ((editor-symbol (intern symbol :editor)))
+ `(defmacro ,symbol (&rest rest)
+ `(,',editor-symbol ,@rest))))
+ (defmacro wrap-list (&rest list)
+ `(progn
+ ,@(loop for s in list
+ collect `(wrap-editor ,s)))))
+
+
+#+cells
+(defmodel point ()
+ ((e-point :initarg :e-point :accessor e-point-of)
+ (buffer :initarg :buffer :accessor buffer-of)))
+
+#+cells
+(defmodel buffer ()
+ ((e-buffer :initarg :e-buffer :accessor e-buffer-of)
+ (point :initarg :point :accessor point-of)))
+
+; (error "fix me")
+; (defun current-point ()
+; ())
+
+(wrap-list
+ argument-digit-command
+ back-to-indentation-command
+ backward-character-command
+ backward-form-command
+ backward-list-command
+ beginning-of-buffer-command
+ beginning-of-line-command
+ bind-key
+ bottom-of-window-command
+ buffer-minor-mode
+ buffer-mode-names
+ buffer-pathname
+ buffers-end
+ buffers-start
+ character-offset
+ clear-undo-command
+ collect-undo
+ copy-point
+ count-lines
+ current-buffer
+ current-point
+ current-window
+ def-ed-var
+ defcommand
+ defmode
+ delete-indentation-command
+ delete-next-character-command
+ delete-previous-character-command
+ delete-point
+ editor-error
+ end-of-buffer-command
+ end-of-line-command
+ find-file-command
+ find-regexp-pattern
+ font-lock-apply-highlight
+ font-lock-fontify-buffer-command
+ font-lock-unfontify-region
+ forward-character-command
+ forward-form-command
+ forward-list-command
+ gesture-to-simple-char
+ goto-line-command
+ insert-string
+ invoke-hook
+ kill-region-command
+ line-end
+ line-offset
+ line-start
+ make-fsa
+ move-point
+ move-to-window-line-command
+ new-line-command
+ next-character
+ next-line-command
+ open-line-command
+ point/=
+ point<
+ point<=
+ point>=
+ point-buffer
+ points-to-string
+ previous-line-command
+ prompt-for-character*
+ prompt-for-prefix
+ refresh-screen-command
+ regular-expression-search
+ revert-buffer-command
+ same-line-p
+ save-all-files-command
+ save-excursion
+ scroll-window-down-command
+ scroll-window-up-command
+ set-current-mark
+ top-of-window-command
+ update-buffer-window
+ use-buffer
+ with-point
+ )