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