{"id":1720,"date":"2016-09-21T23:53:13","date_gmt":"2016-09-21T23:53:13","guid":{"rendered":"http:\/\/enfascination.com\/weblog\/?p=1720"},"modified":"2017-05-17T18:18:56","modified_gmt":"2017-05-17T18:18:56","slug":"zenos-arrow-keys-geometric-text-navigation-sequences-in-vim","status":"publish","type":"post","link":"https:\/\/enfascination.com\/weblog\/post\/1720","title":{"rendered":"Zeno&#8217;s Arrow Keys: Geometric text navigation sequences in vim"},"content":{"rendered":"<p>I wrote a little script for the world&#8217;s best text editor.  It solves a very simple problem whose smallness is counterbalanced by its commonness.  Here is some text:<\/p>\n<blockquote><p>I wrote a little script for the world&#8217;s best text editor.<\/p><\/blockquote>\n<p>I want to get to the word &#8220;text.&#8221;  In Vim you have a few options:<\/p>\n<ul>\n<li>type &#8216;l&#8217; over and over<\/li>\n<li>type <code>$<\/code> then &#8216;h&#8217; over and over.<\/li>\n<li><code>50l<\/code> and then adjust<\/li>\n<li><code>10w<\/code> if you can subitize that high<\/li>\n<li><code>ft;;;;;<\/code> but it would help a lot if you know how many &#8216;t&#8217;s there were without having to thinking about it<\/li>\n<li><code>\/text\\<Enter\\>\\<Esc\\><\/code> but that&#8217;s a lot for a little.<\/li>\n<\/ul>\n<p>What I really want is to just think about being there and I&#8217;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&#8217;m there.  So with the function I wrote, <code>ZenosArrowKeys()<\/code>, mapped to <code>C-l<\/code> for forward and <code>C-h<\/code> for back, I can go to the halfway mark with <code>C-l<\/code>, the 3\/4 mark with <code>C-ll<\/code>, the 1\/4 mark with <code>C-lh<\/code>, the 5\/8 with <code>C-llh<\/code>, and so on.  It&#8217;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&#8217;ve initiated.  The fractions are calculated relative to you current cursor position and the beginning or end of the line.  It&#8217;s my first attempt at Vimscript and I&#8217;m pretty happy with the result.<\/p>\n<p><small><small><small><code><\/p>\n<p>\"\"\" Zeno-style line navigation for vim.<br \/>\n\"\"\"    I want a navigation mode that let's me quickly get to certain<br \/>\n\"\"\"    points in the line.  Even though its up to five keystrokes, it<br \/>\n\"\"\"    probably will never feel like more than two, since your eye knows<br \/>\n\"\"\"    where you want to end up.  Control-left and right takes you half<br \/>\n\"\"\"    the distance it did previously, for two seconds.<br \/>\n\"\"\"    Seth Frey <moctodliamg@gmail.com><br \/>\n\"\"\"    Put this in .vimrc<br \/>\nfunction! ZenosArrowKeys(direction)<br \/>\n    \"\"\" Find current position<br \/>\n    let s:nowpos = getpos(\".\")<br \/>\n    \"\"\" Separate timeouts for vertical and horizontal navigation<br \/>\n    let s:vtimeout = 0.8<br \/>\n    let s:htimeout = 0.8<br \/>\n    \"\"\" Find previous position<br \/>\n    \"\"\"    This command has to have state because how far you navigate<br \/>\n    \"\"\"    depends on how far you just navigated.<br \/>\n    \"\"\"    The 5 below is the numer of seconds to wait before resetting<br \/>\n    \"\"\"    all the state.<br \/>\n    if ( ($ZENONAVLASTTIME != \"\") && (abs(reltime()[0] - str2nr($ZENONAVLASTTIME)) < s:vtimeout ) ) \n        let s:vcontinuing = 1\n    else\n        let s:vcontinuing = 0\n    endif\n    if ( ($ZENONAVLASTTIME != \"\") &#038;&#038; (abs(reltime()[0] - str2nr($ZENONAVLASTTIME)) < s:htimeout ) ) \n        let s:hcontinuing = 1\n    else\n        let s:hcontinuing = 0\n    endif\n    \"\"\" Calculate future position\n    if (a:direction < 2) \" left or right\n        \"\"\" whether left or irght, first zeno press takes you to the halfway\n        \"\"\" point of the line, measured from the indent\n        \"let s:halfway = ( col(\"$\") - col(\"0\") +  0.001) \/ 2\n        if ( s:hcontinuing )\n            let s:diff = abs( str2float($ZENONAVLASTPOSITIONH) ) \/ 2\n            if (a:direction == 0) \" left\n                let s:nowpos[2] = float2nr( round( s:nowpos[2] - s:diff ) )\n            else \" right\n                let s:nowpos[2] = float2nr( round( s:nowpos[2] + s:diff ) )\n            endif\n        else\n            let s:indent = indent( line(\".\") )\n            let s:halfway = ( col(\"$\") - s:indent +  0.001) \/ 2\n            let s:nowpos[2] = float2nr( round( s:indent + s:halfway ) )\n            let s:diff = s:halfway\n        endif\n        let $ZENONAVLASTPOSITIONH = printf( \"%f\", s:diff )\n        \"\"\" make up down scrolling normal\n        if (len(s:nowpos) == 4)\n            let s:nowpos = s:nowpos + [s:nowpos[2]]\n        else\n            let s:nowpos[4] = s:nowpos[2]\n        endif\n    else \" up or down\n        \"\"\" first up and donw zeno action is to the upper or bottom quater,\n        \"\"\" since M already give syou the middle fo the screen\n        if ( s:vcontinuing )\n            let s:diff = abs( str2float($ZENONAVLASTPOSITIONV) ) \/ 2\n        else\n            let s:diff = ( line(\"w$\") - line(\"w0\") +  0.001) \/ 4\n        endif\n        if (a:direction == 2) \"up\n            if ( s:vcontinuing )\n                let s:nowpos[1] = float2nr( ceil( s:nowpos[1] - s:diff ) )\n            else\n                let s:nowpos[1] = float2nr( round( line(\"w0\") + s:diff ) )\n            endif\n        else \"down\n            if ( s:vcontinuing )\n                let s:nowpos[1] = float2nr( floor( s:nowpos[1] + s:diff ) )\n            else\n                let s:nowpos[1] = float2nr( round( line(\"w0\") + ( 3 * s:diff ) ) )\n            endif\n        endif\n        let $ZENONAVLASTPOSITIONV = printf( \"%f\", s:diff )\n    endif\n    \"\"\" Change position and update state for next execution\n    call setpos(\".\", s:nowpos)\n    if ( s:diff <= 1)\n        \"\"\" if the command has topped out and is freezing you, reset \n        \"let $ZENONAVLASTTIME = string( reltime()[0] + s:htimeout )\n        let $ZENONAVLASTTIME = reltime()[0]\n    else\n        let $ZENONAVLASTTIME = reltime()[0]\n    endif\nendfunction\n\"\"\" crazy mappings with iterm2  in iterm, map <C-S-H> to \u001b[1;2P (aka F13) and<br \/>\n\"\"\"  proceed a few more times for the other codes.  than map the F codes to zeno<br \/>\n\"\"\"  Map to specially escaped left and right keys<br \/>\n:set <C-S-H>=\u001b[1;2P<br \/>\n:map <Esc>[1;2P :call ZenosArrowKeys(0)<Enter><br \/>\n:set <C-S-J>=\u001b[1;2Q<br \/>\n:map <Esc>[1;2Q :call ZenosArrowKeys(3)<Enter><br \/>\n:set <C-S-K>=\u001b[1;2R<br \/>\n:map <Esc>[1;2R :call ZenosArrowKeys(2)<Enter><br \/>\n:set <C-S-L>=\u001b[1;2S<br \/>\n:map <Esc>[1;2S :call ZenosArrowKeys(1)<Enter><\/p>\n<p><\/code><\/small><\/small><\/small><\/p>\n<!-- AddThis Advanced Settings generic via filter on the_content --><!-- AddThis Share Buttons generic via filter on the_content --><!-- AddThis Related Posts generic via filter on the_content -->","protected":false},"excerpt":{"rendered":"<p>I wrote a little script for the world&#8217;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&#8217;s best text editor. I want to get to the word &#8220;text.&#8221; In Vim you have a few options: type &hellip; <a href=\"https:\/\/enfascination.com\/weblog\/post\/1720\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Zeno&#8217;s Arrow Keys: Geometric text navigation sequences in vim<\/span><\/a><!-- AddThis Advanced Settings generic via filter on get_the_excerpt --><!-- AddThis Share Buttons generic via filter on get_the_excerpt --><!-- AddThis Related Posts generic via filter on get_the_excerpt --><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"wpupg_custom_link":[],"wpupg_custom_link_behaviour":[],"wpupg_custom_image":[],"wpupg_custom_image_id":[],"footnotes":""},"categories":[16],"tags":[],"_links":{"self":[{"href":"https:\/\/enfascination.com\/weblog\/wp-json\/wp\/v2\/posts\/1720"}],"collection":[{"href":"https:\/\/enfascination.com\/weblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/enfascination.com\/weblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/enfascination.com\/weblog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/enfascination.com\/weblog\/wp-json\/wp\/v2\/comments?post=1720"}],"version-history":[{"count":4,"href":"https:\/\/enfascination.com\/weblog\/wp-json\/wp\/v2\/posts\/1720\/revisions"}],"predecessor-version":[{"id":1792,"href":"https:\/\/enfascination.com\/weblog\/wp-json\/wp\/v2\/posts\/1720\/revisions\/1792"}],"wp:attachment":[{"href":"https:\/\/enfascination.com\/weblog\/wp-json\/wp\/v2\/media?parent=1720"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/enfascination.com\/weblog\/wp-json\/wp\/v2\/categories?post=1720"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/enfascination.com\/weblog\/wp-json\/wp\/v2\/tags?post=1720"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}