This is normal.c in view mode; [Download] [Up]
/* vi:set ts=4 sw=4:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
*/
/*
* Contains the main routine for processing characters in command mode.
* Communicates closely with the code in ops.c to handle the operators.
*/
#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "option.h"
/*
* The Visual area is remembered for reselection.
*/
static int resel_VIsual_mode = NUL; /* 'v', 'V', or Ctrl-V */
static linenr_t resel_VIsual_line_count; /* number of lines */
static colnr_t resel_VIsual_col; /* nr of cols or end col */
static int search_dont_set_mark = FALSE; /* for "*" and "#" */
/*
* Operator characters; The order must correspond to the defines in vim.h!
*/
static char_u *op_chars = (char_u *)"dyc<>!~=Q:UuJq";
static void op_colon __ARGS((OPARG *oap, int VIsual_was_active));
#ifdef USE_MOUSE
static void find_start_of_word __ARGS((FPOS *));
static void find_end_of_word __ARGS((FPOS *));
static int get_mouse_class __ARGS((int));
#endif
static void prep_redo_cmd __ARGS((CMDARG *cap));
static void prep_redo __ARGS((int regname, long, int, int, int, int));
static int checkclearop __ARGS((OPARG *oap));
static int checkclearopq __ARGS((OPARG *oap));
static void clearop __ARGS((OPARG *oap));
static void clearopbeep __ARGS((OPARG *oap));
static void del_from_showcmd __ARGS((int));
static void do_gd __ARGS((OPARG *oap, int nchar));
static int screengo __ARGS((OPARG *oap, int dir, long dist));
static void do_zet __ARGS((CMDARG *cap));
static void do_ident __ARGS((CMDARG *cap, int whole_word));
static void do_scroll __ARGS((CMDARG *cap));
static void do_right __ARGS((CMDARG *cap));
static int do_left __ARGS((CMDARG *cap));
static void do_gotofile __ARGS((CMDARG *cap));
static void do_csearch __ARGS((CMDARG *cap, int dir, int type));
static void do_brackets __ARGS((CMDARG *cap, int dir));
static void do_percent __ARGS((CMDARG *cap));
static int do_replace __ARGS((CMDARG *cap));
static void switch_visual __ARGS((void));
static void do_swapchar __ARGS((CMDARG *cap));
static void do_cursormark __ARGS((CMDARG *cap, int flag, FPOS *pos));
static void do_visop __ARGS((CMDARG *cap));
static void do_optrans __ARGS((CMDARG *cap));
static void do_gomark __ARGS((CMDARG *cap, int flag));
static void do_pcmark __ARGS((CMDARG *cap));
static void do_regname __ARGS((CMDARG *cap, linenr_t opnum));
static void do_visual __ARGS((CMDARG *cap));
static int do_g_cmd __ARGS((CMDARG *cap));
static int do_opencmd __ARGS((CMDARG *cap));
static void do_operator __ARGS((CMDARG *cap));
static void do_lineop __ARGS((CMDARG *cap));
static void do_wordcmd __ARGS((CMDARG *cap, int type));
static void do_goto __ARGS((OPARG *oap, long lnum));
static void do_esc __ARGS((CMDARG *oap, linenr_t opnum));
/*
* normal
*
* Execute a command in normal mode.
*
* This is basically a big switch with the cases arranged in rough categories
* in the following order:
*
* 0. Macros (q, @)
* 1. Screen positioning commands (^U, ^D, ^F, ^B, ^E, ^Y, z)
* 2. Control commands (:, <help>, ^L, ^G, ^^, ZZ, *, ^], ^T)
* 3. Cursor motions (G, H, M, L, l, K_RIGHT, , h, K_LEFT, ^H, k, K_UP,
* ^P, +, CR, LF, j, K_DOWN, ^N, _, |, B, b, W, w, E, e, $, ^, 0)
* 4. Searches (?, /, n, N, T, t, F, f, ,, ;, ], [, %, (, ), {, })
* 5. Edits (., u, K_UNDO, ^R, U, r, J, p, P, ^A, ^S)
* 6. Inserts (A, a, I, i, o, O, R)
* 7. Operators (~, d, c, y, >, <, !, =, Q)
* 8. Abbreviations (x, X, D, C, s, S, Y, &)
* 9. Marks (m, ', `, ^O, ^I)
* 10. Register name setting ('"')
* 11. Visual (v, V, ^V)
* 12. Suspend (^Z)
* 13. Window commands (^W)
* 14. extended commands (starting with 'g')
* 15. mouse click
* 16. scrollbar movement
* 17. The end (ESC)
*/
void
normal_cmd(oap)
OPARG *oap;
{
static linenr_t opnum = 0; /* count before an operator */
CMDARG ca; /* command arguments */
int c;
long n = 0; /* init for GCC */
int flag = FALSE;
int type = 0; /* type of operation */
int dir = FORWARD; /* search direction */
int finish_op;
char_u *searchbuff = NULL; /* buffer for search string */
int command_busy = FALSE;
int ctrl_w = FALSE; /* got CTRL-W command */
int old_col = curwin->w_curswant;
int dont_adjust_op_end = FALSE;
FPOS old_pos; /* cursor position before command */
vim_memset(&ca, 0, sizeof(ca));
ca.oap = oap;
/*
* If there is an operator pending, then the command we take this time
* will terminate it. Finish_op tells us to finish the operation before
* returning this time (unless the operation was cancelled).
*/
finish_op = (oap->op_type != OP_NOP);
if (!finish_op && !oap->regname)
opnum = 0;
State = NORMAL_BUSY;
c = vgetc();
#ifdef HAVE_LANGMAP
LANGMAP_ADJUST(c, TRUE);
#endif
if (c == NUL)
c = K_ZERO;
(void)add_to_showcmd(c, FALSE);
getcount:
/* Pick up any leading digits and compute ca.count0 */
while ( (c >= '1' && c <= '9')
|| (ca.count0 != 0 && (c == K_DEL || c == '0')))
{
if (c == K_DEL)
{
ca.count0 /= 10;
del_from_showcmd(4); /* delete the digit and ~@% */
}
else
ca.count0 = ca.count0 * 10 + (c - '0');
if (ca.count0 < 0) /* got too large! */
ca.count0 = 999999999;
if (ctrl_w)
{
++no_mapping;
++allow_keys; /* no mapping for nchar, but keys */
}
c = vgetc();
#ifdef HAVE_LANGMAP
LANGMAP_ADJUST(c, TRUE);
#endif
if (ctrl_w)
{
--no_mapping;
--allow_keys;
}
(void)add_to_showcmd(c, FALSE);
}
/*
* If we got CTRL-W there may be a/another count
*/
if (c == Ctrl('W') && !ctrl_w && oap->op_type == OP_NOP)
{
ctrl_w = TRUE;
opnum = ca.count0; /* remember first count */
ca.count0 = 0;
++no_mapping;
++allow_keys; /* no mapping for nchar, but keys */
c = vgetc(); /* get next character */
#ifdef HAVE_LANGMAP
LANGMAP_ADJUST(c, TRUE);
#endif
--no_mapping;
--allow_keys;
(void)add_to_showcmd(c, FALSE);
goto getcount; /* jump back */
}
ca.cmdchar = c;
/*
* If we're in the middle of an operator (including after entering a yank
* buffer with '"') AND we had a count before the
* operator, then that count overrides the current value of ca.count0.
* What * this means effectively, is that commands like "3dw" get turned
* into "d3w" which makes things fall into place pretty neatly.
* If you give a count before AND after the operator, they are multiplied.
*/
if (opnum != 0)
{
if (ca.count0)
ca.count0 *= opnum;
else
ca.count0 = opnum;
}
/*
* Always remember the count. It will be set to zero (on the next call,
* above) when there is no pending operator.
*/
opnum = ca.count0;
ca.count1 = (ca.count0 == 0 ? 1 : ca.count0);
/*
* Get an additional character if we need one.
* For CTRL-W we already got it when looking for a count.
*/
if (ctrl_w)
{
ca.nchar = ca.cmdchar;
ca.cmdchar = Ctrl('W');
}
else if ( (oap->op_type == OP_NOP
&& vim_strchr((char_u *)"@zm\"", ca.cmdchar) != NULL)
|| (oap->op_type == OP_NOP
&& !VIsual_active
&& vim_strchr((char_u *)"rZ", ca.cmdchar) != NULL)
|| vim_strchr((char_u *)"tTfF[]g'`", ca.cmdchar) != NULL
|| (ca.cmdchar == 'q'
&& !Recording
&& !Exec_reg))
{
++no_mapping;
++allow_keys; /* no mapping for nchar, but allow key codes */
#ifdef USE_GUI
if (ca.cmdchar == 'r' && gui.in_use)
{
State = REPLACE; /* pretend Replace mode, for cursor shape */
gui_update_cursor(TRUE);
}
#endif
ca.nchar = vgetc();
#ifdef USE_GUI
State = NORMAL_BUSY;
#endif
#ifdef HAVE_LANGMAP
/* adjust chars > 127, except after tTfFr command */
LANGMAP_ADJUST(ca.nchar,
vim_strchr((char_u *)"tTfFr", ca.cmdchar) == NULL);
#endif
#ifdef RIGHTLEFT
/* adjust Hebrew mapped char */
if (p_hkmap && vim_strchr((char_u *)"tTfFr", ca.cmdchar) && KeyTyped)
ca.nchar = hkmap(ca.nchar);
#endif
--no_mapping;
--allow_keys;
(void)add_to_showcmd(ca.nchar, FALSE);
}
/*
* flush the showcmd characters onto the screen so we can see them while
* the command is being executed
*/
if (p_sc)
flushbuf();
State = NORMAL;
if (ca.nchar == ESC)
{
clearop(oap);
goto normal_end;
}
msg_didout = FALSE; /* don't scroll screen up for normal command */
msg_col = 0;
old_pos = curwin->w_cursor; /* remember where cursor was */
#ifdef RIGHTLEFT
if (curwin->w_p_rl && KeyTyped) /* invert horizontal operations */
switch (ca.cmdchar)
{
case 'l': ca.cmdchar = 'h'; break;
case K_RIGHT: ca.cmdchar = K_LEFT; break;
case 'h': ca.cmdchar = 'l'; break;
case K_LEFT: ca.cmdchar = K_RIGHT; break;
case '>': ca.cmdchar = '<'; break;
case '<': ca.cmdchar = '>'; break;
}
#endif
/*
* Generally speaking, every command below should either clear any pending
* operator (with *clearop*()), or set the motion type variable
* oap->motion_type.
*
* When a cursor motion command is made, it is marked as being a character or
* line oriented motion. Then, if an operator is in effect, the operation
* becomes character or line oriented accordingly.
*/
/*
* Variables available here:
* ca.cmdchar command character
* ca.nchar extra command character
* ca.count0 count before command (0 if no count given)
* ca.count1 count before command (1 if no count given)
* oap Operator Arguments (same as ca.oap)
* flag is FALSE, use as you like.
* dir is FORWARD, use as you like.
*/
switch (ca.cmdchar)
{
/*
* 0: Macros
*/
case 'q': /* (stop) recording into a named register */
if (checkclearop(oap))
break;
/* command is ignored while executing a register */
if (!Exec_reg && do_record(ca.nchar) == FAIL)
clearopbeep(oap);
break;
case '@': /* execute a named register */
if (checkclearop(oap))
break;
while (ca.count1--)
{
if (do_execreg(ca.nchar, FALSE, FALSE) == FAIL)
{
clearopbeep(oap);
break;
}
}
break;
/*
* 1: Screen positioning commands
*/
case Ctrl('D'):
flag = TRUE;
case Ctrl('U'):
if ( (ca.cmdchar == Ctrl('U')
&& curwin->w_cursor.lnum == 1)
|| (ca.cmdchar == Ctrl('D')
&& curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count))
clearopbeep(oap);
else
{
if (checkclearop(oap))
break;
halfpage(flag, ca.count0);
}
break;
case Ctrl('B'):
case K_S_UP:
case K_PAGEUP:
case K_KPAGEUP:
dir = BACKWARD;
case Ctrl('F'):
case K_S_DOWN:
case K_PAGEDOWN:
case K_KPAGEDOWN:
if (checkclearop(oap))
break;
(void)onepage(dir, ca.count1);
break;
case Ctrl('E'):
if (checkclearop(oap))
break;
scrollup(ca.count1);
if (p_so)
cursor_correct();
coladvance(curwin->w_curswant);
update_screen(VALID);
break;
case Ctrl('Y'):
if (checkclearop(oap))
break;
scrolldown(ca.count1);
if (p_so)
cursor_correct();
coladvance(curwin->w_curswant);
update_screen(VALID);
break;
case 'z':
if (!checkclearop(oap))
do_zet(&ca);
break;
/*
* 2: Control commands
*/
case ':':
if (VIsual_active)
{
do_operator(&ca);
break;
}
if (checkclearop(oap))
break;
/*
* translate "count:" into ":.,.+(count - 1)"
*/
if (ca.count0)
{
stuffReadbuff((char_u *)".");
if (ca.count0 > 1)
{
stuffReadbuff((char_u *)",.+");
stuffnumReadbuff((long)ca.count0 - 1L);
}
}
do_cmdline(NULL, getexline, NULL, 0);
break;
case 'Q':
/*
* Ignore 'Q' in Visual mode, just give a beep.
*/
if (VIsual_active)
vim_beep();
else
do_exmode();
break;
case K_HELP:
case K_F1:
if (!checkclearopq(oap))
do_help((char_u *)"");
break;
case Ctrl('L'):
if (!checkclearop(oap))
{
#ifdef BEBOX
/*
* Right now, the BeBox doesn't seem to have an easy way to detect
* window resizing, so we cheat and make the user detect it
* manually with CTRL-L instead
*/
mch_get_winsize();
#endif
update_screen(CLEAR);
}
break;
case Ctrl('G'):
/* print full name if count given or :cd used */
if (!checkclearop(oap))
fileinfo((int)ca.count0, FALSE, FALSE);
break;
case K_CCIRCM: /* CTRL-^, short for ":e #" */
if (!checkclearopq(oap))
(void)buflist_getfile((int)ca.count0, (linenr_t)0,
GETF_SETMARK|GETF_ALT, FALSE);
break;
/*
* "ZZ": write if changed, and exit window
* "ZQ": quit window (Elvis compatible)
*/
case 'Z':
if (!checkclearopq(oap))
{
if (ca.nchar == 'Z')
stuffReadbuff((char_u *)":x\n");
else if (ca.nchar == 'Q')
stuffReadbuff((char_u *)":q!\n");
else
clearopbeep(oap);
}
break;
case 163: /* the pound sign, '#' for English keyboards */
ca.cmdchar = '#';
/*FALLTHROUGH*/
case Ctrl(']'): /* :ta to current identifier */
case 'K': /* run program for current identifier */
case '*': /* / to current identifier or string */
case '#': /* ? to current identifier or string */
do_ident(&ca, TRUE);
break;
case Ctrl('T'): /* backwards in tag stack */
if (!checkclearopq(oap))
do_tag((char_u *)"", 2, (int)ca.count1, FALSE);
break;
/*
* Cursor motions
*/
case 'G':
do_goto(oap, ca.count0 == 0 ? (long)curbuf->b_ml.ml_line_count
: ca.count0);
break;
case 'H':
case 'M':
case 'L':
oap->motion_type = MLINE;
setpcmark();
do_scroll(&ca);
cursor_correct(); /* correct for 'so' */
beginline(MAYBE);
break;
case 'l':
case K_RIGHT:
case ' ':
do_right(&ca);
break;
case 'h':
case K_LEFT:
case K_BS:
case Ctrl('H'):
dont_adjust_op_end = do_left(&ca);
break;
case '-':
flag = TRUE;
/* FALLTHROUGH */
case 'k':
case K_UP:
case Ctrl('P'):
oap->motion_type = MLINE;
if (cursor_up(ca.count1, oap->op_type != OP_NOP) == FAIL)
clearopbeep(oap);
else if (flag)
beginline(TRUE);
break;
case '+':
case CR:
flag = TRUE;
/* FALLTHROUGH */
case 'j':
case K_DOWN:
case Ctrl('N'):
case NL:
oap->motion_type = MLINE;
if (cursor_down(ca.count1, oap->op_type != OP_NOP) == FAIL)
clearopbeep(oap);
else if (flag)
beginline(TRUE);
break;
/*
* This is a strange motion command that helps make operators more
* logical. It is actually implemented, but not documented in the
* real Vi. This motion command actually refers to "the current
* line". Commands like "dd" and "yy" are really an alternate form of
* "d_" and "y_". It does accept a count, so "d3_" works to delete 3
* lines.
*/
case '_':
do_lineop(&ca);
break;
case K_HOME:
case K_KHOME:
if ((mod_mask & MOD_MASK_CTRL)) /* CTRL-HOME = goto line 1 */
{
do_goto(oap, 1L);
break;
}
ca.count0 = 1;
/* FALLTHROUGH */
case '|':
oap->motion_type = MCHAR;
oap->inclusive = FALSE;
beginline(FALSE);
if (ca.count0 > 0)
{
coladvance((colnr_t)(ca.count0 - 1));
curwin->w_curswant = (colnr_t)(ca.count0 - 1);
}
else
curwin->w_curswant = 0;
/* keep curswant at the column where we wanted to go, not where
we ended; differs is line is too short */
curwin->w_set_curswant = FALSE;
break;
/*
* Word Motions
*/
case 'B':
type = 1;
/* FALLTHROUGH */
case 'b':
case K_S_LEFT:
oap->motion_type = MCHAR;
oap->inclusive = FALSE;
curwin->w_set_curswant = TRUE;
if (bck_word(ca.count1, type, FALSE) == FAIL)
clearopbeep(oap);
break;
case 'E':
type = TRUE;
/* FALLTHROUGH */
case 'e':
oap->inclusive = TRUE;
do_wordcmd(&ca, type);
break;
case 'W':
type = TRUE;
/* FALLTHROUGH */
case 'w':
case K_S_RIGHT:
oap->inclusive = FALSE;
do_wordcmd(&ca, type);
break;
case K_END:
case K_KEND:
if ((mod_mask & MOD_MASK_CTRL)) /* CTRL-END = goto last line */
{
do_goto(oap, curbuf->b_ml.ml_line_count);
break;
}
/* FALLTHROUGH */
case '$':
oap->motion_type = MCHAR;
oap->inclusive = TRUE;
curwin->w_curswant = MAXCOL; /* so we stay at the end */
if (cursor_down((long)(ca.count1 - 1), oap->op_type != OP_NOP) == FAIL)
{
clearopbeep(oap);
break;
}
break;
case '^':
flag = TRUE;
/* FALLTHROUGH */
case '0':
oap->motion_type = MCHAR;
oap->inclusive = FALSE;
beginline(flag);
break;
/*
* 4: Searches
*/
case '?':
case '/':
if ((searchbuff = getcmdline(ca.cmdchar, ca.count1, 0)) == NULL)
{
clearop(oap);
break;
}
oap->motion_type = MCHAR;
oap->inclusive = FALSE;
curwin->w_set_curswant = TRUE;
n = do_search(oap, ca.cmdchar, searchbuff, ca.count1,
(search_dont_set_mark ? 0 : SEARCH_MARK) |
SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG);
if (n == 0)
clearop(oap);
else if (n == 2)
oap->motion_type = MLINE;
search_dont_set_mark = FALSE;
/* "/$" will put the cursor after the end of the line, may need to
* correct that here */
adjust_cursor();
break;
case 'N':
flag = SEARCH_REV;
case 'n':
oap->motion_type = MCHAR;
oap->inclusive = FALSE;
curwin->w_set_curswant = TRUE;
if (!do_search(oap, 0, NULL, ca.count1,
SEARCH_MARK | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG | flag))
clearop(oap);
/* "/$" will put the cursor after the end of the line, may need to
* correct that here */
adjust_cursor();
break;
/*
* Character searches
*/
case 'T':
dir = BACKWARD;
/* FALLTHROUGH */
case 't':
do_csearch(&ca, dir, TRUE);
break;
case 'F':
dir = BACKWARD;
/* FALLTHROUGH */
case 'f':
do_csearch(&ca, dir, FALSE);
break;
case ',':
flag = 1;
/* FALLTHROUGH */
case ';':
/* ca.nchar == NUL, thus repeat previous search */
do_csearch(&ca, flag, FALSE);
break;
/*
* section or C function searches
*/
case '[':
dir = BACKWARD;
/* FALLTHROUGH */
case ']':
do_brackets(&ca, dir);
break;
case '%':
do_percent(&ca);
break;
case '(':
dir = BACKWARD;
/* FALLTHROUGH */
case ')':
oap->motion_type = MCHAR;
if (ca.cmdchar == ')')
oap->inclusive = FALSE;
else
oap->inclusive = TRUE;
curwin->w_set_curswant = TRUE;
if (findsent(dir, ca.count1) == FAIL)
clearopbeep(oap);
break;
case '{':
dir = BACKWARD;
/* FALLTHROUGH */
case '}':
oap->motion_type = MCHAR;
oap->inclusive = FALSE;
curwin->w_set_curswant = TRUE;
if (!findpar(oap, dir, ca.count1, NUL, FALSE))
clearopbeep(oap);
break;
/*
* 5: Edits
*/
case '.': /* redo command */
if (!checkclearopq(oap))
{
/*
* if restart_edit is TRUE, the last but one command is repeated
* instead of the last command (inserting text). This is used for
* CTRL-O <.> in insert mode
*/
if (start_redo(ca.count0, restart_edit && !arrow_used) == FAIL)
clearopbeep(oap);
}
break;
case 'u': /* undo */
if (VIsual_active ||
oap->op_type == vim_strchr(op_chars, 'u') - op_chars + 1)
{
do_operator(&ca);
break;
}
/* FALLTHROUGH */
case K_UNDO:
if (!checkclearopq(oap))
{
u_undo((int)ca.count1);
curwin->w_set_curswant = TRUE;
}
break;
case Ctrl('R'): /* undo undo */
if (!checkclearopq(oap))
{
u_redo((int)ca.count1);
curwin->w_set_curswant = TRUE;
}
break;
case 'U': /* Undo line */
/* In visual mode and typing "gUU" triggers an operator */
if (VIsual_active ||
oap->op_type == vim_strchr(op_chars, 'U') - op_chars + 1)
{
do_operator(&ca);
break;
}
if (!checkclearopq(oap))
{
u_undoline();
curwin->w_set_curswant = TRUE;
}
break;
case 'r':
if (VIsual_active)
{
ca.cmdchar = 'c';
do_operator(&ca);
break;
}
if (!checkclearop(oap))
command_busy = do_replace(&ca);
break;
case 'J':
if (VIsual_active) /* join the visual lines */
{
do_operator(&ca);
break;
}
if (!checkclearop(oap))
{
if (ca.count0 <= 1)
ca.count0 = 2; /* default for join is two lines! */
if (curwin->w_cursor.lnum + ca.count0 - 1 >
curbuf->b_ml.ml_line_count)
{
clearopbeep(oap); /* beyond last line */
break;
}
prep_redo_cmd(&ca);
do_do_join(ca.count0, TRUE, TRUE);
}
break;
case 'P':
dir = BACKWARD;
/* FALLTHROUGH */
case 'p':
/*
* 'P' after an operator or with Visual: Set current block.
* 'p' after an operator or with Visual: Set current paragraph.
*/
if (oap->op_type != OP_NOP || VIsual_active)
{
if (ca.cmdchar == 'P')
{
if (current_block(oap, '{', ca.count1) == FAIL)
clearopbeep(oap);
}
else
{
if (current_par(oap, ca.cmdchar, ca.count1) == FAIL)
clearopbeep(oap);
}
curwin->w_set_curswant = TRUE;
}
else
{
prep_redo_cmd(&ca);
do_put(oap->regname, dir, ca.count1, FALSE);
}
break;
case Ctrl('A'): /* add to number */
case Ctrl('X'): /* subtract from number */
if (!checkclearopq(oap))
{
if (do_addsub((int)ca.cmdchar, ca.count1) == OK)
prep_redo_cmd(&ca);
}
break;
/*
* 6: Inserts
*/
case 'A':
type = 1;
/* FALLTHROUGH */
case 'a':
if (oap->op_type != OP_NOP || VIsual_active)
{
if (current_word(oap, ca.count1, type) == FAIL)
clearopbeep(oap);
curwin->w_set_curswant = TRUE;
}
else
{
if (ca.cmdchar == 'A')
{
curwin->w_set_curswant = TRUE;
while (oneright() == OK)
;
}
if (u_save_cursor() == OK)
{
/* Works just like an 'i'nsert on the next character. */
if (!lineempty(curwin->w_cursor.lnum))
inc_cursor();
command_busy = edit(ca.cmdchar, FALSE, ca.count1);
}
}
break;
case 'I':
if (checkclearopq(oap))
break;
beginline(TRUE);
/* FALLTHROUGH */
case 'i':
case K_INS:
if (!checkclearopq(oap))
{
if (u_save_cursor() == OK)
command_busy = edit(ca.cmdchar, FALSE, ca.count1);
}
break;
case 'o':
if (VIsual_active) /* switch start and end of visual */
{
switch_visual();
break;
}
/* FALLTHROUGH */
case 'O':
command_busy = do_opencmd(&ca);
break;
case 'R':
if (VIsual_active)
{
ca.cmdchar = 'c';
VIsual_mode = 'V';
do_operator(&ca);
break;
}
if (!checkclearopq(oap))
{
if (u_save_cursor() == OK)
command_busy = edit('R', FALSE, ca.count1);
}
break;
/*
* 7: Operators
*/
case '~': /* swap case */
/*
* if tilde is not an operator and Visual is off: swap case
* of a single character
*/
if ( !p_to
&& !VIsual_active
&& oap->op_type != vim_strchr(op_chars, '~') - op_chars + 1)
{
do_swapchar(&ca);
break;
}
/*FALLTHROUGH*/
case 'd':
case 'c':
case 'y':
case '>':
case '<':
case '!':
case '=':
do_operator(&ca);
break;
/*
* 8: Abbreviations
*/
case 'S':
case 's':
/*
* 's' or 'S' with an operator: Operate on sentence or section.
*/
if (oap->op_type != OP_NOP || VIsual_active)
{
if (ca.cmdchar == 's') /* sentence */
flag = current_sent(oap, ca.count1);
else /* block with () */
flag = current_block(oap, '(', ca.count1);
if (flag == FAIL)
clearopbeep(oap);
curwin->w_set_curswant = TRUE;
break;
}
/* FALLTHROUGH */
case K_DEL:
case 'Y':
case 'D':
case 'C':
case 'x':
case 'X':
if (ca.cmdchar == K_DEL)
ca.cmdchar = 'x'; /* DEL key behaves like 'x' */
/* with Visual these commands are operators */
if (VIsual_active)
{
do_visop(&ca);
break;
}
/* FALLTHROUGH */
case '&':
do_optrans(&ca);
break;
/*
* 9: Marks
*/
case 'm':
if (!checkclearop(oap))
{
if (setmark(ca.nchar) == FAIL)
clearopbeep(oap);
}
break;
case '\'':
flag = TRUE;
/* FALLTHROUGH */
case '`':
do_gomark(&ca, flag);
break;
case Ctrl('O'): /* goto older pcmark */
ca.count1 = -ca.count1;
/* FALLTHROUGH */
case Ctrl('I'): /* goto newer pcmark */
do_pcmark(&ca);
break;
/*
* 10. Register name setting
*/
case '"':
do_regname(&ca, opnum);
break;
/*
* 11. Visual
*/
case 'v':
case 'V':
case Ctrl('V'):
if (!checkclearop(oap))
do_visual(&ca);
break;
/*
* 12. Suspend
*/
case Ctrl('Z'):
clearop(oap);
if (VIsual_active)
end_visual_mode(); /* stop Visual */
stuffReadbuff((char_u *)":st\r"); /* with autowrite */
break;
/*
* 13. Window commands
*/
case Ctrl('W'):
if (!checkclearop(oap))
do_window(ca.nchar, ca.count0); /* everything is in window.c */
break;
/*
* 14. extended commands (starting with 'g')
*/
case 'g':
command_busy = do_g_cmd(&ca);
break;
/*
* 15. mouse click
*/
#ifdef USE_MOUSE
case K_MIDDLEMOUSE:
case K_MIDDLEDRAG:
case K_MIDDLERELEASE:
case K_LEFTMOUSE:
case K_LEFTDRAG:
case K_LEFTRELEASE:
case K_RIGHTMOUSE:
case K_RIGHTDRAG:
case K_RIGHTRELEASE:
(void)do_mouse(oap, ca.cmdchar, BACKWARD, ca.count1, FALSE);
break;
case K_IGNORE:
break;
#endif
#ifdef USE_GUI
/*
* 16. scrollbar movement
*/
case K_SCROLLBAR:
if (oap->op_type != OP_NOP)
clearopbeep(oap);
/* Even if an operator was pending, we still want to scroll */
gui_do_scroll();
break;
case K_HORIZ_SCROLLBAR:
if (oap->op_type != OP_NOP)
clearopbeep(oap);
/* Even if an operator was pending, we still want to scroll */
gui_do_horiz_scroll();
break;
#endif
/*
* 17. The end
*/
case ESC:
do_esc(&ca, opnum);
break;
default: /* not a known command */
clearopbeep(oap);
break;
} /* end of switch on command character */
/*
* if we didn't start or finish an operator, reset oap->regname, unless we
* need it later.
*/
if (!finish_op && !oap->op_type &&
vim_strchr((char_u *)"\"DCYSsXx.", ca.cmdchar) == NULL)
oap->regname = 0;
/*
* If an operation is pending, handle it...
*/
do_pending_operator(&ca, finish_op, searchbuff,
&command_busy, old_col, FALSE, dont_adjust_op_end);
/*
* Wait when a message is displayed that will be overwritten by the mode
* message.
* In Visual mode and with "^O" in Insert mode, a short message will be
* overwritten by the mode message. Wait a bit, until a key is hit.
* In Visual mode, it's more important to keep the Visual area updated
* than keeping a message (e.g. from a /pat search).
* Only do this if the command was typed, not from a mapping.
* Also wait a bit after an error message, e.g. for "^O:".
* Don't redraw the screen, it would remove the message.
*/
if ( ((p_smd
&& ((VIsual_active
&& old_pos.lnum == curwin->w_cursor.lnum
&& old_pos.col == curwin->w_cursor.col)
|| restart_edit)
&& (clear_cmdline
|| redraw_cmdline)
&& msg_didany
&& KeyTyped)
|| (restart_edit
&& !VIsual_active
&& (msg_scroll
|| emsg_on_display
#ifdef SLEEP_IN_EMSG
|| need_sleep
#endif
)))
&& oap->regname == 0
&& !command_busy
&& stuff_empty()
&& oap->op_type == OP_NOP)
{
setcursor();
flushbuf();
if (msg_scroll || emsg_on_display
#ifdef SLEEP_IN_EMSG
|| need_sleep
#endif
)
ui_delay(1000L, TRUE); /* wait at least one second */
ui_delay(10000L, FALSE); /* wait up to ten seconds */
msg_scroll = FALSE;
emsg_on_display = FALSE;
#ifdef SLEEP_IN_EMSG
need_sleep = FALSE;
#endif
}
normal_end:
#ifdef USE_GUI
if (ca.cmdchar == 'r' && gui.in_use)
gui_update_cursor(TRUE);
#endif
if (oap->op_type == OP_NOP && oap->regname == 0)
clear_showcmd();
if ( restart_edit
&& oap->op_type == OP_NOP
&& !VIsual_active
&& !command_busy
&& stuff_empty()
&& oap->regname == 0)
(void)edit(restart_edit, FALSE, 1L);
if (!search_dont_set_mark)
checkpcmark(); /* check if we moved since setting pcmark */
vim_free(searchbuff);
/*
* Update the other windows for the current buffer if modified has been set in
* set_Changed() (This should be done more efficiently)
*/
if (modified)
{
update_other_win();
modified = FALSE;
}
}
/*
* Handle an operator after visual mode or when the movement is finished
*/
void
do_pending_operator(cap, finish_op, searchbuff,
command_busy, old_col, gui_yank, dont_adjust_op_end)
CMDARG *cap;
int finish_op;
char_u *searchbuff;
int *command_busy;
int old_col;
int gui_yank; /* yanking visual area for GUI */
int dont_adjust_op_end;
{
OPARG *oap = cap->oap;
FPOS old_cursor;
int VIsual_was_active = VIsual_active;
/* The visual area is remembered for redo */
static int redo_VIsual_mode = NUL; /* 'v', 'V', or Ctrl-V */
static linenr_t redo_VIsual_line_count; /* number of lines */
static colnr_t redo_VIsual_col; /* number of cols or end column */
static long redo_VIsual_count; /* count for Visual operator */
#ifdef USE_CLIPBOARD
/*
* Yank the visual area into the GUI selection register before we operate
* on it and lose it forever. This could call do_pending_operator()
* recursively, but that's OK because gui_yank will be TRUE for the
* nested call. Note also that we call clip_copy_selection() and not
* clip_auto_select(). This is because even when 'autoselect' is not set,
* if we operate on the text, eg by deleting it, then this is considered to
* be an explicit request for it to be put in the global cut buffer, so we
* always want to do it here. -- webb
*/
if (clipboard.available && oap->op_type != OP_NOP &&
!gui_yank && VIsual_active && !redo_VIsual_busy)
clip_copy_selection();
#endif
old_cursor = curwin->w_cursor;
/*
* If an operation is pending, handle it...
*/
if ((VIsual_active || finish_op) && oap->op_type != OP_NOP)
{
oap->is_VIsual = VIsual_active;
if (oap->op_type != OP_YANK && !VIsual_active) /* can't redo yank */
{
prep_redo(oap->regname, cap->count0, oap->op_prechar,
op_chars[oap->op_type - 1], cap->cmdchar, cap->nchar);
if (cap->cmdchar == '/' || cap->cmdchar == '?') /* was a search */
{
/*
* If 'cpoptions' does not contain 'r', insert the search
* pattern to really repeat the same command.
*/
if (vim_strchr(p_cpo, CPO_REDO) == NULL)
AppendToRedobuff(searchbuff);
AppendToRedobuff(NL_STR);
}
}
if (redo_VIsual_busy)
{
oap->start = curwin->w_cursor;
curwin->w_cursor.lnum += redo_VIsual_line_count - 1;
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
VIsual_mode = redo_VIsual_mode;
if (VIsual_mode == 'v')
{
if (redo_VIsual_line_count <= 1)
curwin->w_cursor.col += redo_VIsual_col - 1;
else
curwin->w_cursor.col = redo_VIsual_col;
}
if (redo_VIsual_col == MAXCOL)
{
curwin->w_curswant = MAXCOL;
coladvance(MAXCOL);
}
cap->count0 = redo_VIsual_count;
if (redo_VIsual_count != 0)
cap->count1 = redo_VIsual_count;
else
cap->count1 = 1;
}
else if (VIsual_active)
{
oap->start = VIsual;
VIsual_end = curwin->w_cursor;
if (VIsual_mode == 'V')
oap->start.col = 0;
}
/*
* Set oap->start to the first position of the operated text, oap->end
* to the end of the operated text. w_cursor is equal to oap->start.
*/
if (lt(oap->start, curwin->w_cursor))
{
oap->end = curwin->w_cursor;
curwin->w_cursor = oap->start;
}
else
{
oap->end = oap->start;
oap->start = curwin->w_cursor;
}
oap->line_count = oap->end.lnum - oap->start.lnum + 1;
if (VIsual_active || redo_VIsual_busy)
{
if (VIsual_mode == Ctrl('V')) /* block mode */
{
colnr_t start, end;
oap->block_mode = TRUE;
getvcol(curwin, &(oap->start),
&oap->start_vcol, NULL, &oap->end_vcol);
if (!redo_VIsual_busy)
{
getvcol(curwin, &(oap->end), &start, NULL, &end);
if (start < oap->start_vcol)
oap->start_vcol = start;
if (end > oap->end_vcol)
oap->end_vcol = end;
}
/* if '$' was used, get oap->end_vcol from longest line */
if (curwin->w_curswant == MAXCOL)
{
curwin->w_cursor.col = MAXCOL;
oap->end_vcol = 0;
for (curwin->w_cursor.lnum = oap->start.lnum;
curwin->w_cursor.lnum <= oap->end.lnum;
++curwin->w_cursor.lnum)
{
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &end);
if (end > oap->end_vcol)
oap->end_vcol = end;
}
}
else if (redo_VIsual_busy)
oap->end_vcol = oap->start_vcol + redo_VIsual_col - 1;
/*
* Correct oap->end.col and oap->start.col to be the
* upper-left and lower-right corner of the block area.
*/
curwin->w_cursor.lnum = oap->end.lnum;
coladvance(oap->end_vcol);
oap->end = curwin->w_cursor;
curwin->w_cursor = oap->start;
coladvance(oap->start_vcol);
oap->start = curwin->w_cursor;
}
if (!redo_VIsual_busy)
{
/*
* Prepare to reselect and redo Visual: this is based on the
* size of the Visual text
*/
resel_VIsual_mode = VIsual_mode;
if (curwin->w_curswant == MAXCOL)
resel_VIsual_col = MAXCOL;
else if (VIsual_mode == Ctrl('V'))
resel_VIsual_col = oap->end_vcol - oap->start_vcol + 1;
else if (oap->line_count > 1)
resel_VIsual_col = oap->end.col;
else
resel_VIsual_col = oap->end.col - oap->start.col + 1;
resel_VIsual_line_count = oap->line_count;
}
/* can't redo yank and : */
if (oap->op_type != OP_YANK && oap->op_type != OP_COLON)
{
prep_redo(oap->regname, 0L, NUL, 'v', oap->op_prechar,
op_chars[oap->op_type - 1]);
redo_VIsual_mode = resel_VIsual_mode;
redo_VIsual_col = resel_VIsual_col;
redo_VIsual_line_count = resel_VIsual_line_count;
redo_VIsual_count = cap->count0;
}
/*
* Mincl defaults to TRUE.
* If op_end is on a NUL (empty line) oap->inclusive becomes FALSE
* This makes "d}P" and "v}dP" work the same.
*/
oap->inclusive = TRUE;
if (VIsual_mode == 'V')
oap->motion_type = MLINE;
else
{
oap->motion_type = MCHAR;
if (*ml_get_pos(&(oap->end)) == NUL)
oap->inclusive = FALSE;
}
redo_VIsual_busy = FALSE;
/*
* Switch Visual off now, so screen updating does
* not show inverted text when the screen is redrawn.
* With OP_YANK and sometimes with OP_COLON and OP_FILTER there is
* no screen redraw, so it is done here to remove the inverted
* part.
*/
if (!gui_yank)
{
VIsual_active = FALSE;
#ifdef USE_MOUSE
setmouse();
#endif
if (p_smd)
clear_cmdline = TRUE; /* unshow visual mode later */
if (oap->op_type == OP_YANK || oap->op_type == OP_COLON ||
oap->op_type == OP_FILTER)
update_curbuf(NOT_VALID);
}
}
curwin->w_set_curswant = TRUE;
/* oap->empty is set when start and end are the same */
oap->empty = (oap->motion_type == MCHAR && !oap->inclusive &&
equal(oap->start, oap->end));
/*
* If the end of an operator is in column one while oap->motion_type is
* MCHAR and oap->inclusive is FALSE, we put op_end after the last character
* in the previous line. If op_start is on or before the first non-blank
* in the line, the operator becomes linewise (strange, but that's the way
* vi does it).
*/
if ( oap->motion_type == MCHAR
&& oap->inclusive == FALSE
&& !dont_adjust_op_end
&& oap->end.col == 0
&& oap->line_count > 1)
{
oap->end_adjusted = TRUE; /* remember that we did this */
--oap->line_count;
--oap->end.lnum;
if (inindent(0))
oap->motion_type = MLINE;
else
{
oap->end.col = STRLEN(ml_get(oap->end.lnum));
if (oap->end.col)
{
--oap->end.col;
oap->inclusive = TRUE;
}
}
}
else
oap->end_adjusted = FALSE;
switch (oap->op_type)
{
case OP_LSHIFT:
case OP_RSHIFT:
op_shift(oap, TRUE, VIsual_was_active ? (int)cap->count1 : 1);
break;
case DO_JOIN:
if (oap->line_count < 2)
oap->line_count = 2;
if (curwin->w_cursor.lnum + oap->line_count - 1 >
curbuf->b_ml.ml_line_count)
beep_flush();
else
do_do_join(oap->line_count, TRUE, TRUE);
break;
case OP_DELETE:
if (!oap->empty)
op_delete(oap);
break;
case OP_YANK:
if (!oap->empty)
(void)op_yank(oap, FALSE, !gui_yank);
break;
case OP_CHANGE:
*command_busy = op_change(oap); /* will call edit() */
break;
case OP_FILTER:
if (vim_strchr(p_cpo, CPO_FILTER) != NULL)
AppendToRedobuff((char_u *)"!\r"); /* use any last used !cmd */
else
bangredo = TRUE; /* do_bang() will put cmd in redo buffer */
case OP_INDENT:
case OP_COLON:
#if defined(LISPINDENT) || defined(CINDENT)
/*
* If 'equalprg' is empty, do the indenting internally.
*/
if (oap->op_type == OP_INDENT && *p_ep == NUL)
{
# ifdef LISPINDENT
if (curbuf->b_p_lisp)
{
op_reindent(oap, get_lisp_indent);
break;
}
# endif
# ifdef CINDENT
op_reindent(oap, get_c_indent);
break;
# endif
}
#endif /* defined(LISPINDENT) || defined(CINDENT) */
op_colon(oap, VIsual_was_active);
break;
case OP_TILDE:
case OP_UPPER:
case OP_LOWER:
if (!oap->empty)
op_tilde(oap);
break;
case OP_FORMAT:
case OP_GFORMAT:
if (*p_fp != NUL)
op_colon(oap, VIsual_was_active); /* use external command */
else
op_format(oap); /* use internal function */
break;
default:
clearopbeep(oap);
}
oap->op_prechar = NUL;
if (!gui_yank)
{
/*
* if 'sol' not set, go back to old column for some commands
*/
if (!p_sol && oap->motion_type == MLINE &&
(oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT ||
oap->op_type == OP_DELETE))
coladvance(curwin->w_curswant = old_col);
oap->op_type = OP_NOP;
}
else
curwin->w_cursor = old_cursor;
oap->block_mode = FALSE;
oap->regname = 0;
}
}
/*
* Handle indent and format operators and visual mode ":".
*/
static void
op_colon(oap, VIsual_was_active)
OPARG *oap;
int VIsual_was_active;
{
stuffcharReadbuff(':');
if (VIsual_was_active)
stuffReadbuff((char_u *)"'<,'>");
else
{
/*
* Make the range look nice, so it can be repeated.
*/
if (oap->start.lnum == curwin->w_cursor.lnum)
stuffcharReadbuff('.');
else
stuffnumReadbuff((long)oap->start.lnum);
if (oap->end.lnum != oap->start.lnum)
{
stuffcharReadbuff(',');
if (oap->end.lnum == curwin->w_cursor.lnum)
stuffcharReadbuff('.');
else if (oap->end.lnum == curbuf->b_ml.ml_line_count)
stuffcharReadbuff('$');
else if (oap->start.lnum == curwin->w_cursor.lnum)
{
stuffReadbuff((char_u *)".+");
stuffnumReadbuff((long)oap->line_count - 1);
}
else
stuffnumReadbuff((long)oap->end.lnum);
}
}
if (oap->op_type != OP_COLON)
stuffReadbuff((char_u *)"!");
if (oap->op_type == OP_INDENT)
{
#ifndef CINDENT
if (*p_ep == NUL)
stuffReadbuff((char_u *)"indent");
else
#endif
stuffReadbuff(p_ep);
stuffReadbuff((char_u *)"\n");
}
else if (oap->op_type == OP_FORMAT || oap->op_type == OP_GFORMAT)
{
if (*p_fp == NUL)
stuffReadbuff((char_u *)"fmt");
else
stuffReadbuff(p_fp);
stuffReadbuff((char_u *)"\n");
}
/*
* do_cmdline() does the rest
*/
}
#ifdef USE_MOUSE
/*
* Do the appropriate action for the current mouse click in the current mode.
*
* Normal Mode:
* event modi- position visual change action
* fier cursor window
* left press - yes end yes
* left press C yes end yes "^]" (2)
* left press S yes end yes "*" (2)
* left drag - yes start if moved no
* left relse - yes start if moved no
* middle press - yes if not active no put register
* middle press - yes if active no yank and put
* right press - yes start or extend yes
* right press S yes no change yes "#" (2)
* right drag - yes extend no
* right relse - yes extend no
*
* Insert or Replace Mode:
* event modi- position visual change action
* fier cursor window
* left press - yes (cannot be active) yes
* left press C yes (cannot be active) yes "CTRL-O^]" (2)
* left press S yes (cannot be active) yes "CTRL-O*" (2)
* left drag - yes start or extend (1) no CTRL-O (1)
* left relse - yes start or extend (1) no CTRL-O (1)
* middle press - no (cannot be active) no put register
* right press - yes start or extend yes CTRL-O
* right press S yes (cannot be active) yes "CTRL-O#" (2)
*
* (1) only if mouse pointer moved since press
* (2) only if click is in same buffer
*
* Return TRUE if start_arrow() should be called for edit mode.
*/
int
do_mouse(oap, c, dir, count, fix_indent)
OPARG *oap; /* operator argument, can be NULL */
int c; /* K_LEFTMOUSE, etc */
int dir; /* Direction to 'put' if necessary */
long count;
int fix_indent; /* Do we fix indent for 'put' if necessary? */
{
static int ignore_drag_release = FALSE;
static FPOS orig_cursor;
static int do_always = FALSE; /* ignore 'mouse' setting next time */
static int got_click = FALSE; /* got a click some time back */
int which_button; /* MOUSE_LEFT, _MIDDLE or _RIGHT */
int is_click; /* If FALSE it's a drag or release event */
int is_drag; /* If TRUE it's a drag event */
int jump_flags = 0; /* flags for jump_to_mouse() */
FPOS start_visual;
FPOS end_visual;
BUF *save_buffer;
int diff;
int moved; /* Has cursor moved? */
int c1, c2;
int VIsual_was_active = VIsual_active;
int regname;
/*
* When GUI is active, always recognize mouse events, otherwise:
* - Ignore mouse event in normal mode if 'mouse' doesn't include 'n'.
* - Ignore mouse event in visual mode if 'mouse' doesn't include 'v'.
* - For command line and insert mode 'mouse' is checked before calling
* do_mouse().
*/
if (do_always)
do_always = FALSE;
else
#ifdef USE_GUI
if (!gui.in_use)
#endif
{
if (VIsual_active)
{
if (!mouse_has(MOUSE_VISUAL))
return FALSE;
}
else if (State == NORMAL && !mouse_has(MOUSE_NORMAL))
return FALSE;
}
which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
/*
* Ignore drag and release events if we didn't get a click.
*/
if (is_click)
got_click = TRUE;
else
{
if (!got_click) /* didn't get click, ignore */
return FALSE;
if (!is_drag) /* release, reset got_click */
got_click = FALSE;
}
/*
* ALT is currently ignored
*/
if ((mod_mask & MOD_MASK_ALT))
return FALSE;
/*
* CTRL right mouse button does CTRL-T
*/
if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT)
{
if (State & INSERT)
stuffcharReadbuff(Ctrl('O'));
stuffcharReadbuff(Ctrl('T'));
got_click = FALSE; /* ignore drag&release now */
return FALSE;
}
/*
* CTRL only works with left mouse button
*/
if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT)
return FALSE;
/*
* When a modifier is down, ignore drag and release events, as well as
* multiple clicks and the middle mouse button.
*/
if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT)) &&
(!is_click || (mod_mask & MOD_MASK_MULTI_CLICK) ||
which_button == MOUSE_MIDDLE))
return FALSE;
/*
* If the button press was used as the movement command for an operator
* (eg "d<MOUSE>"), or it is the middle button that is held down, ignore
* drag/release events.
*/
if (!is_click && (ignore_drag_release || which_button == MOUSE_MIDDLE))
return FALSE;
if (oap != NULL)
regname = oap->regname;
else
regname = 0;
/*
* Middle mouse button does a 'put' of the selected text
*/
if (which_button == MOUSE_MIDDLE)
{
if (State == NORMAL)
{
/*
* If an operator was pending, we don't know what the user wanted
* to do. Go back to normal mode: Clear the operator and beep().
*/
if (oap != NULL && oap->op_type != OP_NOP)
{
clearopbeep(oap);
return FALSE;
}
/*
* If visual was active, yank the highlighted text and put it
* before the mouse pointer position.
*/
if (VIsual_active)
{
stuffcharReadbuff('y');
stuffcharReadbuff(K_MIDDLEMOUSE);
do_always = TRUE; /* ignore 'mouse' setting next time */
return FALSE;
}
/*
* The rest is below jump_to_mouse()
*/
}
/*
* Middle click in insert mode doesn't move the mouse, just insert the
* contents of a register. '.' register is special, can't insert that
* with do_put().
*/
else if (State & INSERT)
{
if (regname == '.')
insert_reg(regname);
else
{
#ifdef USE_CLIPBOARD
if (clipboard.available && regname == 0)
regname = '*';
#endif
do_put(regname, BACKWARD, 1L, fix_indent);
/* Put cursor after the end of the just pasted text. */
curwin->w_cursor = curbuf->b_op_end;
if (gchar_cursor() != NUL)
++curwin->w_cursor.col;
/* Repeat it with CTRL-R x, not exactly the same, but mostly
* works fine. */
AppendCharToRedobuff(Ctrl('R'));
if (regname == 0)
AppendCharToRedobuff('"');
else
AppendCharToRedobuff(regname);
}
return FALSE;
}
else
return FALSE;
}
if (!is_click)
jump_flags |= MOUSE_FOCUS;
start_visual.lnum = 0;
if ((State & (NORMAL | INSERT)) &&
!(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
{
if (which_button == MOUSE_LEFT)
{
if (is_click)
{
if (VIsual_active)
{
end_visual_mode();
update_curbuf(NOT_VALID);
}
}
else
jump_flags |= MOUSE_MAY_VIS;
}
else if (which_button == MOUSE_RIGHT)
{
if (is_click && VIsual_active)
{
/*
* Remember the start and end of visual before moving the
* cursor.
*/
if (lt(curwin->w_cursor, VIsual))
{
start_visual = curwin->w_cursor;
end_visual = VIsual;
}
else
{
start_visual = VIsual;
end_visual = curwin->w_cursor;
}
}
jump_flags |= MOUSE_MAY_VIS;
}
}
if (!is_drag)
{
/*
* If an operator is pending, ignore all drags and releases until the
* next mouse click.
*/
ignore_drag_release = (oap != NULL && oap->op_type != OP_NOP);
}
/*
* Jump!
*/
if (!is_click)
jump_flags |= MOUSE_DID_MOVE;
save_buffer = curbuf;
moved = (jump_to_mouse(jump_flags,
oap == NULL ? NULL : &(oap->inclusive)) & CURSOR_MOVED);
/* When jumping to another buffer, stop visual mode */
if (curbuf != save_buffer && VIsual_active)
{
end_visual_mode();
update_curbuf(NOT_VALID); /* delete the inversion */
}
else if (start_visual.lnum) /* right click in visual mode */
{
/*
* If the click is before the start of visual, change the start. If
* the click is after the end of visual, change the end. If the click
* is inside the visual, change the closest side.
*/
if (lt(curwin->w_cursor, start_visual))
VIsual = end_visual;
else if (lt(end_visual, curwin->w_cursor))
VIsual = start_visual;
else
{
/* In the same line, compare column number */
if (end_visual.lnum == start_visual.lnum)
{
if (curwin->w_cursor.col - start_visual.col >
end_visual.col - curwin->w_cursor.col)
VIsual = start_visual;
else
VIsual = end_visual;
}
/* In different lines, compare line number */
else
{
diff = (curwin->w_cursor.lnum - start_visual.lnum) -
(end_visual.lnum - curwin->w_cursor.lnum);
if (diff > 0) /* closest to end */
VIsual = start_visual;
else if (diff < 0) /* closest to start */
VIsual = end_visual;
else /* in the middle line */
{
if (curwin->w_cursor.col <
(start_visual.col + end_visual.col) / 2)
VIsual = end_visual;
else
VIsual = start_visual;
}
}
}
}
/*
* If Visual mode started in insert mode, execute "CTRL-O"
*/
else if ((State & INSERT) && VIsual_active)
stuffcharReadbuff(Ctrl('O'));
/*
* When the cursor has moved in insert mode, and something was inserted,
* and there are several windows, need to redraw.
*/
if (moved && (State & INSERT) && modified && firstwin->w_next != NULL)
{
update_curbuf(NOT_VALID);
modified = FALSE;
}
/*
* Middle mouse click: Put text before cursor.
*/
if (which_button == MOUSE_MIDDLE)
{
#ifdef USE_CLIPBOARD
if (clipboard.available && regname == 0)
regname = '*';
#endif
if (yank_register_mline(regname))
{
if (mouse_past_bottom)
dir = FORWARD;
}
else if (mouse_past_eol)
dir = FORWARD;
if (fix_indent)
{
c1 = (dir == BACKWARD) ? '[' : ']';
c2 = 'p';
}
else
{
c1 = (dir == FORWARD) ? 'p' : 'P';
c2 = NUL;
}
prep_redo(regname, count, NUL, c1, c2, NUL);
/*
* Remember where the paste started, so in edit() Insstart can be set
* to this position
*/
if (restart_edit)
where_paste_started = curwin->w_cursor;
do_put(regname, dir, count, fix_indent);
/* Put cursor at the end of the just pasted text. */
curwin->w_cursor = curbuf->b_op_end;
if (restart_edit && gchar_cursor() != NUL)
++curwin->w_cursor.col; /* put cursor after the text */
}
/*
* Ctrl-Mouse click jumps to the tag under the mouse pointer
*/
else if ((mod_mask & MOD_MASK_CTRL))
{
if (State & INSERT)
stuffcharReadbuff(Ctrl('O'));
stuffcharReadbuff(Ctrl(']'));
ignore_drag_release = TRUE; /* ignore drag and release now */
}
/*
* Shift-Mouse click searches for the next occurrence of the word under
* the mouse pointer
*/
else if ((mod_mask & MOD_MASK_SHIFT))
{
if (State & INSERT)
stuffcharReadbuff(Ctrl('O'));
if (which_button == MOUSE_LEFT)
stuffcharReadbuff('*');
else /* MOUSE_RIGHT */
stuffcharReadbuff('#');
}
/* Handle double clicks */
else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (NORMAL | INSERT)))
{
if (is_click || !VIsual_active)
{
if (VIsual_active)
orig_cursor = VIsual;
else
{
check_visual_highlight();
VIsual = curwin->w_cursor;
orig_cursor = VIsual;
VIsual_active = TRUE;
#ifdef USE_MOUSE
setmouse();
#endif
if (p_smd)
redraw_cmdline = TRUE; /* show visual mode later */
}
if (mod_mask & MOD_MASK_2CLICK)
VIsual_mode = 'v';
else if (mod_mask & MOD_MASK_3CLICK)
VIsual_mode = 'V';
else if (mod_mask & MOD_MASK_4CLICK)
VIsual_mode = Ctrl('V');
}
if (mod_mask & MOD_MASK_2CLICK)
{
if (lt(curwin->w_cursor, orig_cursor))
{
find_start_of_word(&curwin->w_cursor);
find_end_of_word(&VIsual);
}
else
{
find_start_of_word(&VIsual);
find_end_of_word(&curwin->w_cursor);
}
curwin->w_set_curswant = TRUE;
}
if (is_click)
update_curbuf(NOT_VALID); /* update the inversion */
}
else if (VIsual_active && VIsual_was_active != VIsual_active)
VIsual_mode = 'v';
return moved;
}
static void
find_start_of_word(pos)
FPOS *pos;
{
char_u *ptr;
int cclass;
ptr = ml_get(pos->lnum);
cclass = get_mouse_class(ptr[pos->col]);
/* Can't test pos->col >= 0 because pos->col is unsigned */
while (pos->col > 0 && get_mouse_class(ptr[pos->col]) == cclass)
pos->col--;
if (pos->col != 0 || get_mouse_class(ptr[0]) != cclass)
pos->col++;
}
static void
find_end_of_word(pos)
FPOS *pos;
{
char_u *ptr;
int cclass;
ptr = ml_get(pos->lnum);
cclass = get_mouse_class(ptr[pos->col]);
while (ptr[pos->col] && get_mouse_class(ptr[pos->col]) == cclass)
pos->col++;
pos->col--;
}
static int
get_mouse_class(c)
int c;
{
if (c == ' ' || c == '\t')
return ' ';
if (isidchar(c))
return 'a';
/*
* There are a few special cases where we want certain combinations of
* characters to be considered as a single word. These are things like
* "->", "/ *", "*=", "+=", "&=", "<=", ">=", "!=" etc. Otherwise, each
* character is in it's own class.
*/
if (c != NUL && vim_strchr((char_u *)"-+*/%<>&|^!=", c) != NULL)
return '=';
return c;
}
#endif /* USE_MOUSE */
/*
* Check if highlighting for visual mode is possible, give a warning message
* if not.
*/
void
check_visual_highlight()
{
static int did_check = FALSE;
if (!did_check && highlight_attr[HLF_V] == 0)
MSG("Warning: terminal cannot highlight");
did_check = TRUE;
}
/*
* End visual mode. If we are using the GUI, and autoselect is set, then
* remember what was selected in case we need to paste it somewhere while we
* still own the selection. This function should ALWAYS be called to end
* visual mode.
*/
void
end_visual_mode()
{
#ifdef USE_CLIPBOARD
if (clipboard.available)
clip_auto_select();
#endif
VIsual_active = FALSE;
#ifdef USE_MOUSE
setmouse();
#endif
VIsual_end = curwin->w_cursor; /* remember for '> mark */
if (p_smd)
clear_cmdline = TRUE; /* unshow visual mode later */
}
/*
* Find the identifier under or to the right of the cursor. If none is
* found and find_type has FIND_STRING, then find any non-white string. The
* length of the string is returned, or zero if no string is found. If a
* string is found, a pointer to the string is put in *string, but note that
* the caller must use the length returned as this string may not be NUL
* terminated.
*/
int
find_ident_under_cursor(string, find_type)
char_u **string;
int find_type;
{
char_u *ptr;
int col = 0; /* init to shut up GCC */
int i;
/*
* if i == 0: try to find an identifier
* if i == 1: try to find any string
*/
ptr = ml_get_curline();
for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; ++i)
{
/*
* skip to start of identifier/string
*/
col = curwin->w_cursor.col;
while (ptr[col] != NUL &&
(i == 0 ? !iswordchar(ptr[col]) : vim_iswhite(ptr[col])))
++col;
/*
* Back up to start of identifier/string. This doesn't match the
* real vi but I like it a little better and it shouldn't bother
* anyone.
* When FIND_IDENT isn't defined, we backup until a blank.
*/
while (col > 0 && (i == 0 ? iswordchar(ptr[col - 1]) :
(!vim_iswhite(ptr[col - 1]) &&
(!(find_type & FIND_IDENT) || !iswordchar(ptr[col - 1])))))
--col;
/*
* if we don't want just any old string, or we've found an identifier,
* stop searching.
*/
if (!(find_type & FIND_STRING) || iswordchar(ptr[col]))
break;
}
/*
* didn't find an identifier or string
*/
if (ptr[col] == NUL || (!iswordchar(ptr[col]) && i == 0))
{
if (find_type & FIND_STRING)
EMSG("No string under cursor");
else
EMSG("No identifier under cursor");
return 0;
}
ptr += col;
*string = ptr;
col = 0;
while (i == 0 ? iswordchar(*ptr) : (*ptr != NUL && !vim_iswhite(*ptr)))
{
++ptr;
++col;
}
return col;
}
/*
* Prepare for redo of a normal command.
*/
static void
prep_redo_cmd(cap)
CMDARG *cap;
{
prep_redo(cap->oap->regname, cap->count0,
NUL, cap->cmdchar, NUL, cap->nchar);
}
/*
* Prepare for redo of any command.
*/
static void
prep_redo(regname, num, prechar, cmd, c, nchar)
int regname;
long num;
int prechar;
int cmd;
int c;
int nchar;
{
ResetRedobuff();
if (regname != 0) /* yank from specified buffer */
{
AppendCharToRedobuff('\"');
AppendCharToRedobuff(regname);
}
if (num)
AppendNumberToRedobuff(num);
if (prechar != NUL)
AppendCharToRedobuff(prechar);
AppendCharToRedobuff(cmd);
if (c != NUL)
AppendCharToRedobuff(c);
if (nchar != NUL)
AppendCharToRedobuff(nchar);
}
/*
* check for operator active and clear it
*
* return TRUE if operator was active
*/
static int
checkclearop(oap)
OPARG *oap;
{
if (oap->op_type == OP_NOP)
return FALSE;
clearopbeep(oap);
return TRUE;
}
/*
* check for operator or Visual active and clear it
*
* return TRUE if operator was active
*/
static int
checkclearopq(oap)
OPARG *oap;
{
if (oap->op_type == OP_NOP && !VIsual_active)
return FALSE;
clearopbeep(oap);
return TRUE;
}
static void
clearop(oap)
OPARG *oap;
{
oap->op_type = OP_NOP;
oap->regname = 0;
}
static void
clearopbeep(oap)
OPARG *oap;
{
clearop(oap);
beep_flush();
}
/*
* Routines for displaying a partly typed command
*/
static char_u showcmd_buf[SHOWCMD_COLS + 1];
static char_u old_showcmd_buf[SHOWCMD_COLS + 1]; /* For push_showcmd() */
static int is_showcmd_clear = TRUE;
static void display_showcmd __ARGS((void));
void
clear_showcmd()
{
if (!p_sc)
return;
showcmd_buf[0] = NUL;
/*
* Don't actually display something if there is nothing to clear.
*/
if (is_showcmd_clear)
return;
display_showcmd();
}
/*
* Add 'c' to string of shown command chars.
* Return TRUE if setcursor() has been called.
*/
int
add_to_showcmd(c, display_always)
int c;
int display_always;
{
char_u *p;
int old_len;
int extra_len;
int overflow;
if (!p_sc)
return FALSE;
p = transchar(c);
old_len = STRLEN(showcmd_buf);
extra_len = STRLEN(p);
overflow = old_len + extra_len - SHOWCMD_COLS;
if (overflow > 0)
STRCPY(showcmd_buf, showcmd_buf + overflow);
STRCAT(showcmd_buf, p);
if (!display_always && char_avail())
return FALSE;
display_showcmd();
return TRUE;
}
/*
* Delete 'len' characters from the end of the shown command.
*/
static void
del_from_showcmd(len)
int len;
{
int old_len;
if (!p_sc)
return;
old_len = STRLEN(showcmd_buf);
if (len > old_len)
len = old_len;
showcmd_buf[old_len - len] = NUL;
if (!char_avail())
display_showcmd();
}
void
push_showcmd()
{
if (p_sc)
STRCPY(old_showcmd_buf, showcmd_buf);
}
void
pop_showcmd()
{
if (!p_sc)
return;
STRCPY(showcmd_buf, old_showcmd_buf);
display_showcmd();
}
static void
display_showcmd()
{
int len;
cursor_off();
len = STRLEN(showcmd_buf);
if (len == 0)
is_showcmd_clear = TRUE;
else
{
screen_puts(showcmd_buf, (int)Rows - 1, sc_col, 0);
is_showcmd_clear = FALSE;
}
/*
* clear the rest of an old message by outputing up to SHOWCMD_COLS spaces
*/
screen_puts((char_u *)" " + len, (int)Rows - 1, sc_col + len, 0);
setcursor(); /* put cursor back where it belongs */
}
/*
* Implementation of "gd" and "gD" command.
*/
static void
do_gd(oap, nchar)
OPARG *oap;
int nchar;
{
int len;
char_u *pat;
FPOS old_pos;
int t;
int save_p_ws;
int save_p_scs;
char_u *ptr;
if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0 ||
(pat = alloc(len + 5)) == NULL)
{
clearopbeep(oap);
return;
}
sprintf((char *)pat, iswordchar(*ptr) ? "\\<%.*s\\>" :
"%.*s", len, ptr);
old_pos = curwin->w_cursor;
save_p_ws = p_ws;
save_p_scs = p_scs;
p_ws = FALSE; /* don't wrap around end of file now */
p_scs = FALSE; /* don't switch ignorecase off now */
fo_do_comments = TRUE;
/*
* Search back for the end of the previous function.
* If this fails, and with "gD", go to line 1.
* Search forward for the identifier, ignore comment lines.
*/
if (nchar == 'D' || !findpar(oap, BACKWARD, 1L, '}', FALSE))
{
setpcmark(); /* Set in findpar() otherwise */
curwin->w_cursor.lnum = 1;
}
while ((t = searchit(&curwin->w_cursor, FORWARD, pat, 1L, 0, RE_LAST))
== OK &&
get_leader_len(ml_get_curline(), NULL) &&
old_pos.lnum > curwin->w_cursor.lnum)
++curwin->w_cursor.lnum;
if (t == FAIL || old_pos.lnum <= curwin->w_cursor.lnum)
{
clearopbeep(oap);
curwin->w_cursor = old_pos;
}
else
curwin->w_set_curswant = TRUE;
vim_free(pat);
p_ws = save_p_ws;
p_scs = save_p_scs;
fo_do_comments = FALSE;
}
/*
* screengo() --
*
* move 'dist' lines in direction 'dir', counting lines by *screen*
* lines rather than lines in the file
* 'dist' must be positive.
*
* return OK if able to move cursor, FAIL otherwise.
*/
static int
screengo(oap, dir, dist)
OPARG *oap;
int dir;
long dist;
{
int linelen = linetabsize(ml_get_curline());
int retval = OK;
int atend = FALSE;
int n;
oap->motion_type = MCHAR;
oap->inclusive = FALSE;
/*
* Instead of sticking at the last character of the line in the file we
* try to stick in the last column of the screen
*/
if (curwin->w_curswant == MAXCOL)
{
atend = TRUE;
validate_virtcol();
curwin->w_curswant = ((curwin->w_virtcol +
(curwin->w_p_nu ? 8 : 0)) / Columns + 1) * Columns - 1;
if (curwin->w_p_nu && curwin->w_curswant > 8)
curwin->w_curswant -= 8;
}
else
while (curwin->w_curswant >= (colnr_t)(linelen + Columns))
curwin->w_curswant -= Columns;
while (dist--)
{
if (dir == BACKWARD)
{
/* move back within line */
if ((long)curwin->w_curswant >= Columns)
curwin->w_curswant -= Columns;
else /* to previous line */
{
if (curwin->w_cursor.lnum == 1)
{
retval = FAIL;
break;
}
--curwin->w_cursor.lnum;
linelen = linetabsize(ml_get_curline());
n = ((linelen + (curwin->w_p_nu ? 8 : 0) - 1) / Columns)
* Columns;
if (curwin->w_p_nu &&
(long)curwin->w_curswant >= Columns - 8 && n)
n -= Columns;
curwin->w_curswant += n;
}
}
else /* dir == FORWARD */
{
n = ((linelen + (curwin->w_p_nu ? 8 : 0) - 1) / Columns) * Columns;
if (curwin->w_p_nu && n > 8)
n -= 8;
/* move forward within line */
if (curwin->w_curswant < (colnr_t)n)
curwin->w_curswant += Columns;
else /* to next line */
{
if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
{
retval = FAIL;
break;
}
curwin->w_cursor.lnum++;
linelen = linetabsize(ml_get_curline());
curwin->w_curswant %= Columns;
}
}
}
coladvance(curwin->w_curswant);
if (atend)
curwin->w_curswant = MAXCOL; /* stick in the last column */
return retval;
}
static void
do_zet(cap)
CMDARG *cap;
{
long n;
colnr_t col;
int nchar = cap->nchar;
if (nchar < 0x100 && isdigit(nchar))
{
n = nchar - '0';
for (;;)
{
++no_mapping;
++allow_keys; /* no mapping for nchar, but allow key codes */
nchar = vgetc();
#ifdef HAVE_LANGMAP
LANGMAP_ADJUST(nchar, TRUE);
#endif
--no_mapping;
--allow_keys;
(void)add_to_showcmd(nchar, FALSE);
if (nchar == K_DEL)
n /= 10;
else if (nchar < 0x100 && isdigit(nchar))
n = n * 10 + (nchar - '0');
else if (nchar == CR)
{
win_setheight((int)n);
break;
}
else if (nchar == 'l' || nchar == 'h' ||
nchar == K_LEFT || nchar == K_RIGHT)
{
n = n ? n : 1;
goto dozet;
}
else
{
clearopbeep(cap->oap);
break;
}
}
cap->oap->op_type = OP_NOP;
return;
}
dozet:
/*
* If line number given, set cursor, except for "zh", "zl", "ze" and
* "zs"
*/
if ( vim_strchr((char_u *)"hles", nchar) == NULL
&& nchar != K_LEFT
&& nchar != K_RIGHT
&& cap->count0
&& cap->count0 != curwin->w_cursor.lnum)
{
setpcmark();
if (cap->count0 > curbuf->b_ml.ml_line_count)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
else
curwin->w_cursor.lnum = cap->count0;
}
switch (nchar)
{
case NL: /* put curwin->w_cursor at top of screen */
case CR:
beginline(TRUE);
/* FALLTHROUGH */
case 't':
scroll_cursor_top(0, TRUE);
break;
case '.': /* put curwin->w_cursor in middle of screen */
beginline(TRUE);
/* FALLTHROUGH */
case 'z':
scroll_cursor_halfway(TRUE);
break;
case '-': /* put curwin->w_cursor at bottom of screen */
beginline(TRUE);
/* FALLTHROUGH */
case 'b':
scroll_cursor_bot(0, TRUE);
break;
/* "zh" - scroll screen to the right */
case 'h':
case K_LEFT:
if (!curwin->w_p_wrap)
{
if ((colnr_t)cap->count1 > curwin->w_leftcol)
curwin->w_leftcol = 0;
else
curwin->w_leftcol -= (colnr_t)cap->count1;
leftcol_changed();
}
break;
/* "zl" - scroll screen to the left */
case 'l':
case K_RIGHT:
if (!curwin->w_p_wrap)
{
/* scroll the window left */
curwin->w_leftcol += (colnr_t)cap->count1;
leftcol_changed();
}
break;
/* "zs" - scroll screen, cursor at the start */
case 's':
if (!curwin->w_p_wrap)
{
getvcol(curwin, &curwin->w_cursor, &col, NULL, NULL);
curwin->w_leftcol = col;
changed_cline_bef_curs();
redraw_later(NOT_VALID);
}
break;
/* "ze" - scroll screen, cursor at the end */
case 'e':
if (!curwin->w_p_wrap)
{
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
if ((long)col < Columns)
curwin->w_leftcol = 0;
else
curwin->w_leftcol = col - Columns + 1;
changed_cline_bef_curs();
redraw_later(NOT_VALID);
}
break;
case Ctrl('S'): /* ignore CTRL-S and CTRL-Q to avoid problems */
case Ctrl('Q'): /* with terminals that use xon/xoff */
break;
default:
clearopbeep(cap->oap);
}
update_screen(VALID);
}
/*
* Handle the commands that use the word under the cursor.
*
* Returns TRUE for "*" and "#" commands, indicating that the next search
* should not set the pcmark.
*/
static void
do_ident(cap, whole_word)
CMDARG *cap;
int whole_word; /* include \< and \> for ident search */
{
char_u *ptr = NULL;
int n = 0; /* init for GCC */
/*
* The "CTRL-]" and "K" commands accept an argument in Visual mode.
*/
if (cap->cmdchar == Ctrl(']') || cap->cmdchar == 'K')
{
if (VIsual_active) /* :ta to visual highlighted text */
{
if (VIsual.lnum != curwin->w_cursor.lnum)
{
clearopbeep(cap->oap);
return;
}
if (lt(curwin->w_cursor, VIsual))
{
ptr = ml_get_pos(&curwin->w_cursor);
n = VIsual.col - curwin->w_cursor.col + 1;
}
else
{
ptr = ml_get_pos(&VIsual);
n = curwin->w_cursor.col - VIsual.col + 1;
}
end_visual_mode();
++RedrawingDisabled;
update_curbuf(NOT_VALID); /* update the inversion later */
--RedrawingDisabled;
}
if (checkclearopq(cap->oap))
return;
}
if (ptr == NULL && (n = find_ident_under_cursor(&ptr,
(cap->cmdchar == '*' || cap->cmdchar == '#')
? FIND_IDENT|FIND_STRING : FIND_IDENT)) == 0)
{
clearop(cap->oap);
return;
}
if (cap->count0 && !(cap->cmdchar == 'K' && STRCMP(p_kp, "man") == 0))
stuffnumReadbuff(cap->count0);
switch (cap->cmdchar)
{
case '*':
stuffReadbuff((char_u *)"/");
/* FALLTHROUGH */
case '#':
if (cap->cmdchar == '#')
stuffReadbuff((char_u *)"?");
/*
* Put cursor at start of word, makes search skip the word
* under the cursor.
* Call setpcmark() first, so "*``" puts the cursor back where
* it was, and set search_dont_set_mark to avoid doing it
* again when searching.
*/
setpcmark();
search_dont_set_mark = TRUE;
curwin->w_cursor.col = ptr - ml_get_curline();
if (whole_word && iswordchar(*ptr))
stuffReadbuff((char_u *)"\\<");
no_smartcase = TRUE; /* don't use 'smartcase' now */
break;
case 'K':
if (*p_kp == NUL)
stuffReadbuff((char_u *)":he ");
else
{
stuffReadbuff((char_u *)":! ");
stuffReadbuff(p_kp);
stuffReadbuff((char_u *)" ");
if (STRCMP(p_kp, "man") == 0 && cap->count0)
{
stuffnumReadbuff(cap->count0);
stuffReadbuff((char_u *)" ");
}
}
break;
default:
if (curbuf->b_help)
stuffReadbuff((char_u *)":he ");
else
stuffReadbuff((char_u *)":ta ");
}
/*
* Now grab the chars in the identifier
*/
while (n--)
{
/* put a backslash before \ and some others */
if (*ptr == '\\' || (!(cap->cmdchar == '*' || cap->cmdchar == '#') &&
vim_strchr(escape_chars, *ptr) != NULL))
stuffcharReadbuff('\\');
/* don't interpret the characters as edit commands */
if (*ptr < ' ' || *ptr > '~')
stuffcharReadbuff(Ctrl('V'));
stuffcharReadbuff(*ptr++);
}
if ( whole_word
&& (cap->cmdchar == '*' || cap->cmdchar == '#')
&& iswordchar(ptr[-1]))
stuffReadbuff((char_u *)"\\>");
stuffReadbuff((char_u *)"\n");
}
/*
* Handle scrolling command 'H', 'L' and 'M'.
*/
static void
do_scroll(cap)
CMDARG *cap;
{
int used = 0;
long n;
if (cap->cmdchar == 'L')
{
validate_botline(); /* make sure curwin->w_botline is valid */
curwin->w_cursor.lnum = curwin->w_botline - 1;
if (cap->count1 - 1 >= curwin->w_cursor.lnum)
curwin->w_cursor.lnum = 1;
else
curwin->w_cursor.lnum -= cap->count1 - 1;
}
else
{
if (cap->cmdchar == 'M')
{
validate_botline(); /* make sure w_empty_rows is valid */
for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; ++n)
if ((used += plines(curwin->w_topline + n)) >=
(curwin->w_height - curwin->w_empty_rows + 1) / 2)
break;
if (n && used > curwin->w_height)
--n;
}
else
n = cap->count1 - 1;
curwin->w_cursor.lnum = curwin->w_topline + n;
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
}
}
/*
* Cursor right commands.
*/
static void
do_right(cap)
CMDARG *cap;
{
long n;
cap->oap->motion_type = MCHAR;
cap->oap->inclusive = FALSE;
for (n = cap->count1; n > 0; --n)
{
if (oneright() == FAIL)
{
/*
* <Space> wraps to next line if 'whichwrap' bit 1 set.
* 'l' wraps to next line if 'whichwrap' bit 2 set.
* CURS_RIGHT wraps to next line if 'whichwrap' bit 3 set
*/
if ( ((cap->cmdchar == ' '
&& vim_strchr(p_ww, 's') != NULL)
|| (cap->cmdchar == 'l'
&& vim_strchr(p_ww, 'l') != NULL)
|| (cap->cmdchar == K_RIGHT
&& vim_strchr(p_ww, '>') != NULL))
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
{
/* When deleting we also count the NL as a character.
* Set cap->oap->inclusive when last char in the line is
* included, move to next line after that */
if ( (cap->oap->op_type == OP_DELETE
|| cap->oap->op_type == OP_CHANGE)
&& !cap->oap->inclusive
&& !lineempty(curwin->w_cursor.lnum))
cap->oap->inclusive = TRUE;
else
{
++curwin->w_cursor.lnum;
curwin->w_cursor.col = 0;
curwin->w_set_curswant = TRUE;
cap->oap->inclusive = FALSE;
}
continue;
}
if (cap->oap->op_type == OP_NOP)
beep_flush();
else
{
if (lineempty(curwin->w_cursor.lnum))
clearopbeep(cap->oap);
else
{
cap->oap->inclusive = TRUE;
if (n > 1)
beep_flush();
}
}
break;
}
}
}
/*
* Cursor left commands.
*
* Returns TRUE when operator end should not be adjusted.
*/
static int
do_left(cap)
CMDARG *cap;
{
long n;
int retval = FALSE;
cap->oap->motion_type = MCHAR;
cap->oap->inclusive = FALSE;
n = cap->count1;
while (n--)
{
if (oneleft() == FAIL)
{
/* <BS> and <Del> wrap to previous line if 'whichwrap' has 'b'.
* 'h' wraps to previous line if 'whichwrap' has 'h'.
* CURS_LEFT wraps to previous line if 'whichwrap' has '<'.
*/
if ( (((cap->cmdchar == K_BS
|| cap->cmdchar == Ctrl('H'))
&& vim_strchr(p_ww, 'b') != NULL)
|| (cap->cmdchar == 'h'
&& vim_strchr(p_ww, 'h') != NULL)
|| (cap->cmdchar == K_LEFT
&& vim_strchr(p_ww, '<') != NULL))
&& curwin->w_cursor.lnum > 1)
{
--(curwin->w_cursor.lnum);
coladvance(MAXCOL);
curwin->w_set_curswant = TRUE;
/* When the NL before the first char has to be deleted we
* put the cursor on the NUL after the previous line.
* This is a very special case, be careful!
* don't adjust op_end now, otherwise it won't work */
if ( (cap->oap->op_type == OP_DELETE
|| cap->oap->op_type == OP_CHANGE)
&& !lineempty(curwin->w_cursor.lnum))
{
++curwin->w_cursor.col;
retval = TRUE;
}
continue;
}
else if ( cap->oap->op_type != OP_DELETE
&& cap->oap->op_type != OP_CHANGE)
beep_flush();
else if (cap->count1 == 1)
clearopbeep(cap->oap);
break;
}
}
return retval;
}
/*
* Grab the filename under the cursor and edit it.
*/
static void
do_gotofile(cap)
CMDARG *cap;
{
char_u *ptr;
ptr = file_name_at_cursor(FNAME_MESS|FNAME_HYP|FNAME_EXP, cap->count1);
if (ptr != NULL)
{
/* do autowrite if necessary */
if (curbuf->b_changed && curbuf->b_nwindows <= 1 && !p_hid)
autowrite(curbuf, FALSE);
setpcmark();
(void)do_ecmd(0, ptr, NULL, NULL, (linenr_t)0, p_hid ? ECMD_HIDE : 0);
vim_free(ptr);
}
else
clearop(cap->oap);
}
static void
do_csearch(cap, dir, type)
CMDARG *cap;
int dir;
int type;
{
cap->oap->motion_type = MCHAR;
if (dir == BACKWARD)
cap->oap->inclusive = FALSE;
else
cap->oap->inclusive = TRUE;
curwin->w_set_curswant = TRUE;
if (cap->nchar >= 0x100 || !searchc(cap->nchar, dir, type, cap->count1))
clearopbeep(cap->oap);
}
static void
do_brackets(cap, dir)
CMDARG *cap;
int dir; /* BACKWARD or FORWARD */
{
int len;
FPOS new_pos;
FPOS *pos = NULL; /* init for GCC */
FPOS old_pos; /* cursor position before command */
char_u *ptr;
int flag;
long n;
cap->oap->motion_type = MCHAR;
cap->oap->inclusive = FALSE;
old_pos = curwin->w_cursor;
/*
* "[f" or "]f" : Edit file under the cursor (same as "gf")
*/
if (cap->nchar == 'f')
do_gotofile(cap);
/*
* Find the occurence(s) of the identifier or define under cursor
* in current and included files or jump to the first occurence.
*
* search list jump
* fwd bwd fwd bwd fwd bwd
* identifier "]i" "[i" "]I" "[I" "]^I" "[^I"
* define "]d" "[d" "]D" "[D" "]^D" "[^D"
*/
else if (vim_strchr((char_u *)"iI\011dD\004", cap->nchar) != NULL)
{
if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
clearop(cap->oap);
else
{
find_pattern_in_path(ptr, len, TRUE,
cap->count0 == 0 ? !isupper(cap->nchar) : FALSE,
((cap->nchar & 0xf) == ('d' & 0xf)) ? FIND_DEFINE : FIND_ANY,
cap->count1,
isupper(cap->nchar) ? ACTION_SHOW_ALL :
islower(cap->nchar) ? ACTION_SHOW : ACTION_GOTO,
cap->cmdchar == ']' ? curwin->w_cursor.lnum : (linenr_t)1,
(linenr_t)MAXLNUM);
curwin->w_set_curswant = TRUE;
}
}
/*
* "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
* "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
* "[/", "[*", "]/", "]*": go to Nth comment start/end.
*/
else if ( (cap->cmdchar == '['
&& vim_strchr((char_u *)"{(*/#", cap->nchar) != NULL)
|| (cap->cmdchar == ']'
&& vim_strchr((char_u *)"})*/#", cap->nchar) != NULL))
{
if (cap->nchar == '*')
cap->nchar = '/';
new_pos.lnum = 0;
for (n = cap->count1; n > 0; --n)
{
if ((pos = findmatchlimit(cap->oap, cap->nchar,
(cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL)
{
if (new_pos.lnum == 0) /* nothing found */
clearopbeep(cap->oap);
else
pos = &new_pos; /* use last one found */
break;
}
curwin->w_cursor = *pos;
new_pos = *pos;
}
curwin->w_cursor = old_pos;
if (pos != NULL)
{
setpcmark();
curwin->w_cursor = *pos;
curwin->w_set_curswant = TRUE;
}
}
/*
* "[[", "[]", "]]" and "][": move to start or end of function
*/
else if (cap->nchar == '[' || cap->nchar == ']')
{
if (cap->nchar == cap->cmdchar) /* "]]" or "[[" */
flag = '{';
else
flag = '}'; /* "][" or "[]" */
curwin->w_set_curswant = TRUE;
/*
* Imitate strange Vi behaviour: When using "]]" with an operator
* we also stop at '}'.
*/
if (!findpar(cap->oap, dir, cap->count1, flag,
(cap->oap->op_type != OP_NOP && dir == FORWARD && flag == '{')))
clearopbeep(cap->oap);
else if (cap->oap->op_type == OP_NOP)
beginline(TRUE);
}
/*
* "[p", "[P", "]P" and "]p": put with indent adjustment
*/
else if (cap->nchar == 'p' || cap->nchar == 'P')
{
if (!checkclearopq(cap->oap))
{
prep_redo_cmd(cap);
do_put(cap->oap->regname,
(cap->cmdchar == ']' && cap->nchar == 'p') ? FORWARD : BACKWARD,
cap->count1, TRUE);
}
}
#ifdef USE_MOUSE
/*
* [ or ] followed by a middle mouse click: put selected text with
* indent adjustment. Any other button just does as usual.
*/
else if (cap->nchar >= K_LEFTMOUSE && cap->nchar <= K_RIGHTRELEASE)
{
(void)do_mouse(cap->oap, cap->nchar,
(cap->cmdchar == ']') ? FORWARD : BACKWARD, cap->count1, TRUE);
}
#endif /* USE_MOUSE */
/* Not a valid cap->nchar. */
else
clearopbeep(cap->oap);
}
/*
* Handle Normal mode "%" command.
*/
static void
do_percent(cap)
CMDARG *cap;
{
FPOS *pos;
cap->oap->inclusive = TRUE;
if (cap->count0) /* {cnt}% : goto {cnt} percentage in file */
{
if (cap->count0 > 100)
clearopbeep(cap->oap);
else
{
cap->oap->motion_type = MLINE;
setpcmark();
/* round up, so CTRL-G will give same value */
curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count *
cap->count0 + 99) / 100;
beginline(MAYBE);
}
}
else /* "%" : go to matching paren */
{
cap->oap->motion_type = MCHAR;
if ((pos = findmatch(cap->oap, NUL)) == NULL)
clearopbeep(cap->oap);
else
{
setpcmark();
curwin->w_cursor = *pos;
curwin->w_set_curswant = TRUE;
}
}
}
/*
* Handle the "r" command.
*/
static int
do_replace(cap)
CMDARG *cap;
{
char_u *ptr;
int had_ctrl_v;
int command_busy = FALSE;
long n;
ptr = ml_get_cursor();
/* special key or not enough characters to replace */
if (cap->nchar >= 0x100 || STRLEN(ptr) < (unsigned)cap->count1)
{
clearopbeep(cap->oap);
return FALSE;
}
/*
* Replacing with a TAB is done by edit(), because it is complicated when
* 'expandtab' or 'smarttab' is set.
* Other characters are done below to avoid problems with things like
* CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC).
*/
if (cap->nchar == '\t' && (curbuf->b_p_et || p_sta))
{
stuffnumReadbuff(cap->count1);
stuffcharReadbuff('R');
stuffcharReadbuff('\t');
stuffcharReadbuff(ESC);
return FALSE;
}
if (cap->nchar == Ctrl('V')) /* get another character */
{
had_ctrl_v = Ctrl('V');
cap->nchar = get_literal();
}
else
had_ctrl_v = NUL;
if (u_save_cursor() == FAIL) /* save line for undo */
return FALSE;
if (had_ctrl_v != Ctrl('V') && (cap->nchar == '\r' || cap->nchar == '\n'))
{
/*
* Replace character(s) by a single newline.
* Strange vi behaviour: Only one newline is inserted.
* Delete the characters here.
* Insert the newline with an insert command, takes care of
* autoindent. The insert command depends on being on the last
* character of a line or not.
*/
(void)del_chars(cap->count1, FALSE); /* delete the characters */
stuffcharReadbuff('\r');
stuffcharReadbuff(ESC);
/*
* Give 'r' to edit(), to get the redo command right.
*/
command_busy = edit('r', FALSE, cap->count1);
}
else
{
prep_redo(cap->oap->regname, cap->count1,
NUL, 'r', had_ctrl_v, cap->nchar);
for (n = cap->count1; n > 0; --n) /* replace the characters */
{
/*
* Replace a 'normal' character.
* Get ptr again, because u_save and/or showmatch() will have
* released the line. At the same time we let know that the line
* will be changed.
*/
ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
ptr[curwin->w_cursor.col] = cap->nchar;
if ( p_sm
&& (cap->nchar == ')'
|| cap->nchar == '}'
|| cap->nchar == ']'))
showmatch();
++curwin->w_cursor.col;
}
--curwin->w_cursor.col; /* cursor on the last replaced char */
curwin->w_set_curswant = TRUE;
set_last_insert(cap->nchar);
changed_cline_bef_curs(); /* update cursor screen pos. later */
/* w_botline might change a bit when replacing special characters */
approximate_botline();
update_screenline();
}
CHANGED;
return command_busy;
}
/*
* Exchange start and end of Visual area.
*/
static void
switch_visual()
{
linenr_t lnum;
colnr_t col;
lnum = VIsual.lnum;
VIsual.lnum = curwin->w_cursor.lnum;
curwin->w_cursor.lnum = lnum;
col = VIsual.col;
VIsual.col = curwin->w_cursor.col;
curwin->w_cursor.col = col;
curwin->w_set_curswant = TRUE;
}
/*
* Swap case for "~" command, when it does not work like an operator.
*/
static void
do_swapchar(cap)
CMDARG *cap;
{
long n;
if (checkclearopq(cap->oap))
return;
if (lineempty(curwin->w_cursor.lnum))
{
clearopbeep(cap->oap);
return;
}
prep_redo_cmd(cap);
if (u_save_cursor() == FAIL)
return;
for (n = cap->count1; n > 0; --n)
{
if (gchar_cursor() == NUL)
break;
swapchar(cap->oap->op_type, &curwin->w_cursor);
inc_cursor();
}
curwin->w_set_curswant = TRUE;
CHANGED;
/* assume that the length of the line doesn't change, so w_botline
* remains valid */
update_screenline();
}
/*
* Move cursor to mark.
*/
static void
do_cursormark(cap, flag, pos)
CMDARG *cap;
int flag;
FPOS *pos;
{
if (check_mark(pos) == FAIL)
clearop(cap->oap);
else
{
if (cap->cmdchar == '\'' || cap->cmdchar == '`')
setpcmark();
curwin->w_cursor = *pos;
if (flag)
beginline(TRUE);
else
adjust_cursor();
}
cap->oap->motion_type = flag ? MLINE : MCHAR;
cap->oap->inclusive = FALSE; /* ignored if not MCHAR */
curwin->w_set_curswant = TRUE;
}
/*
* Handle commands that are operators in Visual mode.
*/
static void
do_visop(cap)
CMDARG *cap;
{
static char_u trans[] = "YyDdCcxdXd";
/* uppercase means linewise */
if (isupper(cap->cmdchar) && VIsual_mode != Ctrl('V'))
VIsual_mode = 'V';
cap->cmdchar = *(vim_strchr(trans, cap->cmdchar) + 1);
do_operator(cap);
}
/*
* Translate a command into another command.
*/
static void
do_optrans(cap)
CMDARG *cap;
{
static char_u *(ar[8]) = {(char_u *)"dl", (char_u *)"dh",
(char_u *)"d$", (char_u *)"c$",
(char_u *)"cl", (char_u *)"cc",
(char_u *)"yy", (char_u *)":s\r"};
static char_u *str = (char_u *)"xXDCsSY&";
if (!checkclearopq(cap->oap))
{
if (cap->count0)
stuffnumReadbuff(cap->count0);
stuffReadbuff(ar[(int)(vim_strchr(str, cap->cmdchar) - str)]);
}
}
/*
* Handle "'" and "`" commands.
*/
static void
do_gomark(cap, flag)
CMDARG *cap;
int flag;
{
FPOS *pos;
pos = getmark(cap->nchar, (cap->oap->op_type == OP_NOP));
if (pos == (FPOS *)-1) /* jumped to other file */
{
if (flag)
beginline(TRUE);
}
else
do_cursormark(cap, flag, pos);
}
/*
* Handle CTRL-O and CTRL-I commands.
*/
static void
do_pcmark(cap)
CMDARG *cap;
{
FPOS *pos;
if (!checkclearopq(cap->oap))
{
pos = movemark((int)cap->count1);
if (pos == (FPOS *)-1) /* jump to other file */
curwin->w_set_curswant = TRUE;
else if (pos != NULL) /* can jump */
do_cursormark(cap, FALSE, pos);
else
clearopbeep(cap->oap);
}
}
/*
* Handle '"' command.
*/
static void
do_regname(cap, opnum)
CMDARG *cap;
linenr_t opnum;
{
if (checkclearop(cap->oap))
return;
if (cap->nchar == '=')
cap->nchar = get_expr_register();
if (cap->nchar != NUL && is_yank_register(cap->nchar, FALSE))
{
cap->oap->regname = cap->nchar;
opnum = cap->count0; /* remember count before '"' */
}
else
clearopbeep(cap->oap);
}
/*
* Handle "v", "V" and "CTRL-V" commands.
*/
static void
do_visual(cap)
CMDARG *cap;
{
if (VIsual_active) /* change Visual mode */
{
if (VIsual_mode == cap->cmdchar) /* stop visual mode */
end_visual_mode();
else /* toggle char/block mode */
{ /* or char/line mode */
VIsual_mode = cap->cmdchar;
showmode();
}
update_curbuf(NOT_VALID); /* update the inversion */
}
else /* start Visual mode */
{
VIsual_save = VIsual; /* keep for "gv" */
VIsual_mode_save = VIsual_mode;
check_visual_highlight();
if (cap->count0) /* use previously selected part */
{
if (resel_VIsual_mode == NUL) /* there is none */
{
beep_flush();
return;
}
VIsual = curwin->w_cursor;
VIsual_active = TRUE;
#ifdef USE_MOUSE
setmouse();
#endif
if (p_smd)
redraw_cmdline = TRUE; /* show visual mode later */
/*
* For V and ^V, we multiply the number of lines even if there
* was only one -- webb
*/
if (resel_VIsual_mode != 'v' || resel_VIsual_line_count > 1)
{
curwin->w_cursor.lnum +=
resel_VIsual_line_count * cap->count0 - 1;
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
}
VIsual_mode = resel_VIsual_mode;
if (VIsual_mode == 'v')
{
if (resel_VIsual_line_count <= 1)
curwin->w_cursor.col += resel_VIsual_col * cap->count0 - 1;
else
curwin->w_cursor.col = resel_VIsual_col;
}
if (resel_VIsual_col == MAXCOL)
{
curwin->w_curswant = MAXCOL;
coladvance(MAXCOL);
}
else if (VIsual_mode == Ctrl('V'))
{
validate_virtcol();
curwin->w_curswant = curwin->w_virtcol +
resel_VIsual_col * cap->count0 - 1;
coladvance((colnr_t)curwin->w_curswant);
}
else
curwin->w_set_curswant = TRUE;
update_curbuf(NOT_VALID); /* show the inversion */
}
else
{
VIsual = curwin->w_cursor;
VIsual_mode = cap->cmdchar;
VIsual_active = TRUE;
#ifdef USE_MOUSE
setmouse();
#endif
if (p_smd)
redraw_cmdline = TRUE; /* show visual mode later */
update_screenline(); /* start the inversion */
}
}
}
/*
* Handle the "g" command.
*/
static int
do_g_cmd(cap)
CMDARG *cap;
{
OPARG *oap = cap->oap;
FPOS *pos;
FPOS tpos;
int i;
int flag = FALSE;
int command_busy = FALSE;
switch (cap->nchar)
{
/*
* "gv": reselect the previous visual area
*/
case 'v':
if (checkclearop(oap))
break;
if (VIsual_active)
pos = &VIsual_save;
else
pos = &VIsual;
if (pos->lnum == 0 || pos->lnum > curbuf->b_ml.ml_line_count ||
VIsual_end.lnum == 0)
beep_flush();
else
{
/* exchange previous and current visual area */
if (VIsual_active)
{
tpos = VIsual;
VIsual = VIsual_save;
VIsual_save = tpos;
i = VIsual_mode;
VIsual_mode = VIsual_mode_save;
VIsual_mode_save = i;
tpos = curwin->w_cursor;
}
curwin->w_cursor = VIsual_end;
if (VIsual_active)
VIsual_end = tpos;
check_cursor_lnum();
VIsual_active = TRUE;
#ifdef USE_MOUSE
setmouse();
#endif
update_curbuf(NOT_VALID);
showmode();
}
break;
/*
* "gj" and "gk" two new funny movement keys -- up and down
* movement based on *screen* line rather than *file* line.
*/
case 'j':
case K_DOWN:
/* with 'nowrap' it works just like the normal "j" command */
if (!curwin->w_p_wrap)
{
oap->motion_type = MLINE;
i = cursor_down(cap->count1, oap->op_type != OP_NOP);
}
else
i = screengo(oap, FORWARD, cap->count1);
if (i == FAIL)
clearopbeep(oap);
break;
case 'k':
case K_UP:
/* with 'nowrap' it works just like the normal "k" command */
if (!curwin->w_p_wrap)
{
oap->motion_type = MLINE;
i = cursor_up(cap->count1, oap->op_type != OP_NOP);
}
else
i = screengo(oap, BACKWARD, cap->count1);
if (i == FAIL)
clearopbeep(oap);
break;
/*
* "g0", "g^" and "g$": Like "0", "^" and "$" but for screen lines.
*/
case '^':
flag = TRUE;
/* FALLTHROUGH */
case '0':
case K_HOME:
case K_KHOME:
oap->motion_type = MCHAR;
oap->inclusive = FALSE;
if (curwin->w_p_wrap)
{
validate_virtcol();
i = ((curwin->w_virtcol + (curwin->w_p_nu ? 8 : 0)) /
Columns) * Columns;
if (curwin->w_p_nu && i > 8)
i -= 8;
}
else
i = curwin->w_leftcol;
coladvance((colnr_t)i);
if (flag)
while (vim_iswhite(gchar_cursor()) && oneright() == OK)
;
curwin->w_set_curswant = TRUE;
break;
case '$':
case K_END:
case K_KEND:
oap->motion_type = MCHAR;
oap->inclusive = TRUE;
if (curwin->w_p_wrap)
{
curwin->w_curswant = MAXCOL; /* so we stay at the end */
if (cap->count1 == 1)
{
validate_virtcol();
i = ((curwin->w_virtcol + (curwin->w_p_nu ? 8 : 0)) /
Columns + 1) * Columns - 1;
if (curwin->w_p_nu && i > 8)
i -= 8;
coladvance((colnr_t)i);
}
else if (screengo(oap, FORWARD, cap->count1 - 1) == FAIL)
clearopbeep(oap);
}
else
{
i = curwin->w_leftcol + Columns - 1;
if (curwin->w_p_nu)
i -= 8;
coladvance((colnr_t)i);
curwin->w_set_curswant = TRUE;
}
break;
/*
* "g*" and "g#", like "*" and "#" but without using "\<" and "\>"
*/
case '*':
case '#':
do_ident(cap, FALSE);
break;
/*
* ge and gE: go back to end of word
*/
case 'e':
case 'E':
oap->motion_type = MCHAR;
curwin->w_set_curswant = TRUE;
oap->inclusive = TRUE;
if (bckend_word(cap->count1, cap->nchar == 'E', FALSE) == FAIL)
clearopbeep(oap);
break;
/*
* "g CTRL-G": display info about cursor position
*/
case Ctrl('G'):
cursor_pos_info();
break;
/*
* "gI": Start insert in column 1.
*/
case 'I':
beginline(FALSE);
if (!checkclearopq(oap))
{
if (u_save_cursor() == OK)
command_busy = edit('g', FALSE, cap->count1);
}
break;
/*
* "gf": goto file, edit file under cursor
* "]f" and "[f": can also be used.
*/
case 'f':
do_gotofile(cap);
break;
/*
* "gs": Goto sleep, but keep on checking for CTRL-C
*/
case 's':
while (cap->count1-- && !got_int)
{
ui_delay(1000L, TRUE);
ui_breakcheck();
}
break;
/*
* "ga": Display the ascii value of the character under the
* cursor. It is displayed in decimal, hex, and octal. -- webb
*/
case 'a':
do_ascii();
break;
/*
* "gg": Goto the first line in file. With a count it goes to
* that line number like for "G". -- webb
*/
case 'g':
do_goto(oap, cap->count0); /* do_goto() will change 0 into 1 */
break;
/*
* Two-character operators:
* "gq" Format text
* "g~" Toggle the case of the text.
* "gu" Change text to lower case.
* "gU" Change text to upper case.
*/
case 'q':
case '~':
case 'u':
case 'U':
oap->op_prechar = 'g';
cap->cmdchar = cap->nchar;
do_operator(cap);
break;
/*
* "gd": Find first occurence of pattern under the cursor in the
* current function
* "gD": idem, but in the current file.
*/
case 'd':
case 'D':
do_gd(oap, cap->nchar);
break;
#ifdef USE_MOUSE
/*
* g<*Mouse> : <C-*mouse>
*/
case K_MIDDLEMOUSE:
case K_MIDDLEDRAG:
case K_MIDDLERELEASE:
case K_LEFTMOUSE:
case K_LEFTDRAG:
case K_LEFTRELEASE:
case K_RIGHTMOUSE:
case K_RIGHTDRAG:
case K_RIGHTRELEASE:
mod_mask = MOD_MASK_CTRL;
(void)do_mouse(oap, cap->nchar, BACKWARD, cap->count1, FALSE);
break;
case K_IGNORE:
break;
#endif
default:
clearopbeep(oap);
break;
}
return command_busy;
}
/*
* Handle "o" and "O" commands.
*/
static int
do_opencmd(cap)
CMDARG *cap;
{
int command_busy = FALSE;
if (!checkclearopq(cap->oap))
{
if (has_format_option(FO_OPEN_COMS))
fo_do_comments = TRUE;
if (u_save((linenr_t)(curwin->w_cursor.lnum -
(cap->cmdchar == 'O' ? 1 : 0)),
(linenr_t)(curwin->w_cursor.lnum +
(cap->cmdchar == 'o' ? 1 : 0))
) == OK
&& open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD,
TRUE, FALSE, 0))
command_busy = edit(cap->cmdchar, TRUE, cap->count1);
fo_do_comments = FALSE;
}
return command_busy;
}
/*
* Handle an operator command.
*/
static void
do_operator(cap)
CMDARG *cap;
{
int i;
i = vim_strchr(op_chars, cap->cmdchar) - op_chars + 1;
if (i == cap->oap->op_type) /* double operator works on lines */
do_lineop(cap);
else if (!checkclearop(cap->oap))
{
cap->oap->start = curwin->w_cursor;
cap->oap->op_type = i;
}
}
/*
* Handle linewise operator "dd", "yy", etc.
*/
static void
do_lineop(cap)
CMDARG *cap;
{
cap->oap->motion_type = MLINE;
if (cursor_down(cap->count1 - 1L, cap->oap->op_type != OP_NOP) == FAIL)
clearopbeep(cap->oap);
else if ( cap->oap->op_type == OP_DELETE
|| cap->oap->op_type == OP_LSHIFT
|| cap->oap->op_type == OP_RSHIFT)
beginline(MAYBE);
else if (cap->oap->op_type != OP_YANK) /* 'Y' does not move cursor */
beginline(TRUE);
}
/*
* Handle word motion commands "e", "E", "w" and "W".
*/
static void
do_wordcmd(cap, type)
CMDARG *cap;
int type;
{
int n;
int word_end;
int flag = FALSE;
/*
* Inclusive has been set for the "E" and "e" command.
*/
word_end = cap->oap->inclusive;
/*
* For the "w" and "W" commands we need to check for special cases.
*/
if (!word_end)
{
/*
* "cw" and "cW" are a special case.
*/
if (cap->oap->op_type == OP_CHANGE)
{
n = gchar_cursor();
if (n != NUL) /* not an empty line */
{
if (vim_iswhite(n))
{
/*
* Reproduce a funny Vi behaviour: "cw" on a blank only
* changes one character, not all blanks until the start
* of the next word. Only do this when the 'w' flag is
* included in 'cpoptions'.
*/
if (cap->count1 == 1 && vim_strchr(p_cpo, CPO_CW) != NULL)
{
cap->oap->inclusive = TRUE;
cap->oap->motion_type = MCHAR;
return;
}
}
else
{
/*
* This is a little strange. To match what the real Vi
* does, we effectively map 'cw' to 'ce', and 'cW' to
* 'cE', provided that we are not on a space or a TAB.
* This seems impolite at first, but it's really more what
* we mean when we say 'cw'.
* Another strangeness: When standing on the end of a word
* "ce" will change until the end of the next wordt, but
* "cw" will change only one character! This is done by
* setting flag.
*/
cap->oap->inclusive = TRUE;
word_end = TRUE;
flag = TRUE;
}
}
}
}
cap->oap->motion_type = MCHAR;
curwin->w_set_curswant = TRUE;
if (word_end)
n = end_word(cap->count1, type, flag, FALSE);
else
n = fwd_word(cap->count1, type, cap->oap->op_type != OP_NOP);
if (n == FAIL)
clearopbeep(cap->oap);
}
static void
do_goto(oap, lnum)
OPARG *oap;
long lnum;
{
oap->motion_type = MLINE;
setpcmark();
if (lnum < 1L)
lnum = 1L;
else if (lnum > curbuf->b_ml.ml_line_count)
lnum = curbuf->b_ml.ml_line_count;
curwin->w_cursor.lnum = lnum;
beginline(MAYBE);
}
/*
* ESC in Normal mode: beep, but don't flush buffers.
* Don't even beep if we are canceling a command.
*/
static void
do_esc(cap, opnum)
CMDARG *cap;
linenr_t opnum;
{
if (VIsual_active)
{
end_visual_mode(); /* stop Visual */
update_curbuf(NOT_VALID);
}
else if (cap->oap->op_type == OP_NOP && opnum == 0 &&
cap->count0 == 0 && cap->oap->regname == 0)
vim_beep();
clearop(cap->oap);
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.