aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/options.txt20
-rw-r--r--runtime/doc/quickref.txt1
-rw-r--r--runtime/doc/scroll.txt18
-rw-r--r--runtime/doc/vim_diff.txt1
-rw-r--r--src/nvim/edit.c8
-rw-r--r--src/nvim/mouse.c2
-rw-r--r--src/nvim/normal.c4
-rw-r--r--src/nvim/option.c65
-rw-r--r--src/nvim/option_defs.h8
-rw-r--r--src/nvim/options.lua8
-rw-r--r--test/functional/options/mousescroll_spec.lua151
11 files changed, 271 insertions, 15 deletions
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index eb377697e9..74ba353e0a 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -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")
diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt
index a380088e98..6f16db5cc2 100644
--- a/runtime/doc/quickref.txt
+++ b/runtime/doc/quickref.txt
@@ -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
diff --git a/runtime/doc/scroll.txt b/runtime/doc/scroll.txt
index 7d1da3b409..170c87a1a4 100644
--- a/runtime/doc/scroll.txt
+++ b/runtime/doc/scroll.txt
@@ -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:
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 4b26e5501c..8e853aaf9e 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -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
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 26422f06b1..06f6d7f97b 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -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);
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 3aee20dc7b..2ea108d3df 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -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;
}
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index ed624c0c9f..f871b6a884 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -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 {
diff --git a/src/nvim/option.c b/src/nvim/option.c
index bcaa4bb9b8..a3bd960fa5 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -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;
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 531527ea3c..237288fbad 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -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'
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index a0fbf8d9f0..addc08d560 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -1622,6 +1622,14 @@ return {
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"),
type='string', list='onecomma', scope={'global'},
diff --git a/test/functional/options/mousescroll_spec.lua b/test/functional/options/mousescroll_spec.lua
new file mode 100644
index 0000000000..2c9b2d175e
--- /dev/null
+++ b/test/functional/options/mousescroll_spec.lua
@@ -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)