Vimpulse and vim-mode

Vegard Øye vegard_oye at hotmail.com
Tue Mar 1 20:42:52 CET 2011


On 2011-03-01 16:57 +0100, Frank Fischer wrote:

> Am Dienstag, 1. März 2011 schrieb Vegard Øye:
>
> 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>].

What kind of command it is can only be determined in
`post-command-hook', after the command has done all its stuff --
completion or otherwise. `after-change-functions' collect info while
the command is executing, and then the hook uses that info to decide.

> 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.

True. It's just a heuristic, and it probably will have to be
supplemented with some hard-coding ("this is an indentation command")
as more complex use cases emerge. I don't see a way around the dreaded
blacklist -- just ways to (hopefully) make the blacklist shorter.

> 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).

Well, the rule does say that keystrokes are the default
representation. "tx" will be recorded as keystrokes because it doesn't
trigger an insertion. A command will only be recorded as insertion if
it inserts or deletes text /and/ it doesn't do something which cannot
be represented as an insertion. Otherwise, it's all keystrokes.

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

I agree. We might be overanalyzing this problem. Maybe the blacklist
is a tolerable solution after all, because the odd completion command
is all that needs to be listed.

>>     (vimpulse-define-operator vimpulse-insert (beg end &optional arg)
>>       "Insert before point."
>>       (interactive (list current-prefix-arg))
>>       ...)
>
> Perhaps we could extend the string-variant by further chars
> understood by define-operator? Would make the stuff more readable.

Seems like a bit of a job, judging by the length of `interactive's
docstring. I'm not sure it would be worth the effort.

> 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.

The problem is handling prefix arguments. If you have two counts, like
in "2d2w", the common heuristic is to multiply them together,
effectively giving "d4w". But with Emacs' `digit-argument', it is
difficult to get "in between" the motion count and the motion and say,
"But multiply by 2 first!" Hence, `vimpulse-keypress-parser' reads a
motion and its count without executing them, then the motion count is
multiplied with the operator count (if any), and finally the motion is
executed. Each step is disentangled from the others.

> 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"
>
> 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.

Good question. On the face of it, there doesn't seem to be much use
for :contract, since you can just remember the previous positions.
(Indeed, the `line' transformation is irreversible, so in its case you
/must/ remember them.) The reasons I want contraction are more subtle
and based on experiences with "gv". How do you save a Visual selection
for later use? Well, you can use markers to store the boundaries. But
what boundaries do you store -- the unexpanded ones, or the expanded?
Now we have two approaches:

    * Remember the unexpanded buffer positions of point and mark,
      along with the Visual submode; these three values define the
      selection. In other words, just put everything exactly where
      it was the last time.

    * Remember the /expanded/ positions of point and mark, plus the
      submode. Contract the positions as you re-enable the selection.

I call these the "intuitive approach" and the "other approach",
respectively. As you've guessed by now, the intuitive approach has
some weaknesses. Since point and mark "expand" to an inclusive
selection, the largest position is one less than the end of the
selection. In other words, one of the markers is actually /inside/
the selection.

What would happen time and again when I applied something slightly
destructive on the selection was that the marker would get pushed out
of it. Then, when I did "gv", the selection would be one character
larger than before. ("d" is /too/ destructive to demonstrate this,
while "u"/"U" is at the opposite end of the spectrum since they do
everything character by character. You need something "in between",
preferably a command which inserts and deletes here and there --
like `comment-region'. It's a subtle bug.)

So what the "other approach" does is to get the markers out of the way
by expanding them first. That way, a destructive command won't affect
the markers, since they coincide exactly with the boundaries of the
text the command is working on. Of course, this approach does require
the inverse of expansion -- contraction -- afterwards. And in the case
of `line' you actually cannot contract, so you'll have to fall back on
remembering the original positions. Still, this solved the problem.

Another thing: is using ":" as a prefix separator good for anything in
Emacs Lisp? I haven't seen this practice elsewhere; all other Emacs
packages separate their prefix with a hyphen (as suggested in the
Emacs Lisp Reference Manual).

-- 
Vegard



More information about the implementations-list mailing list