aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/normal.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/normal.c')
-rw-r--r--src/nvim/normal.c404
1 files changed, 279 insertions, 125 deletions
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 2f57d8c610..e064d34e09 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -1414,11 +1414,12 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
int lbr_saved = curwin->w_p_lbr;
- /* 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_vcol; /* number of cols or end column */
- static long redo_VIsual_count; /* count for Visual operator */
+ // 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_vcol; // number of cols or end column
+ static long redo_VIsual_count; // count for Visual operator
+ static int redo_VIsual_arg; // extra argument
bool include_line_break = false;
old_cursor = curwin->w_cursor;
@@ -1430,6 +1431,9 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
|| VIsual_active
) && oap->op_type != OP_NOP) {
// Avoid a problem with unwanted linebreaks in block mode
+ if (curwin->w_p_lbr) {
+ curwin->w_valid &= ~VALID_VIRTCOL;
+ }
curwin->w_p_lbr = false;
oap->is_VIsual = VIsual_active;
if (oap->motion_force == 'V')
@@ -1534,9 +1538,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
curbuf->b_visual_mode_eval = VIsual_mode;
}
- /* In Select mode, a linewise selection is operated upon like a
- * characterwise selection. */
- if (VIsual_select && VIsual_mode == 'V') {
+ // In Select mode, a linewise selection is operated upon like a
+ // characterwise selection.
+ // Special case: gH<Del> deletes the last line.
+ if (VIsual_select && VIsual_mode == 'V'
+ && cap->oap->op_type != OP_DELETE) {
if (lt(VIsual, curwin->w_cursor)) {
VIsual.col = 0;
curwin->w_cursor.col =
@@ -1598,55 +1604,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
virtual_op = virtual_active();
if (VIsual_active || redo_VIsual_busy) {
- if (VIsual_mode == Ctrl_V) { /* block mode */
- colnr_T start, end;
-
- oap->motion_type = MBLOCK;
-
- getvvcol(curwin, &(oap->start),
- &oap->start_vcol, NULL, &oap->end_vcol);
- if (!redo_VIsual_busy) {
- getvvcol(curwin, &(oap->end), &start, NULL, &end);
-
- if (start < oap->start_vcol)
- oap->start_vcol = start;
- if (end > oap->end_vcol) {
- if (*p_sel == 'e' && start >= 1
- && start - 1 >= oap->end_vcol)
- oap->end_vcol = start - 1;
- else
- 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) {
- getvvcol(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_vcol - 1;
- /*
- * Correct oap->end.col and oap->start.col to be the
- * upper-left and lower-right corner of the block area.
- *
- * (Actually, this does convert column positions into character
- * positions)
- */
- 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;
- }
+ get_op_vcol(oap, redo_VIsual_vcol, true);
if (!redo_VIsual_busy && !gui_yank) {
/*
@@ -1701,6 +1659,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
redo_VIsual_vcol = resel_VIsual_vcol;
redo_VIsual_line_count = resel_VIsual_line_count;
redo_VIsual_count = cap->count0;
+ redo_VIsual_arg = cap->arg;
}
}
@@ -1719,20 +1678,15 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
&& (include_line_break || !virtual_op)
) {
oap->inclusive = false;
- /* Try to include the newline, unless it's an operator
- * that works on lines only. */
- if (*p_sel != 'o' && !op_on_lines(oap->op_type)) {
- if (oap->end.lnum < curbuf->b_ml.ml_line_count) {
- ++oap->end.lnum;
- oap->end.col = 0;
- oap->end.coladd = 0;
- ++oap->line_count;
- } else {
- /* Cannot move below the last line, make the op
- * inclusive to tell the operation to include the
- * line break. */
- oap->inclusive = true;
- }
+ // Try to include the newline, unless it's an operator
+ // that works on lines only.
+ if (*p_sel != 'o'
+ && !op_on_lines(oap->op_type)
+ && oap->end.lnum < curbuf->b_ml.ml_line_count) {
+ oap->end.lnum++;
+ oap->end.col = 0;
+ oap->end.coladd = 0;
+ oap->line_count++;
}
}
}
@@ -1750,10 +1704,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
VIsual_active = false;
setmouse();
mouse_dragging = 0;
- if (mode_displayed)
- clear_cmdline = true; /* unshow visual mode later */
- else
- clear_showcmd();
+ may_clear_cmdline();
if ((oap->op_type == OP_YANK
|| oap->op_type == OP_COLON
|| oap->op_type == OP_FUNCTION
@@ -1894,9 +1845,14 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
else
restart_edit_save = 0;
restart_edit = 0;
+
// Restore linebreak, so that when the user edits it looks as before.
- curwin->w_p_lbr = lbr_saved;
- /* Reset finish_op now, don't want it set inside edit(). */
+ if (curwin->w_p_lbr != lbr_saved) {
+ curwin->w_p_lbr = lbr_saved;
+ get_op_vcol(oap, redo_VIsual_mode, false);
+ }
+
+ // Reset finish_op now, don't want it set inside edit().
finish_op = false;
if (op_change(oap)) /* will call edit() */
cap->retval |= CA_COMMAND_BUSY;
@@ -1974,7 +1930,10 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
restart_edit = 0;
// Restore linebreak, so that when the user edits it looks as before.
- curwin->w_p_lbr = lbr_saved;
+ if (curwin->w_p_lbr != lbr_saved) {
+ curwin->w_p_lbr = lbr_saved;
+ get_op_vcol(oap, redo_VIsual_mode, false);
+ }
op_insert(oap, cap->count1);
@@ -1997,7 +1956,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
CancelRedo();
} else {
// Restore linebreak, so that when the user edits it looks as before.
- curwin->w_p_lbr = lbr_saved;
+ if (curwin->w_p_lbr != lbr_saved) {
+ curwin->w_p_lbr = lbr_saved;
+ get_op_vcol(oap, redo_VIsual_mode, false);
+ }
+
op_replace(oap, cap->nchar);
}
break;
@@ -2026,6 +1989,20 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
deleteFold(oap->start.lnum, oap->end.lnum,
oap->op_type == OP_FOLDDELREC, oap->is_VIsual);
break;
+
+ case OP_NR_ADD:
+ case OP_NR_SUB:
+ if (empty_region_error) {
+ vim_beep(BO_OPER);
+ CancelRedo();
+ } else {
+ VIsual_active = true;
+ curwin->w_p_lbr = lbr_saved;
+ op_addsub(oap, cap->count1, redo_VIsual_arg);
+ VIsual_active = false;
+ }
+ check_cursor_col();
+ break;
default:
clearopbeep(oap);
}
@@ -2367,8 +2344,9 @@ do_mouse (
if (mouse_row == 0 && firstwin->w_winrow > 0) {
if (is_drag) {
if (in_tab_line) {
- c1 = TabPageIdxs[mouse_col];
- tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
+ tabpage_move(tab_page_click_defs[mouse_col].type == kStlClickTabClose
+ ? 9999
+ : tab_page_click_defs[mouse_col].tabnr - 1);
}
return false;
}
@@ -2378,41 +2356,114 @@ do_mouse (
&& cmdwin_type == 0
&& mouse_col < Columns) {
in_tab_line = true;
- c1 = TabPageIdxs[mouse_col];
- if (c1 >= 0) {
- if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
- /* double click opens new page */
- end_visual_mode();
- tabpage_new();
- tabpage_move(c1 == 0 ? 9999 : c1 - 1);
- } else {
- /* Go to specified tab page, or next one if not clicking
- * on a label. */
- goto_tabpage(c1);
-
- /* It's like clicking on the status line of a window. */
- if (curwin != old_curwin)
+ c1 = tab_page_click_defs[mouse_col].tabnr;
+ switch (tab_page_click_defs[mouse_col].type) {
+ case kStlClickDisabled: {
+ break;
+ }
+ case kStlClickTabClose: {
+ tabpage_T *tp;
+
+ // Close the current or specified tab page.
+ if (c1 == 999) {
+ tp = curtab;
+ } else {
+ tp = find_tabpage(c1);
+ }
+ if (tp == curtab) {
+ if (first_tabpage->tp_next != NULL) {
+ tabpage_close(false);
+ }
+ } else if (tp != NULL) {
+ tabpage_close_other(tp, false);
+ }
+ break;
+ }
+ case kStlClickTabSwitch: {
+ if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
+ // double click opens new page
end_visual_mode();
+ tabpage_new();
+ tabpage_move(c1 == 0 ? 9999 : c1 - 1);
+ } else {
+ // Go to specified tab page, or next one if not clicking
+ // on a label.
+ goto_tabpage(c1);
+
+ // It's like clicking on the status line of a window.
+ if (curwin != old_curwin) {
+ end_visual_mode();
+ }
+ }
+ break;
+ }
+ case kStlClickFuncRun: {
+ typval_T argv[] = {
+ {
+ .v_lock = VAR_FIXED,
+ .v_type = VAR_NUMBER,
+ .vval = {
+ .v_number = (varnumber_T) tab_page_click_defs[mouse_col].tabnr
+ },
+ },
+ {
+ .v_lock = VAR_FIXED,
+ .v_type = VAR_NUMBER,
+ .vval = {
+ .v_number = (((mod_mask & MOD_MASK_MULTI_CLICK)
+ == MOD_MASK_4CLICK)
+ ? 4
+ : ((mod_mask & MOD_MASK_MULTI_CLICK)
+ == MOD_MASK_3CLICK)
+ ? 3
+ : ((mod_mask & MOD_MASK_MULTI_CLICK)
+ == MOD_MASK_2CLICK)
+ ? 2
+ : 1)
+ },
+ },
+ {
+ .v_lock = VAR_FIXED,
+ .v_type = VAR_STRING,
+ .vval = { .v_string = (char_u *) (which_button == MOUSE_LEFT
+ ? "l"
+ : which_button == MOUSE_RIGHT
+ ? "r"
+ : which_button == MOUSE_MIDDLE
+ ? "m"
+ : "?") },
+ },
+ {
+ .v_lock = VAR_FIXED,
+ .v_type = VAR_STRING,
+ .vval = {
+ .v_string = (char_u[]) {
+ (char_u) (mod_mask & MOD_MASK_SHIFT ? 's' : ' '),
+ (char_u) (mod_mask & MOD_MASK_CTRL ? 'c' : ' '),
+ (char_u) (mod_mask & MOD_MASK_ALT ? 'a' : ' '),
+ (char_u) (mod_mask & MOD_MASK_META ? 'm' : ' '),
+ NUL
+ }
+ },
+ }
+ };
+ typval_T rettv;
+ int doesrange;
+ (void) call_func((char_u *) tab_page_click_defs[mouse_col].func,
+ (int) strlen(tab_page_click_defs[mouse_col].func),
+ &rettv, ARRAY_SIZE(argv), argv,
+ curwin->w_cursor.lnum, curwin->w_cursor.lnum,
+ &doesrange, true, NULL);
+ clear_tv(&rettv);
+ break;
}
- } else if (c1 < 0) {
- tabpage_T *tp;
-
- /* Close the current or specified tab page. */
- if (c1 == -999)
- tp = curtab;
- else
- tp = find_tabpage(-c1);
- if (tp == curtab) {
- if (first_tabpage->tp_next != NULL)
- tabpage_close(false);
- } else if (tp != NULL)
- tabpage_close_other(tp, false);
}
}
return true;
} else if (is_drag && in_tab_line) {
- c1 = TabPageIdxs[mouse_col];
- tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
+ tabpage_move(tab_page_click_defs[mouse_col].type == kStlClickTabClose
+ ? 9999
+ : tab_page_click_defs[mouse_col].tabnr - 1);
in_tab_line = false;
return false;
}
@@ -2885,10 +2936,7 @@ void end_visual_mode(void)
if (!virtual_active())
curwin->w_cursor.coladd = 0;
- if (mode_displayed)
- clear_cmdline = true; /* unshow visual mode later */
- else
- clear_showcmd();
+ may_clear_cmdline();
adjust_cursor_eol();
}
@@ -3154,10 +3202,19 @@ static void unshift_special(cmdarg_T *cap)
cap->cmdchar = simplify_key(cap->cmdchar, &mod_mask);
}
-/*
- * Routines for displaying a partly typed command
- */
+/// If the mode is currently displayed clear the command line or update the
+/// command displayed.
+static void may_clear_cmdline(void)
+{
+ if (mode_displayed) {
+ // unshow visual mode later
+ clear_cmdline = true;
+ } else {
+ clear_showcmd();
+ }
+}
+// Routines for displaying a partly typed command
# define SHOWCMD_BUFLEN SHOWCMD_COLS + 1 + 30
static char_u showcmd_buf[SHOWCMD_BUFLEN];
static char_u old_showcmd_buf[SHOWCMD_BUFLEN]; /* For push_showcmd() */
@@ -3536,9 +3593,16 @@ static void nv_help(cmdarg_T *cap)
*/
static void nv_addsub(cmdarg_T *cap)
{
- if (!checkclearopq(cap->oap)
- && do_addsub(cap->cmdchar, cap->count1))
+ if (!VIsual_active && cap->oap->op_type == OP_NOP) {
prep_redo_cmd(cap);
+ cap->oap->op_type = cap->cmdchar == Ctrl_A ? OP_NR_ADD : OP_NR_SUB;
+ op_addsub(cap->oap, cap->count1, cap->arg);
+ cap->oap->op_type = OP_NOP;
+ } else if (VIsual_active) {
+ nv_operator(cap);
+ } else {
+ clearop(cap->oap);
+ }
}
/*
@@ -6360,9 +6424,20 @@ static void nv_g_cmd(cmdarg_T *cap)
bool flag = false;
switch (cap->nchar) {
- /*
- * "gR": Enter virtual replace mode.
- */
+ // "g^A/g^X": Sequentially increment visually selected region.
+ case Ctrl_A:
+ case Ctrl_X:
+ if (VIsual_active) {
+ cap->arg = true;
+ cap->cmdchar = cap->nchar;
+ cap->nchar = NUL;
+ nv_addsub(cap);
+ } else {
+ clearopbeep(oap);
+ }
+ break;
+
+ // "gR": Enter virtual replace mode.
case 'R':
cap->arg = true;
nv_Replace(cap);
@@ -6877,10 +6952,16 @@ static void n_opencmd(cmdarg_T *cap)
(cap->cmdchar == 'o' ? 1 : 0))
)
&& open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD,
- has_format_option(FO_OPEN_COMS) ? OPENLINE_DO_COM :
- 0, 0)) {
- if (curwin->w_p_cole > 0 && oldline != curwin->w_cursor.lnum)
+ has_format_option(FO_OPEN_COMS)
+ ? OPENLINE_DO_COM : 0,
+ 0)) {
+ if (curwin->w_p_cole > 0 && oldline != curwin->w_cursor.lnum) {
update_single_line(curwin, oldline);
+ }
+ if (curwin->w_p_cul) {
+ // force redraw of cursorline
+ curwin->w_valid &= ~VALID_CROW;
+ }
invoke_edit(cap, false, cap->cmdchar, true);
}
}
@@ -7664,6 +7745,10 @@ static void nv_put(cmdarg_T *cap)
if (was_visual) {
curbuf->b_visual.vi_start = curbuf->b_op_start;
curbuf->b_visual.vi_end = curbuf->b_op_end;
+ // need to adjust cursor position
+ if (*p_sel == 'e') {
+ inc(&curbuf->b_visual.vi_end);
+ }
}
/* When all lines were selected and deleted do_put() leaves an empty
@@ -7698,6 +7783,75 @@ static void nv_open(cmdarg_T *cap)
n_opencmd(cap);
}
+// Calculate start/end virtual columns for operating in block mode.
+static void get_op_vcol(
+ oparg_T *oap,
+ colnr_T redo_VIsual_vcol,
+ bool initial // when true: adjust position for 'selectmode'
+)
+{
+ colnr_T start;
+ colnr_T end;
+
+ if (VIsual_mode != Ctrl_V
+ || (!initial && oap->end.col < curwin->w_width)) {
+ return;
+ }
+
+ oap->motion_type = MBLOCK;
+
+ // prevent from moving onto a trail byte
+ if (has_mbyte) {
+ mb_adjustpos(curwin->w_buffer, &oap->end);
+ }
+
+ getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol);
+ if (!redo_VIsual_busy) {
+ getvvcol(curwin, &(oap->end), &start, NULL, &end);
+
+ if (start < oap->start_vcol) {
+ oap->start_vcol = start;
+ }
+ if (end > oap->end_vcol) {
+ if (initial && *p_sel == 'e'
+ && start >= 1
+ && start - 1 >= oap->end_vcol) {
+ oap->end_vcol = start - 1;
+ } else {
+ 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) {
+ getvvcol(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_vcol - 1;
+ }
+
+ // Correct oap->end.col and oap->start.col to be the
+ // upper-left and lower-right corner of the block area.
+ //
+ // (Actually, this does convert column positions into character
+ // positions)
+ 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;
+}
+
// Handle an arbitrary event in normal mode
static void nv_event(cmdarg_T *cap)
{