Zeno’s Arrow Keys: Geometric text navigation sequences in vim

I wrote a little script for the world’s best text editor. It solves a very simple problem whose smallness is counterbalanced by its commonness. Here is some text:

I wrote a little script for the world’s best text editor.

I want to get to the word “text.” In Vim you have a few options:

  • type ‘l’ over and over
  • type $ then ‘h’ over and over.
  • 50l and then adjust
  • 10w if you can subitize that high
  • ft;;;;; but it would help a lot if you know how many ‘t’s there were without having to thinking about it
  • /text\\ but that’s a lot for a little.

What I really want is to just think about being there and I’m there. Short of that, I want a command that just goes to the right part of the line. Short of that, I want to solve this problem they way Zeno would: get halfway, then go half of that, and half of that, until I’m there. So with the function I wrote, ZenosArrowKeys(), mapped to C-l for forward and C-h for back, I can go to the halfway mark with C-l, the 3/4 mark with C-ll, the 1/4 mark with C-lh, the 5/8 with C-llh, and so on. It’s a few strokes, but you can type them unconsciously because your eye knows where you want to end up so your brain can form a motor plan at stroke one. The halving resets 2 seconds after you’ve initiated. The fractions are calculated relative to you current cursor position and the beginning or end of the line. It’s my first attempt at Vimscript and I’m pretty happy with the result.

""" Zeno-style line navigation for vim.
""" I want a navigation mode that let's me quickly get to certain
""" points in the line. Even though its up to five keystrokes, it
""" probably will never feel like more than two, since your eye knows
""" where you want to end up. Control-left and right takes you half
""" the distance it did previously, for two seconds.
""" Seth Frey
""" Put this in .vimrc
function! ZenosArrowKeys(isForward)
""" Find current position
let s:nowpos = getpos(".")
let s:nowcol = s:nowpos[2]
""" Find previous position
""" This command has to have state because how far you navigate
""" depends on how far you just navigated.
""" The 5 below is the numer of seconds to wait before resetting
""" all the state.
if ( ($ZENONAVLASTTIME != "") && (abs(reltime()[0] - str2nr($ZENONAVLASTTIME)) < 2) ) let s:oldcol = str2nr($ZENONAVLASTPOSITION) else """ if no previous position, use beginning or end of line if (a:isForward) let s:oldcol = col("$") else let s:oldcol = 0 endif endif """ Calculate future position if (a:isForward) let s:nowpos[2] = s:nowcol + float2nr( round( abs( ( s:oldcol - s:nowcol ) / 2 ) + 0.01 ) ) else let s:nowpos[2] = s:nowcol - float2nr( round( abs( ( s:oldcol - s:nowcol ) / 2 ) + 0.01 ) ) endif """ Change position call setpos(".", s:nowpos) """ Update state for next execution let $ZENONAVLASTPOSITION = s:nowcol let $ZENONAVLASTTIME = reltime()[0] endfunction """ Map to left and right keys :noremap :call ZenosArrowKeys(1)
:noremap :call ZenosArrowKeys(0)