3

Background

In :h motion.txt, it says:

FORCING A MOTION TO BE LINEWISE, CHARACTERWISE OR BLOCKWISE

When a motion is not of the type you would like to use, you can force another
type by using "v", "V" or CTRL-V just after the operator.
Example: >
    dj
deletes two lines >
    dvj
deletes from the cursor position until the character below the cursor >
    d<C-V>j
deletes the character under the cursor and the character below the cursor. >

In :h omap-info

To ignore the starting cursor position and select different text, you can have
the omap start Visual mode to select the text to be operated upon.  Example
that operates on a function name in the current line: >
    onoremap <silent> F :<C-U>normal! 0f(hviw<CR>

In :h movement for what can be used as a motion

    - Ex commands can be used to move the cursor.  This can be
      used to call a function that does some complicated motion.
      The motion is always characterwise exclusive, no matter
      what ":" command is used.  This means it's impossible to
      include the last character of a line without the line break
      (unless 'virtualedit' is set).

However, when these are combined:

  1. the motion is an Ex command; and
  2. the Ex command select an visual area;

the overwrite will not has an effect; e.g. the overwrite V in the following command

dV:normal hvl<cr>

will not be able to change the motion to be linewise.

Question

Now I want to define an textobject, for example

function! s:motion()
  " set the marks here
  return ":\<c-u>normal! `[v`]\<cr>"
endfunction
omap <expr> X s:motion()

to visually select the area. But I still want the motion wiseness of the textobject can be overwritten by v, V, <C-V>. For example:

dX       " to work characterwise
dvX      " to work characterwise, exclusive
dVX      " to work linewise
d<c-v>X  " to work blockwise

" can we get the overwrite v or V or <c-v> inside s:motion()? so that we can do
function! s:motion()
  " set the marks here
  let overwrite = ??
  return printf(":\<c-u>normal! `[%s`]\<cr>", overwrite)
endfunction
  1. Is it possible to achieve this without defining four different maps for X, vX, VX, and <c-v>X.
  2. If the overwritten mode v or V or <c-v> can be returned by a function, then the problem solves. But I cannot find such a function.
  3. Alternatively, can we select an area without using Ex command such as :normal xxVxx<cr>?
  • 3
    I don't think this is currently possible without defining extra mappings. There is an open issue 3490 for it. – Christian Brabandt Nov 28 '18 at 13:51
  • Thank you. The link you provided is very helpful. Hope the patch can be added to the release soon. How can I know the patch number of it? – Liu Sha Nov 28 '18 at 14:40
  • 1
    First of all, getting feedback at the given issue number is always helpful. So you might want to state that you need this feature for your plugin and that the given patch works. This might convince Bram to include this feature sooner rather than later – Christian Brabandt Nov 28 '18 at 14:45
  • I will add a comment over there tomorrow. – Liu Sha Nov 28 '18 at 15:07
3

@christian had referred a very useful link to me, which already provided a solution in the future release of vim. Specifically: mode(1) will return

"no"         " for operator-pending
"nov"        " for operator-pending forced to characterwise
"noV"        " for operator-pending forced to linewise
"no\<c-v>"   " for operator-pending forced to blockwise

With the patched provided, I will be able to do

function! s:motion()
  " set the marks here
  let motion_force = mode(1)[2:]
  let motion_force = motion_force == ''? 'v' : motion_force
  return printf(":\<c-u>normal! `[%s`]\<cr>", motion_force)
endfunction
onoremap <expr> X s:motion()
  • mode() is a string so I think get() won't work here (also the unicode quotes are wrong) – Mass Nov 28 '18 at 15:38
  • Ha, yes. I always try to use string as a list in vimscript. But they actually have different syntax. I edited the answer. In vimscript, string cannot even be indexed only sliced, which is not so convenient either. By the way, do you have any good method to reverse a string? – Liu Sha Nov 29 '18 at 1:27
  • join(reverse(split('string', '\zs')), '') looks inefficient but I think such a procedure would be necessary with unicode chars anyway – Mass Nov 29 '18 at 3:41
1

If your desired operator is a motion rather than a text object (i.e., does not need to alter the starting position), and it is exclusive, you can instead use absolute positioning.

function! s:motion()
  " ... compute cursor position for motion ...
  call cursor(28, 10)
endfunction
onoremap X :<c-u>call <sid>motion()<cr>

Then dvX, dVX and d<c-v>X work as expected. Thus it is not a problem to use ex commands, it is only bad to enter visual mode, as in your example maps.

  • Yes. You are right. If the Ex command does not select an area visually, the overwritten mode works. – Liu Sha Nov 29 '18 at 0:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.