aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/mouse.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-11-29 21:52:58 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-11-29 21:52:58 +0000
commit931bffbda3668ddc609fc1da8f9eb576b170aa52 (patch)
treed8c1843a95da5ea0bb4acc09f7e37843d9995c86 /src/nvim/mouse.c
parent142d9041391780ac15b89886a54015fdc5c73995 (diff)
parent4a8bf24ac690004aedf5540fa440e788459e5e34 (diff)
downloadrneovim-userreg.tar.gz
rneovim-userreg.tar.bz2
rneovim-userreg.zip
Merge remote-tracking branch 'upstream/master' into userreguserreg
Diffstat (limited to 'src/nvim/mouse.c')
-rw-r--r--src/nvim/mouse.c737
1 files changed, 437 insertions, 300 deletions
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 950b025e53..8fe3864424 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -1,31 +1,30 @@
-// This is an open source non-commercial project. Dear PVS-Studio, please check
-// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
-#include "nvim/ascii.h"
+#include "nvim/ascii_defs.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/drawscreen.h"
+#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
-#include "nvim/eval/typval_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/fold.h"
+#include "nvim/func_attr.h"
#include "nvim/getchar.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/keycodes.h"
-#include "nvim/macros.h"
+#include "nvim/macros_defs.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
+#include "nvim/memory.h"
#include "nvim/menu.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
@@ -33,18 +32,18 @@
#include "nvim/normal.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/option_vars.h"
#include "nvim/plines.h"
-#include "nvim/pos.h"
-#include "nvim/screen.h"
+#include "nvim/popupmenu.h"
+#include "nvim/pos_defs.h"
#include "nvim/search.h"
#include "nvim/state.h"
#include "nvim/statusline.h"
#include "nvim/strings.h"
-#include "nvim/syntax.h"
-#include "nvim/types.h"
+#include "nvim/types_defs.h"
#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
-#include "nvim/vim.h"
+#include "nvim/vim_defs.h"
#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -86,15 +85,11 @@ static int get_mouse_class(char *p)
/// Move "pos" back to the start of the word it's in.
static void find_start_of_word(pos_T *pos)
{
- char *line;
- int cclass;
- int col;
-
- line = ml_get(pos->lnum);
- cclass = get_mouse_class(line + pos->col);
+ char *line = ml_get(pos->lnum);
+ int cclass = get_mouse_class(line + pos->col);
while (pos->col > 0) {
- col = pos->col - 1;
+ int col = pos->col - 1;
col -= utf_head_off(line, line + col);
if (get_mouse_class(line + col) != cclass) {
break;
@@ -107,18 +102,14 @@ static void find_start_of_word(pos_T *pos)
/// When 'selection' is "exclusive", the position is just after the word.
static void find_end_of_word(pos_T *pos)
{
- char *line;
- int cclass;
- int col;
-
- line = ml_get(pos->lnum);
+ char *line = ml_get(pos->lnum);
if (*p_sel == 'e' && pos->col > 0) {
pos->col--;
pos->col -= utf_head_off(line, line + pos->col);
}
- cclass = get_mouse_class(line + pos->col);
+ int cclass = get_mouse_class(line + pos->col);
while (line[pos->col] != NUL) {
- col = pos->col + utfc_ptr2len(line + pos->col);
+ int col = pos->col + utfc_ptr2len(line + pos->col);
if (get_mouse_class(line + col) != cclass) {
if (*p_sel == 'e') {
pos->col = col;
@@ -143,6 +134,8 @@ static void move_tab_to_mouse(void)
}
}
+static bool got_click = false; // got a click some time back
+
/// Call click definition function for column "col" in the "click_defs" array for button
/// "which_button".
static void call_click_def_func(StlClickDefinition *click_defs, int col, int which_button)
@@ -198,6 +191,8 @@ static void call_click_def_func(StlClickDefinition *click_defs, int col, int whi
typval_T rettv;
(void)call_vim_function(click_defs[col].func, ARRAY_SIZE(argv), argv, &rettv);
tv_clear(&rettv);
+ // Make sure next click does not register as drag when callback absorbs the release event.
+ got_click = false;
}
/// Translate window coordinates to buffer position without any side effects.
@@ -218,27 +213,37 @@ static int get_fpos_of_mouse(pos_T *mpos)
if (wp == NULL) {
return IN_UNKNOWN;
}
+ int winrow = row;
+ int wincol = col;
+
+ // compute the position in the buffer line from the posn on the screen
+ bool below_buffer = mouse_comp_pos(wp, &row, &col, &mpos->lnum);
+
+ if (!below_buffer && *wp->w_p_stc != NUL
+ && (wp->w_p_rl
+ ? wincol >= wp->w_width_inner - win_col_off(wp)
+ : wincol < win_col_off(wp))) {
+ return MOUSE_STATUSCOL;
+ }
// winpos and height may change in win_enter()!
- if (row + wp->w_winbar_height >= wp->w_height) { // In (or below) status line
+ if (winrow >= wp->w_height_inner) { // In (or below) status line
return IN_STATUS_LINE;
}
- if (col >= wp->w_width) { // In vertical separator line
- return IN_SEP_LINE;
- }
- if (wp != curwin) {
- return IN_UNKNOWN;
+ if (winrow == -1 && wp->w_winbar_height != 0) {
+ return MOUSE_WINBAR;
}
- // compute the position in the buffer line from the posn on the screen
- if (mouse_comp_pos(curwin, &row, &col, &mpos->lnum)) {
- return IN_STATUS_LINE; // past bottom
+ if (wincol >= wp->w_width_inner) { // In vertical separator line
+ return IN_SEP_LINE;
}
- mpos->col = vcol2col(wp, mpos->lnum, col);
+ if (wp != curwin || below_buffer) {
+ return IN_UNKNOWN;
+ }
- mpos->coladd = 0;
+ mpos->col = vcol2col(wp, mpos->lnum, col, &mpos->coladd);
return IN_BUFFER;
}
@@ -281,10 +286,8 @@ static int get_fpos_of_mouse(pos_T *mpos)
/// @param fixindent PUT_FIXINDENT if fixing indent necessary
///
/// @return true if start_arrow() should be called for edit mode.
-bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
+bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
{
- static bool got_click = false; // got a click some time back
-
int which_button; // MOUSE_LEFT, _MIDDLE or _RIGHT
bool is_click; // If false it's a drag or release event
bool is_drag; // If true it's a drag event
@@ -296,20 +299,18 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
bool in_status_line; // mouse in status line
static bool in_tab_line = false; // mouse clicked in tab line
bool in_sep_line; // mouse in vertical separator line
- int c1, c2;
- pos_T save_cursor;
+ int c1;
win_T *old_curwin = curwin;
static pos_T orig_cursor;
colnr_T leftcol, rightcol;
pos_T end_visual;
- long diff;
int old_active = VIsual_active;
int old_mode = VIsual_mode;
int regname;
- save_cursor = curwin->w_cursor;
+ pos_T save_cursor = curwin->w_cursor;
- for (;;) {
+ while (true) {
which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
if (is_drag) {
// If the next character is the same mouse event then use that
@@ -367,7 +368,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
stuffnumReadbuff(count);
}
stuffcharReadbuff(Ctrl_T);
- got_click = false; // ignore drag&release now
+ got_click = false; // ignore drag&release now
return false;
}
@@ -449,7 +450,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
if ((State & REPLACE_FLAG) && !yank_register_mline(regname)) {
insert_reg(regname, true);
} else {
- do_put(regname, NULL, BACKWARD, 1L,
+ do_put(regname, NULL, BACKWARD, 1,
(fixindent ? PUT_FIXINDENT : 0) | PUT_CURSEND);
// Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r
@@ -538,6 +539,11 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
// shift-left button -> right button
// alt-left button -> alt-right button
if (mouse_model_popup()) {
+ pos_T m_pos;
+ int m_pos_flag = get_fpos_of_mouse(&m_pos);
+ if (m_pos_flag & (IN_STATUS_LINE|MOUSE_WINBAR|MOUSE_STATUSCOL)) {
+ goto popupexit;
+ }
if (which_button == MOUSE_RIGHT
&& !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
if (!is_click) {
@@ -547,17 +553,11 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
}
jump_flags = 0;
if (strcmp(p_mousem, "popup_setpos") == 0) {
- // First set the cursor position before showing the popup
- // menu.
+ // First set the cursor position before showing the popup menu.
if (VIsual_active) {
- pos_T m_pos;
- // set MOUSE_MAY_STOP_VIS if we are outside the
- // selection or the current window (might have false
- // negative here)
- if (mouse_row < curwin->w_winrow
- || mouse_row > (curwin->w_winrow + curwin->w_height)) {
- jump_flags = MOUSE_MAY_STOP_VIS;
- } else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER) {
+ // set MOUSE_MAY_STOP_VIS if we are outside the selection
+ // or the current window (might have false negative here)
+ if (m_pos_flag != IN_BUFFER) {
jump_flags = MOUSE_MAY_STOP_VIS;
} else {
if (VIsual_mode == 'V') {
@@ -601,6 +601,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
mod_mask &= ~MOD_MASK_SHIFT;
}
}
+popupexit:
if ((State & (MODE_NORMAL | MODE_INSERT))
&& !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
@@ -652,7 +653,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
in_sep_line = (jump_flags & IN_SEP_LINE);
if ((in_winbar || in_status_line || in_statuscol) && is_click) {
- // Handle click event on window bar or status lin
+ // Handle click event on window bar, status line or status column
int click_grid = mouse_grid;
int click_row = mouse_row;
int click_col = mouse_col;
@@ -672,6 +673,10 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
click_col = mouse_col;
}
+ if (in_statuscol && wp->w_p_rl) {
+ click_col = wp->w_width_inner - click_col - 1;
+ }
+
if (click_defs != NULL) {
switch (click_defs[click_col].type) {
case kStlClickDisabled:
@@ -680,7 +685,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
call_click_def_func(click_defs, click_col, which_button);
break;
default:
- assert(false && "winbar and statusline only support %@ for clicks");
+ assert(false && "winbar, statusline and statuscolumn only support %@ for clicks");
break;
}
}
@@ -703,9 +708,9 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
&& which_button == MOUSE_LEFT) {
// open or close a fold at this line
if (jump_flags & MOUSE_FOLD_OPEN) {
- openFold(curwin->w_cursor, 1L);
+ openFold(curwin->w_cursor, 1);
} else {
- closeFold(curwin->w_cursor, 1L);
+ closeFold(curwin->w_cursor, 1);
}
// don't move the cursor if still in the same window
if (curwin == old_curwin) {
@@ -726,11 +731,12 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
// When dragging the mouse above the window, scroll down.
if (is_drag && mouse_row < 0 && !in_status_line) {
- scroll_redraw(false, 1L);
+ scroll_redraw(false, 1);
mouse_row = 0;
}
if (start_visual.lnum) { // right click in visual mode
+ linenr_T diff;
// When ALT is pressed make Visual mode blockwise.
if (mod_mask & MOD_MASK_ALT) {
VIsual_mode = Ctrl_V;
@@ -800,6 +806,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
// Middle mouse click: Put text before cursor.
if (which_button == MOUSE_MIDDLE) {
+ int c2;
if (regname == 0 && eval_has_provider("clipboard")) {
regname = '*';
}
@@ -835,7 +842,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
} else { // location list window
do_cmdline_cmd(".ll");
}
- got_click = false; // ignore drag&release now
+ got_click = false; // ignore drag&release now
} else if ((mod_mask & MOD_MASK_CTRL)
|| (curbuf->b_help && (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)) {
// Ctrl-Mouse click (or double click in a help window) jumps to the tag
@@ -844,7 +851,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
stuffcharReadbuff(Ctrl_O);
}
stuffcharReadbuff(Ctrl_RSB);
- got_click = false; // ignore drag&release now
+ got_click = false; // ignore drag&release now
} else if ((mod_mask & MOD_MASK_SHIFT)) {
// Shift-Mouse click searches for the next occurrence of the word under
// the mouse pointer
@@ -888,13 +895,13 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
// A double click selects a word or a block.
if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
pos_T *pos = NULL;
- int gc;
if (is_click) {
// If the character under the cursor (skipping white space) is
// not a word character, try finding a match and select a (),
// {}, [], #if/#endif, etc. block.
end_visual = curwin->w_cursor;
+ int gc;
while (gc = gchar_pos(&end_visual), ascii_iswhite(gc)) {
inc(&end_visual);
}
@@ -956,6 +963,146 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
return moved;
}
+void ins_mouse(int c)
+{
+ win_T *old_curwin = curwin;
+
+ undisplay_dollar();
+ pos_T tpos = curwin->w_cursor;
+ if (do_mouse(NULL, c, BACKWARD, 1, 0)) {
+ win_T *new_curwin = curwin;
+
+ if (curwin != old_curwin && win_valid(old_curwin)) {
+ // Mouse took us to another window. We need to go back to the
+ // previous one to stop insert there properly.
+ curwin = old_curwin;
+ curbuf = curwin->w_buffer;
+ if (bt_prompt(curbuf)) {
+ // Restart Insert mode when re-entering the prompt buffer.
+ curbuf->b_prompt_insert = 'A';
+ }
+ }
+ start_arrow(curwin == old_curwin ? &tpos : NULL);
+ if (curwin != new_curwin && win_valid(new_curwin)) {
+ curwin = new_curwin;
+ curbuf = curwin->w_buffer;
+ }
+ set_can_cindent(true);
+ }
+
+ // redraw status lines (in case another window became active)
+ redraw_statuslines();
+}
+
+/// Common mouse wheel scrolling, shared between Insert mode and NV modes.
+/// Default action is to scroll mouse_vert_step lines (or mouse_hor_step columns
+/// depending on the scroll direction) or one page when Shift or Ctrl is used.
+/// Direction is indicated by "cap->arg":
+/// K_MOUSEUP - MSCR_UP
+/// K_MOUSEDOWN - MSCR_DOWN
+/// K_MOUSELEFT - MSCR_LEFT
+/// K_MOUSERIGHT - MSCR_RIGHT
+/// "curwin" may have been changed to the window that should be scrolled and
+/// differ from the window that actually has focus.
+void do_mousescroll(cmdarg_T *cap)
+{
+ bool shift_or_ctrl = mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL);
+
+ if (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN) {
+ // Vertical scrolling
+ if ((State & MODE_NORMAL) && shift_or_ctrl) {
+ // whole page up or down
+ (void)onepage(cap->arg ? FORWARD : BACKWARD, 1);
+ } else {
+ if (shift_or_ctrl) {
+ // whole page up or down
+ cap->count1 = curwin->w_botline - curwin->w_topline;
+ } else {
+ cap->count1 = (int)p_mousescroll_vert;
+ }
+ if (cap->count1 > 0) {
+ cap->count0 = cap->count1;
+ nv_scroll_line(cap);
+ }
+ }
+ } else {
+ // Horizontal scrolling
+ int step = shift_or_ctrl ? curwin->w_width_inner : (int)p_mousescroll_hor;
+ colnr_T leftcol = curwin->w_leftcol + (cap->arg == MSCR_RIGHT ? -step : +step);
+ if (leftcol < 0) {
+ leftcol = 0;
+ }
+ (void)do_mousescroll_horiz(leftcol);
+ }
+}
+
+/// Implementation for scrolling in Insert mode in direction "dir", which is one
+/// of the MSCR_ values.
+void ins_mousescroll(int dir)
+{
+ cmdarg_T cap;
+ oparg_T oa;
+ CLEAR_FIELD(cap);
+ clear_oparg(&oa);
+ cap.oap = &oa;
+ cap.arg = dir;
+
+ switch (dir) {
+ case MSCR_UP:
+ cap.cmdchar = K_MOUSEUP;
+ break;
+ case MSCR_DOWN:
+ cap.cmdchar = K_MOUSEDOWN;
+ break;
+ case MSCR_LEFT:
+ cap.cmdchar = K_MOUSELEFT;
+ break;
+ case MSCR_RIGHT:
+ cap.cmdchar = K_MOUSERIGHT;
+ break;
+ default:
+ siemsg("Invalid ins_mousescroll() argument: %d", dir);
+ }
+
+ win_T *old_curwin = curwin;
+ if (mouse_row >= 0 && mouse_col >= 0) {
+ // Find the window at the mouse pointer coordinates.
+ // NOTE: Must restore "curwin" to "old_curwin" before returning!
+ int grid = mouse_grid;
+ int row = mouse_row;
+ int col = mouse_col;
+ curwin = mouse_find_win(&grid, &row, &col);
+ if (curwin == NULL) {
+ curwin = old_curwin;
+ return;
+ }
+ curbuf = curwin->w_buffer;
+ }
+
+ if (curwin == old_curwin) {
+ // Don't scroll the current window if the popup menu is visible.
+ if (pum_visible()) {
+ return;
+ }
+
+ undisplay_dollar();
+ }
+
+ pos_T orig_cursor = curwin->w_cursor;
+
+ // Call the common mouse scroll function shared with other modes.
+ do_mousescroll(&cap);
+
+ curwin->w_redr_status = true;
+ curwin = old_curwin;
+ curbuf = curwin->w_buffer;
+
+ if (!equalpos(curwin->w_cursor, orig_cursor)) {
+ start_arrow(&orig_cursor);
+ set_can_cindent(true);
+ }
+}
+
/// Return true if "c" is a mouse key.
bool is_mouse_key(int c)
{
@@ -1036,8 +1183,6 @@ int jump_to_mouse(int flags, bool *inclusive, int which_button)
static int prev_col = -1;
static int did_drag = false; // drag was noticed
- win_T *wp, *old_curwin;
- pos_T old_cursor;
int count;
bool first;
int row = mouse_row;
@@ -1090,34 +1235,28 @@ retnomove:
if (flags & MOUSE_SETPOS) {
goto retnomove; // ugly goto...
}
- old_curwin = curwin;
- old_cursor = curwin->w_cursor;
+ win_T *old_curwin = curwin;
+ pos_T old_cursor = curwin->w_cursor;
if (row < 0 || col < 0) { // check if it makes sense
return IN_UNKNOWN;
}
// find the window where the row is in
- wp = mouse_find_win(&grid, &row, &col);
+ win_T *wp = mouse_find_win(&grid, &row, &col);
if (wp == NULL) {
return IN_UNKNOWN;
}
- on_status_line = (grid == DEFAULT_GRID_HANDLE && row + wp->w_winbar_height >= wp->w_height)
- ? row + wp->w_winbar_height - wp->w_height + 1 == 1
- : false;
-
- on_winbar = (row == -1)
- ? wp->w_winbar_height != 0
- : false;
-
- on_statuscol = !on_status_line && !on_winbar && col < win_col_off(wp)
- ? *wp->w_p_stc != NUL
- : false;
-
- on_sep_line = grid == DEFAULT_GRID_HANDLE && col >= wp->w_width
- ? col - wp->w_width + 1 == 1
- : false;
+ bool below_window = grid == DEFAULT_GRID_HANDLE && row + wp->w_winbar_height >= wp->w_height;
+ on_status_line = below_window && row + wp->w_winbar_height - wp->w_height + 1 == 1;
+ on_sep_line = grid == DEFAULT_GRID_HANDLE && col >= wp->w_width && col - wp->w_width + 1 == 1;
+ on_winbar = row == -1 && wp->w_winbar_height != 0;
+ on_statuscol = !below_window && !on_status_line && !on_sep_line && !on_winbar
+ && *wp->w_p_stc != NUL
+ && (wp->w_p_rl
+ ? col >= wp->w_width_inner - win_col_off(wp)
+ : col < win_col_off(wp));
// The rightmost character of the status line might be a vertical
// separator character if there is no connecting window to the right.
@@ -1150,7 +1289,7 @@ retnomove:
dragwin = NULL;
// winpos and height may change in win_enter()!
- if (grid == DEFAULT_GRID_HANDLE && row + wp->w_winbar_height >= wp->w_height) {
+ if (below_window) {
// In (or below) status line
status_line_offset = row + wp->w_winbar_height - wp->w_height + 1;
dragwin = wp;
@@ -1306,14 +1445,13 @@ retnomove:
}
first = false;
- if (hasFolding(curwin->w_topline, NULL, &curwin->w_topline)
- && curwin->w_topline == curbuf->b_ml.ml_line_count) {
- break;
- }
-
if (curwin->w_topfill > 0) {
curwin->w_topfill--;
} else {
+ if (hasFolding(curwin->w_topline, NULL, &curwin->w_topline)
+ && curwin->w_topline == curbuf->b_ml.ml_line_count) {
+ break;
+ }
curwin->w_topline++;
curwin->w_topfill = win_get_fill(curwin, curwin->w_topline);
}
@@ -1336,15 +1474,15 @@ retnomove:
}
}
+ colnr_T col_from_screen = -1;
+ int mouse_fold_flags = 0;
+ mouse_check_grid(&col_from_screen, &mouse_fold_flags);
+
// compute the position in the buffer line from the posn on the screen
if (mouse_comp_pos(curwin, &row, &col, &curwin->w_cursor.lnum)) {
mouse_past_bottom = true;
}
- if (!(flags & MOUSE_RELEASED) && which_button == MOUSE_LEFT) {
- col = mouse_adjust_click(curwin, row, col);
- }
-
// Start Visual mode before coladvance(), for when 'sel' != "old"
if ((flags & MOUSE_MAY_VIS) && !VIsual_active) {
VIsual = old_cursor;
@@ -1359,6 +1497,10 @@ retnomove:
}
}
+ if (col_from_screen >= 0) {
+ col = col_from_screen;
+ }
+
curwin->w_curswant = col;
curwin->w_set_curswant = false; // May still have been true
if (coladvance(col) == FAIL) { // Mouse click beyond end of line
@@ -1376,41 +1518,110 @@ retnomove:
count |= CURSOR_MOVED; // Cursor has moved
}
- count |= mouse_check_fold();
+ count |= mouse_fold_flags;
return count;
}
-// Compute the position in the buffer line from the posn on the screen in
-// window "win".
-// Returns true if the position is below the last line.
+/// Make a horizontal scroll to "leftcol".
+/// @return true if the cursor moved, false otherwise.
+static bool do_mousescroll_horiz(colnr_T leftcol)
+{
+ if (curwin->w_p_wrap) {
+ return false; // no horizontal scrolling when wrapping
+ }
+ if (curwin->w_leftcol == leftcol) {
+ return false; // already there
+ }
+
+ // When the line of the cursor is too short, move the cursor to the
+ // longest visible line.
+ if (!virtual_active()
+ && leftcol > scroll_line_len(curwin->w_cursor.lnum)) {
+ curwin->w_cursor.lnum = find_longest_lnum();
+ curwin->w_cursor.col = 0;
+ }
+
+ return set_leftcol(leftcol);
+}
+
+/// Normal and Visual modes implementation for scrolling in direction
+/// "cap->arg", which is one of the MSCR_ values.
+void nv_mousescroll(cmdarg_T *cap)
+{
+ win_T *const old_curwin = curwin;
+
+ if (mouse_row >= 0 && mouse_col >= 0) {
+ // Find the window at the mouse pointer coordinates.
+ // NOTE: Must restore "curwin" to "old_curwin" before returning!
+ int grid = mouse_grid;
+ int row = mouse_row;
+ int col = mouse_col;
+ curwin = mouse_find_win(&grid, &row, &col);
+ if (curwin == NULL) {
+ curwin = old_curwin;
+ return;
+ }
+ curbuf = curwin->w_buffer;
+ }
+
+ // Call the common mouse scroll function shared with other modes.
+ do_mousescroll(cap);
+
+ if (curwin != old_curwin && curwin->w_p_cul) {
+ redraw_for_cursorline(curwin);
+ }
+ curwin->w_redr_status = true;
+ curwin = old_curwin;
+ curbuf = curwin->w_buffer;
+}
+
+/// Mouse clicks and drags.
+void nv_mouse(cmdarg_T *cap)
+{
+ (void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0);
+}
+
+/// Compute the position in the buffer line from the posn on the screen in
+/// window "win".
+/// Returns true if the position is below the last line.
bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
{
int col = *colp;
int row = *rowp;
- linenr_T lnum;
bool retval = false;
- int off;
int count;
if (win->w_p_rl) {
col = win->w_width_inner - 1 - col;
}
- lnum = win->w_topline;
+ linenr_T lnum = win->w_topline;
while (row > 0) {
// Don't include filler lines in "count"
- if (win_may_fill(win)
- && !hasFoldingWin(win, lnum, NULL, NULL, true, NULL)) {
+ if (win_may_fill(win)) {
if (lnum == win->w_topline) {
row -= win->w_topfill;
} else {
row -= win_get_fill(win, lnum);
}
- count = plines_win_nofill(win, lnum, true);
+ count = plines_win_nofill(win, lnum, false);
} else {
- count = plines_win(win, lnum, true);
+ count = plines_win(win, lnum, false);
+ }
+
+ if (win->w_skipcol > 0 && lnum == win->w_topline) {
+ // Adjust for 'smoothscroll' clipping the top screen lines.
+ // A similar formula is used in curs_columns().
+ int width1 = win->w_width_inner - win_col_off(win);
+ int skip_lines = 0;
+ if (win->w_skipcol > width1) {
+ skip_lines = (win->w_skipcol - width1) / (width1 + win_col_off2(win)) + 1;
+ } else if (win->w_skipcol > 0) {
+ skip_lines = 1;
+ }
+ count -= skip_lines;
}
if (count > row) {
@@ -1429,13 +1640,16 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
if (!retval) {
// Compute the column without wrapping.
- off = win_col_off(win) - win_col_off2(win);
+ int off = win_col_off(win) - win_col_off2(win);
if (col < off) {
col = off;
}
col += row * (win->w_width_inner - off);
- // add skip column (for long wrapping line)
- col += win->w_skipcol;
+
+ // Add skip column for the topline.
+ if (lnum == win->w_topline) {
+ col += win->w_skipcol;
+ }
}
if (!win->w_p_wrap) {
@@ -1467,11 +1681,9 @@ win_T *mouse_find_win(int *gridp, int *rowp, int *colp)
return NULL;
}
- frame_T *fp;
-
- fp = topframe;
+ frame_T *fp = topframe;
*rowp -= firstwin->w_winrow;
- for (;;) {
+ while (true) {
if (fp->fr_layout == FR_LEAF) {
break;
}
@@ -1535,20 +1747,27 @@ static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp)
}
/// Convert a virtual (screen) column to a character column.
-/// The first column is one.
-colnr_T vcol2col(win_T *const wp, const linenr_T lnum, const colnr_T vcol)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+/// The first column is zero.
+colnr_T vcol2col(win_T *wp, linenr_T lnum, colnr_T vcol, colnr_T *coladdp)
+ FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT
{
// try to advance to the specified column
- char *line = ml_get_buf(wp->w_buffer, lnum, false);
+ char *line = ml_get_buf(wp->w_buffer, lnum);
chartabsize_T cts;
init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL) {
- cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
+ int size = win_lbr_chartabsize(&cts, NULL);
+ if (cts.cts_vcol + size > vcol) {
+ break;
+ }
+ cts.cts_vcol += size;
MB_PTR_ADV(cts.cts_ptr);
}
clear_chartabsize_arg(&cts);
+ if (coladdp != NULL) {
+ *coladdp = vcol - cts.cts_vcol;
+ }
return (colnr_T)(cts.cts_ptr - line);
}
@@ -1569,15 +1788,13 @@ static void set_mouse_topline(win_T *wp)
orig_topfill = wp->w_topfill;
}
-///
/// Return length of line "lnum" for horizontal scrolling.
-///
static colnr_T scroll_line_len(linenr_T lnum)
{
colnr_T col = 0;
char *line = ml_get(lnum);
if (*line != NUL) {
- for (;;) {
+ while (true) {
int numchar = win_chartabsize(curwin, line, col);
MB_PTR_ADV(line);
if (*line == NUL) { // don't count the last character
@@ -1589,9 +1806,7 @@ static colnr_T scroll_line_len(linenr_T lnum)
return col;
}
-///
/// Find longest visible line number.
-///
static linenr_T find_longest_lnum(void)
{
linenr_T ret = 0;
@@ -1602,17 +1817,17 @@ static linenr_T find_longest_lnum(void)
if (curwin->w_topline <= curwin->w_cursor.lnum
&& curwin->w_botline > curwin->w_cursor.lnum
&& curwin->w_botline <= curbuf->b_ml.ml_line_count + 1) {
- long max = 0;
+ colnr_T max = 0;
// Use maximum of all visible lines. Remember the lnum of the
// longest line, closest to the cursor line. Used when scrolling
// below.
for (linenr_T lnum = curwin->w_topline; lnum < curwin->w_botline; lnum++) {
colnr_T len = scroll_line_len(lnum);
- if (len > (colnr_T)max) {
+ if (len > max) {
max = len;
ret = lnum;
- } else if (len == (colnr_T)max
+ } else if (len == max
&& abs(lnum - curwin->w_cursor.lnum)
< abs(ret - curwin->w_cursor.lnum)) {
ret = lnum;
@@ -1626,194 +1841,116 @@ static linenr_T find_longest_lnum(void)
return ret;
}
-/// Do a horizontal scroll.
-/// @return true if the cursor moved, false otherwise.
-bool mouse_scroll_horiz(int dir)
-{
- if (curwin->w_p_wrap) {
- return false;
- }
-
- int step = (int)p_mousescroll_hor;
- if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
- step = curwin->w_width_inner;
- }
-
- int leftcol = curwin->w_leftcol + (dir == MSCR_RIGHT ? -step : +step);
- if (leftcol < 0) {
- leftcol = 0;
- }
-
- if (curwin->w_leftcol == leftcol) {
- return false;
- }
-
- curwin->w_leftcol = (colnr_T)leftcol;
-
- // When the line of the cursor is too short, move the cursor to the
- // longest visible line.
- if (!virtual_active()
- && (colnr_T)leftcol > scroll_line_len(curwin->w_cursor.lnum)) {
- curwin->w_cursor.lnum = find_longest_lnum();
- curwin->w_cursor.col = 0;
- }
-
- return leftcol_changed();
-}
-
-/// Adjusts the clicked column position when 'conceallevel' > 0
-static int mouse_adjust_click(win_T *wp, int row, int col)
+/// Check clicked cell on its grid
+static void mouse_check_grid(colnr_T *vcolp, int *flagsp)
+ FUNC_ATTR_NONNULL_ALL
{
- if (!(wp->w_p_cole > 0 && curbuf->b_p_smc > 0
- && wp->w_leftcol < curbuf->b_p_smc && conceal_cursor_line(wp))) {
- return col;
- }
-
- // `col` is the position within the current line that is highlighted by the
- // cursor without consideration for concealed characters. The current line is
- // scanned *up to* `col`, nudging it left or right when concealed characters
- // are encountered.
- //
- // win_chartabsize() is used to keep track of the virtual column position
- // relative to the line's bytes. For example: if col == 9 and the line
- // starts with a tab that's 8 columns wide, we would want the cursor to be
- // highlighting the second byte, not the ninth.
-
- linenr_T lnum = wp->w_cursor.lnum;
- char *line = ml_get(lnum);
- char *ptr = line;
- char *ptr_end;
- char *ptr_row_offset = line; // Where we begin adjusting `ptr_end`
-
- // Find the offset where scanning should begin.
- int offset = wp->w_leftcol;
- if (row > 0) {
- offset += row * (wp->w_width_inner - win_col_off(wp) - win_col_off2(wp) -
- wp->w_leftcol + wp->w_skipcol);
- }
-
- int vcol;
-
- if (offset) {
- // Skip everything up to an offset since nvim takes care of displaying the
- // correct portion of the line when horizontally scrolling.
- // When 'wrap' is enabled, only the row (of the wrapped line) needs to be
- // checked for concealed characters.
- vcol = 0;
- while (vcol < offset && *ptr != NUL) {
- vcol += win_chartabsize(curwin, ptr, vcol);
- ptr += utfc_ptr2len(ptr);
- }
-
- ptr_row_offset = ptr;
- }
-
- // Align `ptr_end` with `col`
- vcol = offset;
- ptr_end = ptr_row_offset;
- while (vcol < col && *ptr_end != NUL) {
- vcol += win_chartabsize(curwin, ptr_end, vcol);
- ptr_end += utfc_ptr2len(ptr_end);
- }
-
- int matchid;
- int prev_matchid;
- int nudge = 0;
- int cwidth = 0;
-
- vcol = offset;
-
-#define INCR() nudge++; ptr_end += utfc_ptr2len((char *)ptr_end)
-#define DECR() nudge--; ptr_end -= utfc_ptr2len((char *)ptr_end)
-
- while (ptr < ptr_end && *ptr != NUL) {
- cwidth = win_chartabsize(curwin, ptr, vcol);
- vcol += cwidth;
- if (cwidth > 1 && *ptr == '\t' && nudge > 0) {
- // A tab will "absorb" any previous adjustments.
- cwidth = MIN(cwidth, nudge);
- while (cwidth > 0) {
- DECR();
- cwidth--;
- }
- }
-
- matchid = syn_get_concealed_id(wp, lnum, (colnr_T)(ptr - line));
- if (matchid != 0) {
- if (wp->w_p_cole == 3) {
- INCR();
- } else {
- if (!(row > 0 && ptr == ptr_row_offset)
- && (wp->w_p_cole == 1 || (wp->w_p_cole == 2
- && (wp->w_p_lcs_chars.conceal != NUL
- || syn_get_sub_char() != NUL)))) {
- // At least one placeholder character will be displayed.
- DECR();
- }
-
- prev_matchid = matchid;
+ int click_grid = mouse_grid;
+ int click_row = mouse_row;
+ int click_col = mouse_col;
- while (prev_matchid == matchid && *ptr != NUL) {
- INCR();
- ptr += utfc_ptr2len(ptr);
- matchid = syn_get_concealed_id(wp, lnum, (colnr_T)(ptr - line));
+ // XXX: this doesn't change click_grid if it is 1, even with multigrid
+ win_T *wp = mouse_find_win(&click_grid, &click_row, &click_col);
+ // Only use vcols[] after the window was redrawn. Mainly matters
+ // for tests, a user would not click before redrawing.
+ if (wp == NULL || wp->w_redr_type != 0) {
+ return;
+ }
+ ScreenGrid *gp = &wp->w_grid;
+ int start_row = 0;
+ int start_col = 0;
+ grid_adjust(&gp, &start_row, &start_col);
+ if (gp->handle != click_grid || gp->chars == NULL) {
+ return;
+ }
+ click_row += start_row;
+ click_col += start_col;
+ if (click_row < 0 || click_row >= gp->rows
+ || click_col < 0 || click_col >= gp->cols) {
+ return;
+ }
+
+ const size_t off = gp->line_offset[click_row] + (size_t)click_col;
+ colnr_T col_from_screen = gp->vcols[off];
+
+ if (col_from_screen == MAXCOL) {
+ // When clicking after end of line, still need to set correct curswant
+ size_t off_l = gp->line_offset[click_row] + (size_t)start_col;
+ if (gp->vcols[off_l] < MAXCOL) {
+ // Binary search to find last char in line
+ size_t off_r = off;
+ while (off_l < off_r) {
+ size_t off_m = (off_l + off_r + 1) / 2;
+ if (gp->vcols[off_m] < MAXCOL) {
+ off_l = off_m;
+ } else {
+ off_r = off_m - 1;
}
-
- continue;
}
+ colnr_T eol_vcol = gp->vcols[off_r];
+ assert(eol_vcol < MAXCOL);
+ if (eol_vcol < 0) {
+ // Empty line or whole line before w_leftcol,
+ // with columns before buffer text
+ eol_vcol = wp->w_leftcol - 1;
+ }
+ *vcolp = eol_vcol + (int)(off - off_r);
+ } else {
+ // Empty line or whole line before w_leftcol
+ *vcolp = click_col - start_col + wp->w_leftcol;
}
-
- ptr += utfc_ptr2len(ptr);
+ } else if (col_from_screen >= 0) {
+ // Use the virtual column from vcols[], it is accurate also after
+ // concealed characters.
+ *vcolp = col_from_screen;
}
- return col + nudge;
+ if (col_from_screen == -2) {
+ *flagsp |= MOUSE_FOLD_OPEN;
+ } else if (col_from_screen == -3) {
+ *flagsp |= MOUSE_FOLD_CLOSE;
+ }
}
-// Check clicked cell is foldcolumn
-int mouse_check_fold(void)
+/// "getmousepos()" function
+void f_getmousepos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- int click_grid = mouse_grid;
- int click_row = mouse_row;
- int click_col = mouse_col;
- int mouse_char = ' ';
- int max_row = Rows;
- int max_col = Columns;
- int multigrid = ui_has(kUIMultigrid);
-
- win_T *wp;
-
- wp = mouse_find_win(&click_grid, &click_row, &click_col);
- if (wp && multigrid) {
- max_row = wp->w_grid_alloc.rows;
- max_col = wp->w_grid_alloc.cols;
- }
+ int row = mouse_row;
+ int col = mouse_col;
+ int grid = mouse_grid;
+ varnumber_T winid = 0;
+ varnumber_T winrow = 0;
+ varnumber_T wincol = 0;
+ linenr_T lnum = 0;
+ varnumber_T column = 0;
+ colnr_T coladd = 0;
- if (wp && mouse_row >= 0 && mouse_row < max_row
- && mouse_col >= 0 && mouse_col < max_col) {
- ScreenGrid *gp = multigrid ? &wp->w_grid_alloc : &default_grid;
- int fdc = win_fdccol_count(wp);
- int row = multigrid && mouse_grid == 0 ? click_row : mouse_row;
- int col = multigrid && mouse_grid == 0 ? click_col : mouse_col;
+ tv_dict_alloc_ret(rettv);
+ dict_T *d = rettv->vval.v_dict;
- // Remember the character under the mouse, might be one of foldclose or
- // foldopen fillchars in the fold column.
- if (gp->chars != NULL) {
- mouse_char = utf_ptr2char((char *)gp->chars[gp->line_offset[row]
- + (unsigned)col]);
- }
+ tv_dict_add_nr(d, S_LEN("screenrow"), (varnumber_T)mouse_row + 1);
+ tv_dict_add_nr(d, S_LEN("screencol"), (varnumber_T)mouse_col + 1);
- // Check for position outside of the fold column.
- if (wp->w_p_rl ? click_col < wp->w_width_inner - fdc :
- click_col >= fdc + (cmdwin_type == 0 ? 0 : 1)) {
- mouse_char = ' ';
+ win_T *wp = mouse_find_win(&grid, &row, &col);
+ if (wp != NULL) {
+ int height = wp->w_height + wp->w_hsep_height + wp->w_status_height;
+ // The height is adjusted by 1 when there is a bottom border. This is not
+ // necessary for a top border since `row` starts at -1 in that case.
+ if (row < height + wp->w_border_adj[2]) {
+ winid = wp->handle;
+ winrow = row + 1 + wp->w_winrow_off; // Adjust by 1 for top border
+ wincol = col + 1 + wp->w_wincol_off; // Adjust by 1 for left border
+ if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width) {
+ (void)mouse_comp_pos(wp, &row, &col, &lnum);
+ col = vcol2col(wp, lnum, col, &coladd);
+ column = col + 1;
+ }
}
}
-
- if (wp && mouse_char == wp->w_p_fcs_chars.foldclosed) {
- return MOUSE_FOLD_OPEN;
- } else if (mouse_char != ' ') {
- return MOUSE_FOLD_CLOSE;
- }
-
- return 0;
+ tv_dict_add_nr(d, S_LEN("winid"), winid);
+ tv_dict_add_nr(d, S_LEN("winrow"), winrow);
+ tv_dict_add_nr(d, S_LEN("wincol"), wincol);
+ tv_dict_add_nr(d, S_LEN("line"), (varnumber_T)lnum);
+ tv_dict_add_nr(d, S_LEN("column"), column);
+ tv_dict_add_nr(d, S_LEN("coladd"), coladd);
}