From my research into this, you need to define at least two custom motions. The first will be for just moving the cursor, and the second will be for use in operators. For a full functioning setup, it is much more complicated, and I would recommend looking at the code in CamelCaseMotion by Ingo Karkat which shows significantly more thought on doing this right than I do below. Most of my answer is based on quick information found in Vim help and experimentation.
In the following examples, I'm going to remap the
( motions to do the each end of a sentence. You can use other characters as you wish by modifying the example. I'm also going your example of
vis<ESC> as the more robust option (compared to
)T.). In doing so, if you are already at the end of a sentence, you end up staying at the end instead of moving to the end after this one, because of the way
is works. You can try replacing it with
as for different results. This should actually work for turning either side of a text object into a motion.
First, the normal mode movement
Note This doesn't work in visual mode or operator pending mode
function! MoveToEnd(type, ...)
exe "normal `]"
map <silent> \) :set opfunc=MoveToEnd<CR>g@
nmap ) \)is
First, we define a new function called
MoveToEnd whose sole job is to put the cursor at the the "last character of the previously changed or yanked text", which is the
] mark (see
Next, we create a custom operator
\) that calls our
MoveToEnd function on whatever motion is given to it. In this way, the
] mark becomes the end of the selection given to our new operator. (see
Finally, we map
) to call our custom operator with the
is mostion, thereby effectively giving the equivalent of
vis<ESC> without changing the last visual selection and without changing modes.
The reverse is similar, just using the other directions:
function! MoveToBeginning(type, ...)
exe "normal `["
map <silent> \( :set opfunc=MoveToBeginning<CR>g@
:nmap ( \(is
Second, the operator movement
This one is trickier. Our motion above destroys the start position, so operators like 'd' end up working against nothing. What we need is to be able to save our start position and add the end position.
:help omap-info (operator pending mapping) gives a tip on doing this, which is to actually use visual mode.
What we'll do then, is save our position to one end of the visual selection register, use our normal mode motion from above, set the other visual selection register, and then activate visual mode:
normal m< "Set start point of visual selection
normal ) "Move to end of sentance (relies on mapping from earlier)
normal m> "Set end point of visual selection
normal gv " 'Go Visual' activate visual mode using selection marks
omap ) :call SelectToEnd()<CR>
And for our back motion:
normal m> "Set end point to current position (since we are moving backwards)
normal ( "Move to beginning of sentence (relies on mapping from earlier)
normal m< "Set start point
normal gv "Activate visual mode based on our new marks
omap ( :call SelectToBeginning()
With this, you can do
d) to delete to the end of the sentence (instead of the original of deleting to the beginning of the next one). All other operators should work as well.
<esc>s? You only need one.
)gE. I know you said you always end sentences with a period, but it's better to not even have to assume that.
isdo not only work in visual mode. You can do
dasin normal mode to Delete A Sentence.