Vimpulse and vim-mode

Frank Fischer frank.fischer at mathematik.tu-chemnitz.de
Tue Mar 1 16:57:55 CET 2011


Am Dienstag, 1. März 2011 schrieb Vegard Øye:

> My point is that auto-completion /is/ a linear change. 

But doesn't this depend on the concrete package? Completion could also 
insert text behind point (I think of some of those snippet packages).

But that was not what I meant. When some of those completion-dialogs 
pops up, the user can keep on typing or use the cursor-keys or whatever 
to select possible completions. How do you realize that these commands 
should *not* be recorded although they may look like, e.g., a motion (I 
just don't know how these packages really work). If this packages 
inserts text behind point it will drop you in the second case, but then 
the sequence of keys just does not represent what has been inserted 
(the sequence may be something like [a b b <down> <down> <up> c d 
<enter>]. There are two cases: if you wait for some time after typing 
the second b you go into completion mode and the motions are mapped to 
completion dialog. But if you do not wait those motions are really 
motions. One would have to realize in which situation we are).

But maybe I'm just too scary of those packages and such strange things 
never happen.

Another point why I would prefer key-sequence as default in all cases is 
that sometimes insertion triggers other changes, too, like indentation. 
I this case repeation of key-sequences does what you want (indent 
according to where the repeation took place) while direct insertion of 
text may fail. This may depend, in this example, on whether the 
additional spaces are inserted just before point.

In fact, is there a way to detect motion of point? Again another 
pre-/post-command-hook hack or something better? I ask because in some 
situations a certain motion or sequence of motions will actually change 
point while in other situations point will left unchanged (I think of 
motions like "tx" which does not move point if we are right before an 
x - okay we are in insert mode but again such situations may happen, 
e.g., end-of-line by leave point unchanged in one situation but when we 
repeat the keq-sequence elsewhere it would not).

It seems to me that the key-sequence approach is almost always the right 
way. If there weren't those nasty completion things ... ;)

Anyway, don't care about my arguments, I propably see problems where no 
problems are.

> Yeah, and the `define-operator' macro should let one add code to that
> specification (not replace it, just augment it). Actually,
> `vimpulse-define-operator' allowed for this, just not in a very
> obvious way. If the body contained an `interactive' form, the return
> value of that form (which must be a list) would be concatenated with
> the calculated values for BEG and END. E.g.,
>
>     (vimpulse-define-operator vimpulse-insert (beg end &optional arg)
>       "Insert before point."
>       (interactive (list current-prefix-arg))
>       ...)
>
> This would roughly expand to the following function definition:
>
>     (defun vimpulse-insert (beg end &optional arg)
>       "Insert before point."
>       (interactive (append (vimpulse-calculate-beg-and-end)
>                            (list current-prefix-arg)))
>       ...)
>
> There might be a better syntax for this, but the functionality
> is there.

Perhaps we could extend the string-variant by further chars understood 
by define-operator? Would make the stuff more readable.

> >> Btw, what about commands like , and ;. The do not have a specific
> >> type. Their type may be either explicit or implicit depending on
> >> the previous search command.
>
> Good point. "}" is another motion which, depending on the
> circumstances, may use the `line' type instead of `exclusive'. I
> ended up with a global variable for the current type, which is
> initially set when the motion is executed, but may also be changed by
> the motion itself. The final value is used for the type when the
> buffer positions are normalized.

Is a valid solution and should work.

> >>> There is one case where I cannot see any other solution than a
> >>> blacklist, though: normalizing Emacs' region to the Visual
> >>> selection. We have to do this /before/ the next command is called
> >>> (so we can't inspect its behavior), and we don't want to do it if
> >>> the next command is a motion.
> >>
> >> I do not understand. You can inspect the command in a pre-command
> >> hook and normalize the region accordingly or what do you mean? Of
> >> course, this only works if the commands use the interactive
> >> statement nicely.
>
> You can inspect the command, but you can't really know what it is
> going to do. You could check out `interactive-form' to get a clue
> (which I tried), but the problem is that the form turns into mush if
> the function is compiled. Also, the command's behavior may be
> contingent on the activation of the region (e.g., skeletons).
> Consequently, there is no reliable way to identify region commands,
> at least not before the command is executed.

Exactly - I even doubt if this is possible after the command is executed 
in all cases. But never mind. Propably no one uses non-*evil* ;) 
motions anyway or only standard emacs stuff. Although I wish there were 
a better solution than a blacklist.

> `when' and `unless' are provided by vanilla Elisp (in subr.el), cl.el
> just redefines them. `defun*' is for keyword arguments, which we'll
> probably not need. :) `lexical-let' provides lexical scope, which
> I've never used in Vimpulse. (I've only had one use for it: in my
> test framework, I needed to evaluate forms defined outside the
> evaluating function; regular local bindings could overwrite the form.
> What do you use `lexical-let' for? Closures?)

Yes, I used closures for vim:defcmd and vim:defmotion magic and perhaps 
for a few other things.

Don't care about dropping cl, I'm just too used to it and never cared 
about this dependency (at least at compile-time). For me cl with all 
its nice functions was a perfectly legal library to use and I really do 
not understand why it's not part of the core or I why its use is 
banished ... but this is has nothing to with our project, so never 
mind, just delete ;)

> >> When you press "dw", "d" calls `delete', `delete' reads "w", "w"
> >> is bound to `forward-word', `forward-word' is executed to get
> >> buffer positions, and the positions are passed to the operator's
> >> arguments. Then the operator's body is executed.
> >
> > It's just the difference to how vim-mode combines motions and
> > operators. In vim-mode operator-pending is just a state as any
> > other state, insert, normal or visual. When an operator is
> > executed, the command's body is not executed immediately. Instead
> > it is remembered for later use, then vim-mode switches to
> > operator-pending mode and gives the control completely back to
> > Emacs. When a motion is executed in operator-pending mode the
> > stored operator is finally executed along with the motion. Similar
> > things happen when a register is selected. In this case the
> > register is stored and then vim-mode returns to normal mode and
> > proceeds as usual.
>
> If I understand vim-mode correctly, the motion is read and executed
> in another iteration of the command loop. In Vimpulse, everything
> happens immediately: "w" is read from the keyboard, the corresponding
> command is found with (key-binding "w"), and then it is executed. I
> ended up building a little "keypress parser" for handling counts as
> well. This is able to handle things that Emacs' command loop actually
> can't, such as "da2w". See below (search for
> `vimpulse-keypress-parser'):
>
> http://gitorious.org/vimpulse/vimpulse/blobs/master/vimpulse-operator
>.el
>
> Vimpulse does have a brief Operator-Pending state, which is entered
> before `key-binding' is called. When the motion is finished
> executing, the state promptly switches back to Normal. Thus,
> everything is done in a single iteration of the command loop.

Using a custom key-parser was how vim-mode started. But then I realized 
that the default key-handling of Emacs is really enough and provides 
everything I need.

Of course, I never handled the key-stuff in interactive.

But I wonder if it is possible to switch to operator-pending, then call 
read-key-sequence (or something like this), then switch back and 
continue as before. Of course, before switching one has to read the 
numeric prefix but this should be possible, I think.

Of course, you could not handle commands like "da2w" this way but "2d3w" 
should work. But I think "da2w" is not really important - vim does not 
know it, too, I think. Even more, it makes only sense after an "a" 
or "i", but "dg3j" looks odd. Or perhaps someone uses longer 
key-sequences.


> However, we may reuse Emacs' /concept/ of sentences and words. The
> `sentence-end-double-space' variable, for example, influences how
> sentences are understood. Should we honor such things, or should we
> invent our own system from scratch?

Well, if written well those motions are rather stand-alone so it is not 
important to decide it now. Usually I prefer to reuse Emacs' own 
definition of things but often they do not really match Vim's - and 
this may lead to confusion of someone uses both, Emacs and Vim, and 
wants to have Emacs as close to Vim as possible. E.g. forward-sentence 
moves to the final period while Vim's version move to the beginning of 
the next sentence.

In any case I would try to respect Emacs' own definitions if possible 
(sentence-end-double-space is such an example). But propably we just 
will have to have something own to make the motions behave as expected.



Another question about types. You use :contract and :expand ... I wonder 
something like the following would better reflect what we need:

   evil:region  
  "returns BEG and END of the real region covered by motion"

   evil:line-region
  "returns BEG end END of the lines covered"

I mean, every motion is pair of coordinates and a type. We just need 
sometimes the Emacs region covered by this motion return by evil:region 
and sometimes vim:line-region to cover the corresponding lines. I think 
this should somehow correspond to :expand and :contract. I do not like 
those terms because I would expect from them that I can apply them 
several times (i.e., expand several times) - well, you can do it but it 
does not make sense. But I think you have a special use case where the 
suggestion above does not fit, right? So what do you expect 
from :expand and :contract, especially, why do you need the inverse 
transformation? It should be equally easy to store the original pair of 
BEG and END so the inversion should not be necessary.


Well, whatever we decide for all those points above, I think it's time 
to start (if we want, I do ;)).

To summarize, I think the follow things have to be done first:

- create gitorious project
- start basic documentation of texinfo sources (outline)
- basic initialization of evil
- state macros, simple state switching and keymaps, a state requires 
(roughly)
  * name
  * list of keymaps
  * cursor type (?)
  * a mode-line symbol (?)

If this works, i.e., we can start the mode and switch happily between 
the states and all keymap-stuff works, would should start with the 
define-{operator,motions,...} macros. We do not need full functionality 
at this point only the basic stuff, so we can execute simple commands, 
perhaps simple operators (something like gu or g~ should be enough) and 
a few motions. Now we should implement the complete repeation and undo 
stuff, which is propably the most difficult part. But once this is 
ready we have a basis to implement all commands and motions and 
operators we need. 

From now on we can propably reuse much from vimpulse and vim-mode, 
because each motion/command should be almost independent from the rest. 
This means we can easily test different possibilities and choose the 
best.

Ex-mode as well as the interactive search can also be done now, it is 
just another independent subsystem.

Once the core is ready I suppose the implementation of most commands and 
motions should be done relatively fast because they are already there.

Bye,
Frank


-- 
Frank Fischer
Chemnitz University of Technology, Department of Mathematics
eMail: frank.fischer at mathematik.tu-chemnitz.de
Tel.: +49 (371) 531-36913



More information about the implementations-list mailing list