You could use this function to do what you want:
It takes 3 arguments:
- A normal mode command (which should move the cursor)
- The mark where to beginning the motions
- The destination mark
It will return the minimal number of motions required to reach or overstep the destination mark
It test several cases and stop the function if
- The direction of the motion (forward or backward) doesn't allow to reach the destionation mark
- The command given as first parameter doesn't move the cursor
- One of the mark is undefined
- The mark are in the same position (0 is returned)
Examples of calls would be:
:call CountMotions("fa", "a", "b")
:call CountMotions("w", "c", "b")
:call CountMotions("h", "c", "e")
Beware if the first parameter is a destructive command (dd
for example) it will be executed
at least once.
Here is the function:
fun! CountMotions(motion, mark1, mark2)
" Save the cursor position
let savedPosition = getpos('.')
" Get position of the marks
execute "let m1Position = getpos(\"'" . a:mark1 . "\")"
execute "let m2Position = getpos(\"'" . a:mark2 . "\")"
" If a mark is undefined stop the function
if (m1Position[1] == 0 && m1Position[2] == 0)
echo "'" . a:mark1 . " is not defined"
return -1
endif
if (m2Position[1] == 0 && m2Position[2] == 0)
echo "'" . a:mark2 . " is not defined"
return -1
endif
" If the marks are not on the same buffer stop the function
if m1Position[0] != m2Position[0]
echo "The marks are not on the same buffer"
return -1
endif
" Position the cursor on the first mark
call setpos('.', m1Position)
" Get the distance between the two marks
let distance = sqrt( pow((m2Position[1]-m1Position[1]),2) + pow(( m2Position[2]-m1Position[2] ),2) )
let previousDistance = distance + 1
" If the marks are on the same chatacter stop the function
if ( distance == 0 )
echo "the marks are on the same character"
return 0
endif
" Check which mark is the first one in the buffer
let firstMark = a:mark1
if (m1Position[1] > m2Position[1])
let firstMark = a:mark2
elseif (m1Position[1] == m2Position[1])
if (m1Position[2] > m2Position[2])
let firstMark = a:mark2
endif
endif
" Check in which direction the motion goes
let direction = "forward"
" Execute one motion and increment the count
execute "normal " . a:motion
" Get the position after the motion
let newPosition = getpos('.')
" Reposition the cursor on the first mark
call setpos('.', m1Position)
if (m1Position == newPosition)
echo "The motion doesn't move the cursor"
return -1
endif
if (m1Position[1] > newPosition[1])
let direction = "backward"
elseif (m1Position[1] == newPosition[1])
if (m1Position[2] > newPosition[2])
let direction = "backward"
endif
endif
" If the direction doesn't allows to reach the second mark stop the function
if ( (firstMark == a:mark1 && direction == "backward") || (firstMark == a:mark2 && direction == "forward") )
echo "wrong way, mark unreachable"
return -1
endif
let countMotion = 0
let previousPosition = []
let continue = 1
" Count the number of motions
while continue == 1
" Execute one motion and increment the count
execute "normal " . a:motion
let countMotion = countMotion + 1
" Update position
let m1Position = getpos('.')
" Calculate the changes in the distance
let previousDistance = distance
let distance = sqrt( pow((m2Position[1] - m1Position[1]), 2) + pow((m2Position[2] - m1Position[2]), 2))
"echo "[" . m1Position[1] . " " . m1Position[2] . "]" . "[" . m2Position[1] . " " . m2Position[2] . "]" . "distances " . string( distance ) . " " . string( previousDistance )
" Check if the loop should continue
if (distance >= previousDistance)
let continue = 0
endif
endwhile
" restore cursor position
call setpos('.', savedPosition)
echo a:motion . " needed: " . countMotion
return countMotion
endfunction
w
), you may overshoot the mark (if your mark is inside a word). If more than one type of motion is possible, you may need to invent an artificial intelligence-based algorithm :)