Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions runtime/doc/options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
1 change: 1 addition & 0 deletions runtime/doc/quickref.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 11 additions & 7 deletions runtime/doc/scroll.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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:
1 change: 1 addition & 0 deletions runtime/doc/vim_diff.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 3 additions & 5 deletions src/nvim/edit.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/nvim/mouse.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
4 changes: 2 additions & 2 deletions src/nvim/normal.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
65 changes: 65 additions & 0 deletions src/nvim/option.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -2859,6 +2922,8 @@ static char *did_set_string_option(int opt_idx, char_u **varp, bool new_value_al
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;
Expand Down
8 changes: 8 additions & 0 deletions src/nvim/option_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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'
Expand Down
8 changes: 8 additions & 0 deletions src/nvim/options.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
151 changes: 151 additions & 0 deletions test/functional/options/mousescroll_spec.lua
Original file line number Diff line number Diff line change
@@ -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)