mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 11:28:22 +00:00
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:
@@ -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.
|
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'* *E547*
|
||||||
'mouseshape' 'mouses' string (default "i:beam,r:beam,s:updown,sd:cross,
|
'mouseshape' 'mouses' string (default "i:beam,r:beam,s:updown,sd:cross,
|
||||||
m:no,ml:up-arrow,v:rightup-arrow")
|
m:no,ml:up-arrow,v:rightup-arrow")
|
||||||
|
@@ -785,6 +785,7 @@ Short explanation of each option: *option-list*
|
|||||||
'mousefocus' 'mousef' keyboard focus follows the mouse
|
'mousefocus' 'mousef' keyboard focus follows the mouse
|
||||||
'mousehide' 'mh' hide mouse pointer while typing
|
'mousehide' 'mh' hide mouse pointer while typing
|
||||||
'mousemodel' 'mousem' changes meaning of mouse buttons
|
'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
|
'mouseshape' 'mouses' shape of the mouse pointer in different modes
|
||||||
'mousetime' 'mouset' max time between mouse double-click
|
'mousetime' 'mouset' max time between mouse double-click
|
||||||
'nrformats' 'nf' number formats recognized for CTRL-A command
|
'nrformats' 'nf' number formats recognized for CTRL-A command
|
||||||
|
@@ -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
|
line if the cursor line is about to be scrolled off the screen (similarly to
|
||||||
how the horizontal scrollbar works).
|
how the horizontal scrollbar works).
|
||||||
|
|
||||||
You can modify the default behavior by mapping the keys. For example, to make
|
You can control the number of lines / columns to scroll by using the
|
||||||
the scroll wheel move one line or half a page in Normal mode: >
|
'mousescroll' option. You can also modify the default behavior by mapping
|
||||||
:map <ScrollWheelUp> <C-Y>
|
the keys. For example, to scroll a page at a time in normal mode: >
|
||||||
:map <S-ScrollWheelUp> <C-U>
|
:map <ScrollWheelUp> <C-B>
|
||||||
:map <ScrollWheelDown> <C-E>
|
:map <ScrollWheelDown> <C-F>
|
||||||
:map <S-ScrollWheelDown> <C-D>
|
Scroll keys can also be combined with modifiers such as Shift, Ctrl, and Alt.
|
||||||
You can also use Alt and Ctrl modifiers.
|
|
||||||
|
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:
|
vim:tw=78:ts=8:noet:ft=help:norl:
|
||||||
|
@@ -238,6 +238,7 @@ Options:
|
|||||||
'inccommand' shows interactive results for |:substitute|-like commands
|
'inccommand' shows interactive results for |:substitute|-like commands
|
||||||
and |:command-preview| commands
|
and |:command-preview| commands
|
||||||
'laststatus' global statusline support
|
'laststatus' global statusline support
|
||||||
|
'mousescroll' amount to scroll by when scrolling with a mouse
|
||||||
'pumblend' pseudo-transparent popupmenu
|
'pumblend' pseudo-transparent popupmenu
|
||||||
'scrollback'
|
'scrollback'
|
||||||
'signcolumn' supports up to 9 dynamic/fixed columns
|
'signcolumn' supports up to 9 dynamic/fixed columns
|
||||||
|
@@ -8557,14 +8557,12 @@ static void ins_mousescroll(int dir)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Don't scroll the window in which completion is being done.
|
// Don't scroll the window in which completion is being done.
|
||||||
if (!pum_visible()
|
if (!pum_visible() || curwin != old_curwin) {
|
||||||
|| curwin != old_curwin) {
|
|
||||||
if (dir == MSCR_DOWN || dir == MSCR_UP) {
|
if (dir == MSCR_DOWN || dir == MSCR_UP) {
|
||||||
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
|
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
|
||||||
scroll_redraw(dir,
|
scroll_redraw(dir, (long)(curwin->w_botline - curwin->w_topline));
|
||||||
(curwin->w_botline - curwin->w_topline));
|
|
||||||
} else {
|
} else {
|
||||||
scroll_redraw(dir, 3L);
|
scroll_redraw(dir, p_mousescroll_vert);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mouse_scroll_horiz(dir);
|
mouse_scroll_horiz(dir);
|
||||||
|
@@ -688,7 +688,7 @@ bool mouse_scroll_horiz(int dir)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int step = 6;
|
int step = (int)p_mousescroll_hor;
|
||||||
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
|
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
|
||||||
step = curwin->w_width_inner;
|
step = curwin->w_width_inner;
|
||||||
}
|
}
|
||||||
|
@@ -3392,8 +3392,8 @@ static void nv_mousescroll(cmdarg_T *cap)
|
|||||||
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
|
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
|
||||||
(void)onepage(cap->arg ? FORWARD : BACKWARD, 1L);
|
(void)onepage(cap->arg ? FORWARD : BACKWARD, 1L);
|
||||||
} else {
|
} else {
|
||||||
cap->count1 = 3;
|
cap->count1 = p_mousescroll_vert;
|
||||||
cap->count0 = 3;
|
cap->count0 = p_mousescroll_vert;
|
||||||
nv_scroll_line(cap);
|
nv_scroll_line(cap);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@@ -2366,6 +2366,69 @@ static bool valid_spellfile(const char_u *val)
|
|||||||
return true;
|
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.
|
/// Handle string options that need some action to perform when changed.
|
||||||
/// Returns NULL for success, or an error message for an error.
|
/// 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) {
|
if (check_opt_strings(p_mousem, p_mousem_values, false) != OK) {
|
||||||
errmsg = e_invarg;
|
errmsg = e_invarg;
|
||||||
}
|
}
|
||||||
|
} else if (varp == &p_mousescroll) { // 'mousescroll'
|
||||||
|
errmsg = check_mousescroll((char *)p_mousescroll);
|
||||||
} else if (varp == &p_swb) { // 'switchbuf'
|
} else if (varp == &p_swb) { // 'switchbuf'
|
||||||
if (opt_strings_flags(p_swb, p_swb_values, &swb_flags, true) != OK) {
|
if (opt_strings_flags(p_swb, p_swb_values, &swb_flags, true) != OK) {
|
||||||
errmsg = e_invarg;
|
errmsg = e_invarg;
|
||||||
|
@@ -153,6 +153,11 @@
|
|||||||
#define MOUSE_NONE ' ' // don't use Visual selection
|
#define MOUSE_NONE ' ' // don't use Visual selection
|
||||||
#define MOUSE_NONEF 'x' // forced modeless 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'
|
#define COCU_ALL "nvic" // flags for 'concealcursor'
|
||||||
|
|
||||||
/// characters for p_shm option:
|
/// characters for p_shm option:
|
||||||
@@ -528,6 +533,9 @@ EXTERN long p_mls; // 'modelines'
|
|||||||
EXTERN char_u *p_mouse; // 'mouse'
|
EXTERN char_u *p_mouse; // 'mouse'
|
||||||
EXTERN char_u *p_mousem; // 'mousemodel'
|
EXTERN char_u *p_mousem; // 'mousemodel'
|
||||||
EXTERN int p_mousef; // 'mousefocus'
|
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 long p_mouset; // 'mousetime'
|
||||||
EXTERN int p_more; // 'more'
|
EXTERN int p_more; // 'more'
|
||||||
EXTERN char_u *p_opfunc; // 'operatorfunc'
|
EXTERN char_u *p_opfunc; // 'operatorfunc'
|
||||||
|
@@ -1621,6 +1621,14 @@ return {
|
|||||||
varname='p_mousem',
|
varname='p_mousem',
|
||||||
defaults={if_true="extend"}
|
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',
|
full_name='mouseshape', abbreviation='mouses',
|
||||||
short_desc=N_("shape of the mouse pointer in different modes"),
|
short_desc=N_("shape of the mouse pointer in different modes"),
|
||||||
|
151
test/functional/options/mousescroll_spec.lua
Normal file
151
test/functional/options/mousescroll_spec.lua
Normal 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)
|
Reference in New Issue
Block a user