Compute the distance between two marks

I came across a question, while writing some vimscript. I wanted to know the "distance in motion" between two marks in the buffer.

The interesting point of this can be to know how many time I have to exectue a vim motion to go from one mark to the other in order to calculate the number of moves between the two marks.

How would you proceed?

• What kind of motions? For example, if motion is fixed (say `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 :) May 4, 2016 at 12:45
• Yeah, I guess I haven't thought about this case ^^, but I think you could move forward (or backward) until you reach the mark or overtake it. May 4, 2016 at 12:53
• The number of motions needed to jump to a mark is always exactly 1. May 4, 2016 at 13:24

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
``````