This is expected behaviour.
Because :call is part of an Ex command, it behaves as described at
:help {motion}
(the fifth of the 'dot points'), i.e. it is always characterwise exclusive. A way
to solve it is to use an <expr> map to avoid an Ex command altogether. Idea
implementation:
function! MyOp(op)
let c = getchar()
echoerr "Executing ".a:op.nr2char(c)
return a:op.nr2char(c)
endfunction
onoremap <expr> f MyOp('f')
Ben.
Send instant messages to your online friends http://au.messenger.yahoo.com
I'm probably doing something incorrectly. Instead of off-by-one or
whatever, I get:
gvim --noplugin -U none -U NONE omap_cf.vim
/Now run/
cf
:call MyOp('f')<CR>[] <---- this shows up on the display where [] marks
the cursor position
now type that w...
:call MyOp('f')<CR>w[]
Beyond the directions: I then typed a <cr>. I got a half-height cursor
at the : on the command line, but nothing was otherwise happening.
I then pressed yet another <cr> on in:
Error detected while processing function MyOp:
line 2:
Executing f^M
E488: Trailing characters
Press ENTER or type command to continue
(I have a huge version of vim, 7.1.245)
Regards,
Chip Campbell
I'm using a huge version
Another way:
:h o_v
:onoremap f v:call MyOp('f')<CR>
The only change is inserting a "v" in the mapping.
--
Andy
Thanks Ben and Andy.
I keep getting closer and closer, I can almost taste it.
Using v: has got me almost where I need to be, but yet again I find a
minor discrepancy.
I have the following text:
foo(1,2)
Placing the cursor on foo and hitting cw changes up to and including
the "(". It should only include "foo".
Looking at :h o_v it says:
*o_v*
v When used after an operator, before the motion command: Force
the operator to work characterwise, also when the motion is
linewise. If the motion was linewise, it will become
|exclusive|.
If the motion already was characterwise, toggle
inclusive/exclusive. This can be used to make an exclusive
motion inclusive and an inclusive motion exclusive.
:h w
<S-Right> or *<S-Right>* *w*
w [count] words forward. |exclusive| motion.
:h e
*e*
e Forward to the end of word [count] |inclusive|.
So, using v:, can change an exclusive motion into an inclusive one.
So if anything that should result in basically "cw" becoming the same
as "ce" (if I interpret this correctly).
ce works as expected and only changes "foo".
Here is the script you can execute to demonstrate the issue. Just
source this script.
*****
set nocompatible
function! MyOp(op)
let op_motion = a:op
" Check if we are in operator-pending mode
if op_motion =~? '\(f\|t\)'
" If the operator pending mode is f or t
" request the final character from the user
let c = getchar()
let op_motion = op_motion . nr2char(c)
endif
let v_count = ((v:count > 0)?(v:count):'')
echoerr "Executing ".v_count.op_motion
exec "normal! ".v_count.op_motion
" return a:op.nr2char(c)
endfunction
onoremap f v:call MyOp('f')<CR>
onoremap t v:call MyOp('t')<CR>
onoremap w v:call MyOp('w')<CR>
onoremap e v:call MyOp('e')<CR>
" Now run:
" foo(1,2)
*****
Comments?
Thanks,
Dave
For "t" and "w" the "v" is to be omitted.
> onoremap e v:call MyOp('e')<CR>
>
> " Now run:
> " foo(1,2)
> *****
>
> Comments?
>
> Thanks,
> Dave
The builtin "w" motion is exclusive. If you omap "w" to an Ex command
you'll get an exclusive motion, per default. That's just what you want;
prepending "v" to the Ex command makes your "w" motion inclusive (not
wanted).
"cw" is a confusing example, because it is an exception (works like
"ce").
:h cw
Indeed, "e" is inclusive, but it stops earlier than an inclusive "w"
would do:
"foo bar" -- ce --> "| bar"
"foo bar" -- cw --> "|ar"
v:operator can help to detect the "c" operator. Maybe you could try
this mapping:
:ono <expr> w v:operator=="c" ? "v:call MyOp('e')<cr>"
\ : ":call MyOp('w')<cr>"
What are you going to do?
Is it still for the yankring plugin, where you need the omap-motion to
yank what it moves over (AIUI), as a side effect? In this case you
cannot use :map-<expr> (see Ben's post) -- although I'd also prefer it,
because it is more reliable. Remapping all the motions your way is
difficult because of the subtle exceptions -- there is not only "cw".
Minor:
- I'd rename "MyOp" into "MyOmap" or "MyMotion" ...
--
Andy
Ah, wrong, it was a little different. You want to cancel
Omap-mode and then execute v:operator + motion argument in
MyOp(). I just looked up
:h v:operator
Some day I'll get it ;-)
--
Andy
I just got a simple idea. Works even for "cw".
nn <script> yy yy<sid>cr
ono <script> w w<sid>cr
" copy register
nn <silent> <sid>cr :call <sid>cr()<cr>
ino <script> <sid>cr <c-o><sid>cr
func! <sid>cr()
let reg = getreg(v:register)
echo reg
endfunc
--
Andy
> ino <script> <sid>cr <c-o><sid>cr
Hmm, this breaks the redo command "." after "cw", therefore:
:ino <silent> <sid>cr <c-r>=<sid>cr()<cr>
> func! <sid>cr()
> let reg = getreg(v:register)
> echo reg
return ""
> endfunc
--
Andy
" Current try (works well for me :-) :
ono <expr><script> f <sid>getzapchar("f"). "<sid>cr"
ono <expr><script> F <sid>getzapchar("F"). "<sid>cr"
func! <sid>getzapchar(fcmd)
return a:fcmd. s:Getchar()
endfunc
func! s:Getchar()
let c = getchar()
if c != 0
let c = nr2char(c)
endif
return c
endfunc
Looks like "." doesn't repeat the expr-eval, only the result.
--
Andy
Thanks for the response Andy.
I tried this out when you originally posted it (sometime ago) and have
tried a few work arounds.
> " Current try (works well for me :-) :
Yes, what you provided works but not in the original case.
Your map:
> ono <expr><script> F <sid>getzapchar("F"). "<sid>cr"
Successfully calls getzapchar(), but once getzapchar() completes, it
does not call "<sid>cr" which is the reason I cannot use <expr> maps.
Not sure why this limitation exists, but everything else is working
great except the f and t maps.
Unforutnately, I use this religiously and have to have them working
and repeatable before I release the next version of the YankRing.
Any other possible suggestions?
TIA,
Dave
For no vimrc, no gvimrc, no plugins, but 'nocompatible' mode, use:
vim -N -u NONE
for the same, but with global plugins, use
vim -N -u NORC
This, however, will also load any additional global plugins in
$HOME/.vim/plugin (Unix), $HOME/vimfiles/plugin (Windows),
$VIM/vimfiles/plugin (all). To avoid these, you may want to temporarily
rename their .vim and vimfiles parents to something where Vim won't look
into.
To avoid sourcing your own vimrc and gvimrc, and still enable (among
others) syntax highlighting in a way that Bram can reproduce, you can use:
(Windows)
gvim -u C:\PROGRA~1\vim\vim71\vimrc_example.vim -U NONE
(Unix)
gvim -u /usr/local/share/vim/vim71/vimrc_example.vim -U NONE
assuming default install paths in both cases, and 8.3 dirnames on
Windows to avoid the problems sometimes caused by spaces in them. "-N"
can be omitted in this case because the vimrc_example.vim sets
'nocompatible' as it starts.
The same remarks about own-installed plugins applies; see under -u NORC
above.
Best regards,
Tony.
--
There were the Scots
Who kept the Sabbath
And everything else they could lay their hands on.
Then there were the Welsh
Who prayed on their knees and their neighbors.
Thirdly there were the Irish
Who never knew what they wanted
But were willing to fight for it anyway.
Lastly there were the English
Who considered themselves a self-made nation
Thus relieving the Almighty of a dreadful responsibility.
---------- Forwarded message ----------
Okay, here is a recap.
Goal:
For every operation in Vim that effects a register, record the changed
value in the YankRing plugin for later use.
The current version of the YankRing (3.0) does this but not for all
motions and text objects. I also does not handle any changes (like
cw).
The new version handles everything (including all text objects :h
text-objects), changes, clipboard.
It has been accomplished using this technique for the maps:
nnoremap <script> w w<SID>YRRecord2
Since the above is an "omap", if I issue a dw, cw, yw it triggers the
map. Thanks to Andy, it executes the 'w' to complete the motion, then
calls a function in the YankRing plugin to record the changed
register. This technique also supports "." (repeat). Hot diggidy
dog!
This works for all motions and text-objects _except_ 't' and 'f'.
t and f are special cases since we must ask the user for the character
to perform the motion up to.
If you have this word in your buffer: my_word_with_underscores
df_ will delete up to and including the first _, resulting in:
word_with_underscores
dt_ will delete up to the first _, resulting in: _word_with_underscores
Handling these 2 special cases has been troublesome.
Andy and Ben suggested using a <expr> map.
This works great. My function knows it is a 't' or 'f' map and
prompts for the following character. And it is executed.
The problem with the above is for some reason when an <expr> map is
used, any command following the function call is not called by Vim.
So with the previous technique:
nnoremap <script> w w<SID>YRRecord2
We execute w, the omap simply returns 'w', telling Vim to do the usual
operation and once it finishes, it calls the YRRecord2 function.
The same approach with the <expr> map, execute the f motion but will
NOT call YRRecord2.
The YRRecord2 function must be called _after_ the map (or function
completes) since the registers are _not_ updated until the function
completes. Hence I cannot record any changed registers.
Ben had another suggested approach. Instead of using an <expr> map,
use an omap, but cancel the omap first and then execute the command in
your own function. Something like this:
onoremap w <Esc>:<C-U>call TestOMap()<CR>
TestOMap() will execute the v:operator.w and attempt to record the changes made.
This fails for a number of reasons:
- The registers are not updated until the function completes
- Cancelling the omap with <Esc> and re-executing it runs into
inclusive vs exclusive problems (which <expr> maps avoid entirely)
I attempted to solve this by using feedkeys(). So in the TestOMap
function, after executing the command I did this:
call feedkeys(":silent! :call YRRecord('".user_register."')\<CR>", 't')
So, when the function completed, I "fed" a call to my function. This
only partially worked.
Some of the test cases when starting Vim using:
gvim -u NONE -U NONE --noplugin omap_cf.vim
***** omap_cf.vim *****
set nocompatible
set hidden
function! MyOp(op)
let op_motion = a:op
" Check if we are in operator-pending mode
if op_motion =~? '\(f\|t\)'
" If the operator pending mode is f or t
" request the final character from the user
let c = getchar()
let op_motion = op_motion . nr2char(c)
endif
let v_count = ((v:count > 0)?(v:count):'')
" echoerr "Executing ".v:count.op_motion
exec "normal! ".v:count.op_motion
" return a:op.nr2char(c)
endfunction
function! MyDisplay()
echo 'reg:'.@"
let @a = @"
endfunction
function! MyExprOp(op)
let c = getchar()
return a:op.nr2char(c)
endfunction
onoremap f v:call MyOp('f')<CR>
onoremap t v:call MyOp('t')<CR>
onoremap w :call MyOp('w')<CR>
" onoremap e v:call MyOp('e')<CR>
" onoremap iw v:call MyOp('iw')<CR>
onoremap f v:call MyOp('f')<CR>
onoremap t v:call MyOp('t')<bar>:call MyDisplay()<CR>
onoremap w :call MyOp('w')<CR>
onoremap f v:call MyOp('f')<CR>
onoremap t v:call MyOp('t')<bar>:call MyDisplay()<CR>
onoremap <expr> f MyExprOp('f') |:call MyDisplay()<CR>
onoremap <expr> f MyExprOp('f')<bar>:call MyDisplay()<CR>
onoremap w :call MyOp('w')<CR>
" Now run:
" foo(1,2)
**********
I am happy to use expression maps if I can somehow get a function to
be called after them using the technique (or otherwise) above:
onoremap <expr> f MyExprOp('f')<bar>:call MyDisplay()<CR>
Just wondering if there is anyway how this might be accomplished.
TIA,
Dave
The ":call MyDisplay()<CR>" should either be part of the expression that MyExprOp
returns, or appended to it with the "." operator. So far you've tried including it
as for a regular map, not an <expr> map. Try (roughly)
func! MyExprOp(arg)
let char=getchar()
return "f".char.":call MyDisplay()\<CR>"
endfunc
or
onoremap <expr> f MyExprOp('f').":call MyDisplay()\<CR>"
and let me know how you go...
Ben.
YES!
That works like a charm and it makes sense too!