This feed contains pages in the "vim_mode" category.
v8 of lw-vim-mode has been posted to common-lisp.net.
Download: darcs get http://common-lisp.net/project/lw-vim-mode/darcs/lw-vim-mode.
For further announcements, please subscribe to the lw-vim-mode-announce mailing list.
Posted Fri 13 Jun 2008 08:17:00 AM EDTI got approved for a project at common-lisp.net. I haven't uploaded my source tree yet, but I have done a tarball.
Also, there are two mailing lists (one for developers and one for announcements), and even a wiki and bug tracking page (which I can't currently log in to; I've emailed the c-l.net admin about that).
Update 30 May 2008: Gary King fixed it (trac) on Memorial Day and dropped me an email. Thanks, Gary! Also, I uploaded a source tree and put it under darcs. See here.
Posted Thu 22 May 2008 10:40:45 PM EDTI got no replies (so far) or emails about my announcement yesterday, but I got a lot of hits on the website. I'm going to look on the bright side and hope that people are interested and not pointing and laughing and/or shaking their heads in disbelief. 
So I requested a project at http://www.common-lisp.net with darcs and trac and mailing lists and everything. More updates as events warrant.
Posted Tue 20 May 2008 08:23:17 AM EDTI've written a partial vi-mode for the Lispworks editor.
Questions
Why did I publicize it?
Beyond a certain point, working in a vacuum sucks. I'd like to know if anyone else is interested in it.
If nobody is, I'll keep working on it at my current widely variable pace, for my own edification and instruction. But if other people want to use it too, then I'll do all the usual stuff for an Open Source software project, like make my Wiki actually editable by others, create a mailing list, use an actual distributed revision control system instead of RCS on my local box, that sort of thing.
Doing all that takes a certain amount of time, work, and effort, though, and if all I hear is crickets, then I won't bother. 
Why did I write it?
Mostly 'cause I like the vi input model and because I've used it for circa 18 years.
Other reasons, in varying degrees of usefulness:
- http://article.gmane.org/gmane.lisp.lispworks.general/6570
- http://article.gmane.org/gmane.lisp.lispworks.general/8052
- http://www.lispniks.com/pipermail/slim-vim/2007-May/000555.html
Download?
Documentation?
ASDF / ASDF-install?
No, just a load file at the moment. Not even a Lispworks project file. See the documentation link above.
Changelog?
Blog?
You're looking at it. See also here.
Mailing lists?
None yet. If and only if people are interested, I'll set one up here or get a project page at cl.net. For now, post in the forum [update: disabled], reply to the thread on c.l.l, email me at larry at theclapp dot org, or start something on programming.reddit.com. In particular, please do not discuss this on the Lispworks HUG mailing list.
Prominently missing features
- Visual mode
- Ex mode
- registers
- yank / put
- Syntax highlighting (except for Lisp, of course)
- folding
I'm most interested in "visual mode", but that requires an Ex mode to do much of anything useful with, and marks and registers too, so it'll have to wait.
Of course the Emacs emulation has a kind of visual mode, and registers, and yank/put, and lots of other stuff, I just haven't wired in the vi-ish analogs.
In part I posted everywhere about it to find out what people would want next.
"What the heck are you talking about?"
If the first you're hearing about this is via a subscription to my blog, I published a very brief notice on the Lispworks HUG and slim-vim mailing lists, and also on comp.lang.lisp and comp.editors.
Posted Mon 19 May 2008 09:07:35 AM EDTlw-vim-mode v7 posted here. Changelog:
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
pnow bound toP, since it puts the cut text in front of the current line. Jnow joins with the next line instead of the previous line.
Mappings added:
Escin insert mode|-- move to columnP-- Put cut text above the current lineC-y/C-en/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. 
lw-vim-mode v6 posted here. Changelog:
- Made
eandge"inclusive" - Separated
WandE-- they used to map to the same command; now they don't. - Renamed things like
Vim Forward WORD CAPStoVim Forward BIGWORD. - Added with-move macro; reimplemented many movement commands using it.
Mappings changed
W/E
Mappings Added
E/gEa/Ac{motion}f/F/t/T/;/,gf
I haven't had a lot of time for blogging lately, what with work and the chorus I'm in and trying to absorb the Getting Things Done methodology that I picked up over New Years. So I thought I'd try to jump back in and talk about a small DSL I wrote for moving the cursor around in Vim-mode.
Vim has many commands for moving the cursor around by what it calls "words"
and "WORDS", which I call :words and :bigwords.
According to the Vim help file:
A word consists of a sequence of letters, digits and underscores, or a sequence of other non-blank characters, separated with white space (spaces, tabs, <EOL>). This can be changed with the 'iskeyword' option. An empty line is also considered to be a word.
A WORD consists of a sequence of non-blank characters, separated with white space. An empty line is also considered to be a WORD.
The basic core of this kind of movement is, search (forward or backwards) until you find a character that is (or isn't) whitespace or a keyword.
My approach to coding is to get it to work and then make it pretty, so I wrote
functions to a) figure out if the current character is in a certain class (e.g.
:whitespace or (:not :keyword :whitespace)), b) search (forward or backwards)
for characters in a given list of classes, and c) write direct calls to these to
move the cursor around.
The first one looks like this:
(defmethod vim-char-attribute ((type symbol) &optional (point (current-point)))
(member (next-character point) (gethash type b-vim-char-attributes)))
(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)))
the second one looks like this:
(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)))))
and I long since deleted the third one and rewrote it, so I don't have an example
of that one. Suffice to say, it was pretty unreadable. In retrospect, it reminds
me of what Kent Pitman (I think) has said about printed output in Lisp, before the advent
of format -- the stuff you actually wanted to print (or the cursor movements I wanted to make)
got lost in the rest of the code.
So once you get that out of the way, you find that the next layer is basically finding
boundaries, which means skipping
the current kind of character and then leaving the cursor in the right place. You skip
the current kind of character by figuring out what kind of character you're currently on
(i.e. :whitespace, :keyword, or (:not :whitespace :keyword)), and then moving
until you run out of that kind of character.
Vim has several movement commands, both forwards and backwards, that
leave the cursor at the beginning or end of the :word (or :bigword).
w [count] words forward.
W [count] WORDS forward.
e Forward to the end of word [count]
E Forward to the end of WORD [count]
b [count] words backward.
B [count] WORDS backward.
ge Backward to the end of word [count]
gE Backward to the end of WORD [count]
One really interesting part of the process came when I realized that the algorithm
for w and ge are the same, just going different directions, and similarly for e
and b. So if you're going forward to the front of the next word (w), or you're
going backwards to the end of the previous word (ge), you're doing the same thing:
find a boundary, and then skip whitespace.
So I wrote a couple of methods to move the cursor by one or more :words, or one or more
:bigwords. Here's the former, along with the defgeneric:
(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)))
And now we get to the meat of it: with-move. with-move defines a context where you can
easily move forwards or backwards, turn around, bump the cursor one character one way or
the other, and so on, and not have to keep repeating the movement direction or variable name
with the cursor in it.
The core subfunctions are
skip-current: figure out what you're on and skip to the end of itboundary: skip-current, and then leave the cursor in the right placeskip: given a character type, move until you're not on that type any more. If you're already on that type, don't move at all
There are also some auxiliary subfunctions, among them
u-turn: change the direction you're movingbump: move the cursor one character in the current directionunbump: move the cursor one character in the opposite of the current directionfind: given an attribute, find the first character of that kind, leaving the cursor in the right place
And one final feature: if any subfunction fails (i.e. you've come to the beginning or
end of the buffer), quit immediately. This is acheived by wrapping most movement in a
local must macro, that says basically "do all the movement in order; if any fail, exit
with-move immediately".
(macrolet ((must (&rest x)
`(let ((res (and ,@x)))
(if res res (return-from with-move nil)))))
[...])
And finally, here's the macro itself:
(defmacro with-move ((forward point &key (word-type :keyword)) &body body)
(rebinding (forward point)
`(block with-move
(macrolet ((must (&rest x)
`(let ((res (and ,@x)))
(if res res (return-from with-move nil)))))
(labels ((set-point (new-point)
(setf ,point new-point))
(set-direction (new-direction)
(setf ,forward new-direction))
(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 (list '(:whitespace)
(list ,word-type)
(list :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)))
,@body)))))
I'm not completely happy with this macro. It defines 12 new functions every time you use it. (Lispworks may be smart enough to compile-away any you don't use in any given invocation, but it still bugs me.) I'm thinking about changing most of those local subfunctions to be global functions that take defaulted optional arguments, and changing with-move to just establish a context that sets the defaults once so you can ignore them. I've run out of time just now or I'd try to give an example of what I mean, but that'll have to wait for later.
In the meantime, happy Lisping!
Posted Sat 19 Jan 2008 09:31:25 AM ESTI thought I'd take a few minutes and touch on a piece of my Vim-mode code that I rather like, the specification of Vim variables. (This is all about Vim variables used in the code so far, not about Vim-user-level variables, which I don't even have yet, but if/when I do they may well just use this same framework.) Knowing a little bit about Vim or vi will come in handy in this discussion, but hopefully I can explain most of it.
Background: Vim-mode operations happen in (at least) three contexts: global, buffer, and window. For example, it's a global option whether you want to highlight all the matches of the last thing you searched for. (This corresponds to Vim's hlsearch option.)1 Second, Vim allows you to perform a command, and then a movement, and have the command apply to the moved-over text. To facilitate this, I store the before and after character positions in buffer-local variables -- each buffer has its very own before and after variables. Last, Vim allows you to preceed most commands with a count just by starting to type in some digits, and you can be in the middle of typing a count in one window and doing something else in another window, so I have a window-local flag that tells me whether I should consider a digit as part of a count, or part of some other command. (This is particularly important to process 0 (zero) correctly -- 0 normally moves to the beginning of the line.)
So to store and keep track of all this, I have four things:
- a class that defines a variable
- a hash table that holds all the variable objects
- some functions to access these variables
- some macrology to hide the functions
The class looks like this, and has a name, a type, a value, an initialization function, and a doc string:
(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)))
The values slot is not the value of the variable, it's actually a hash table storing the value (or values) of the variable in all the appropriate contexts, e.g. for buffer variables, a value for each buffer.
The hash table that stores all the variable records is keyed on the name of the variable and its type. I could just key on name, and look up the type from there, but Vim lets you have same-named user-level variables in different contexts, so I wanted to allow that.
Since each kind of variable (global, buffer, window) needs to know its context (for example a buffer variable needs to know the currently active buffer), first you need to define each kind of variable, and specify how to find out the context. I call the thing that defines the context the "selector", and I store the selector in a hash table keyed on the type.
So accessing a variable is a three step process: given its name and type, look up the vim-var instance that has all the values for variables of that name and type, then look up and call the selector for that type, then use the selector value to look up the actual value of the variable in the current context. Here's the code to do all that:
(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))))
I also have a function to set a variable, of course, and the defsetf
that allows you to use setf instead:
(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)
I also define a macro per context that makes it easy to define variables of that type. The definer macro creates an instance of vim-var, puts it in the hash table, and (here's the handy part) creates a symbol macro so access to the variable -- which is actually via a function call -- looks like a normal variable reference.
Last, defining all this by hand would be a drag, so I have a macro that defines a new context, which sets up the selector for the type and defines a macro to define instances of the type:
(defmacro def-vim-var-definer (definer-name type selector)
`(progn
(setf (gethash ,type *vim-var-selectors*) (lambda () ,selector))
(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)))))))
This may be my first macro-defining-macro. 
Here's the definition of buffer-local variables:
(def-vim-var-definer def-vim-buffer-var :buffer (current-buffer))
This means that to define a buffer-local variable, call def-vim-buffer-var,
and to use a buffer-local variable, figure out what the buffer is
by calling current-buffer.
Here's the definition of a buffer-local variable:
(def-vim-buffer-var b-vim-point-before-movement (copy-point (current-point)) "The point before a movement command.")
This defines b-vim-point-before-movement as a buffer-local variable, and says
that when you first access b-vim-point-before-movement in a new buffer,
use (copy-point (current-point)) to initialize the value for that buffer.
note 1: I don't actually use this option yet, but I will eventually.
Posted Sat 29 Dec 2007 02:03:57 AM ESTlw-vim-mode v5 posted here. Changelog here.
Posted Sat 29 Dec 2007 01:28:57 AM ESTOriginally posted Saturday, 17-Nov-2007
- expand my vi/vim mode
- document and publish my vi/vim mode so others could actually use it, and perhaps even participate
- write a compiler for either Vim syntax modes, or UltraEdit syntax files
- Figure out how to set modes automatically (I never said these weren't stupid :). lw's normal mode lines only work for a single major mode, and have to be on the first line of the file. That makes it impossible to set "shell mode" and "vim command" mode, and also impossible to do anything on shell scripts, which have to start with #!/usr/bin/zsh
- added 12/6/2007:
- get visual marking mode working. lw will already do visual marking of some sort (at least it will using Edi's lw add ons), but I want to integrate that with my Vim mode
- change lw's undo so it's more like Vim's. I'd like undo to go backwards in time and redo to go forwards. The normal lw/emacs undo model adds each undo command to the undo stack, and any cursor movement moves you back to the top of the stack, which makes things weird, to me.
- it'd be nifty to have a "size window" command so that for editing small files you can make the window take up only enough space to show the entire file
- Make ESC be vi's ESC, instead of lw's "meta" prefix (done)
- write a bug report listing the various editor code not included in the LW Pro distribution
- write a parser for "ed mode" colon-commands, e.g. :s/this/that/g.
- improve my Shell Mode and Perl Mode syntax highlighting so that they recognize nested single and double quotes, and make Shell Mode recognize when a shell script calls Perl in-line, and highlight the Perl code using Perl highlighting.
- added 12/7/2007:
- Saw -Ofun on reddit. That article had some good advice.
- Put vim-mode under darcs and publish it
- Give commit access to anybody that wants it
- announce it on the slim-vim list, the lispworks list, cll, and comp.editors.
- commit changes more often
- think about other ways to make Vim-mode development fun
- added 12/27/2007:
- implement gf (this is mostly done)
- added 01/10/2008:
- implement n and friends for "find next/prev item" (done)
- verify that z- works the way it's supposed to
- 01/11/2008:
- implement a and A (done)