home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 16 / 16.iso / w / w055 / 4.ddi / SOURCES.LIF / VI.PEL < prev    next >
Encoding:
Text File  |  1990-09-27  |  63.8 KB  |  2,808 lines

  1. # $Header:   P:/source/ppee/macros/vi.pev   1.84   15 Jun 1990 17:50:32   JB  $
  2.  
  3. ##############################################################################
  4. #
  5. #           Sage Software - POLYTRON Division
  6. #             1700 NW 167th Place
  7. #               Beaverton, OR 97006
  8. #
  9. #   Copyright 1990, Sage Software, Inc.
  10. #
  11. #   Permission is hereby granted for licensed users of Sage Professional
  12. #   Editor and PolyAwk to copy and modify this source code for their own
  13. #   personal use.  These derivative works may be distributed only to other
  14. #   licensed Sage Professional Editor and PolyAwk users.  All other usage
  15. #   is prohibited without express written permission from Sage Software.
  16. #
  17. ##############################################################################
  18.  
  19. #### $Workfile:   vi.pel  $:  vi support keymaps and macros
  20.  
  21. #### VI emulation mode release notes:
  22. #
  23. #  EXTENSIONS:
  24. #
  25. #    This editor is capable of editing multiple files at once.  Thus it's 
  26. #    unnecessary to write the current file before switching to a new one, 
  27. #    nor is it necessary to process multiple files in strict sequential 
  28. #    order.  ":n" switches to next buffer without writing file; 
  29. #    ":p"switches to the prev buffer; and ":e file" loads a new file or 
  30. #    switches to an existing buffer by that name -- all without requiring 
  31. #    files to be written at each step.  The unnamed put buffer's contents 
  32. #    are not lost when buffer changes, thereby allowing cuts and pastes 
  33. #    between buffers without writing to intermediate files.
  34. #
  35. #    This implementation allows empty files and files with incomplete last
  36. #    lines to be edited.  This is in contrast with VI, which prohibits
  37. #    incomplete last lines and insists that there exist at least a single
  38. #    newline character in the file.
  39. #
  40. #    This implementation provides multiple windows.  Each window may view 
  41. #    different parts of the same file or different files.
  42. #
  43. #    The mouse can be used for all window operations, and for making text 
  44. #    selections.  Vi operators apply to the selected text, if any, in lieu 
  45. #    of a vi motion.  (Make a selection, then type "d" to delete it.)  The
  46. #    mouse allows column selections in addition to character- and line-
  47. #    oriented ones.
  48. #
  49. #    vi-style undo has been augmented by Sage's unlimited undo and redo.
  50. #    <Alt-U> undoes and <Alt-Y> redoes.  Also, vi's "U" command has been
  51. #    extended to toggle between the fully edited and undone versions of 
  52. #    the current line, like "u" does for a single edit.
  53. #
  54. #    This implementation always portrays a 100% accurate representation 
  55. #    of the text in the buffer.  Standard vi employed "@" and "$" symbols
  56. #    to indicate where text had been or would be deleted while minimizing 
  57. #    screen updates.  This kludge is unnecessary and unsupported in this 
  58. #    implementation.
  59. #
  60. #    Similarly, this editor is capable of scrolling horizontally, so long
  61. #    lines are not wrapped.  The redraw commands are not needed, so
  62. #    <Ctrl-L> and <Ctrl-R> scroll left and right, respectively.
  63. #    
  64. #    Search commands have additional history associated with them.
  65. #    Previous search patterns entered are accessable via vertical arrow
  66. #    keys.  The default is the most recently entered search pattern, same
  67. #    as normal vi.  One subtle difference is that the previous pattern is
  68. #    visible on the command line.  Patterns histories may be accepted by 
  69. #    pressing enter, or rejected by pressing escape.  They also may be
  70. #    edited with the DEL, backspace, and horizontal arrow keys.  Newly
  71. #    entered text overwrites the pattern unless an editing character has
  72. #    been pressed first.  Thus "/<Enter>" and "/newpattern<Enter>" work 
  73. #    exactly as before, although additional functionality is available as
  74. #    a superset of previous key sequences.
  75. #
  76. #    Rub-Out is a normal ASCII character, and does not cancel an insert.
  77. #    ^C terminates an insert.  Ctrl-Break resets the editor.
  78. #
  79. #    ^V is only needed to quote keys that have a special meaning in insert 
  80. #    mode (  ^J  ^M  ^[  ^@  ^C  ^D  ^H  ^W  ^U  ^V  ).  Other control 
  81. #    keys will self-insert, though they still can be quoted with ^V.
  82. #
  83. #    The various forms of ^D are not required in insert mode.  
  84. #    Backspace unindents, and tab indents.
  85. #
  86. #    The Ctrl-@ command in insert mode works for any previous insertion,
  87. #    and is not limited to 128 chars.  If typed as NOT the first character,
  88. #    it inserts the previous insertion without terminating insert mode.
  89. #
  90. #    Most of the native mode function- and Alt-keys are implemented for 
  91. #    convenience.  Thus <Alt-W><Alt-Q> works like :wq
  92. #
  93. #    Other special features, such as error-fix, word-processing, and "Code 
  94. #    Processing" support are available in VI mode via "<F10>command", and
  95. #    other native key bindings.
  96. #
  97. #    Code Processing template inserting is bound to the <F2> key during
  98. #    insert mode.
  99. #
  100. #    A new command is added: "g" goes to top of buffer if no address is 
  101. #    specified, otherwise goes to the specified line, like "G".
  102. #    
  103. #
  104. #  BUGS/OMISSIONS FIXED IN RELEASE 1.1:
  105. #
  106. #    Numerous cursor positioning errors: $x   $~   $bcw   rx
  107. #
  108. #    again (.) fixed for broken commands: rx  sxx$  
  109. #
  110. #    repeat counts, eg.: 2dd.3.
  111. #
  112. #    cw and cW now work correctly (differntly from dw and dW).
  113. #
  114. #    correct cursor positioning after  nS  cc  dd  yyP  and  yyp
  115. #
  116. #
  117. #  Known limitations of this emulation:
  118. #
  119. #    Should allow cascaded searches: "/abc/;/def/"
  120. #
  121. #    Positioning past EOL and EOF are sometimes off by one:
  122. #
  123. #        "A^["   "Gj"
  124. #
  125. #    Sentence motions should stop at paragraph and section breaks.
  126. #
  127. #    The ":l" command and "l" suffix to ":g" and ":s" should work
  128. #    differently from "p"
  129. #
  130. #
  131. #  TODO LIST (to be implemented next):
  132. #
  133. #    should use VI's regex syntax instead of our own
  134. #
  135. #    Implement common  :set  commands.
  136. #
  137. #    ":e %"  and  ":e #"   and  Ctrl-^ == ":e #"
  138. #
  139. #    Precise modeling of VI's file/buffer status vs. :w :r :e :q commands;
  140. #    appropriately issue "[new file]", "[Read only]" and other standard
  141. #    messages for the following circumstances:
  142. #
  143. #        buffer is modified
  144. #        buffers are modified
  145. #        new name; first write to existing file
  146. #        partial write to existing file
  147. #        read-only file/buffer
  148. #
  149. #    semi-colons ";" separating ":" commands
  150. #
  151. #    :map and :map! commands
  152. #
  153. #    :abb command
  154. #
  155. #
  156. #    ":m" -- block move command
  157. #
  158. #    ":s" and ":g" commands should accept multi-line input
  159. #
  160. #    ":g//i..." and ":g//a..." need to be implemented
  161. #
  162. #    ":G" and ":V" commands ( :g or :v with prompt ).
  163. #
  164. #    :n filename... command (use :p and :e instead)
  165. #
  166. #    should prohibit motion past EOL of last line
  167. #
  168. #    should prohibit having 0 lines in buffer
  169. #
  170. #    Ctags commands: ^T => pop, ^] => tag; :ta file; :pop
  171. #
  172. #    ^H/BS should be able to cancel ":", "/", and "?" command prompts
  173. #
  174. #    :r !command        :w !command
  175. #
  176. #    :w >>file
  177. #
  178. #
  179. #  OMISSIONS (likely never to be implemented):
  180. #
  181. #    Several vi commands take a count to adjust the window size.  Presently
  182. #    all such counts are ignored, and, given the more robust windowing
  183. #    capabilities, they are unlikely ever to be implemented. In some 
  184. #    cases, like searches, the counts may be changed to repeat counts.
  185. #    Commands I know about (purportedly all "large motion" commands)...
  186. #    
  187. #        "z<num>." (change window size to <num>)
  188. #
  189. #        ^D  ^U  ^F  ^B  /  ?  [[  ]]  `  and '  
  190. #
  191. #        maybe:  ^E  ^Y   n   N
  192. #
  193. #    # -- macro character -- substitute for FN keys on keyboards w/out
  194. #
  195. #    @ macro commands
  196. #
  197. #    & command -- "synonym for :& by analogy with ex &" (??)
  198. #
  199. #    nO -- opens n lines
  200. #
  201. #    lisp mode commands: "(", ")", "{", "}", "="
  202. #
  203. ####
  204.  
  205. ### local variables
  206.  
  207. ## vi keymaps:
  208.  
  209. local vi_command_keymap = -1
  210. local vi_insert_keymap = -1
  211.  
  212.  
  213. ## vi constants:
  214.  
  215. local DEFAULT_BID = "1"        # default buffer id if not explicitly spec'd
  216. local LINE_BUFFER = 0x80000    # user buffer flag for line mode buffers
  217. local AND_DELETE = 1        # optional arg to vi_yank()
  218. local DEFAULT_ONE = 1        # optional arg to ex_ and select_range()
  219. #local DONT_DELETE = 0        # optional arg to vi_yank()
  220. local SEARCH_HIST  = "SEARCH"    # history index for "/" and "?" commands
  221. local NEWLINE_BEFORE = 1    # insert flag -- put newline before or
  222. local NEWLINE_AFTER = 2        #     -- after  current line
  223.  
  224. local ALT_KEY = 0x08         # alt key flag
  225. local CTRL_AT = 768        # <Ctrl-@> keycode
  226. local ENTER_KEY = 7181
  227.  
  228. #local vi_exact_mark = "`"    # syntax for exact mark motion
  229. local vi_line_mark = "'"    # syntax for line mark motion
  230.     # block of reserved marks (ugh!!)
  231. local vi_major_mark = 909    # place from which last major motion moved
  232. local vi_base_mark = 910    # named marks --  first is "''" / "``"
  233.  
  234. local DONT_MARK = 2        # op_type_2b() arg to suppress mark_context()
  235. local MAJOR_MARK = 1        # op_type_2b() arg mark major motion
  236.  
  237.  
  238. ## vi emulation internal states:
  239.  
  240. local pending_operator        # name of pending operator, if any
  241.  
  242. local number_register = 0    # number register used to repeat commands
  243.  
  244. local vi_read_name        # file name in last read command
  245. local in_glob            # executing a :g or :v command
  246. local browser_bid = 0        # buffer containing ":" command's "printed" output
  247.  
  248. local buf_id            # selected get/put buffer
  249. local buf_id_map        # maps buf_id names to buffer handles
  250.  
  251. local vi_search_dir         # previous search direction for search_again
  252. local vi_search_flags        # vi search flags
  253.  
  254. local vi_find_com        # last find command, if any
  255. local vi_find_char        # last find character, if any
  256.  
  257.     # address ranges for ":" commands:
  258.  
  259. local ex_addr1            # first address if any
  260. local ex_addr2            # second address if any
  261. local ex_addresses = 0        # count of addresses supplied
  262. local ex_com            # command string being parsed
  263.  
  264. local line_search         # signal from ex_address() to vi_search_key()
  265.  
  266.     # state vars for vi-style undo and "."/again:
  267.  
  268. local undo_baseline        # undo indicies to undo previous command
  269. local undo_line_baseline        # undo indicies to undo all changes to...
  270. local undo_line            # ...the current line (remembered here)
  271.  
  272. local again_string        # playback string to re-execute last command
  273. local tmp_again            # interim version of again_string
  274. local playing_again = 0        # flag for insert, et al.
  275.  
  276. local prev_number_register = 1    # previous repeat count
  277. local prev_buf_id        # previous buf id
  278. local prev_mark_id        # previous mark id
  279. local vi_r_ch            # previous "r" command replacement string
  280.  
  281.  
  282. ## vi mode user-visible external states, flags and modes
  283.  
  284. local vi_ai_mode = 1        # vi auto indent mode
  285.  
  286.  
  287. ### vi emulation
  288. #
  289.  
  290. ## vi -- enter vi mode
  291. #
  292. function vi(){                            #PUBLIC #VOID
  293.  
  294.     emulation_mode = "vi"
  295.  
  296.     # Misc. items...
  297.  
  298.     visible_end_buffer = default_visible_end_buffer = ""
  299.     visible_virtual_lines = default_visible_virtual_lines = "~"
  300.     color_errors = color_warnings = color_text
  301.  
  302.     # default_page_overlap = 
  303.         window_page_overlap = 2
  304.  
  305.     vi_search_flags = search_flags             \
  306.         = SEARCH_REGEX                \
  307.         + SEARCH_MAXIMAL_MATCH            \
  308.         + SEARCH_WRAPS                \
  309.         + SEARCH_ADVANCE            \
  310.     #    + SEARCH_IGNORE_CASE
  311.  
  312.     buffer_flags = default_buffer_flags         \
  313.         = or( default_buffer_flags,         \
  314.                 BUFFER_REAL_SPACE_ONLY    \
  315.             +    BUFFER_WHOLE_LINES     )
  316.         
  317.     toggle_dialog( 1 )
  318.     toggle_electric( 1 )
  319.  
  320.     create_vi_keymaps()
  321.     vi_command_mode()        # vi starts in command mode
  322. }
  323.  
  324.     
  325. ## entering vi command mode
  326. #
  327. #    This gets called at the conclusion of insert mode, after all errors, 
  328. #    after all "operators", and after each "ex" command.
  329. #
  330. local function vi_command_mode(){
  331.  
  332.     # reset to normal state
  333.  
  334.     current_keymap = vi_command_keymap
  335.     number_register = 0     # calling use_number() overwrites prev num
  336.     buf_id = DEFAULT_BID
  337.     in_glob = 0
  338.     op_flush()
  339.  
  340.     # if( current_line_length > 0         \
  341.     # && current_line_length <= current_line_offset )
  342.     #     prev_char()
  343. }
  344.  
  345. ## indicate a common error
  346.  
  347. function vi_beep( s ){
  348.     beep()
  349.     pending_operator = ""    # cancel any pending operator
  350.     vi_command_mode()    # reset to command mode
  351.     error( s )        # cause longjump to editor top-level
  352.                 #     ( "never" returns )
  353. }
  354.  
  355. function establish_baseline(){
  356.     undo_baseline[ current_buffer ] = undo_index()
  357.  
  358.     # message( "baseline: " undo_index())
  359.     if( undo_line[ current_buffer ] != current_line ){
  360.         undo_line[ current_buffer ] = current_line
  361.         undo_line_baseline[ current_buffer ] = undo_index()
  362.     }
  363. }
  364.  
  365. function vi_undo_redo( baseline ){
  366.     local new, old
  367.  
  368.     # message( "undo/redo: " undo_index())
  369.     if( current_buffer in baseline ){
  370.         new = baseline[ current_buffer ]
  371.         baseline[ current_buffer ] = old = undo_index()
  372.     
  373.         if( new < old )
  374.             undo( new )
  375.         else
  376.             redo( new )
  377.     } else {
  378.         vi_beep()
  379.     }
  380. }
  381.  
  382. function vi_undo(){
  383.     vi_undo_redo( undo_baseline )
  384. }
  385.  
  386. function vi_undo_line(){
  387.     if( undo_line[ current_buffer ] == current_line )
  388.         vi_undo_redo( undo_line_baseline )
  389.     else
  390.         vi_beep()
  391. }
  392.  
  393. function vi_again(){
  394.     local i, ch
  395.  
  396.     if( !again_string )
  397.         vi_beep()
  398.  
  399.     if( prev_buf_id ~ /[1-8]/ )
  400.         prev_buf_id = chr( ord( prev_buf_id ) + 1 )
  401.     buf_id = prev_buf_id
  402.  
  403.     if( !number_register )
  404.         number_register = prev_number_register 
  405.  
  406.     playing_again = 1
  407.     playback( again_string )
  408.     playing_again = 0
  409. }
  410.  
  411.  
  412. # setup for a single-char command, like "Y" or "~".
  413. #
  414. #    Generate an error if there is a pending operator.
  415. #    Record the appropriate information for undo and again.
  416. #     Special case "single-char" commands like "[[" and "]]" must
  417. #        manually insert an extra record_op()
  418.  
  419. local function op_type_1(){
  420.     operator_disallowed()
  421.     prev_number_register = number_register 
  422.     establish_baseline()
  423.     record_op()
  424.     op_wrap()
  425. }
  426.  
  427. # setup for the first of a two-char operator command, like "dW" or "yy":
  428. #
  429. #    Record the appropriate information for undo and again.
  430.  
  431. local function op_type_2a(){
  432.     prev_number_register = number_register 
  433.     record_op()
  434.     establish_baseline()
  435. }
  436.  
  437. # setup for a motion that might be paired with a two-char operator.
  438. #    like "!}" or ">w" (may or may not be paired with an operator):
  439. #
  440. #    Record the appropriate information for undo and again.
  441.  
  442. local function op_type_2b( context_type ){
  443.     if( pending_operator )
  444.         prev_number_register = number_register 
  445.  
  446.     if( tmp_again )
  447.         record_op()
  448.  
  449.     mark_context( context_type )
  450. }
  451.  
  452. # wrap-up for the successful completion of any command
  453.  
  454. local function op_wrap(){
  455.     if( tmp_again ){
  456.         prev_buf_id = buf_id
  457.         again_string = tmp_again
  458.         tmp_again = ""
  459.     }
  460. }
  461.  
  462. # cancel command history accumulated thus far
  463.  
  464. local function op_flush(){
  465.     tmp_again = ""
  466. }
  467.  
  468. # record a key into the again string
  469.  
  470. local function record_op( key ){
  471.     if( !argcount())
  472.         key = current_key
  473.  
  474.     if( key == 0 )
  475.         key = CTRL_AT
  476.  
  477.     tmp_again = tmp_again   chr( key )   chr( shiftr( key, 8 ))
  478. }
  479.  
  480.  
  481. ## a numeric count may preceed most commands.
  482. #
  483. #    leading zeros are illegal ("0" is a command, executed here).
  484. #     no count means 1.
  485. #    the count is reset after every command, whether the count is used or not.
  486. #    a copy of the previous count is maintained for vi_again
  487.  
  488. function vi_digit(){
  489.     local key = and( current_key, 0xF )
  490.  
  491.     if( number_register )
  492.         number_register = number_register * 10 + key
  493.     else
  494.         if( key )
  495.             number_register = key
  496.         else
  497.             vi_goto_bol()
  498. }
  499.  
  500. local function use_number(){
  501.     local prev = number_register
  502.  
  503.     if( prev == 0 )
  504.         prev = 1
  505.  
  506.     number_register = 0
  507.  
  508.     return prev
  509. }
  510.  
  511. local function disallow_number(){
  512.     if( number_register != 0 )
  513.         warning( "count ignored with this command" )
  514.     number_register = 0
  515. }
  516.  
  517. local function vi_getc(){
  518.     local ch = getkey()
  519.  
  520.     if( and( ch, 255 ))
  521.         return chr( ch )
  522.     vi_beep()
  523. }
  524.  
  525.  
  526.  
  527. ### vi operators
  528.  
  529. # process operator command
  530. #
  531. #    if first one and no selection then remember it pending next motion
  532. #    if first one and existing selection, execute operator
  533. #    if second one matches previous one, execute operator in line mode
  534. #    else error
  535.  
  536. function vi_operator(){
  537.     local ch = chr( and( current_key, 255 ))    # get operator
  538.  
  539.     op_type_2a()
  540.  
  541.     if( pending_operator ){
  542.         if( ch == pending_operator ){
  543.             select_lines( use_number())
  544.             xeq_op()        # operate on lines
  545.         } else 
  546.             vi_beep()        # operator mismatch
  547.     } else if( region_type()){
  548.         pending_operator = ch        # operate on selection
  549.         xeq_op()
  550.                     # !! can't do again
  551.     } else
  552.         pending_operator = ch        # remember for later
  553. }
  554.  
  555. local function operator_disallowed(){
  556.     tmp_again = ""            # avoids problem w/vi_space, etc
  557.                     #    followed by "."
  558.     if( pending_operator )
  559.         vi_beep()        # never returns
  560. }
  561.  
  562. # manually insert an operator
  563. #
  564. #    usage: "pend_operator( op ); motion()"
  565. #
  566. local function pend_operator( op ){        
  567.     operator_disallowed()
  568.     pending_operator = op
  569. }
  570.  
  571. # process pending operator, if any
  572. #
  573. #    mode specifies the mode of the operation: line, normal, or inclusive
  574. #    if a region has already been selected, mode is ignored
  575.  
  576. local function xeq_op( mode ){
  577.     local rt
  578.     local line
  579.  
  580.     if( pending_operator ){
  581.  
  582.         if( !argcount())
  583.             mode = NORMAL_SELECTION
  584.     
  585.         op_wrap()
  586.     
  587.         if(( rt = region_type()))
  588.             mode = rt
  589.         else {
  590.             drop_anchor( mode )
  591.             goto_mark( vi_base_mark )
  592.         }
  593.     
  594.         if( pending_operator == "y" ){
  595.             vi_yank()
  596.             raise_anchor()
  597.     
  598.         } else if( pending_operator == "d" ){
  599.             vi_yank( AND_DELETE )
  600.             if( mode == LINE_SELECTION )
  601.                 skip_whitespace()
  602.  
  603.         } else if( pending_operator == "c" ){
  604.             vi_yank( AND_DELETE )
  605.             vi_insert(( mode == LINE_SELECTION )    \
  606.                 ? NEWLINE_BEFORE         \
  607.                 : 0 )
  608.  
  609.         } else if( pending_operator == "<" ){
  610.             outdent_tabs()
  611.             raise_anchor()
  612.     
  613.         } else if( pending_operator == ">" ){
  614.             indent_tabs()
  615.             raise_anchor()
  616.     
  617.         } else if( pending_operator == "!" ){
  618.             vi_filter()
  619.             raise_anchor()
  620.     
  621.         } else 
  622.             vi_beep( "bad VI operator: " pending_operator )
  623.     
  624.         pending_operator = ""
  625.  
  626.     } else
  627.         op_flush()
  628.  
  629.     vi_command_mode()
  630. }
  631.  
  632.  
  633.  
  634. ### yank/put buffers & associated stuff
  635.  
  636.  
  637. # select n lines, starting with the current
  638.  
  639. local function select_lines( n ){
  640.     local target = current_line + n - 1
  641.  
  642.     create_mark( TEMP_MARK )
  643.     if( n > 1 ){
  644.         current_line = target
  645.     
  646.         if( current_line != target ){
  647.             goto_mark( TEMP_MARK )
  648.             vi_beep() ## "too few lines remain in buffer" )
  649.         }
  650.     }
  651.  
  652.     drop_anchor( LINE_SELECTION )
  653.     goto_mark( TEMP_MARK )
  654. }
  655.  
  656. local function move_down( n ){
  657.     local target = current_line + n
  658.  
  659.     create_mark( TEMP_MARK )
  660.     current_line = target
  661.     if( current_line != target ){
  662.         goto_mark( TEMP_MARK )
  663.         vi_beep()    ## "too few lines remain in buffer" )
  664.     }
  665. }
  666.  
  667. # select n characters, n>0 => select towards EOL; n<0 => select towards BOL
  668. #    error if selection bypasses BOL or EOL
  669. #    n=0 => error
  670.  
  671.  
  672. local function select_chars( n ){
  673.     if( n > 0 && current_line_length - current_line_offset >= n ){
  674.         drop_anchor( NORMAL_SELECTION )
  675.         next_char( n )
  676.     } else if( n < 0 && current_line_offset >= -n ){
  677.         drop_anchor( NORMAL_SELECTION )
  678.         prev_char( -n )
  679.     } else
  680.         vi_beep()
  681. }
  682.  
  683. # prompt for buffer id
  684.  
  685. function vi_bufid_key(){
  686.     local ch = vi_getc()
  687.  
  688.     if( ch ~ /[a-zA-Z1-9]/ )  # buffer id "@" is also used internally
  689.         buf_id = ch
  690.     else
  691.         vi_beep()
  692. }
  693.  
  694. # returns a buffer id of a system buffer associated with the current buf_id
  695. #    if clear is true, the buffer returned is empty.
  696. #
  697. local function yank_bid( put, del ){
  698.     local bid = tolower( buf_id )
  699.     local buf, i, b2, b1
  700.  
  701.     if( bid in buf_id_map ){     # we're reusing an existing buffer:
  702.         if( bid == DEFAULT_BID && del ){
  703.             # save 9 most recent deletes:
  704.             if( "9" in buf_id_map )
  705.                 delete_buffer( buf_id_map[ "9" ])
  706.             for( i = 9; i > 1; i-- ){
  707.                 b2 = "" i
  708.                 b1 = "" i - 1
  709.                 if( b1 in buf_id_map )
  710.                     buf_id_map[ b2 ] = buf_id_map[ b1 ]
  711.             }
  712.             # and fall to the return to create the new one
  713.         } else {
  714.             if( put || isupper( buf_id ))     # upper=> append          # !del || 
  715.                 return buf_id_map[ bid ]
  716.             else
  717.                 # we're reusing a bid and need it to be empty
  718.                 delete_buffer( buf_id_map[ bid ])
  719.                 # and we fall through to create an empty one
  720.         }
  721.  
  722.     } else if( put )
  723.         vi_beep( "Nothing has been put in buffer \"" buf_id )
  724.  
  725.     return ( buf_id_map[ bid ] = create_buffer( "vi_yank_" bid, "", BUFFER_SYSTEM ))
  726. }
  727.  
  728. # yank or delete the current selection into the current "put" buffer
  729. #
  730. #     this is the yank command, and also used by other commands.
  731.  
  732. function vi_yank( del ){
  733.     local prev = current_buffer
  734.     local line = ( region_type() == LINE_SELECTION )
  735.  
  736.     if( del )
  737.         delete_to_scrap()
  738.     else {
  739.         copy_to_scrap()
  740.         raise_anchor()
  741.     }
  742.     
  743.     current_buffer = yank_bid( 0, del )
  744.  
  745.     if( line )
  746.         buffer_flags = or( buffer_flags, LINE_BUFFER )
  747.     else
  748.         buffer_flags = and( buffer_flags, not( LINE_BUFFER ))
  749.  
  750.     insert_scrap()
  751.     current_buffer = prev
  752.  
  753.     return line
  754. }
  755.  
  756. # put the current yank buffer's contents into the scrap buffer,
  757. #    and return true if the buffer was a "LINE" buffer.
  758. #
  759. #    Most clients need to perform some fine positioning
  760. #    depending on the line status, before actually inserting the text.
  761. #    Hence the obscure nature of this function.
  762. #
  763. local function put_buffer_to_scrap(){
  764.     local prev = current_buffer
  765.     local line_oriented
  766.  
  767.     current_buffer = yank_bid( 1, 0 )
  768.  
  769.     goto_buffer_top()
  770.     drop_anchor()
  771.     goto_buffer_bottom()
  772.     copy_to_scrap()
  773.     raise_anchor()
  774.  
  775.     line_oriented =  and( buffer_flags, LINE_BUFFER )
  776.     current_buffer = prev
  777.  
  778.     return line_oriented
  779. }
  780.  
  781. #  local function insert_put_buffer( id ){
  782. #    buf_id = id
  783. #    put_buffer_to_scrap()
  784. #    insert_scrap()
  785. #  }
  786.  
  787. function vi_Y(){                # yank lines
  788.     op_type_1()
  789.     select_lines( use_number())
  790.     vi_yank()
  791. }
  792.  
  793. function vi_p(){                # put after cursor/line
  794.     local lnum, lines
  795.  
  796.     op_type_1()
  797.     if(( lines = put_buffer_to_scrap())){
  798.         down()
  799.         goto_bol()
  800.         lnum = current_line
  801.     } else 
  802.         right()
  803.  
  804.      insert_scrap()
  805.  
  806.     if( lines )
  807.         vi_goto_line( lnum )
  808.     else
  809.         prev_char()
  810. }
  811.  
  812. function vi_P(){                # put before cursor/line
  813.     local lnum, lines
  814.  
  815.     op_type_1()
  816.     if(( lines = put_buffer_to_scrap())){
  817.         goto_bol()
  818.         lnum = current_line
  819.     }
  820.  
  821.       insert_scrap()
  822.  
  823.     if( lines )
  824.         vi_goto_line( lnum )
  825.     else
  826.         prev_char()
  827. }
  828.  
  829.  
  830. ### insert mode and auxiliary operations
  831.  
  832. local insert_index
  833. local insert_offset
  834. local insert_text
  835.  
  836. local function vi_insert( newline ){
  837.     local n, count, col, start_line, line, offset
  838.  
  839.     count = use_number()
  840.  
  841.     if( playing_again ){
  842.         if( and( buffer_flags, BUFFER_OVERTYPE_MODE ))
  843.             delete_chars( length( insert_text ))
  844.  
  845.         reinsert_string( insert_text )
  846.  
  847.     } else {
  848.  
  849.         # insert requested newlines
  850.  
  851.         if( newline ){
  852.             if( newline == NEWLINE_BEFORE )
  853.                 vi_ai_b4()
  854.             else
  855.                 vi_ai_nl()
  856.             insert_text = "\n"
  857.         } else
  858.             insert_text = ""
  859.  
  860.         # remember starting position:
  861.     
  862.         insert_index = undo_index()
  863.         start_line = current_line
  864.         insert_offset = buffer_offset
  865.  
  866.         current_keymap = vi_insert_keymap 
  867.     
  868.         message( "[ insert mode ]" )
  869.         process_begin()            # interpret insert keymap
  870.         message( "" )
  871.     
  872.         # copy inserted text to insert_text
  873.     
  874.         if( current_line == start_line ){
  875.             insert_text = insert_text     \
  876.                 read_buffer( insert_offset - buffer_offset )
  877.         } else {
  878.             line = current_line - 1
  879.             offset = buffer_offset
  880.             goto_buffer_offset( insert_offset )
  881.  
  882.             insert_text = insert_text read_buffer()
  883.             current_column = 0
  884.  
  885.             while( current_line++ < line )
  886.                 insert_text = insert_text "\n" read_buffer()
  887.  
  888.             goto_buffer_offset( offset )
  889.             insert_text = insert_text "\n" read_buffer( -current_line_offset )
  890.         }
  891.     }
  892.  
  893.     # process repeat count, if any
  894.  
  895.     while( count-- > 1 )
  896.         reinsert_string( insert_text )
  897.  
  898.     if( current_column > 1 )
  899.         prev_char()
  900.  
  901.     vi_command_mode()
  902. }
  903.  
  904.  
  905. # properly auto-indent reinserted text
  906. #    (if auto-indent is disabled, this is the same as insert_string())
  907.  
  908. local function reinsert_string( text ){
  909.     local i, line, s, n
  910.     
  911.     n = split( text, line, "\n" )
  912.     
  913.     for( i = 1; i<= n; i++ ){
  914.         s = line[ i ]
  915.         
  916.         if( i > 1 )
  917.             sub( /^[ \t]+/, "", s )
  918.             
  919.         insert_string( s )
  920.         
  921.         if( i < n )
  922.             vi_ai_cr()
  923.     }
  924. }
  925.  
  926.  
  927. # compute the buffer offset such that it is either
  928. #
  929. #    a. the starting point of the insert (insert_offset), or
  930. #    b. the BOL of the current line (buffer_offset - current_line_offset)
  931. #
  932. # -- whichever is closer to buffer_offset
  933. #
  934. local function insert_backstop(){
  935.     local offset = buffer_offset - current_line_offset
  936.  
  937.     return offset < insert_offset ? insert_offset : offset
  938. }
  939.  
  940.  
  941. function vi_insert_bs(){
  942.     if( insert_backstop() < buffer_offset )
  943.         backspace()
  944.     else
  945.         beep()
  946. }
  947.  
  948. # "dB", limited by scope of current indent
  949.  
  950. function vi_insert_dB(){
  951.     local offset = insert_backstop()
  952.     local sflags = SEARCH_REGEX + SEARCH_MAXIMAL_MATCH     \
  953.             + SEARCH_ADVANCE + SEARCH_BACKWARD
  954.  
  955.     if( buffer_offset <= offset )
  956.         beep()
  957.     else {
  958.         drop_anchor( NORMAL_SELECTION )
  959.  
  960.         if( search( WORD_patt, sflags )){
  961.             if( buffer_offset < offset )
  962.                 goto_buffer_offset( offset )
  963.             delete_chars()
  964.         }
  965.     }
  966. }
  967.  
  968. function vi_insert_cancel(){
  969.     undo( insert_index )
  970. }
  971.  
  972.  
  973. function vi_insert_quoted(){
  974.     local ch
  975.  
  976.     message( "type ascii key to be inserted" )
  977.  
  978.     ch = getkey()
  979.  
  980.     if( and( ch, 255 ) || ch == CTRL_AT ){
  981.         insert_key( ch )
  982.         message( "" )
  983.     } else
  984.         warning( "Can't insert non-ascii keys" )
  985.             # error() would screw-up insert mode
  986. }
  987.  
  988. function vi_insert_unindent(){
  989.     local ch = read_buffer( -1 )
  990.  
  991.     if( ch == "^" || ch == "0" ){    # no way for ^^D to save for next
  992.         goto_bol()
  993.         drop_anchor( NORMAL_SELECTION )
  994.         goto_eol()
  995.         delete_chars()
  996.  
  997.     } else if( ch == "" || ch == "\t" || ch == " " ){
  998.         vi_insert_bs()
  999.     } else
  1000.         beep()
  1001. }
  1002.  
  1003. function vi_reinsert(){
  1004.     local flag = ( insert_backstop() < buffer_offset )
  1005.  
  1006.     reinsert_string( insert_text )
  1007.  
  1008.     if( !flag )
  1009.         process_end()
  1010.  
  1011.     # a more accurate implementation would beep if !flag
  1012.     # and always process_end() the insert; this is an extension
  1013. }
  1014.  
  1015.  
  1016. ## vi auto-indent mode:
  1017. #    
  1018. #    vi ai mode is different from electric (a) because normal ai is
  1019. #    broken in non-virtual space mode, and (b) because vi auto-indent
  1020. #    works differently.
  1021.  
  1022. function vi_ai_b4(){
  1023.     if( current_line == 1 ){ 
  1024.          goto_bol()
  1025.         insert_newline()
  1026.         up()
  1027.     } else {
  1028.         up()
  1029.         vi_ai_nl()
  1030.     }
  1031. }
  1032.  
  1033. function vi_ai_nl(){
  1034.     goto_eol()
  1035.     vi_ai_cr()
  1036. }
  1037.  
  1038.  
  1039. function vi_ai_cr( repeat ){
  1040.  
  1041.     insert_string( "\n" )
  1042.  
  1043.     if( vi_ai_mode ){
  1044.                prev_line()
  1045.         drop_anchor()
  1046.         goto_bol()
  1047.         copy_to_scrap()
  1048.         raise_anchor()
  1049.         down()
  1050.         insert_scrap()
  1051.     }
  1052. }
  1053.  
  1054.  
  1055. ### insertion commands
  1056.  
  1057. function vi_i(){
  1058.     op_type_1()
  1059.  
  1060.     vi_insert()
  1061. }
  1062.  
  1063. function vi_I(){
  1064.     op_type_1()
  1065.  
  1066.     skip_whitespace()
  1067.     vi_insert()
  1068. }
  1069.  
  1070.  
  1071. function vi_a(){
  1072.     op_type_1()
  1073.  
  1074.     if( current_line_length > current_line_offset )
  1075.         right()
  1076.     vi_insert()
  1077. }
  1078.  
  1079. function vi_A(){
  1080.     op_type_1()
  1081.  
  1082.     goto_eol()
  1083.     vi_insert()
  1084. }
  1085.  
  1086.  
  1087. function vi_o(){
  1088.     op_type_1()
  1089.  
  1090.     vi_insert( NEWLINE_AFTER )
  1091. }
  1092.  
  1093. function vi_O(){
  1094.     op_type_1()
  1095.  
  1096.     vi_insert( NEWLINE_BEFORE )
  1097. }
  1098.  
  1099. ### deletions
  1100. #
  1101. #    all deletions delete_to_scrap() even if single character
  1102.  
  1103.  
  1104. function vi_X(){
  1105.     op_type_1()
  1106.  
  1107.     select_chars( -use_number())
  1108.     vi_yank( AND_DELETE )
  1109. }
  1110.  
  1111. function vi_x(){
  1112.     op_type_1()
  1113.  
  1114.     select_chars( use_number())
  1115.     vi_yank( AND_DELETE )
  1116.  
  1117.     if( current_line_length <= current_line_offset        \
  1118.     && current_line_offset )
  1119.         prev_char()
  1120. }
  1121.  
  1122. ## more complex edits -- combinations of delete/insert
  1123.  
  1124. function vi_J(){
  1125.     local n = use_number()
  1126.     local ins
  1127.  
  1128.     op_type_1()
  1129.  
  1130.     if( n > 1 )
  1131.         n -= 1
  1132.  
  1133.     while( n-- > 0 ){
  1134.         goto_eol()    
  1135.         while( read_buffer( -1 ) ~ /[ \t]/ )
  1136.             prev_char()
  1137.  
  1138.         ins = ( read_buffer( -1 ) == "." ) ? "  " : " "
  1139.     
  1140.         drop_anchor()
  1141.         next_line()
  1142.         vi_yank( AND_DELETE )
  1143.     
  1144.         if( read_buffer( 1 ) != "(" ){
  1145.             insert_string( ins )
  1146.         }
  1147.     }
  1148. }
  1149.  
  1150. function vi_D(){
  1151.     op_type_1()
  1152.     pend_operator( "d" )
  1153.     vi_goto_eol()
  1154. }
  1155.  
  1156. function vi_C(){
  1157.     vi_D()
  1158.     vi_insert()
  1159. }
  1160.  
  1161. function vi_R(){
  1162.     op_type_1()
  1163.     buffer_flags = or( buffer_flags, BUFFER_OVERTYPE_MODE )
  1164.     vi_insert()
  1165.     buffer_flags = xor( buffer_flags, BUFFER_OVERTYPE_MODE )
  1166. }
  1167.  
  1168. function vi_r( ){
  1169.     local n
  1170.  
  1171.     op_type_1()
  1172.  
  1173.     n = use_number()
  1174.  
  1175.     # limit scope to current line
  1176.  
  1177.     if( current_line_length - current_line_offset < n )
  1178.         vi_beep()
  1179.  
  1180.     if( !playing_again )
  1181.         vi_r_ch = vi_getc()
  1182.     if( vi_r_ch == "\r" || vi_r_ch == "\n" )
  1183.         vi_r_ch = "\n"
  1184.  
  1185.     select_chars( n )
  1186.     vi_yank( AND_DELETE )
  1187.  
  1188.     if( vi_r_ch == "\n" ){
  1189.         while( n-- > 0 )
  1190.             vi_ai_cr()
  1191.     } else {
  1192.         while( n-- > 0 )
  1193.             insert_string( vi_r_ch )
  1194.  
  1195.         if( current_column > 1 )
  1196.             prev_char()
  1197.     }
  1198. }
  1199.  
  1200. function vi_S(){
  1201.     op_type_1()
  1202.  
  1203.     select_lines( use_number())
  1204.     vi_yank( AND_DELETE )
  1205.     vi_insert( NEWLINE_BEFORE )
  1206. }
  1207.  
  1208. function vi_s(){
  1209.     op_type_1()
  1210.  
  1211.     select_chars( use_number())
  1212.     vi_yank( AND_DELETE )
  1213.     vi_insert()
  1214. }
  1215.  
  1216. function vi_tilde(){
  1217.     op_type_1()
  1218.  
  1219.     select_chars( use_number())
  1220.     reverse()
  1221.     raise_anchor()
  1222.  
  1223.     if( current_line_length <= current_line_offset        \
  1224.     && current_line_offset )
  1225.         prev_char()
  1226. }
  1227.  
  1228. ### vi motion commands
  1229.  
  1230. local function vi_goto_line( line ){
  1231.     current_line = line
  1232.     skip_whitespace()
  1233. }
  1234.  
  1235. function vi_goto( n ){
  1236.     op_type_2b( MAJOR_MARK )
  1237.  
  1238.     if( number_register )
  1239.         n = use_number()        # need a range check
  1240.     else
  1241.         if( !n )
  1242.             n = buffer_last_line - 1
  1243.  
  1244.     vi_goto_line( n )
  1245.  
  1246.     xeq_op( LINE_SELECTION )
  1247. }
  1248.  
  1249. function vi_left(){
  1250.     local n = use_number() 
  1251.  
  1252.     if( current_line_offset < n )
  1253.         vi_beep()
  1254.  
  1255.     op_type_2b()
  1256.     prev_char( n )
  1257.     xeq_op()
  1258. }
  1259.  
  1260. function vi_right(){
  1261.     local n = use_number() 
  1262.  
  1263.     if( current_line_length - current_line_offset <= n )
  1264.         vi_beep()
  1265.     
  1266.     op_type_2b()
  1267.     next_char( n )
  1268.     xeq_op()
  1269. }
  1270.  
  1271. local vert_column
  1272. local vert_motion
  1273.  
  1274. function vi_up(){
  1275.     local n = use_number() 
  1276.  
  1277.     if( current_line <= n )
  1278.         vi_beep()
  1279.  
  1280.     op_type_2b()
  1281.     up( n )
  1282.  
  1283.     if( prev_command != vert_motion)
  1284.         vert_column = current_column
  1285.     else
  1286.         current_column = vert_column
  1287.     vert_motion = current_command
  1288.  
  1289.     if( and( buffer_flags, BUFFER_POSITION_IS_VIRTUAL ))
  1290.         prev_char()
  1291.  
  1292.     xeq_op( LINE_SELECTION )
  1293. }
  1294.  
  1295. function vi_down(){
  1296.     local n = use_number() 
  1297.  
  1298.     op_type_2b()
  1299.     move_down( n )
  1300.  
  1301.     if( prev_command != vert_motion)
  1302.         vert_column = current_column
  1303.     else
  1304.         current_column = vert_column
  1305.     vert_motion = current_command
  1306.  
  1307.     if( and( buffer_flags, BUFFER_POSITION_IS_VIRTUAL ))
  1308.         prev_char()
  1309.  
  1310.     xeq_op( LINE_SELECTION )
  1311. }
  1312.  
  1313. function vi_space(){
  1314.     if( region_type()){
  1315.         operator_disallowed()
  1316.         if( and( keyboard_flags, ALT_KEY ))
  1317.             outdent_columns()     
  1318.         else
  1319.             indent_columns()     
  1320.     } else
  1321.         vi_right()
  1322. }
  1323.  
  1324. function vi_bksp(){
  1325.     if( region_type()){
  1326.         operator_disallowed()
  1327.         outdent_columns()     
  1328.     } else
  1329.         vi_left()
  1330. }
  1331.  
  1332. function vi_tab(){
  1333.     if( region_type()){
  1334.         operator_disallowed()
  1335.         if( and( keyboard_flags, ALT_KEY ))
  1336.             outdent_tabs()
  1337.         else
  1338.             indent_tabs()
  1339.     } else
  1340.         vi_beep()
  1341. }
  1342.  
  1343. function vi_back_tab(){
  1344.     if( region_type()){
  1345.         operator_disallowed()
  1346.         outdent_tabs()
  1347.     } else
  1348.         vi_beep() 
  1349. }
  1350.  
  1351.  
  1352. function vi_goto_column(){
  1353.     local n = use_number() 
  1354.  
  1355.     if( current_line_width < n )
  1356.         vi_beep()
  1357.  
  1358.     op_type_2b()
  1359.     current_column = n
  1360.     xeq_op()
  1361. }
  1362.  
  1363. function vi_H(){
  1364.     local n = use_number() - 1
  1365.  
  1366.     op_type_2b( MAJOR_MARK )
  1367.  
  1368.     goto_window_top()
  1369.     if( n >= window_text_height )
  1370.         n = window_text_height - 1
  1371.     down( n )
  1372.     skip_whitespace()
  1373.  
  1374.     xeq_op( LINE_SELECTION )
  1375. }
  1376.  
  1377. function vi_M(){
  1378.     disallow_number()
  1379.     op_type_2b( MAJOR_MARK )
  1380.  
  1381.     goto_window_middle()
  1382.     skip_whitespace()
  1383.  
  1384.     xeq_op( LINE_SELECTION )
  1385. }
  1386.  
  1387. function vi_L(){
  1388.     local n = use_number() - 1
  1389.  
  1390.     op_type_2b( MAJOR_MARK )
  1391.  
  1392.     goto_window_bottom()
  1393.     if( n >= window_text_height )
  1394.         n = window_text_height - 1
  1395.     up( n )
  1396.     skip_whitespace()
  1397.  
  1398.     xeq_op( LINE_SELECTION )
  1399. }
  1400.  
  1401. function vi_goto_bol(){
  1402.     disallow_number()
  1403.  
  1404.     op_type_2b()
  1405.     goto_bol()
  1406.     xeq_op()
  1407. }
  1408.  
  1409. function vi_goto_eol(){
  1410.     local n = use_number()
  1411.  
  1412.     op_type_2b()
  1413.     if( n > 1 )
  1414.         down( n - 1 )
  1415.     current_column = current_line_width    # goto_eol()
  1416.     xeq_op( INCLUSIVE_SELECTION )
  1417. }
  1418.  
  1419. function vi_next_line(){
  1420.     local n = use_number()
  1421.  
  1422.     op_type_2b()
  1423.     next_line( n )
  1424.     xeq_op( LINE_SELECTION )
  1425. }
  1426.  
  1427. function vi_prev_line(){
  1428.     local n = use_number()
  1429.  
  1430.     op_type_2b()
  1431.     prev_line( n )
  1432.     xeq_op( LINE_SELECTION )
  1433. }
  1434.  
  1435. function vi_skip_whitespace(){
  1436.     local n = use_number()
  1437.  
  1438.     op_type_2b()
  1439.     skip_whitespace()
  1440.     xeq_op()
  1441. }
  1442.  
  1443. function vi_goto_matching(){
  1444.     local n = use_number()
  1445.  
  1446.     op_type_2b( MAJOR_MARK )
  1447.     if( read_buffer( 1 ) !~ /[(){}[\]]/ )
  1448.         if( match( read_buffer(), "[(){}[\\]]" ))
  1449.             next_char( RSTART - 1 )
  1450.         else
  1451.             vi_beep()
  1452.  
  1453.     goto_matching()
  1454.     xeq_op( INCLUSIVE_SELECTION )
  1455. }
  1456.  
  1457.  
  1458.  
  1459. # Word, sentence, paragraph, and section motions.
  1460. # Different commands call vi_motion with one of the patterns below.
  1461. #
  1462. # Different dialects of vi implement subtle variations for certain combos:
  1463. #
  1464. #    Commands.....        SPE    MKS    Sun    AIX
  1465. #                
  1466. #    !w >W etc.        yes    yes    error    error
  1467. #
  1468. #    w W b B over \n        skips    skips    stops    error
  1469. #                \n    \n    at \n
  1470. #
  1471. #
  1472. #    add "|^[ \t]*$" to word_, WORD_, end_, and END_patt to make these
  1473. #    motions treat newlines as words, ala Sun vi.
  1474.  
  1475.  
  1476. local word_patt = "[a-z0-9A-Z_]+|[^a-z0-9A-Z_ \t]+"
  1477. local WORD_patt = "[^ \t]+"
  1478. local  end_patt = "[ \t]*([a-z0-9A-Z_]+|[^a-z0-9A-Z_ \t]+)\\c"
  1479. local  END_patt = "[ \t]*[^ \t]+\\c"
  1480. local sent_patt    = "(\\.|!|\\?)[)\"'\\]]*(  |$)"
  1481. local para_patt    = "^$|^\\.PP$|^\\.NH|^\\.IP|^\\.LP|^\\.PP|^\\.QP|^\\.LI"
  1482. local sect_patt    = "^\\.NH|^\\.SH|^\\.H([ \t]|$)|^\\.HU|^\\{|^function|^local[ \t]+function|^global[ \t]+function|^\f"
  1483.  
  1484.  
  1485. local function vi_motion( patt, direction, special ){
  1486.     local sflags = SEARCH_REGEX + SEARCH_MAXIMAL_MATCH + SEARCH_ADVANCE
  1487.     local count
  1488.  
  1489.     search_count = count = use_number()
  1490.     op_type_2b( MAJOR_MARK )
  1491.  
  1492.     save_position()
  1493.  
  1494.     if( count != search( patt, direction + sflags )){
  1495.         restore_position( 1 )
  1496.         vi_beep()
  1497.     }
  1498.  
  1499.     restore_position( 0 )
  1500. }
  1501.  
  1502.  
  1503. function vi_w( patt ){
  1504.  
  1505.     if( !argcount())
  1506.         patt = word_patt
  1507.  
  1508.     if( pending_operator ~ /[ycd]/ )
  1509.         patt = patt "|$"
  1510.  
  1511.     if( pending_operator == "c" ){
  1512.         vi_cw( patt )
  1513.  
  1514.     } else {
  1515.         vi_motion( patt, SEARCH_FORWARD )
  1516.         xeq_op( NORMAL_SELECTION )
  1517.     }
  1518. }
  1519.  
  1520. function vi_cw( patt ){
  1521.     local ch = read_buffer( 1 )
  1522.  
  1523.     vi_motion( patt, SEARCH_FORWARD )
  1524.  
  1525.     if( ch !~ /[ \t]/ )
  1526.         search( "[^ \t]\\c", SEARCH_REGEX + SEARCH_ADVANCE )
  1527.  
  1528.     xeq_op( NORMAL_SELECTION )
  1529.  
  1530. }
  1531.  
  1532. function vi_W(){
  1533.     vi_w( WORD_patt )
  1534. }
  1535.  
  1536. function vi_e(){
  1537.     vi_motion( end_patt, SEARCH_FORWARD )
  1538.     prev_char()
  1539.     xeq_op( INCLUSIVE_SELECTION )
  1540. }
  1541.  
  1542. function vi_E(){
  1543.     vi_motion( END_patt, SEARCH_FORWARD )
  1544.     prev_char()
  1545.     xeq_op( INCLUSIVE_SELECTION )
  1546. }
  1547.  
  1548. function vi_B(){
  1549.     vi_motion( WORD_patt, SEARCH_BACKWARD )
  1550.     xeq_op( NORMAL_SELECTION )
  1551. }
  1552.  
  1553. function vi_b(){
  1554.     vi_motion( word_patt, SEARCH_BACKWARD )
  1555.     xeq_op( NORMAL_SELECTION )
  1556. }
  1557.  
  1558. function vi_prev_sent(){
  1559.     local sflags = SEARCH_BKWD_REGEX_ADV + SEARCH_MAXIMAL_MATCH 
  1560.     local count = use_number() + 1
  1561.     
  1562.     op_type_2b( MAJOR_MARK )
  1563.  
  1564.     search_count = count
  1565.     if( search( sent_patt, sflags ) == count )
  1566.         next_word()
  1567.     else
  1568.         goto_buffer_top()
  1569.  
  1570.     xeq_op( NORMAL_SELECTION )
  1571. }
  1572.  
  1573. function vi_next_sent(){
  1574.     vi_motion( sent_patt, SEARCH_FORWARD )
  1575.     next_word()
  1576.     xeq_op( NORMAL_SELECTION )
  1577. }
  1578.  
  1579. function vi_prev_para(){
  1580.     vi_motion( para_patt, SEARCH_BACKWARD )
  1581.     if( pending_operator )
  1582.         current_line--
  1583.     xeq_op( LINE_SELECTION )
  1584. }
  1585.  
  1586. function vi_next_para(){
  1587.     vi_motion( para_patt, SEARCH_FORWARD )
  1588.     if( pending_operator )
  1589.         current_line--
  1590.     xeq_op( LINE_SELECTION )
  1591. }
  1592.  
  1593. function vi_prev_sect(){
  1594.     record_op()        # see op_type_1()
  1595.     disallow_number()    # removing this would be a useful EXTENSION
  1596.     vi_motion( sect_patt, SEARCH_BACKWARD )
  1597.     if( pending_operator )
  1598.         current_line--
  1599.     xeq_op( LINE_SELECTION )
  1600. }
  1601.  
  1602. function vi_next_sect(){
  1603.     record_op()        # see op_type_1()
  1604.     disallow_number()    # removing this would be a useful EXTENSION
  1605.     vi_motion( sect_patt, SEARCH_FORWARD )
  1606.     if( pending_operator )
  1607.         current_line--
  1608.     xeq_op( LINE_SELECTION )
  1609. }
  1610.  
  1611.  
  1612.  
  1613.  
  1614. ## vi "find" commands
  1615. #    fx    skip forward to character "x"
  1616. #    Fx    skip backwards to character "x"
  1617. #    tx    skip forwards to just before character "x"
  1618. #    Tx    backwards just before "x"
  1619. #    ;    repeat last find command
  1620. #    ,    execute last find command in reverse direction
  1621.  
  1622. function vi_find_repeat(){
  1623.     vi_find_it( vi_find_com )
  1624. }
  1625.  
  1626. function vi_find_reverse(){
  1627.     local com = toreverse( vi_find_com )
  1628.     vi_find_it( com )
  1629. }
  1630.  
  1631. function vi_find(){
  1632.     vi_find_com = chr( current_key )
  1633.     vi_find_it( vi_find_com, 1 )
  1634. }
  1635.  
  1636. local function vi_find_it( com, needChar ){
  1637.     local i, n
  1638.  
  1639.     op_type_2b( MAJOR_MARK )
  1640.  
  1641.     if( needChar )         ## !playing_again && )
  1642.         vi_find_char = vi_getc()
  1643.  
  1644.     if( !vi_find_char )
  1645.         vi_beep()
  1646.  
  1647.     n = use_number()
  1648.     
  1649.     while( n-- > 0 ){
  1650.         if( islower( com )){        # forwards
  1651.             i = index( substr( read_buffer(), 2), vi_find_char )
  1652.             if( i )
  1653.                 next_char( i - ( com == "t" ))
  1654.         } else {            # backwards
  1655.             i = bindex( read_buffer( - current_column ), vi_find_char )
  1656.             if( i )
  1657.                 prev_char( i - ( com == "T" ))
  1658.         }
  1659.     }
  1660.     if( !i )
  1661.         vi_beep()
  1662.  
  1663.     xeq_op( islower( com ) ? INCLUSIVE_SELECTION : NORMAL_SELECTION )
  1664. }
  1665.  
  1666. # backwards index
  1667. #    return the offset left from the end of the string of the rightmost 
  1668. #    instance of ch, if any, in s
  1669.  
  1670. local function bindex( s, ch ){
  1671.     local n = rindex( s, ch )
  1672.     if( n )
  1673.         n = length( s ) - n + 1
  1674.     return n
  1675. }
  1676.  
  1677. ## marks & mark motion
  1678.  
  1679. # translate a mark ascii name to a bookmark id
  1680.  
  1681. local function vi_mark_id( ch ){
  1682.     if( !argcount())
  1683.         ch = vi_getc()
  1684.  
  1685.     prev_mark_id = ch 
  1686.  
  1687.     if( ch ~ /[`']/ )
  1688.         return vi_base_mark
  1689.     if( ch ~ /[a-zA-Z]/ )
  1690.         return vi_base_mark + ord( toupper( ch )) - ord( "@" )
  1691.     vi_beep()
  1692. }
  1693.  
  1694.  
  1695. # record current position into "`" mark
  1696. #    called by all motion commands to permit goto previous location,
  1697. #    and to mark the range of vi operators
  1698. #
  1699. #    context_type:    0 or Null =>    mark motion starting point
  1700. #            1 =>        mark non-relative motion starting point
  1701. #            2 =>        don't mark (for vi_place_mark)
  1702.  
  1703. local function mark_context( context_type ){ 
  1704.     if( context_type == DONT_MARK )
  1705.         return
  1706.  
  1707.     if( context_type == MAJOR_MARK )
  1708.         create_mark( vi_major_mark ) # , current_line, current_column )
  1709.  
  1710.     create_mark( vi_base_mark ) #, current_line, current_column )
  1711. }
  1712.  
  1713.  
  1714. # m command -- places a mark here
  1715.  
  1716. function vi_place_mark(){
  1717.     operator_disallowed()
  1718.     disallow_number()
  1719.     record_op()
  1720.     create_mark( vi_mark_id(), current_line, current_column )
  1721. }
  1722.  
  1723. function vi_goto_mark(){
  1724.     local lines = chr( current_key ) == vi_line_mark
  1725.     local mark
  1726.  
  1727.     disallow_number()
  1728.     record_op()
  1729.     op_type_2b( DONT_MARK )
  1730.     mark = vi_mark_id()
  1731.  
  1732.     if( mark == vi_base_mark )
  1733.         swap_marks( vi_major_mark )
  1734.     else {
  1735.         if( !mark_defined( mark ))
  1736.             vi_beep()
  1737.         mark_context( MAJOR_MARK )
  1738.         goto_mark( mark )
  1739.     }
  1740.  
  1741.     if( lines )
  1742.         skip_whitespace()
  1743.  
  1744.     xeq_op( lines ? LINE_SELECTION : NORMAL_SELECTION )
  1745. }
  1746.  
  1747. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  1748.  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  1749. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  1750.  
  1751. ### ex mode
  1752. #
  1753. #    ex_key:        prompt the user; call ex_mode if something entered
  1754. #
  1755. #    ex_mode:    handle non-globable prefix and suffix
  1756. #
  1757. #    ex_mode1:     called recursively by ex_glob()
  1758. #
  1759. #    Bug: this version ignores superflurous stuff after a valid command
  1760.  
  1761. function ex_key(){
  1762.     local com
  1763.  
  1764.     operator_disallowed()
  1765.     establish_baseline()
  1766.  
  1767.     com = prompt_history( "EX_MODE", ":" )
  1768.     if( com )
  1769.         ex_mode( com )
  1770. }
  1771.  
  1772. local function ex_mode( command ){
  1773.     ex_com = command 
  1774.     ex_address_range()
  1775.  
  1776.     ex_mode1()
  1777.  
  1778.     if( browser_bid )
  1779.         browse_printed()
  1780.  
  1781.     vi_command_mode()
  1782. }
  1783.  
  1784. local function ex_mode1( command ){
  1785.     local i, n, com
  1786.  
  1787.     if( command )
  1788.         ex_com = command
  1789.  
  1790.     com = eat_char()
  1791.     if( com == "s" && ex_com ~ /^h/ )
  1792.         com = com eat_char()
  1793.  
  1794.     if( com == "s" ){
  1795.         ex_sub()
  1796.  
  1797.     } else if( com == "g" || com == "v" ){
  1798.         ex_glob( com )
  1799.  
  1800.     } else if( com == "w" ){
  1801.         if( eat_match( "q" )){
  1802.             ex_write()
  1803.             ex_quit()
  1804.         } else
  1805.             ex_write()
  1806.  
  1807.     } else if( com == "d" ){
  1808.         select_range( DEFAULT_ONE )
  1809.         vi_yank( AND_DELETE )
  1810.  
  1811.     } else if( com == "=" ){
  1812.         n = ( ex_addresses > 1 ) ? ex_addr2 : ex_addr1
  1813.         if( in_glob )
  1814.             pbuf( n )
  1815.         else
  1816.             message( n )
  1817.  
  1818.         # message( "(%d) addresses: %d, %d",     \
  1819.         #    ex_addresses,             \
  1820.         #    ex_addr1,             \
  1821.         #    ex_addr2 )
  1822.  
  1823.     } else if( com == "p" || com == "l" || ex_addresses > 11 && com == "" ){
  1824.         select_range( DEFAULT_ONE )
  1825.         copy_to_scrap()
  1826.         scrap_to_browser()
  1827.         raise_anchor()
  1828.  
  1829.     } else if( com == "" && ex_addresses == 1 ){
  1830.         vi_goto_line( ex_addr1 )
  1831.  
  1832.     } else if( com == "r" ){
  1833.         ex_read()
  1834.  
  1835.     } else if( in_glob ){
  1836.         message( "can't glob :" com ex_com )
  1837.  
  1838.     } else if( ex_addresses ){
  1839.         message( "addresses are illegal for :" com ex_com )
  1840.  
  1841.     } else if( com == "e" ){
  1842.         ex_edit()
  1843.  
  1844.     } else if( com == "f" ){
  1845.         if(( com = eat_arg()))
  1846.             buffer_filename = com
  1847.         ex_show_file()
  1848.  
  1849.     } else if( com == "n" ){
  1850.         next_buffer()
  1851.         ex_show_file()
  1852.  
  1853.     # } else if( com == "p" ){
  1854.     #    prev_buffer()
  1855.     #    ex_show_file()
  1856.  
  1857.     } else if( com == "sh" ){
  1858.         system()
  1859.  
  1860.     } else if( com == "!" ){
  1861.         if( eat_bang())
  1862.             com = current_history_item( "!" )
  1863.         else
  1864.             com = add_prompt_history( "!", eat_arg())
  1865.         system( com )
  1866.  
  1867.     } else if( com == "x" ){
  1868.         write_and_exit()
  1869.  
  1870.     } else if( com == "q" ){
  1871.         ex_quit()
  1872.  
  1873.     } else
  1874.         message( "unrecognized command" )
  1875. }
  1876.  
  1877. local function ex_quit( force ){
  1878.     force = force || eat_bang()
  1879.  
  1880.     if( !force && buffers_modified )
  1881.         warning( "file has been modified; \"q!\" quits discarding changes" )
  1882.     else 
  1883.         quit( force )
  1884. }
  1885.  
  1886. local function ex_write(){
  1887.     local force = eat_bang()
  1888.     local name = eat_arg()
  1889.  
  1890.     if( !name )
  1891.         name = buffer_filename
  1892.  
  1893.     if( region_type()){
  1894.         write_marked_block( name )
  1895.  
  1896.      } else if( ex_addresses ){
  1897.         select_range()
  1898.         write_marked_block( name )
  1899.         raise_anchor()
  1900.  
  1901.     } else
  1902.         write_buffer( name )
  1903. }
  1904.  
  1905. local function ex_read(){
  1906.     # local force = eat_bang()
  1907.     local name = eat_arg()
  1908.  
  1909.     if( !name )
  1910.         name = vi_read_name
  1911.     if( !name )
  1912.         vi_beep( "must supply file name to read" )
  1913.     vi_read_name = name
  1914.  
  1915.     if( ex_addresses > 1 )
  1916.         vi_beep( "only 0 or 1 addresses allowed for :r" )
  1917.  
  1918.     if( ex_addresses )
  1919.         current_line = ex_addr1 + 1    # may be 0
  1920.     else
  1921.         current_line++
  1922.  
  1923.     goto_bol()
  1924.  
  1925.     read_file( name )
  1926. }
  1927.  
  1928. local function ex_edit( ){
  1929.     # local force = eat_bang()
  1930.     local name = eat_arg()
  1931.  
  1932.     if( name ){
  1933.         edit_file( name )
  1934.         ex_show_file()
  1935.     }
  1936. }
  1937.  
  1938. local function vi_filter(){
  1939.     local com = prompt_history( "!", "!", "", 1 )
  1940.  
  1941.     if( com )
  1942.         filter( com )
  1943. }
  1944.  
  1945. function ex_show_file( annotation ){
  1946.     local pct = 100
  1947.  
  1948.     # # faster, but less accurate:
  1949.     # if( buffer_size > 0 )
  1950.     #    pct = 100 * buffer_offset / buffer_size
  1951.     if( buffer_last_line > 0 )
  1952.         pct = 100 * current_line / buffer_last_line
  1953.  
  1954.     if( !annotation && and( buffer_flags, BUFFER_MODIFIED ))
  1955.         annotation = "[modified]"
  1956.         
  1957.     message( "\"%s\" %sline %d of %d -- %d%% --",     \
  1958.         buffer_filename,                 \
  1959.         ( annotation ? annotation " " : "" ),        \
  1960.         current_line,                     \
  1961.         buffer_last_line,                \
  1962.         pct )
  1963. }
  1964.  
  1965. local function ex_sub(){
  1966.     local quote = eat_char()
  1967.     local old = search_hist( eat_regex( quote ))
  1968.     local new = eat_regex( quote )
  1969.     local gp = eat_match( "[gp]+" ) # ":s/foo" is the same as ":s/foo//"
  1970.     local pflag, start, stop, count, n = 0
  1971.     local sflags = or( or( SEARCH_FWD_REGEX_MAX, SEARCH_BLOCK ),    \
  1972.                 and( search_flags, SEARCH_IGNORE_CASE ))
  1973.  
  1974.     if( match( "", old ) && RLENGTH == 0 )
  1975.         vi_beep( "NULL search pattern length." );
  1976.  
  1977.     if( gp !~ "p" ){
  1978.         search_count = 0x7FFFFFFF;
  1979.         if( gp !~ /g/ ){
  1980.             sflags = sflags + SEARCH_ONCE_PER_LINE
  1981.         }
  1982.         select_range( DEFAULT_ONE )
  1983.         n = replace( old, new, sflags )
  1984.         raise_anchor()
  1985.     } else {
  1986.         stop = ex_range( DEFAULT_ONE )
  1987.         start = current_line
  1988.         count = ( gp ~ /g/ ) ? 0x7FFFFFFF : 1
  1989.         pflag = ( gp ~ /p/ )
  1990.  
  1991.         while( start <= stop ){
  1992.             goto_pos( start++, 1 )
  1993.             drop_anchor( LINE_SELECTION )
  1994.             search_count = count
  1995.             n += replace( old, new, sflags )
  1996.             if( pflag ){
  1997.                 copy_to_scrap()
  1998.                 scrap_to_browser()
  1999.             }
  2000.             raise_anchor()
  2001.         }
  2002.     }
  2003.  
  2004.     if( !in_glob )
  2005.         message( "%d replacements made", n )
  2006. }
  2007.  
  2008. local function ex_glob( gv ){
  2009.     local com, quote, regex, n = 0
  2010.     local sflags = or( or( SEARCH_FWD_REGEX_MAX, SEARCH_BLOCK ),    \
  2011.                 or( SEARCH_ADVANCE,            \
  2012.                     and( search_flags, SEARCH_IGNORE_CASE )))
  2013.  
  2014.     if( in_glob )
  2015.         vi_beep( "recursive :" gv " is not allowed" )
  2016.     in_glob = 1
  2017.     gv = ( gv == "v" )
  2018.  
  2019.     quote = eat_char()
  2020.     regex = search_hist( eat_regex( quote ))
  2021.  
  2022.     if( match( "", regex ) && RLENGTH == 0 )
  2023.         vi_beep( "NULL search pattern length." );
  2024.  
  2025.     com = ex_com        # save remainder of glob command
  2026.     search_count = 1
  2027.  
  2028.     select_range()
  2029.     save_position()
  2030.  
  2031.     while( search( regex, sflags )){
  2032.  
  2033.         restore_position()
  2034.         save_position()
  2035.  
  2036.         ex_addr1 = ex_addr2 = current_line
  2037.         ex_addresses = 1
  2038.         ex_mode1( com )
  2039.  
  2040.         if(( n++ % 10 ) == 3 )
  2041.             display_update()
  2042.  
  2043.         if( com !~ /d/ )
  2044.             goto_eol()
  2045.     }
  2046.  
  2047.     restore_position( 1 )
  2048.  
  2049.     if( region_type())
  2050.         raise_anchor()
  2051.     in_glob = 0
  2052.  
  2053.     message( "%d lines processed", n )
  2054. }
  2055.  
  2056. local function ex_range( defaultOne ){
  2057.     local start, stop
  2058.  
  2059.     if( region_type()){
  2060.         stop = current_line
  2061.         swap_marks()
  2062.         start = current_line
  2063.         raise_anchor()
  2064.         if( stop < start ){
  2065.             start = stop
  2066.             stop = current_line
  2067.         } 
  2068.     } else if( ex_addresses ){
  2069.         start = ex_addr1
  2070.         stop = ex_addr2
  2071.     } else if( defaultOne ){
  2072.         start = stop = current_line
  2073.     } else {
  2074.         start = 1
  2075.         stop = buffer_last_line - 1
  2076.     }
  2077.  
  2078.     current_line = start
  2079.     return stop
  2080. }
  2081.  
  2082.  
  2083. local function browser_buffer(){
  2084.     if( !browser_bid )
  2085.         browser_bid = create_buffer( "", "", 1 )
  2086.  
  2087.     return browser_bid
  2088. }
  2089.  
  2090. local function scrap_to_browser(){
  2091.     local cur_buf = current_buffer
  2092.  
  2093.     current_buffer = browser_buffer()
  2094.     goto_buffer_bottom()
  2095.     insert_scrap()
  2096.  
  2097.     current_buffer = cur_buf
  2098. }
  2099.  
  2100. local browser_keymap = -1
  2101.  
  2102. local function browse_printed(){
  2103.     local cur_win = current_window
  2104.     local cur_buf = current_buffer
  2105.     local cur_map = current_keymap
  2106.  
  2107.     if( browser_keymap == -1 ){
  2108.         current_keymap = browser_keymap = create_keymap( empty_keymap )
  2109.  
  2110.         assign_key( "<Esc>",        "process_end" )
  2111.         assign_key( "G",        "vi_goto" )
  2112.         assign_key( "g",        "vi_goto 1" )
  2113.         assign_key( "<Home>",        "vi_goto_bol" )
  2114.         assign_key( "<End>",        "vi_goto_eol" )
  2115.         assign_key( "+",        "vi_next_line" )
  2116.         assign_key( "-",        "vi_prev_line" )
  2117.         assign_key( "<Enter>",        "vi_next_line" )
  2118.         assign_key( "<Keypad-Minus>",    "vi_prev_line" )
  2119.         assign_key( "<Keypad-+>",    "vi_next_line" )
  2120.         assign_key( "H",        "vi_H" )
  2121.         assign_key( "M",        "vi_M" )
  2122.         assign_key( "L",        "vi_L" )
  2123.         assign_key( "<Ctrl-PgUp>",    "goto_buffer_top" )
  2124.         assign_key( "<Ctrl-PgDn>",    "goto_buffer_bottom" )
  2125.         assign_key( "<PgUp>",        "page_up" )
  2126.         assign_key( "<PgDn>",        "page_down" )
  2127.         assign_key( "<Ctrl-B>",        "page_up" )
  2128.         assign_key( "<Ctrl-F>",        "page_down" )
  2129.         assign_key( "<Ctrl-D>",        "scroll_down_half" )
  2130.         assign_key( "<Ctrl-U>",        "scroll_up_half" )
  2131.         assign_key( "<Ctrl-E>",        "scroll_down_1" )
  2132.         assign_key( "<Ctrl-Y>",        "scroll_up_1" )
  2133.     }
  2134.  
  2135.     current_keymap = browser_keymap
  2136.  
  2137.     current_window = create_window()
  2138.     current_buffer = browser_buffer()
  2139.     attach_window_buffer( current_window, current_buffer )
  2140.     window_name  = "Command Output Listing"
  2141.     message( "Use motion keys to browse, ESC to exit" )
  2142.  
  2143.     process_begin()
  2144.  
  2145.     message( "" )
  2146.     delete_buffer( current_buffer )
  2147.     delete_window( current_window )
  2148.     current_buffer = cur_buf
  2149.     current_window = cur_win
  2150.     current_keymap = cur_map
  2151.  
  2152.     browser_bid = 0
  2153.  
  2154. }
  2155.  
  2156. local function pbuf( s ){
  2157.     local cur_buf = current_buffer
  2158.  
  2159.     current_buffer = browser_buffer()
  2160.     insert_string( s "\n" )
  2161.  
  2162.     current_buffer = cur_buf
  2163. }
  2164.  
  2165.  
  2166. # select a region based on starting and ending addresses
  2167.  
  2168. local function select_range( defaultOne ){
  2169.      if( ex_addresses ){
  2170.         current_line = ex_addr2
  2171.         current_column = 1
  2172.         drop_anchor( LINE_SELECTION )
  2173.         if( ex_addresses > 1 )
  2174.             current_line = ex_addr1
  2175.     } else if( defaultOne )
  2176.         drop_anchor( LINE_SELECTION )
  2177.     else
  2178.         current_line = current_column = 1
  2179. }
  2180.  
  2181. # parse a range address expression; result is 0, 1 or 2 numbers
  2182. #
  2183. #    term:
  2184. #        .
  2185. #        $
  2186. #        'a
  2187. #        /.../
  2188. #        ?...?
  2189. #    addr:    
  2190. #        term
  2191. #        term + num
  2192. #        term - num
  2193. #        + num            # . + num
  2194. #        - num            # . - num
  2195. #        term +            # term + 1
  2196. #        term -            # term - 1
  2197. #        num
  2198. #    range:
  2199. #        /* empty */        # 1,$
  2200. #        addr
  2201. #        addr , addr
  2202. #        addr ; addr
  2203. #        ,            # 1,$
  2204. #        ;            # .,$
  2205. #
  2206. local function ex_address(){
  2207.     local len1, len = length( ex_com )
  2208.     local num, patt, mark, op
  2209.  
  2210.     # get stand-alone term, if any:
  2211.  
  2212.     if( eat_match( "/" )){
  2213.         vi_search( eat_regex( "/" ), SEARCH_FORWARD )
  2214.         ex_addr2 = current_line
  2215.  
  2216.     } else if( eat_match( "\\?" )){
  2217.         vi_search( eat_regex( "?" ), SEARCH_BACKWARD )
  2218.         ex_addr2 = current_line
  2219.  
  2220.     } else if( eat_match( "\\." )){
  2221.         ex_addr2 = current_line
  2222.  
  2223.     } else if( eat_match( "\\$" )){
  2224.         ex_addr2 = buffer_last_line - 1
  2225.  
  2226.     } else if(( patt = eat_match( "'." ))){
  2227.         mark = vi_mark_id( substr( patt, 2 ))
  2228.         if( !mark_defined( mark ))
  2229.             bad_addr()
  2230.         ex_addr2 = mark_line( mark )
  2231.  
  2232.     } else
  2233.         ex_addr2 = current_line        # missing -- set default
  2234.  
  2235.     len1 = len - length( ex_com )
  2236.  
  2237.     # get operator, if any:
  2238.  
  2239.     op = eat_match( "[+\\-]" )
  2240.  
  2241.     line_search = !!op        # signal to vi_search_key()
  2242.  
  2243.     # get number, if any:
  2244.  
  2245.     if(( num = eat_match( "[0-9]+" ))){
  2246.         if( len1 && op == "" )
  2247.             bad_addr()
  2248.         num = 0 + num
  2249.     } else
  2250.         num = 1
  2251.  
  2252.     # compute final value:
  2253.  
  2254.     if( op == "+" )
  2255.         ex_addr2 = ex_addr2 + num
  2256.     else if( op == "-" )
  2257.         ex_addr2 = ex_addr2 - num
  2258.     else if( !len1 )    # if no op and no stand-alone term
  2259.         ex_addr2 = num
  2260.  
  2261.     # update address count, if any:
  2262.  
  2263.     if( len != length( ex_com ))
  2264.         ex_addresses ++
  2265.  
  2266.     return ex_addr2
  2267. }
  2268.  
  2269. local function ex_address_range(){
  2270.     local ch
  2271.  
  2272.     ex_addresses = 0
  2273.     mark_context()
  2274.     ex_addr1 = ex_address()
  2275.  
  2276.     if( ex_addresses ){
  2277.         if( ex_com ~ /^;/ ){
  2278.             vi_goto_line( ex_addr1 )
  2279.             goto_bol()
  2280.         }
  2281.         if( eat_match( "[,;]" )){
  2282.             ex_addr2 = ex_address()
  2283.             if( ex_addresses != 2 )
  2284.                 bad_addr()
  2285.         }
  2286.  
  2287.     } else if( eat_match( "[,%]" )){
  2288.         ex_addr1 = 1
  2289.         ex_addr2 = buffer_last_line - 1
  2290.         ex_addresses = 2
  2291.  
  2292.     } else if( eat_match( ";" )){
  2293.         ex_addr1 = current_line
  2294.         ex_addr2 = buffer_last_line - 1
  2295.         ex_addresses = 2
  2296.  
  2297.     } else {
  2298.         ex_addr1 = ex_addr2 = current_line
  2299.     }
  2300.  
  2301.     if( ex_addresses > 1 && ex_addr1 > ex_addr2 )
  2302.         bad_addr()
  2303.  
  2304.     goto_mark( vi_base_mark )
  2305. }
  2306.  
  2307. local function bad_addr(){
  2308.     goto_mark( vi_base_mark )        # restore initial position
  2309.     vi_beep( "Ill-formed address" )
  2310. }
  2311.  
  2312. ## address and ex mode parser helpers
  2313. #
  2314. #    eat_... => if match then return the matched string and advance ex_com; 
  2315. #        else return null without advancing ex_com.
  2316.  
  2317. local function eat_match( patt ){
  2318.     local r = ""
  2319.  
  2320.     if( match( ex_com, "^" patt )){
  2321.         r = substr( ex_com, 1, RLENGTH )
  2322.         ex_com = substr( ex_com, RLENGTH + 1 )
  2323.     }
  2324.     return r
  2325. }
  2326.  
  2327. local function eat_space(){
  2328.     return eat_match( "[ \t]*" )
  2329. }
  2330.  
  2331. local function eat_bang(){
  2332.     return eat_match( "!" )
  2333. }
  2334.  
  2335. local function eat_arg(){
  2336.     eat_space()
  2337.     return eat_match( "[^;]+" )
  2338. }
  2339.  
  2340. local function eat_char(){
  2341.     local ch = substr( ex_com, 1, 1 )
  2342.     ex_com = substr( ex_com, 2 )
  2343.     return ch
  2344. }
  2345.  
  2346. # eat a regular expression, with <sep> as the separator.
  2347. #
  2348. #    syntax is     sep regex sep
  2349. #           or    sep regex EOL
  2350. #
  2351. #    sep is        "/"  or  "?"
  2352. #
  2353. #    Leading separator should already have been removed.
  2354. #    We don't use eat_match to avoid the slower anchored search.
  2355. #    This is a good place to implement nomagic and other regex
  2356. #        transformations.
  2357. #
  2358. local function eat_regex( sep ){
  2359.     local patt, regex, len
  2360.  
  2361.     # closing seperator may be prefixed by an even number of backslashes;
  2362.     # an odd number of backslashes quote the seperator, requiring it to
  2363.     # be included in the regex.
  2364.  
  2365.     patt = "[^\\\\](\\\\\\\\)*"    
  2366.     if( sep ~ /[|\]^$*+(){}.?\\]/ )    # regex seperators must be quoted
  2367.         sep =  "\\" sep
  2368.  
  2369.     # split the input into a regex LHS and a remainder RHS, discarding
  2370.     # the closing seperator; two matches because first may not be longest
  2371.  
  2372.     if( match( ex_com, "^" sep ) || match( ex_com, patt sep )){
  2373.         len = RSTART + RLENGTH - 1 # index of closing seperator
  2374.         regex = substr( ex_com, 1, len - 1 )
  2375.         ex_com = substr( ex_com, len + 1 )
  2376.     } else {
  2377.         regex = ex_com
  2378.         ex_com = ""
  2379.     }
  2380.  
  2381.     # Now remove quotes from quoted seperators.  By definition, they must 
  2382.     # have an odd number of preceeding backslashes, one of which must be
  2383.     # removed.  However, regex chars need to retain the quoting.
  2384.  
  2385.     if( length( sep ) == 1 )
  2386.         gsub( "\\" sep, sep, regex )
  2387.  
  2388.     return regex
  2389. }
  2390.  
  2391. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  2392.  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  2393. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  2394.  
  2395. ### regex searches
  2396. #
  2397. #    vi_search    searches forwards or backwards for 1 pattern
  2398. #            vi_beep()s if the pattern is not found.
  2399. #    vi_search_key    prompts for input, and otherwise handles the 
  2400. #            "/" and "?" commands
  2401. #    vi_search_again    handles the "n", "N", and Alt-F5 and Alt-F6 commands.
  2402. #
  2403. #    search_hist()    recalls or remembers previous search pattern
  2404. #
  2405.  
  2406. function vi_search_key( key ){
  2407.     local patt, addr
  2408.  
  2409.     disallow_number()
  2410.     op_type_2b( DONT_MARK )
  2411.  
  2412.     patt = prompt_history( "SEARCH", key, "", 1 )
  2413.  
  2414.     if( patt ){
  2415.         record_op( ENTER_KEY )
  2416.     
  2417.         vi_search_dir = ( key == "/" ? SEARCH_FORWARD : SEARCH_BACKWARD )
  2418.         mark_context( MAJOR_MARK )    # mark context for motion
  2419.     
  2420.         ex_com = key patt
  2421.         addr = ex_address()    # parse command, call vi_search() as needed
  2422.     
  2423.         # execute op, if any, in line or normal mode
  2424.     
  2425.         if( line_search ){
  2426.             vi_goto_line( addr )
  2427.             xeq_op( LINE_SELECTION )
  2428.         } else
  2429.             xeq_op()
  2430.     
  2431.     } else
  2432.         op_flush()
  2433. }
  2434.  
  2435. local function search_hist( patt ){
  2436.  
  2437.     if( patt )
  2438.         add_prompt_history( SEARCH_HIST, patt )
  2439.     else {
  2440.         patt = current_history_item( SEARCH_HIST )
  2441.         if( !patt )
  2442.             vi_beep( "Missing previous pattern" )
  2443.     }
  2444.  
  2445.     return patt
  2446. }
  2447.  
  2448.  
  2449. local function vi_search( pattern, dir ){
  2450.     pattern = search_hist( pattern )
  2451.     if( !search( pattern, or( vi_search_flags, dir )))
  2452.         vi_beep()
  2453. }
  2454.  
  2455. # search again is called by 4 different keys, with 4 different meanings:
  2456. #
  2457. #    n     search again, same direction as originally
  2458. #    N     search again, opposite direction as originally
  2459. #    Alt-F5    search again, backwards
  2460. #    Alt-F6    search again, forwards
  2461. #
  2462. function vi_search_again( key ){
  2463.     local dir 
  2464.  
  2465.     disallow_number()
  2466.     op_type_2b( MAJOR_MARK )
  2467.  
  2468.     # figure out what direction we're going...
  2469.             
  2470.     dir = ( argcount()                        \
  2471.         ? (( key == "?" )                    \
  2472.             ? SEARCH_BACKWARD                \
  2473.             : SEARCH_FORWARD )                \
  2474.         : (( chr( and( 255, current_key )) == "N" )        \
  2475.             ? (( vi_search_dir == SEARCH_FORWARD )        \
  2476.                 ? SEARCH_BACKWARD            \
  2477.                 : SEARCH_FORWARD )            \
  2478.             :                        \
  2479.                 vi_search_dir ))
  2480.  
  2481.     message( "%s%s", ( dir == SEARCH_FORWARD ? "/" : "?" ), search_hist())
  2482.  
  2483.     # execute search operator:
  2484.  
  2485.     vi_search( "", dir )
  2486.     xeq_op()
  2487. }
  2488.  
  2489.  
  2490.  
  2491.  
  2492. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  2493.  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  2494. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  2495.  
  2496. ### create the VI keymaps if they have not already been
  2497. #
  2498. local function create_vi_keymaps(){
  2499.     
  2500.     # Enable the keymap.  Subsequent mods to the keymap will persist 
  2501.     #     through the end of the session. 
  2502.     # Only one-time initialization should follow this point. 
  2503.  
  2504.     if( vi_command_keymap >= 0 ){
  2505.         current_keymap = vi_command_keymap 
  2506.         return
  2507.     }
  2508.  
  2509.     current_keymap = 
  2510.         vi_command_keymap = 
  2511.         create_keymap( empty_keymap )
  2512.  
  2513.     assign_key( "<Esc>",        "vi_beep" )
  2514.  
  2515.     # ex-mode commands
  2516.  
  2517.     assign_key( ":",        "ex_key" )
  2518.  
  2519.     # searches
  2520.     
  2521.     assign_key( "?",        "vi_search_key ?" )
  2522.     assign_key( "/",        "vi_search_key /" )
  2523.     assign_key( "n",        "vi_search_again" )
  2524.     assign_key( "N",        "vi_search_again" )
  2525.  
  2526.     # command count prefixes ("0" command handled by vi_digit)
  2527.  
  2528.     assign_key( "0",        "vi_digit" )  
  2529.     assign_key( "1",        "vi_digit" )
  2530.     assign_key( "2",        "vi_digit" )
  2531.     assign_key( "3",        "vi_digit" )
  2532.     assign_key( "4",        "vi_digit" )
  2533.     assign_key( "5",        "vi_digit" )
  2534.     assign_key( "6",        "vi_digit" )
  2535.     assign_key( "7",        "vi_digit" )
  2536.     assign_key( "8",        "vi_digit" )
  2537.     assign_key( "9",        "vi_digit" )
  2538.     
  2539.     # simple motions
  2540.  
  2541.     assign_key( "h",        "vi_left" )
  2542.     assign_key( "<Left>",        "vi_left" )
  2543.     assign_key( "j",        "vi_down" )
  2544.     assign_key( "<Down>",        "vi_down" )
  2545.     assign_key( "<Ctrl-N>",        "vi_down" )
  2546.     assign_key( "k",        "vi_up" )
  2547.     assign_key( "<Up>",        "vi_up" )
  2548.     assign_key( "<Ctrl-P>",        "vi_up" )
  2549.     assign_key( "l",        "vi_right" )
  2550.     assign_key( "<Right>",        "vi_right" )
  2551.     assign_key( "G",        "vi_goto" )
  2552.     assign_key( "g",        "vi_goto 1" )
  2553.     assign_key( "|",        "vi_goto_column" )
  2554.  
  2555.     assign_key( "<Home>",        "vi_goto_bol" )
  2556.     assign_key( "^",        "vi_skip_whitespace" )
  2557.     assign_key( "$",        "vi_goto_eol" )
  2558.     assign_key( "<End>",        "vi_goto_eol" )
  2559.     assign_key( "+",        "vi_next_line" )
  2560.     assign_key( "-",        "vi_prev_line" )
  2561.     assign_key( "<Enter>",        "vi_next_line" )
  2562.     assign_key( "%",        "vi_goto_matching" )
  2563.     assign_key( "<Ctrl-Right>",    "vi_W" )
  2564.     assign_key( "<Ctrl-Left>",    "vi_B" )
  2565.  
  2566.     assign_key( "<Keypad-Minus>",    "vi_prev_line" )
  2567.     assign_key( "<Keypad-+>",    "vi_next_line" )
  2568.  
  2569.     assign_key( "H",        "vi_H" )
  2570.     assign_key( "M",        "vi_M" )
  2571.     assign_key( "L",        "vi_L" )
  2572.     assign_key( "<Ctrl-PgUp>",    "goto_buffer_top" )    # extension!
  2573.     assign_key( "<Ctrl-PgDn>",    "goto_buffer_bottom" )    # extension!
  2574.  
  2575.     # need work to be truly integrated with VI:
  2576.  
  2577.     assign_key( " ",        "vi_space" )
  2578.     assign_key( "#8",        "vi_bksp" )    # Ctrl-H or Backspace
  2579.     assign_key( "#9",        "vi_tab" )    # Ctrl-I or Tab
  2580.     assign_key( "<Shift-Tab>",    "vi_back_tab" )
  2581.     assign_key( "<Alt-Tab>",    "vi_back_tab" )
  2582.  
  2583.     # need work to be truly integrated with VI:
  2584.  
  2585.     # assign_key( "<Keypad 4>",    "left" )
  2586.     # assign_key( "<Keypad 6>",    "right" )
  2587.  
  2588.     # word motions and other structured motions:
  2589.  
  2590.     assign_key( "w",        "vi_w" )
  2591.     assign_key( "b",        "vi_b" )
  2592.     assign_key( "e",        "vi_e" )
  2593.     assign_key( "W",        "vi_W" )
  2594.     assign_key( "B",        "vi_B" )
  2595.     assign_key( "E",        "vi_E" )
  2596.     assign_key( "(",        "vi_prev_sent" )
  2597.     assign_key( ")",        "vi_next_sent" )
  2598.     assign_key( "{",        "vi_prev_para" )
  2599.     assign_key( "}",        "vi_next_para" )
  2600.     assign_key( "[[",        "vi_prev_sect" )
  2601.     assign_key( "]]",        "vi_next_sect" )
  2602.  
  2603.     # "find" motions:
  2604.  
  2605.     assign_key( "f",        "vi_find" )
  2606.     assign_key( "F",        "vi_find" )
  2607.     assign_key( "t",        "vi_find" )
  2608.     assign_key( "T",        "vi_find" )
  2609.     assign_key( ";",        "vi_find_repeat" )
  2610.     assign_key( ",",        "vi_find_reverse" )
  2611.  
  2612.     # mark placement & motion:
  2613.  
  2614.     assign_key( "m",        "vi_place_mark" )
  2615.     assign_key( "'",        "vi_goto_mark" )
  2616.     assign_key( "`",        "vi_goto_mark" )
  2617.  
  2618.     # These window "motions" are known or believed not to be motions in 
  2619.     # the VI sense.  However they probably should clear pending operators 
  2620.     # (to avoid confusion, eg., on "dz-").
  2621.     # They also should disallow_number().
  2622.  
  2623.     assign_key( "<PgUp>",        "page_up" )        # not a motion
  2624.     assign_key( "<PgDn>",        "page_down" )        # not a motion
  2625.     assign_key( "<Ctrl-B>",        "page_up" )        # not a motion
  2626.     assign_key( "<Ctrl-F>",        "page_down" )        # not a motion
  2627.     assign_key( "<Ctrl-D>",        "scroll_down_half" )    # not a motion
  2628.     assign_key( "<Ctrl-U>",        "scroll_up_half" )    # not a motion
  2629.     assign_key( "<Ctrl-E>",        "scroll_down_1" )    # not a motion
  2630.     assign_key( "<Ctrl-Y>",        "scroll_up_1" )        # not a motion
  2631.     assign_key( "<Ctrl-Home>",    "goto_window_top" )
  2632.     assign_key( "<Ctrl-End>",    "goto_window_bottom" )
  2633.  
  2634.     assign_key( "z<Enter>",        "scroll_window_top" )
  2635.     assign_key( "z.",        "scroll_window_middle" )
  2636.     assign_key( "z-",        "scroll_window_bottom" )
  2637.  
  2638.     # misc. edits
  2639.     
  2640.     assign_key( "i",        "vi_i" )
  2641.     assign_key( "a",        "vi_a" )
  2642.     assign_key( "A",        "vi_A" )
  2643.     assign_key( "I",        "vi_I" )
  2644.     assign_key( "o",        "vi_o" )
  2645.     assign_key( "O",        "vi_O" )
  2646.  
  2647.     assign_key( "r",        "vi_r" )
  2648.     assign_key( "R",        "vi_R" )
  2649.     assign_key( "s",        "vi_s" )
  2650.     assign_key( "S",        "vi_S" )
  2651.  
  2652.     assign_key( "C",        "vi_C" )
  2653.     assign_key( "D",        "vi_D" )
  2654.     assign_key( "J",        "vi_J" )
  2655.     assign_key( "x",        "vi_x" )
  2656.     assign_key( "X",        "vi_X" )
  2657.  
  2658.     assign_key( "~",        "vi_tilde" )
  2659.  
  2660.     # editing operators:
  2661.  
  2662.     assign_key( "d",        "vi_operator" )
  2663.     assign_key( "c",        "vi_operator" )
  2664.     assign_key( "\\<",        "vi_operator" )
  2665.     assign_key( ">",        "vi_operator" )
  2666.     assign_key( "!",        "vi_operator" )
  2667.     assign_key( "y",        "vi_operator" )
  2668.            
  2669.            
  2670.     # Yank, Put, Undo, Repeat, Retrieve:
  2671.            
  2672.     assign_key( "\"",        "vi_bufid_key" )
  2673.     assign_key( "Y",        "vi_Y" )
  2674.     assign_key( "p",        "vi_p" )
  2675.     assign_key( "P",        "vi_P" )
  2676.            
  2677.     assign_key( ".",        "vi_again" )
  2678.     assign_key( "u",        "vi_undo" )
  2679.     assign_key( "U",        "vi_undo_line" )
  2680.  
  2681.  
  2682.     # getting out & misc:
  2683.  
  2684.     assign_key( "<Alt-Q>",        "quit" )
  2685.     assign_key( "ZZ",        "write_and_exit" )
  2686.  
  2687.     assign_key( "<Ctrl-L>",        "scroll_left_1" )
  2688.     assign_key( "<Ctrl-R>",        "scroll_right_1" ) # inexact but appropriate substitution
  2689.     assign_key( "<Ctrl-G>",        "ex_show_file" )
  2690.  
  2691.     # extensions in common with native keymap:
  2692.  
  2693.     assign_key( "<Ins>",        "vi_P" )
  2694.     assign_key( "<Del>",        "vi_yank 1" )
  2695.     assign_key( "<Alt-Minus>",    "delete_buffer_key" )
  2696.  
  2697.     assign_key( "<Ctrl-A>",        "optional_function display_ascii_table" )
  2698.     assign_key( "<Ctrl-Z>",        "system" )
  2699.  
  2700.     # Function key combinations & other useful native bindings:
  2701.  
  2702.     assign_key( "<F1>",        "prev_window" )
  2703.     assign_key( "<F2>",        "next_window" )
  2704.     assign_key( "<F3>",        "prev_buffer_key" )
  2705.     assign_key( "<F4>",        "next_buffer_key" )
  2706.     assign_key( "<F5>",        "vi_search_key ?" )
  2707.     assign_key( "<F6>",        "vi_search_key /" )
  2708.     assign_key( "<F7>",        "record_key" )
  2709.     assign_key( "<F8>",        "playback" )
  2710.     assign_key( "<F9>",        "system_key" )
  2711.     assign_key( "<F10>",        "invoke_function" )
  2712.  
  2713.     assign_key( "<Alt-F1>",        "split_window_horizontal" )
  2714.     assign_key( "<Alt-F2>",        "split_window_vertical" )
  2715.     assign_key( "<Alt-F3>",        "make_window" )
  2716.     assign_key( "<Alt-F4>",        "delete_tiled_window" )
  2717.     assign_key( "<Alt-F5>",        "vi_search_again ?" )
  2718.     assign_key( "<Alt-F6>",        "vi_search_again /" )
  2719.     assign_key( "<Alt-F7>",        "learn_key" )
  2720. #    assign_key( "<Alt-F8>",        )
  2721. #    assign_key( "<Alt-F9>",        )
  2722.     assign_key( "<Alt-F10>",    "compile_buffer" )
  2723.  
  2724.     assign_key( "<Shift-F1>",    "smaller_window" )
  2725.     assign_key( "<Shift-F2>",    "larger_window" )
  2726.     assign_key( "<Shift-F3>",    "organize_windows" )
  2727.     assign_key( "<Shift-F4>",    "organize_buffers" )
  2728. #    assign_key( "<Shift-F5>",    )
  2729. #    assign_key( "<Shift-F6>",    )
  2730.     assign_key( "<Shift-F7>",    "display_errors" )
  2731.     assign_key( "<Shift-F8>",    "goto_next_error" )
  2732. #    assign_key( "<Shift-F9>",    )
  2733. #    assign_key( "<Shift-F10>",    )
  2734.  
  2735. #    assign_key( "<Ctrl-F1>",    )
  2736. #    assign_key( "<Ctrl-F2>",    )
  2737. #    assign_key( "<Ctrl-F3>",    )
  2738. #    assign_key( "<Ctrl-F4>",    )
  2739. #    assign_key( "<Ctrl-F5>",    )
  2740. #    assign_key( "<Ctrl-F6>",    )
  2741. #    assign_key( "<Ctrl-F7>",    )
  2742. #    assign_key( "<Ctrl-F8>",    )
  2743. #    assign_key( "<Ctrl-F9>",    )
  2744. #    assign_key( "<Ctrl-F10>",    )
  2745.  
  2746.     assign_key( "<Alt-A>",        "set_exclusive_mark" )
  2747.     assign_key( "<Alt-B>",        "buffer_list" )
  2748.     assign_key( "<Alt-C>",        "copy_to_scrap_key" )
  2749.     assign_key( "<Alt-D>",        "native_delete" )
  2750.     assign_key( "<Alt-E>",        "edit_file_key" )
  2751.     assign_key( "<Alt-F>",        "display_filename" )
  2752.     assign_key( "<Alt-G>",        "goto_line_or_mark" )
  2753.     assign_key( "<Alt-H>",        "help" )
  2754.     assign_key( "<Alt-I>",        "toggle_insert_mode" )
  2755.     assign_key( "<Alt-J>",        "vi_J" )
  2756.     assign_key( "<Alt-K>",        "delete_to_eol" )
  2757.     assign_key( "<Alt-L>",        "set_line_mark" )
  2758.     assign_key( "<Alt-M>",        "mark_matching" )
  2759.     assign_key( "<Alt-N>",        "next_buffer_key" )
  2760.     assign_key( "<Alt-O>",        "change_output_name" )
  2761.     assign_key( "<Alt-P>",        "wrap_paragraph" )
  2762.     assign_key( "<Alt-Q>",        "done" )
  2763.     assign_key( "<Alt-R>",        "read_file_key" )
  2764.     assign_key( "<Alt-S>",        "search_forward" )
  2765.     assign_key( "<Alt-T>",        "replace_forward" )
  2766.     assign_key( "<Alt-U>",        "undo" )
  2767.     assign_key( "<Alt-V>",        "print_version" )
  2768.     assign_key( "<Alt-W>",        "write_block_key" )
  2769.     assign_key( "<Alt-X>",        "done" )
  2770.     assign_key( "<Alt-Y>",        "redo" )
  2771.     assign_key( "<Alt-Z>",        "system" )
  2772.  
  2773.     #  Alt-digit codes:
  2774.  
  2775.     assign_key( "<Alt-1>",        "place_bookmark 1" )
  2776.     assign_key( "<Alt-2>",        "place_bookmark 2" )
  2777.     assign_key( "<Alt-3>",        "place_bookmark 3" )
  2778.     assign_key( "<Alt-4>",        "place_bookmark 4" )
  2779.     assign_key( "<Alt-5>",        "place_bookmark 5" )
  2780.     assign_key( "<Alt-6>",        "place_bookmark 6" )
  2781.     assign_key( "<Alt-7>",        "place_bookmark 7" )
  2782.     assign_key( "<Alt-8>",        "place_bookmark 8" )
  2783.     assign_key( "<Alt-9>",        "place_bookmark 9" )
  2784.     assign_key( "<Alt-0>",        "place_bookmark 10" )
  2785.  
  2786.     assign_mouse_buttons()
  2787.  
  2788.     # insert-mode keymap
  2789.  
  2790.     vi_insert_keymap = current_keymap = create_keymap( ascii_keymap )
  2791.  
  2792.     assign_key( "<Esc>",        "process_end" )
  2793.     assign_key( "#27",        "process_end" )        # Esc or Ctrl-]
  2794.     assign_key( "<Ctrl-C>",        "vi_beep" )
  2795.     assign_key( "<Ctrl-@>",        "vi_reinsert" )
  2796.     assign_key( "#10",        "vi_ai_nl" )        # Ctrl-J or Ctrl-Enter
  2797.     assign_key( "#13",        "vi_ai_cr" )        # Ctrl-M or Enter
  2798.     assign_key( "<Bksp>",        "vi_insert_bs" )
  2799.     assign_key( "<Ctrl-H>",        "vi_insert_bs" )
  2800.     assign_key( "<Ctrl-W>",        "vi_insert_dB" )
  2801.     assign_key( "<Ctrl-U>",        "vi_insert_cancel" )
  2802.     assign_key( "<Ctrl-V>",        "vi_insert_quoted" )
  2803.     assign_key( "<Ctrl-D>",        "vi_insert_unindent" )
  2804.     assign_key( "<Ctrl-T>",        "goto_next_tab" )
  2805.     assign_key( "<F1>",        "expand_template" )
  2806.     assign_key( "<F2>",        "expand_template" )
  2807. }
  2808.