vimpulse bug: visual-line does not select final newline

Vegard Øye vegard_oye at
Mon Aug 23 00:42:09 CEST 2010

On 2010-08-19 19:06, Štěpán Němec wrote:

> I went for the minor mode approach (and thus the second recommended
> solution above) to be able to enable and disable the plugin cleanly.


> There are still glitches, though, e.g. with the mode enabled,
> `cw' eats the space after the word being changed, unlike in plain
> Vim(pulse) ... I guess I'll try to investigate when I get the
> time'n'energy, but the relevant parts of Vimpulse make my head
> explode, so I welcome any advice or contribution. ;-)

In a simple world, motions and operators would be completely
orthogonal. In the real world, special cases turn out to have their
uses. The "w" and "W" motions normally include the trailing space,
unless the operator is "c", in which case they exclude it. (This
strange-yet-intuitive behavior is not a Vim hack, but is part of
classical vi, as can be seen by running Viper only or enabling :set
compatible in Vim.)

Which raises an implementation question: are operators functions of
text ranges, or functions of motions? If motions and operators are
orthogonal, then it doesn't matter where the text range came from, and
operators may take a range as their input. If the motion does matter,
on the other hand, then that should be the operator's argument.

Vimpulse's solution is to accept both in different contexts. If the
operator is called interactively, then not only is a motion read from
the keyboard, but the current motion and operator can be accessed via
the variables `vimpulse-this-motion' and `vimpulse-this-operator'. If
the operator is called programmatically, on the other hand, then only
a text range is specified, and no motion enters the picture. Finally,
there's `vimpulse-operator-apply', which lets us pass a motion to an
operator in a programmatic context. It is used by the repeat system,
which remembers the last repeatable operator/motion combination in
`vimpulse-last-operator' and `vimpulse-last-motion'.

Based on this, a cleaner way to define the dispatchers would be

    (defun vimpulse-change-surround-or-change (beg end &optional dont-save)
      (interactive (vimpulse-range))
      (if (memq vimpulse-this-motion
          (vimpulse-surround-change beg end (eq *vimpulse-surrounding* 'strip))
        (vimpulse-operator-apply 'vimpulse-change

where the `memq' expression would of course be replaced by a call to a
`vimpulse-surround-object-p' function which looked up the motion in a
list autogenerated by the `vimpulse-surround-define-text-object'
macro. This definition also obviates the need to list the dispatcher
in `vimpulse-newline-cmds', since it is an operator proper.

Speaking of macros, I think an even cleaner solution would be to build
a condition into all operators for bypassing the operator's body when
called interactively. If the motion sets some variable to t, then the
operator will do nothing -- only the motion is executed. Defining the
surround commands as "standalone motions", there is no need for
dispatcher functions at all, which lets vimpulse-surround play nicely
with other extensions which might do similar things.

Furthermore, such a variable would allow for a reversal of roles where
the motion is calling the operator (which is actually how vip and
Viper were implemented), letting one add instructions /after/ the
operator. For example, a custom "dap" object may clean up whitespace
between paragraphs as a separate step, so that the deleted paragraph
may be pasted without the extra whitespace.

There's a lot of possibilites once you have a general
`vimpulse-define-operator' macro in place.


More information about the implementations-list mailing list