aboutsummaryrefslogtreecommitdiff
path: root/src/nvim
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim')
-rw-r--r--src/nvim/buffer_defs.h10
-rw-r--r--src/nvim/cursor.c27
-rw-r--r--src/nvim/drawline.c28
-rw-r--r--src/nvim/drawscreen.c20
-rw-r--r--src/nvim/edit.c7
-rw-r--r--src/nvim/eval/userfunc.c20
-rw-r--r--src/nvim/ex_cmds.c6
-rw-r--r--src/nvim/ex_getln.c2
-rw-r--r--src/nvim/generators/gen_vimvim.lua11
-rw-r--r--src/nvim/grid.c27
-rw-r--r--src/nvim/mouse.c37
-rw-r--r--src/nvim/move.c725
-rw-r--r--src/nvim/normal.c37
-rw-r--r--src/nvim/ops.c2
-rw-r--r--src/nvim/option.c16
-rw-r--r--src/nvim/option_defs.h1
-rw-r--r--src/nvim/options.lua9
-rw-r--r--src/nvim/plines.c22
-rw-r--r--src/nvim/popupmenu.c157
-rw-r--r--src/nvim/spell.c21
20 files changed, 887 insertions, 298 deletions
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 02226f3cc4..ce8ee21882 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -192,6 +192,8 @@ typedef struct {
#define w_p_rlc w_onebuf_opt.wo_rlc // 'rightleftcmd'
long wo_scr;
#define w_p_scr w_onebuf_opt.wo_scr // 'scroll'
+ int wo_sms;
+#define w_p_sms w_onebuf_opt.wo_sms // 'smoothscroll'
int wo_spell;
#define w_p_spell w_onebuf_opt.wo_spell // 'spell'
int wo_cuc;
@@ -1163,11 +1165,12 @@ struct window_S {
bool w_botfill; // true when filler lines are actually
// below w_topline (at end of file)
bool w_old_botfill; // w_botfill at last redraw
- colnr_T w_leftcol; // window column number of the left most
+ colnr_T w_leftcol; // screen column number of the left most
// character in the window; used when
// 'wrap' is off
- colnr_T w_skipcol; // starting column when a single line
- // doesn't fit in the window
+ colnr_T w_skipcol; // starting screen column for the first
+ // line in the window; used when 'wrap' is
+ // on; does not include win_col_off()
// six fields that are only used when there is a WinScrolled autocommand
linenr_T w_last_topline; ///< last known value for w_topline
@@ -1220,6 +1223,7 @@ struct window_S {
int w_valid;
pos_T w_valid_cursor; // last known position of w_cursor, used to adjust w_valid
colnr_T w_valid_leftcol; // last known w_leftcol
+ colnr_T w_valid_skipcol; // last known w_skipcol
bool w_viewport_invalid;
linenr_T w_viewport_last_topline; // topline when the viewport was last updated
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
index 6d90b32545..8ba0b2ffb3 100644
--- a/src/nvim/cursor.c
+++ b/src/nvim/cursor.c
@@ -115,7 +115,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
col = wcol;
if ((addspaces || finetune) && !VIsual_active) {
- curwin->w_curswant = linetabsize(line) + one_more;
+ curwin->w_curswant = linetabsize_str(line) + one_more;
if (curwin->w_curswant > 0) {
curwin->w_curswant--;
}
@@ -129,7 +129,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
&& curwin->w_width_inner != 0
&& wcol >= (colnr_T)width
&& width > 0) {
- csize = linetabsize(line);
+ csize = linetabsize_str(line);
if (csize > 0) {
csize--;
}
@@ -439,23 +439,27 @@ void adjust_cursor_col(void)
}
}
-/// When curwin->w_leftcol has changed, adjust the cursor position.
+/// Set "curwin->w_leftcol" to "leftcol".
+/// Adjust the cursor position if needed.
///
/// @return true if the cursor was moved.
-bool leftcol_changed(void)
+bool set_leftcol(colnr_T leftcol)
{
- // TODO(hinidu): I think it should be colnr_T or int, but p_siso is long.
- // Perhaps we can change p_siso to int.
- int64_t lastcol;
- colnr_T s, e;
- bool retval = false;
+ // Return quickly when there is no change.
+ if (curwin->w_leftcol == leftcol) {
+ return false;
+ }
+ curwin->w_leftcol = leftcol;
changed_cline_bef_curs();
- lastcol = curwin->w_leftcol + curwin->w_width_inner - curwin_col_off() - 1;
+ // TODO(hinidu): I think it should be colnr_T or int, but p_siso is long.
+ // Perhaps we can change p_siso to int.
+ int64_t lastcol = curwin->w_leftcol + curwin->w_width_inner - curwin_col_off() - 1;
validate_virtcol();
+ bool retval = false;
// If the cursor is right or left of the screen, move it to last or first
- // character.
+ // visible character.
long siso = get_sidescrolloff_value(curwin);
if (curwin->w_virtcol > (colnr_T)(lastcol - siso)) {
retval = true;
@@ -468,6 +472,7 @@ bool leftcol_changed(void)
// If the start of the character under the cursor is not on the screen,
// advance the cursor one more char. If this fails (last char of the
// line) adjust the scrolling.
+ colnr_T s, e;
getvvcol(curwin, &curwin->w_cursor, &s, NULL, &e);
if (e > (colnr_T)lastcol) {
retval = true;
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 9bdf6a8255..e6cdf3d60d 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -168,7 +168,7 @@ static void margin_columns_win(win_T *wp, int *left_col, int *right_col)
return;
}
- width1 = wp->w_width - cur_col_off;
+ width1 = wp->w_width_inner - cur_col_off;
width2 = width1 + win_col_off2(wp);
*left_col = 0;
@@ -595,19 +595,23 @@ static int get_line_number_attr(win_T *wp, winlinevars_T *wlv)
static void handle_lnum_col(win_T *wp, winlinevars_T *wlv, int num_signs, int sign_idx,
int sign_num_attr, int sign_cul_attr)
{
+ bool has_cpo_n = vim_strchr(p_cpo, CPO_NUMCOL) != NULL;
+
if ((wp->w_p_nu || wp->w_p_rnu)
- && (wlv->row == wlv->startrow + wlv->filler_lines
- || vim_strchr(p_cpo, CPO_NUMCOL) == NULL)) {
- // If 'signcolumn' is set to 'number' and a sign is present
- // in "lnum", then display the sign instead of the line
- // number.
+ && (wlv->row == wlv->startrow + wlv->filler_lines || !has_cpo_n)
+ // there is no line number in a wrapped line when "n" is in
+ // 'cpoptions', but 'breakindent' assumes it anyway.
+ && !((has_cpo_n && !wp->w_p_bri) && wp->w_skipcol > 0 && wlv->lnum == wp->w_topline)) {
+ // If 'signcolumn' is set to 'number' and a sign is present in "lnum",
+ // then display the sign instead of the line number.
if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u' && num_signs > 0) {
get_sign_display_info(true, wp, wlv, sign_idx, sign_cul_attr);
} else {
// Draw the line number (empty space after wrapping).
- if (wlv->row == wlv->startrow + wlv->filler_lines) {
+ if (wlv->row == wlv->startrow + wlv->filler_lines
+ && (wp->w_skipcol == 0 || wlv->row > 0 || (wp->w_p_nu && wp->w_p_rnu))) {
get_line_number_str(wp, wlv->lnum, wlv->extra, sizeof(wlv->extra));
- if (wp->w_skipcol > 0) {
+ if (wp->w_skipcol > 0 && wlv->startrow == 0) {
for (wlv->p_extra = wlv->extra; *wlv->p_extra == ' '; wlv->p_extra++) {
*wlv->p_extra = '-';
}
@@ -754,7 +758,7 @@ static void handle_breakindent(win_T *wp, winlinevars_T *wlv)
wlv->n_extra = 0;
}
}
- if (wp->w_skipcol > 0 && wp->w_p_wrap && wp->w_briopt_sbr) {
+ if (wp->w_skipcol > 0 && wlv->startrow == 0 && wp->w_p_wrap && wp->w_briopt_sbr) {
wlv->need_showbreak = false;
}
// Correct end of highlighted area for 'breakindent',
@@ -804,7 +808,7 @@ static void handle_showbreak_and_filler(win_T *wp, winlinevars_T *wlv)
wlv->c_final = NUL;
wlv->n_extra = (int)strlen(sbr);
wlv->char_attr = win_hl_attr(wp, HLF_AT);
- if (wp->w_skipcol == 0 || !wp->w_p_wrap) {
+ if (wp->w_skipcol == 0 || wlv->startrow != 0 || !wp->w_p_wrap) {
wlv->need_showbreak = false;
}
wlv->vcol_sbr = wlv->vcol + mb_charlen(sbr);
@@ -1379,7 +1383,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
// 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the
// first character to be displayed.
if (wp->w_p_wrap) {
- v = wp->w_skipcol;
+ v = startrow == 0 ? wp->w_skipcol : 0;
} else {
v = wp->w_leftcol;
}
@@ -2595,7 +2599,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
if (c == NUL) {
// Highlight 'cursorcolumn' & 'colorcolumn' past end of the line.
if (wp->w_p_wrap) {
- v = wp->w_skipcol;
+ v = wlv.startrow == 0 ? wp->w_skipcol : 0;
} else {
v = wp->w_leftcol;
}
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index b5e516005b..ec5163f37a 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -1447,6 +1447,26 @@ static void win_update(win_T *wp, DecorProviders *providers)
init_search_hl(wp, &screen_search_hl);
+ // Make sure skipcol is valid, it depends on various options and the window
+ // width.
+ if (wp->w_skipcol > 0) {
+ int w = 0;
+ int width1 = wp->w_width_inner - win_col_off(wp);
+ int width2 = width1 + win_col_off2(wp);
+ int add = width1;
+
+ while (w < wp->w_skipcol) {
+ if (w > 0) {
+ add = width2;
+ }
+ w += add;
+ }
+ if (w != wp->w_skipcol) {
+ // always round down, the higher value may not be valid
+ wp->w_skipcol = w - add;
+ }
+ }
+
// Force redraw when width of 'number' or 'relativenumber' column
// changes.
int nrwidth = (wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc) ? number_width(wp) : 0;
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 0fb1102f4f..2078fc4251 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -193,7 +193,7 @@ static void insert_enter(InsertState *s)
}
}
- Insstart_textlen = (colnr_T)linetabsize(get_cursor_line_ptr());
+ Insstart_textlen = linetabsize_str(get_cursor_line_ptr());
Insstart_blank_vcol = MAXCOL;
if (!did_ai) {
@@ -2251,7 +2251,7 @@ int stop_arrow(void)
// right, except when nothing was inserted yet.
update_Insstart_orig = false;
}
- Insstart_textlen = (colnr_T)linetabsize(get_cursor_line_ptr());
+ Insstart_textlen = linetabsize_str(get_cursor_line_ptr());
if (u_save_cursor() == OK) {
arrow_used = false;
@@ -2449,6 +2449,7 @@ void beginline(int flags)
}
curwin->w_set_curswant = true;
}
+ adjust_skipcol();
}
// oneright oneleft cursor_down cursor_up
@@ -2490,6 +2491,7 @@ int oneright(void)
curwin->w_cursor.col += l;
curwin->w_set_curswant = true;
+ adjust_skipcol();
return OK;
}
@@ -2538,6 +2540,7 @@ int oneleft(void)
// if the character on the left of the current cursor is a multi-byte
// character, move to its first byte
mb_adjust_cursor();
+ adjust_skipcol();
return OK;
}
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 1f5a6eaec4..bfda2c4b9b 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -2587,19 +2587,13 @@ void ex_function(exarg_T *eap)
// Check for ":let v =<< [trim] EOF"
// and ":let [a, b] =<< [trim] EOF"
- arg = skipwhite(skiptowhite(p));
- if (*arg == '[') {
- arg = vim_strchr(arg, ']');
- }
- if (arg != NULL) {
- arg = skipwhite(skiptowhite(arg));
- if (arg[0] == '='
- && arg[1] == '<'
- && arg[2] == '<'
- && (p[0] == 'l'
- && p[1] == 'e'
- && (!ASCII_ISALNUM(p[2])
- || (p[2] == 't' && !ASCII_ISALNUM(p[3]))))) {
+ arg = p;
+ if (checkforcmd(&arg, "let", 2)) {
+ while (vim_strchr("$@&", *arg) != NULL) {
+ arg++;
+ }
+ arg = skipwhite(find_name_end(arg, NULL, NULL, FNE_INCL_BR));
+ if (arg[0] == '=' && arg[1] == '<' && arg[2] == '<') {
p = skipwhite(arg + 3);
while (true) {
if (strncmp(p, "trim", 4) == 0) {
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index f276e8ae24..2c31f742c3 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -346,10 +346,8 @@ static int linelen(int *has_tab)
last > first && ascii_iswhite(last[-1]); last--) {}
char save = *last;
*last = NUL;
- // Get line length.
- len = linetabsize(line);
- // Check for embedded TAB.
- if (has_tab != NULL) {
+ len = linetabsize_str(line); // Get line length.
+ if (has_tab != NULL) { // Check for embedded TAB.
*has_tab = vim_strchr(first, TAB) != NULL;
}
*last = save;
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 5018c9268b..af2ec3356f 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -504,6 +504,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat
}
validate_cursor();
+
// May redraw the status line to show the cursor position.
if (p_ru && (curwin->w_status_height > 0 || global_stl_height() > 0)) {
curwin->w_redr_status = true;
@@ -598,6 +599,7 @@ static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool
magic_overruled = s->magic_overruled_save;
validate_cursor(); // needed for TAB
+ status_redraw_all();
redraw_all_later(UPD_SOME_VALID);
if (call_update_screen) {
update_screen();
diff --git a/src/nvim/generators/gen_vimvim.lua b/src/nvim/generators/gen_vimvim.lua
index 09a7cab0c6..29355d3cda 100644
--- a/src/nvim/generators/gen_vimvim.lua
+++ b/src/nvim/generators/gen_vimvim.lua
@@ -112,13 +112,12 @@ for _, au in ipairs(auevents.events) do
w(' ' .. au)
end
end
-for au, _ in pairs(auevents.aliases) do
- if not auevents.nvim_specific[au] then
- if lld.line_length > 850 then
- w('\n' .. vimau_start)
- end
- w(' ' .. au)
+for _, au in pairs(auevents.aliases) do
+ if lld.line_length > 850 then
+ w('\n' .. vimau_start)
end
+ -- au[1] is aliased to au[2]
+ w(' ' .. au[1])
end
local nvimau_start = 'syn keyword nvimAutoEvent contained '
diff --git a/src/nvim/grid.c b/src/nvim/grid.c
index 7745daf69a..037606c38f 100644
--- a/src/nvim/grid.c
+++ b/src/nvim/grid.c
@@ -22,7 +22,7 @@
#include "nvim/highlight.h"
#include "nvim/log.h"
#include "nvim/message.h"
-#include "nvim/option_defs.h"
+#include "nvim/option.h"
#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -503,6 +503,7 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle
int col = 0;
bool redraw_next; // redraw_this for next character
bool clear_next = false;
+ bool topline = row == 0;
int char_cells; // 1: normal char
// 2: occupies two display cells
int start_dirty = -1, end_dirty = 0;
@@ -529,6 +530,30 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle
max_off_from = linebuf_size;
max_off_to = grid->line_offset[row] + (size_t)grid->cols;
+ // Take care of putting "<<<" on the first line for 'smoothscroll'.
+ if (topline && wp->w_skipcol > 0
+ // do not overwrite the 'showbreak' text with "<<<"
+ && *get_showbreak_value(wp) == NUL
+ // do not overwrite the 'listchars' "precedes" text with "<<<"
+ && !(wp->w_p_list && wp->w_p_lcs_chars.prec != 0)) {
+ int off = 0;
+ int skip = 0;
+ if (wp->w_p_nu && wp->w_p_rnu) {
+ // do not overwrite the line number, change "123 text" to
+ // "123>>>xt".
+ while (skip < wp->w_width_inner && ascii_isdigit(*linebuf_char[off])) {
+ off++;
+ skip++;
+ }
+ }
+
+ for (int i = 0; i < 3 && i + skip < wp->w_width_inner; i++) {
+ schar_from_ascii(linebuf_char[off], '<');
+ linebuf_attr[off] = HL_ATTR(HLF_AT);
+ off++;
+ }
+ }
+
if (rlflag) {
// Clear rest first, because it's left of the text.
if (clear_width > 0) {
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index e03eec184f..8189fde83c 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -223,7 +223,7 @@ static int get_fpos_of_mouse(pos_T *mpos)
}
// winpos and height may change in win_enter()!
- if (winrow + wp->w_winbar_height >= wp->w_height) { // In (or below) status line
+ if (winrow >= wp->w_height_inner) { // In (or below) status line
return IN_STATUS_LINE;
}
@@ -231,7 +231,7 @@ static int get_fpos_of_mouse(pos_T *mpos)
return MOUSE_WINBAR;
}
- if (wincol >= wp->w_width) { // In vertical separator line
+ if (wincol >= wp->w_width_inner) { // In vertical separator line
return IN_SEP_LINE;
}
@@ -557,10 +557,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
if (VIsual_active) {
// set MOUSE_MAY_STOP_VIS if we are outside the selection
// or the current window (might have false negative here)
- if (mouse_row < curwin->w_winrow
- || mouse_row > (curwin->w_winrow + curwin->w_height)) {
- jump_flags = MOUSE_MAY_STOP_VIS;
- } else if (m_pos_flag != IN_BUFFER) {
+ if (m_pos_flag != IN_BUFFER) {
jump_flags = MOUSE_MAY_STOP_VIS;
} else {
if (VIsual_mode == 'V') {
@@ -1413,9 +1410,22 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
} else {
row -= win_get_fill(win, lnum);
}
- count = plines_win_nofill(win, lnum, true);
+ count = plines_win_nofill(win, lnum, false);
} else {
- count = plines_win(win, lnum, true);
+ count = plines_win(win, lnum, false);
+ }
+
+ if (win->w_skipcol > 0 && lnum == win->w_topline) {
+ // Adjust for 'smoothscroll' clipping the top screen lines.
+ // A similar formula is used in curs_columns().
+ int width1 = win->w_width_inner - win_col_off(win);
+ int skip_lines = 0;
+ if (win->w_skipcol > width1) {
+ skip_lines = (win->w_skipcol - width1) / (width1 + win_col_off2(win)) + 1;
+ } else if (win->w_skipcol > 0) {
+ skip_lines = 1;
+ }
+ count -= skip_lines;
}
if (count > row) {
@@ -1439,8 +1449,11 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
col = off;
}
col += row * (win->w_width_inner - off);
- // add skip column (for long wrapping line)
- col += win->w_skipcol;
+
+ // Add skip column for the topline.
+ if (lnum == win->w_topline) {
+ col += win->w_skipcol;
+ }
}
if (!win->w_p_wrap) {
@@ -1651,8 +1664,6 @@ bool mouse_scroll_horiz(int dir)
return false;
}
- curwin->w_leftcol = (colnr_T)leftcol;
-
// When the line of the cursor is too short, move the cursor to the
// longest visible line.
if (!virtual_active()
@@ -1661,7 +1672,7 @@ bool mouse_scroll_horiz(int dir)
curwin->w_cursor.col = 0;
}
- return leftcol_changed();
+ return set_leftcol(leftcol);
}
/// Adjusts the clicked column position when 'conceallevel' > 0
diff --git a/src/nvim/move.c b/src/nvim/move.c
index b749d07d15..447926ceb8 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -57,6 +57,43 @@ typedef struct {
# include "move.c.generated.h"
#endif
+/// Reduce "n" for the screen lines skipped with "wp->w_skipcol".
+static int adjust_plines_for_skipcol(win_T *wp, int n)
+{
+ if (wp->w_skipcol == 0) {
+ return n;
+ }
+
+ int off = 0;
+ int width = wp->w_width_inner - win_col_off(wp);
+ if (wp->w_skipcol >= width) {
+ off++;
+ int skip = wp->w_skipcol - width;
+ width -= win_col_off2(wp);
+ while (skip >= width) {
+ off++;
+ skip -= width;
+ }
+ }
+ wp->w_valid &= ~VALID_WROW;
+ return n - off;
+}
+
+/// Return how many lines "lnum" will take on the screen, taking into account
+/// whether it is the first line, whether w_skipcol is non-zero and limiting to
+/// the window height.
+static int plines_correct_topline(win_T *wp, linenr_T lnum, linenr_T *nextp, bool *foldedp)
+{
+ int n = plines_win_full(wp, lnum, nextp, foldedp, true, false);
+ if (lnum == wp->w_topline) {
+ n = adjust_plines_for_skipcol(wp, n);
+ }
+ if (n > wp->w_height_inner) {
+ return wp->w_height_inner;
+ }
+ return n;
+}
+
// Compute wp->w_botline for the current wp->w_topline. Can be called after
// wp->w_topline changed.
static void comp_botline(win_T *wp)
@@ -78,7 +115,7 @@ static void comp_botline(win_T *wp)
for (; lnum <= wp->w_buffer->b_ml.ml_line_count; lnum++) {
linenr_T last = lnum;
bool folded;
- int n = plines_win_full(wp, lnum, &last, &folded, true);
+ int n = plines_correct_topline(wp, lnum, &last, &folded);
if (lnum <= wp->w_cursor.lnum && last >= wp->w_cursor.lnum) {
wp->w_cline_row = done;
wp->w_cline_height = n;
@@ -127,6 +164,51 @@ static void redraw_for_cursorcolumn(win_T *wp)
}
}
+/// Calculates how much overlap the smoothscroll marker "<<<" overlaps with
+/// buffer text for curwin.
+/// Parameter "extra2" should be the padding on the 2nd line, not the first
+/// line.
+/// Returns the number of columns of overlap with buffer text, excluding the
+/// extra padding on the ledge.
+static int smoothscroll_marker_overlap(win_T *wp, int extra2)
+{
+ // We don't draw the <<< marker when in showbreak mode, thus no need to
+ // account for it. See grid_put_linebuf().
+ if (*get_showbreak_value(wp) != NUL) {
+ return 0;
+ }
+ return extra2 > 3 ? 0 : 3 - extra2;
+}
+
+/// Calculates the skipcol offset for window "wp" given how many
+/// physical lines we want to scroll down.
+static int skipcol_from_plines(win_T *wp, int plines_off)
+{
+ int width1 = wp->w_width_inner - win_col_off(wp);
+
+ int skipcol = 0;
+ if (plines_off > 0) {
+ skipcol += width1;
+ }
+ if (plines_off > 1) {
+ skipcol += (width1 + win_col_off2(wp)) * (plines_off - 1);
+ }
+ return skipcol;
+}
+
+/// Set wp->s_skipcol to zero and redraw later if needed.
+static void reset_skipcol(win_T *wp)
+{
+ if (wp->w_skipcol != 0) {
+ wp->w_skipcol = 0;
+
+ // Should use the least expensive way that displays all that changed.
+ // UPD_NOT_VALID is too expensive, UPD_REDRAW_TOP does not redraw
+ // enough when the top line gets another screen line.
+ redraw_later(wp, UPD_SOME_VALID);
+ }
+}
+
// Update curwin->w_topline to move the cursor onto the screen.
void update_topline(win_T *wp)
{
@@ -178,7 +260,7 @@ void update_topline(win_T *wp)
bool check_topline = false;
// If the cursor is above or near the top of the window, scroll the window
// to show the line the cursor is in, with 'scrolloff' context.
- if (wp->w_topline > 1) {
+ if (wp->w_topline > 1 || wp->w_skipcol > 0) {
// If the cursor is above topline, scrolling is always needed.
// If the cursor is far below topline and there is no folding,
// scrolling down is never needed.
@@ -186,6 +268,17 @@ void update_topline(win_T *wp)
check_topline = true;
} else if (check_top_offset()) {
check_topline = true;
+ } else if (wp->w_skipcol > 0 && wp->w_cursor.lnum == wp->w_topline) {
+ colnr_T vcol;
+
+ // Check that the cursor position is visible. Add columns for the
+ // smoothscroll marker "<<<" displayed in the top-left if needed.
+ getvvcol(wp, &wp->w_cursor, &vcol, NULL, NULL);
+ int smoothscroll_overlap = smoothscroll_marker_overlap(wp,
+ win_col_off(wp) - win_col_off2(wp));
+ if (wp->w_skipcol + smoothscroll_overlap > vcol) {
+ check_topline = true;
+ }
}
}
// Check if there are more filler lines than allowed.
@@ -314,12 +407,9 @@ void update_topline(win_T *wp)
if (wp->w_topline != old_topline
|| wp->w_topfill != old_topfill) {
dollar_vcol = -1;
- if (wp->w_skipcol != 0) {
- wp->w_skipcol = 0;
- redraw_later(wp, UPD_NOT_VALID);
- } else {
- redraw_later(wp, UPD_VALID);
- }
+ redraw_later(wp, UPD_VALID);
+ reset_skipcol(wp);
+
// May need to set w_skipcol when cursor in w_topline.
if (wp->w_cursor.lnum == wp->w_topline) {
validate_cursor();
@@ -392,7 +482,15 @@ void check_cursor_moved(win_T *wp)
|VALID_CHEIGHT|VALID_CROW|VALID_TOPLINE);
wp->w_valid_cursor = wp->w_cursor;
wp->w_valid_leftcol = wp->w_leftcol;
+ wp->w_valid_skipcol = wp->w_skipcol;
wp->w_viewport_invalid = true;
+ } else if (wp->w_skipcol != wp->w_valid_skipcol) {
+ wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
+ |VALID_CHEIGHT|VALID_CROW
+ |VALID_BOTLINE|VALID_BOTLINE_AP);
+ wp->w_valid_cursor = wp->w_cursor;
+ wp->w_valid_leftcol = wp->w_leftcol;
+ wp->w_valid_skipcol = wp->w_skipcol;
} else if (wp->w_cursor.col != wp->w_valid_cursor.col
|| wp->w_leftcol != wp->w_valid_leftcol
|| wp->w_cursor.coladd !=
@@ -564,7 +662,7 @@ static void curs_rows(win_T *wp)
} else {
linenr_T last = lnum;
bool folded;
- int n = plines_win_full(wp, lnum, &last, &folded, false);
+ int n = plines_correct_topline(wp, lnum, &last, &folded);
lnum = last + 1;
if (folded && lnum > wp->w_cursor.lnum) {
break;
@@ -581,7 +679,7 @@ static void curs_rows(win_T *wp)
&& (!wp->w_lines[i].wl_valid
|| wp->w_lines[i].wl_lnum != wp->w_cursor.lnum))) {
wp->w_cline_height = plines_win_full(wp, wp->w_cursor.lnum, NULL,
- &wp->w_cline_folded, true);
+ &wp->w_cline_folded, true, true);
} else if (i > wp->w_lines_valid) {
// a line that is too long to fit on the last screen line
wp->w_cline_height = 0;
@@ -628,7 +726,7 @@ void validate_cheight(void)
curwin->w_cline_height = plines_win_full(curwin, curwin->w_cursor.lnum,
NULL, &curwin->w_cline_folded,
- true);
+ true, true);
curwin->w_valid |= VALID_CHEIGHT;
}
@@ -679,8 +777,8 @@ int curwin_col_off(void)
}
// Return the difference in column offset for the second screen line of a
-// wrapped line. It's 8 if 'number' or 'relativenumber' is on and 'n' is in
-// 'cpoptions'.
+// wrapped line. It's positive if 'number' or 'relativenumber' is on and 'n'
+// is in 'cpoptions'.
int win_col_off2(win_T *wp)
{
if ((wp->w_p_nu || wp->w_p_rnu) && vim_strchr(p_cpo, CPO_NUMCOL) != NULL) {
@@ -694,19 +792,14 @@ int curwin_col_off2(void)
return win_col_off2(curwin);
}
-// Compute curwin->w_wcol and curwin->w_virtcol.
-// Also updates curwin->w_wrow and curwin->w_cline_row.
-// Also updates curwin->w_leftcol.
+// Compute wp->w_wcol and wp->w_virtcol.
+// Also updates wp->w_wrow and wp->w_cline_row.
+// Also updates wp->w_leftcol.
// @param may_scroll when true, may scroll horizontally
void curs_columns(win_T *wp, int may_scroll)
{
- int n;
- int width = 0;
colnr_T startcol;
colnr_T endcol;
- colnr_T prev_skipcol;
- long so = get_scrolloff_value(wp);
- long siso = get_sidescrolloff_value(wp);
// First make sure that w_topline is valid (after moving the cursor).
update_topline(wp);
@@ -736,8 +829,11 @@ void curs_columns(win_T *wp, int may_scroll)
// Now compute w_wrow, counting screen lines from w_cline_row.
wp->w_wrow = wp->w_cline_row;
- int textwidth = wp->w_width_inner - extra;
- if (textwidth <= 0) {
+ int n;
+ int width1 = wp->w_width_inner - extra; // text width for first screen line
+ int width2 = 0; // text width for second and later screen line
+ bool did_sub_skipcol = false;
+ if (width1 <= 0) {
// No room for text, put cursor in last char of window.
// If not wrapping, the last non-empty line.
wp->w_wcol = wp->w_width_inner - 1;
@@ -747,13 +843,28 @@ void curs_columns(win_T *wp, int may_scroll)
wp->w_wrow = wp->w_height_inner - 1 - wp->w_empty_rows;
}
} else if (wp->w_p_wrap && wp->w_width_inner != 0) {
- width = textwidth + win_col_off2(wp);
+ width2 = width1 + win_col_off2(wp);
+
+ // skip columns that are not visible
+ if (wp->w_cursor.lnum == wp->w_topline
+ && wp->w_skipcol > 0
+ && wp->w_wcol >= wp->w_skipcol) {
+ // Deduct by multiples of width2. This allows the long line wrapping
+ // formula below to correctly calculate the w_wcol value when wrapping.
+ if (wp->w_skipcol <= width1) {
+ wp->w_wcol -= width2;
+ } else {
+ wp->w_wcol -= width2 * (((wp->w_skipcol - width1) / width2) + 1);
+ }
+
+ did_sub_skipcol = true;
+ }
// long line wrapping, adjust wp->w_wrow
if (wp->w_wcol >= wp->w_width_inner) {
// this same formula is used in validate_cursor_col()
- n = (wp->w_wcol - wp->w_width_inner) / width + 1;
- wp->w_wcol -= n * width;
+ n = (wp->w_wcol - wp->w_width_inner) / width2 + 1;
+ wp->w_wcol -= n * width2;
wp->w_wrow += n;
// When cursor wraps to first char of next line in Insert
@@ -775,6 +886,7 @@ void curs_columns(win_T *wp, int may_scroll)
// If Cursor is right of the screen, scroll leftwards
// If we get closer to the edge than 'sidescrolloff', scroll a little
// extra
+ long siso = get_sidescrolloff_value(wp);
assert(siso <= INT_MAX);
int off_left = startcol - wp->w_leftcol - (int)siso;
int off_right =
@@ -785,8 +897,8 @@ void curs_columns(win_T *wp, int may_scroll)
// When far off or not enough room on either side, put cursor in
// middle of window.
int new_leftcol;
- if (p_ss == 0 || diff >= textwidth / 2 || off_right >= off_left) {
- new_leftcol = wp->w_wcol - extra - textwidth / 2;
+ if (p_ss == 0 || diff >= width1 / 2 || off_right >= off_left) {
+ new_leftcol = curwin->w_wcol - extra - width1 / 2;
} else {
if (diff < p_ss) {
assert(p_ss <= INT_MAX);
@@ -823,9 +935,9 @@ void curs_columns(win_T *wp, int may_scroll)
wp->w_wrow += win_get_fill(wp, wp->w_cursor.lnum);
}
- prev_skipcol = wp->w_skipcol;
-
int plines = 0;
+ long so = get_scrolloff_value(wp);
+ colnr_T prev_skipcol = wp->w_skipcol;
if ((wp->w_wrow >= wp->w_height_inner
|| ((prev_skipcol > 0
|| wp->w_wrow + so >= wp->w_height_inner)
@@ -833,7 +945,7 @@ void curs_columns(win_T *wp, int may_scroll)
>= wp->w_height_inner))
&& wp->w_height_inner != 0
&& wp->w_cursor.lnum == wp->w_topline
- && width > 0
+ && width2 > 0
&& wp->w_width_inner != 0) {
// Cursor past end of screen. Happens with a single line that does
// not fit on screen. Find a skipcol to show the text around the
@@ -842,7 +954,7 @@ void curs_columns(win_T *wp, int may_scroll)
// 2: Less than "p_so" lines below
// 3: both of them
extra = 0;
- if (wp->w_skipcol + so * width > wp->w_virtcol) {
+ if (wp->w_skipcol + so * width2 > wp->w_virtcol) {
extra = 1;
}
// Compute last display line of the buffer line that we want at the
@@ -857,13 +969,13 @@ void curs_columns(win_T *wp, int may_scroll)
} else {
n = plines;
}
- if ((colnr_T)n >= wp->w_height_inner + wp->w_skipcol / width - so) {
+ if ((colnr_T)n >= wp->w_height_inner + wp->w_skipcol / width2 - so) {
extra += 2;
}
- if (extra == 3 || plines <= so * 2) {
+ if (extra == 3 || wp->w_height_inner <= so * 2) {
// not enough room for 'scrolloff', put cursor in the middle
- n = wp->w_virtcol / width;
+ n = wp->w_virtcol / width2;
if (n > wp->w_height_inner / 2) {
n -= wp->w_height_inner / 2;
} else {
@@ -873,51 +985,58 @@ void curs_columns(win_T *wp, int may_scroll)
if (n > plines - wp->w_height_inner + 1) {
n = plines - wp->w_height_inner + 1;
}
- wp->w_skipcol = n * width;
+ wp->w_skipcol = n * width2;
} else if (extra == 1) {
// less than 'scrolloff' lines above, decrease skipcol
assert(so <= INT_MAX);
- extra = (wp->w_skipcol + (int)so * width - wp->w_virtcol
- + width - 1) / width;
+ extra = (wp->w_skipcol + (int)so * width2 - wp->w_virtcol + width2 - 1) / width2;
if (extra > 0) {
- if ((colnr_T)(extra * width) > wp->w_skipcol) {
- extra = wp->w_skipcol / width;
+ if ((colnr_T)(extra * width2) > wp->w_skipcol) {
+ extra = wp->w_skipcol / width2;
}
- wp->w_skipcol -= extra * width;
+ wp->w_skipcol -= extra * width2;
}
} else if (extra == 2) {
// less than 'scrolloff' lines below, increase skipcol
- endcol = (n - wp->w_height_inner + 1) * width;
+ endcol = (n - wp->w_height_inner + 1) * width2;
while (endcol > wp->w_virtcol) {
- endcol -= width;
+ endcol -= width2;
}
if (endcol > wp->w_skipcol) {
wp->w_skipcol = endcol;
}
}
- wp->w_wrow -= wp->w_skipcol / width;
+ // adjust w_wrow for the changed w_skipcol
+ if (did_sub_skipcol) {
+ wp->w_wrow -= (wp->w_skipcol - prev_skipcol) / width2;
+ } else {
+ wp->w_wrow -= wp->w_skipcol / width2;
+ }
+
if (wp->w_wrow >= wp->w_height_inner) {
// small window, make sure cursor is in it
extra = wp->w_wrow - wp->w_height_inner + 1;
- wp->w_skipcol += extra * width;
+ wp->w_skipcol += extra * width2;
wp->w_wrow -= extra;
}
// extra could be either positive or negative
- extra = ((int)prev_skipcol - (int)wp->w_skipcol) / width;
+ extra = (prev_skipcol - wp->w_skipcol) / width2;
win_scroll_lines(wp, 0, extra);
- } else {
+ } else if (!wp->w_p_sms) {
wp->w_skipcol = 0;
}
if (prev_skipcol != wp->w_skipcol) {
- redraw_later(wp, UPD_NOT_VALID);
+ redraw_later(wp, UPD_SOME_VALID);
}
- redraw_for_cursorcolumn(curwin);
+ redraw_for_cursorcolumn(wp);
- // now w_leftcol is valid, avoid check_cursor_moved() thinking otherwise
+ // now w_leftcol and w_skipcol are valid, avoid check_cursor_moved()
+ // thinking otherwise
wp->w_valid_leftcol = wp->w_leftcol;
+ wp->w_valid_skipcol = wp->w_skipcol;
wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL;
}
@@ -966,18 +1085,18 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp,
// similar to what is done in validate_cursor_col()
colnr_T col = scol;
col += off;
- int width = wp->w_width - off + win_col_off2(wp);
+ int width = wp->w_width_inner - off + win_col_off2(wp);
// long line wrapping, adjust row
- if (wp->w_p_wrap && col >= (colnr_T)wp->w_width && width > 0) {
+ if (wp->w_p_wrap && col >= (colnr_T)wp->w_width_inner && width > 0) {
// use same formula as what is used in curs_columns()
- rowoff = visible_row ? ((col - wp->w_width) / width + 1) : 0;
+ rowoff = visible_row ? ((col - wp->w_width_inner) / width + 1) : 0;
col -= rowoff * width;
}
col -= wp->w_leftcol;
- if (col >= 0 && col < wp->w_width && row + rowoff <= wp->w_height) {
+ if (col >= 0 && col < wp->w_width_inner && row + rowoff <= wp->w_height_inner) {
coloff = col - scol + (local ? 0 : wp->w_wincol + wp->w_wincol_off) + 1;
row += local ? 0 : wp->w_winrow + wp->w_winrow_off;
} else {
@@ -1064,32 +1183,69 @@ void f_virtcol2col(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
bool scrolldown(long line_count, int byfold)
{
int done = 0; // total # of physical lines done
+ int width1 = 0;
+ int width2 = 0;
+ bool do_sms = curwin->w_p_wrap && curwin->w_p_sms;
+
+ if (do_sms) {
+ width1 = curwin->w_width_inner - curwin_col_off();
+ width2 = width1 + curwin_col_off2();
+ }
// Make sure w_topline is at the first of a sequence of folded lines.
(void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
validate_cursor(); // w_wrow needs to be valid
- while (line_count-- > 0) {
+ for (long todo = line_count; todo > 0; todo--) {
if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)
&& curwin->w_topfill < curwin->w_height_inner - 1) {
curwin->w_topfill++;
done++;
} else {
- if (curwin->w_topline == 1) {
+ // break when at the very top
+ if (curwin->w_topline == 1 && (!do_sms || curwin->w_skipcol < width1)) {
break;
}
- curwin->w_topline--;
- curwin->w_topfill = 0;
- // A sequence of folded lines only counts for one logical line
- linenr_T first;
- if (hasFolding(curwin->w_topline, &first, NULL)) {
- done++;
- if (!byfold) {
- line_count -= curwin->w_topline - first - 1;
+ if (do_sms && curwin->w_skipcol >= width1) {
+ // scroll a screen line down
+ if (curwin->w_skipcol >= width1 + width2) {
+ curwin->w_skipcol -= width2;
+ } else {
+ curwin->w_skipcol -= width1;
}
- curwin->w_botline -= curwin->w_topline - first;
- curwin->w_topline = first;
+ redraw_later(curwin, UPD_NOT_VALID);
+ done++;
} else {
- done += plines_win_nofill(curwin, curwin->w_topline, true);
+ // scroll a text line down
+ curwin->w_topline--;
+ curwin->w_skipcol = 0;
+ curwin->w_topfill = 0;
+ // A sequence of folded lines only counts for one logical line
+ linenr_T first;
+ if (hasFolding(curwin->w_topline, &first, NULL)) {
+ done++;
+ if (!byfold) {
+ todo -= curwin->w_topline - first - 1;
+ }
+ curwin->w_botline -= curwin->w_topline - first;
+ curwin->w_topline = first;
+ } else {
+ if (do_sms) {
+ int size = (int)win_linetabsize(curwin, curwin->w_topline,
+ ml_get(curwin->w_topline), (colnr_T)MAXCOL);
+ if (size > width1) {
+ curwin->w_skipcol = width1;
+ size -= width1;
+ redraw_later(curwin, UPD_NOT_VALID);
+ }
+ while (size > width2) {
+ curwin->w_skipcol += width2;
+ size -= width2;
+ }
+ done++;
+ } else {
+ done += plines_win_nofill(curwin, curwin->w_topline, true);
+ }
+ }
}
}
curwin->w_botline--; // approximate w_botline
@@ -1134,9 +1290,39 @@ bool scrolldown(long line_count, int byfold)
foldAdjustCursor();
coladvance(curwin->w_curswant);
}
+
+ if (curwin->w_cursor.lnum == curwin->w_topline && do_sms) {
+ long so = get_scrolloff_value(curwin);
+ long scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2;
+
+ // make sure the cursor is in the visible text
+ validate_virtcol();
+ long col = curwin->w_virtcol - curwin->w_skipcol + scrolloff_cols;
+ int row = 0;
+ if (col >= width1) {
+ col -= width1;
+ row++;
+ }
+ if (col > width2 && width2 > 0) {
+ row += (int)col / width2;
+ col = col % width2;
+ }
+ if (row >= curwin->w_height_inner) {
+ curwin->w_curswant = curwin->w_virtcol - (row - curwin->w_height_inner + 1) * width2;
+ coladvance(curwin->w_curswant);
+ }
+ }
return moved;
}
+/// Return TRUE if scrollup() will scroll by screen line rather than text line.
+static int scrolling_screenlines(bool byfold)
+{
+ return (curwin->w_p_wrap && curwin->w_p_sms)
+ || (byfold && hasAnyFolding(curwin))
+ || (curwin->w_p_diff && !curwin->w_p_wrap);
+}
+
/// Scroll the current window up by "line_count" logical lines. "CTRL-E"
///
/// @param line_count number of lines to scroll
@@ -1145,28 +1331,68 @@ bool scrollup(long line_count, int byfold)
{
linenr_T topline = curwin->w_topline;
linenr_T botline = curwin->w_botline;
+ int do_sms = curwin->w_p_wrap && curwin->w_p_sms;
+
+ if (scrolling_screenlines(byfold) || win_may_fill(curwin)) {
+ int width1 = curwin->w_width_inner - curwin_col_off();
+ int width2 = width1 + curwin_col_off2();
+ unsigned size = 0;
+ linenr_T prev_topline = curwin->w_topline;
+
+ if (do_sms) {
+ size = linetabsize(curwin, curwin->w_topline);
+ }
- if ((byfold && hasAnyFolding(curwin))
- || win_may_fill(curwin)) {
- // count each sequence of folded lines as one logical line
- linenr_T lnum = curwin->w_topline;
- while (line_count--) {
+ // diff mode: first consume "topfill"
+ // 'smoothscroll': increase "w_skipcol" until it goes over the end of
+ // the line, then advance to the next line.
+ // folding: count each sequence of folded lines as one logical line.
+ for (long todo = line_count; todo > 0; todo--) {
if (curwin->w_topfill > 0) {
curwin->w_topfill--;
} else {
+ linenr_T lnum = curwin->w_topline;
if (byfold) {
+ // for a closed fold: go to the last line in the fold
(void)hasFolding(lnum, NULL, &lnum);
}
- if (lnum >= curbuf->b_ml.ml_line_count) {
- break;
+ if (lnum == curwin->w_topline && do_sms) {
+ // 'smoothscroll': increase "w_skipcol" until it goes over
+ // the end of the line, then advance to the next line.
+ int add = curwin->w_skipcol > 0 ? width2 : width1;
+ curwin->w_skipcol += add;
+ if ((unsigned)curwin->w_skipcol >= size) {
+ if (lnum == curbuf->b_ml.ml_line_count) {
+ // at the last screen line, can't scroll further
+ curwin->w_skipcol -= add;
+ break;
+ }
+ lnum++;
+ }
+ } else {
+ if (lnum >= curbuf->b_ml.ml_line_count) {
+ break;
+ }
+ lnum++;
+ }
+
+ if (lnum > curwin->w_topline) {
+ // approximate w_botline
+ curwin->w_botline += lnum - curwin->w_topline;
+ curwin->w_topline = lnum;
+ curwin->w_topfill = win_get_fill(curwin, lnum);
+ curwin->w_skipcol = 0;
+ if (todo > 1 && do_sms) {
+ size = linetabsize(curwin, curwin->w_topline);
+ }
}
- lnum++;
- curwin->w_topfill = win_get_fill(curwin, lnum);
}
}
- // approximate w_botline
- curwin->w_botline += lnum - curwin->w_topline;
- curwin->w_topline = lnum;
+
+ if (curwin->w_topline == prev_topline) {
+ // need to redraw even though w_topline didn't change
+ redraw_later(curwin, UPD_NOT_VALID);
+ }
} else {
curwin->w_topline += (linenr_T)line_count;
curwin->w_botline += (linenr_T)line_count; // approximate w_botline
@@ -1194,12 +1420,117 @@ bool scrollup(long line_count, int byfold)
coladvance(curwin->w_curswant);
}
- bool moved = topline != curwin->w_topline
- || botline != curwin->w_botline;
+ if (curwin->w_cursor.lnum == curwin->w_topline && do_sms && curwin->w_skipcol > 0) {
+ int col_off = curwin_col_off();
+ int col_off2 = curwin_col_off2();
+
+ int width1 = curwin->w_width_inner - col_off;
+ int width2 = width1 + col_off2;
+ int extra2 = col_off - col_off2;
+ long so = get_scrolloff_value(curwin);
+ long scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2;
+ int space_cols = (curwin->w_height_inner - 1) * width2;
+
+ // If we have non-zero scrolloff, just ignore the <<< marker as we are
+ // going past it anyway.
+ int smoothscroll_overlap = scrolloff_cols != 0 ? 0 :
+ smoothscroll_marker_overlap(curwin, extra2);
+
+ // Make sure the cursor is in a visible part of the line, taking
+ // 'scrolloff' into account, but using screen lines.
+ // If there are not enough screen lines put the cursor in the middle.
+ if (scrolloff_cols > space_cols / 2) {
+ scrolloff_cols = space_cols / 2;
+ }
+ validate_virtcol();
+ if (curwin->w_virtcol < curwin->w_skipcol + smoothscroll_overlap + scrolloff_cols) {
+ colnr_T col = curwin->w_virtcol;
+
+ if (col < width1) {
+ col += width1;
+ }
+ while (col < curwin->w_skipcol + smoothscroll_overlap + scrolloff_cols) {
+ col += width2;
+ }
+ curwin->w_curswant = col;
+ coladvance(curwin->w_curswant);
+
+ // validate_virtcol() marked various things as valid, but after
+ // moving the cursor they need to be recomputed
+ curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL);
+ }
+ }
+
+ bool moved = topline != curwin->w_topline || botline != curwin->w_botline;
return moved;
}
+/// Called after changing the cursor column: make sure that curwin->w_skipcol is
+/// valid for 'smoothscroll'.
+void adjust_skipcol(void)
+{
+ if (!curwin->w_p_wrap || !curwin->w_p_sms || curwin->w_cursor.lnum != curwin->w_topline) {
+ return;
+ }
+
+ int width1 = curwin->w_width_inner - curwin_col_off();
+ if (width1 <= 0) {
+ return; // no text will be displayed
+ }
+ int width2 = width1 + curwin_col_off2();
+ long so = get_scrolloff_value(curwin);
+ long scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2;
+ bool scrolled = false;
+
+ validate_cheight();
+ if (curwin->w_cline_height == curwin->w_height_inner
+ // w_cline_height may be capped at w_height_inner, check there aren't
+ // actually more lines.
+ && plines_win(curwin, curwin->w_cursor.lnum, false) <= curwin->w_height_inner) {
+ // the line just fits in the window, don't scroll
+ reset_skipcol(curwin);
+ return;
+ }
+
+ validate_virtcol();
+ while (curwin->w_skipcol > 0
+ && curwin->w_virtcol < curwin->w_skipcol + 3 + scrolloff_cols) {
+ // scroll a screen line down
+ if (curwin->w_skipcol >= width1 + width2) {
+ curwin->w_skipcol -= width2;
+ } else {
+ curwin->w_skipcol -= width1;
+ }
+ redraw_later(curwin, UPD_NOT_VALID);
+ scrolled = true;
+ validate_virtcol();
+ }
+ if (scrolled) {
+ return; // don't scroll in the other direction now
+ }
+ long col = curwin->w_virtcol - curwin->w_skipcol + scrolloff_cols;
+ int row = 0;
+ if (col >= width1) {
+ col -= width1;
+ row++;
+ }
+ if (col > width2) {
+ row += (int)col / width2;
+ col = col % width2;
+ }
+ if (row >= curwin->w_height_inner) {
+ if (curwin->w_skipcol == 0) {
+ curwin->w_skipcol += width1;
+ row--;
+ }
+ if (row >= curwin->w_height_inner) {
+ curwin->w_skipcol += (row - curwin->w_height_inner) * width2;
+ }
+ redraw_later(curwin, UPD_NOT_VALID);
+ }
+}
+
/// Don't end up with too many filler lines in the window.
///
/// @param down when true scroll down when not enough space
@@ -1316,7 +1647,8 @@ void scrollup_clamp(void)
// a (wrapped) text line. Uses and sets "lp->fill".
// Returns the height of the added line in "lp->height".
// Lines above the first one are incredibly high: MAXCOL.
-static void topline_back(win_T *wp, lineoff_T *lp)
+// When "winheight" is true limit to window height.
+static void topline_back_winheight(win_T *wp, lineoff_T *lp, int winheight)
{
if (lp->fill < win_get_fill(wp, lp->lnum)) {
// Add a filler line
@@ -1331,11 +1663,16 @@ static void topline_back(win_T *wp, lineoff_T *lp)
// Add a closed fold
lp->height = 1;
} else {
- lp->height = plines_win_nofill(wp, lp->lnum, true);
+ lp->height = plines_win_nofill(wp, lp->lnum, winheight);
}
}
}
+static void topline_back(win_T *wp, lineoff_T *lp)
+{
+ topline_back_winheight(wp, lp, true);
+}
+
// Add one line below "lp->lnum". This can be a filler line, a closed fold or
// a (wrapped) text line. Uses and sets "lp->fill".
// Returns the height of the added line in "lp->height".
@@ -1388,12 +1725,9 @@ static void topline_botline(lineoff_T *lp)
// If "always" is true, always set topline (for "zt").
void scroll_cursor_top(int min_scroll, int always)
{
- int scrolled = 0;
- linenr_T top; // just above displayed lines
- linenr_T bot; // just below displayed lines
linenr_T old_topline = curwin->w_topline;
+ int old_skipcol = curwin->w_skipcol;
linenr_T old_topfill = curwin->w_topfill;
- linenr_T new_topline;
int off = (int)get_scrolloff_value(curwin);
if (mouse_dragging > 0) {
@@ -1406,11 +1740,14 @@ void scroll_cursor_top(int min_scroll, int always)
// - moved at least 'scrolljump' lines and
// - at least 'scrolloff' lines above and below the cursor
validate_cheight();
+ int scrolled = 0;
int used = curwin->w_cline_height; // includes filler lines above
if (curwin->w_cursor.lnum < curwin->w_topline) {
scrolled = used;
}
+ linenr_T top; // just above displayed lines
+ linenr_T bot; // just below displayed lines
if (hasFolding(curwin->w_cursor.lnum, &top, &bot)) {
top--;
bot++;
@@ -1418,7 +1755,7 @@ void scroll_cursor_top(int min_scroll, int always)
top = curwin->w_cursor.lnum - 1;
bot = curwin->w_cursor.lnum + 1;
}
- new_topline = top + 1;
+ linenr_T new_topline = top + 1;
// "used" already contains the number of filler lines above, don't add it
// again.
@@ -1431,6 +1768,15 @@ void scroll_cursor_top(int min_scroll, int always)
int i = hasFolding(top, &top, NULL)
? 1 // count one logical line for a sequence of folded lines
: plines_win_nofill(curwin, top, true);
+ if (top < curwin->w_topline) {
+ scrolled += i;
+ }
+
+ // If scrolling is needed, scroll at least 'sj' lines.
+ if ((new_topline >= curwin->w_topline || scrolled > min_scroll) && extra >= off) {
+ break;
+ }
+
used += i;
if (extra + i <= off && bot < curbuf->b_ml.ml_line_count) {
if (hasFolding(bot, NULL, &bot)) {
@@ -1443,15 +1789,6 @@ void scroll_cursor_top(int min_scroll, int always)
if (used > curwin->w_height_inner) {
break;
}
- if (top < curwin->w_topline) {
- scrolled += i;
- }
-
- // If scrolling is needed, scroll at least 'sj' lines.
- if ((new_topline >= curwin->w_topline || scrolled > min_scroll)
- && extra >= off) {
- break;
- }
extra += i;
new_topline = top;
@@ -1466,7 +1803,7 @@ void scroll_cursor_top(int min_scroll, int always)
scroll_cursor_halfway(false, false);
} else {
// If "always" is false, only adjust topline to a lower value, higher
- // value may happen with wrapping lines
+ // value may happen with wrapping lines.
if (new_topline < curwin->w_topline || always) {
curwin->w_topline = new_topline;
}
@@ -1481,7 +1818,13 @@ void scroll_cursor_top(int min_scroll, int always)
}
}
check_topfill(curwin, false);
+ // TODO(vim): if the line doesn't fit may optimize w_skipcol
+ if (curwin->w_topline == curwin->w_cursor.lnum
+ && curwin->w_skipcol >= curwin->w_cursor.col) {
+ reset_skipcol(curwin);
+ }
if (curwin->w_topline != old_topline
+ || curwin->w_skipcol != old_skipcol
|| curwin->w_topfill != old_topfill) {
curwin->w_valid &=
~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
@@ -1519,20 +1862,18 @@ void set_empty_rows(win_T *wp, int used)
void scroll_cursor_bot(int min_scroll, int set_topbot)
{
int used;
- int scrolled = 0;
- int extra = 0;
lineoff_T loff;
- lineoff_T boff;
- int fill_below_window;
- linenr_T old_topline = curwin->w_topline;
- int old_topfill = curwin->w_topfill;
- linenr_T old_botline = curwin->w_botline;
- int old_valid = curwin->w_valid;
+ linenr_T old_topline = curwin->w_topline;
+ int old_skipcol = curwin->w_skipcol;
+ int old_topfill = curwin->w_topfill;
+ linenr_T old_botline = curwin->w_botline;
+ int old_valid = curwin->w_valid;
int old_empty_rows = curwin->w_empty_rows;
- linenr_T cln = curwin->w_cursor.lnum; // Cursor Line Number
- long so = get_scrolloff_value(curwin);
+ linenr_T cln = curwin->w_cursor.lnum; // Cursor Line Number
if (set_topbot) {
+ bool set_skipcol = false;
+
used = 0;
curwin->w_botline = cln + 1;
loff.fill = 0;
@@ -1540,9 +1881,24 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
curwin->w_topline > 1;
curwin->w_topline = loff.lnum) {
loff.lnum = curwin->w_topline;
- topline_back(curwin, &loff);
- if (loff.height == MAXCOL
- || used + loff.height > curwin->w_height_inner) {
+ topline_back_winheight(curwin, &loff, false);
+ if (loff.height == MAXCOL) {
+ break;
+ }
+ if (used + loff.height > curwin->w_height_inner) {
+ if (curwin->w_p_sms && curwin->w_p_wrap) {
+ // 'smoothscroll' and 'wrap' are set. The above line is
+ // too long to show in its entirety, so we show just a part
+ // of it.
+ if (used < curwin->w_height_inner) {
+ int plines_offset = used + loff.height - curwin->w_height_inner;
+ used = curwin->w_height_inner;
+ curwin->w_topfill = loff.fill;
+ curwin->w_topline = loff.lnum;
+ curwin->w_skipcol = skipcol_from_plines(curwin, plines_offset);
+ set_skipcol = true;
+ }
+ }
break;
}
used += loff.height;
@@ -1551,8 +1907,15 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
set_empty_rows(curwin, used);
curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
if (curwin->w_topline != old_topline
- || curwin->w_topfill != old_topfill) {
+ || curwin->w_topfill != old_topfill
+ || set_skipcol
+ || curwin->w_skipcol != 0) {
curwin->w_valid &= ~(VALID_WROW|VALID_CROW);
+ if (set_skipcol) {
+ redraw_later(curwin, UPD_NOT_VALID);
+ } else {
+ reset_skipcol(curwin);
+ }
}
} else {
validate_botline(curwin);
@@ -1561,16 +1924,50 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
// The lines of the cursor line itself are always used.
used = plines_win_nofill(curwin, cln, true);
- // If the cursor is below botline, we will at least scroll by the height
- // of the cursor line. Correct for empty lines, which are really part of
- // botline.
+ int scrolled = 0;
+ int min_scrolled = 1;
+ // If the cursor is on or below botline, we will at least scroll by the
+ // height of the cursor line, which is "used". Correct for empty lines,
+ // which are really part of botline.
if (cln >= curwin->w_botline) {
scrolled = used;
if (cln == curwin->w_botline) {
scrolled -= curwin->w_empty_rows;
}
+ min_scrolled = scrolled;
+ if (curwin->w_p_sms && curwin->w_p_wrap) {
+ // 'smoothscroll' and 'wrap' are set
+ if (cln > curwin->w_botline) {
+ // add screen lines below w_botline
+ for (linenr_T lnum = curwin->w_botline + 1; lnum <= cln; lnum++) {
+ min_scrolled += plines_win_nofill(curwin, lnum, true);
+ }
+ }
+
+ // Calculate how many screen lines the current top line of window
+ // occupies. If it is occupying more than the entire window, we
+ // need to scroll the additional clipped lines to scroll past the
+ // top line before we can move on to the other lines.
+ int top_plines = plines_win_nofill(curwin, curwin->w_topline, false);
+ int skip_lines = 0;
+ int width1 = curwin->w_width_inner - curwin_col_off();
+ int width2 = width1 + curwin_col_off2();
+ // similar formula is used in curs_columns()
+ if (curwin->w_skipcol > width1) {
+ skip_lines += (curwin->w_skipcol - width1) / width2 + 1;
+ } else if (curwin->w_skipcol > 0) {
+ skip_lines = 1;
+ }
+
+ top_plines -= skip_lines;
+ if (top_plines > curwin->w_height_inner) {
+ scrolled += (top_plines - curwin->w_height_inner);
+ min_scrolled += (top_plines - curwin->w_height_inner);
+ }
+ }
}
+ lineoff_T boff;
// Stop counting lines to scroll when
// - hitting start of the file
// - scrolled nothing or at least 'sj' lines
@@ -1582,9 +1979,10 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
}
loff.fill = 0;
boff.fill = 0;
- fill_below_window = win_get_fill(curwin, curwin->w_botline)
- - curwin->w_filler_rows;
+ int fill_below_window = win_get_fill(curwin, curwin->w_botline) - curwin->w_filler_rows;
+ int extra = 0;
+ long so = get_scrolloff_value(curwin);
while (loff.lnum > 1) {
// Stop when scrolled nothing or at least "min_scroll", found "extra"
// context for 'scrolloff' and counted all lines below the window.
@@ -1670,13 +2068,27 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
if (line_count >= curwin->w_height_inner && line_count > min_scroll) {
scroll_cursor_halfway(false, true);
} else {
- scrollup(line_count, true);
+ // With 'smoothscroll' scroll at least the height of the cursor line,
+ // unless it would move the cursor.
+ if (curwin->w_p_wrap && curwin->w_p_sms && line_count < min_scrolled
+ && (curwin->w_cursor.lnum < curwin->w_topline
+ || (curwin->w_virtcol - curwin->w_skipcol >=
+ curwin->w_width_inner - curwin_col_off()))) {
+ line_count = min_scrolled;
+ }
+ if (line_count > 0) {
+ if (scrolling_screenlines(true)) {
+ scrollup(scrolled, true); // TODO(vim):
+ } else {
+ scrollup(line_count, true);
+ }
+ }
}
// If topline didn't change we need to restore w_botline and w_empty_rows
// (we changed them).
// If topline did change, update_screen() will set botline.
- if (curwin->w_topline == old_topline && set_topbot) {
+ if (curwin->w_topline == old_topline && curwin->w_skipcol == old_skipcol && set_topbot) {
curwin->w_botline = old_botline;
curwin->w_empty_rows = old_empty_rows;
curwin->w_valid = old_valid;
@@ -1691,27 +2103,65 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
///
void scroll_cursor_halfway(bool atend, bool prefer_above)
{
- int above = 0;
- int topfill = 0;
- int below = 0;
- lineoff_T loff;
- lineoff_T boff;
linenr_T old_topline = curwin->w_topline;
-
- loff.lnum = boff.lnum = curwin->w_cursor.lnum;
+ lineoff_T loff = { .lnum = curwin->w_cursor.lnum };
+ lineoff_T boff = { .lnum = curwin->w_cursor.lnum };
(void)hasFolding(loff.lnum, &loff.lnum, &boff.lnum);
int used = plines_win_nofill(curwin, loff.lnum, true);
loff.fill = 0;
boff.fill = 0;
linenr_T topline = loff.lnum;
+ colnr_T skipcol = 0;
+ bool set_skipcol = false;
+
+ int half_height = 0;
+ bool smooth_scroll = false;
+ if (curwin->w_p_sms && curwin->w_p_wrap) {
+ // 'smoothscroll' and 'wrap' are set
+ smooth_scroll = true;
+ half_height = (curwin->w_height_inner - used) / 2;
+ used = 0;
+ }
+ int topfill = 0;
while (topline > 1) {
+ // If using smoothscroll, we can precisely scroll to the
+ // exact point where the cursor is halfway down the screen.
+ if (smooth_scroll) {
+ topline_back_winheight(curwin, &loff, false);
+ if (loff.height == MAXCOL) {
+ break;
+ } else {
+ used += loff.height;
+ }
+ if (used > half_height) {
+ if (used - loff.height < half_height) {
+ int plines_offset = used - half_height;
+ loff.height -= plines_offset;
+ used = half_height;
+
+ topline = loff.lnum;
+ topfill = loff.fill;
+ skipcol = skipcol_from_plines(curwin, plines_offset);
+ set_skipcol = true;
+ }
+ break;
+ }
+ topline = loff.lnum;
+ topfill = loff.fill;
+ continue;
+ }
+
+ // If not using smoothscroll, we have to iteratively find how many
+ // lines to scroll down to roughly fit the cursor.
// This may not be right in the middle if the lines'
// physical height > 1 (e.g. 'wrap' is on).
// Depending on "prefer_above" we add a line above or below first.
// Loop twice to avoid duplicating code.
bool done = false;
+ int above = 0;
+ int below = 0;
for (int round = 1; round <= 2; round++) {
if (prefer_above
? (round == 2 && below < above)
@@ -1757,8 +2207,15 @@ void scroll_cursor_halfway(bool atend, bool prefer_above)
}
}
- if (!hasFolding(topline, &curwin->w_topline, NULL)) {
+ if (!hasFolding(topline, &curwin->w_topline, NULL)
+ && (curwin->w_topline != topline || set_skipcol || curwin->w_skipcol != 0)) {
curwin->w_topline = topline;
+ if (set_skipcol) {
+ curwin->w_skipcol = skipcol;
+ redraw_later(curwin, UPD_NOT_VALID);
+ } else {
+ reset_skipcol(curwin);
+ }
}
curwin->w_topfill = topfill;
if (old_topline > curwin->w_topline + curwin->w_height_inner) {
@@ -1809,6 +2266,16 @@ void cursor_correct(void)
return;
}
+ if (curwin->w_p_sms && !curwin->w_p_wrap) {
+ // 'smoothscroll is active
+ if (curwin->w_cline_height == curwin->w_height_inner) {
+ // The cursor line just fits in the window, don't scroll.
+ reset_skipcol(curwin);
+ return;
+ }
+ // TODO(vim): If the cursor line doesn't fit in the window then only adjust w_skipcol.
+ }
+
// Narrow down the area where the cursor can be put by taking lines from
// the top and the bottom until:
// - the desired context lines are found
@@ -1861,9 +2328,9 @@ void cursor_correct(void)
curwin->w_viewport_invalid = true;
}
-// move screen 'count' pages up or down and update screen
+// Move screen "count" pages up or down and update screen.
//
-// return FAIL for failure, OK otherwise
+// Return FAIL for failure, OK otherwise.
int onepage(Direction dir, long count)
{
long n;
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 5a5286905f..e39a5e1ab7 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -2158,9 +2158,8 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff)
}
// do the horizontal scroll
- if (want_hor && curwin->w_leftcol != tgt_leftcol) {
- curwin->w_leftcol = tgt_leftcol;
- leftcol_changed();
+ if (want_hor) {
+ (void)set_leftcol(tgt_leftcol);
}
}
@@ -2432,7 +2431,7 @@ bool find_decl(char *ptr, size_t len, bool locally, bool thisblock, int flags_ar
/// @return true if able to move cursor, false otherwise.
static bool nv_screengo(oparg_T *oap, int dir, long dist)
{
- int linelen = linetabsize(get_cursor_line_ptr());
+ int linelen = linetabsize_str(get_cursor_line_ptr());
bool retval = true;
bool atend = false;
int col_off1; // margin offset for first screen line
@@ -2494,7 +2493,7 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist)
retval = false;
break;
}
- linelen = linetabsize(get_cursor_line_ptr());
+ linelen = linetabsize_str(get_cursor_line_ptr());
if (linelen > width1) {
int w = (((linelen - width1 - 1) / width2) + 1) * width2;
assert(curwin->w_curswant <= INT_MAX - w);
@@ -2525,7 +2524,7 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist)
if (curwin->w_curswant >= width1) {
curwin->w_curswant -= width2;
}
- linelen = linetabsize(get_cursor_line_ptr());
+ linelen = linetabsize_str(get_cursor_line_ptr());
}
}
}
@@ -2566,6 +2565,8 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist)
if (atend) {
curwin->w_curswant = MAXCOL; // stick in the last column
}
+ adjust_skipcol();
+
return retval;
}
@@ -2633,6 +2634,7 @@ static void nv_scroll_line(cmdarg_T *cap)
void scroll_redraw(int up, long count)
{
linenr_T prev_topline = curwin->w_topline;
+ int prev_skipcol = curwin->w_skipcol;
int prev_topfill = curwin->w_topfill;
linenr_T prev_lnum = curwin->w_cursor.lnum;
@@ -2640,7 +2642,7 @@ void scroll_redraw(int up, long count)
scrollup(count, true) :
scrolldown(count, true);
- if (get_scrolloff_value(curwin)) {
+ if (get_scrolloff_value(curwin) > 0) {
// Adjust the cursor position for 'scrolloff'. Mark w_topline as
// valid, otherwise the screen jumps back at the end of the file.
cursor_correct();
@@ -2651,6 +2653,7 @@ void scroll_redraw(int up, long count)
// we get stuck at one position. Don't move the cursor up if the
// first line of the buffer is already on the screen
while (curwin->w_topline == prev_topline
+ && curwin->w_skipcol == prev_skipcol
&& curwin->w_topfill == prev_topfill) {
if (up) {
if (curwin->w_cursor.lnum > prev_lnum
@@ -2890,27 +2893,21 @@ static void nv_zet(cmdarg_T *cap)
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();
+ (void)set_leftcol((colnr_T)cap->count1 > curwin->w_leftcol
+ ? 0 : curwin->w_leftcol - (colnr_T)cap->count1);
}
break;
- // "zL" - scroll screen left half-page
+ // "zL" - scroll window left half-page
case 'L':
cap->count1 *= curwin->w_width_inner / 2;
FALLTHROUGH;
- // "zl" - scroll screen to the left
+ // "zl" - scroll window to the left if not wrapping
case 'l':
case K_RIGHT:
if (!curwin->w_p_wrap) {
- // scroll the window left
- curwin->w_leftcol += (colnr_T)cap->count1;
- leftcol_changed();
+ (void)set_leftcol(curwin->w_leftcol + (colnr_T)cap->count1);
}
break;
@@ -5349,7 +5346,7 @@ static void nv_g_dollar_cmd(cmdarg_T *cap)
colnr_T vcol;
getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol);
- if (vcol >= curwin->w_leftcol + curwin->w_width - col_off) {
+ if (vcol >= curwin->w_leftcol + curwin->w_width_inner - col_off) {
curwin->w_cursor.col--;
}
}
@@ -5493,7 +5490,7 @@ static void nv_g_cmd(cmdarg_T *cap)
case 'M':
oap->motion_type = kMTCharWise;
oap->inclusive = false;
- i = linetabsize(get_cursor_line_ptr());
+ i = linetabsize_str(get_cursor_line_ptr());
if (cap->count0 > 0 && cap->count0 <= 100) {
coladvance((colnr_T)(i * cap->count0 / 100));
} else {
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 89fe9b464d..d8380303a3 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -5503,7 +5503,7 @@ void cursor_pos_info(dict_T *dict)
validate_virtcol();
col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1,
(int)curwin->w_virtcol + 1);
- col_print(buf2, sizeof(buf2), (int)strlen(p), linetabsize(p));
+ col_print(buf2, sizeof(buf2), (int)strlen(p), linetabsize_str(p));
if (char_count_cursor == byte_count_cursor
&& char_count == byte_count) {
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 3264d80a2f..a977fc4f86 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -2621,6 +2621,19 @@ static const char *did_set_showtabline(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
+/// Process the updated 'smoothscroll' option value.
+static const char *did_set_smoothscroll(optset_T *args FUNC_ATTR_UNUSED)
+{
+ win_T *win = (win_T *)args->os_win;
+ if (win->w_p_sms) {
+ return NULL;
+ }
+
+ win->w_skipcol = 0;
+ changed_line_abv_curs_win(win);
+ return NULL;
+}
+
/// Process the new 'foldlevel' option value.
static const char *did_set_foldlevel(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -4417,6 +4430,8 @@ static char *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
return (char *)&(win->w_p_rlc);
case PV_SCROLL:
return (char *)&(win->w_p_scr);
+ case PV_SMS:
+ return (char *)&(win->w_p_sms);
case PV_WRAP:
return (char *)&(win->w_p_wrap);
case PV_LBR:
@@ -4648,6 +4663,7 @@ void copy_winopt(winopt_T *from, winopt_T *to)
to->wo_briopt = copy_option_val(from->wo_briopt);
to->wo_scb = from->wo_scb;
to->wo_scb_save = from->wo_scb_save;
+ to->wo_sms = from->wo_sms;
to->wo_crb = from->wo_crb;
to->wo_crb_save = from->wo_crb_save;
to->wo_spell = from->wo_spell;
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 40e77550aa..944cc583b3 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -949,6 +949,7 @@ enum {
WV_RLC,
WV_SCBIND,
WV_SCROLL,
+ WV_SMS,
WV_SISO,
WV_SO,
WV_SPELL,
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index e028fbb6a6..c4a85969c0 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -2012,6 +2012,15 @@ return {
defaults={if_true=0}
},
{
+ full_name='smoothscroll', abbreviation='sms',
+ short_desc=N_("scroll by screen lines when 'wrap' is set"),
+ type='bool', scope={'window'},
+ pv_name='p_sms',
+ redraw={'current_window'},
+ defaults={if_true=0},
+ cb='did_set_smoothscroll'
+ },
+ {
full_name='scrollback', abbreviation='scbk',
short_desc=N_("lines to scroll with CTRL-U and CTRL-D"),
type='number', scope={'buffer'},
diff --git a/src/nvim/plines.c b/src/nvim/plines.c
index b2a4ac710d..3e69e547cb 100644
--- a/src/nvim/plines.c
+++ b/src/nvim/plines.c
@@ -189,10 +189,11 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column)
/// @param[out] nextp if not NULL, the line after a fold
/// @param[out] foldedp if not NULL, whether lnum is on a fold
/// @param[in] cache whether to use the window's cache for folds
+/// @param[in] winheight when true limit to window height
///
/// @return the total number of screen lines
int plines_win_full(win_T *wp, linenr_T lnum, linenr_T *const nextp, bool *const foldedp,
- const bool cache)
+ const bool cache, const bool winheight)
{
bool folded = hasFoldingWin(wp, lnum, NULL, nextp, cache, NULL);
if (foldedp) {
@@ -201,9 +202,9 @@ int plines_win_full(win_T *wp, linenr_T lnum, linenr_T *const nextp, bool *const
if (folded) {
return 1;
} else if (lnum == wp->w_topline) {
- return plines_win_nofill(wp, lnum, true) + wp->w_topfill;
+ return plines_win_nofill(wp, lnum, winheight) + wp->w_topfill;
}
- return plines_win(wp, lnum, true);
+ return plines_win(wp, lnum, winheight);
}
int plines_m_win(win_T *wp, linenr_T first, linenr_T last)
@@ -212,7 +213,7 @@ int plines_m_win(win_T *wp, linenr_T first, linenr_T last)
while (first <= last) {
linenr_T next = first;
- count += plines_win_full(wp, first, &next, NULL, false);
+ count += plines_win_full(wp, first, &next, NULL, false, true);
first = next + 1;
}
return count;
@@ -243,12 +244,12 @@ int win_chartabsize(win_T *wp, char *p, colnr_T col)
/// @param s
///
/// @return Number of characters the string will take on the screen.
-int linetabsize(char *s)
+int linetabsize_str(char *s)
{
return linetabsize_col(0, s);
}
-/// Like linetabsize(), but "s" starts at column "startcol".
+/// Like linetabsize_str(), but "s" starts at column "startcol".
///
/// @param startcol
/// @param s
@@ -265,7 +266,7 @@ int linetabsize_col(int startcol, char *s)
return cts.cts_vcol;
}
-/// Like linetabsize(), but for a given window instead of the current one.
+/// Like linetabsize_str(), but for a given window instead of the current one.
///
/// @param wp
/// @param line
@@ -284,6 +285,13 @@ unsigned win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len)
return (unsigned)cts.cts_vcol;
}
+/// Return the number of cells line "lnum" of window "wp" will take on the
+/// screen, taking into account the size of a tab and text properties.
+unsigned linetabsize(win_T *wp, linenr_T lnum)
+{
+ return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum, false), (colnr_T)MAXCOL);
+}
+
/// Prepare the structure passed to chartabsize functions.
///
/// "line" is the start of the line, "ptr" is the first relevant character.
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c
index 3246ef2c71..d404aa9647 100644
--- a/src/nvim/popupmenu.c
+++ b/src/nvim/popupmenu.c
@@ -133,8 +133,6 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
validate_cursor_col();
int above_row = 0;
int below_row = cmdline_row;
- int row_off = 0;
- int col_off = 0;
// wildoptions=pum
if (State == MODE_CMDLINE) {
@@ -145,7 +143,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
// anchor position: the start of the completed word
pum_win_row = curwin->w_wrow;
if (pum_rl) {
- cursor_col = curwin->w_width - curwin->w_wcol - 1;
+ cursor_col = curwin->w_width_inner - curwin->w_wcol - 1;
} else {
cursor_col = curwin->w_wcol;
}
@@ -153,10 +151,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
pum_anchor_grid = (int)curwin->w_grid.target->handle;
pum_win_row += curwin->w_grid.row_offset;
cursor_col += curwin->w_grid.col_offset;
- if (ui_has(kUIMultigrid)) {
- row_off = curwin->w_winrow;
- col_off = curwin->w_wincol;
- } else if (curwin->w_grid.target != &default_grid) {
+ if (!ui_has(kUIMultigrid) && curwin->w_grid.target != &default_grid) {
pum_anchor_grid = (int)default_grid.handle;
pum_win_row += curwin->w_winrow;
cursor_col += curwin->w_wincol;
@@ -202,6 +197,21 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
}
}
+ int min_row = 0;
+ int min_col = 0;
+ int max_col = Columns;
+ int win_start_col = curwin->w_wincol;
+ int win_end_col = W_ENDCOL(curwin);
+ if (!(State & MODE_CMDLINE) && ui_has(kUIMultigrid)) {
+ above_row -= curwin->w_winrow;
+ below_row = MAX(below_row - curwin->w_winrow, curwin->w_grid.rows);
+ min_row = -curwin->w_winrow;
+ min_col = -curwin->w_wincol;
+ max_col = MAX(Columns - curwin->w_wincol, curwin->w_grid.cols);
+ win_start_col = 0;
+ win_end_col = curwin->w_grid.cols;
+ }
+
// Figure out the size and position of the pum.
if (size < PUM_DEF_HEIGHT) {
pum_height = size;
@@ -215,8 +225,8 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
// Put the pum below "pum_win_row" if possible.
// If there are few lines decide on where there is more room.
- if (pum_win_row + row_off + 2 >= below_row - pum_height
- && pum_win_row + row_off - above_row > (below_row - above_row) / 2) {
+ if (pum_win_row + 2 >= below_row - pum_height
+ && pum_win_row - above_row > (below_row - above_row) / 2) {
// pum above "pum_win_row"
pum_above = true;
@@ -232,12 +242,12 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
}
}
- if (pum_win_row + row_off >= size + context_lines) {
+ if (pum_win_row - min_row >= size + context_lines) {
pum_row = pum_win_row - size - context_lines;
pum_height = size;
} else {
- pum_row = -row_off;
- pum_height = pum_win_row + row_off - context_lines;
+ pum_row = min_row;
+ pum_height = pum_win_row - min_row - context_lines;
}
if (p_ph > 0 && pum_height > p_ph) {
@@ -262,8 +272,8 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
}
pum_row = pum_win_row + context_lines;
- if (size > below_row - row_off - pum_row) {
- pum_height = below_row - row_off - pum_row;
+ if (size > below_row - pum_row) {
+ pum_height = below_row - pum_row;
} else {
pum_height = size;
}
@@ -279,9 +289,9 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
}
// If there is a preview window above avoid drawing over it.
- if (pvwin != NULL && pum_row + row_off < above_row && pum_height > above_row) {
- pum_row = above_row - row_off;
- pum_height = pum_win_row + row_off - above_row;
+ if (pvwin != NULL && pum_row < above_row && pum_height > above_row) {
+ pum_row = above_row;
+ pum_height = pum_win_row - above_row;
}
pum_array = array;
@@ -307,19 +317,19 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
def_width = max_width;
}
- if (((cursor_col + col_off < Columns - p_pw
- || cursor_col + col_off < Columns - max_width) && !pum_rl)
- || (pum_rl && (cursor_col + col_off > p_pw
- || cursor_col + col_off > max_width))) {
+ if (((cursor_col < max_col - p_pw
+ || cursor_col < max_col - max_width) && !pum_rl)
+ || (pum_rl && (cursor_col - min_col > p_pw
+ || cursor_col - min_col > max_width))) {
// align pum with "cursor_col"
pum_col = cursor_col;
// start with the maximum space available
if (pum_rl) {
- pum_width = pum_col + col_off - pum_scrollbar + 1;
+ pum_width = pum_col - min_col - pum_scrollbar + 1;
} else {
- assert(Columns - col_off - pum_col - pum_scrollbar >= 0);
- pum_width = Columns - col_off - pum_col - pum_scrollbar;
+ assert(max_col - pum_col - pum_scrollbar >= 0);
+ pum_width = max_col - pum_col - pum_scrollbar;
}
if (pum_width > max_width + pum_kind_width + pum_extra_width + 1
@@ -331,42 +341,42 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
if (pum_width < p_pw) {
pum_width = (int)p_pw;
}
- } else if (((cursor_col + col_off > p_pw
- || cursor_col + col_off > max_width) && !pum_rl)
- || (pum_rl && (cursor_col + col_off < Columns - p_pw
- || cursor_col + col_off < Columns - max_width))) {
+ } else if (((cursor_col - min_col > p_pw
+ || cursor_col - min_col > max_width) && !pum_rl)
+ || (pum_rl && (cursor_col < max_col - p_pw
+ || cursor_col < max_col - max_width))) {
// align pum edge with "cursor_col"
- if (pum_rl && W_ENDCOL(curwin) < max_width + pum_scrollbar + 1) {
+ if (pum_rl && win_end_col < max_width + pum_scrollbar + 1) {
pum_col = cursor_col + max_width + pum_scrollbar + 1;
- if (pum_col + col_off >= Columns) {
- pum_col = Columns - col_off - 1;
+ if (pum_col >= max_col) {
+ pum_col = max_col - 1;
}
} else if (!pum_rl) {
- if (curwin->w_wincol > Columns - max_width - pum_scrollbar
+ if (win_start_col > max_col - max_width - pum_scrollbar
&& max_width <= p_pw) {
// use full width to end of the screen
- pum_col = Columns - col_off - max_width - pum_scrollbar;
- if (pum_col + col_off < 0) {
- pum_col = -col_off;
+ pum_col = max_col - max_width - pum_scrollbar;
+ if (pum_col < min_col) {
+ pum_col = min_col;
}
}
}
if (pum_rl) {
- pum_width = pum_col + col_off - pum_scrollbar + 1;
+ pum_width = pum_col - min_col - pum_scrollbar + 1;
} else {
- pum_width = Columns - col_off - pum_col - pum_scrollbar;
+ pum_width = max_col - pum_col - pum_scrollbar;
}
if (pum_width < p_pw) {
pum_width = (int)p_pw;
if (pum_rl) {
- if (pum_width > pum_col + col_off) {
- pum_width = pum_col + col_off;
+ if (pum_width > pum_col - min_col) {
+ pum_width = pum_col - min_col;
}
} else {
- if (pum_width >= Columns - col_off - pum_col) {
- pum_width = Columns - col_off - pum_col - 1;
+ if (pum_width >= max_col - pum_col) {
+ pum_width = max_col - pum_col - 1;
}
}
} else if (pum_width > max_width + pum_kind_width + pum_extra_width + 1
@@ -377,26 +387,23 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
}
}
}
- } else if (Columns < def_width) {
+ } else if (max_col - min_col < def_width) {
// not enough room, will use what we have
if (pum_rl) {
- assert(Columns - col_off - 1 >= INT_MIN);
- pum_col = Columns - col_off - 1;
+ pum_col = max_col - 1;
} else {
- pum_col = -col_off;
+ pum_col = min_col;
}
- pum_width = Columns - 1;
+ pum_width = max_col - min_col - 1;
} else {
if (max_width > p_pw) {
// truncate
max_width = (int)p_pw;
}
-
if (pum_rl) {
- pum_col = max_width - col_off - 1;
+ pum_col = min_col + max_width - 1;
} else {
- assert(Columns - max_width >= 0);
- pum_col = Columns - col_off - max_width;
+ pum_col = max_col - max_width;
}
pum_width = max_width - pum_scrollbar;
}
@@ -435,18 +442,26 @@ void pum_redraw(void)
int col_off = 0;
bool extra_space = false;
if (pum_rl) {
- col_off = pum_width;
- if (pum_col < curwin->w_wincol + curwin->w_width - 1) {
+ col_off = pum_width - 1;
+ assert(!(State & MODE_CMDLINE));
+ int win_end_col = ui_has(kUIMultigrid) ? curwin->w_grid.cols : W_ENDCOL(curwin);
+ if (pum_col < win_end_col - 1) {
grid_width += 1;
extra_space = true;
}
- } else if (pum_col > 0) {
- grid_width += 1;
- col_off = 1;
- extra_space = true;
+ } else {
+ int min_col = (!(State & MODE_CMDLINE) && ui_has(kUIMultigrid)) ? -curwin->w_wincol : 0;
+ if (pum_col > min_col) {
+ grid_width += 1;
+ col_off = 1;
+ extra_space = true;
+ }
}
if (pum_scrollbar > 0) {
grid_width++;
+ if (pum_rl) {
+ col_off++;
+ }
}
grid_assign_handle(&pum_grid);
@@ -974,42 +989,44 @@ void pum_set_event_info(dict_T *dict)
static void pum_position_at_mouse(int min_width)
{
- int row_off = 0;
- int col_off = 0;
+ int min_row = 0;
+ int max_row = Rows;
+ int max_col = Columns;
if (mouse_grid > 1) {
win_T *wp = get_win_by_grid_handle(mouse_grid);
if (wp != NULL) {
- row_off = wp->w_winrow;
- col_off = wp->w_wincol;
+ min_row = -wp->w_winrow;
+ max_row = MAX(Rows - wp->w_winrow, wp->w_grid.rows);
+ max_col = MAX(Columns - wp->w_wincol, wp->w_grid.cols);
}
}
pum_anchor_grid = mouse_grid;
- if (Rows - row_off - mouse_row > pum_size) {
+ if (max_row - mouse_row > pum_size) {
// Enough space below the mouse row.
pum_above = false;
pum_row = mouse_row + 1;
- if (pum_height > Rows - row_off - pum_row) {
- pum_height = Rows - row_off - pum_row;
+ if (pum_height > max_row - pum_row) {
+ pum_height = max_row - pum_row;
}
} else {
// Show above the mouse row, reduce height if it does not fit.
pum_above = true;
pum_row = mouse_row - pum_size;
- if (pum_row + row_off < 0) {
- pum_height += pum_row + row_off;
- pum_row = -row_off;
+ if (pum_row < min_row) {
+ pum_height += pum_row - min_row;
+ pum_row = min_row;
}
}
- if (Columns - col_off - mouse_col >= pum_base_width
- || Columns - col_off - mouse_col > min_width) {
+ if (max_col - mouse_col >= pum_base_width
+ || max_col - mouse_col > min_width) {
// Enough space to show at mouse column.
pum_col = mouse_col;
} else {
// Not enough space, right align with window.
- pum_col = Columns - col_off - (pum_base_width > min_width ? min_width : pum_base_width);
+ pum_col = max_col - (pum_base_width > min_width ? min_width : pum_base_width);
}
- pum_width = Columns - col_off - pum_col;
+ pum_width = max_col - pum_col;
if (pum_width > pum_base_width + 1) {
pum_width = pum_base_width + 1;
}
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 0784c4c8ff..d1d1b9180f 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -1206,8 +1206,8 @@ static void decor_spell_nav_start(win_T *wp)
decor_redraw_reset(wp, &decor_state);
}
-static bool decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_lnum, int col,
- char **decor_error)
+static TriState decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_lnum, int col,
+ char **decor_error)
{
if (*decor_lnum != lnum) {
decor_providers_invoke_spell(wp, lnum - 1, col, lnum - 1, -1, decor_error);
@@ -1215,7 +1215,7 @@ static bool decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_lnum,
*decor_lnum = lnum;
}
decor_redraw_col(wp, col, col, false, &decor_state);
- return decor_state.spell == kTrue;
+ return decor_state.spell;
}
static inline bool can_syn_spell(win_T *wp, linenr_T lnum, int col)
@@ -1352,9 +1352,18 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
: p - buf) > wp->w_cursor.col)) {
col = (colnr_T)(p - buf);
- bool can_spell = (!has_syntax && (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) == 0)
- || decor_spell_nav_col(wp, lnum, &decor_lnum, col, &decor_error)
- || (has_syntax && can_syn_spell(wp, lnum, col));
+ bool no_plain_buffer = (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) != 0;
+ bool can_spell = !no_plain_buffer;
+ switch (decor_spell_nav_col(wp, lnum, &decor_lnum, col, &decor_error)) {
+ case kTrue:
+ can_spell = true; break;
+ case kFalse:
+ can_spell = false; break;
+ case kNone:
+ if (has_syntax) {
+ can_spell = can_syn_spell(wp, lnum, col);
+ }
+ }
if (!can_spell) {
attr = HLF_COUNT;