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.c139
1 files changed, 71 insertions, 68 deletions
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 8ba375f29d..be9987cc7f 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -28,12 +28,14 @@
#include "nvim/digraph.h"
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
+#include "nvim/errors.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
+#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
@@ -349,6 +351,7 @@ static const struct nv_cmd {
{ K_F1, nv_help, NV_NCW, 0 },
{ K_XF1, nv_help, NV_NCW, 0 },
{ K_SELECT, nv_select, 0, 0 },
+ { K_PASTE_START, nv_paste, NV_KEEPREG, 0 },
{ K_EVENT, nv_event, NV_KEEPREG, 0 },
{ K_COMMAND, nv_colon, 0, 0 },
{ K_LUA, nv_colon, 0, 0 },
@@ -835,7 +838,10 @@ static void normal_get_additional_char(NormalState *s)
while ((s->c = vpeekc()) > 0
&& (s->c >= 0x100 || MB_BYTE2LEN(vpeekc()) > 1)) {
s->c = plain_vgetc();
- if (!utf_iscomposing(s->c)) {
+ // TODO(bfredl): only allowing up to two composing chars is cringe af.
+ // Could reuse/abuse schar_T to at least allow us to input anything we are able
+ // to display and use the stateful utf8proc algorithm like utf_composinglike
+ if (!utf_iscomposing_legacy(s->c)) {
vungetc(s->c); // it wasn't, put it back
break;
} else if (s->ca.ncharC1 == 0) {
@@ -1659,7 +1665,6 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char **text
// When starting on a ']' count it, so that we include the '['.
bn = ptr[col] == ']';
- //
// 2. Back up to start of identifier/text.
//
// Remember class of character under cursor.
@@ -1685,9 +1690,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char **text
// If we don't want just any old text, or we've found an
// identifier, stop searching.
- if (this_class > 2) {
- this_class = 2;
- }
+ this_class = MIN(this_class, 2);
if (!(find_type & FIND_STRING) || this_class == 2) {
break;
}
@@ -1986,7 +1989,7 @@ bool add_to_showcmd(int c)
size_t overflow = old_len + extra_len - limit;
memmove(showcmd_buf, showcmd_buf + overflow, old_len - overflow + 1);
}
- STRCAT(showcmd_buf, p);
+ strcat(showcmd_buf, p);
if (char_avail()) {
return false;
@@ -2011,9 +2014,7 @@ static void del_from_showcmd(int len)
}
int old_len = (int)strlen(showcmd_buf);
- if (len > old_len) {
- len = old_len;
- }
+ len = MIN(len, old_len);
showcmd_buf[old_len - len] = NUL;
if (!char_avail()) {
@@ -2096,17 +2097,23 @@ static void display_showcmd(void)
grid_line_flush();
}
+int get_vtopline(win_T *wp)
+{
+ return plines_m_win_fill(wp, 1, wp->w_topline) - wp->w_topfill;
+}
+
/// When "check" is false, prepare for commands that scroll the window.
/// When "check" is true, take care of scroll-binding after the window has
/// scrolled. Called from normal_cmd() and edit().
void do_check_scrollbind(bool check)
{
static win_T *old_curwin = NULL;
- static linenr_T old_topline = 0;
- static int old_topfill = 0;
+ static linenr_T old_vtopline = 0;
static buf_T *old_buf = NULL;
static colnr_T old_leftcol = 0;
+ int vtopline = get_vtopline(curwin);
+
if (check && curwin->w_p_scb) {
// If a ":syncbind" command was just used, don't scroll, only reset
// the values.
@@ -2119,10 +2126,9 @@ void do_check_scrollbind(bool check)
if ((curwin->w_buffer == old_buf
|| curwin->w_p_diff
)
- && (curwin->w_topline != old_topline
- || curwin->w_topfill != old_topfill
+ && (vtopline != old_vtopline
|| curwin->w_leftcol != old_leftcol)) {
- check_scrollbind(curwin->w_topline - old_topline, curwin->w_leftcol - old_leftcol);
+ check_scrollbind(vtopline - old_vtopline, curwin->w_leftcol - old_leftcol);
}
} else if (vim_strchr(p_sbo, 'j')) { // jump flag set in 'scrollopt'
// When switching between windows, make sure that the relative
@@ -2133,14 +2139,13 @@ void do_check_scrollbind(bool check)
// resync is performed, some of the other 'scrollbind' windows may
// need to jump so that the current window's relative position is
// visible on-screen.
- check_scrollbind(curwin->w_topline - (linenr_T)curwin->w_scbind_pos, 0);
+ check_scrollbind(vtopline - curwin->w_scbind_pos, 0);
}
- curwin->w_scbind_pos = curwin->w_topline;
+ curwin->w_scbind_pos = vtopline;
}
old_curwin = curwin;
- old_topline = curwin->w_topline;
- old_topfill = curwin->w_topfill;
+ old_vtopline = vtopline;
old_buf = curwin->w_buffer;
old_leftcol = curwin->w_leftcol;
}
@@ -2148,20 +2153,18 @@ void do_check_scrollbind(bool check)
/// Synchronize any windows that have "scrollbind" set, based on the
/// number of rows by which the current window has changed
/// (1998-11-02 16:21:01 R. Edward Ralston <eralston@computer.org>)
-void check_scrollbind(linenr_T topline_diff, int leftcol_diff)
+void check_scrollbind(linenr_T vtopline_diff, int leftcol_diff)
{
win_T *old_curwin = curwin;
buf_T *old_curbuf = curbuf;
int old_VIsual_select = VIsual_select;
int old_VIsual_active = VIsual_active;
colnr_T tgt_leftcol = curwin->w_leftcol;
- linenr_T topline;
- linenr_T y;
// check 'scrollopt' string for vertical and horizontal scroll options
- bool want_ver = (vim_strchr(p_sbo, 'v') && topline_diff != 0);
- want_ver |= old_curwin->w_p_diff;
- bool want_hor = (vim_strchr(p_sbo, 'h') && (leftcol_diff || topline_diff != 0));
+ bool want_ver = old_curwin->w_p_diff
+ || (vim_strchr(p_sbo, 'v') && vtopline_diff != 0);
+ bool want_hor = (vim_strchr(p_sbo, 'h') && (leftcol_diff || vtopline_diff != 0));
// loop through the scrollbound windows and scroll accordingly
VIsual_select = VIsual_active = 0;
@@ -2178,16 +2181,19 @@ void check_scrollbind(linenr_T topline_diff, int leftcol_diff)
if (old_curwin->w_p_diff && curwin->w_p_diff) {
diff_set_topline(old_curwin, curwin);
} else {
- curwin->w_scbind_pos += topline_diff;
- topline = (linenr_T)curwin->w_scbind_pos;
- if (topline > curbuf->b_ml.ml_line_count) {
- topline = curbuf->b_ml.ml_line_count;
- }
- if (topline < 1) {
- topline = 1;
- }
+ curwin->w_scbind_pos += vtopline_diff;
+ int curr_vtopline = get_vtopline(curwin);
- y = topline - curwin->w_topline;
+ // Perf: reuse curr_vtopline to reduce the time in plines_m_win_fill().
+ // Equivalent to:
+ // int max_vtopline = plines_m_win_fill(curwin, 1, curbuf->b_ml.ml_line_count);
+ int max_vtopline = curr_vtopline + curwin->w_topfill
+ + plines_m_win_fill(curwin, curwin->w_topline + 1,
+ curbuf->b_ml.ml_line_count);
+
+ int new_vtopline = MAX(MIN((linenr_T)curwin->w_scbind_pos, max_vtopline), 1);
+
+ int y = new_vtopline - curr_vtopline;
if (y > 0) {
scrollup(curwin, y, false);
} else {
@@ -2515,9 +2521,7 @@ bool nv_screengo(oparg_T *oap, int dir, int dist)
} else {
n = width1;
}
- if (curwin->w_curswant >= n) {
- curwin->w_curswant = n - 1;
- }
+ curwin->w_curswant = MIN(curwin->w_curswant, n - 1);
}
while (dist--) {
@@ -2776,11 +2780,7 @@ static void nv_zet(cmdarg_T *cap)
if (cap->count0 == 0) {
// No count given: put cursor at the line below screen
validate_botline(curwin); // make sure w_botline is valid
- if (curwin->w_botline > curbuf->b_ml.ml_line_count) {
- curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
- } else {
- curwin->w_cursor.lnum = curwin->w_botline;
- }
+ curwin->w_cursor.lnum = MIN(curwin->w_botline, curbuf->b_ml.ml_line_count);
}
FALLTHROUGH;
case NL:
@@ -3049,9 +3049,7 @@ static void nv_zet(cmdarg_T *cap)
case 'm':
if (curwin->w_p_fdl > 0) {
curwin->w_p_fdl -= cap->count1;
- if (curwin->w_p_fdl < 0) {
- curwin->w_p_fdl = 0;
- }
+ curwin->w_p_fdl = MAX(curwin->w_p_fdl, 0);
}
old_fdl = -1; // force an update
curwin->w_p_fen = true;
@@ -3069,9 +3067,7 @@ static void nv_zet(cmdarg_T *cap)
curwin->w_p_fdl += cap->count1;
{
int d = getDeepestNesting(curwin);
- if (curwin->w_p_fdl >= d) {
- curwin->w_p_fdl = d;
- }
+ curwin->w_p_fdl = MIN(curwin->w_p_fdl, d);
}
break;
@@ -3662,10 +3658,7 @@ static void nv_scroll(cmdarg_T *cap)
n = lnum - curwin->w_topline;
}
}
- 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;
- }
+ curwin->w_cursor.lnum = MIN(curwin->w_topline + n, curbuf->b_ml.ml_line_count);
}
// Correct for 'so', except when an operator is pending.
@@ -3972,6 +3965,12 @@ static void nv_next(cmdarg_T *cap)
normal_search(cap, 0, NULL, 0, SEARCH_MARK | cap->arg, NULL);
cap->count1 -= 1;
}
+
+ // Redraw the window to refresh the highlighted matches.
+ if (i > 0 && p_hls && !no_hlsearch
+ && win_hl_attr(curwin, HLF_LC) != win_hl_attr(curwin, HLF_L)) {
+ redraw_later(curwin, UPD_SOME_VALID);
+ }
}
/// Search for "pat" in direction "dir" ('/' or '?', 0 for repeat).
@@ -3983,6 +3982,7 @@ static void nv_next(cmdarg_T *cap)
static int normal_search(cmdarg_T *cap, int dir, char *pat, size_t patlen, int opt, int *wrapped)
{
searchit_arg_T sia;
+ pos_T const prev_cursor = curwin->w_cursor;
cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
@@ -4006,6 +4006,11 @@ static int normal_search(cmdarg_T *cap, int dir, char *pat, size_t patlen, int o
foldOpenCursor();
}
}
+ // Redraw the window to refresh the highlighted matches.
+ if (!equalpos(curwin->w_cursor, prev_cursor) && p_hls && !no_hlsearch
+ && win_hl_attr(curwin, HLF_LC) != win_hl_attr(curwin, HLF_L)) {
+ redraw_later(curwin, UPD_SOME_VALID);
+ }
// "/$" will put the cursor after the end of the line, may need to
// correct that here
@@ -4332,12 +4337,8 @@ static void nv_percent(cmdarg_T *cap)
curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count *
cap->count0 + 99) / 100;
}
- if (curwin->w_cursor.lnum < 1) {
- curwin->w_cursor.lnum = 1;
- }
- if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
- curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
- }
+ curwin->w_cursor.lnum = MIN(MAX(curwin->w_cursor.lnum, 1), curbuf->b_ml.ml_line_count);
+
beginline(BL_SOL | BL_FIX);
}
} else { // "%" : go to matching paren
@@ -5634,6 +5635,7 @@ static void nv_g_cmd(cmdarg_T *cap)
// "go": goto byte count from start of buffer
case 'o':
+ oap->inclusive = false;
goto_byte(cap->count0);
break;
@@ -6076,11 +6078,7 @@ static void nv_goto(cmdarg_T *cap)
if (cap->count0 != 0) {
lnum = cap->count0;
}
- if (lnum < 1) {
- lnum = 1;
- } else if (lnum > curbuf->b_ml.ml_line_count) {
- lnum = curbuf->b_ml.ml_line_count;
- }
+ lnum = MIN(MAX(lnum, 1), curbuf->b_ml.ml_line_count);
curwin->w_cursor.lnum = lnum;
beginline(BL_SOL | BL_FIX);
if ((fdo_flags & FDO_JUMP) && KeyTyped && cap->oap->op_type == OP_NOP) {
@@ -6410,9 +6408,8 @@ static void nv_join(cmdarg_T *cap)
return;
}
- if (cap->count0 <= 1) {
- cap->count0 = 2; // default for join is two lines!
- }
+ cap->count0 = MAX(cap->count0, 2); // default for join is two lines!
+
if (curwin->w_cursor.lnum + cap->count0 - 1 >
curbuf->b_ml.ml_line_count) {
// can't join when on the last line
@@ -6597,15 +6594,21 @@ static void nv_open(cmdarg_T *cap)
}
}
+/// Handles K_PASTE_START, repeats pasted text.
+static void nv_paste(cmdarg_T *cap)
+{
+ paste_repeat(cap->count1);
+}
+
/// Handle an arbitrary event in normal mode
static void nv_event(cmdarg_T *cap)
{
// Garbage collection should have been executed before blocking for events in
- // the `os_inchar` in `state_enter`, but we also disable it here in case the
- // `os_inchar` branch was not executed (!multiqueue_empty(loop.events), which
+ // the `input_get` in `state_enter`, but we also disable it here in case the
+ // `input_get` branch was not executed (!multiqueue_empty(loop.events), which
// could have `may_garbage_collect` set to true in `normal_check`).
//
- // That is because here we may run code that calls `os_inchar`
+ // That is because here we may run code that calls `input_get`
// later(`f_confirm` or `get_keystroke` for example), but in these cases it is
// not safe to perform garbage collection because there could be unreferenced
// lists or dicts being used.