mirror of
https://github.com/neovim/neovim.git
synced 2026-10-25 23:20:23 -05:00
fix(mouse): mouse=n should not adjust visual selection
Problem:
When mouse=n is set
- Dragging the mouse enters visual mode, and then stops listening for
mouse events.
- Double/Triple/Quad clicking performs selections.
- Clicking in visual mode moves the cursor (though not through the TUI).
Solution:
Explicitly gate mouse actions that affect visual mode with a check for
MOUSE_VISUAL. This matches the behavior described in :help mouse.
> If enabled for "v" (Visual mode) then double-click selects word-wise,
> triple-click makes it line-wise, and quadruple-click makes it
> rectangular block-wise.
(cherry picked from commit 24f7182390)
This commit is contained in:
1 parent
b05675bccc
commit
b490fba786
3 files changed
+195
-19
No files matched your search
+14
-5
@@ -609,9 +609,10 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
|
|||||||
|
|
||||||
pos_T end_visual = { 0 };
|
pos_T end_visual = { 0 };
|
||||||
pos_T start_visual = { 0 };
|
pos_T start_visual = { 0 };
|
||||||
|
bool mouse_can_visual = ui_mouse_has(MOUSE_VISUAL);
|
||||||
if ((State & (MODE_NORMAL | MODE_INSERT))
|
if ((State & (MODE_NORMAL | MODE_INSERT))
|
||||||
&& !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
|
&& !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
|
||||||
if (which_button == MOUSE_LEFT) {
|
if (which_button == MOUSE_LEFT && mouse_can_visual) {
|
||||||
if (is_click) {
|
if (is_click) {
|
||||||
// stop Visual mode for a left click in a window, but not when on a status line
|
// stop Visual mode for a left click in a window, but not when on a status line
|
||||||
if (VIsual_active) {
|
if (VIsual_active) {
|
||||||
@@ -620,7 +621,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
|
|||||||
} else {
|
} else {
|
||||||
jump_flags |= MOUSE_MAY_VIS;
|
jump_flags |= MOUSE_MAY_VIS;
|
||||||
}
|
}
|
||||||
} else if (which_button == MOUSE_RIGHT) {
|
} else if (which_button == MOUSE_RIGHT && mouse_can_visual) {
|
||||||
if (is_click && VIsual_active) {
|
if (is_click && VIsual_active) {
|
||||||
// Remember the start and end of visual before moving the cursor.
|
// Remember the start and end of visual before moving the cursor.
|
||||||
if (lt(curwin->w_cursor, VIsual)) {
|
if (lt(curwin->w_cursor, VIsual)) {
|
||||||
@@ -631,8 +632,10 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
|
|||||||
end_visual = curwin->w_cursor;
|
end_visual = curwin->w_cursor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jump_flags |= MOUSE_FOCUS;
|
|
||||||
jump_flags |= MOUSE_MAY_VIS;
|
jump_flags |= MOUSE_MAY_VIS;
|
||||||
|
jump_flags |= MOUSE_FOCUS;
|
||||||
|
} else if (which_button == MOUSE_RIGHT) {
|
||||||
|
jump_flags |= MOUSE_FOCUS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -650,7 +653,12 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
|
|||||||
// JUMP!
|
// JUMP!
|
||||||
int old_active = VIsual_active;
|
int old_active = VIsual_active;
|
||||||
pos_T save_cursor = curwin->w_cursor;
|
pos_T save_cursor = curwin->w_cursor;
|
||||||
jump_flags = jump_to_mouse(jump_flags, oap == NULL ? NULL : &(oap->inclusive), which_button);
|
|
||||||
|
// Even though we gate *_VIS flags above, we want to make sure the cursor doesn't move
|
||||||
|
// in visual mode unless it is set as a mouse option
|
||||||
|
if (!VIsual_active || mouse_can_visual) {
|
||||||
|
jump_flags = jump_to_mouse(jump_flags, oap == NULL ? NULL : &(oap->inclusive), which_button);
|
||||||
|
}
|
||||||
|
|
||||||
bool moved = (jump_flags & CURSOR_MOVED);
|
bool moved = (jump_flags & CURSOR_MOVED);
|
||||||
bool in_winbar = (jump_flags & MOUSE_WINBAR);
|
bool in_winbar = (jump_flags & MOUSE_WINBAR);
|
||||||
@@ -891,7 +899,8 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
|
|||||||
} else if (in_status_line || in_sep_line) {
|
} else if (in_status_line || in_sep_line) {
|
||||||
// Do nothing if on status line or vertical separator
|
// Do nothing if on status line or vertical separator
|
||||||
// Handle double clicks otherwise
|
// Handle double clicks otherwise
|
||||||
} else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (MODE_NORMAL | MODE_INSERT))) {
|
} else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (MODE_NORMAL | MODE_INSERT))
|
||||||
|
&& mouse_can_visual) {
|
||||||
if (is_click || !VIsual_active) {
|
if (is_click || !VIsual_active) {
|
||||||
if (VIsual_active) {
|
if (VIsual_active) {
|
||||||
orig_cursor = VIsual;
|
orig_cursor = VIsual;
|
||||||
|
|||||||
+26
-14
@@ -630,32 +630,44 @@ void ui_check_mouse(void)
|
|||||||
checkfor = ' '; // don't use mouse for ":!cmd"
|
checkfor = ' '; // don't use mouse for ":!cmd"
|
||||||
}
|
}
|
||||||
|
|
||||||
// mouse should be active if at least one of the following is true:
|
if (ui_mouse_has(checkfor)) {
|
||||||
// - "c" is in 'mouse', or
|
has_mouse = true;
|
||||||
// - 'a' is in 'mouse' and "c" is in MOUSE_A, or
|
}
|
||||||
// - the current buffer is a help file and 'h' is in 'mouse' and we are in a
|
}
|
||||||
// normal editing mode (not at hit-return message).
|
|
||||||
|
// check if 'mouse' is active for the given mode
|
||||||
|
//
|
||||||
|
// mouse should be active if at least one of the following is true:
|
||||||
|
// - "mode" is in 'mouse', or
|
||||||
|
// - 'a' is in 'mouse' and "mode" is in MOUSE_A, or
|
||||||
|
// - the current buffer is a help file and 'h' is in 'mouse' and we are in a
|
||||||
|
// normal editing mode (not at hit-return message).
|
||||||
|
bool ui_mouse_has(int mode)
|
||||||
|
{
|
||||||
for (char *p = p_mouse; *p; p++) {
|
for (char *p = p_mouse; *p; p++) {
|
||||||
switch (*p) {
|
switch (*p) {
|
||||||
case 'a':
|
case 'a':
|
||||||
if (vim_strchr(MOUSE_A, checkfor) != NULL) {
|
if (vim_strchr(MOUSE_A, mode) != NULL) {
|
||||||
has_mouse = true;
|
return true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case MOUSE_HELP:
|
case MOUSE_HELP:
|
||||||
if (checkfor != MOUSE_RETURN && curbuf->b_help) {
|
if (mode != MOUSE_RETURN && curbuf->b_help) {
|
||||||
has_mouse = true;
|
return true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (checkfor == *p) {
|
if (mode == *p) {
|
||||||
has_mouse = true;
|
return true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if current mode has changed.
|
/// Check if current mode has changed.
|
||||||
|
|||||||
@@ -111,6 +111,63 @@ describe('ui/mouse/input', function()
|
|||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('double left click stays in normal mode if mouse does not contain v', function()
|
||||||
|
api.nvim_set_option_value('mouse', 'n', {})
|
||||||
|
|
||||||
|
feed('<LeftMouse><0,0>')
|
||||||
|
feed('<LeftRelease><0,0>')
|
||||||
|
feed('<LeftMouse><0,0>')
|
||||||
|
feed('<LeftRelease><0,0>')
|
||||||
|
screen:expect({
|
||||||
|
any = {
|
||||||
|
'%^testing',
|
||||||
|
'mouse',
|
||||||
|
'support and selection',
|
||||||
|
},
|
||||||
|
mode = 'normal',
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('triple left click stays in normal mode if mouse does not contain v', function()
|
||||||
|
api.nvim_set_option_value('mouse', 'n', {})
|
||||||
|
|
||||||
|
feed('<LeftMouse><0,0>')
|
||||||
|
feed('<LeftRelease><0,0>')
|
||||||
|
feed('<LeftMouse><0,0>')
|
||||||
|
feed('<LeftRelease><0,0>')
|
||||||
|
feed('<LeftMouse><0,0>')
|
||||||
|
feed('<LeftRelease><0,0>')
|
||||||
|
screen:expect({
|
||||||
|
any = {
|
||||||
|
'%^testing',
|
||||||
|
'mouse',
|
||||||
|
'support and selection',
|
||||||
|
},
|
||||||
|
mode = 'normal',
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('quadruple left click stays in normal mode if mouse does not contain v', function()
|
||||||
|
api.nvim_set_option_value('mouse', 'n', {})
|
||||||
|
|
||||||
|
feed('<LeftMouse><0,0>')
|
||||||
|
feed('<LeftRelease><0,0>')
|
||||||
|
feed('<LeftMouse><0,0>')
|
||||||
|
feed('<LeftRelease><0,0>')
|
||||||
|
feed('<LeftMouse><0,0>')
|
||||||
|
feed('<LeftRelease><0,0>')
|
||||||
|
feed('<LeftMouse><0,0>')
|
||||||
|
feed('<LeftRelease><0,0>')
|
||||||
|
screen:expect({
|
||||||
|
any = {
|
||||||
|
'%^testing',
|
||||||
|
'mouse',
|
||||||
|
'support and selection',
|
||||||
|
},
|
||||||
|
mode = 'normal',
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
describe('tab drag', function()
|
describe('tab drag', function()
|
||||||
it('in tabline on filler space moves tab to the end', function()
|
it('in tabline on filler space moves tab to the end', function()
|
||||||
feed_command('%delete')
|
feed_command('%delete')
|
||||||
@@ -579,6 +636,104 @@ describe('ui/mouse/input', function()
|
|||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('left drag moves cursor if mouse does not contain v', function()
|
||||||
|
api.nvim_set_option_value('mouse', 'n', {})
|
||||||
|
|
||||||
|
-- drag events must be preceded by a click
|
||||||
|
feed('<LeftMouse><2,1>')
|
||||||
|
screen:expect({
|
||||||
|
any = {
|
||||||
|
'testing',
|
||||||
|
'mo%^use',
|
||||||
|
'support and selection',
|
||||||
|
},
|
||||||
|
mode = 'normal',
|
||||||
|
})
|
||||||
|
feed('<LeftDrag><4,1>')
|
||||||
|
screen:expect({
|
||||||
|
any = {
|
||||||
|
'testing',
|
||||||
|
'mous%^e',
|
||||||
|
'support and selection',
|
||||||
|
},
|
||||||
|
mode = 'normal',
|
||||||
|
})
|
||||||
|
feed('<LeftDrag><2,2>')
|
||||||
|
screen:expect({
|
||||||
|
any = {
|
||||||
|
'testing',
|
||||||
|
'mouse',
|
||||||
|
'su%^pport and selection',
|
||||||
|
},
|
||||||
|
mode = 'normal',
|
||||||
|
})
|
||||||
|
feed('<LeftDrag><0,0>')
|
||||||
|
screen:expect({
|
||||||
|
any = {
|
||||||
|
'%^testing',
|
||||||
|
'mouse',
|
||||||
|
'support and selection',
|
||||||
|
},
|
||||||
|
mode = 'normal',
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('left drag does not adjust existing visual selection if mouse does not contain v', function()
|
||||||
|
api.nvim_set_option_value('mouse', 'n', {})
|
||||||
|
|
||||||
|
feed('gg^vlj')
|
||||||
|
screen:expect({
|
||||||
|
any = {
|
||||||
|
'{17:testing}',
|
||||||
|
'{17:m}%^ouse',
|
||||||
|
'support and selection',
|
||||||
|
'VISUAL',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
-- drag events must be preceded by a click
|
||||||
|
feed('<LeftMouse><2,1>')
|
||||||
|
screen:expect({
|
||||||
|
any = {
|
||||||
|
'{17:testing}',
|
||||||
|
'{17:m}%^ouse',
|
||||||
|
'support and selection',
|
||||||
|
'VISUAL',
|
||||||
|
},
|
||||||
|
unchanged = true,
|
||||||
|
})
|
||||||
|
feed('<LeftDrag><4,1>')
|
||||||
|
screen:expect({
|
||||||
|
any = {
|
||||||
|
'{17:testing}',
|
||||||
|
'{17:m}%^ouse',
|
||||||
|
'support and selection',
|
||||||
|
'VISUAL',
|
||||||
|
},
|
||||||
|
unchanged = true,
|
||||||
|
})
|
||||||
|
feed('<LeftDrag><2,2>')
|
||||||
|
screen:expect({
|
||||||
|
any = {
|
||||||
|
'{17:testing}',
|
||||||
|
'{17:m}%^ouse',
|
||||||
|
'support and selection',
|
||||||
|
'VISUAL',
|
||||||
|
},
|
||||||
|
unchanged = true,
|
||||||
|
})
|
||||||
|
feed('<LeftRelease>')
|
||||||
|
screen:expect({
|
||||||
|
any = {
|
||||||
|
'{17:testing}',
|
||||||
|
'{17:m}%^ouse',
|
||||||
|
'support and selection',
|
||||||
|
'VISUAL',
|
||||||
|
},
|
||||||
|
unchanged = true,
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
it('left drag changes visual selection after tab click', function()
|
it('left drag changes visual selection after tab click', function()
|
||||||
feed_command('silent file foo | tabnew | file bar')
|
feed_command('silent file foo | tabnew | file bar')
|
||||||
insert('this is bar')
|
insert('this is bar')
|
||||||
|
|||||||
Reference in new issue
Block a user