surround.vim vimpulse extension

Tim Harper timcharper at gmail.com
Sun Jul 25 09:53:23 CEST 2010


On Sat, Jul 24, 2010 at 3:23 PM, Vegard Øye <vegard_oye at hotmail.com> wrote:
> On 2010-07-23 20:46, Tim Harper wrote:
>
>> I'm not extremely happy with my approach on how I'm looking into the
>> delete/change commands. Basically, I'm hijacking them in doing an
>> advise (as in defadvice) approach.
>
> There isn't a perfect way, not even in Vim itself. surround.vim does
> this:
>
>    nmap ds <Plug>Dsurround
>
> Which corresponds to making a "careful" binding in Vimpulse -- that
> is, one of these lines:
>
>    (vimpulse-map "ds" 'vimpulse-surround-delete)
>    (vimpulse-make-careful-binding viper-vi-basic-map "ds" 'vimpulse-surround-delete)
>
> Which will execute `vimpulse-surround-delete' when the user hits "ds"
> and `vimpulse-delete' when the user hits "d" + any other key than "s".
> The fatal flaw with this approach, both in Emacs and Vim, is that the
> keystrokes are read /before/ Operator-Pending mode is entered, so the
> half-height "operator cursor" is permanently lost.
>
> Second approach: write a "null motion" (one that returns an empty
> range) and bind it to "s" in Operator-Pending mode:
>
>    (defun vimpulse-surround-delete (&optional arg)
>      (interactive)
>      ;; Delete surrounding characters
>      ...
>      (viper-move-marker-locally 'viper-com-point (point)))
>
>    (define-key vimpulse-operator-basic-map "s" 'vimpulse-surround-delete)
>
> This will preserve the half-height cursor. The call to
> `viper-move-marker-locally' ensures that the starting point is
> equivalent to the ending point -- e.g., (vimpulse-delete 45 45) --
> hence no actual deletion takes place.
>
> Third approach: we could make the function bodies of `vimpulse-delete'
> and `vimpulse-delete' contingent on a "cancellation variable". A
> pseudo-motion like above could set this to t during its execution to
> inhibit the deletion code of `vimpulse-delete', the change code of
> `vimpulse-change', and so on. (Conceivably, `vimpulse-range' itself
> could cancel further code execution with `keyboard-quit', but this
> would probably be noisy.)
>
> Fourth approach: stay with the delete-or-surround DWIM functions, but
> bind them in a separate, toggleable keymap rather than overwriting the
> basic bindings. Like this:
>
>    (define-minor-mode surround-mode)
>
>    (vimpulse-define-key 'surround-mode 'vi-state "d" 'vimpulse-delete-surround-or-delete)
>    (vimpulse-define-key 'surround-mode 'vi-state "c" 'vimpulse-change-surround-or-change)
>
> Then you can enable surround-mode in certain buffers as appropriate,
> or turn it into a global minor mode.

Approach 4 does seem pretty good (better than the current behavior of
hijacking another keymap).  What about option 5:

advise vimpulse-range to set the *vimpulse-surrounding* variable to
nil before calling it
advise vimpulse-delete with an around filter, if
*vimpulse-surrounding* is non-nil, then call vimpulse-surround-delete,
otherwise invoke as normal with ad-do-it

Benefits:

* If for some sick reason one decides to remap vimpulse-delete, they
can do so with less complexity of having to remap
vimpulse-delete-surround-or-delete (maybe dvorak users would want to?)
* Multiple plugins can chain in this way. Both the current solution
and approach 4 have the limitation that if another plugin wants to do
something similar, it will conflict with vimpulse-surround.

Drawbacks:

* We are depending on side-effect value from a previous function call.
This adds complexity and can make things difficult to follow.



More information about the implementations-list mailing list