feat: add 'mousescroll' option (#12355)

Add 'mousescroll' option to control how many lines to scroll by when a
mouse wheel keycode is received. The mousescroll option controls both
horizontal and vertical scrolling. The option is a string in the format:

    set mousescroll=direction:count,direction:count

Where direction is either "ver" or "hor", and count is a non negative
integer. If a direction is omitted, a default value is used. The default
values remain unchanged, that is 3 for vertical scrolling, and 6 for
horizontal scrolling. As such, the mousescroll default is "ver:3,hor:6".

Add mousescroll documentation
 - Add option documentation in options.txt
 - Add brief summary in quickref.txt

Update :help scroll-mouse-wheel
 - Mention mousescroll option as a means of controlling scrolling.
 - Remove obsolete suggestion to map scroll wheel keys to <C-U> to
   scroll by a single line -- users should prefer the mousescroll option.
 - Add some information about the consequences of remapping scroll wheel
   keys (they lose their magic ability to affect inactive windows).

Update :help vim-differences
 - Add brief mousescroll summary under Options

Add mousescroll tests
 - Test option validation
 - Test default mousescroll value and behavior
 - Test fallback to default values
 - Test mouse vertical and horizontal scrolling in normal mode
 - Test mouse vertical and horizontal scrolling in insert mode
This commit is contained in:
Jay 2022-07-06 12:34:24 +01:00 committed by GitHub
parent 9ced054134
commit 93c8fe77cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 271 additions and 15 deletions

View File

@ -4210,6 +4210,26 @@ A jump table for the options with a short description can be found at |Q_op|.
The 'mousemodel' option is set by the |:behave| command.
*mousescroll*
'mousescroll' string (default "ver:3,hor:6")
global
This option controls the number of lines / columns to scroll by when
scrolling with a mouse. The option is a comma separated list of parts.
Each part consists of a direction and a count as follows:
direction:count,direction:count
Direction is one of either "hor" or "ver", "hor" controls horizontal
scrolling and "ver" controls vertical scrolling. Count sets the amount
to scroll by for the given direction, it should be a non negative
integer. Each direction should be set at most once. If a direction
is omitted, a default value is used (6 for horizontal scrolling and 3
for vertical scrolling). You can disable mouse scrolling by using
a count of 0.
Example: >
:set mousescroll=ver:5,hor:2
< Will make Nvim scroll 5 lines at a time when scrolling vertically, and
scroll 2 columns at a time when scrolling horizontally.
*'mouseshape'* *'mouses'* *E547*
'mouseshape' 'mouses' string (default "i:beam,r:beam,s:updown,sd:cross,
m:no,ml:up-arrow,v:rightup-arrow")

View File

@ -785,6 +785,7 @@ Short explanation of each option: *option-list*
'mousefocus' 'mousef' keyboard focus follows the mouse
'mousehide' 'mh' hide mouse pointer while typing
'mousemodel' 'mousem' changes meaning of mouse buttons
'mousescroll' amount to scroll by when scrolling with a mouse
'mouseshape' 'mouses' shape of the mouse pointer in different modes
'mousetime' 'mouset' max time between mouse double-click
'nrformats' 'nf' number formats recognized for CTRL-A command

View File

@ -239,12 +239,16 @@ the "h" flag in 'guioptions' is set, the cursor moves to the longest visible
line if the cursor line is about to be scrolled off the screen (similarly to
how the horizontal scrollbar works).
You can modify the default behavior by mapping the keys. For example, to make
the scroll wheel move one line or half a page in Normal mode: >
:map <ScrollWheelUp> <C-Y>
:map <S-ScrollWheelUp> <C-U>
:map <ScrollWheelDown> <C-E>
:map <S-ScrollWheelDown> <C-D>
You can also use Alt and Ctrl modifiers.
You can control the number of lines / columns to scroll by using the
'mousescroll' option. You can also modify the default behavior by mapping
the keys. For example, to scroll a page at a time in normal mode: >
:map <ScrollWheelUp> <C-B>
:map <ScrollWheelDown> <C-F>
Scroll keys can also be combined with modifiers such as Shift, Ctrl, and Alt.
When scrolling with a mouse, the window currently under the cursor is
scrolled. This allows you to scroll inactive windows. Note that when scroll
keys are remapped to keyboard keys, the active window is affected regardless
of the current cursor position.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -238,6 +238,7 @@ Options:
'inccommand' shows interactive results for |:substitute|-like commands
and |:command-preview| commands
'laststatus' global statusline support
'mousescroll' amount to scroll by when scrolling with a mouse
'pumblend' pseudo-transparent popupmenu
'scrollback'
'signcolumn' supports up to 9 dynamic/fixed columns

View File

@ -8557,14 +8557,12 @@ static void ins_mousescroll(int dir)
}
// Don't scroll the window in which completion is being done.
if (!pum_visible()
|| curwin != old_curwin) {
if (!pum_visible() || curwin != old_curwin) {
if (dir == MSCR_DOWN || dir == MSCR_UP) {
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
scroll_redraw(dir,
(curwin->w_botline - curwin->w_topline));
scroll_redraw(dir, (long)(curwin->w_botline - curwin->w_topline));
} else {
scroll_redraw(dir, 3L);
scroll_redraw(dir, p_mousescroll_vert);
}
} else {
mouse_scroll_horiz(dir);

View File

@ -688,7 +688,7 @@ bool mouse_scroll_horiz(int dir)
return false;
}
int step = 6;
int step = (int)p_mousescroll_hor;
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
step = curwin->w_width_inner;
}

View File

@ -3392,8 +3392,8 @@ static void nv_mousescroll(cmdarg_T *cap)
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
(void)onepage(cap->arg ? FORWARD : BACKWARD, 1L);
} else {
cap->count1 = 3;
cap->count0 = 3;
cap->count1 = p_mousescroll_vert;
cap->count0 = p_mousescroll_vert;
nv_scroll_line(cap);
}
} else {

View File

@ -2366,6 +2366,69 @@ static bool valid_spellfile(const char_u *val)
return true;
}
/// Handle setting 'mousescroll'.
/// @return error message, NULL if it's OK.
static char *check_mousescroll(char *string)
{
long vertical = -1;
long horizontal = -1;
for (;;) {
char *end = vim_strchr(string, ',');
size_t length = end ? (size_t)(end - string) : STRLEN(string);
// Both "ver:" and "hor:" are 4 bytes long.
// They should be followed by at least one digit.
if (length <= 4) {
return e_invarg;
}
long *direction;
if (memcmp(string, "ver:", 4) == 0) {
direction = &vertical;
} else if (memcmp(string, "hor:", 4) == 0) {
direction = &horizontal;
} else {
return e_invarg;
}
// If the direction has already been set, this is a duplicate.
if (*direction != -1) {
return e_invarg;
}
// Verify that only digits follow the colon.
for (size_t i = 4; i < length; i++) {
if (!ascii_isdigit(string[i])) {
return N_("E548: digit expected");
}
}
string += 4;
*direction = getdigits_int(&string, false, -1);
// Num options are generally kept within the signed int range.
// We know this number won't be negative because we've already checked for
// a minus sign. We'll allow 0 as a means of disabling mouse scrolling.
if (*direction == -1) {
return e_invarg;
}
if (!end) {
break;
}
string = end + 1;
}
// If a direction wasn't set, fallback to the default value.
p_mousescroll_vert = (vertical == -1) ? MOUSESCROLL_VERT_DFLT : vertical;
p_mousescroll_hor = (horizontal == -1) ? MOUSESCROLL_HOR_DFLT : horizontal;
return NULL;
}
/// Handle string options that need some action to perform when changed.
/// Returns NULL for success, or an error message for an error.
///
@ -2859,6 +2922,8 @@ ambw_end:
if (check_opt_strings(p_mousem, p_mousem_values, false) != OK) {
errmsg = e_invarg;
}
} else if (varp == &p_mousescroll) { // 'mousescroll'
errmsg = check_mousescroll((char *)p_mousescroll);
} else if (varp == &p_swb) { // 'switchbuf'
if (opt_strings_flags(p_swb, p_swb_values, &swb_flags, true) != OK) {
errmsg = e_invarg;

View File

@ -153,6 +153,11 @@
#define MOUSE_NONE ' ' // don't use Visual selection
#define MOUSE_NONEF 'x' // forced modeless selection
// default vertical and horizontal mouse scroll values.
// Note: This should be in sync with the default mousescroll option.
#define MOUSESCROLL_VERT_DFLT 3
#define MOUSESCROLL_HOR_DFLT 6
#define COCU_ALL "nvic" // flags for 'concealcursor'
/// characters for p_shm option:
@ -528,6 +533,9 @@ EXTERN long p_mls; // 'modelines'
EXTERN char_u *p_mouse; // 'mouse'
EXTERN char_u *p_mousem; // 'mousemodel'
EXTERN int p_mousef; // 'mousefocus'
EXTERN char_u *p_mousescroll; // 'mousescroll'
EXTERN long p_mousescroll_vert INIT(= MOUSESCROLL_VERT_DFLT);
EXTERN long p_mousescroll_hor INIT(= MOUSESCROLL_HOR_DFLT);
EXTERN long p_mouset; // 'mousetime'
EXTERN int p_more; // 'more'
EXTERN char_u *p_opfunc; // 'operatorfunc'

View File

@ -1621,6 +1621,14 @@ return {
varname='p_mousem',
defaults={if_true="extend"}
},
{
full_name='mousescroll',
short_desc=N_("amount to scroll by when scrolling with a mouse"),
type='string', list='comma', scope={'global'},
vi_def=true,
varname='p_mousescroll',
defaults={if_true="ver:3,hor:6"}
},
{
full_name='mouseshape', abbreviation='mouses',
short_desc=N_("shape of the mouse pointer in different modes"),

View File

@ -0,0 +1,151 @@
local helpers = require('test.functional.helpers')(after_each)
local command = helpers.command
local clear = helpers.clear
local eval = helpers.eval
local eq = helpers.eq
local exc_exec = helpers.exc_exec
local feed = helpers.feed
local scroll = function(direction)
return helpers.request('nvim_input_mouse', 'wheel', direction, '', 0, 2, 2)
end
local screenrow = function()
return helpers.call('screenrow')
end
local screencol = function()
return helpers.call('screencol')
end
describe("'mousescroll'", function()
local invalid_arg = 'Vim(set):E474: Invalid argument: mousescroll='
local digit_expected = 'Vim(set):E548: digit expected: mousescroll='
local function should_fail(val, errorstr)
eq(errorstr..val, exc_exec('set mousescroll='..val))
end
local function should_succeed(val)
eq(0, exc_exec('set mousescroll='..val))
end
before_each(function()
clear()
command('set nowrap lines=20 columns=20 virtualedit=all')
feed('100o<Esc>50G10|')
end)
it('handles invalid values', function()
should_fail('', invalid_arg) -- empty string
should_fail('foo:123', invalid_arg) -- unknown direction
should_fail('hor:1,hor:2', invalid_arg) -- duplicate direction
should_fail('ver:99999999999999999999', invalid_arg) -- integer overflow
should_fail('ver:bar', digit_expected) -- expected digit
should_fail('ver:-1', digit_expected) -- negative count
end)
it('handles valid values', function()
should_succeed('hor:1,ver:1') -- both directions set
should_succeed('hor:1') -- only horizontal
should_succeed('ver:1') -- only vertical
should_succeed('hor:0,ver:0') -- zero
should_succeed('hor:2147483647') -- large count
end)
it('default set correctly', function()
eq('ver:3,hor:6', eval('&mousescroll'))
eq(10, screenrow())
scroll('up')
eq(13, screenrow())
scroll('down')
eq(10, screenrow())
eq(10, screencol())
scroll('right')
eq(4, screencol())
scroll('left')
eq(10, screencol())
end)
it('vertical scrolling falls back to default value', function()
command('set mousescroll=hor:1')
eq(10, screenrow())
scroll('up')
eq(13, screenrow())
end)
it('horizontal scrolling falls back to default value', function()
command('set mousescroll=ver:1')
eq(10, screencol())
scroll('right')
eq(4, screencol())
end)
it('count of zero disables mouse scrolling', function()
command('set mousescroll=hor:0,ver:0')
eq(10, screenrow())
scroll('up')
eq(10, screenrow())
scroll('down')
eq(10, screenrow())
eq(10, screencol())
scroll('right')
eq(10, screencol())
scroll('left')
eq(10, screencol())
end)
local test_vertical_scrolling = function()
eq(10, screenrow())
command('set mousescroll=ver:1')
scroll('up')
eq(11, screenrow())
command('set mousescroll=ver:2')
scroll('down')
eq(9, screenrow())
command('set mousescroll=ver:5')
scroll('up')
eq(14, screenrow())
end
it('controls vertical scrolling in normal mode', function()
test_vertical_scrolling()
end)
it('controls vertical scrolling in insert mode', function()
feed('i')
test_vertical_scrolling()
end)
local test_horizontal_scrolling = function()
eq(10, screencol())
command('set mousescroll=hor:1')
scroll('right')
eq(9, screencol())
command('set mousescroll=hor:3')
scroll('right')
eq(6, screencol())
command('set mousescroll=hor:2')
scroll('left')
eq(8, screencol())
end
it('controls horizontal scrolling in normal mode', function()
test_horizontal_scrolling()
end)
it('controls horizontal scrolling in insert mode', function()
feed('i')
test_horizontal_scrolling()
end)
end)