Merge pull request #23320 from luukvbaal/smoothscroll

feat(ui): add 'smoothscroll' option
This commit is contained in:
zeertzjq 2023-05-02 19:42:31 +08:00 committed by GitHub
commit 808752f1b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 2389 additions and 235 deletions

View File

@ -49,6 +49,9 @@ iterators |luaref-in|.
• Added |vim.treesitter.query.omnifunc()| for treesitter query files (set by • Added |vim.treesitter.query.omnifunc()| for treesitter query files (set by
default). default).
• |'smoothscroll'| option to scroll by screen line rather than by text line
when |'wrap'| is set.
============================================================================== ==============================================================================
CHANGED FEATURES *news-changed* CHANGED FEATURES *news-changed*

View File

@ -5652,6 +5652,16 @@ A jump table for the options with a short description can be found at |Q_op|.
option. Also see |ins-expandtab|. When 'expandtab' is not set, the option. Also see |ins-expandtab|. When 'expandtab' is not set, the
number of spaces is minimized by using <Tab>s. number of spaces is minimized by using <Tab>s.
*'smoothscroll'* *'sms'* *'nosmoothscroll'* *'nosms'*
'smoothscroll' 'sms' boolean (default off)
local to window
Scrolling works with screen lines. When 'wrap' is set and the first
line in the window wraps part of it may not be visible, as if it is
above the window. "<<<" is displayed at the start of the first line,
highlighted with |hl-NonText|.
NOTE: only partly implemented, currently works with CTRL-E, CTRL-Y
and scrolling with the mouse.
*'softtabstop'* *'sts'* *'softtabstop'* *'sts'*
'softtabstop' 'sts' number (default 0) 'softtabstop' 'sts' number (default 0)
local to buffer local to buffer

View File

@ -870,6 +870,7 @@ Short explanation of each option: *option-list*
'smartcase' 'scs' no ignore case when pattern has uppercase 'smartcase' 'scs' no ignore case when pattern has uppercase
'smartindent' 'si' smart autoindenting for C programs 'smartindent' 'si' smart autoindenting for C programs
'smarttab' 'sta' use 'shiftwidth' when inserting <Tab> 'smarttab' 'sta' use 'shiftwidth' when inserting <Tab>
'smoothscroll' 'sms' scroll by screen lines when 'wrap' is set
'softtabstop' 'sts' number of spaces that <Tab> uses while editing 'softtabstop' 'sts' number of spaces that <Tab> uses while editing
'spell' enable spell checking 'spell' enable spell checking
'spellcapcheck' 'spc' pattern to locate end of a sentence 'spellcapcheck' 'spc' pattern to locate end of a sentence

View File

@ -305,6 +305,9 @@ call <SID>Header(gettext("displaying text"))
call <SID>AddOption("scroll", gettext("number of lines to scroll for CTRL-U and CTRL-D")) call <SID>AddOption("scroll", gettext("number of lines to scroll for CTRL-U and CTRL-D"))
call append("$", "\t" .. s:local_to_window) call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("scr") call <SID>OptionL("scr")
call <SID>AddOption("smoothscroll", gettext("scroll by screen line"))
call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("sms")
call <SID>AddOption("scrolloff", gettext("number of screen lines to show around the cursor")) call <SID>AddOption("scrolloff", gettext("number of screen lines to show around the cursor"))
call append("$", " \tset so=" . &so) call append("$", " \tset so=" . &so)
call <SID>AddOption("wrap", gettext("long lines wrap")) call <SID>AddOption("wrap", gettext("long lines wrap"))

View File

@ -192,6 +192,8 @@ typedef struct {
#define w_p_rlc w_onebuf_opt.wo_rlc // 'rightleftcmd' #define w_p_rlc w_onebuf_opt.wo_rlc // 'rightleftcmd'
long wo_scr; long wo_scr;
#define w_p_scr w_onebuf_opt.wo_scr // 'scroll' #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; int wo_spell;
#define w_p_spell w_onebuf_opt.wo_spell // 'spell' #define w_p_spell w_onebuf_opt.wo_spell // 'spell'
int wo_cuc; int wo_cuc;
@ -1163,11 +1165,12 @@ struct window_S {
bool w_botfill; // true when filler lines are actually bool w_botfill; // true when filler lines are actually
// below w_topline (at end of file) // below w_topline (at end of file)
bool w_old_botfill; // w_botfill at last redraw 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 // character in the window; used when
// 'wrap' is off // 'wrap' is off
colnr_T w_skipcol; // starting column when a single line colnr_T w_skipcol; // starting screen column for the first
// doesn't fit in the window // 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 // six fields that are only used when there is a WinScrolled autocommand
linenr_T w_last_topline; ///< last known value for w_topline linenr_T w_last_topline; ///< last known value for w_topline
@ -1220,6 +1223,7 @@ struct window_S {
int w_valid; int w_valid;
pos_T w_valid_cursor; // last known position of w_cursor, used to adjust 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_leftcol; // last known w_leftcol
colnr_T w_valid_skipcol; // last known w_skipcol
bool w_viewport_invalid; bool w_viewport_invalid;
linenr_T w_viewport_last_topline; // topline when the viewport was last updated linenr_T w_viewport_last_topline; // topline when the viewport was last updated

View File

@ -115,7 +115,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
col = wcol; col = wcol;
if ((addspaces || finetune) && !VIsual_active) { 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) { if (curwin->w_curswant > 0) {
curwin->w_curswant--; 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 && curwin->w_width_inner != 0
&& wcol >= (colnr_T)width && wcol >= (colnr_T)width
&& width > 0) { && width > 0) {
csize = linetabsize(line); csize = linetabsize_str(line);
if (csize > 0) { if (csize > 0) {
csize--; 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. /// @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. // Return quickly when there is no change.
// Perhaps we can change p_siso to int. if (curwin->w_leftcol == leftcol) {
int64_t lastcol; return false;
colnr_T s, e; }
bool retval = false; curwin->w_leftcol = leftcol;
changed_cline_bef_curs(); 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(); validate_virtcol();
bool retval = false;
// If the cursor is right or left of the screen, move it to last or first // 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); long siso = get_sidescrolloff_value(curwin);
if (curwin->w_virtcol > (colnr_T)(lastcol - siso)) { if (curwin->w_virtcol > (colnr_T)(lastcol - siso)) {
retval = true; retval = true;
@ -468,6 +472,7 @@ bool leftcol_changed(void)
// If the start of the character under the cursor is not on the screen, // 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 // advance the cursor one more char. If this fails (last char of the
// line) adjust the scrolling. // line) adjust the scrolling.
colnr_T s, e;
getvvcol(curwin, &curwin->w_cursor, &s, NULL, &e); getvvcol(curwin, &curwin->w_cursor, &s, NULL, &e);
if (e > (colnr_T)lastcol) { if (e > (colnr_T)lastcol) {
retval = true; retval = true;

View File

@ -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, 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) 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) if ((wp->w_p_nu || wp->w_p_rnu)
&& (wlv->row == wlv->startrow + wlv->filler_lines && (wlv->row == wlv->startrow + wlv->filler_lines || !has_cpo_n)
|| vim_strchr(p_cpo, CPO_NUMCOL) == NULL)) { // there is no line number in a wrapped line when "n" is in
// If 'signcolumn' is set to 'number' and a sign is present // 'cpoptions', but 'breakindent' assumes it anyway.
// in "lnum", then display the sign instead of the line && !((has_cpo_n && !wp->w_p_bri) && wp->w_skipcol > 0 && wlv->lnum == wp->w_topline)) {
// number. // 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) { 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); get_sign_display_info(true, wp, wlv, sign_idx, sign_cul_attr);
} else { } else {
// Draw the line number (empty space after wrapping). // 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 > wp->w_winrow || (wp->w_p_nu && wp->w_p_rnu))) {
get_line_number_str(wp, wlv->lnum, wlv->extra, sizeof(wlv->extra)); 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++) { for (wlv->p_extra = wlv->extra; *wlv->p_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; 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; wlv->need_showbreak = false;
} }
// Correct end of highlighted area for 'breakindent', // 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->c_final = NUL;
wlv->n_extra = (int)strlen(sbr); wlv->n_extra = (int)strlen(sbr);
wlv->char_attr = win_hl_attr(wp, HLF_AT); 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->need_showbreak = false;
} }
wlv->vcol_sbr = wlv->vcol + mb_charlen(sbr); 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 // 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the
// first character to be displayed. // first character to be displayed.
if (wp->w_p_wrap) { if (wp->w_p_wrap) {
v = wp->w_skipcol; v = startrow == 0 ? wp->w_skipcol : 0;
} else { } else {
v = wp->w_leftcol; 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) { if (c == NUL) {
// Highlight 'cursorcolumn' & 'colorcolumn' past end of the line. // Highlight 'cursorcolumn' & 'colorcolumn' past end of the line.
if (wp->w_p_wrap) { if (wp->w_p_wrap) {
v = wp->w_skipcol; v = wlv.startrow == 0 ? wp->w_skipcol : 0;
} else { } else {
v = wp->w_leftcol; v = wp->w_leftcol;
} }

View File

@ -1447,6 +1447,26 @@ static void win_update(win_T *wp, DecorProviders *providers)
init_search_hl(wp, &screen_search_hl); 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 // Force redraw when width of 'number' or 'relativenumber' column
// changes. // changes.
int nrwidth = (wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc) ? number_width(wp) : 0; int nrwidth = (wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc) ? number_width(wp) : 0;

View File

@ -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; Insstart_blank_vcol = MAXCOL;
if (!did_ai) { if (!did_ai) {
@ -2251,7 +2251,7 @@ int stop_arrow(void)
// right, except when nothing was inserted yet. // right, except when nothing was inserted yet.
update_Insstart_orig = false; 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) { if (u_save_cursor() == OK) {
arrow_used = false; arrow_used = false;
@ -2449,6 +2449,7 @@ void beginline(int flags)
} }
curwin->w_set_curswant = true; curwin->w_set_curswant = true;
} }
adjust_skipcol();
} }
// oneright oneleft cursor_down cursor_up // oneright oneleft cursor_down cursor_up
@ -2490,6 +2491,7 @@ int oneright(void)
curwin->w_cursor.col += l; curwin->w_cursor.col += l;
curwin->w_set_curswant = true; curwin->w_set_curswant = true;
adjust_skipcol();
return OK; return OK;
} }
@ -2538,6 +2540,7 @@ int oneleft(void)
// if the character on the left of the current cursor is a multi-byte // if the character on the left of the current cursor is a multi-byte
// character, move to its first byte // character, move to its first byte
mb_adjust_cursor(); mb_adjust_cursor();
adjust_skipcol();
return OK; return OK;
} }

View File

@ -346,10 +346,8 @@ static int linelen(int *has_tab)
last > first && ascii_iswhite(last[-1]); last--) {} last > first && ascii_iswhite(last[-1]); last--) {}
char save = *last; char save = *last;
*last = NUL; *last = NUL;
// Get line length. len = linetabsize_str(line); // Get line length.
len = linetabsize(line); if (has_tab != NULL) { // Check for embedded TAB.
// Check for embedded TAB.
if (has_tab != NULL) {
*has_tab = vim_strchr(first, TAB) != NULL; *has_tab = vim_strchr(first, TAB) != NULL;
} }
*last = save; *last = save;

View File

@ -504,6 +504,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat
} }
validate_cursor(); validate_cursor();
// May redraw the status line to show the cursor position. // May redraw the status line to show the cursor position.
if (p_ru && (curwin->w_status_height > 0 || global_stl_height() > 0)) { if (p_ru && (curwin->w_status_height > 0 || global_stl_height() > 0)) {
curwin->w_redr_status = true; 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; magic_overruled = s->magic_overruled_save;
validate_cursor(); // needed for TAB validate_cursor(); // needed for TAB
status_redraw_all();
redraw_all_later(UPD_SOME_VALID); redraw_all_later(UPD_SOME_VALID);
if (call_update_screen) { if (call_update_screen) {
update_screen(); update_screen();

View File

@ -22,7 +22,7 @@
#include "nvim/highlight.h" #include "nvim/highlight.h"
#include "nvim/log.h" #include "nvim/log.h"
#include "nvim/message.h" #include "nvim/message.h"
#include "nvim/option_defs.h" #include "nvim/option.h"
#include "nvim/types.h" #include "nvim/types.h"
#include "nvim/ui.h" #include "nvim/ui.h"
#include "nvim/vim.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; int col = 0;
bool redraw_next; // redraw_this for next character bool redraw_next; // redraw_this for next character
bool clear_next = false; bool clear_next = false;
bool topline = row == 0;
int char_cells; // 1: normal char int char_cells; // 1: normal char
// 2: occupies two display cells // 2: occupies two display cells
int start_dirty = -1, end_dirty = 0; 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_from = linebuf_size;
max_off_to = grid->line_offset[row] + (size_t)grid->cols; 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) { if (rlflag) {
// Clear rest first, because it's left of the text. // Clear rest first, because it's left of the text.
if (clear_width > 0) { if (clear_width > 0) {

View File

@ -1410,9 +1410,22 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
} else { } else {
row -= win_get_fill(win, lnum); row -= win_get_fill(win, lnum);
} }
count = plines_win_nofill(win, lnum, true); count = plines_win_nofill(win, lnum, false);
} else { } 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) { if (count > row) {
@ -1436,9 +1449,12 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
col = off; col = off;
} }
col += row * (win->w_width_inner - off); col += row * (win->w_width_inner - off);
// add skip column (for long wrapping line)
// Add skip column for the topline.
if (lnum == win->w_topline) {
col += win->w_skipcol; col += win->w_skipcol;
} }
}
if (!win->w_p_wrap) { if (!win->w_p_wrap) {
col += win->w_leftcol; col += win->w_leftcol;
@ -1648,8 +1664,6 @@ bool mouse_scroll_horiz(int dir)
return false; return false;
} }
curwin->w_leftcol = (colnr_T)leftcol;
// When the line of the cursor is too short, move the cursor to the // When the line of the cursor is too short, move the cursor to the
// longest visible line. // longest visible line.
if (!virtual_active() if (!virtual_active()
@ -1658,7 +1672,7 @@ bool mouse_scroll_horiz(int dir)
curwin->w_cursor.col = 0; curwin->w_cursor.col = 0;
} }
return leftcol_changed(); return set_leftcol(leftcol);
} }
/// Adjusts the clicked column position when 'conceallevel' > 0 /// Adjusts the clicked column position when 'conceallevel' > 0

File diff suppressed because it is too large Load Diff

View File

@ -2158,9 +2158,8 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff)
} }
// do the horizontal scroll // do the horizontal scroll
if (want_hor && curwin->w_leftcol != tgt_leftcol) { if (want_hor) {
curwin->w_leftcol = tgt_leftcol; (void)set_leftcol(tgt_leftcol);
leftcol_changed();
} }
} }
@ -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. /// @return true if able to move cursor, false otherwise.
static bool nv_screengo(oparg_T *oap, int dir, long dist) 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 retval = true;
bool atend = false; bool atend = false;
int col_off1; // margin offset for first screen line 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; retval = false;
break; break;
} }
linelen = linetabsize(get_cursor_line_ptr()); linelen = linetabsize_str(get_cursor_line_ptr());
if (linelen > width1) { if (linelen > width1) {
int w = (((linelen - width1 - 1) / width2) + 1) * width2; int w = (((linelen - width1 - 1) / width2) + 1) * width2;
assert(curwin->w_curswant <= INT_MAX - w); 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) { if (curwin->w_curswant >= width1) {
curwin->w_curswant -= width2; 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) { if (atend) {
curwin->w_curswant = MAXCOL; // stick in the last column curwin->w_curswant = MAXCOL; // stick in the last column
} }
adjust_skipcol();
return retval; return retval;
} }
@ -2633,6 +2634,7 @@ static void nv_scroll_line(cmdarg_T *cap)
void scroll_redraw(int up, long count) void scroll_redraw(int up, long count)
{ {
linenr_T prev_topline = curwin->w_topline; linenr_T prev_topline = curwin->w_topline;
int prev_skipcol = curwin->w_skipcol;
int prev_topfill = curwin->w_topfill; int prev_topfill = curwin->w_topfill;
linenr_T prev_lnum = curwin->w_cursor.lnum; linenr_T prev_lnum = curwin->w_cursor.lnum;
@ -2640,7 +2642,7 @@ void scroll_redraw(int up, long count)
scrollup(count, true) : scrollup(count, true) :
scrolldown(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 // Adjust the cursor position for 'scrolloff'. Mark w_topline as
// valid, otherwise the screen jumps back at the end of the file. // valid, otherwise the screen jumps back at the end of the file.
cursor_correct(); 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 // we get stuck at one position. Don't move the cursor up if the
// first line of the buffer is already on the screen // first line of the buffer is already on the screen
while (curwin->w_topline == prev_topline while (curwin->w_topline == prev_topline
&& curwin->w_skipcol == prev_skipcol
&& curwin->w_topfill == prev_topfill) { && curwin->w_topfill == prev_topfill) {
if (up) { if (up) {
if (curwin->w_cursor.lnum > prev_lnum if (curwin->w_cursor.lnum > prev_lnum
@ -2890,27 +2893,21 @@ static void nv_zet(cmdarg_T *cap)
case 'h': case 'h':
case K_LEFT: case K_LEFT:
if (!curwin->w_p_wrap) { if (!curwin->w_p_wrap) {
if ((colnr_T)cap->count1 > curwin->w_leftcol) { (void)set_leftcol((colnr_T)cap->count1 > curwin->w_leftcol
curwin->w_leftcol = 0; ? 0 : curwin->w_leftcol - (colnr_T)cap->count1);
} else {
curwin->w_leftcol -= (colnr_T)cap->count1;
}
leftcol_changed();
} }
break; break;
// "zL" - scroll screen left half-page // "zL" - scroll window left half-page
case 'L': case 'L':
cap->count1 *= curwin->w_width_inner / 2; cap->count1 *= curwin->w_width_inner / 2;
FALLTHROUGH; FALLTHROUGH;
// "zl" - scroll screen to the left // "zl" - scroll window to the left if not wrapping
case 'l': case 'l':
case K_RIGHT: case K_RIGHT:
if (!curwin->w_p_wrap) { if (!curwin->w_p_wrap) {
// scroll the window left (void)set_leftcol(curwin->w_leftcol + (colnr_T)cap->count1);
curwin->w_leftcol += (colnr_T)cap->count1;
leftcol_changed();
} }
break; break;
@ -5493,7 +5490,7 @@ static void nv_g_cmd(cmdarg_T *cap)
case 'M': case 'M':
oap->motion_type = kMTCharWise; oap->motion_type = kMTCharWise;
oap->inclusive = false; oap->inclusive = false;
i = linetabsize(get_cursor_line_ptr()); i = linetabsize_str(get_cursor_line_ptr());
if (cap->count0 > 0 && cap->count0 <= 100) { if (cap->count0 > 0 && cap->count0 <= 100) {
coladvance((colnr_T)(i * cap->count0 / 100)); coladvance((colnr_T)(i * cap->count0 / 100));
} else { } else {

View File

@ -5503,7 +5503,7 @@ void cursor_pos_info(dict_T *dict)
validate_virtcol(); validate_virtcol();
col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1, col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1,
(int)curwin->w_virtcol + 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 if (char_count_cursor == byte_count_cursor
&& char_count == byte_count) { && char_count == byte_count) {

View File

@ -2621,6 +2621,19 @@ static const char *did_set_showtabline(optset_T *args FUNC_ATTR_UNUSED)
return NULL; 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. /// Process the new 'foldlevel' option value.
static const char *did_set_foldlevel(optset_T *args FUNC_ATTR_UNUSED) 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); return (char *)&(win->w_p_rlc);
case PV_SCROLL: case PV_SCROLL:
return (char *)&(win->w_p_scr); return (char *)&(win->w_p_scr);
case PV_SMS:
return (char *)&(win->w_p_sms);
case PV_WRAP: case PV_WRAP:
return (char *)&(win->w_p_wrap); return (char *)&(win->w_p_wrap);
case PV_LBR: 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_briopt = copy_option_val(from->wo_briopt);
to->wo_scb = from->wo_scb; to->wo_scb = from->wo_scb;
to->wo_scb_save = from->wo_scb_save; to->wo_scb_save = from->wo_scb_save;
to->wo_sms = from->wo_sms;
to->wo_crb = from->wo_crb; to->wo_crb = from->wo_crb;
to->wo_crb_save = from->wo_crb_save; to->wo_crb_save = from->wo_crb_save;
to->wo_spell = from->wo_spell; to->wo_spell = from->wo_spell;

View File

@ -949,6 +949,7 @@ enum {
WV_RLC, WV_RLC,
WV_SCBIND, WV_SCBIND,
WV_SCROLL, WV_SCROLL,
WV_SMS,
WV_SISO, WV_SISO,
WV_SO, WV_SO,
WV_SPELL, WV_SPELL,

View File

@ -2011,6 +2011,15 @@ return {
pv_name='p_scroll', pv_name='p_scroll',
defaults={if_true=0} 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', full_name='scrollback', abbreviation='scbk',
short_desc=N_("lines to scroll with CTRL-U and CTRL-D"), short_desc=N_("lines to scroll with CTRL-U and CTRL-D"),

View File

@ -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] nextp if not NULL, the line after a fold
/// @param[out] foldedp if not NULL, whether lnum is on 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] 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 /// @return the total number of screen lines
int plines_win_full(win_T *wp, linenr_T lnum, linenr_T *const nextp, bool *const foldedp, 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); bool folded = hasFoldingWin(wp, lnum, NULL, nextp, cache, NULL);
if (foldedp) { if (foldedp) {
@ -201,9 +202,9 @@ int plines_win_full(win_T *wp, linenr_T lnum, linenr_T *const nextp, bool *const
if (folded) { if (folded) {
return 1; return 1;
} else if (lnum == wp->w_topline) { } 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) 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) { while (first <= last) {
linenr_T next = first; 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; first = next + 1;
} }
return count; return count;
@ -243,12 +244,12 @@ int win_chartabsize(win_T *wp, char *p, colnr_T col)
/// @param s /// @param s
/// ///
/// @return Number of characters the string will take on the screen. /// @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); 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 startcol
/// @param s /// @param s
@ -265,7 +266,7 @@ int linetabsize_col(int startcol, char *s)
return cts.cts_vcol; 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 wp
/// @param line /// @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 (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. /// Prepare the structure passed to chartabsize functions.
/// ///
/// "line" is the start of the line, "ptr" is the first relevant character. /// "line" is the start of the line, "ptr" is the first relevant character.

View File

@ -194,4 +194,51 @@ describe('display', function()
it('display "lastline" works correctly with multibyte fillchar', function() it('display "lastline" works correctly with multibyte fillchar', function()
run_test_display_lastline(true) run_test_display_lastline(true)
end) end)
-- oldtest: Test_display_long_lastline
it('display "lastline" shows correct text when end of wrapped line is deleted', function()
local screen = Screen.new(35, 14)
screen:attach()
exec([[
set display=lastline scrolloff=5
call setline(1, [
\'aaaaa'->repeat(100),
\'bbbbb '->repeat(7) .. 'ccccc '->repeat(7) .. 'ddddd '->repeat(7)
\])
]])
feed('482|')
screen:expect([[
<<<aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaa^aaaaaaaaa|
aaaaaaaaaa |
|
]])
feed('D')
screen:expect([[
<<<aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
aaaaaaaaaaaaaaaaaaaaaaaaa^a |
bbbbb bbbbb bbbbb bbbbb bbbbb bb@@@|
|
]])
end)
end) end)

View File

@ -0,0 +1,777 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
local exec = helpers.exec
local feed = helpers.feed
before_each(clear)
describe('smoothscroll', function()
local screen
before_each(function()
screen = Screen.new(40, 12)
screen:attach()
end)
-- oldtest: Test_CtrlE_CtrlY_stop_at_end()
it('disabled does not break <C-E> and <C-Y> stop at end', function()
exec([[
enew
call setline(1, ['one', 'two'])
set number
]])
feed('<C-Y>')
screen:expect({any = " 1 ^one"})
feed('<C-E><C-E><C-E>')
screen:expect({any = " 2 ^two"})
end)
-- oldtest: Test_smoothscroll_CtrlE_CtrlY()
it('works with <C-E> and <C-E>', function()
exec([[
call setline(1, [ 'line one', 'word '->repeat(20), 'line three', 'long word '->repeat(7), 'line', 'line', 'line', ])
set smoothscroll scrolloff=5
:5
]])
local s1 = [[
word word word word word word word word |
word word word word word word word word |
word word word word |
line three |
long word long word long word long word |
long word long word long word |
^line |
line |
line |
~ |
~ |
|
]]
local s2 = [[
<<<d word word word word word word word |
word word word word |
line three |
long word long word long word long word |
long word long word long word |
^line |
line |
line |
~ |
~ |
~ |
|
]]
local s3 = [[
<<<d word word word |
line three |
long word long word long word long word |
long word long word long word |
^line |
line |
line |
~ |
~ |
~ |
~ |
|
]]
local s4 = [[
line three |
long word long word long word long word |
long word long word long word |
line |
line |
^line |
~ |
~ |
~ |
~ |
~ |
|
]]
local s5 = [[
<<<d word word word |
line three |
long word long word long word long word |
long word long word long word |
line |
line |
^line |
~ |
~ |
~ |
~ |
|
]]
local s6 = [[
<<<d word word word word word word word |
word word word word |
line three |
long word long word long word long word |
long word long word long word |
line |
line |
^line |
~ |
~ |
~ |
|
]]
local s7 = [[
word word word word word word word word |
word word word word word word word word |
word word word word |
line three |
long word long word long word long word |
long word long word long word |
line |
line |
^line |
~ |
~ |
|
]]
local s8 = [[
line one |
word word word word word word word word |
word word word word word word word word |
word word word word |
line three |
long word long word long word long word |
long word long word long word |
line |
line |
^line |
~ |
|
]]
feed('<C-E>')
screen:expect(s1)
feed('<C-E>')
screen:expect(s2)
feed('<C-E>')
screen:expect(s3)
feed('<C-E>')
screen:expect(s4)
feed('<C-Y>')
screen:expect(s5)
feed('<C-Y>')
screen:expect(s6)
feed('<C-Y>')
screen:expect(s7)
feed('<C-Y>')
screen:expect(s8)
exec('set foldmethod=indent')
-- move the cursor so we can reuse the same dumps
feed('5G<C-E>')
screen:expect(s1)
feed('<C-E>')
screen:expect(s2)
feed('7G<C-Y>')
screen:expect(s7)
feed('<C-Y>')
screen:expect(s8)
end)
-- oldtest: Test_smoothscroll_number()
it("works 'number' and 'cpo'+=n", function()
exec([[
call setline(1, [ 'one ' .. 'word '->repeat(20), 'two ' .. 'long word '->repeat(7), 'line', 'line', 'line', ])
set smoothscroll scrolloff=5
set number cpo+=n
:3
func g:DoRel()
set number relativenumber scrolloff=0
:%del
call setline(1, [ 'one', 'very long text '->repeat(12), 'three', ])
exe "normal 2Gzt\<C-E>"
endfunc
]])
screen:expect([[
1 one word word word word word word wo|
rd word word word word word word word wo|
rd word word word word word |
2 two long word long word long word lo|
ng word long word long word long word |
3 ^line |
4 line |
5 line |
~ |
~ |
~ |
|
]])
feed('<C-E>')
screen:expect([[
<<<word word word word word word word wo|
rd word word word word word |
2 two long word long word long word lo|
ng word long word long word long word |
3 ^line |
4 line |
5 line |
~ |
~ |
~ |
~ |
|
]])
feed('<C-E>')
screen:expect([[
<<<word word word word word |
2 two long word long word long word lo|
ng word long word long word long word |
3 ^line |
4 line |
5 line |
~ |
~ |
~ |
~ |
~ |
|
]])
exec('set cpo-=n')
screen:expect([[
<<< d word word word word word word |
2 two long word long word long word lo|
ng word long word long word long wor|
d |
3 ^line |
4 line |
5 line |
~ |
~ |
~ |
~ |
|
]])
feed('<C-Y>')
screen:expect([[
<<< rd word word word word word word wor|
d word word word word word word |
2 two long word long word long word lo|
ng word long word long word long wor|
d |
3 ^line |
4 line |
5 line |
~ |
~ |
~ |
|
]])
feed('<C-Y>')
screen:expect([[
1 one word word word word word word wo|
rd word word word word word word wor|
d word word word word word word |
2 two long word long word long word lo|
ng word long word long word long wor|
d |
3 ^line |
4 line |
5 line |
~ |
~ |
|
]])
exec('call DoRel()')
screen:expect([[
2<<<^ong text very long text very long te|
xt very long text very long text ver|
y long text very long text very long|
text very long text very long text |
1 three |
~ |
~ |
~ |
~ |
~ |
~ |
--No lines in buffer-- |
]])
end)
-- oldtest: Test_smoothscroll_list()
it("works with list mode", function()
screen:try_resize(40, 8)
exec([[
set smoothscroll scrolloff=0
set list
call setline(1, [ 'one', 'very long text '->repeat(12), 'three', ])
exe "normal 2Gzt\<C-E>"
]])
screen:expect([[
<<<t very long text very long text very |
^long text very long text very long text |
very long text very long text very long |
text very long text- |
three |
~ |
~ |
|
]])
exec('set listchars+=precedes:#')
screen:expect([[
#ext very long text very long text very |
^long text very long text very long text |
very long text very long text very long |
text very long text- |
three |
~ |
~ |
|
]])
end)
-- oldtest: Test_smoothscroll_diff_mode()
it("works with diff mode", function()
screen:try_resize(40, 8)
exec([[
let text = 'just some text here'
call setline(1, text)
set smoothscroll
diffthis
new
call setline(1, text)
set smoothscroll
diffthis
]])
screen:expect([[
- ^just some text here |
~ |
~ |
[No Name] [+] |
- just some text here |
~ |
[No Name] [+] |
|
]])
feed('<C-Y>')
screen:expect_unchanged()
feed('<C-E>')
screen:expect_unchanged()
end)
-- oldtest: Test_smoothscroll_wrap_scrolloff_zero()
it("works with zero 'scrolloff'", function()
screen:try_resize(40, 8)
exec([[
call setline(1, ['Line' .. (' with some text'->repeat(7))]->repeat(7))
set smoothscroll scrolloff=0 display=
:3
]])
screen:expect([[
<<<h some text with some text |
Line with some text with some text with |
some text with some text with some text |
with some text with some text |
^Line with some text with some text with |
some text with some text with some text |
with some text with some text |
|
]])
feed('j')
screen:expect_unchanged()
-- moving cursor down - whole bottom line shows
feed('<C-E>j')
screen:expect_unchanged()
feed('G')
screen:expect_unchanged()
-- moving cursor up right after the >>> marker - no need to show whole line
feed('2gj3l2k')
screen:expect([[
<<<^h some text with some text |
Line with some text with some text with |
some text with some text with some text |
with some text with some text |
Line with some text with some text with |
some text with some text with some text |
with some text with some text |
|
]])
-- moving cursor up where the >>> marker is - whole top line shows
feed('2j02k')
screen:expect([[
^Line with some text with some text with |
some text with some text with some text |
with some text with some text |
Line with some text with some text with |
some text with some text with some text |
with some text with some text |
@ |
|
]])
end)
-- oldtest: Test_smoothscroll_wrap_long_line()
it("adjusts the cursor position in a long line", function()
screen:try_resize(40, 6)
exec([[
call setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(30)) .. ' end', 'four'])
set smoothscroll scrolloff=0
normal 3G10|zt
]])
-- scrolling up, cursor moves screen line down
screen:expect([[
Line with^ lots of text with lots of text|
with lots of text with lots of text wit|
h lots of text with lots of text with lo|
ts of text with lots of text with lots o|
f text with lots of text with lots of te|
|
]])
feed('<C-E>')
screen:expect([[
<<<th lot^s of text with lots of text wit|
h lots of text with lots of text with lo|
ts of text with lots of text with lots o|
f text with lots of text with lots of te|
xt with lots of text with lots of text w|
|
]])
feed('5<C-E>')
screen:expect([[
<<< lots ^of text with lots of text with |
lots of text with lots of text with lots|
of text with lots of text with lots of |
text with lots of text with lots of text|
with lots of text with lots of text wit|
|
]])
-- scrolling down, cursor moves screen line up
feed('5<C-Y>')
screen:expect([[
<<<th lots of text with lots of text wit|
h lots of text with lots of text with lo|
ts of text with lots of text with lots o|
f text with lots of text with lots of te|
xt with l^ots of text with lots of text w|
|
]])
feed('<C-Y>')
screen:expect([[
Line with lots of text with lots of text|
with lots of text with lots of text wit|
h lots of text with lots of text with lo|
ts of text with lots of text with lots o|
f text wi^th lots of text with lots of te|
|
]])
-- 'scrolloff' set to 1, scrolling up, cursor moves screen line down
exec('set scrolloff=1')
feed('10|<C-E>')
screen:expect([[
<<<th lots of text with lots of text wit|
h lots of^ text with lots of text with lo|
ts of text with lots of text with lots o|
f text with lots of text with lots of te|
xt with lots of text with lots of text w|
|
]])
-- 'scrolloff' set to 1, scrolling down, cursor moves screen line up
feed('<C-E>gjgj<C-Y>')
screen:expect([[
<<<th lots of text with lots of text wit|
h lots of text with lots of text with lo|
ts of text with lots of text with lots o|
f text wi^th lots of text with lots of te|
xt with lots of text with lots of text w|
|
]])
-- 'scrolloff' set to 2, scrolling up, cursor moves screen line down
exec('set scrolloff=2')
feed('10|<C-E>')
screen:expect([[
<<<th lots of text with lots of text wit|
h lots of text with lots of text with lo|
ts of tex^t with lots of text with lots o|
f text with lots of text with lots of te|
xt with lots of text with lots of text w|
|
]])
-- 'scrolloff' set to 2, scrolling down, cursor moves screen line up
feed('<C-E>gj<C-Y>')
screen:expect_unchanged()
-- 'scrolloff' set to 0, move cursor down one line. Cursor should move properly,
-- and since this is a really long line, it will be put on top of the screen.
exec('set scrolloff=0')
feed('0j')
screen:expect([[
<<<of text with lots of text with lots o|
f text with lots of text end |
^four |
~ |
~ |
|
]])
-- Test zt/zz/zb that they work properly when a long line is above it
feed('zb')
screen:expect([[
<<<th lots of text with lots of text wit|
h lots of text with lots of text with lo|
ts of text with lots of text with lots o|
f text with lots of text end |
^four |
|
]])
feed('zz')
screen:expect([[
<<<of text with lots of text with lots o|
f text with lots of text end |
^four |
~ |
~ |
|
]])
feed('zt')
screen:expect([[
^four |
~ |
~ |
~ |
~ |
|
]])
-- Repeat the step and move the cursor down again.
-- This time, use a shorter long line that is barely long enough to span more
-- than one window. Note that the cursor is at the bottom this time because
-- Vim prefers to do so if we are scrolling a few lines only.
exec("call setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(10)) .. ' end', 'four'])")
feed('3Gztj')
screen:expect([[
<<<th lots of text with lots of text wit|
h lots of text with lots of text with lo|
ts of text with lots of text with lots o|
f text with lots of text end |
^four |
|
]])
-- Repeat the step but this time start it when the line is smooth-scrolled by
-- one line. This tests that the offset calculation is still correct and
-- still end up scrolling down to the next line with cursor at bottom of
-- screen.
feed('3Gzt<C-E>j')
screen:expect([[
<<<th lots of text with lots of text wit|
h lots of text with lots of text with lo|
ts of text with lots of text with lots o|
f text with lots of text end |
fou^r |
|
]])
end)
-- oldtest: Test_smoothscroll_one_long_line()
it("scrolls correctly when moving the cursor", function()
screen:try_resize(40, 6)
exec([[
call setline(1, 'with lots of text '->repeat(7))
set smoothscroll scrolloff=0
]])
local s1 = [[
^with lots of text with lots of text with|
lots of text with lots of text with lot|
s of text with lots of text with lots of|
text |
~ |
|
]]
screen:expect(s1)
feed('<C-E>')
screen:expect([[
<<<ts of text with lots of text with lot|
^s of text with lots of text with lots of|
text |
~ |
~ |
|
]])
feed('0')
screen:expect(s1)
end)
-- oldtest: Test_smoothscroll_long_line_showbreak()
it("cursor is not one screen line too far down", function()
screen:try_resize(40, 6)
-- a line that spans four screen lines
exec("call setline(1, 'with lots of text in one line '->repeat(6))")
exec('set smoothscroll scrolloff=0 showbreak=+++\\ ')
local s1 = [[
^with lots of text in one line with lots |
+++ of text in one line with lots of tex|
+++ t in one line with lots of text in o|
+++ ne line with lots of text in one lin|
+++ e with lots of text in one line |
|
]]
screen:expect(s1)
feed('<C-E>')
screen:expect([[
+++ ^of text in one line with lots of tex|
+++ t in one line with lots of text in o|
+++ ne line with lots of text in one lin|
+++ e with lots of text in one line |
~ |
|
]])
feed('0')
screen:expect(s1)
end)
-- oldtest: Test_smoothscroll_zero_width()
it("does not divide by zero with a narrow window", function()
screen:try_resize(12, 2)
screen:set_default_attr_ids({
[1] = {foreground = Screen.colors.Brown},
[2] = {foreground = Screen.colors.Blue1, bold = true},
})
exec([[
call setline(1, ['a'->repeat(100)])
set wrap smoothscroll number laststatus=0
wincmd v
wincmd v
wincmd v
wincmd v
]])
screen:expect([[
{1: 1^ }{1: }{1: }{1: }{1: }|
|
]])
feed('llllllllll<C-W>o')
screen:expect([[
{2:<<<}{1: }aa^aaaaaa|
|
]])
end)
it("works with virt_lines above and below", function()
screen:try_resize(55, 7)
exec([=[
call setline(1, ['Line' .. (' with some text'->repeat(7))]->repeat(3))
set smoothscroll
let ns = nvim_create_namespace('')
call nvim_buf_set_extmark(0, ns, 0, 0, {'virt_lines':[[['virt_below1']]]})
call nvim_buf_set_extmark(0, ns, 1, 0, {'virt_lines':[[['virt_above1']]],'virt_lines_above':1})
call nvim_buf_set_extmark(0, ns, 1, 0, {'virt_lines':[[['virt_below2']]]})
call nvim_buf_set_extmark(0, ns, 2, 0, {'virt_lines':[[['virt_above2']]],'virt_lines_above':1})
norm ggL
]=])
screen:expect([[
Line with some text with some text with some text with |
some text with some text with some text with some text |
virt_below1 |
virt_above1 |
^Line with some text with some text with some text with |
some text with some text with some text with some text |
|
]])
feed('<C-E>')
screen:expect([[
<<<e text with some text with some text with some text |
virt_below1 |
virt_above1 |
^Line with some text with some text with some text with |
some text with some text with some text with some text |
virt_below2 |
|
]])
feed('<C-E>')
screen:expect([[
virt_below1 |
virt_above1 |
^Line with some text with some text with some text with |
some text with some text with some text with some text |
virt_below2 |
virt_above2 |
|
]])
feed('<C-E>')
screen:expect([[
virt_above1 |
^Line with some text with some text with some text with |
some text with some text with some text with some text |
virt_below2 |
virt_above2 |
Line with some text with some text with some text wi@@@|
|
]])
feed('<C-E>')
screen:expect([[
^Line with some text with some text with some text with |
some text with some text with some text with some text |
virt_below2 |
virt_above2 |
Line with some text with some text with some text with |
some text with some text with some text with some text |
|
]])
feed('<C-E>')
screen:expect([[
<<<e text with some text with some text with some tex^t |
virt_below2 |
virt_above2 |
Line with some text with some text with some text with |
some text with some text with some text with some text |
~ |
|
]])
end)
it('<<< marker shows with tabline, winbar and splits', function()
screen:try_resize(40, 12)
exec([[
call setline(1, ['Line' .. (' with some text'->repeat(7))]->repeat(7))
set smoothscroll scrolloff=0
norm sj
]])
screen:expect([[
<<<e text with some text with some text |
with some text with some text |
Line with some text with some text with |
some text with some text with some text |
with some text with some text |
[No Name] [+] |
<<<e text with some text with some text |
^with some text with some text |
Line with some text with some text with |
some text with some text with some te@@@|
[No Name] [+] |
|
]])
exec('set showtabline=2')
feed('<C-E>')
screen:expect([[
2+ [No Name] |
<<<e text with some text with some text |
with some text with some text |
Line with some text with some text with |
some text with some text with some text |
with some text with some text |
[No Name] [+] |
<<<e text with some text with some text |
^with some text with some text |
Line with some text with some text wi@@@|
[No Name] [+] |
|
]])
exec('set winbar=winbar')
feed('<C-w>k<C-E>')
screen:expect([[
2+ [No Name] |
winbar |
<<<e text with some text with some text |
^with some text with some text |
Line with some text with some text with |
some text with some text with some te@@@|
[No Name] [+] |
winbar |
<<<e text with some text with some text |
with some text with some text |
[No Name] [+] |
|
]])
end)
end)

View File

@ -2396,7 +2396,7 @@ describe('builtin popupmenu', function()
-- can't draw the pum, but check we don't crash -- can't draw the pum, but check we don't crash
screen:try_resize(12,2) screen:try_resize(12,2)
screen:expect([[ screen:expect([[
text^ | {1:<<<}t^ |
{2:-- INSERT -} | {2:-- INSERT -} |
]]) ]])

View File

@ -17,7 +17,6 @@ source test_global.vim
source test_move.vim source test_move.vim
source test_put.vim source test_put.vim
source test_reltime.vim source test_reltime.vim
source test_scroll_opt.vim
source test_searchpos.vim source test_searchpos.vim
source test_set.vim source test_set.vim
source test_shift.vim source test_shift.vim

View File

@ -583,7 +583,7 @@ func Test_breakindent16()
redraw! redraw!
let lines = s:screen_lines(1,10) let lines = s:screen_lines(1,10)
let expect = [ let expect = [
\ " 789012", \ "<<< 789012",
\ " 345678", \ " 345678",
\ " 901234", \ " 901234",
\ ] \ ]
@ -611,7 +611,7 @@ func Test_breakindent16_vartabs()
redraw! redraw!
let lines = s:screen_lines(1,10) let lines = s:screen_lines(1,10)
let expect = [ let expect = [
\ " 789012", \ "<<< 789012",
\ " 345678", \ " 345678",
\ " 901234", \ " 901234",
\ ] \ ]
@ -711,25 +711,25 @@ endfunc
func Test_breakindent20_cpo_n_nextpage() func Test_breakindent20_cpo_n_nextpage()
let s:input = "" let s:input = ""
call s:test_windows('setl breakindent briopt=min:14 cpo+=n number') call s:test_windows('setl breakindent briopt=min:14 cpo+=n number')
call setline(1, repeat('a', 200)) call setline(1, repeat('abcdefghijklmnopqrst', 10))
norm! 1gg norm! 1gg
redraw! redraw!
let lines = s:screen_lines(1, 20) let lines = s:screen_lines(1, 20)
let expect = [ let expect = [
\ " 1 aaaaaaaaaaaaaaaa", \ " 1 abcdefghijklmnop",
\ " aaaaaaaaaaaaaaaa", \ " qrstabcdefghijkl",
\ " aaaaaaaaaaaaaaaa", \ " mnopqrstabcdefgh",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
" Scroll down one screen line " Scroll down one screen line
setl scrolloff=5 setl scrolloff=5
norm! 5gj norm! 6gj
redraw! redraw!
let lines = s:screen_lines(1, 20) let lines = s:screen_lines(1, 20)
let expect = [ let expect = [
\ "--1 aaaaaaaaaaaaaaaa", \ "<<< qrstabcdefghijkl",
\ " aaaaaaaaaaaaaaaa", \ " mnopqrstabcdefgh",
\ " aaaaaaaaaaaaaaaa", \ " ijklmnopqrstabcd",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
@ -737,18 +737,18 @@ func Test_breakindent20_cpo_n_nextpage()
norm! 1gg norm! 1gg
let lines = s:screen_lines(1, 20) let lines = s:screen_lines(1, 20)
let expect = [ let expect = [
\ " 1 aaaaaaaaaaaaaaaa", \ " 1 abcdefghijklmnop",
\ " aaaaaaaaaaaaaa", \ " qrstabcdefghij",
\ " aaaaaaaaaaaaaa", \ " klmnopqrstabcd",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
" Scroll down one screen line " Scroll down one screen line
norm! 5gj norm! 6gj
let lines = s:screen_lines(1, 20) let lines = s:screen_lines(1, 20)
let expect = [ let expect = [
\ "--1 aaaaaaaaaaaaaa", \ "<<< qrstabcdefghij",
\ " aaaaaaaaaaaaaa", \ " klmnopqrstabcd",
\ " aaaaaaaaaaaaaa", \ " efghijklmnopqr",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)

View File

@ -1605,6 +1605,21 @@ func Test_diff_scroll()
call delete('Xright') call delete('Xright')
endfunc endfunc
" This was scrolling too many lines.
func Test_diff_scroll_wrap_on()
20new
40vsplit
call setline(1, map(range(1, 9), 'repeat(v:val, 200)'))
setlocal number diff so=0
redraw
normal! jj
call assert_equal(1, winsaveview().topline)
normal! j
call assert_equal(2, winsaveview().topline)
bwipe!
bwipe!
endfunc
" This was trying to update diffs for a buffer being closed " This was trying to update diffs for a buffer being closed
func Test_diff_only() func Test_diff_only()
silent! lfile silent! lfile

View File

@ -478,5 +478,26 @@ func Test_display_lastline()
call assert_fails(':set fillchars=lastline:', 'E474:') call assert_fails(':set fillchars=lastline:', 'E474:')
endfunc endfunc
func Test_display_long_lastline()
CheckScreendump
let lines =<< trim END
set display=lastline
call setline(1, [
\'aaaaa'->repeat(100),
\'bbbbb '->repeat(7) .. 'ccccc '->repeat(7) .. 'ddddd '->repeat(7)
\])
END
call writefile(lines, 'XdispLongline', 'D')
let buf = RunVimInTerminal('-S XdispLongline', #{rows: 14, cols: 35})
call term_sendkeys(buf, "482|")
call VerifyScreenDump(buf, 'Test_display_long_line_1', {})
call term_sendkeys(buf, "D")
call VerifyScreenDump(buf, 'Test_display_long_line_2', {})
call StopVimInTerminal(buf)
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@ -223,7 +223,7 @@ func Test_virtual_block_and_vbA()
exe "norm! $3B\<C-v>eAx\<Esc>" exe "norm! $3B\<C-v>eAx\<Esc>"
let lines = s:screen_lines([1, 10], winwidth(0)) let lines = s:screen_lines([1, 10], winwidth(0))
let expect = [ let expect = [
\ "foobar foobar ", \ "<<<bar foobar ",
\ "foobar foobar ", \ "foobar foobar ",
\ "foobar foobar ", \ "foobar foobar ",
\ "foobar foobar ", \ "foobar foobar ",

View File

@ -266,7 +266,7 @@ func Test_chinese_char_on_wrap_column()
norm! $ norm! $
redraw! redraw!
let expect=[ let expect=[
\ '中aaaaaaaaaaaaaaaaa>', \ '<<<aaaaaaaaaaaaaaaa>',
\ '中aaaaaaaaaaaaaaaaa>', \ '中aaaaaaaaaaaaaaaaa>',
\ '中aaaaaaaaaaaaaaaaa>', \ '中aaaaaaaaaaaaaaaaa>',
\ '中aaaaaaaaaaaaaaaaa>', \ '中aaaaaaaaaaaaaaaaa>',

View File

@ -3738,15 +3738,45 @@ endfunc
" Test for 'scrolloff' with a long line that doesn't fit in the screen " Test for 'scrolloff' with a long line that doesn't fit in the screen
func Test_normal_scroloff() func Test_normal_scroloff()
10new 10new
80vnew 60vnew
call setline(1, repeat('a', 1000)) call setline(1, ' 1 ' .. repeat('a', 57)
\ .. ' 2 ' .. repeat('b', 57)
\ .. ' 3 ' .. repeat('c', 57)
\ .. ' 4 ' .. repeat('d', 57)
\ .. ' 5 ' .. repeat('e', 57)
\ .. ' 6 ' .. repeat('f', 57)
\ .. ' 7 ' .. repeat('g', 57)
\ .. ' 8 ' .. repeat('h', 57)
\ .. ' 9 ' .. repeat('i', 57)
\ .. '10 ' .. repeat('j', 57)
\ .. '11 ' .. repeat('k', 57)
\ .. '12 ' .. repeat('l', 57)
\ .. '13 ' .. repeat('m', 57)
\ .. '14 ' .. repeat('n', 57)
\ .. '15 ' .. repeat('o', 57)
\ .. '16 ' .. repeat('p', 57)
\ .. '17 ' .. repeat('q', 57)
\ .. '18 ' .. repeat('r', 57)
\ .. '19 ' .. repeat('s', 57)
\ .. '20 ' .. repeat('t', 57)
\ .. '21 ' .. repeat('u', 57)
\ .. '22 ' .. repeat('v', 57)
\ .. '23 ' .. repeat('w', 57)
\ .. '24 ' .. repeat('x', 57)
\ .. '25 ' .. repeat('y', 57)
\ .. '26 ' .. repeat('z', 57)
\ )
set scrolloff=10 set scrolloff=10
normal gg10gj normal gg10gj
call assert_equal(8, winline()) call assert_equal(6, winline())
normal 10gj normal 10gj
call assert_equal(10, winline()) call assert_equal(6, winline())
normal 10gk normal 10gk
call assert_equal(3, winline()) call assert_equal(6, winline())
normal 0
call assert_equal(1, winline())
normal $
call assert_equal(10, winline())
set scrolloff& set scrolloff&
close! close!
endfunc endfunc

View File

@ -138,7 +138,7 @@ func Test_number_with_linewrap1()
call s:validate_cursor() call s:validate_cursor()
let lines = s:screen_lines(1, 3) let lines = s:screen_lines(1, 3)
let expect = [ let expect = [
\ "--1 aaaa", \ "<<< aaaa",
\ " aaaa", \ " aaaa",
\ " aaaa", \ " aaaa",
\ ] \ ]

View File

@ -721,7 +721,7 @@ func Test_backupskip()
let &backupskip = backupskip let &backupskip = backupskip
endfunc endfunc
func Test_copy_winopt() func Test_buf_copy_winopt()
set hidden set hidden
" Test copy option from current buffer in window " Test copy option from current buffer in window
@ -775,6 +775,108 @@ func Test_copy_winopt()
set hidden& set hidden&
endfunc endfunc
func Test_split_copy_options()
let values = [
\['cursorbind', 1, 0],
\['fillchars', '"vert:-"', '"' .. &fillchars .. '"'],
\['list', 1, 0],
\['listchars', '"space:-"', '"' .. &listchars .. '"'],
\['number', 1, 0],
\['relativenumber', 1, 0],
\['scrollbind', 1, 0],
\['smoothscroll', 1, 0],
\['virtualedit', '"block"', '"' .. &virtualedit .. '"'],
"\ ['wincolor', '"Search"', '"' .. &wincolor .. '"'],
\['wrap', 0, 1],
\]
if has('linebreak')
let values += [
\['breakindent', 1, 0],
\['breakindentopt', '"min:5"', '"' .. &breakindentopt .. '"'],
\['linebreak', 1, 0],
\['numberwidth', 7, 4],
\['showbreak', '"++"', '"' .. &showbreak .. '"'],
\]
endif
if has('rightleft')
let values += [
\['rightleft', 1, 0],
\['rightleftcmd', '"search"', '"' .. &rightleftcmd .. '"'],
\]
endif
if has('statusline')
let values += [
\['statusline', '"---%f---"', '"' .. &statusline .. '"'],
\]
endif
if has('spell')
let values += [
\['spell', 1, 0],
\]
endif
if has('syntax')
let values += [
\['cursorcolumn', 1, 0],
\['cursorline', 1, 0],
\['cursorlineopt', '"screenline"', '"' .. &cursorlineopt .. '"'],
\['colorcolumn', '"+1"', '"' .. &colorcolumn .. '"'],
\]
endif
if has('diff')
let values += [
\['diff', 1, 0],
\]
endif
if has('conceal')
let values += [
\['concealcursor', '"nv"', '"' .. &concealcursor .. '"'],
\['conceallevel', '3', &conceallevel],
\]
endif
if has('terminal')
let values += [
\['termwinkey', '"<C-X>"', '"' .. &termwinkey .. '"'],
\['termwinsize', '"10x20"', '"' .. &termwinsize .. '"'],
\]
endif
if has('folding')
let values += [
\['foldcolumn', '"5"', &foldcolumn],
\['foldenable', 0, 1],
\['foldexpr', '"2 + 3"', '"' .. &foldexpr .. '"'],
\['foldignore', '"+="', '"' .. &foldignore .. '"'],
\['foldlevel', 4, &foldlevel],
\['foldmarker', '">>,<<"', '"' .. &foldmarker .. '"'],
\['foldmethod', '"marker"', '"' .. &foldmethod .. '"'],
\['foldminlines', 3, &foldminlines],
\['foldnestmax', 17, &foldnestmax],
\['foldtext', '"closed"', '"' .. &foldtext .. '"'],
\]
endif
if has('signs')
let values += [
\['signcolumn', '"number"', '"' .. &signcolumn .. '"'],
\]
endif
" set options to non-default value
for item in values
exe $"let &{item[0]} = {item[1]}"
endfor
" check values are set in new window
split
for item in values
exe $'call assert_equal({item[1]}, &{item[0]}, "{item[0]}")'
endfor
" restore
close
for item in values
exe $"let &{item[0]} = {item[1]}"
endfor
endfunc
func Test_shortmess_F() func Test_shortmess_F()
new new
call assert_match('\[No Name\]', execute('file')) call assert_match('\[No Name\]', execute('file'))

View File

@ -1,4 +1,8 @@
" Test for reset 'scroll' " Test for reset 'scroll' and 'smoothscroll'
source check.vim
source screendump.vim
source mouse.vim
func Test_reset_scroll() func Test_reset_scroll()
let scr = &l:scroll let scr = &l:scroll
@ -51,4 +55,554 @@ func Test_scolloff_even_line_count()
bwipe! bwipe!
endfunc endfunc
func Test_CtrlE_CtrlY_stop_at_end()
enew
call setline(1, ['one', 'two'])
set number
exe "normal \<C-Y>"
call assert_equal([" 1 one "], ScreenLines(1, 10))
exe "normal \<C-E>\<C-E>\<C-E>"
call assert_equal([" 2 two "], ScreenLines(1, 10))
bwipe!
set nonumber
endfunc
func Test_smoothscroll_CtrlE_CtrlY()
CheckScreendump
let lines =<< trim END
vim9script
setline(1, [
'line one',
'word '->repeat(20),
'line three',
'long word '->repeat(7),
'line',
'line',
'line',
])
set smoothscroll
:5
END
call writefile(lines, 'XSmoothScroll', 'D')
let buf = RunVimInTerminal('-S XSmoothScroll', #{rows: 12, cols: 40})
call term_sendkeys(buf, "\<C-E>")
call VerifyScreenDump(buf, 'Test_smoothscroll_1', {})
call term_sendkeys(buf, "\<C-E>")
call VerifyScreenDump(buf, 'Test_smoothscroll_2', {})
call term_sendkeys(buf, "\<C-E>")
call VerifyScreenDump(buf, 'Test_smoothscroll_3', {})
call term_sendkeys(buf, "\<C-E>")
call VerifyScreenDump(buf, 'Test_smoothscroll_4', {})
call term_sendkeys(buf, "\<C-Y>")
call VerifyScreenDump(buf, 'Test_smoothscroll_5', {})
call term_sendkeys(buf, "\<C-Y>")
call VerifyScreenDump(buf, 'Test_smoothscroll_6', {})
call term_sendkeys(buf, "\<C-Y>")
call VerifyScreenDump(buf, 'Test_smoothscroll_7', {})
call term_sendkeys(buf, "\<C-Y>")
call VerifyScreenDump(buf, 'Test_smoothscroll_8', {})
if has('folding')
call term_sendkeys(buf, ":set foldmethod=indent\<CR>")
" move the cursor so we can reuse the same dumps
call term_sendkeys(buf, "5G")
call term_sendkeys(buf, "\<C-E>")
call VerifyScreenDump(buf, 'Test_smoothscroll_1', {})
call term_sendkeys(buf, "\<C-E>")
call VerifyScreenDump(buf, 'Test_smoothscroll_2', {})
call term_sendkeys(buf, "7G")
call term_sendkeys(buf, "\<C-Y>")
call VerifyScreenDump(buf, 'Test_smoothscroll_7', {})
call term_sendkeys(buf, "\<C-Y>")
call VerifyScreenDump(buf, 'Test_smoothscroll_8', {})
endif
call StopVimInTerminal(buf)
endfunc
func Test_smoothscroll_number()
CheckScreendump
let lines =<< trim END
vim9script
setline(1, [
'one ' .. 'word '->repeat(20),
'two ' .. 'long word '->repeat(7),
'line',
'line',
'line',
])
set smoothscroll
set number cpo+=n
:3
def g:DoRel()
set number relativenumber scrolloff=0
:%del
setline(1, [
'one',
'very long text '->repeat(12),
'three',
])
exe "normal 2Gzt\<C-E>"
enddef
END
call writefile(lines, 'XSmoothNumber', 'D')
let buf = RunVimInTerminal('-S XSmoothNumber', #{rows: 12, cols: 40})
call VerifyScreenDump(buf, 'Test_smooth_number_1', {})
call term_sendkeys(buf, "\<C-E>")
call VerifyScreenDump(buf, 'Test_smooth_number_2', {})
call term_sendkeys(buf, "\<C-E>")
call VerifyScreenDump(buf, 'Test_smooth_number_3', {})
call term_sendkeys(buf, ":set cpo-=n\<CR>")
call VerifyScreenDump(buf, 'Test_smooth_number_4', {})
call term_sendkeys(buf, "\<C-Y>")
call VerifyScreenDump(buf, 'Test_smooth_number_5', {})
call term_sendkeys(buf, "\<C-Y>")
call VerifyScreenDump(buf, 'Test_smooth_number_6', {})
call term_sendkeys(buf, ":call DoRel()\<CR>")
call VerifyScreenDump(buf, 'Test_smooth_number_7', {})
call StopVimInTerminal(buf)
endfunc
func Test_smoothscroll_list()
CheckScreendump
let lines =<< trim END
vim9script
set smoothscroll scrolloff=0
set list
setline(1, [
'one',
'very long text '->repeat(12),
'three',
])
exe "normal 2Gzt\<C-E>"
END
call writefile(lines, 'XSmoothList', 'D')
let buf = RunVimInTerminal('-S XSmoothList', #{rows: 8, cols: 40})
call VerifyScreenDump(buf, 'Test_smooth_list_1', {})
call term_sendkeys(buf, ":set listchars+=precedes:#\<CR>")
call VerifyScreenDump(buf, 'Test_smooth_list_2', {})
call StopVimInTerminal(buf)
endfunc
func Test_smoothscroll_diff_mode()
CheckScreendump
let lines =<< trim END
vim9script
var text = 'just some text here'
setline(1, text)
set smoothscroll
diffthis
new
setline(1, text)
set smoothscroll
diffthis
END
call writefile(lines, 'XSmoothDiff', 'D')
let buf = RunVimInTerminal('-S XSmoothDiff', #{rows: 8})
call VerifyScreenDump(buf, 'Test_smooth_diff_1', {})
call term_sendkeys(buf, "\<C-Y>")
call VerifyScreenDump(buf, 'Test_smooth_diff_1', {})
call term_sendkeys(buf, "\<C-E>")
call VerifyScreenDump(buf, 'Test_smooth_diff_1', {})
call StopVimInTerminal(buf)
endfunc
func Test_smoothscroll_wrap_scrolloff_zero()
CheckScreendump
let lines =<< trim END
vim9script
setline(1, ['Line' .. (' with some text'->repeat(7))]->repeat(7))
set smoothscroll scrolloff=0
:3
END
call writefile(lines, 'XSmoothWrap', 'D')
let buf = RunVimInTerminal('-S XSmoothWrap', #{rows: 8, cols: 40})
call VerifyScreenDump(buf, 'Test_smooth_wrap_1', {})
" moving cursor down - whole bottom line shows
call term_sendkeys(buf, "j")
call VerifyScreenDump(buf, 'Test_smooth_wrap_2', {})
call term_sendkeys(buf, "\<C-E>j")
call VerifyScreenDump(buf, 'Test_smooth_wrap_3', {})
call term_sendkeys(buf, "G")
call VerifyScreenDump(buf, 'Test_smooth_wrap_4', {})
" moving cursor up right after the >>> marker - no need to show whole line
call term_sendkeys(buf, "2gj3l2k")
call VerifyScreenDump(buf, 'Test_smooth_wrap_5', {})
" moving cursor up where the >>> marker is - whole top line shows
call term_sendkeys(buf, "2j02k")
call VerifyScreenDump(buf, 'Test_smooth_wrap_6', {})
call StopVimInTerminal(buf)
endfunc
func Test_smoothscroll_wrap_long_line()
CheckScreendump
let lines =<< trim END
vim9script
setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(30)) .. ' end', 'four'])
set smoothscroll scrolloff=0
normal 3G10|zt
END
call writefile(lines, 'XSmoothWrap', 'D')
let buf = RunVimInTerminal('-S XSmoothWrap', #{rows: 6, cols: 40})
call VerifyScreenDump(buf, 'Test_smooth_long_1', {})
" scrolling up, cursor moves screen line down
call term_sendkeys(buf, "\<C-E>")
call VerifyScreenDump(buf, 'Test_smooth_long_2', {})
call term_sendkeys(buf, "5\<C-E>")
call VerifyScreenDump(buf, 'Test_smooth_long_3', {})
" scrolling down, cursor moves screen line up
call term_sendkeys(buf, "5\<C-Y>")
call VerifyScreenDump(buf, 'Test_smooth_long_4', {})
call term_sendkeys(buf, "\<C-Y>")
call VerifyScreenDump(buf, 'Test_smooth_long_5', {})
" 'scrolloff' set to 1, scrolling up, cursor moves screen line down
call term_sendkeys(buf, ":set scrolloff=1\<CR>")
call term_sendkeys(buf, "10|\<C-E>")
call VerifyScreenDump(buf, 'Test_smooth_long_6', {})
" 'scrolloff' set to 1, scrolling down, cursor moves screen line up
call term_sendkeys(buf, "\<C-E>")
call term_sendkeys(buf, "gjgj")
call term_sendkeys(buf, "\<C-Y>")
call VerifyScreenDump(buf, 'Test_smooth_long_7', {})
" 'scrolloff' set to 2, scrolling up, cursor moves screen line down
call term_sendkeys(buf, ":set scrolloff=2\<CR>")
call term_sendkeys(buf, "10|\<C-E>")
call VerifyScreenDump(buf, 'Test_smooth_long_8', {})
" 'scrolloff' set to 2, scrolling down, cursor moves screen line up
call term_sendkeys(buf, "\<C-E>")
call term_sendkeys(buf, "gj")
call term_sendkeys(buf, "\<C-Y>")
call VerifyScreenDump(buf, 'Test_smooth_long_9', {})
" 'scrolloff' set to 0, move cursor down one line.
" Cursor should move properly, and since this is a really long line, it will
" be put on top of the screen.
call term_sendkeys(buf, ":set scrolloff=0\<CR>")
call term_sendkeys(buf, "0j")
call VerifyScreenDump(buf, 'Test_smooth_long_10', {})
" Test zt/zz/zb that they work properly when a long line is above it
call term_sendkeys(buf, "zb")
call VerifyScreenDump(buf, 'Test_smooth_long_11', {})
call term_sendkeys(buf, "zz")
call VerifyScreenDump(buf, 'Test_smooth_long_12', {})
call term_sendkeys(buf, "zt")
call VerifyScreenDump(buf, 'Test_smooth_long_13', {})
" Repeat the step and move the cursor down again.
" This time, use a shorter long line that is barely long enough to span more
" than one window. Note that the cursor is at the bottom this time because
" Vim prefers to do so if we are scrolling a few lines only.
call term_sendkeys(buf, ":call setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(10)) .. ' end', 'four'])\<CR>")
call term_sendkeys(buf, "3Gzt")
call term_sendkeys(buf, "j")
call VerifyScreenDump(buf, 'Test_smooth_long_14', {})
" Repeat the step but this time start it when the line is smooth-scrolled by
" one line. This tests that the offset calculation is still correct and
" still end up scrolling down to the next line with cursor at bottom of
" screen.
call term_sendkeys(buf, "3Gzt")
call term_sendkeys(buf, "\<C-E>j")
call VerifyScreenDump(buf, 'Test_smooth_long_15', {})
call StopVimInTerminal(buf)
endfunc
func Test_smoothscroll_one_long_line()
CheckScreendump
let lines =<< trim END
vim9script
setline(1, 'with lots of text '->repeat(7))
set smoothscroll scrolloff=0
END
call writefile(lines, 'XSmoothOneLong', 'D')
let buf = RunVimInTerminal('-S XSmoothOneLong', #{rows: 6, cols: 40})
call VerifyScreenDump(buf, 'Test_smooth_one_long_1', {})
call term_sendkeys(buf, "\<C-E>")
call VerifyScreenDump(buf, 'Test_smooth_one_long_2', {})
call term_sendkeys(buf, "0")
call VerifyScreenDump(buf, 'Test_smooth_one_long_1', {})
call StopVimInTerminal(buf)
endfunc
func Test_smoothscroll_long_line_showbreak()
CheckScreendump
let lines =<< trim END
vim9script
# a line that spans four screen lines
setline(1, 'with lots of text in one line '->repeat(6))
set smoothscroll scrolloff=0 showbreak=+++\
END
call writefile(lines, 'XSmoothLongShowbreak', 'D')
let buf = RunVimInTerminal('-S XSmoothLongShowbreak', #{rows: 6, cols: 40})
call VerifyScreenDump(buf, 'Test_smooth_long_showbreak_1', {})
call term_sendkeys(buf, "\<C-E>")
call VerifyScreenDump(buf, 'Test_smooth_long_showbreak_2', {})
call term_sendkeys(buf, "0")
call VerifyScreenDump(buf, 'Test_smooth_long_showbreak_1', {})
call StopVimInTerminal(buf)
endfunc
func s:check_col_calc(win_col, win_line, buf_col)
call assert_equal(a:win_col, wincol())
call assert_equal(a:win_line, winline())
call assert_equal(a:buf_col, col('.'))
endfunc
" Test that if the current cursor is on a smooth scrolled line, we correctly
" reposition it. Also check that we don't miscalculate the values by checking
" the consistency between wincol() and col('.') as they are calculated
" separately in code.
func Test_smoothscroll_cursor_position()
call NewWindow(10, 20)
setl smoothscroll wrap
call setline(1, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
call s:check_col_calc(1, 1, 1)
exe "normal \<C-E>"
" Move down another line to avoid blocking the <<< display
call s:check_col_calc(1, 2, 41)
exe "normal \<C-Y>"
call s:check_col_calc(1, 3, 41)
normal gg3l
exe "normal \<C-E>"
" Move down only 1 line when we are out of the range of the <<< display
call s:check_col_calc(4, 1, 24)
exe "normal \<C-Y>"
call s:check_col_calc(4, 2, 24)
normal ggg$
exe "normal \<C-E>"
call s:check_col_calc(20, 1, 40)
exe "normal \<C-Y>"
call s:check_col_calc(20, 2, 40)
normal gg
" Test number, where we have indented lines
setl number
call s:check_col_calc(5, 1, 1)
exe "normal \<C-E>"
" Move down only 1 line when the <<< display is on the number column
call s:check_col_calc(5, 1, 17)
exe "normal \<C-Y>"
call s:check_col_calc(5, 2, 17)
normal ggg$
exe "normal \<C-E>"
call s:check_col_calc(20, 1, 32)
exe "normal \<C-Y>"
call s:check_col_calc(20, 2, 32)
normal gg
setl numberwidth=1
" Move down another line when numberwidth is too short to cover the whole
" <<< display
call s:check_col_calc(3, 1, 1)
exe "normal \<C-E>"
call s:check_col_calc(3, 2, 37)
exe "normal \<C-Y>"
call s:check_col_calc(3, 3, 37)
normal ggl
" Only move 1 line down when we are just past the <<< display
call s:check_col_calc(4, 1, 2)
exe "normal \<C-E>"
call s:check_col_calc(4, 1, 20)
exe "normal \<C-Y>"
call s:check_col_calc(4, 2, 20)
normal gg
setl numberwidth&
" Test number + showbreak, so test that the additional indentation works
setl number showbreak=+++
call s:check_col_calc(5, 1, 1)
exe "normal \<C-E>"
call s:check_col_calc(8, 1, 17)
exe "normal \<C-Y>"
call s:check_col_calc(8, 2, 17)
normal gg
" Test number + cpo+=n mode, where wrapped lines aren't indented
setl number cpo+=n showbreak=
call s:check_col_calc(5, 1, 1)
exe "normal \<C-E>"
call s:check_col_calc(1, 2, 37)
exe "normal \<C-Y>"
call s:check_col_calc(1, 3, 37)
normal gg
bwipe!
endfunc
func Test_smoothscroll_cursor_scrolloff()
call NewWindow(10, 20)
setl smoothscroll wrap
setl scrolloff=3
" 120 chars are 6 screen lines
call setline(1, "abcdefghijklmnopqrstABCDEFGHIJKLMNOPQRSTabcdefghijklmnopqrstABCDEFGHIJKLMNOPQRSTabcdefghijklmnopqrstABCDEFGHIJKLMNOPQRST")
call setline(2, "below")
call s:check_col_calc(1, 1, 1)
" CTRL-E shows "<<<DEFG...", cursor move four lines down
exe "normal \<C-E>"
call s:check_col_calc(1, 4, 81)
" cursor on start of second line, "gk" moves into first line, skipcol doesn't
" change
exe "normal G0gk"
call s:check_col_calc(1, 5, 101)
" move cursor left one window width worth, scrolls one screen line
exe "normal 20h"
call s:check_col_calc(1, 5, 81)
" move cursor left one window width worth, scrolls one screen line
exe "normal 20h"
call s:check_col_calc(1, 4, 61)
" cursor on last line, "gk" should not cause a scroll
set scrolloff=0
normal G0
call s:check_col_calc(1, 7, 1)
normal gk
call s:check_col_calc(1, 6, 101)
bwipe!
endfunc
" Test that mouse picking is still accurate when we have smooth scrolled lines
func Test_smoothscroll_mouse_pos()
CheckNotGui
CheckUnix
let save_mouse = &mouse
"let save_term = &term
"let save_ttymouse = &ttymouse
set mouse=a "term=xterm ttymouse=xterm2
call NewWindow(10, 20)
setl smoothscroll wrap
" First line will wrap to 3 physical lines. 2nd/3rd lines are short lines.
call setline(1, ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "line 2", "line 3"])
func s:check_mouse_click(row, col, buf_row, buf_col)
call MouseLeftClick(a:row, a:col)
call assert_equal(a:col, wincol())
call assert_equal(a:row, winline())
call assert_equal(a:buf_row, line('.'))
call assert_equal(a:buf_col, col('.'))
endfunc
" Check that clicking without scroll works first.
call s:check_mouse_click(3, 5, 1, 45)
call s:check_mouse_click(4, 1, 2, 1)
call s:check_mouse_click(4, 6, 2, 6)
call s:check_mouse_click(5, 1, 3, 1)
call s:check_mouse_click(5, 6, 3, 6)
" Smooth scroll, and checks that this didn't mess up mouse clicking
exe "normal \<C-E>"
call s:check_mouse_click(2, 5, 1, 45)
call s:check_mouse_click(3, 1, 2, 1)
call s:check_mouse_click(3, 6, 2, 6)
call s:check_mouse_click(4, 1, 3, 1)
call s:check_mouse_click(4, 6, 3, 6)
exe "normal \<C-E>"
call s:check_mouse_click(1, 5, 1, 45)
call s:check_mouse_click(2, 1, 2, 1)
call s:check_mouse_click(2, 6, 2, 6)
call s:check_mouse_click(3, 1, 3, 1)
call s:check_mouse_click(3, 6, 3, 6)
" Make a new first line 11 physical lines tall so it's taller than window
" height, to test overflow calculations with really long lines wrapping.
normal gg
call setline(1, "12345678901234567890"->repeat(11))
exe "normal 6\<C-E>"
call s:check_mouse_click(5, 1, 1, 201)
call s:check_mouse_click(6, 1, 2, 1)
call s:check_mouse_click(7, 1, 3, 1)
let &mouse = save_mouse
"let &term = save_term
"let &ttymouse = save_ttymouse
endfunc
" this was dividing by zero
func Test_smoothscrol_zero_width()
CheckScreendump
let lines =<< trim END
winsize 0 0
vsplit
vsplit
vsplit
vsplit
vsplit
sil norm H
set wrap
set smoothscroll
set number
END
call writefile(lines, 'XSmoothScrollZero', 'D')
let buf = RunVimInTerminal('-u NONE -i NONE -n -m -X -Z -e -s -S XSmoothScrollZero', #{rows: 6, cols: 60, wait_for_ruler: 0})
call TermWait(buf, 3000)
call VerifyScreenDump(buf, 'Test_smoothscroll_zero_1', {})
call term_sendkeys(buf, ":sil norm \<C-V>\<C-W>\<C-V>\<C-N>\<CR>")
call VerifyScreenDump(buf, 'Test_smoothscroll_zero_2', {})
call StopVimInTerminal(buf)
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@ -1734,7 +1734,7 @@ func Test_splitkeep_options()
" let &t_WS = save_WS " let &t_WS = save_WS
endfunc endfunc
function Test_splitkeep_cmdwin_cursor_position() func Test_splitkeep_cmdwin_cursor_position()
set splitkeep=screen set splitkeep=screen
call setline(1, range(&lines)) call setline(1, range(&lines))
@ -1759,9 +1759,9 @@ function Test_splitkeep_cmdwin_cursor_position()
%bwipeout! %bwipeout!
set splitkeep& set splitkeep&
endfunction endfunc
function Test_splitkeep_misc() func Test_splitkeep_misc()
set splitkeep=screen set splitkeep=screen
set splitbelow set splitbelow
@ -1794,7 +1794,7 @@ function Test_splitkeep_misc()
set splitkeep& set splitkeep&
endfunc endfunc
function Test_splitkeep_callback() func Test_splitkeep_callback()
CheckScreendump CheckScreendump
let lines =<< trim END let lines =<< trim END
set splitkeep=screen set splitkeep=screen
@ -1827,7 +1827,7 @@ function Test_splitkeep_callback()
call StopVimInTerminal(buf) call StopVimInTerminal(buf)
endfunc endfunc
function Test_splitkeep_fold() func Test_splitkeep_fold()
CheckScreendump CheckScreendump
let lines =<< trim END let lines =<< trim END
@ -1857,9 +1857,9 @@ function Test_splitkeep_fold()
call VerifyScreenDump(buf, 'Test_splitkeep_fold_4', {}) call VerifyScreenDump(buf, 'Test_splitkeep_fold_4', {})
call StopVimInTerminal(buf) call StopVimInTerminal(buf)
endfunction endfunc
function Test_splitkeep_status() func Test_splitkeep_status()
CheckScreendump CheckScreendump
let lines =<< trim END let lines =<< trim END
@ -1877,9 +1877,9 @@ function Test_splitkeep_status()
call VerifyScreenDump(buf, 'Test_splitkeep_status_1', {}) call VerifyScreenDump(buf, 'Test_splitkeep_status_1', {})
call StopVimInTerminal(buf) call StopVimInTerminal(buf)
endfunction endfunc
function Test_new_help_window_on_error() func Test_new_help_window_on_error()
help change.txt help change.txt
execute "normal! /CTRL-@\<CR>" execute "normal! /CTRL-@\<CR>"
silent! execute "normal! \<C-W>]" silent! execute "normal! \<C-W>]"
@ -1889,7 +1889,26 @@ function Test_new_help_window_on_error()
call assert_equal(wincount, winnr('$')) call assert_equal(wincount, winnr('$'))
call assert_equal(expand("<cword>"), "'mod'") call assert_equal(expand("<cword>"), "'mod'")
endfunction endfunc
func Test_smoothscroll_in_zero_width_window()
let save_lines = &lines
let save_columns = &columns
winsize 0 24
set cpo+=n
exe "noremap 0 \<C-W>n\<C-W>L"
norm 000000
set number smoothscroll
exe "norm \<C-Y>"
only!
let &lines = save_lines
let &columns = save_columns
set cpo-=n
unmap 0
set nonumber nosmoothscroll
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab