home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 5 Edit
/
05-Edit.zip
/
p2demo21.exe
/
PEL
/
VI.PEL
< prev
next >
Wrap
Text File
|
1995-04-10
|
151KB
|
5,785 lines
# $Header: P:\source\wmacros\vi.pev 1.74 10 Apr 1995 17:08:00 NOBLE $
## $Tabs:4 7$
##############################################################################
#
# Compuware Corporation
# 31440 Northwestern Highway
# Farmington Hills, Michigan 48334-2564
#
# This source code listing contains information that is
# proprietary to Compuware Corporation and may not be copied
# duplicated, translated, transmitted, stored, retrieved
# or in any manner or by any method conveyed or disclosed
# to a third party or parties without express written
# permission from Compuware Corporation.
#
#
##############################################################################
#### $Workfile: vi.pel $: vi support keymaps and macros
#### VI emulation mode release notes:
#
# EXTENSIONS:
#
# This editor is capable of editing multiple files at once. Thus it's
# unnecessary to write the current file before switching to a new one,
# nor is it necessary to process multiple files in strict sequential
# order. ":n" switches to next buffer without writing file;
# and ":e file" loads a new file or
# switches to an existing buffer by that name -- all without requiring
# files to be written at each step. The unnamed put buffer's contents
# are not lost when buffer changes, thereby allowing cuts and pastes
# between buffers without writing to intermediate files.
#
# This implementation allows empty files and files with incomplete last
# lines to be edited. This is in contrast with VI, which prohibits
# incomplete last lines and insists that there exist at least a single
# newline character in the file.
#
# This implementation provides multiple windows. Each window may view
# different parts of the same file or different files.
#
# The mouse can be used for all window operations, and for making text
# selections. Vi operators apply to the selected text, if any, in lieu
# of a vi motion. (Make a selection, then type "dd" to delete it.) The
# mouse allows column selections in addition to character- and line-
# oriented ones, however, line commands such as dd, yy, << and >> will
# treat all selections as line selections.
#
# vi-style undo has been augmented by PREDITOR's unlimited undo and redo.
# <Alt-U> undoes and <Alt-Y> redoes. Also, vi's "U" command has been
# extended to toggle between the fully edited and undone versions of
# the current line, like "u" does for a single edit.
#
# This implementation always portrays a 100% accurate representation
# of the text in the buffer. Standard vi employed "@" and "$" symbols
# to indicate where text had been or would be deleted while minimizing
# screen updates. This kludge is unnecessary and unsupported in this
# implementation.
#
# Similarly, this editor is capable of scrolling horizontally, so long
# lines are not wrapped. The redraw commands are not needed, so
# <Ctrl-L> and <Ctrl-R> scroll left and right, respectively.
#
# Search commands have additional history associated with them.
# Previous search patterns entered are accessable via vertical arrow
# keys. The default is the most recently entered search pattern, same
# as normal vi. One subtle difference is that the previous pattern is
# visible on the command line. Patterns histories may be accepted by
# pressing enter, or rejected by pressing escape. They also may be
# edited with the DEL, backspace, and horizontal arrow keys. Newly
# entered text overwrites the pattern unless an editing character has
# been pressed first. Thus "/<Enter>" and "/newpattern<Enter>" work
# exactly as before, although additional functionality is available as
# a superset of previous key sequences.
#
# DEL and ^U are used to cancel insert input on the current line. ^C
# terminates an insert. Ctrl-Break resets the editor.
#
# ^V is only needed to quote keys that have a special meaning in insert
# mode ( ^J ^M ^[ ^@ ^C ^D ^H ^W ^U ^V ). Other control
# keys will self-insert, though they still can be quoted with ^V.
#
# The various forms of ^D are not required in insert mode.
# Backspace unindents, and tab indents.
#
# The Ctrl-@ command in insert mode works for any previous insertion,
# and is not limited to 128 chars. If typed as NOT the first character,
# it inserts the previous insertion without terminating insert mode.
#
# Most of the native mode function- and Alt-keys are implemented for
# convenience. Thus <Alt-W><Alt-Q> works like :wq
#
# Other special features, such as error-fix, word-processing, and "Code
# Processing" support are available in VI mode via "<F10>command", and
# other native key bindings.
#
# Code Processing template inserting is bound to the <F2> key during
# insert mode.
#
# A new command is added: "g" goes to top of buffer if no address is
# specified, otherwise goes to the specified line, like "G".
#
#
# Known limitations of this emulation:
#
# Should allow cascaded searches: "/abc/;/def/"
#
# Positioning past EOL and EOF are sometimes off by one:
#
# "A^[" "Gj"
#
# Sentence motions should stop at paragraph and section breaks.
#
# The ":l" command and "l" suffix to ":g" and ":s" should work
# differently from "p"
#
#
# TODO LIST (to be implemented next):
#
# should use VI's regex syntax instead of our own
#
# ":s" and ":g" commands should accept multi-line input
#
# ":G" and ":V" commands ( :g or :v with prompt ).
#
# should prohibit motion past EOL of last line
#
# should prohibit having 0 lines in buffer
#
#
# OMISSIONS (likely never to be implemented):
#
# Several vi commands take a count to adjust the window size. Presently
# all such counts are ignored, and, given the more robust windowing
# capabilities, they are unlikely ever to be implemented. In some
# cases, like searches, the counts may be changed to repeat counts.
# Commands I know about (purportedly all "large motion" commands)...
#
# "z<num>." (change window size to <num>)
#
# ^D ^U ^F ^B / ? [[ ]] ` and '
#
# maybe: ^E ^Y n N
#
# # -- macro character -- substitute for FN keys on keyboards w/out
#
# @ macro commands
#
# :n filename... command (use :e instead) -- can manually add or delete
# from buffer list, but can't replace it. Not meaningful,
# since we are a multi-buffer editor.
#
# nO to open n lines -- no way to "open" lines. text is always 100%
# accurate re the buffer being edited
#
# lisp mode commands: "(", ")", "{", "}", "="
#
####
### local variables
## vi keymaps:
local vi_command_keymap = -1
local vi_insert_keymap = -1
local default_vi_command_keymap # copy of command mode keymap used when vi.pel is recompiled
local default_vi_insert_keymap # similarily for insert mode keymap
## vi constants:
local DEFAULT_BID = "1" # default buffer id if not explicitly spec'd
local LINE_BUFFER = 0x80000 # user buffer flag for line mode buffers
local AND_DELETE = 1 # optional arg to vi_yank()
local DEFAULT_ONE = 1 # optional arg to ex_ and select_range()
#local DONT_DELETE = 0 # optional arg to vi_yank()
local SEARCH_HIST = "SEARCH" # history index for "/" and "?" commands
local NEWLINE_BEFORE = 1 # insert flag -- put newline before or
local NEWLINE_AFTER = 2 # -- after current line
local ESC = 0x011B # escape key
local MOVE = 1 # move-mode argument to ex_copy
local COPY = 0 # copy-mode argument to ex_copy
local VI_EXPAND_TEMPLATE_KEY = "<F2>"
local VI_NEXT_FIELD_KEY = "<Ctrl-Enter>"
#local vi_exact_mark = "`" # syntax for exact mark motion
local vi_line_mark = "'" # syntax for line mark motion
# block of reserved marks (ugh!!)
local vi_major_mark = 909 # place from which last major motion moved
local vi_base_mark = 910 # named marks -- first is "''" / ""
local DONT_MARK = 2 # op_type_2b() arg to suppress mark_context()
local MAJOR_MARK = 1 # op_type_2b() arg mark major motion
#local MAGIC = 1 # 1 if :e! file should delete current
# buffer before starting new file; 0 otherwise
local READWRITE = 2 # read/write mode for fopen
local END_FILE = 2 # end of file mode for fseek
local BOB = 908 # beginning of buffer bookmark
local B1 = 907 # marks one end of text to be appended to a file
local B2 = 906 # marks the other end
local in_ex_mode = 1 # vi mode flag, 1 = command mode, 0 = insert mode
## vi emulation internal states:
local pending_operator # name of pending operator, if any
local number_register = 0 # number register used to repeat commands
local option_disable[] # array of disable flags for :set options
local option_value[] # value associated with :set option
local recursion_count[] # recursion protector for :map command
local insert_macro_table[] # table of inert mode macros for :map
local command_macro_table[] # table of command mode macros for :map
local covered_command_macro_body[] # record of overwritten macros
local covered_insert_macro_body[]
local covered_insert_macro[]
local covered_command_macro[]
local templates[] # abbreviations for :abb mode
local original_com # original version of command string
local save_command # pattern part of last :s command for replay
local prev_old # previous match pattern for :s command
local prev_new # previous substitution string for :s command
local last_buffer # previous buffer for :e #
local vi_read_name # file name in last read command
local in_glob # executing a :g or :v command
local browser_bid = 0 # buffer containing ":" command's "printed" output
local browser_keymap = -1 # keymap for browser process
local confirmer_bid = 0 # buffer for confirmer process
local confirmer_keymap = -1 # keymap for confirmer process
local buf_id # selected get/put buffer
local buf_id_map # maps buf_id names to buffer handles
local vi_search_dir # previous search direction for search_again
local vi_search_flags # vi search flags
local vi_find_com # last find command, if any
local vi_find_char # last find character, if any
# address ranges for ":" commands:
local ex_addr1 # first address if any
local ex_addr2 # second address if any
local ex_addresses = 0 # count of addresses supplied
local ex_com # command string being parsed
local line_search # signal from ex_address() to vi_search_key()
# state vars for vi-style undo and "."/again:
local undo_baseline # undo indicies to undo previous command
local redo_baseline # undo indicies to undo previous command
local undo_line_baseline # undo indicies to undo all changes to...
local undo_line # ...the current line (remembered here)
local again_string # playback string to re-execute last command
local tmp_again # interim version of again_string
local playing_again = 0 # flag for insert, et al.
local prev_number_register = 1 # previous repeat count
local prev_buf_id # previous buf id
local prev_mark_id # previous mark id
local vi_r_ch # previous "r" command replacement string
local insert_buffer # buffer at start of insert mode
local insert_window # window at start of insert mode
## vi mode user-visible external states, flags and modes
local clipboard_only = 1 # Do not use vi numbered delete registers
# much improved performance for cuts/pastes
local last_yank_type = 0 # Used when pasting from system clipboard. Set
# to LINE_BUFFER when yanking lines.
local bs_over_ai = 1 # allow bs over ai in insert mode
# (extension to standard vi)
local mouse_handlers[] # prexisting mouse handlers overriden by insert mode
### vi emulation
#
## vi -- enter vi mode
#
function vi()
{
if ( emulation_mode == "vi" )
return;
emulation_mode = "vi"
execute_event_handler( EVENT.EMULATION_CHANGED );
# Misc. items...
visible_end_buffer = default_visible_end_buffer = ""
visible_virtual_lines = default_visible_virtual_lines = "~"
# turn on prompts on statusbar
status_bar_flags = or(status_bar_flags, STB_PROMPTS)
window_page_overlap = 2
search_flags = vi_search_flags = SEARCH_REGEX \
+ SEARCH_MAXIMAL_MATCH \
+ SEARCH_WRAPS \
+ SEARCH_ADVANCE \
+ SEARCH_FORWARD \
+ SEARCH_CASE
vi_check_flags()
if (!editor_running)
attach_event_handler( EVENT.EDITOR_RUNNING, "vi_setup")
else
vi_setup()
}
function vi_setup()
{
# message("begin vi_setup")
delete_event( EVENT.EDITOR_RUNNING, "vi_setup")
attach_event_handler( EVENT.EMULATION_CHANGED, "vi_restore" )
attach_event_handler( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
attach_event_handler( EVENT.NEW_CURNT_WINDOW, "vi_new_current_win")
attach_event_handler( EVENT.ERROR, "vi_reset" )
# initialize all the ex :set options
ex_init_state()
assign_key( VI_EXPAND_TEMPLATE_KEY, "beep" )
assign_key( VI_NEXT_FIELD_KEY, "beep" )
set_expand_template_key( VI_EXPAND_TEMPLATE_KEY )
set_next_field_key( VI_NEXT_FIELD_KEY )
create_vi_command_keymaps()
vi_menu()
create_vi_insert_keymaps()
vi_command_mode(1) # vi starts in command mode
# message("vi_setup complete")
}
# change flags so that they are correct for vi
#
local function vi_check_flags()
{
# local mod_ro = and(buffer_flags, BUFFER_READ_ONLY+BUFFER_MODIFIED)
#
# buffer_flags = default_buffer_flags = or( default_buffer_flags,
# BUFFER_REAL_SPACE_ONLY + \
# BUFFER_WHOLE_LINES )
# buffer_flags = default_buffer_flags = or( default_buffer_flags,
# BUFFER_REAL_SPACE_ONLY)
# # restore those bits
# #
# buffer_flags = or(buffer_flags, mod_ro)
}
function vi_process_end()
{
# check to see if in insert mode, if so end process
#
#if (current_keymap == vi_insert_keymap )
if (!in_ex_mode || (current_keymap != vi_command_keymap) )
{
# since we use keymap as a check, change keymap so that we don't come in here
# again
#current_keymap = vi_command_keymap
# Note: this call to vi_command_mode() may be redundant but we'll do it
# here in case process_end() fails.
vi_command_mode(1) # 1 - force the command keymap if it's not current
}
process_end() # does nothing if not in process_begin
}
# handler invoked when changing out of vi mode
function vi_restore()
{
# clear buffer flags set during vi mode initialization
# buffer_flags = buffer_flags \
# = and( buffer_flags, \
# not(BUFFER_REAL_SPACE_ONLY + BUFFER_WHOLE_LINES ))
# reset misc. display attributes
visible_virtual_lines = default_visible_virtual_lines = ""
# remove this event handler
delete_event( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
delete_event( EVENT.NEW_CURNT_WINDOW, "vi_new_current_win")
delete_event( EVENT.EMULATION_CHANGED, "vi_restore" )
delete_event( EVENT.ERROR, "vi_reset" )
restore_id()
}
## entering vi command mode
#
# This gets called at the conclusion of insert mode, after all errors,
# after all "operators", and after each "ex" command.
#
local function vi_command_mode(force_keymap)
{
# reset to normal state
if (!in_ex_mode)
{
in_ex_mode = 1
# if (!handler_active(EVENT.LMOUSE_DRAG, function_id("lmouse_drag")))
# attach_event_handler(EVENT.LMOUSE_DRAG, function_id("lmouse_drag"))
# if (!handler_active(EVENT.MOUSE_LEFT_DOWN, function_id("mouse_left_down")))
# attach_event_handler(EVENT.MOUSE_LEFT_DOWN, function_id("mouse_left_down"))
# if (!handler_active(EVENT.MOUSE_LEFT_UP, function_id("mouse_left_up")))
# attach_event_handler(EVENT.MOUSE_LEFT_UP, function_id("mouse_left_up"))
message( "[ Command mode ]" )
}
if (force_keymap && current_keymap != vi_command_keymap)
{
if (!vi_keymap_valid(vi_command_keymap))
{
create_vi_command_keymaps()
}
current_keymap = vi_command_keymap
}
# calling use_number() overwrites prev num
number_register = 0
buf_id = DEFAULT_BID
in_glob = 0
op_flush()
}
## indicate a common error
local function vi_error( s )
{
vi_warning()
pending_operator = "" # cancel any pending operator
#vi_command_mode()
delete_event( EVENT.ERROR, "vi_reset" )
vi_reset()# reset to command mode
attach_event_handler( EVENT.ERROR, "vi_reset" )
error( s ) # cause longjump to editor top-level
# ( "never" returns )
}
## errors e.g. within insert mode must return
local function vi_warning( s )
{
if ( s )
warning( s )
else if ( ! option_disable[ "errorbells" ] )
beep()
# if ( ! option_disable[ "errorbells" ] && pause_on_error )
# beep()
}
function vi_redo_index()
{
return redo_index()
}
# kludge to force accurate undo_index within macros
function vi_undo_index()
{
# record( 1 )
# record( 0 )
return undo_index()
}
function establish_baseline()
{
undo_baseline[ current_buffer ] = vi_undo_index()
redo_baseline[ current_buffer ] = vi_redo_index()
if ( undo_line[ current_buffer ] != current_line )
{
undo_line[ current_buffer ] = current_line
undo_line_baseline[ current_buffer ] = vi_undo_index()
}
}
function vi_undo_redo( baseline )
{
local new, old
if ( current_buffer in baseline )
{
new = baseline[ current_buffer ]
baseline[ current_buffer ] = old = vi_undo_index()
if ( new < old )
undo( new )
else
redo( redo_baseline[ current_buffer ] )
}
else
vi_error()
}
function vi_undo()
{
vi_undo_redo( undo_baseline )
}
function vi_undo_line()
{
if ( undo_line[ current_buffer ] == current_line )
vi_undo_redo( undo_line_baseline )
}
function vi_again()
{
local i, ch
if ( !again_string )
vi_error()
if ( prev_buf_id ~ /[1-8]/ )
prev_buf_id = chr( ord( prev_buf_id ) + 1 )
buf_id = prev_buf_id
if ( !number_register )
number_register = prev_number_register
playing_again = 1
playback( again_string )
playing_again = 0
}
# setup for a single-char command, like "Y" or "~".
#
# Generate an error if there is a pending operator.
# Record the appropriate information for undo and again.
# Special case "single-char" commands like "[[" and "]]" must
# manually insert an extra record_op()
local function op_type_1()
{
operator_disallowed()
prev_number_register = number_register
record_op()
establish_baseline()
op_wrap()
}
# setup for the first of a two-char operator command, like "dW" or "yy":
#
# Record the appropriate information for undo and again.
local function op_type_2a()
{
prev_number_register = number_register
record_op()
establish_baseline()
}
# setup for a motion that might be paired with a two-char operator.
# like "!" or ">w" (may or may not be paired with an operator):
#
# Record the appropriate information for undo and again.
local function op_type_2b( context_type )
{
if ( pending_operator )
prev_number_register = number_register
if ( tmp_again )
record_op()
mark_context( context_type )
}
# wrap-up for the successful completion of any command
local function op_wrap()
{
if ( tmp_again )
{
prev_buf_id = buf_id
again_string = tmp_again
tmp_again = ""
}
}
# cancel command history accumulated thus far
local function op_flush()
{
tmp_again = ""
}
# record a key into the again string
local function record_op( key )
{
if ( !argcount() )
key = current_key
if ( key == 0 )
key = key_to_int("<Ctrl-@>")
tmp_again = tmp_again chr( key ) chr( shiftr( key, 8 ))
}
## a numeric count may preceed most commands.
#
# leading zeros are illegal ("0" is a command, executed here).
# no count means 1.
# the count is reset after every command, whether the count is used or not.
# a copy of the previous count is maintained for vi_again
function vi_digit()
{
local key = and( current_key, 0xF )
if ( number_register )
number_register = number_register * 10 + key
else
if ( key )
number_register = key
else
vi_goto_bol()
# message("number_register = %d", number_register);
}
function vi_prompt( a, b, c, d )
{
local r
attach_event_handler( EVENT.INVALID_PCHAR, "vi_prompt_err" )
r = prompt_history( a, b, c, d )
delete_event( EVENT.INVALID_PCHAR, "vi_prompt_err" )
return r
}
# allow <Backspace> to cancel prompts:
function vi_prompt_err()
{
if ( prompt_response == "" && current_key == key_to_int("<Backspace>") )
ungetkey( ESC )
}
local function use_number()
{
local prev = number_register
if ( prev == 0 )
prev = 1
number_register = 0
return prev
}
local function disallow_number()
{
if ( number_register != 0 )
message( "Count ignored with this command." )
number_register = 0
}
local function vi_getc()
{
local ch
if (!playing_again && (ch = int_to_ascii(getkey())) && (ch != ASCII_ESC) )
return chr( ch )
else
vi_error()
}
### vi operators
# process operator command
#
# if first one and no selection then remember it pending next motion
# if first one and existing selection, execute operator
# if second one matches previous one, execute operator in line mode
# else error
function vi_operator()
{
local ch = chr( int_to_ascii( current_key )) # get operator
local selection
op_type_2a()
if ( pending_operator )
{
if ( ch == pending_operator )
{
# always operate on full lines
select_lines( use_number() )
xeq_op() # operate on lines
}
else
vi_error() # operator mismatch
}
else
{
pending_operator = ch # remember for later
if ( selection_type() )
{
# Convert possible mouse selection into a regular selection
selection = get_selection_info()
UnhighlightMouseSel()
cua_remove_selection(1)
restore_selection_info(selection)
# xeq_op() # !! can't do again # now we can!
}
}
}
local function operator_disallowed(){
tmp_again = "" # avoids problem w/vi_space, etc
# followed by "."
if( pending_operator )
vi_error() # never returns
}
# manually insert an operator
#
# usage: "pend_operator( op ); motion()"
#
local function pend_operator( op )
{
operator_disallowed()
pending_operator = op
}
# process pending operator, if any
#
# mode specifies the mode of the operation: line, normal, or inclusive
# if a region has already been selected, mode is ignored
local function xeq_op( mode )
{
local rt
local line
if ( pending_operator )
{
if ( !argcount() )
mode = NORMAL_SELECTION
op_wrap()
if ( ( rt = selection_type()) )
mode = rt
else
{
drop_anchor( mode )
goto_mark( vi_base_mark )
}
if ( pending_operator == "y" )
{
vi_yank()
#raise_anchor()
}
else if ( pending_operator == "d" )
{
vi_yank( AND_DELETE )
if( mode == LINE_SELECTION )
skip_whitespace()
}
else if( pending_operator == "c" )
{
vi_yank( AND_DELETE )
vi_insert( (mode == LINE_SELECTION) ? NEWLINE_BEFORE : 0 )
}
else if ( pending_operator == "<" )
{
# outdent_tabs()
outdent_lines()
raise_anchor()
}
else if ( pending_operator == ">" )
{
# indent_tabs()
indent_lines()
raise_anchor()
}
else if ( pending_operator == "!" )
{
vi_filter()
raise_anchor()
}
else
vi_error( "bad VI operator: " pending_operator )
pending_operator = ""
}
else
op_flush()
vi_command_mode()
}
### yank/put buffers & associated stuff
# select n lines, starting with the current
local function select_lines( n )
{
local target = current_line + n - 1
local selection
if (selection_type() != LINE_SELECTION)
{
if (selection_type())
{
# convert current selection into a line selection
selection = get_selection_info()
remove_selection()
n = (selection.endline - selection.startline) + 1
prev_number_register = n # set so vi_again will work correctly
goto_line(selection.startline) # move to 1st column of line
skip_whitespace()
}
save_position()
if ( n > 1 )
{
target = current_line + n - 1
current_line = target
if ( current_line != target )
{
# there were not enough lines in the file
restore_position(1)
vi_error() # never returns
}
}
# select from the end to the beginning, current position is then
# at the beginning of the selection
begin_selection( LINE_SELECTION )
restore_position(1)
}
}
# select n characters, n>0 => select towards EOL; n<0 => select towards BOL
# error if selection bypasses BOL or EOL
# n=0 => error
# do nothing if there is already a selection
local function select_chars( n )
{
local selection
if (!selection_type())
{
if ( n > 0 && current_line_length - current_line_offset >= n )
{
drop_anchor( NORMAL_SELECTION )
next_char( n )
}
else if ( (n < 0) && (current_line_offset >= -n) )
{
drop_anchor( NORMAL_SELECTION )
prev_char( -n )
}
else
vi_error()
}
else
{
selection = get_selection_info()
if (selection.startline != selection.endline)
vi_error()
else
{
n = marked_region_size()
prev_number_register = n # set so vi_again will work correctly
}
}
return n
}
local function move_down( n )
{
local target = current_line + n
#create_mark( TEMP_MARK )
save_position()
current_line = target
if ( current_line != target )
{
#goto_mark( TEMP_MARK )
restore_position(1)
vi_error()
}
else
restore_position(0)
}
# prompt for buffer id
function vi_bufid_key()
{
local ch = vi_getc()
# buffer id "@" is also used internally
if ( ch ~ /[a-zA-Z1-9]/ )
buf_id = ch
else
vi_error()
}
# returns a buffer id of a system buffer associated with the current buf_id
# if clear is true, the buffer returned is empty.
#
local function yank_bid( put, del )
{
local bid = tolower( buf_id )
local buf, i, b2, b1
if ( bid in buf_id_map ) # we're reusing an existing buffer:
{
if ( bid == DEFAULT_BID && del )
{
# save 9 most recent deletes:
if ( "9" in buf_id_map )
delete_buffer( buf_id_map[ "9" ])
for( i = 9; i > 1; i-- )
{
b2 = "" i
b1 = "" i - 1
if ( b1 in buf_id_map )
buf_id_map[ b2 ] = buf_id_map[ b1 ]
}
# and fall to the return to create the new one
}
else
{
if ( put || isupper( buf_id )) # upper=> append # !del ||
return buf_id_map[ bid ]
# we're reusing a bid and need it to be empty
# and we fall through to create an empty one
else
delete_buffer( buf_id_map[ bid ])
}
}
else if ( put )
vi_error( "Nothing has been put in buffer \"" buf_id )
return ( buf_id_map[ bid ] = create_buffer( "vi_yank_" bid, "", BUFFER_SYSTEM ))
}
# yank or delete the current selection into the current "put" buffer
#
# this is the yank command, and also used by other commands.
function vi_yank( del )
{
local prev = current_buffer
local line = ( selection_type() == LINE_SELECTION )
local end_line
local selection = get_selection_info()
local real_space
#local str = get_region_as_str()
# if line > 0, then we have a line selection, so set line to be the
# number of lines to be yanked
if (line)
line = selection.endline - selection.startline + 1
# must set last_yank_type to be used by paste if using clipboard only
last_yank_type = line ? LINE_BUFFER : 0
#restore_selection_info(selection)
if( del )
{
# Turn off REAL_SPACE_ONLY to prevent shorter lines that follow a deleted
# line from being padded out to the current cursor column.
real_space = and(buffer_flags, BUFFER_REAL_SPACE_ONLY)
buffer_flags = set_flag_bits(buffer_flags, BUFFER_REAL_SPACE_ONLY, 0)
delete_to_scrap()
if (line)
{
goto_bol()
skip_whitespace()
}
buffer_flags = set_flag_bits(buffer_flags, BUFFER_REAL_SPACE_ONLY, real_space)
}
else
copy_to_scrap()
raise_anchor()
if (!clipboard_only)
{
delete_event( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
current_buffer = yank_bid( 0, del )
if( line )
buffer_flags = or( buffer_flags, LINE_BUFFER )
else
buffer_flags = and( buffer_flags, not( LINE_BUFFER ))
insert_scrap()
#insert_string(str)
current_buffer = prev
attach_event_handler( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
}
# The following code deletes the last line of the buffer by removing
# the EOL character from the preceding line.
if (line && buffer_offset >= buffer_size)
{
end_line = current_line
prev_char()
if (current_line < end_line && read_buffer() == "")
delete_chars(1)
else
next_char()
}
message("%s%s%s%s to scrap", (line > 1 ? line " ": ""),
(line ? "Line" : "Block"),
(line > 1 ? "s " : " "),
(del ? "deleted" : "copied") );
return line
}
# put the current yank buffer's contents into the scrap buffer,
# and return true if the buffer was a "LINE" buffer.
#
# Most clients need to perform some fine positioning
# depending on the line status, before actually inserting the text.
# Hence the obscure nature of this function.
#
local function put_buffer_to_scrap()
{
local prev = current_buffer
local line_oriented
delete_event( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
current_buffer = yank_bid( 1, 0 )
goto_buffer_top()
drop_anchor()
goto_buffer_bottom()
copy_to_scrap()
raise_anchor()
line_oriented = and( buffer_flags, LINE_BUFFER )
current_buffer = prev
attach_event_handler( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
return line_oriented
}
function vi_Y() # yank lines or selected block
{
local temp = selection_type();
local n = use_number();
op_type_1()
if ( !temp )
select_lines( n )
vi_yank()
}
function vi_p() # put after cursor/line
{
local lnum, lines
disallow_number()
op_type_1()
lines = clipboard_only ? last_yank_type : put_buffer_to_scrap()
#if( (lines = put_buffer_to_scrap()) )
if( lines )
{
down()
goto_bol()
lnum = current_line
}
else
right()
insert_scrap()
if( lines )
vi_goto_line( lnum )
else
prev_char()
}
function vi_P() # put before cursor/line
{
local lnum, lines
op_type_1()
lines = clipboard_only ? last_yank_type : put_buffer_to_scrap()
#if( (lines = put_buffer_to_scrap()) )
if( lines )
{
goto_bol()
lnum = current_line
}
insert_scrap()
if( lines )
vi_goto_line( lnum )
else
prev_char()
}
### insert mode and auxiliary operations
local insert_text # copy of text of previous insert
local insert_start # start offset of insert
local insert_start_line # start line of insert
local insert_backstop # backstop of this line; cancel deletes back to here
local insert_indent # flag, true <=> backstop follows auto-indent text
# set insert_backstop and insert_indent
local function set_backstop( ai )
{
insert_indent = ai
insert_backstop = buffer_offset
}
local function vi_reset_insert()
{
set_backstop( 0 )
insert_text = ""
# remember starting position:
insert_start = buffer_offset
insert_start_line = current_line
}
function vi_insert_mouse_handler()
{
if ( event_id == EVENT.MOUSE_LEFT_DOWN ||
event_id == EVENT.MOUSE_RIGHT_DOWN )
{
mouse_left_down()
vi_reset_insert()
}
}
local function vi_override_events()
{
local event
local mouse_events[]
local mouse_drag_events[]
local exit_events[]
local dummy_handler_id = function_id( "vi_insert_mouse_handler" )
# store all mouse event handlers
mouse_events = save_and_remove_events( EVENT.MOUSE_LEFT_DOWN, EVENT.MOUSE_MID_CLICK2 )
mouse_drag_events = save_and_remove_events( EVENT.LMOUSE_DRAG, EVENT.RMOUSE_DRAG )
mouse_handlers = merge_arrays( mouse_events, mouse_drag_events )
# attach dummy handlers for all mouse events
for ( event in mouse_handlers )
attach_event_handler( event, dummy_handler_id )
}
local function vi_restore_events()
{
local event
local dummy_handler_id = function_id( "vi_insert_mouse_handler" )
# delete dummy handlers for all mouse events
for ( event in mouse_handlers )
delete_event( event, dummy_handler_id )
restore_events( mouse_handlers )
delete mouse_handlers
}
# all common text insertion functionality
#
# -- auto indents
# -- repeated inserts
# -- macro playback
# -- intra-line editing
local function vi_insert( newline )
{
local n, count, col, line, offset
local new_cb, new_cw
local insert_buffer_name
in_ex_mode = 0
count = use_number()
# insert requested newlines
# _BEFORE => before current line (e.g. "O", "S")
# _AFTER => after current line (e.g. "o")
# else => no newline
if ( newline == NEWLINE_BEFORE )
{
if ( current_line == 1 )
{
goto_bol()
insert_newline()
up()
}
else
{
up()
vi_insert_nl(newline)
}
# insert_text = "\n"
}
else if ( newline == NEWLINE_AFTER )
{
vi_insert_nl()
# insert_text = "\n"
}
if( playing_again )
{
if ( and(buffer_flags, BUFFER_OVERTYPE_MODE) )
delete_chars( length( insert_text ))
#warning("insert_text is: |%s|", insert_text)
reinsert_string( insert_text, newline)
}
else
{
# mark starting point for BS/CAN and remember starting position
vi_reset_insert()
# interpret insert keymap:
message( "[ Insert mode ]" )
if (current_keymap != vi_insert_keymap)
{
if (!vi_keymap_valid(vi_insert_keymap))
{
if (!vi_keymap_valid(default_vi_insert_keymap))
create_vi_insert_keymaps()
else
vi_insert_keymap = default_vi_insert_keymap
}
current_keymap = vi_insert_keymap
}
# save buffer and window we start in before process_begin
#
insert_buffer = current_buffer
insert_buffer_name = buffer_filename
insert_window = current_window
if (playingback)
return
vi_override_events()
process_begin()
# warning("Leaving Insert Mode")
vi_restore_events()
if (current_buffer != insert_buffer)
{
vi_reset(1)
return
}
# save the new buffer and window we may now be in
#
new_cb = current_buffer
new_cw = current_window
# change to the window then the buffer you were in before the process_begin
# to finish executing the following code
# if (window_valid(insert_window) && new_cw != insert_window)
# current_window = insert_window
if (insert_buffer && new_cb != insert_buffer)
next_buffer(insert_buffer_name, 0, 1)
# current_buffer = insert_buffer
# no longer in insert mode--erase status message
# message( "" )
# copy inserted text to insert_text (!!string length limit)
if (buffer_offset <= insert_start)
insert_text = ""
else if ( current_line == insert_start_line )
{
insert_text = insert_text \
read_buffer( insert_start - buffer_offset ) # read backwards
}
else
{
line = current_line - 1
offset = buffer_offset
goto_buffer_offset( insert_start )
# if the only text before the first line is whitespace, then include
# it in insert_text. This will allow reinsert_string() to properly
# handle auto_indents.
if ( !(read_buffer(-current_line_offset) ~ /[^ \t]/) )
goto_bol()
insert_text = insert_text read_buffer()
current_column = 0
while( current_line++ < line )
insert_text = insert_text "\n" read_buffer()
goto_buffer_offset( offset )
insert_text = insert_text "\n" read_buffer( -current_line_offset )
}
}
# process repeat count, if any
while( count-- > 1 )
reinsert_string( insert_text, newline)
# change to new window first, then the buffer we were in after process_begin
#
# if ( new_cw && (new_cw != current_window) )
# current_window = new_cw
if ( new_cb && (new_cb != current_buffer) )
current_buffer = new_cb
left() # move back one character after insert to better emulate real vi
vi_command_mode(1)
}
local function get_column_diff(str_a, str_b)
{
local diff
local end_offset
save_position()
goto_eol()
save_position()
insert_string("\n")
insert_string(str_a)
diff = current_column
goto_bol()
delete_to_eol()
insert_string(str_b)
diff -= current_column
end_offset = buffer_offset
restore_position(1)
delete_chars(end_offset - buffer_offset - 1)
restore_position(1)
return diff
}
##
# reinsert_string (text, newline)
# Reinsert previously inserted text, properly auto-indented. 'text' contains the
# text to insert which is usually the global 'insert_text' set by vi_insert().
# 'newline' will be non-zero if vi_insert() was called by the o or O commands. If
# it is non-zero, then vi_insert() has already inserted the first carriage return.
#
local function reinsert_string( text, newline)
{
local i, line, s, n
local prev_white
local curr_white
local diff
local dist
if (!auto_indent_mode)
insert_string(text)
else
{
n = split( text, line, "\n" )
for ( i = 1; i<= n; i++ )
{
s = line[ i ]
# if this is the first line of a muli-line insertion, and the original
# insertion was on a new line (o or O commands), then strip off the
# leading whitespace since we are already at the proper auto indent level.
if ( i == 1 && i < n && newline)
sub( /^[ \t]+/, "", s )
else if (i > 1 && match(line[i-1], /^[ \t]+/))
{
# subsequent lines must remove extra whitesspace already accounted for
# by auto indent
prev_white = substr(line[i-1], RSTART, RLENGTH)
if (!sub(prev_white, "", s))
{
# if however, this line is indented less than the previous one
# figure out how far back to move it
match(line[i], /^[ \t]+/)
curr_white = substr(line[i], RSTART, RLENGTH)
# get the actual column difference between the two lines
diff = get_column_diff(prev_white, curr_white)
# move back to the closest tab stop
while (diff > 0)
{
dist = distance_prev_tab
current_column -= dist
diff -= dist
}
# remove the extra whitespace from the line
delete_to_eol()
# remove leading whitespace from string since we're at indent level
sub(curr_white, "", s)
}
}
insert_string( s )
if ( i < n )
vi_insert_cr()
} # end for(lines)
}
}
## following functions are bound to keys for insert mode actions
# cancel input thus far on the current line
function vi_insert_cancel()
{
local n = buffer_offset - insert_backstop
if( n > 0 )
{
prev_char( n )
delete_chars( n )
}
}
# backspace in insert mode
#
# The standard VI BS function can only bs over user-generated chars,
# and in particular cannot bs over auto-indent text. This
# implementation allows backspacing over a-i text. To disallow it
# (and more faithfully emulate VI) set the bs_over_ai variable to 0.
#
function vi_insert_bs()
{
if ( insert_backstop < buffer_offset )
backspace()
else if ( bs_over_ai &&
insert_indent &&
(current_line_offset < buffer_offset) )
{
backspace()
set_backstop( insert_indent )
}
else
vi_warning()
}
# Standard VI ^D/unindent and ^T/reindent functions: ?? incomplete
function vi_insert_unindent()
{
local ch = read_buffer( -1 )
local start_offset
if ( ch == "^" || ch == "0" )
{
insert_backstop = buffer_offset - current_line_offset
vi_insert_cancel()
# ^D should save this new indent for subsequent lines
}
else if ( (ch == "\t" || ch == " ") &&
!(read_buffer(-current_line_offset) ~ /[^ \t]/) )
{
if (ch == "\t")
backspace()
else
{
start_offset = buffer_offset
current_column = current_column - distance_prev_tab
delete_chars(start_offset - buffer_offset)
}
insert_backstop = buffer_offset
if (insert_start_line == current_line)
vi_reset_insert()
}
else
vi_warning()
}
function vi_insert_reindent()
{
if ( !(read_buffer(-current_line_offset) ~ /[^ \t]/) )
insert_string( "\t" )
else
vi_warning()
}
# "dB", limited by scope of current insert
function vi_insert_dB()
{
local patt = "[^a-z0-9A-Z_ \t]+" word_patt
local sflags = SEARCH_REGEX + SEARCH_MAXIMAL_MATCH \
+ SEARCH_ADVANCE + SEARCH_BACKWARD \
+ and( search_flags, SEARCH_CASE ) \
+ and( search_flags, SEARCH_WRAPS )
#+ and( vi_search_flags, SEARCH_CASE ) \
#+ and( vi_search_flags, SEARCH_WRAPS )
if( buffer_offset <= insert_backstop )
vi_warning()
else
{
drop_anchor( NORMAL_SELECTION )
#if( search( WORD_patt, sflags ))
if( search( patt, sflags ))
{
if ( buffer_offset < insert_backstop )
goto_buffer_offset( insert_backstop )
delete_chars()
#warning("after delete_chars")
}
raise_anchor()
}
}
function vi_reinsert()
{
local flag = ( insert_backstop < buffer_offset )
reinsert_string( insert_text )
if( !flag )
process_end()
# a more accurate implementation would beep if !flag
# and always process_end() the insert; this is an extension
}
function vi_insert_quoted()
{
local key
local ch
message( "Type ascii key to be inserted:" )
key = getkey()
ch = int_to_ascii(key)
if ( ch || key == key_to_int("<Ctrl-@>") )
{
insert_key( ch )
message( "" )
}
else
# error() would screw-up insert mode
warning( "Can't insert non-ascii keys." )
}
# normal newline text entry; implements auto-indenting if enabled.
function vi_insert_cr(newline)
{
local str
insert_string( "\n" )
if ( auto_indent_mode )
{
# if the command was O, use indenting from line below, else use line above
if (newline == NEWLINE_BEFORE)
next_line()
else
prev_line()
drop_anchor()
goto_bol()
# copy_to_scrap()
str = get_region_as_str()
raise_anchor()
if (newline == NEWLINE_BEFORE)
up()
else
down()
insert_string(str)
# insert_scrap()
}
set_backstop( auto_indent_mode )
}
# shift-enter -- goto EOL and enter
function vi_insert_nl(newline)
{
goto_eol()
vi_insert_cr(newline)
}
### Template Editing
#
#
# vi_expand_template() checks the buffer for an abbreviation,
# and replaces the abbreviation with the expansion
# if there is one
#
# Function is activated by the space, tab, or <Enter> keys
# in insertion mode.
function vi_expand_template()
{
local string, len, abbreviation, ok
for ( abbreviation in templates )
{
len = length( abbreviation )
string = read_buffer( - len -1 )
if ( ( ok = string ~ "^[ \t]" ) )
string = substr( string, 2 )
if ( ok || current_column == len + 1 )
{
if ( abbreviation == string )
{
current_column -= len
delete_chars( len )
insert_string( templates[ abbreviation ] )
}
}
}
if ( and( current_key, 0x00ff) == 13 )
vi_insert_cr()
else
insert_key()
}
### insertion commands
function vi_i()
{
op_type_1()
vi_insert()
}
function vi_I()
{
op_type_1()
skip_whitespace()
vi_insert()
}
function vi_a()
{
op_type_1()
if ( current_line_length > current_line_offset )
right()
vi_insert()
}
function vi_A()
{
op_type_1()
goto_eol()
vi_insert()
}
function vi_o()
{
op_type_1()
vi_insert( NEWLINE_AFTER )
}
function vi_O()
{
op_type_1()
vi_insert( NEWLINE_BEFORE )
}
### deletions
#
# all deletions delete_to_scrap() even if single character
function vi_X()
{
op_type_1()
select_chars( -use_number() )
vi_yank( AND_DELETE )
}
function vi_x()
{
op_type_1()
select_chars( use_number())
vi_yank( AND_DELETE )
if ( (current_line_length <= current_line_offset) &&
current_line_offset )
prev_char()
}
## more complex edits -- combinations of delete/insert
function vi_J()
{
local n = use_number()
local ins
op_type_1()
if ( n > 1 )
n -= 1
while ( n-- > 0 )
{
goto_eol()
while ( read_buffer( -1 ) ~ /[ \t]/ )
prev_char()
ins = ( read_buffer( -1 ) == "." ) ? " " : " "
drop_anchor()
next_line()
vi_yank( AND_DELETE )
if ( read_buffer( 1 ) != "(" )
insert_string( ins )
}
}
function vi_D()
{
op_type_1()
pend_operator( "d" )
vi_goto_eol()
}
function vi_C()
{
vi_D()
vi_insert()
}
function vi_R()
{
op_type_1()
buffer_flags = or( buffer_flags, BUFFER_OVERTYPE_MODE )
vi_insert()
buffer_flags = xor( buffer_flags, BUFFER_OVERTYPE_MODE )
}
function vi_r( )
{
local n
op_type_1()
n = use_number()
# limit scope to current line
if ( current_line_length - current_line_offset < n )
vi_error()
if ( !playing_again )
vi_r_ch = vi_getc()
if ( vi_r_ch == "\r" || vi_r_ch == "\n" )
vi_r_ch = "\n"
# number returned will be different than n if there is already a selection
n = select_chars( n )
vi_yank( AND_DELETE )
if ( vi_r_ch == "\n" )
{
while( n-- > 0 )
vi_insert_cr()
}
else
{
while( n-- > 0 )
insert_string( vi_r_ch )
if ( current_column > 1 )
prev_char()
}
}
function vi_S()
{
op_type_1()
select_lines( use_number())
vi_yank( AND_DELETE )
vi_insert( NEWLINE_BEFORE )
}
function vi_s()
{
op_type_1()
# This command is not needed, restore position is never called
# on it. I removed it because of a current bug in raise_anchor:
# it will restore the selection set by save_position, leaving
# the user an unwanted selection. DWM
# save_position();
select_chars( use_number())
vi_yank( AND_DELETE )
vi_insert()
}
function vi_tilde()
{
op_type_1()
select_chars( use_number())
reverse()
raise_anchor()
if ( (current_line_length <= current_line_offset) &&
current_line_offset )
prev_char()
}
### vi motion commands
local function vi_goto_line( line )
{
current_line = line
skip_whitespace()
}
function vi_goto( n )
{
op_type_2b( MAJOR_MARK )
if ( number_register )
n = use_number() # need a range check
else if( !n )
n = buffer_last_line
vi_goto_line( n )
xeq_op( LINE_SELECTION )
}
local vert_column
local vert_motion
# vi_arrow()
# Arrow key handler.
# which: 1 = up, 2 = down, 3 = left, 4 = right
function vi_arrow(which)
{
local n = 1
local remaining = current_line_length - (current_line_offset + 1)
local down_target
if (in_ex_mode)
{
n = use_number()
if (which < 1 || which > 4)
which = 1
if ( which == 1 && current_line <= n )
n = current_line
else if (which == 2 && (down_target = current_line + n) > buffer_last_line)
n = buffer_last_line - current_line
else if (which == 3 && current_line_offset < n )
n = current_line_offset
else if (which == 4 && remaining <= n )
n = remaining + (pending_operator == "" ? 0 : 1)
if (n < 1)
vi_error()
op_type_2b()
}
vi_move(which, n)
if (in_ex_mode)
xeq_op(which < 3 ? LINE_SELECTION : 0)
else
vi_reset_insert()
}
# vi_insert_arrow()
# Arrow key handler when in insert mode.
# which: 1 = up, 2 = down, 3 = left, 4 = right,
# 5 = home, 6 = end, 7 = page up, 8 = page down
function vi_insert_arrow(which)
{
if (which < 5)
vi_move(which, 1)
else if (which == 5)
home_key()
else if (which == 6)
end_key()
else if (which == 7)
page_up()
else if (which == 8)
page_down()
vi_reset_insert()
}
local function vi_move(dir, n)
{
if (dir < 3) # up or down
if ( prev_command != vert_motion )
vert_column = current_column
else
current_column = vert_column
if (dir == 1)
{
vert_motion = current_command
up( n )
vi_adjust_column()
}
else if (dir == 2)
{
vert_motion = current_command
move_down( n )
vi_adjust_column()
}
else if (dir == 3)
{
vert_motion = 0
prev_char( n )
}
else if (dir == 4)
{
vert_motion = 0
next_char( n )
}
}
function vi_space()
{
if ( selection_type() )
{
operator_disallowed()
if ( and(keyboard_flags, KB_ALT) )
outdent_columns()
else
indent_columns()
}
else
vi_arrow(4) # right
}
function vi_bksp()
{
if ( selection_type() )
{
operator_disallowed()
outdent_columns()
}
else
vi_arrow(3) # left
}
function vi_tab()
{
if ( selection_type() )
{
operator_disallowed()
if ( and(keyboard_flags, KB_ALT) )
outdent_lines()
#outdent_tabs()
else
indent_lines()
#indent_tabs()
}
else
vi_error()
}
function vi_back_tab()
{
if ( selection_type() )
{
operator_disallowed()
outdent_lines()
#outdent_tabs()
}
else
vi_error()
}
function vi_goto_column()
{
local n = use_number()
if ( current_line_width < n )
vi_error()
op_type_2b()
current_column = n
xeq_op()
}
function vi_H()
{
local n = use_number() - 1
op_type_2b( MAJOR_MARK )
goto_window_top()
if ( n >= window_text_height )
n = window_text_height - 1
down( n )
skip_whitespace()
xeq_op( LINE_SELECTION )
}
function vi_M()
{
disallow_number()
op_type_2b( MAJOR_MARK )
goto_window_middle()
skip_whitespace()
xeq_op( LINE_SELECTION )
}
function vi_L()
{
local n = use_number() - 1
op_type_2b( MAJOR_MARK )
goto_window_bottom()
if ( n >= window_text_height )
n = window_text_height - 1
up( n )
skip_whitespace()
xeq_op( LINE_SELECTION )
}
function vi_goto_bol()
{
disallow_number()
op_type_2b()
goto_bol()
xeq_op()
}
function vi_goto_eol()
{
local n = use_number()
op_type_2b()
if ( n > 1 )
down( n - 1 )
current_column = current_line_width # goto_eol()
xeq_op( current_line_width ? INCLUSIVE_SELECTION : NORMAL_SELECTION )
}
function vi_next_line()
{
local n = use_number()
op_type_2b()
next_line( n )
xeq_op( LINE_SELECTION )
}
function vi_prev_line()
{
local n = use_number()
op_type_2b()
prev_line( n )
xeq_op( LINE_SELECTION )
}
function vi_skip_whitespace()
{
local n = use_number()
op_type_2b()
skip_whitespace()
xeq_op()
}
function vi_goto_matching()
{
local n = use_number()
op_type_2b( MAJOR_MARK )
if ( read_buffer( 1 ) !~ /[(){}[\]]/ )
if ( match(read_buffer(), "[(){}[\\]]") )
next_char( RSTART - 1 )
else
vi_error()
goto_matching()
xeq_op( INCLUSIVE_SELECTION )
}
local function vi_adjust_column()
{
# if ( and( buffer_flags, BUFFER_POSITION_IS_VIRTUAL ) ||
# ( current_line_offset == current_line_length ) )
if ( current_line_offset >= current_line_length )
{
goto_eol()
if ( current_line_offset )
prev_char()
}
origPos = buffer_offset;
}
# Word, sentence, paragraph, and section motions.
# Different commands call vi_motion with one of the patterns below.
#
# Different dialects of vi implement subtle variations for certain combos:
#
# Commands..... SPE MKS Sun AIX
#
# !w >W etc. yes yes error error
#
# w W b B over \n skips skips stops error
# \n \n at \n
#
#
# add "|^[ \t]*$" to word_, WORD_, end_, and END_patt to make these
# motions treat newlines as words, ala Sun vi.
# the following search pattern would be better to use, but cursor placement
# seems to mess things up.
local word_patt = "[a-z0-9A-Z_]+"
local WORD_patt = "[^ \t]+"
#local end_patt = "[a-z0-9A-Z_]*[a-z0-9A-Z_]\\c"#|[^a-z0-9A-Z_ \t]*\\c[^a-z0-9A-Z_ \t]"
# The following 2 patterns are used by vi_e() to define a word. See the comment
# for the vi_e() function.
local end_patt = "[a-z0-9A-Z_]*\\c[a-z0-9A-Z_]"
local end_patt2 = "[^a-z0-9A-Z_ \t]*\\c[^a-z0-9A-Z_ \t]"
local END_patt = "[^ \t]*\\c[^ \t]"
#local END_patt = "[a-z0-9A-Z_]([ \t]+|$)"
#local sent_patt = "(\\.|!|\\?)[)\"'\\]]*(\t| |$|^)"
#local para_patt = "^$|^\\.PP$|^\\.NH|^\\.IP|^\\.LP|^\\.PP|^\\.QP|^\\.LI"
#local sect_patt = "^\\.NH|^\\.SH|^\\.H([ \t]|$)|^\\.HU|^\\{|^function|^local[ \t]+function|^global[ \t]+function|^\f"
local function vi_motion( patt, direction )
{
if ( direction )
return vi_forward_motion( patt )
else
return vi_backward_motion( patt )
}
local origPos
# this function is never used
#local function vi_save_pos()
#{
# origPos = buffer_offset;
#}
local function vi_forward_motion( patt )
{
local sflags = SEARCH_REGEX + SEARCH_MAXIMAL_MATCH + SEARCH_FORWARD + SEARCH_HIGHLIGHT
local count
origPos = buffer_offset;
search_count = 0
count = use_number()
op_type_2b( MAJOR_MARK )
save_position()
do
{
if ( !search( patt, sflags ) )
{
restore_position( 1 )
vi_error()
break
}
if ( buffer_offset == origPos )
{
# didn't move, so advance past current pattern
if ( !search_string_length )
break
goto_buffer_offset( origPos + search_string_length )
count++
}
else
origPos = buffer_offset
# WGN - Removed following to better emulate vi
#if ( patt == END_patt )
#right();
}
while ( --count > 0 )
restore_position( 0 )
}
local function vi_backward_motion( patt )
{
local sflags = SEARCH_REGEX +
SEARCH_MAXIMAL_MATCH +
SEARCH_FORWARD +
SEARCH_ONCE_PER_LINE
local count
local found
local line
local origPos = buffer_offset
count = use_number()
search_count = 0
op_type_2b( MAJOR_MARK )
save_position()
do
{
# advance forward to the match immediately preceeding the
# cursor position
#
found = FALSE
save_position()
for ( line = current_line; line; line-- )
{
goto_pos( line, 1 )
while ( search( patt, sflags ) && (buffer_offset < origPos ) )
{
found = TRUE
restore_position( 0 )
save_position()
goto_buffer_offset( buffer_offset + search_string_length )
}
if ( found )
{
restore_position( 1 )
origPos = buffer_offset
count--
break
}
}
} while ( count > 0 && line )
restore_position( 0 )
if ( !found )
{
restore_position( 1 )
vi_error()
}
}
function vi_w( patt )
{
local toLeft = 0;
if ( !argcount() )
# patt = word_patt
# WGN - Added 0-9 to following pattern to work like real vi
# patt = "[^a-zA-Z_ \t]+|" word_patt
patt = "[^a-z0-9A-Z_ \t]+|" word_patt
if ( pending_operator ~ /[ycd]/ )
patt = patt "|$"
if ( pending_operator == "c" )
vi_cw( patt )
else
{
vi_motion( patt, SEARCH_FORWARD )
xeq_op( NORMAL_SELECTION )
}
}
function vi_cw( patt )
{
local ch = read_buffer( 1 )
vi_motion( patt, SEARCH_FORWARD )
if ( ch !~ /[ \t]/ )
{
search( "[^ \t]\\c",
SEARCH_REGEX +
SEARCH_ADVANCE +
and( search_flags, SEARCH_CASE ) +
and( search_flags, SEARCH_WRAPS) )
#and( vi_search_flags, SEARCH_CASE ) +
#and( vi_search_flags, SEARCH_WRAPS) )
}
xeq_op( NORMAL_SELECTION )
}
function vi_W()
{
vi_w( WORD_patt )
}
## vi_e()
##
# Move to the end of the current word, or the end of the next word if already
# at the end of a word. For vi_e(), a word is either a string of letters,
# numbers and underscores, or a string of punctuation characters. Which
# definition of a word to use depends on the value of the next non-whitespace
# character.
function vi_e()
{
local char
local patt = end_patt
save_position()
next_char()
char = read_buffer(1)
while (char && isspace(char))
{
next_char()
char = read_buffer(1)
}
if (char && (char ~ end_patt2) )
patt = end_patt2
restore_position(1) # go back to where we started
vi_motion( patt, SEARCH_FORWARD )
xeq_op( INCLUSIVE_SELECTION )
}
## vi_E()
##
# Move to the end of the current word, or the end of the next word if already
# at the end of a word. For vi_E(), a word is either a string of
# non-whitespace characters.
function vi_E()
{
vi_motion( END_patt, SEARCH_FORWARD )
xeq_op( INCLUSIVE_SELECTION )
}
function vi_B()
{
vi_motion( WORD_patt, SEARCH_BACKWARD )
xeq_op( NORMAL_SELECTION )
}
function vi_b()
{
local toLeft;
local patt = "[^a-z0-9A-Z_ \t]+|" word_patt
vi_motion( patt, SEARCH_BACKWARD )
xeq_op( NORMAL_SELECTION )
}
function vi_prev_sent()
{
op_type_2b( MAJOR_MARK )
if (!prev_sentence(use_number()))
goto_buffer_top()
else
xeq_op( NORMAL_SELECTION )
}
function vi_next_sent()
{
op_type_2b( MAJOR_MARK )
if (!next_sentence(use_number()))
goto_buffer_bottom()
else
xeq_op( NORMAL_SELECTION )
}
function vi_prev_para()
{
op_type_2b( MAJOR_MARK )
if (!prev_paragraph(use_number()))
goto_buffer_top()
else
{
if ( pending_operator )
current_line--
xeq_op( LINE_SELECTION )
}
}
function vi_next_para()
{
op_type_2b( MAJOR_MARK )
if (!next_paragraph(use_number()))
goto_buffer_bottom()
else
{
if ( pending_operator )
current_line--
xeq_op( LINE_SELECTION )
}
}
function vi_prev_sect()
{
local ch = "["
op_type_2b( MAJOR_MARK )
if (!playing_again)
ch = vi_getc()
if (ch != "[")
vi_error()
else
{
if (!prev_section(use_number()))
goto_buffer_top()
if ( pending_operator && current_line > 1)
current_line--
# must update view to use window_first
update_current_view()
if (current_line > 1 && current_line == window_first)
{
save_position()
message("scrolling window")
scroll_up_1()
restore_position(1)
}
xeq_op( LINE_SELECTION )
}
}
function vi_next_sect()
{
local ch = "]"
local result = 0
local patt = section_pattern
op_type_2b( MAJOR_MARK )
if (!playing_again)
ch = vi_getc()
if (ch != "]")
vi_error()
else
{
if (pending_operator)
patt = section_end_pattern "|" patt
if (!next_section(use_number(), patt))
{
goto_buffer_bottom()
goto_bol()
}
else
if (pending_operator && (read_buffer(1) ~ section_pattern) )
current_line--
xeq_op( LINE_SELECTION )
}
}
## vi "find" commands
# fx skip forward to character "x"
# Fx skip backwards to character "x"
# tx skip forwards to just before character "x"
# Tx backwards just before "x"
# ; repeat last find command
# , execute last find command in reverse direction
function vi_find_repeat()
{
vi_find_it( vi_find_com )
}
function vi_find_reverse()
{
local com = toreverse( vi_find_com )
vi_find_it( com )
}
function vi_find()
{
vi_find_com = chr( current_key )
vi_find_it( vi_find_com, 1 )
}
local function vi_find_it( com, needChar )
{
local i, n
op_type_2b( MAJOR_MARK )
if ( needChar && !playing_again)
vi_find_char = vi_getc()
if ( !vi_find_char )
vi_error()
n = use_number()
while ( n-- > 0 )
{
# forwards
if ( islower( com ) )
{
i = index( substr( read_buffer(), 2), vi_find_char )
if ( i )
next_char( i - ( com == "t" ))
}
# backwards
else
{
i = bindex( read_buffer( - current_column ), vi_find_char )
if ( i )
prev_char( i - ( com == "T" ))
}
}
if ( !i )
vi_error()
xeq_op( islower( com ) ? INCLUSIVE_SELECTION : NORMAL_SELECTION )
}
# backwards index
# return the offset left from the end of the string of the rightmost
# instance of ch, if any, in s
local function bindex( s, ch )
{
local n = rindex( s, ch )
if ( n )
n = length( s ) - n + 1
return n
}
function vi_scroll_window()
{
local key
local c
disallow_number()
if ( !playing_again )
key = getkey()
if ( key == KEYCODE_KEYPAD_ENTER || key == KEYCODE_ENTER)
scroll_window_top()
else
{
c = chr(int_to_ascii(key))
if (c == ".")
scroll_window_middle()
else if (c == "-")
scroll_window_bottom()
else
vi_error()
}
}
## marks & mark motion
# translate a mark ascii name to a bookmark id
local function vi_mark_id( ch )
{
if ( !argcount())
if (!playing_again)
ch = vi_getc()
else
ch = prev_mark_id
prev_mark_id = ch
if ( ch ~ /[`']/ )
return vi_base_mark
if ( ch ~ /[a-zA-Z]/ )
return vi_base_mark + ord( toupper( ch )) - ord( "@" )
vi_error()
}
# record current position into "`" mark
# called by all motion commands to permit goto previous location,
# and to mark the range of vi operators
#
# context_type: 0 or Null => mark motion starting point
# 1 => mark non-relative motion starting point
# 2 => don't mark (for vi_place_mark)
local function mark_context( context_type )
{
if ( context_type == DONT_MARK )
return
if ( context_type == MAJOR_MARK )
create_mark( vi_major_mark )
create_mark( vi_base_mark ) #, current_line, current_column )
}
# m command -- places a mark here
function vi_place_mark()
{
operator_disallowed()
disallow_number()
record_op()
create_mark( vi_mark_id(), current_line, current_column )
}
function vi_goto_mark()
{
local lines = chr( int_to_ascii(current_key) ) == vi_line_mark
local mark
disallow_number()
record_op()
op_type_2b( DONT_MARK )
mark = vi_mark_id()
if ( mark == vi_base_mark )
swap_marks( vi_major_mark, 0 )
else
{
if ( !mark_defined( mark ))
vi_error()
mark_context( MAJOR_MARK )
goto_mark( mark )
}
if ( lines )
skip_whitespace()
xeq_op( lines ? LINE_SELECTION : NORMAL_SELECTION )
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
### ex mode
#
# ex_key: prompt the user; call x_mode if something entered
#
# ex_mode: handle non-globable prefix and suffix
#
# ex_mode1: called recursively by ex_glob()
#
# Bug: this version ignores superflurous stuff after a valid command
function ex_key()
{
local com
operator_disallowed()
establish_baseline()
com = vi_prompt( "EX_MODE", ":" )
original_com = com
if ( com )
ex_mode( com )
}
local function ex_mode( command )
{
ex_com = command
ex_address_range()
ex_mode1()
if ( browser_bid )
browse_printed()
vi_command_mode()
}
local function ex_mode1( command )
{
local times, n, com, variant, macro_name, key_function, macro_body
local tmp_str = ""
local covered_macro, covered_name, covered_body
local abbreviation
local buf_char
local count, mark
local NI = " -- not implemented"
if( command )
ex_com = command
com = eat_command() # eat_command operates on ex_com (via eat_match())
#count = eat_count()
# message("com = %s, ex_addresses = %d, ex_addr1 = %d, ex_addr2 = %d, command = %s",
# com, ex_addresses, ex_addr1, ex_addr2, command)
# message("original_com = %s, command = %s, ex_com = %s, com = %s",
# original_com, command, ex_com, com)
#return
if( com == "" && ex_addresses == 1)
vi_goto_line( ex_addr1 )
else if ( com == "" && ex_addresses > 1 )
{
select_range( DEFAULT_ONE )
copy_to_scrap()
scrap_to_browser()
raise_anchor()
}
else if( com == "" )
{
vi_error( "unrecognized command" )
}
else if( index( "append", com ) == 1 )
{
eat_space()
next_line()
prev_char()
insert_string( ex_com )
insert_newline()
}
else if( index( "abbreviate", com ) == 1 )
{
if( in_glob )
vi_error( "can't glob:" com ex_com )
else if( ex_addresses )
vi_error( "addresses are illegal for :" com ex_com )
else
{
abbreviation = eat_word()
if( !abbreviation)
vi_error( "null abbreviation not allowed" )
else
{
eat_space()
templates[ abbreviation ] = ex_com
}
}
}
else if( index( "args", com ) == 1 )
vi_error( "args" NI )
else if( index( "change", com ) == 1 )
{
count = eat_count()
if( ex_addresses == 1 && count )
{
ex_addresses = 2
ex_addr2 = ex_addr1 + count - 1
}
select_range( DEFAULT_ONE )
vi_yank( AND_DELETE )
variant = eat_bang()
if( variant )
auto_indent_mode = option_disable[ "autoindent" ]
vi_O()
if( variant )
auto_indent_mode = ! option_disable[ "autoindent" ]
}
else if( index( "cd", com ) == 1 )
vi_error( "cd" NI )
else if( index( "copy", com ) == 1 )
ex_copy( COPY )
else if( index( "cr", com ) == 1 )
vi_error( "cr" NI )
else if( com == "X" )
vi_error( "X" NI )
else if( index( "delete", com ) == 1 )
{
eat_space()
buf_char = eat_match( "[a-zA-Z]" )
count = eat_count()
if( ex_addresses == 1 && count )
{
ex_addresses = 2
ex_addr2 = ex_addr1 + count - 1
}
select_range( DEFAULT_ONE )
if( buf_char ) buf_id = buf_char
vi_yank( AND_DELETE )
}
else if( index( "ex", com ) == 1 || index( "edit", com ) == 1 )
{
if( in_glob )
vi_error( "can't glob:" com ex_com )
else if( ex_addresses )
vi_error( "addresses are illegal for :" com ex_com )
else
ex_edit()
}
else if( index( "file", com ) == 1 )
{
if( in_glob )
vi_error( "can't glob:" com ex_com )
else if( ex_addresses )
vi_error( "addresses are illegal for :" com ex_com )
else
{
ex_file_subst()
if(( com = eat_arg()))
{
buffer_filename = com
buffer_name = path_fname(buffer_filename) \
path_ext(buffer_filename)
buffer_flags = and( buffer_flags, not( BUFFER_MODIFIED ))
display_redraw()
}
}
ex_show_file()
}
else if( index( "global", com ) == 1 || com == "v" )
{
if( com != "v" && eat_bang() )
com = "v"
ex_glob( com )
}
else if( index( "insert", com ) == 1 )
{
eat_space()
prev_char()
insert_string( ex_com )
insert_newline()
}
else if( index( "join", com ) == 1 )
{
count = eat_count()
if (ex_addresses)
vi_goto_line( ex_addr1 )
if (ex_addresses == 2)
number_register = ex_addr2 - ex_addr1
else
number_register = count
if (number_register < 1)
number_register = 1;
vi_J()
# vi_error( "join" NI )
}
else if( index( "list", com ) == 1 || index( "print", com ) == 1 )
{
count = eat_count()
if( ex_addresses == 1 && count )
{
ex_addresses = 2
ex_addr2 = ex_addr1 + count - 1
}
select_range( DEFAULT_ONE )
copy_to_scrap()
scrap_to_browser()
raise_anchor()
}
else if( index( "move", com ) == 1 )
ex_copy( MOVE )
else if( index( "mark", com ) == 1 || com == "k")
{
eat_space()
mark = eat_letter()
create_mark( vi_mark_id( mark ))
}
else if( match( com, "^k[a-z]" ))
{
mark = substr( com, 2 )
create_mark( vi_mark_id( mark ))
}
else if( index( "map", com ) == 1 )
{
if( in_glob )
vi_error( "can't glob:" com ex_com)
else if( ex_addresses )
vi_error( "addresses are illegal for :" com ex_com )
else
{
variant = eat_bang()
macro_name = eat_word()
if( !macro_name )
{
if( !variant )
{
for( macro_name in command_macro_table)
pbuf( macro_name " " command_macro_table[ macro_name ] )
}
else
for( macro_name in insert_macro_table )
pbuf( macro_name " " insert_macro_table[ macro_name ] )
}
else
{
macro_body = eat_word()
if ( !variant )
{
covered_name = covered_binding( macro_name, variant)
covered_command_macro[ macro_name ] =covered_name
covered_command_macro_body[ macro_name ] = keymap_binding( covered_name, current_keymap)
delete command_macro_table[ covered_command_macro[ macro_name ]]
command_macro_table[ macro_name ] = macro_body
assign_key( macro_name, "expand_macro " macro_name )
}
else
{
current_keymap = vi_insert_keymap
covered_name = covered_binding( macro_name, variant )
covered_insert_macro[ macro_name ] = covered_name
covered_insert_macro_body[ macro_name ] = keymap_binding( covered_name, current_keymap )
delete insert_macro_table[ covered_insert_macro[ macro_name ]]
insert_macro_table[ macro_name ] = macro_body
assign_key( macro_name, "expand_macro " macro_name )
current_keymap = vi_command_keymap
}
}
}
}
else if( index( "next", com ) == 1 )
{
variant = eat_bang()
if( in_glob )
vi_error( "can't glob:" com ex_com )
else if( ex_addresses )
vi_error( "addresses are illegal for :" com ex_com )
else
{
ex_file_subst()
if( !variant )
ex_autowrite()
next_buffer()
ex_show_file()
}
}
else if( index( "number", com ) == 1 || com == "#" )
vi_error( "number" NI )
else if( index( "open", com ) == 1 )
vi_error( "open" NI )
else if( index( "put", com ) == 1 )
{
if( in_glob )
vi_error( "can't glob:" com ex_com )
else if (eat_count())
vi_error( "count invalid for: put" )
else
{
if (ex_addresses == 1)
vi_goto_line( ex_addr1 )
else if (ex_addresses == 2)
vi_goto_line( ex_addr2 )
vi_p()
}
}
else if( index( "preserve", com ) == 1 )
{
# can't make :p = prev_buffer, since it is defined as 'print' above (see list)
vi_error( "preserve" NI )
}
else if( index( "quit", com ) == 1 )
{
if( in_glob )
vi_error( "can't glob:" com ex_com )
else if( ex_addresses )
vi_error( "addresses are illegal for :" com ex_com )
else
ex_quit()
}
else if( index( "read", com ) == 1 )
{
eat_space()
if( eat_bang() )
ex_shell_read()
else
ex_read()
}
else if( index( "recover", com ) == 1 )
vi_error( "recover" NI )
else if( index( "rewind", com ) == 1 )
# need to account for autowrite when this code is implemented
# see :next code for method
vi_error( "rewind" NI )
else if ( index( "substitute", com ) == 1 )
{
if( ex_com )
save_command = ex_com
ex_sub()
}
else if ( index( "set", com ) == 1 )
ex_set()
else if( index( "shell", com ) == 1 )
{
if( in_glob )
vi_error( "can't glob:" com ex_com )
else if( ex_addresses )
vi_error( "addresses are illegal for :" com ex_com )
else
system()
}
else if( index( "source", com ) == 1 )
vi_error( "source" NI )
else if( index( "t", com ) == 1 )
ex_copy( COPY )
else if( index( "tag", com ) == 1 )
{
variant = eat_bang()
n = eat_word()
if( !variant )
ex_autowrite()
vi_tags( n )
}
else if( index( "undo", com ) == 1 )
{
# vi_error( "undo" NI )
undo()
}
else if( index( "unabbreviate", com ) == 1 )
{
if( in_glob )
vi_error( "can't glob:" com ex_com )
else if( ex_addresses )
vi_error( "addresses are illegal for :" com ex_com )
else
{
abbreviation = eat_word()
if( !abbreviation )
vi_error( "null abbreviation not allowed" )
else
delete templates[ abbreviation ]
}
}
else if( index( "unmap", com ) == 1 )
{
if( in_glob )
vi_error( "can't glob:" com ex_com)
else if( ex_addresses )
vi_error( "addresses are illegal for :" com ex_com )
else
{
variant = eat_bang()
macro_name = eat_word()
if( !macro_name )
vi_error( "need macro name to unmap" )
else
{
if( !variant )
{
delete command_macro_table[ macro_name ]
covered_macro = covered_command_macro[ macro_name ]
if( covered_macro )
{
covered_body = covered_command_macro_body[ macro_name ]
if( covered_body ~ /expand_macro/ )
command_macro_table[ covered_macro ] = covered_body
delete covered_command_macro_body[ macro_name ]
delete covered_command_macro[ macro_name ]
assign_key( covered_macro, covered_body )
}
}
else
{
delete insert_macro_table[ macro_name ]
covered_macro = covered_insert_macro[ macro_name ]
if( covered_macro )
{
covered_body = covered_insert_macro_body[ macro_name ]
if( covered_body ~ /expand_macro/ )
insert_macro_table[ covered_macro ] = covered_body
delete covered_insert_macro_body[ macro_name ]
delete covered_insert_macro[ macro_name ]
push_keymap( vi_insert_keymap )
assign_key( covered_macro, covered_body )
pop_keymap()
}
}
}
}
}
else if( index( "version", com ) == 1 )
print_version()
# else if( index( "visual", com ) == 1 )
#
else if( index( "w", com ) == 1 && match( ex_com, "^ !" ))
{
eat_match( " !" )
ex_shell_write()
}
else if( index( "write", com ) == 1 )
ex_write()
else if( index( "xit", com ) == 1 || com == "wq" )
{
if( in_glob )
vi_error( "can't glob:" com ex_com )
else if( ex_addresses )
vi_error( "addresses are illegal for :" com ex_com )
else
ex_write_and_quit()
}
else if( index( "yank", com ) == 1 )
{
eat_space()
buf_char = eat_match( "[a-zA-Z]" )
count = eat_count()
if( ex_addresses == 1 && count )
{
ex_addresses = 2
ex_addr2 = ex_addr1 + count - 1
}
#message("buf_char = %s", buf_char);
select_range( DEFAULT_ONE )
if( buf_char )
buf_id = buf_char
vi_yank()
}
else if( com == "z" )
{
if( in_glob )
vi_error( "can't glob:" com ex_com )
else if (eat_count())
vi_error( "count invalid for: z" )
else
{
if (ex_addresses == 1)
vi_goto_line( ex_addr1 )
else if (ex_addresses == 2)
vi_goto_line( ex_addr2 )
if (ex_com == ".")
scroll_window_middle()
else if (ex_com == "-")
scroll_window_bottom()
else if (ex_com == "")
scroll_window_top()
else
vi_error( "Extra characters at end of z command")
}
}
else if( index( com, "<" ) == 1 || index( com, ">" ) == 1 )
{
count = eat_count()
if (count && ex_addresses == 2)
vi_goto_line(ex_addr2)
else
vi_goto_line(ex_addr1)
if (!count)
count = ex_addr2 - ex_addr1 + 1
message("count = %d", count)
times = length(com)
while (times-- > 0)
if ( index( com, "<") )
outdent_lines(count)
else
indent_lines(count)
}
else if( com == "!" )
{
if( in_glob )
vi_error( "can't glob:" com ex_com )
else if( ex_addresses )
vi_error( "addresses are illegal for :" com ex_com )
else
{
ex_file_subst()
if( !option_disable[ "warn" ] && buffers_modified )
vi_error( "warning: buffers have been modified" )
ex_autowrite()
if( eat_bang())
com = current_history_item( "SYSTEM" )
# com = current_history_item( "!" )
else
# com = add_prompt_history( "!", eat_arg())
com = eat_arg()
system_key( com )
}
}
else if( com == "=" )
{
n = ( ex_addresses > 1 ) ? ex_addr2 : ex_addr1
if( in_glob )
pbuf( n )
else
message( n )
}
else if( com == "&" )
{
if( save_command )
{
ex_com = save_command
ex_sub()
}
else
vi_error( "no previous :s command to repeat" )
}
else
vi_error( "unrecognized command" )
}
local function ex_show_state( option )
{
#was message
message( option_disable[ option ] option (option_value[ option ]? "=" option_value[ option ] : ""))
}
local function ex_set_state( option, option_switch, value )
{
#message("ex_set_state");
option_disable[ option ] = option_switch
option_value[ option ] = value
}
function ex_show_match()
{
local i, top_line, found_match, the_line, the_column
local terminator = ""
#message("ex_show_match");
insert_key()
if (chr(current_key) != "}")
terminator = "{"
the_line = current_line
the_column = current_column
left(1)
top_line = current_line - distance_to_window_top()
found_match = goto_matching(terminator)
if( current_line >= top_line && found_match )
{
begin_selection(INCLUSIVE_SELECTION)
end_selection()
display_update()
for(i=1; i<10000; i++) {} # ugly delay
remove_selection()
goto_pos( the_line, the_column )
display_update()
}
else
goto_pos( the_line, the_column )
}
local function ex_copy( mode ) {
local destination
#message("ex_copy");
select_range( DEFAULT_ONE )
copy_to_scrap()
raise_anchor()
destination = ex_address()
if( destination )
destination++
goto_line( destination )
insert_scrap()
if( mode )
{
select_range( DEFAULT_ONE )
delete_to_scrap()
}
}
# set the state of all the settable options
local function ex_init_state()
{
#message("ex_init_state");
ex_set_state( "autoindent", "no", "" )
ex_set_state( "autoprint", "", "" )
ex_set_state( "autowrite", "no", "" )
ex_set_state( "beautify", "no", "" )
ex_set_state( "directory", "", "/tmp" )
ex_set_state( "edcompatible", "no", "" )
ex_set_state( "errorbells", "", "" )
ex_set_state( "flash", "no", "" )
ex_set_state( "hardtabs", "", "8" )
ex_set_state( "ignorecase", "no", "" )
ex_set_state( "lisp", "no", "" )
ex_set_state( "list", "no", "" )
ex_set_state( "magic", "", "" )
ex_set_state( "mesg", "", "" )
ex_set_state( "modelines", "no", "" )
ex_set_state( "novice", "no", "" )
ex_set_state( "number", "no", "" )
ex_set_state( "optimize", "no", "" )
ex_set_state( "paragraphs", "", "IPLPPPQPP LIbp" )
ex_set_state( "prompt", "", "" )
ex_set_state( "readonly", "no", "" )
ex_set_state( "redraw", "", "" )
ex_set_state( "remap", "", "" )
ex_set_state( "report", "", "5" )
ex_set_state( "scroll", "", "11" )
ex_set_state( "sections", "", "NHSHH HU" )
ex_set_state( "shell", "", "/bin/sh" )
ex_set_state( "shiftwidth", "", "8h" )
ex_set_state( "showmatch", "no", "" )
ex_set_state( "showmode", "no", "" )
ex_set_state( "slowopen", "no", "" )
ex_set_state( "tabstop", "", "8" )
ex_set_state( "taglength", "", "0" )
ex_set_state( "tags", "", "tags" )
# vi_tags_init( "tags" )
vi_tags_init( tags_path )
ex_set_state( "term", "", "vt100" )
ex_set_state( "terse", "no", "" )
ex_set_state( "timeout", "", "" )
ex_set_state( "ttytype", "", "vt100" )
ex_set_state( "warn", "", "" )
ex_set_state( "window", "", "8" )
ex_set_state( "wrapscan", "", "" )
ex_set_state( "wrapmargin", "", "0" )
ex_set_state( "writeany", "no", "" )
}
local function ex_set() {
local option, option_switch, query
option_switch = eat_no()
option = eat_option()
query = eat_question_mark()
#message("ex_set: option = %s; op_switch = %s; query = %s", option, option_switch, query);
if( option == "all" || option == "")
{
if (auto_indent_mode)
ex_set_state( "autoindent", "", "" )
else
ex_set_state( "autoindent", "no", "" )
for ( option in option_disable )
pbuf( option_disable[ option ] option (option_value[ option ]? "=" option_value[ option ] : "" ))
}
else if( option == "ai" || option == "autoindent" )
{
if( query )
ex_show_state( "autoindent" )
else
{
default_auto_indent_mode = auto_indent_mode = !option_switch
ex_set_state( "autoindent", option_switch, "" )
}
}
else if( option == "ap" || option == "autoprint" )
{
if( query )
ex_show_state( "autoprint" )
else
ex_set_state( "autoprint", option_switch, "" )
}
else if( option == "aw" || option == "autowrite" )
{
if( query )
ex_show_state( "autowrite" )
else
ex_set_state( "autowrite", option_switch, "" )
}
else if( option == "bf" || option == "beautify" )
{
if( query )
ex_show_state( "beautify" )
else
ex_set_state( "beautify", option_switch, "" )
}
else if( option == "dir" || option == "directory" )
{
if( option_switch )
vi_error( option " cannot be disabled" )
else
if( query )
ex_show_state( "directory" )
else
if( eat_equal())
ex_set_state( "directory", "", ex_com )
else
vi_error( "syntax" )
}
else if( option == "ed" || option == "edcompatible" )
{
if( query )
ex_show_state( "edcompatible" )
else
ex_set_state( "edcompatible", option_switch, "" )
}
else if( option == "eb" || option == "errorbells" )
{
if( query )
ex_show_state( "errorbells" )
else
ex_set_state( "errorbells", option_switch, "" )
}
else if( option == "fl" || option == "flash" )
{
if( query )
ex_show_state( "flash" )
else
ex_set_state( "flash", option_switch, "" )
}
else if( option == "ht" || option == "hardtabs" )
{
if( option_switch )
vi_error( option " cannot be disabled" )
else
if( query )
ex_show_state( "hardtabs" )
else
if( eat_equal())
ex_set_state( "hardtabs", "", ex_com )
}
else if( option == "ic" || option == "ignorecase" )
{
if( query )
ex_show_state( "ignorecase" )
else
{
ex_set_state( "ignorecase", option_switch, "" )
if( option_switch )
search_flags = vi_search_flags = or( search_flags, SEARCH_CASE )
#search_flags = vi_search_flags = or( vi_search_flags, SEARCH_CASE )
else
search_flags = vi_search_flags = and( search_flags, not(SEARCH_CASE) )
#search_flags = vi_search_flags = and( vi_search_flags, not(SEARCH_CASE) )
}
}
else if( option == "lisp" )
{
if( query )
ex_show_state( "lisp" )
else
ex_set_state( "lisp", option_switch, "" )
}
else if( option == "list" )
{
if( query )
ex_show_state( "list" )
else
ex_set_state( "list", option_switch, "" )
}
else if( option == "magic" )
{
if( query )
ex_show_state( "magic" )
else
ex_set_state( "magic", option_switch, "" )
}
else if( option == "mesg" )
{
if( query )
ex_show_state( "mesg" )
else
ex_set_state( "mesg", option_switch, "" )
}
else if( option == "modelines" )
{
if( query )
ex_show_state("modelines" )
else
ex_set_state( "modelines", option_switch, "" )
}
else if( option == "nu" || option == "number" )
{
if( query )
ex_show_state( "number" )
else
{
if( option_switch )
linenumber_format = ""
else
linenumber_format = "%5d"
ex_set_state( "number", option_switch, "" )
}
}
else if( option == "opt" || option == "optimize" )
{
if( query )
ex_show_state( "optimize" )
else
ex_set_state( "optimize", option_switch, "" )
}
else if( option == "para" || option == "paragraphs" )
{
if( option_switch )
vi_error( option " cannot be disabled." )
else
if( query )
ex_show_state( "paragraphs" )
else
if( eat_equal())
ex_set_state( "paragraphs", "", ex_com )
else
vi_error( "syntax" )
}
else if( option == "prompt" )
{
if( query )
ex_show_state( "prompt" )
else
ex_set_state( "prompt", option_switch, "" )
}
else if( option == "ro" || option == "readonly" )
{
if( query )
ex_show_state( "readonly" )
else
ex_set_state( "readonly", option_switch, "" )
}
else if( option == "redraw" )
{
}
else if( option == "remap" )
{
if( query )
ex_show_state( "remap" )
else
ex_set_state( "remap", option_switch, "" )
}
else if( option == "report" )
{
}
else if( option == "scroll" )
{
}
else if( option == "sh" || option == "shell" )
{
if( option_switch )
vi_error( option " cannot be disabled." )
else
if( query )
ex_show_state( "shell" )
else
if( eat_equal() )
ex_set_state( "shell", "", ex_com )
else
vi_error( "syntax" )
}
else if( option == "sw" || option == "shiftwidth" )
{
}
else if( option == "sm" || option == "showmatch" )
{
if( query )
ex_show_state( "showmatch" )
else
ex_set_state( "showmatch", option_switch, "" )
push_keymap( vi_insert_keymap )
if( option_switch )
{
assign_key( ")", "insert_key" )
assign_key( "]", "insert_key" )
assign_key( "}", "insert_key" )
}
else
{
assign_key( ")", "ex_show_match" )
assign_key( "]", "ex_show_match" )
assign_key( "}", "ex_show_match" )
}
pop_keymap()
}
else if( option == "showmode" )
{
}
else if( option == "slow" || option == "slowopen" )
{
}
else if( option == "ts" || option == "tabstop" )
{
}
else if( option == "tl" || option == "taglength" )
{
}
else if( option == "tags" )
{
if( query )
ex_show_state( "tags" )
else
if( eat_equal())
{
ex_set_state( "tags", "", ex_com )
vi_tags_init( ex_com )
}
}
else if( option == "term" )
{
}
else if( option == "terse" )
{
}
else if( option == "timeout" )
{
}
else if( option == "ttytype" )
{
}
else if( option == "warn" )
{
if( query )
ex_show_state( "warn" )
else
ex_set_state( "warn", option_switch, "" )
}
else if( option == "window" )
{
}
else if( option == "ws" || option == "wrapscan" )
{
if( query )
ex_show_state( "wrapscan" )
else
{
ex_set_state( "wrapscan", option_switch, "" )
if( option_switch )
search_flags = vi_search_flags = and( search_flags, not( SEARCH_WRAPS ))
#search_flags = vi_search_flags = and( vi_search_flags, not( SEARCH_WRAPS ))
else
search_flags = vi_search_flags = or( search_flags, SEARCH_WRAPS )
#search_flags = vi_search_flags = or( vi_search_flags, SEARCH_WRAPS )
}
}
else if( option == "wm" || option == "wrapmargin" )
{
}
else if( option == "wa" || option == "writeany" )
{
}
else
vi_error( "unknown option" )
}
# finds and substitutes file names for % and #
local function ex_file_subst()
{
local remainder, subs = 0
local start_buf = current_buffer
#message("ex_file_subst");
remainder = index(original_com, ex_com )
subs = gsub( /%/, buffer_filename, ex_com )
if (index(ex_com, /#/))
{
if (!last_buffer)
{
last_buffer = current_buffer
prev_buffer("", 0, 1)
}
else
{
current_buffer = last_buffer
last_buffer = start_buf
}
subs = subs + gsub( /#/, buffer_filename, ex_com )
}
if( subs )
message( ":" substr(original_com, 1, remainder - 1 ) ex_com )
current_buffer = start_buf
}
function vi_ZZ()
{
local ch = "["
if (!playing_again)
ch = vi_getc()
if (ch != "Z")
vi_error()
else
{
ex_addresses = 0
ex_com = ""
ex_write_and_quit()
}
}
function ex_write_and_quit() {
ex_write()
ex_quit()
}
local function ex_quit( force ){
force = force || eat_bang()
if( !force && buffers_modified )
warning( "File has been modified;\n\"q!\" quits and discards changes" )
else
done( force )
}
function vi_next_buffer(next_flag)
{
if (argcount() < 1)
next_flag = 1
if (next_flag)
next_buffer_mask()
else
prev_buffer_mask()
ex_show_file()
}
function ex_subs_repeat() {
#message("ex_subs_repeate");
if( save_command )
{
ex_com = save_command
ex_sub()
}
else
vi_error( "no previous :s command to repeat" )
}
local function append_buffer( filename )
{
local ret_val = -1 # failure
local fileID = fopen( filename, READWRITE )
if ( fileID )
{
fseek( fileID, 0, END_FILE )
fwrite( fileID, create_mark( BOB, 1, 1 ), buffer_size )
fclose( fileID )
ret_val = TRUE
}
else
vi_error( "cannot open file " filename )
return ret_val
}
local function append_marked_block( filename ) {
local line_1, line_2, length_line_1, length_line_2, length
local mark_1, mark_2
local fileID = fopen( filename, READWRITE )
#message("append marked block");
if( fileID )
{
fseek( fileID, 0, END_FILE )
line_1 = current_line
length_line_1= current_line_length
swap_selection_marks()
line_2 = current_line
length_line_2= current_line_length
swap_selection_marks()
mark_1 = create_mark( B1, line_1, 1 )
mark_2 = create_mark( B2, line_2, 1 )
length = distance_between_marks( mark_1, mark_2 )
if( line_1 > line_2 )
{
length_line_2 = length_line_1
mark_1 = mark_2
}
length = length + length_line_2
fwrite( fileID, mark_1, length )
fclose( fileID )
}
else
vi_error( "cannot open file " filename )
}
local function ex_shell_read()
{
local input_redirection
ex_file_subst()
if ( ex_addresses > 1 )
vi_error( "only 0 or 1 addresses allowed for :r!" )
if ( ex_addresses )
current_line = ex_addr1 + 1
else
current_line ++
goto_bol()
if ( ex_com ~ "\\<" )
input_redirection = ""
else
input_redirection = "<temp_inp"
system( ex_com input_redirection " >&temp_out" )
read_file( "temp_out" )
}
local function ex_shell_write()
{
local output_redirection
ex_file_subst()
select_range( DEFAULT_ONE )
write_marked_block( "temp_inp" )
if ( ex_com ~ "\\>" )
{
output_redirection = ""
sub( "\\>", " >\\&", ex_com )
}
else
output_redirection = " >&temp_out"
system( ex_com " <temp_inp" output_redirection )
if ( output_redirection && filesize( "temp_out" ) > 0 )
file_to_browser( "temp_out" )
}
function vi_saveas( fn )
{
if ( !fn )
fn = prompt( ":w ", "" )
if (fn)
ex_write(fn);
}
function vi_save()
{
if ( is_scratch_file(current_buffer) )
vi_saveas()
else
ex_write()
}
local function ex_write( name )
{
local force = eat_bang()
local append, name_supplied, file_exists, name_changed
local success = -1
#message("ex_write");
ex_file_subst()
append = eat_append()
# check for filename passed in as parameter
#
if ( !name )
{
name = eat_arg()
name_supplied = name
}
if ( !name )
name = buffer_filename
file_exists = filesize( name ) >= 0
name_changed = (buffer_filename != buffer_original_filename)
if ( file_exists && ! force && ( name_supplied || name_changed ) && ! append)
vi_error( "attempt to overwrite existing file; use \"w!\" to overwrite" )
else if ( force ||
!file_exists ||
(!name_changed && buffer_is_modified()) ||
(name_changed && !file_exists) ||
append )
{
if ( read_only() )
vi_error( "file " name " is read-only" )
else if ( !option_disable[ "readonly" ] && ! force )
vi_error( "readonly option enabled; use \"w!\" to write" )
else if ( selection_type() )
{
if ( append )
append_marked_block( name )
else
write_marked_block( name )
}
else if ( ex_addresses )
{
select_range()
if ( append )
append_marked_block( name )
else
write_marked_block( name )
raise_anchor()
}
else
{
if ( append )
success = append_buffer( name )
else
{
if (buffer_filename == name)
backup_file( buffer_filename )
success = write_buffer( name )
}
# reset BUFFER_MODIFIED only on a full write
if ( success != -1 )
buffer_flags = and( buffer_flags, not( BUFFER_MODIFIED ))
}
ex_show_file()
}
}
local function ex_autowrite()
{
if ( !option_disable[ "autowrite" ] && buffer_is_modified())
{
if ( read_only() )
vi_error( "file " buffer_filename " is read_only; unable to autowrite" )
else if ( option_disable[ "readonly" ] )
vi_error( "readonly option enabled; unable to autowrite" )
else
{
write_buffer()
buffer_flags = and( buffer_flags, not( BUFFER_MODIFIED ))
}
}
}
# this catches <Ctl>^ keystrokes
function last_buffer_key()
{
ex_autowrite()
prev_buffer()
}
# tests if current file is read-only
local function read_only()
{
local is_ro = FALSE
local mode = filemode( buffer_filename )
# check for non-error ret mode
if ( mode >= 0 )
is_ro = and( mode, _READ_ONLY)
return is_ro
}
function vi_read( fn )
{
if ( !fn )
fn = prompt_history( "EDITFILE", ":r ", "" )
if (fn)
ex_read(fn);
}
local function ex_read( name )
{
ex_file_subst()
if ( !name )
name = eat_arg()
if ( !name )
name = vi_read_name
if ( !name )
vi_error( "must supply file name to read" )
vi_read_name = name
if ( filesize( name ) < 0 )
vi_error( name ": file does not exist" )
if ( ex_addresses > 1 )
vi_error( "only 0 or 1 addresses allowed for :r" )
if ( ex_addresses )
current_line = ex_addr1 + 1 # may be 0
else
current_line++
goto_bol()
read_file( name )
ex_show_file()
}
function vi_open( fn )
{
if ( !fn )
fn = prompt_history( "EDITFILE", ":e ", "" )
if (fn)
ex_edit(fn);
}
local function ex_edit( fn )
{
local force = eat_bang()
local poundLine
local name
ex_file_subst()
# read space or semicolon delimited argument
eat_space()
if ( fn )
name = fn
else
name = eat_match( "[^; ]+" )
if ( substr(name,1,1) == "+" )
{
# read line number to start at in specified file
poundLine = substr( name, 2 ) + 0
name = eat_arg()
}
if( !name && !force )
vi_error( "file name required; use \"e!\" to restart current file" )
else if ( name == buffer_name )
vi_error( "file name same as current file; use \"e!\" to restart current file" )
else
{
if ( !name )
{
name = buffer_filename
if ( !name )
return
}
if ( force ) #&& MAGIC )
{
buffer_flags = and( buffer_flags, not( BUFFER_MODIFIED ) )
delete_buffer()
}
# edit_file( name )
create_buf_and_win(name)
ex_show_file()
if( poundLine )
goto_line( poundLine )
}
}
local function vi_filter()
{
local com = prompt_history( "!", "!", "", 1 )
if ( com )
filter( com )
}
local function vi_tags_init( tags )
{
sub( " ", ";", tags )
tags_path = tags
}
local function vi_tags( n ){
vi_tags_push()
tags( n )
}
function vi_tags_auto(){
vi_tags_push()
tags_auto()
}
local vi_tags_index = 0
local vi_tags_stack
function vi_tags_push(){
vi_tags_stack[ vi_tags_index++ ] = buffer_filename " " \
buffer_offset " " \
distance_to_window_top()
}
function vi_tags_pop(){
local loc
if( vi_tags_index <= 0 )
warning( "vi tags stack empty" )
split( vi_tags_stack[ --vi_tags_index ], loc, " " )
delete vi_tags_stack[ vi_tags_index ]
edit_file( loc[ 1 ])
goto_buffer_offset( loc[ 2 ])
# restore window orientation, if possible
scroll_window_top( loc[ 3 ])
}
function ex_show_file( annotation )
{
local pct = 100
# # faster, but less accurate:
# if( buffer_size > 0 )
# pct = 100 * buffer_offset / buffer_size
if( buffer_last_line > 0 )
pct = 100 * current_line / buffer_last_line
if( !annotation && and( buffer_flags, BUFFER_MODIFIED ))
annotation = "[modified]"
message( "\"%s\" %sline %d of %d -- %d%% --", \
buffer_filename, \
( annotation ? annotation " " : "" ), \
current_line, \
buffer_last_line, \
pct )
}
local function ex_sub()
{
local quote, old, new, gc, start, stop, count, n = 0, sflags
local pflag, cflag, gflag
local line_count
#message("ex_sub");
quote = eat_punctuation()
if( !quote ) {
if( prev_old && prev_new ) {
old = prev_old
new = prev_new
} else
vi_error( "no previous substitute pattern to reuse" )
} else {
old = search_hist( eat_regex( quote ))
new = eat_regex( quote )
prev_old = old
prev_new = new
}
gc = eat_match( "[gc]+" ) # ":s/foo" is the same as ":s/foo//"
sflags = SEARCH_FORWARD \
+ SEARCH_MAXIMAL_MATCH \
+ SEARCH_BLOCK \
+ SEARCH_REGEX \
+ and( search_flags, \
or( SEARCH_CASE, \
SEARCH_WRAPS ))
#+ and( vi_search_flags, \
# or( SEARCH_CASE, \
# SEARCH_WRAPS ))
line_count = eat_count()
if( ex_addresses == 1 && line_count )
{
ex_addresses = 2
ex_addr2 = ex_addr1 + line_count
}
else
ex_addr2 ++
pflag = eat_match( "p" )
cflag = gc ~ /c/
gflag = gc ~ /g/
if( match( "", old ) && RLENGTH == 0 )
vi_error( "NULL search pattern length." );
if( ! gflag && ! cflag ){
search_count = 0x7FFFFFFF;
if( ! gflag ){
sflags = sflags + SEARCH_ONCE_PER_LINE
}
select_range( DEFAULT_ONE )
n = replace( old, new, sflags )
raise_anchor()
} else {
stop = ex_range( DEFAULT_ONE )
start = current_line
count = ( gflag ) ? 0x7FFFFFFF : 1
while( start <= stop )
{
goto_pos( start++, 1 )
drop_anchor( LINE_SELECTION )
search_count = count
if( cflag? ex_sub_confirm( old ) : 1 ) {
n += replace( old, new, sflags )
if( pflag ){
copy_to_scrap()
scrap_to_browser()
}
}
raise_anchor()
}
}
if( !in_glob )
message( "%d replacements made", n )
}
local function ex_sub_confirm( old ) {
local results
#message("ex_sub_confirm");
if( search( old, 23 + and( vi_search_flags, SEARCH_CASE ))) {
copy_to_scrap()
scrap_to_confirmer()
results = confirmer( current_column, 1, length( old ), 1 )
return results
}
}
local function ex_glob( gv ){
local com, quote, regex, n = 0
local sflags = or( or( SEARCH_FWD_REGEX_MAX, SEARCH_BLOCK ), \
or( SEARCH_ADVANCE, \
and( search_flags, SEARCH_CASE )))
#and( vi_search_flags, SEARCH_CASE )))
#message("ex_glob");
if( in_glob )
vi_error( "recursive :" gv " is not allowed" )
in_glob = 1
gv = ( gv == "v" )
quote = eat_char()
regex = search_hist( eat_regex( quote ))
if( match( "", regex ) && RLENGTH == 0 )
vi_error( "NULL search pattern length." );
com = ex_com # save remainder of glob command
search_count = 1
select_range()
save_position()
while( search( regex, sflags ) )
{
restore_position()
save_position()
ex_addr1 = ex_addr2 = current_line
ex_addresses = 1
ex_mode1( com )
if(( n++ % 10 ) == 3 )
display_update()
if( com !~ /d/ )
goto_eol()
}
restore_position( 1 )
if( selection_type())
raise_anchor()
in_glob = 0
message( "%d lines processed", n )
}
local function ex_range( defaultOne ){
local start, stop
#message("ex_range");
if( selection_type())
{
stop = current_line
swap_selection_marks()
start = current_line
raise_anchor()
if( stop < start )
{
start = stop
stop = current_line
}
}
else if( ex_addresses )
{
start = ex_addr1
stop = ex_addr2 - 1
}
else if( defaultOne )
{
start = stop = current_line
}
else
{
start = 1
stop = buffer_last_line - 1
}
current_line = start
return stop
}
local function browser_buffer(){
#message("browser_buffer");
if( !browser_bid )
browser_bid = create_buffer( "", "", BUFFER_SYSTEM )
return browser_bid
}
local function file_to_browser( filename ) {
local cur_buf = current_buffer
#message("file_to_browser");
delete_event( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
current_buffer = browser_buffer()
read_file( filename )
current_buffer = cur_buf
attach_event_handler( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
}
local function scrap_to_browser(){
local cur_buf = current_buffer
#message("scrap_to_browser");
delete_event( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
current_buffer = browser_buffer()
goto_buffer_bottom()
insert_scrap()
current_buffer = cur_buf
attach_event_handler( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
}
local function scrap_to_confirmer() {
local cur_buf = current_buffer
#message("scrap_to_confirmer");
delete_event( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
current_buffer = confirmer_buffer()
goto_buffer_bottom()
insert_scrap()
current_buffer = cur_buf
attach_event_handler( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
}
local function confirmer_buffer() {
#message("confirmer_buffer")
if( ! confirmer_bid )
confirmer_bid = create_buffer( "", "", 1 )
return confirmer_bid
}
########################################################
#
# confirmer(col, line, width, height )
# displays lines placed by line_to confirmer in a window
# returns 1 or 0 depending upon whether process was
# terminated by a "y" key or not
local function confirmer( col, line, width, height )
{
local cur_win = current_window
local cur_buf = current_buffer
local cur_map = current_keymap
local i
local results
local old_wflags = window_flags
#message("confirmer");
# Must execute the next lines before changing keymaps since changing
# buffers or windows sets the vi_command_keymap
current_window = create_window()
current_buffer = confirmer_buffer()
window_flags = or(window_flags, WINDOW_SHOW_SYSTEM)
attach_window_buffer( current_window, current_buffer )
window_flags = old_wflags
window_name = "Confirmer"
if( confirmer_keymap == -1 ){
current_keymap = confirmer_keymap = create_keymap( empty_keymap )
for( i = 0; i < 256; i ++ )
assign_key( "#" i, "process_end" )
assign_key( "y", "process_end 1" )
assign_key( "Y", "process_end 1" )
}
current_keymap = confirmer_keymap
message( "Confirm with \"y\" key; reject with any other ascii key" )
# highlight_window( window_text_x0 + col -2, window_text_y0 + line -2, width, height, 0x20 )
results = process_begin()
delete_buffer( current_buffer )
delete_window( current_window )
current_buffer = cur_buf
current_window = cur_win
current_keymap = cur_map
confirmer_bid = 0
return results
}
local function browse_printed()
{
local cur_buf = current_buffer
local cur_map = current_keymap
local win_name = window_name
local old_wflags = window_flags
# Changing the buffer must come before the keymap assignment because
# changing buffers or windows causes the vi_command_keymap to be
# assigned to current_keymap.
current_buffer = browser_buffer()
window_flags = or(window_flags, WINDOW_SHOW_SYSTEM)
attach_window_buffer( current_window, current_buffer )
window_flags = old_wflags
goto_buffer_top()
#message("browse_printed");
if( browser_keymap == -1 )
{
current_keymap = browser_keymap = create_keymap( empty_keymap )
assign_key( "<Esc>", "vi_process_end" )
# motion keys which call vi_error are not safe to use within
# browse_printed. The following simulate vi
assign_key( "<Ctrl-PageUp>", "goto_buffer_top" )
assign_key( "<Ctrl-PageDown>", "goto_buffer_bottom" )
assign_key( "<PageUp>", "page_up" )
assign_key( "<PageDown>", "page_down" )
assign_key( "<Ctrl-B>", "page_up" )
assign_key( "<Ctrl-F>", "page_down" )
assign_key( "<Ctrl-D>", "scroll_down_half" )
assign_key( "<Ctrl-U>", "scroll_up_half" )
assign_key( "<Ctrl-E>", "scroll_down_1" )
assign_key( "<Ctrl-Y>", "scroll_up_1" )
assign_key( "h", "prev_char" )
assign_key( "j", "down" )
assign_key( "k", "up" )
assign_key( "l", "next_char" )
assign_key( "<Left>", "prev_char" )
assign_key( "<Up>", "up" )
assign_key( "<Down>", "down" )
assign_key( "<Right>", "next_char" )
assign_key( "<Home>", "goto_bol" )
assign_key( "<End>", "goto_eol" )
assign_key( "G", "goto_buffer_bottom" )
assign_key( "g", "goto_buffer_top" )
}
else
current_keymap = browser_keymap
window_name = "Command Output Listing"
message( "Use <Esc> to exit, PageUp/PageDown keys to browse" )
process_begin()
message( "" )
delete_buffer( current_buffer )
window_name = win_name
current_buffer = cur_buf
current_keymap = cur_map
browser_bid = 0
}
local function pbuf( s ){
local cur_buf = current_buffer
current_buffer = browser_buffer()
insert_string( s "\n" )
current_buffer = cur_buf
}
# select a region based on starting and ending addresses
local function select_range( defaultOne ){
#message("select_range");
if( ex_addresses )
{
current_line = ex_addr1
current_column = 1
drop_anchor( LINE_SELECTION )
if( ex_addresses > 1 )
current_line = ex_addr2
}
else
{
if( defaultOne )
{
drop_anchor( LINE_SELECTION )
}
else
current_line = current_column = 1
}
}
# parse a range address expression; result is 0, 1 or 2 numbers
#
# term:
# .
# $
# 'a
# /.../
# ?...?
# addr:
# term
# term + num
# term - num
# + num # . + num
# - num # . - num
# term + # term + 1
# term - # term - 1
# num
# range:
# /* empty */ # 1,$
# addr
# addr , addr
# addr ; addr
# , # 1,$
# ; # .,$
#
local function ex_address()
{
local len1, len = length( ex_com )
local num, patt, mark, op, ex_addr2 # made ex_addr2 local /Gj/
#message("ex_address");
# get stand-alone term, if any:
if( eat_match( "/" )){
vi_search( eat_regex( "/" ), SEARCH_FORWARD )
ex_addr2 = current_line
} else if( eat_match( "\\?" )){
vi_search( eat_regex( "?" ), SEARCH_BACKWARD )
ex_addr2 = current_line
} else if( eat_match( "\\." )){
ex_addr2 = current_line
} else if( eat_match( "\\$" )){
#ex_addr2 = buffer_last_line - 1
ex_addr2 = buffer_last_line
} else if(( patt = eat_match( "'." ))){
mark = vi_mark_id( substr( patt, 2 ))
if( !mark_defined( mark ))
bad_addr()
ex_addr2 = mark_line( mark )
} else
ex_addr2 = current_line # missing -- set default
len1 = len - length( ex_com )
# get operator, if any:
op = eat_match( "[+\\-]" )
line_search = !!op # signal to vi_search_key()
# get number, if any:
if(( num = eat_match( "[0-9]+" ))){
if( len1 && op == "" )
bad_addr()
num = 0 + num
} else
num = 1
# compute final value:
if( op == "+" )
ex_addr2 = ex_addr2 + num
else if( op == "-" )
ex_addr2 = ex_addr2 - num
else if( !len1 ) # if no op and no stand-alone term
ex_addr2 = num
# update address count, if any:
if( len != length( ex_com ))
ex_addresses ++
return ex_addr2
}
local function ex_address_range()
{
local ch
ex_addresses = 0
mark_context()
ex_addr1 = ex_address()
#message("ex_address_range");
if( ex_addresses )
{
if( ex_com ~ /^;/ )
{
vi_goto_line( ex_addr1 )
goto_bol()
}
if( eat_match( "[,;]" ))
{
ex_addr2 = ex_address()
if( ex_addresses != 2 )
bad_addr()
}
}
else if( eat_match( "[,%]" ))
{
ex_addr1 = 1
ex_addr2 = buffer_last_line - 1
ex_addresses = 2
}
else if( eat_match( ";" ))
{
ex_addr1 = current_line
ex_addr2 = buffer_last_line - 1
ex_addresses = 2
}
else
{
ex_addr1 = ex_addr2 = current_line
}
if( ex_addresses > 1 && ex_addr1 > ex_addr2 )
bad_addr()
goto_mark( vi_base_mark )
}
local function bad_addr(){
#message("bad_addr")
goto_mark( vi_base_mark ) # restore initial position
vi_error( "Ill-formed address" )
}
## address and ex mode parser helpers
#
# eat_... => if match then return the matched string and advance ex_com;
# else return null without advancing ex_com.
local function eat_match( patt )
{
local r = ""
if( match( ex_com, "^" patt )){
r = substr( ex_com, 1, RLENGTH )
ex_com = substr( ex_com, RLENGTH + 1 )
}
return r
}
local function eat_count() {
local num
eat_space()
num = eat_match( "[0-9]+" )
#message("eat_count = %d", num);
if( num )
return num + 0
else
return 0
}
local function eat_append() {
local app
eat_space()
app = eat_match( "\\>\\>" )
#message("eat_append = %s", app);
return app;
}
local function eat_word() {
local word;
eat_space()
word = eat_match( "[^" " \t]+" )
return word
}
local function eat_command()
{
local com = eat_match( "[a-zA-Z]+|!|#|=|&|[<]+|[>]+" )
return com;
}
local function eat_no() {
local no;
eat_space()
no = eat_match( "no" )
return no;
}
local function eat_option() {
local op;
eat_space()
op = eat_match( "[a-z]+" )
return op;
}
local function eat_space(){
local space = eat_match( "[ \t]*" )
return space;
}
local function eat_bang(){
local bang = eat_match( "!" )
return bang;
}
local function eat_equal() {
local equal = eat_match( "=" )
return equal
}
local function eat_question_mark() {
local q = eat_match( "\\?" )
return q;
}
local function eat_arg(){
local arg;
eat_space()
arg = eat_match( "[^;]+" )
return arg;
}
local function eat_char(){
local ch = substr( ex_com, 1, 1 )
ex_com = substr( ex_com, 2 )
#message("eat_char = %s", ch);
return ch
}
local function eat_punctuation()
{
local p = eat_match( "[!@#%&-_={}\\:;\"\\'<>,/~\\`]" )
return p;
}
local function eat_letter()
{
local l = eat_match( "[a-z]" )
return l;
}
# eat a regular expression, with <sep> as the separator.
#
# syntax is sep regex sep
# or sep regex EOL
#
# sep is "/" or "?"
#
# Leading separator should already have been removed.
# We don't use eat_match to avoid the slower anchored search.
# This is a good place to implement nomagic and other regex
# transformations.
#
local function eat_regex( sep )
{
local patt, regex, len
# closing seperator may be prefixed by an even number of backslashes;
# an odd number of backslashes quote the seperator, requiring it to
# be included in the regex.
patt = "[^\\\\](\\\\\\\\)*"
if( sep ~ /[|\]^$*+(){}.?\\]/ ) # regex seperators must be quoted
sep = "\\" sep
# split the input into a regex LHS and a remainder RHS, discarding
# the closing seperator; two matches because first may not be longest
if( match( ex_com, "^" sep ) || match( ex_com, patt sep ))
{
len = RSTART + RLENGTH - 1 # index of closing seperator
regex = substr( ex_com, 1, len - 1 )
ex_com = substr( ex_com, len + 1 )
}
else
{
regex = ex_com
ex_com = ""
}
# Now remove quotes from quoted seperators. By definition, they must
# have an odd number of preceeding backslashes, one of which must be
# removed. However, regex chars need to retain the quoting.
if( length( sep ) == 1 )
gsub( "\\" sep, sep, regex )
return regex
}
#########################################################################
#
# expand_macro replaces a set of keystrokes with
# a string specified with the :map ex command.
# Looks at setting of remap option to allow or
# inhibit nested macros
function expand_macro( macro_name )
{
local macro_body, the_key, success
if( macro_name in recursion_count )
{
if( in_ex_mode )
vi_error( "recursive map not allowed" )
else
{
vi_warning( "recursive map not allowed" )
flush_keyboard()
process_end()
}
}
else
{
recursion_count[ macro_name ] = ""
if( option_disable[ "remap" ] )
{
if( in_ex_mode )
push_keymap( default_vi_command_keymap )
else
push_keymap( default_vi_insert_keymap )
}
if( in_ex_mode)
{
macro_body = command_macro_table[ macro_name ]
the_key = keymap_binding( macro_body )
gsub( ".", "&\0", macro_body )
the_key = keymap_binding( macro_body )
success =playback( macro_body )
}
else
{
macro_body = insert_macro_table[ macro_name ]
gsub( ".", "&\0", macro_body )
playback( macro_body )
}
delete recursion_count[ macro_name ]
if( option_disable[ "remap" ] )
pop_keymap()
}
}
##########################################################################
#
#` covered_binding finds any keybinding that will be covered
# by the specified binding. Returns the
# key string that will be covered.
#
local function covered_binding( macro_name, variant)
{
local i, found = 1, name = "", prev_name, keymap, prev_prev_name
# check keymap first for anything that might get covered
if( variant )
keymap = vi_insert_keymap
else
keymap = vi_command_keymap
for( i = length( macro_name); found; i-- )
{
prev_name = name
name = substr( macro_name, 1, i )
found = keymap_binding( name, keymap )
}
if( prev_name ) # see if the one you are about to cover covers anything
{
if( variant )
{
prev_prev_name = covered_insert_macro[ prev_name ]
return prev_prev_name? prev_prev_name: prev_name
}
else
{
prev_prev_name = covered_command_macro[ prev_name ]
return prev_prev_name? prev_prev_name: prev_name
}
}
else # if no luck in the keymap, check existing macros
{
if (variant)
{
for( prev_name in insert_macro_table)
if( index( macro_name, prev_name ) == 1 )
return prev_name
}
else
{
for( prev_name in command_macro_table )
if( index( macro_name, prev_name ) == 1 )
return prev_name
}
}
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
### regex searches
#
# vi_search searches forwards or backwards for 1 pattern
# vi_error()s if the pattern is not found.
# vi_search_key prompts for input, and otherwise handles the
# "/" and "?" commands
# vi_search_again handles the "n", "N", and Alt-F5 and Alt-F6 commands.
#
# search_hist() recalls or remembers previous search pattern
#
function vi_search_key( key )
{
local patt
patt = vi_prompt( "SEARCH", key, "", 1 )
if (patt == key)
{
delete_history_item("SEARCH", patt)
vi_search_again(key)
}
else
{
disallow_number()
op_type_2b( DONT_MARK )
vi_search_on_pattern( key, patt )
}
}
function vi_search_on_pattern( key, patt )
{
local addr
if( patt )
{
record_op( key_to_int("<Enter>") )
vi_search_dir = ( key == "/" ? SEARCH_FORWARD : SEARCH_BACKWARD )
mark_context( MAJOR_MARK ) # mark context for motion
ex_com = key patt
addr = ex_address() # parse command, call vi_search() as needed
# execute op, if any, in line or normal mode
if( line_search )
{
vi_goto_line( addr )
xeq_op( LINE_SELECTION )
}
else
xeq_op()
}
else
op_flush()
}
local function search_hist( patt )
{
if( patt )
add_prompt_history( SEARCH_HIST, patt )
else
{
patt = current_history_item( SEARCH_HIST )
if( !patt )
vi_error( "Missing previous pattern" )
}
return patt
}
local function vi_search( pattern, dir )
{
local search_op
local orig_offset = buffer_offset
local offset_diff
# set vi_search_flags to not have a direction. We must do it this
# way since SEARCH_FORWARD = 4 and SEARCH_BACKWARD = 0
vi_search_flags = or(SEARCH_ADVANCE, and(search_flags, not(SEARCH_FORWARD)))
pattern = search_hist( pattern )
if( !search( pattern, or( vi_search_flags, dir )))
{
message("Pattern not found: " pattern )
vi_error() # never returns
}
else
{
if( eat_match( "z-" ))
scroll_window_bottom()
else if( eat_match( "z." ))
scroll_window_middle()
else if( eat_match( "z" ))
scroll_window_top()
else if( eat_match( ";" ))
if(( search_op = eat_match( "[/?]" )))
vi_search_on_pattern( search_op, ex_com )
else
vi_error( "syntax" )
offset_diff = (dir == SEARCH_FORWARD) ? buffer_offset - orig_offset :
orig_offset - buffer_offset
if (offset_diff < 0)
notify("Search wrapped around")
else
message("")
}
}
# search again is called by 4 different keys, with 4 different meanings:
#
# n search again, same direction as originally
# N search again, opposite direction as originally
# Alt-F5 search again, backwards
# Alt-F6 search again, forwards
#
function vi_search_again( key )
{
local dir
local last_dir = vi_search_dir
disallow_number()
op_type_2b( MAJOR_MARK )
#message("search again key is: %s", key)
# figure out what direction we're going...
if (key == "?")
dir = SEARCH_BACKWARD
else if (key == "/")
dir = SEARCH_FORWARD
else if (key == "N")
dir = (last_dir == SEARCH_FORWARD) ? SEARCH_BACKWARD : SEARCH_FORWARD
else
dir = last_dir
#dir = (argcount() ? (( key == "?" ) ? SEARCH_BACKWARD : SEARCH_FORWARD ) \
# : (( chr( int_to_ascii( current_key )) == "N" ) \
# ? (( vi_search_dir == SEARCH_FORWARD ) \
# ? SEARCH_BACKWARD : SEARCH_FORWARD ) \
# : vi_search_dir ))
message( "%s%s", ( dir == SEARCH_FORWARD ? "/" : "?" ), search_hist())
# execute search operator:
vi_search( "", dir )
xeq_op()
}
function vi_new_current_win()
{
# Do NOT call vi_reset() if bogus NEW_CURNT_WINDOW event (i.e. mouse click)
# or if we are already in ex_mode
if (!in_ex_mode && (current_window != insert_window || current_buffer != insert_buffer))
{
vi_process_end()
}
}
function vi_reset(force_keymap)
{
# message("In vi_reset, event_id is: %d", event_id)
if (event_id == EVENT.ERROR)
force_keymap = 1
number_register=0
pending_operator = ""
raise_anchor()
remove_selection()
vi_command_mode(force_keymap)
process_end()
}
function vi_delete_buffer()
{
# Delete the current buffer and make the NEXT buffer current
# (0 instead of 1 makes the previous buffer current).
delete_buffer_key("",1)
ex_show_file()
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
local function vi_keymap_valid(km)
{
local result = 1
if (km <= 0 || km > 16)
result = 0
# message("Keymap id is: %d", km)
# if ( (km <= 0) || !push_keymap(km))
# result = 0
# else
# pop_keymap()
return result
}
### create the VI keymaps if they have not already been
#
local function create_vi_command_keymaps()
{
# Enable the keymap. Subsequent mods to the keymap will persist
# through the end of the session.
# Only one-time initialization should follow this point.
if( vi_keymap_valid(vi_command_keymap) )
{
current_keymap = vi_command_keymap
}
else if (vi_keymap_valid(default_vi_command_keymap) )
{
vi_command_keymap = default_vi_command_keymap
current_keymap = vi_command_keymap
}
else
{
current_keymap = vi_command_keymap = create_keymap( empty_keymap )
assign_vi_command_keys()
optional_function("local_keys")
default_vi_command_keymap = create_keymap( vi_command_keymap )
}
}
local function assign_vi_command_keys()
{
assign_key( "0", "vi_digit" )
assign_key( "1", "vi_digit" )
assign_key( "2", "vi_digit" )
assign_key( "3", "vi_digit" )
assign_key( "4", "vi_digit" )
assign_key( "5", "vi_digit" )
assign_key( "6", "vi_digit" )
assign_key( "7", "vi_digit" )
assign_key( "8", "vi_digit" )
assign_key( "9", "vi_digit" )
assign_key( "<Alt-1>", "place_bookmark 1" )
assign_key( "<Alt-2>", "place_bookmark 2" )
assign_key( "<Alt-3>", "place_bookmark 3" )
assign_key( "<Alt-4>", "place_bookmark 4" )
assign_key( "<Alt-5>", "place_bookmark 5" )
assign_key( "<Alt-6>", "place_bookmark 6" )
assign_key( "<Alt-7>", "place_bookmark 7" )
assign_key( "<Alt-8>", "place_bookmark 8" )
assign_key( "<Alt-9>", "place_bookmark 9" )
assign_key( "<Alt-0>", "place_bookmark 10" )
assign_key( "a", "vi_a" )
assign_key( "b", "vi_b" )
assign_key( "c", "vi_operator" )
assign_key( "d", "vi_operator" )
assign_key( "e", "vi_e" )
assign_key( "f", "vi_find" )
assign_key( "g", "vi_goto 1" )
# assign_key( "h", "vi_left" )
assign_key( "h", "vi_arrow 3" )
assign_key( "i", "vi_i" )
# assign_key( "j", "vi_down" )
assign_key( "j", "vi_arrow 2" )
# assign_key( "k", "vi_up" )
# assign_key( "l", "vi_right" )
assign_key( "k", "vi_arrow 1" )
assign_key( "l", "vi_arrow 4" )
assign_key( "m", "vi_place_mark" )
assign_key( "n", "vi_search_again n" )
assign_key( "o", "vi_o" )
assign_key( "p", "vi_p" )
assign_key( "r", "vi_r" )
assign_key( "s", "vi_s" )
assign_key( "t", "vi_find" )
assign_key( "u", "vi_undo" )
assign_key( "w", "vi_w" )
assign_key( "x", "vi_x" )
assign_key( "y", "vi_operator" )
assign_key( "z", "vi_scroll_window" )
assign_key( "A", "vi_A" )
assign_key( "B", "vi_B" )
assign_key( "C", "vi_C" )
assign_key( "D", "vi_D" )
assign_key( "E", "vi_E" )
assign_key( "F", "vi_find" )
assign_key( "G", "vi_goto" )
assign_key( "H", "vi_H" )
assign_key( "I", "vi_I" )
assign_key( "J", "vi_J" )
assign_key( "L", "vi_L" )
assign_key( "M", "vi_M" )
assign_key( "N", "vi_search_again N" )
assign_key( "O", "vi_O" )
assign_key( "P", "vi_P" )
assign_key( "R", "vi_R" )
assign_key( "S", "vi_S" )
assign_key( "T", "vi_find" )
assign_key( "U", "vi_undo_line" )
assign_key( "W", "vi_W" )
assign_key( "X", "vi_X" )
assign_key( "Y", "vi_Y" )
assign_key( "Z", "vi_ZZ" )
assign_key( "<Alt-A>", "set_exclusive_mark" )
assign_key( "<Alt-B>", "buffer_list" )
assign_key( "<Alt-C>", "copy_to_scrap_key" )
assign_key( "<Alt-D>", "native_delete" )
assign_key( "<Alt-E>", "create_buf_and_win_key" )
assign_key( "<Alt-F>", "ex_show_file" )
assign_key( "<Alt-G>", "goto_line_or_mark" )
assign_key( "<Alt-H>", "display_help_item" )
assign_key( "<Alt-I>", function_id("toggle_buffer_flags", BUFFER_OVERTYPE_MODE))
assign_key( "<Alt-J>", "vi_J" )
assign_key( "<Alt-K>", "delete_to_eol" )
assign_key( "<Alt-L>", "set_line_mark" )
assign_key( "<Alt-M>", "mark_matching" )
assign_key( "<Alt-N>", "vi_next_buffer 1" )
assign_key( "<Alt-->", "vi_next_buffer 0" )
assign_key( "<Alt-O>", "change_output_name" )
assign_key( "<Alt-P>", "wrap_paragraph" )
assign_key( "<Alt-Q>", "done" )
assign_key( "<Alt-R>", "vi_read" )
assign_key( "<Alt-S>", "search_forward" )
assign_key( "<Alt-T>", "replace_forward" )
assign_key( "<Alt-U>", "undo" )
assign_key( "<Alt-V>", "print_version" )
assign_key( "<Alt-W>", "vi_save" )
assign_key( "<Alt-X>", "done" )
assign_key( "<Alt-Y>", "redo" )
assign_key( "<Alt-Z>", "system" )
assign_key( "<Ctrl-A>", "optional_function display_ascii_table" )
assign_key( "<Ctrl-B>", "page_up" ) # not a motion
assign_key( "<Ctrl-C>", "vi_process_end" )
assign_key( "<Ctrl-D>", "scroll_down_half" ) # not a motion
assign_key( "<Ctrl-E>", "scroll_down_1" ) # not a motion
assign_key( "<Ctrl-F>", "page_down" ) # not a motion
assign_key( "<Ctrl-G>", "ex_show_file" )
assign_key( "<Ctrl-H>", "vi_bksp" ) # Ctrl-H or Backspace
assign_key( "<Ctrl-I>", "vi_tab" ) # Ctrl-I or Tab
assign_key( "<Ctrl-L>", "scroll_left_1" )
assign_key( "<Ctrl-N>", "vi_arrow 2" ) # down
assign_key( "<Ctrl-P>", "vi_arrow 1" ) # up
assign_key( "<Ctrl-R>", "scroll_right_1" ) # inexact but appropriate substitution
assign_key( "<Ctrl-T>", "vi_tags_pop" )
assign_key( "<Ctrl-U>", "scroll_up_half" ) # not a motion
assign_key( "<Ctrl-Y>", "scroll_up_1" ) # not a motion
assign_key( "<Ctrl-Z>", "system" )
assign_key( "<Ctrl-->", "vi_delete_buffer" )
assign_key( "<Esc>", "vi_reset 1" )
assign_key( "<PageUp>" , "page_up" ) # not a motion
assign_key( "<PageDown>" , "page_down" ) # not a motion
assign_key( "<Home>" , "vi_goto_bol" )
assign_key( "<End>" , "vi_goto_eol" )
assign_key( "<Up>" , "vi_arrow 1" )
assign_key( "<Down>" , "vi_arrow 2" )
assign_key( "<Left>" , "vi_arrow 3" )
assign_key( "<Right>" , "vi_arrow 4" )
assign_key( "<BackTab>" , "vi_back_tab" ) #in PM use BackTab, else use shift-tab
assign_key( "<Tab>" , "vi_tab" ) # Ctrl-I or Tab
assign_key( "<Enter>" , "vi_next_line" )
assign_key( "<Space>" , "vi_space" )
assign_key( "<Shift-Space>" , "vi_space" )
assign_key( "<Backspace>" , "vi_bksp" ) # Ctrl-H or Backspace
assign_key( "<Insert>" , "vi_P" )
#assign_key( "<Delete>" , "vi_yank 1" )
assign_key( "<Delete>" , "delete_chars" )
assign_key( "<Ctrl-PageUp>" , "goto_buffer_top" ) # extension!
assign_key( "<Ctrl-PageDown>", "goto_buffer_bottom" ) # extension!
assign_key( "<Ctrl-Left>" , "vi_B" )
assign_key( "<Ctrl-Right>" , "vi_W" )
assign_key( "<Ctrl-Home>" , "goto_window_top" )
assign_key( "<Ctrl-End>" , "goto_window_bottom" )
assign_key( "<Alt-Tab>" , "vi_back_tab" )
assign_key( "^", "vi_skip_whitespace" )
assign_key( "$", "vi_goto_eol" )
assign_key( "|", "vi_goto_column" )
assign_key( "+", "vi_next_line" )
assign_key( "-", "vi_prev_line" )
assign_key( ":", "ex_key" )
assign_key( "?", "vi_search_key ?" )
assign_key( "/", "vi_search_key /" )
assign_key( "%", "vi_goto_matching" )
assign_key( "(", "vi_prev_sent" )
assign_key( ")", "vi_next_sent" )
assign_key( "{", "vi_prev_para" )
assign_key( "}", "vi_next_para" )
assign_key( "[", "vi_prev_sect" )
assign_key( "]", "vi_next_sect" )
assign_key( ";", "vi_find_repeat" )
assign_key( ",", "vi_find_reverse" )
assign_key( "'", "vi_goto_mark" )
assign_key( "`", "vi_goto_mark" )
assign_key( ">", "vi_operator" )
assign_key( "!", "vi_operator" )
assign_key( ".", "vi_again" )
assign_key( "~", "vi_tilde" )
assign_key( "&", "ex_subs_repeat" )
assign_key( "\"", "vi_bufid_key" )
assign_key( "<", "vi_operator" )
# assign_key( "<Num-4>", "left" )
# assign_key( "<Num-6>", "right" )
assign_key( "<Num-->", "vi_prev_line" )
assign_key( "<Num-+>", "vi_next_line" )
assign_key( "<Ctrl-^>", "last_buffer_key" )
assign_key( "<Ctrl-]>", "vi_tags_auto" )
assign_key( "<F3>", "vi_next_buffer 0" )
assign_key( "<F4>", "vi_next_buffer 1" )
assign_key( "<F5>", "vi_search_key ?" )
assign_key( "<F6>", "vi_search_key /" )
# assign_key( "<F7>", "record_key" )
# assign_key( "<F8>", "playback" )
assign_key( "<F7>", "record_macro" )
assign_key( "<F8>", "playback_macro" )
assign_key( "<F9>", "system_key" )
assign_key( "<F10>", "invoke_function" )
# assign_key( "<Alt-F1>", "split_window_horizontal" )
# assign_key( "<Alt-F2>", "split_window_vertical" )
# assign_key( "<Alt-F3>", "make_window" )
# assign_key( "<Alt-F4>", "delete_tiled_window" )
assign_key( "<Alt-F4>", "done" )
assign_key( "<Alt-F5>", "vi_search_again ?" )
assign_key( "<Alt-F6>", "vi_search_again /" )
assign_key( "<Alt-F7>", "learn_key" )
# assign_key( "<Alt-F8>", )
# assign_key( "<Alt-F9>", )
assign_key( "<Alt-F10>", "compile_buffer" )
# electric-c template expansion keys
assign_key( VI_EXPAND_TEMPLATE_KEY, "beep" )
assign_key( VI_NEXT_FIELD_KEY, "beep" )
# assign_key( "<Shift-F1>", "smaller_window" )
# assign_key( "<Shift-F2>", "larger_window" )
# assign_key( "<Shift-F3>", "organize_windows" )
# assign_key( "<Shift-F4>", "organize_buffers" )
# assign_key( "<Shift-F5>", )
# assign_key( "<Shift-F6>", )
# assign_key( "<Shift-F7>", "display_errors" )
# assign_key( "<Shift-F8>", "goto_next_error" )
# assign_key( "<Shift-F9>", )
# assign_key( "<Shift-F10>", )
assign_key( "<Ctrl-F1>", "display_help_item" )
# assign_key( "<Ctrl-F2>", )
# assign_key( "<Ctrl-F3>", )
# assign_key( "<Ctrl-F4>", )
# assign_key( "<Ctrl-F5>", )
# assign_key( "<Ctrl-F6>", )
# assign_key( "<Ctrl-F7>", )
# assign_key( "<Ctrl-F8>", )
# assign_key( "<Ctrl-F9>", )
# assign_key( "<Ctrl-F10>", )
}
local function create_vi_insert_keymaps()
{
# Enable the keymap. Subsequent mods to the keymap will persist
# through the end of the session.
# Only one-time initialization should follow this point.
if( vi_insert_keymap > 0 )
{
current_keymap = vi_insert_keymap
}
else if (default_vi_insert_keymap > 0)
{
current_keymap = vi_insert_keymap = default_vi_insert_keymap
}
else
{
current_keymap = vi_insert_keymap = create_keymap( ascii_keymap )
# changed the following from process_end to vi_process_end to add
# some fail-safe processing.
assign_key( "<Esc>", "vi_process_end" )
assign_key( "<Ctrl-]>", "vi_process_end" ) # Esc or Ctrl-]
assign_key( "<Ctrl-C>", "vi_process_end" )
assign_key( "<Ctrl-@>", "vi_reinsert" )
assign_key( "<Ctrl-Enter>", "vi_insert_nl" ) # Ctrl-J or Ctrl-Enter
assign_key( "<Ctrl-J>", "vi_insert_nl" ) # Ctrl-J or Ctrl-Enter
assign_key( "<Enter>", "vi_insert_cr" ) # Ctrl-M or Enter
assign_key( "<Backspace>", "vi_insert_bs" )
assign_key( "<Shift-Backspace>", "vi_insert_bs" )
assign_key( "<Ctrl-H>", "vi_insert_bs" )
# assign_key( "#", "vi_insert_bs" ) # obsolete
assign_key( "<Ctrl-W>", "vi_insert_dB" )
assign_key( "<Ctrl-U>", "vi_insert_cancel" )
assign_key( "<Delete>", "vi_insert_cancel" )
assign_key( "<Up>" , "vi_insert_arrow 1" )
assign_key( "<Down>" , "vi_insert_arrow 2" )
assign_key( "<Left>" , "vi_insert_arrow 3" )
assign_key( "<Right>" , "vi_insert_arrow 4" )
assign_key( "<Num-Up>" , "vi_insert_arrow 1" )
assign_key( "<Num-Down>" , "vi_insert_arrow 2" )
assign_key( "<Num-Left>" , "vi_insert_arrow 3" )
assign_key( "<Num-Right>" , "vi_insert_arrow 4" )
assign_key( "<Home>" , "vi_insert_arrow 5" )
assign_key( "<End>" , "vi_insert_arrow 6" )
assign_key( "<PageUp>" , "vi_insert_arrow 7" )
assign_key( "<PageDown>" , "vi_insert_arrow 8" )
# assign_key( "@", "vi_insert_cancel" ) # obsolete
assign_key( "<Ctrl-V>", "vi_insert_quoted" )
assign_key( "<Ctrl-D>", "vi_insert_unindent" )
assign_key( "<Ctrl-T>", "vi_insert_reindent" )
assign_key( "<Space>", "insert_key 32" )
assign_key( "<Shift-Space>", "insert_key 32" )
# electric-c template expansion keys
assign_key( VI_EXPAND_TEMPLATE_KEY, "expand_template_key" )
assign_key( VI_NEXT_FIELD_KEY, "next_field_key" ) # Ctrl-J or Ctrl-Enter
# assign_key( "<Space>", "vi_expand_template" )
# assign_key( "<Tab>", "vi_expand_template" )
#this was Enter
# assign_key( "<Ctrl-X>", "vi_expand_template" )
default_vi_insert_keymap = create_keymap( vi_insert_keymap )
}
}
# This function will restore the menu and toolbar functions after VI
# has messed them up
#
function restore_id()
{
local tbhand = toolbar_info(0);
local undo = "undo"
local redo = "redo"
local cut = "delete_to_scrap"
local copy = "copy_to_scrap_key"
local paste = "insert_scrap"
menu_functions[IDM_UNDO] = undo
menu_functions[IDM_REDO] = redo
menu_functions[IDM_CUT] = cut
menu_functions[IDM_COPY] = copy
menu_functions[IDM_PASTE] = paste
menu_functions[POP_CUT] = cut
menu_functions[POP_COPY] = copy
menu_functions[POP_PASTE] = paste
if ( tbhand )
{
modify_toolbaritem( tbhand, IDM_UNDO, TI_FUNCTIONID, function_id(undo))
modify_toolbaritem( tbhand, IDM_REDO, TI_FUNCTIONID, function_id(redo))
modify_toolbaritem( tbhand, IDM_CUT, TI_FUNCTIONID, function_id(cut))
modify_toolbaritem( tbhand, IDM_COPY, TI_FUNCTIONID, function_id(copy))
modify_toolbaritem( tbhand, IDM_PASTE, TI_FUNCTIONID, function_id(paste))
}
}
# this function is used to customize the menu so that the keys you have
# assigned to do the same thing as the menuitem will show on the menu
# It should be global and start with the string inside emulation_mode
function vi_menu()
{
local tbhand = toolbar_info(0);
local menu = menu_info(0, MI_MENUHANDLE);
local undo = "vi_undo"
local redo = "vi_undo"
local cut = "vi_yank 1"
local copy = "vi_Y"
local paste = "vi_P"
menu_functions[IDM_UNDO] = undo
menu_functions[IDM_REDO] = redo
menu_functions[IDM_CUT] = cut
menu_functions[IDM_COPY] = copy
menu_functions[IDM_PASTE] = paste
menu_functions[POP_CUT] = cut
menu_functions[POP_COPY] = copy
menu_functions[POP_PASTE] = paste
modify_toolbaritem( tbhand, IDM_UNDO, TI_FUNCTIONID, function_id(undo))
modify_toolbaritem( tbhand, IDM_REDO, TI_FUNCTIONID, function_id(redo))
modify_toolbaritem( tbhand, IDM_CUT, TI_FUNCTIONID, function_id(cut))
modify_toolbaritem( tbhand, IDM_COPY, TI_FUNCTIONID, function_id(copy))
modify_toolbaritem( tbhand, IDM_PASTE, TI_FUNCTIONID, function_id(paste))
if ( menu )
{
modify_menuitem( menu, IDM_ADDFILE, MI_KEYSTEXT, ":e filename")
modify_menuitem( menu, IDM_INSERTFILE, MI_KEYSTEXT, ":r")
modify_menuitem( menu, IDM_SAVE, MI_KEYSTEXT, ":w")
modify_menuitem( menu, IDM_SAVEAS, MI_KEYSTEXT, ":w filename")
modify_menuitem( menu, IDM_SAVEALL, MI_KEYSTEXT, "")
modify_menuitem( menu, IDM_PRINT, MI_KEYSTEXT, "")
modify_menuitem( menu, IDM_EXIT, MI_KEYSTEXT, ":q")
modify_menuitem( menu, IDM_UNDO, MI_KEYSTEXT, "u")
modify_menuitem( menu, IDM_REDO, MI_KEYSTEXT, "u")
modify_menuitem( menu, IDM_CUT, MI_KEYSTEXT, "Delete")
modify_menuitem( menu, IDM_COPY, MI_KEYSTEXT, "Y")
modify_menuitem( menu, IDM_PASTE, MI_KEYSTEXT, "P")
modify_menuitem( menu, IDM_DELETE, MI_KEYSTEXT, "d")
modify_menuitem( menu, IDM_FIND, MI_KEYSTEXT, "/")
modify_menuitem( menu, IDM_FINDAGAIN, MI_KEYSTEXT, "n")
modify_menuitem( menu, IDM_CHANGE, MI_KEYSTEXT, "")
modify_menuitem( menu, IDM_GOTOLINE, MI_KEYSTEXT, "(number) G")
modify_menuitem( menu, IDM_COMMAND, MI_KEYSTEXT, "F10")
modify_menuitem( menu, IDM_NEXT, MI_KEYSTEXT, "F3")
modify_menuitem( menu, IDM_PREV, MI_KEYSTEXT, "F4")
modify_menuitem( menu, IDM_CLOSE, MI_KEYSTEXT, "")
modify_menuitem( menu, IDM_BUFFERLIST, MI_KEYSTEXT, "")
modify_menuitem( menu, IDM_INDEX, MI_KEYSTEXT, "F1")
modify_menuitem( menu, IDM_KEYWORDHELP, MI_KEYSTEXT, "Ctrl-F1" )
}
}