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 adjust10w
if you can subitize that highft;;;;;
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
""" 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
:set
:map
:set
:map
:set
:map