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(direction)
""" Find current position
let s:nowpos = getpos(".")
""" Separate timeouts for vertical and horizontal navigation
let s:vtimeout = 0.8
let s:htimeout = 0.8
""" 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)) < s:vtimeout ) ) let s:vcontinuing = 1 else let s:vcontinuing = 0 endif if ( ($ZENONAVLASTTIME != "") && (abs(reltime()[0] - str2nr($ZENONAVLASTTIME)) < s:htimeout ) ) let s:hcontinuing = 1 else let s:hcontinuing = 0 endif """ Calculate future position if (a:direction < 2) " left or right """ whether left or irght, first zeno press takes you to the halfway """ point of the line, measured from the indent "let s:halfway = ( col("$") - col("0") + 0.001) / 2 if ( s:hcontinuing ) let s:diff = abs( str2float($ZENONAVLASTPOSITIONH) ) / 2 if (a:direction == 0) " left let s:nowpos[2] = float2nr( round( s:nowpos[2] - s:diff ) ) else " right let s:nowpos[2] = float2nr( round( s:nowpos[2] + s:diff ) ) endif else let s:indent = indent( line(".") ) let s:halfway = ( col("$") - s:indent + 0.001) / 2 let s:nowpos[2] = float2nr( round( s:indent + s:halfway ) ) let s:diff = s:halfway endif let $ZENONAVLASTPOSITIONH = printf( "%f", s:diff ) """ make up down scrolling normal if (len(s:nowpos) == 4) let s:nowpos = s:nowpos + [s:nowpos[2]] else let s:nowpos[4] = s:nowpos[2] endif else " up or down """ first up and donw zeno action is to the upper or bottom quater, """ since M already give syou the middle fo the screen if ( s:vcontinuing ) let s:diff = abs( str2float($ZENONAVLASTPOSITIONV) ) / 2 else let s:diff = ( line("w$") - line("w0") + 0.001) / 4 endif if (a:direction == 2) "up if ( s:vcontinuing ) let s:nowpos[1] = float2nr( ceil( s:nowpos[1] - s:diff ) ) else let s:nowpos[1] = float2nr( round( line("w0") + s:diff ) ) endif else "down if ( s:vcontinuing ) let s:nowpos[1] = float2nr( floor( s:nowpos[1] + s:diff ) ) else let s:nowpos[1] = float2nr( round( line("w0") + ( 3 * s:diff ) ) ) endif endif let $ZENONAVLASTPOSITIONV = printf( "%f", s:diff ) endif """ Change position and update state for next execution call setpos(".", s:nowpos) if ( s:diff <= 1) """ if the command has topped out and is freezing you, reset "let $ZENONAVLASTTIME = string( reltime()[0] + s:htimeout ) let $ZENONAVLASTTIME = reltime()[0] else let $ZENONAVLASTTIME = reltime()[0] endif endfunction """ crazy mappings with iterm2 in iterm, map to  (aka F13) and
""" proceed a few more times for the other codes. than map the F codes to zeno
""" Map to specially escaped left and right keys
:set =
:map [1;2P :call ZenosArrowKeys(0)
:set =
:map [1;2Q :call ZenosArrowKeys(3)
:set =
:map [1;2R :call ZenosArrowKeys(2)
:set =
:map [1;2S :call ZenosArrowKeys(1)