aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2022-11-01 09:14:25 +0800
committerGitHub <noreply@github.com>2022-11-01 09:14:25 +0800
commitc3aba403c6091a56c517e60beb73d9873d4bb8af (patch)
tree2d4eeaa33947ce74c4748296e6bcc8c4062d17e4
parentd8dbf58b4372d1415e3ca69541fc6fe71890ba53 (diff)
downloadrneovim-c3aba403c6091a56c517e60beb73d9873d4bb8af.tar.gz
rneovim-c3aba403c6091a56c517e60beb73d9873d4bb8af.tar.bz2
rneovim-c3aba403c6091a56c517e60beb73d9873d4bb8af.zip
refactor: move do_mouse() and its helpers to mouse.c (#20895)
Vim moved it in patch 8.1.2062, and this is required for patch 9.0.0822. Also change macros in mouse.h to enums.
-rw-r--r--src/nvim/mouse.c874
-rw-r--r--src/nvim/mouse.h81
-rw-r--r--src/nvim/normal.c862
3 files changed, 918 insertions, 899 deletions
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 734ece73b4..88dd81da8b 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -4,18 +4,26 @@
#include <stdbool.h>
#include "nvim/ascii.h"
+#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
#include "nvim/drawscreen.h"
+#include "nvim/eval.h"
+#include "nvim/ex_docmd.h"
#include "nvim/fold.h"
+#include "nvim/getchar.h"
#include "nvim/grid.h"
#include "nvim/memline.h"
+#include "nvim/menu.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
+#include "nvim/ops.h"
+#include "nvim/option.h"
#include "nvim/os_unix.h"
#include "nvim/plines.h"
+#include "nvim/search.h"
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
@@ -31,10 +39,156 @@
static linenr_T orig_topline = 0;
static int orig_topfill = 0;
+/// Get class of a character for selection: same class means same word.
+/// 0: blank
+/// 1: punctuation groups
+/// 2: normal word character
+/// >2: multi-byte word character.
+static int get_mouse_class(char_u *p)
+{
+ if (MB_BYTE2LEN(p[0]) > 1) {
+ return mb_get_class(p);
+ }
+
+ const int c = *p;
+ if (c == ' ' || c == '\t') {
+ return 0;
+ }
+ if (vim_iswordc(c)) {
+ return 2;
+ }
+
+ // There are a few special cases where we want certain combinations of
+ // characters to be considered as a single word. These are things like
+ // "->", "/ *", "*=", "+=", "&=", "<=", ">=", "!=" etc. Otherwise, each
+ // character is in its own class.
+ if (c != NUL && vim_strchr("-+*/%<>&|^!=", c) != NULL) {
+ return 1;
+ }
+ return c;
+}
+
+/// Move "pos" back to the start of the word it's in.
+static void find_start_of_word(pos_T *pos)
+{
+ char_u *line;
+ int cclass;
+ int col;
+
+ line = (char_u *)ml_get(pos->lnum);
+ cclass = get_mouse_class(line + pos->col);
+
+ while (pos->col > 0) {
+ col = pos->col - 1;
+ col -= utf_head_off((char *)line, (char *)line + col);
+ if (get_mouse_class(line + col) != cclass) {
+ break;
+ }
+ pos->col = col;
+ }
+}
+
+/// Move "pos" forward to the end of the word it's in.
+/// When 'selection' is "exclusive", the position is just after the word.
+static void find_end_of_word(pos_T *pos)
+{
+ char_u *line;
+ int cclass;
+ int col;
+
+ line = (char_u *)ml_get(pos->lnum);
+ if (*p_sel == 'e' && pos->col > 0) {
+ pos->col--;
+ pos->col -= utf_head_off((char *)line, (char *)line + pos->col);
+ }
+ cclass = get_mouse_class(line + pos->col);
+ while (line[pos->col] != NUL) {
+ col = pos->col + utfc_ptr2len((char *)line + pos->col);
+ if (get_mouse_class(line + col) != cclass) {
+ if (*p_sel == 'e') {
+ pos->col = col;
+ }
+ break;
+ }
+ pos->col = col;
+ }
+}
+
+/// Move the current tab to tab in same column as mouse or to end of the
+/// tabline if there is no tab there.
+static void move_tab_to_mouse(void)
+{
+ int tabnr = tab_page_click_defs[mouse_col].tabnr;
+ if (tabnr <= 0) {
+ tabpage_move(9999);
+ } else if (tabnr < tabpage_index(curtab)) {
+ tabpage_move(tabnr - 1);
+ } else {
+ tabpage_move(tabnr);
+ }
+}
+
+/// 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)
+{
+ typval_T argv[] = {
+ {
+ .v_lock = VAR_FIXED,
+ .v_type = VAR_NUMBER,
+ .vval = {
+ .v_number = (varnumber_T)click_defs[col].tabnr
+ },
+ },
+ {
+ .v_lock = VAR_FIXED,
+ .v_type = VAR_NUMBER,
+ .vval = {
+ .v_number = ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK
+ ? 4
+ : ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK
+ ? 3
+ : ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK
+ ? 2
+ : 1)))
+ },
+ },
+ {
+ .v_lock = VAR_FIXED,
+ .v_type = VAR_STRING,
+ .vval = {
+ .v_string = (which_button == MOUSE_LEFT
+ ? "l"
+ : (which_button == MOUSE_RIGHT
+ ? "r"
+ : (which_button == MOUSE_MIDDLE
+ ? "m"
+ : "?")))
+ },
+ },
+ {
+ .v_lock = VAR_FIXED,
+ .v_type = VAR_STRING,
+ .vval = {
+ .v_string = (char[]) {
+ (char)(mod_mask & MOD_MASK_SHIFT ? 's' : ' '),
+ (char)(mod_mask & MOD_MASK_CTRL ? 'c' : ' '),
+ (char)(mod_mask & MOD_MASK_ALT ? 'a' : ' '),
+ (char)(mod_mask & MOD_MASK_META ? 'm' : ' '),
+ NUL
+ }
+ },
+ }
+ };
+ typval_T rettv;
+ (void)call_vim_function(click_defs[col].func, ARRAY_SIZE(argv), argv, &rettv);
+ tv_clear(&rettv);
+}
+
/// Translate window coordinates to buffer position without any side effects.
/// Returns IN_BUFFER and sets "mpos->col" to the column when in buffer text.
/// The column is one for the first column.
-int get_fpos_of_mouse(pos_T *mpos)
+static int get_fpos_of_mouse(pos_T *mpos)
{
int grid = mouse_grid;
int row = mouse_row;
@@ -73,6 +227,715 @@ int get_fpos_of_mouse(pos_T *mpos)
return IN_BUFFER;
}
+/// Do the appropriate action for the current mouse click in the current mode.
+/// Not used for Command-line mode.
+///
+/// Normal and Visual Mode:
+/// event modi- position visual change action
+/// fier cursor window
+/// left press - yes end yes
+/// left press C yes end yes "^]" (2)
+/// left press S yes end (popup: extend) yes "*" (2)
+/// left drag - yes start if moved no
+/// left relse - yes start if moved no
+/// middle press - yes if not active no put register
+/// middle press - yes if active no yank and put
+/// right press - yes start or extend yes
+/// right press S yes no change yes "#" (2)
+/// right drag - yes extend no
+/// right relse - yes extend no
+///
+/// Insert or Replace Mode:
+/// event modi- position visual change action
+/// fier cursor window
+/// left press - yes (cannot be active) yes
+/// left press C yes (cannot be active) yes "CTRL-O^]" (2)
+/// left press S yes (cannot be active) yes "CTRL-O*" (2)
+/// left drag - yes start or extend (1) no CTRL-O (1)
+/// left relse - yes start or extend (1) no CTRL-O (1)
+/// middle press - no (cannot be active) no put register
+/// right press - yes start or extend yes CTRL-O
+/// right press S yes (cannot be active) yes "CTRL-O#" (2)
+///
+/// (1) only if mouse pointer moved since press
+/// (2) only if click is in same buffer
+///
+/// @param oap operator argument, can be NULL
+/// @param c K_LEFTMOUSE, etc
+/// @param dir Direction to 'put' if necessary
+/// @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)
+{
+ 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
+ int jump_flags = 0; // flags for jump_to_mouse()
+ pos_T start_visual;
+ bool moved; // Has cursor moved?
+ bool in_winbar; // mouse in window bar
+ 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;
+ 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;
+
+ for (;;) {
+ 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
+ // one. Speeds up dragging the status line.
+ // Note: Since characters added to the stuff buffer in the code
+ // below need to come before the next character, do not do this
+ // when the current character was stuffed.
+ if (!KeyStuffed && vpeekc() != NUL) {
+ int nc;
+ int save_mouse_grid = mouse_grid;
+ int save_mouse_row = mouse_row;
+ int save_mouse_col = mouse_col;
+
+ // Need to get the character, peeking doesn't get the actual one.
+ nc = safe_vgetc();
+ if (c == nc) {
+ continue;
+ }
+ vungetc(nc);
+ mouse_grid = save_mouse_grid;
+ mouse_row = save_mouse_row;
+ mouse_col = save_mouse_col;
+ }
+ }
+ break;
+ }
+
+ if (c == K_MOUSEMOVE) {
+ // Mouse moved without a button pressed.
+ return false;
+ }
+
+ // Ignore drag and release events if we didn't get a click.
+ if (is_click) {
+ got_click = true;
+ } else {
+ if (!got_click) { // didn't get click, ignore
+ return false;
+ }
+ if (!is_drag) { // release, reset got_click
+ got_click = false;
+ if (in_tab_line) {
+ in_tab_line = false;
+ return false;
+ }
+ }
+ }
+
+ // CTRL right mouse button does CTRL-T
+ if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT) {
+ if (State & MODE_INSERT) {
+ stuffcharReadbuff(Ctrl_O);
+ }
+ if (count > 1) {
+ stuffnumReadbuff(count);
+ }
+ stuffcharReadbuff(Ctrl_T);
+ got_click = false; // ignore drag&release now
+ return false;
+ }
+
+ // CTRL only works with left mouse button
+ if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT) {
+ return false;
+ }
+
+ // When a modifier is down, ignore drag and release events, as well as
+ // multiple clicks and the middle mouse button.
+ // Accept shift-leftmouse drags when 'mousemodel' is "popup.*".
+ if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT
+ | MOD_MASK_META))
+ && (!is_click
+ || (mod_mask & MOD_MASK_MULTI_CLICK)
+ || which_button == MOUSE_MIDDLE)
+ && !((mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))
+ && mouse_model_popup()
+ && which_button == MOUSE_LEFT)
+ && !((mod_mask & MOD_MASK_ALT)
+ && !mouse_model_popup()
+ && which_button == MOUSE_RIGHT)) {
+ return false;
+ }
+
+ // If the button press was used as the movement command for an operator (eg
+ // "d<MOUSE>"), or it is the middle button that is held down, ignore
+ // drag/release events.
+ if (!is_click && which_button == MOUSE_MIDDLE) {
+ return false;
+ }
+
+ if (oap != NULL) {
+ regname = oap->regname;
+ } else {
+ regname = 0;
+ }
+
+ // Middle mouse button does a 'put' of the selected text
+ if (which_button == MOUSE_MIDDLE) {
+ if (State == MODE_NORMAL) {
+ // If an operator was pending, we don't know what the user wanted to do.
+ // Go back to normal mode: Clear the operator and beep().
+ if (oap != NULL && oap->op_type != OP_NOP) {
+ clearopbeep(oap);
+ return false;
+ }
+
+ // If visual was active, yank the highlighted text and put it
+ // before the mouse pointer position.
+ // In Select mode replace the highlighted text with the clipboard.
+ if (VIsual_active) {
+ if (VIsual_select) {
+ stuffcharReadbuff(Ctrl_G);
+ stuffReadbuff("\"+p");
+ } else {
+ stuffcharReadbuff('y');
+ stuffcharReadbuff(K_MIDDLEMOUSE);
+ }
+ return false;
+ }
+ // The rest is below jump_to_mouse()
+ } else if ((State & MODE_INSERT) == 0) {
+ return false;
+ }
+
+ // Middle click in insert mode doesn't move the mouse, just insert the
+ // contents of a register. '.' register is special, can't insert that
+ // with do_put().
+ // Also paste at the cursor if the current mode isn't in 'mouse' (only
+ // happens for the GUI).
+ if ((State & MODE_INSERT)) {
+ if (regname == '.') {
+ insert_reg(regname, true);
+ } else {
+ if (regname == 0 && eval_has_provider("clipboard")) {
+ regname = '*';
+ }
+ if ((State & REPLACE_FLAG) && !yank_register_mline(regname)) {
+ insert_reg(regname, true);
+ } else {
+ do_put(regname, NULL, BACKWARD, 1L,
+ (fixindent ? PUT_FIXINDENT : 0) | PUT_CURSEND);
+
+ // Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r
+ AppendCharToRedobuff(Ctrl_R);
+ AppendCharToRedobuff(fixindent ? Ctrl_P : Ctrl_O);
+ AppendCharToRedobuff(regname == 0 ? '"' : regname);
+ }
+ }
+ return false;
+ }
+ }
+
+ // When dragging or button-up stay in the same window.
+ if (!is_click) {
+ jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
+ }
+
+ start_visual.lnum = 0;
+
+ // Check for clicking in the tab page line.
+ if (mouse_grid <= 1 && mouse_row == 0 && firstwin->w_winrow > 0) {
+ if (is_drag) {
+ if (in_tab_line) {
+ move_tab_to_mouse();
+ }
+ return false;
+ }
+
+ // click in a tab selects that tab page
+ if (is_click && cmdwin_type == 0 && mouse_col < Columns) {
+ in_tab_line = true;
+ c1 = tab_page_click_defs[mouse_col].tabnr;
+ switch (tab_page_click_defs[mouse_col].type) {
+ case kStlClickDisabled:
+ break;
+ case kStlClickTabClose: {
+ tabpage_T *tp;
+
+ // Close the current or specified tab page.
+ if (c1 == 999) {
+ tp = curtab;
+ } else {
+ tp = find_tabpage(c1);
+ }
+ if (tp == curtab) {
+ if (first_tabpage->tp_next != NULL) {
+ tabpage_close(false);
+ }
+ } else if (tp != NULL) {
+ tabpage_close_other(tp, false);
+ }
+ break;
+ }
+ case kStlClickTabSwitch:
+ if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
+ // double click opens new page
+ end_visual_mode();
+ tabpage_new();
+ tabpage_move(c1 == 0 ? 9999 : c1 - 1);
+ } else {
+ // Go to specified tab page, or next one if not clicking
+ // on a label.
+ goto_tabpage(c1);
+
+ // It's like clicking on the status line of a window.
+ if (curwin != old_curwin) {
+ end_visual_mode();
+ }
+ }
+ break;
+ case kStlClickFuncRun:
+ call_click_def_func(tab_page_click_defs, mouse_col, which_button);
+ break;
+ }
+ }
+ return true;
+ } else if (is_drag && in_tab_line) {
+ move_tab_to_mouse();
+ return false;
+ }
+
+ // When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
+ // right button up -> pop-up menu
+ // shift-left button -> right button
+ // alt-left button -> alt-right button
+ if (mouse_model_popup()) {
+ if (which_button == MOUSE_RIGHT
+ && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
+ if (!is_click) {
+ // Ignore right button release events, only shows the popup
+ // menu on the button down event.
+ return false;
+ }
+ jump_flags = 0;
+ if (strcmp(p_mousem, "popup_setpos") == 0) {
+ // 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) {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ } else {
+ if (VIsual_mode == 'V') {
+ if ((curwin->w_cursor.lnum <= VIsual.lnum
+ && (m_pos.lnum < curwin->w_cursor.lnum || VIsual.lnum < m_pos.lnum))
+ || (VIsual.lnum < curwin->w_cursor.lnum
+ && (m_pos.lnum < VIsual.lnum || curwin->w_cursor.lnum < m_pos.lnum))) {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ }
+ } else if ((ltoreq(curwin->w_cursor, VIsual)
+ && (lt(m_pos, curwin->w_cursor) || lt(VIsual, m_pos)))
+ || (lt(VIsual, curwin->w_cursor)
+ && (lt(m_pos, VIsual) || lt(curwin->w_cursor, m_pos)))) {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ } else if (VIsual_mode == Ctrl_V) {
+ getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
+ getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
+ if (m_pos.col < leftcol || m_pos.col > rightcol) {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ }
+ }
+ }
+ } else {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ }
+ }
+ if (jump_flags) {
+ jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
+ redraw_curbuf_later(VIsual_active ? UPD_INVERTED : UPD_VALID);
+ update_screen();
+ setcursor();
+ ui_flush(); // Update before showing popup menu
+ }
+ show_popupmenu();
+ got_click = false; // ignore release events
+ return (jump_flags & CURSOR_MOVED) != 0;
+ }
+ if (which_button == MOUSE_LEFT
+ && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))) {
+ which_button = MOUSE_RIGHT;
+ mod_mask &= ~MOD_MASK_SHIFT;
+ }
+ }
+
+ if ((State & (MODE_NORMAL | MODE_INSERT))
+ && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
+ if (which_button == MOUSE_LEFT) {
+ if (is_click) {
+ // stop Visual mode for a left click in a window, but not when on a status line
+ if (VIsual_active) {
+ jump_flags |= MOUSE_MAY_STOP_VIS;
+ }
+ } else {
+ jump_flags |= MOUSE_MAY_VIS;
+ }
+ } else if (which_button == MOUSE_RIGHT) {
+ if (is_click && VIsual_active) {
+ // Remember the start and end of visual before moving the cursor.
+ if (lt(curwin->w_cursor, VIsual)) {
+ start_visual = curwin->w_cursor;
+ end_visual = VIsual;
+ } else {
+ start_visual = VIsual;
+ end_visual = curwin->w_cursor;
+ }
+ }
+ jump_flags |= MOUSE_FOCUS;
+ jump_flags |= MOUSE_MAY_VIS;
+ }
+ }
+
+ // If an operator is pending, ignore all drags and releases until the next mouse click.
+ if (!is_drag && oap != NULL && oap->op_type != OP_NOP) {
+ got_click = false;
+ oap->motion_type = kMTCharWise;
+ }
+
+ // When releasing the button let jump_to_mouse() know.
+ if (!is_click && !is_drag) {
+ jump_flags |= MOUSE_RELEASED;
+ }
+
+ // JUMP!
+ jump_flags = jump_to_mouse(jump_flags,
+ oap == NULL ? NULL : &(oap->inclusive),
+ which_button);
+
+ moved = (jump_flags & CURSOR_MOVED);
+ in_winbar = (jump_flags & MOUSE_WINBAR);
+ in_status_line = (jump_flags & IN_STATUS_LINE);
+ in_sep_line = (jump_flags & IN_SEP_LINE);
+
+ if ((in_winbar || in_status_line) && is_click) {
+ // Handle click event on window bar or status lin
+ int click_grid = mouse_grid;
+ int click_row = mouse_row;
+ int click_col = mouse_col;
+ win_T *wp = mouse_find_win(&click_grid, &click_row, &click_col);
+ if (wp == NULL) {
+ return false;
+ }
+
+ StlClickDefinition *click_defs = in_status_line ? wp->w_status_click_defs
+ : wp->w_winbar_click_defs;
+
+ if (in_status_line && global_stl_height() > 0) {
+ // global statusline is displayed for the current window,
+ // and spans the whole screen.
+ click_defs = curwin->w_status_click_defs;
+ click_col = mouse_col;
+ }
+
+ if (click_defs != NULL) {
+ switch (click_defs[click_col].type) {
+ case kStlClickDisabled:
+ break;
+ case kStlClickFuncRun:
+ call_click_def_func(click_defs, click_col, which_button);
+ break;
+ default:
+ assert(false && "winbar and statusline only support %@ for clicks");
+ break;
+ }
+ }
+
+ return false;
+ } else if (in_winbar) {
+ // A drag or release event in the window bar has no side effects.
+ return false;
+ }
+
+ // When jumping to another window, clear a pending operator. That's a bit
+ // friendlier than beeping and not jumping to that window.
+ if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP) {
+ clearop(oap);
+ }
+
+ if (mod_mask == 0
+ && !is_drag
+ && (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
+ && which_button == MOUSE_LEFT) {
+ // open or close a fold at this line
+ if (jump_flags & MOUSE_FOLD_OPEN) {
+ openFold(curwin->w_cursor, 1L);
+ } else {
+ closeFold(curwin->w_cursor, 1L);
+ }
+ // don't move the cursor if still in the same window
+ if (curwin == old_curwin) {
+ curwin->w_cursor = save_cursor;
+ }
+ }
+
+ // Set global flag that we are extending the Visual area with mouse dragging;
+ // temporarily minimize 'scrolloff'.
+ if (VIsual_active && is_drag && get_scrolloff_value(curwin)) {
+ // In the very first line, allow scrolling one line
+ if (mouse_row == 0) {
+ mouse_dragging = 2;
+ } else {
+ mouse_dragging = 1;
+ }
+ }
+
+ // When dragging the mouse above the window, scroll down.
+ if (is_drag && mouse_row < 0 && !in_status_line) {
+ scroll_redraw(false, 1L);
+ mouse_row = 0;
+ }
+
+ if (start_visual.lnum) { // right click in visual mode
+ // When ALT is pressed make Visual mode blockwise.
+ if (mod_mask & MOD_MASK_ALT) {
+ VIsual_mode = Ctrl_V;
+ }
+
+ // In Visual-block mode, divide the area in four, pick up the corner
+ // that is in the quarter that the cursor is in.
+ if (VIsual_mode == Ctrl_V) {
+ getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
+ if (curwin->w_curswant > (leftcol + rightcol) / 2) {
+ end_visual.col = leftcol;
+ } else {
+ end_visual.col = rightcol;
+ }
+ if (curwin->w_cursor.lnum >=
+ (start_visual.lnum + end_visual.lnum) / 2) {
+ end_visual.lnum = start_visual.lnum;
+ }
+
+ // move VIsual to the right column
+ start_visual = curwin->w_cursor; // save the cursor pos
+ curwin->w_cursor = end_visual;
+ coladvance(end_visual.col);
+ VIsual = curwin->w_cursor;
+ curwin->w_cursor = start_visual; // restore the cursor
+ } else {
+ // If the click is before the start of visual, change the start.
+ // If the click is after the end of visual, change the end. If
+ // the click is inside the visual, change the closest side.
+ if (lt(curwin->w_cursor, start_visual)) {
+ VIsual = end_visual;
+ } else if (lt(end_visual, curwin->w_cursor)) {
+ VIsual = start_visual;
+ } else {
+ // In the same line, compare column number
+ if (end_visual.lnum == start_visual.lnum) {
+ if (curwin->w_cursor.col - start_visual.col >
+ end_visual.col - curwin->w_cursor.col) {
+ VIsual = start_visual;
+ } else {
+ VIsual = end_visual;
+ }
+ } else {
+ // In different lines, compare line number
+ diff = (curwin->w_cursor.lnum - start_visual.lnum) -
+ (end_visual.lnum - curwin->w_cursor.lnum);
+
+ if (diff > 0) { // closest to end
+ VIsual = start_visual;
+ } else if (diff < 0) { // closest to start
+ VIsual = end_visual;
+ } else { // in the middle line
+ if (curwin->w_cursor.col <
+ (start_visual.col + end_visual.col) / 2) {
+ VIsual = end_visual;
+ } else {
+ VIsual = start_visual;
+ }
+ }
+ }
+ }
+ }
+ } else if ((State & MODE_INSERT) && VIsual_active) {
+ // If Visual mode started in insert mode, execute "CTRL-O"
+ stuffcharReadbuff(Ctrl_O);
+ }
+
+ // Middle mouse click: Put text before cursor.
+ if (which_button == MOUSE_MIDDLE) {
+ if (regname == 0 && eval_has_provider("clipboard")) {
+ regname = '*';
+ }
+ if (yank_register_mline(regname)) {
+ if (mouse_past_bottom) {
+ dir = FORWARD;
+ }
+ } else if (mouse_past_eol) {
+ dir = FORWARD;
+ }
+
+ if (fixindent) {
+ c1 = (dir == BACKWARD) ? '[' : ']';
+ c2 = 'p';
+ } else {
+ c1 = (dir == FORWARD) ? 'p' : 'P';
+ c2 = NUL;
+ }
+ prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
+
+ // Remember where the paste started, so in edit() Insstart can be set to this position
+ if (restart_edit != 0) {
+ where_paste_started = curwin->w_cursor;
+ }
+ do_put(regname, NULL, dir, count,
+ (fixindent ? PUT_FIXINDENT : 0)| PUT_CURSEND);
+ } else if (((mod_mask & MOD_MASK_CTRL) || (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
+ && bt_quickfix(curbuf)) {
+ // Ctrl-Mouse click or double click in a quickfix window jumps to the
+ // error under the mouse pointer.
+ if (curwin->w_llist_ref == NULL) { // quickfix window
+ do_cmdline_cmd(".cc");
+ } else { // location list window
+ do_cmdline_cmd(".ll");
+ }
+ 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
+ // under the mouse pointer.
+ if (State & MODE_INSERT) {
+ stuffcharReadbuff(Ctrl_O);
+ }
+ stuffcharReadbuff(Ctrl_RSB);
+ 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
+ if (State & MODE_INSERT || (VIsual_active && VIsual_select)) {
+ stuffcharReadbuff(Ctrl_O);
+ }
+ if (which_button == MOUSE_LEFT) {
+ stuffcharReadbuff('*');
+ } else { // MOUSE_RIGHT
+ stuffcharReadbuff('#');
+ }
+ } else if (in_status_line || in_sep_line) {
+ // Do nothing if on status line or vertical separator
+ // Handle double clicks otherwise
+ } else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (MODE_NORMAL | MODE_INSERT))) {
+ if (is_click || !VIsual_active) {
+ if (VIsual_active) {
+ orig_cursor = VIsual;
+ } else {
+ VIsual = curwin->w_cursor;
+ orig_cursor = VIsual;
+ VIsual_active = true;
+ VIsual_reselect = true;
+ // start Select mode if 'selectmode' contains "mouse"
+ may_start_select('o');
+ setmouse();
+ }
+ if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
+ // Double click with ALT pressed makes it blockwise.
+ if (mod_mask & MOD_MASK_ALT) {
+ VIsual_mode = Ctrl_V;
+ } else {
+ VIsual_mode = 'v';
+ }
+ } else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK) {
+ VIsual_mode = 'V';
+ } else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK) {
+ VIsual_mode = Ctrl_V;
+ }
+ }
+ // 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;
+ while (gc = gchar_pos(&end_visual), ascii_iswhite(gc)) {
+ inc(&end_visual);
+ }
+ if (oap != NULL) {
+ oap->motion_type = kMTCharWise;
+ }
+ if (oap != NULL
+ && VIsual_mode == 'v'
+ && !vim_iswordc(gchar_pos(&end_visual))
+ && equalpos(curwin->w_cursor, VIsual)
+ && (pos = findmatch(oap, NUL)) != NULL) {
+ curwin->w_cursor = *pos;
+ if (oap->motion_type == kMTLineWise) {
+ VIsual_mode = 'V';
+ } else if (*p_sel == 'e') {
+ if (lt(curwin->w_cursor, VIsual)) {
+ VIsual.col++;
+ } else {
+ curwin->w_cursor.col++;
+ }
+ }
+ }
+ }
+
+ if (pos == NULL && (is_click || is_drag)) {
+ // When not found a match or when dragging: extend to include a word.
+ if (lt(curwin->w_cursor, orig_cursor)) {
+ find_start_of_word(&curwin->w_cursor);
+ find_end_of_word(&VIsual);
+ } else {
+ find_start_of_word(&VIsual);
+ if (*p_sel == 'e' && *get_cursor_pos_ptr() != NUL) {
+ curwin->w_cursor.col +=
+ utfc_ptr2len(get_cursor_pos_ptr());
+ }
+ find_end_of_word(&curwin->w_cursor);
+ }
+ }
+ curwin->w_set_curswant = true;
+ }
+ if (is_click) {
+ redraw_curbuf_later(UPD_INVERTED); // update the inversion
+ }
+ } else if (VIsual_active && !old_active) {
+ if (mod_mask & MOD_MASK_ALT) {
+ VIsual_mode = Ctrl_V;
+ } else {
+ VIsual_mode = 'v';
+ }
+ }
+
+ // If Visual mode changed show it later.
+ if ((!VIsual_active && old_active && mode_displayed)
+ || (VIsual_active && p_smd && msg_silent == 0
+ && (!old_active || VIsual_mode != old_mode))) {
+ redraw_cmdline = true;
+ }
+
+ return moved;
+}
+
/// Return true if "c" is a mouse key.
bool is_mouse_key(int c)
{
@@ -99,6 +962,13 @@ bool is_mouse_key(int c)
|| c == K_X2DRAG
|| c == K_X2RELEASE;
}
+
+/// @return true when 'mousemodel' is set to "popup" or "popup_setpos".
+static bool mouse_model_popup(void)
+{
+ return p_mousem[0] == 'p';
+}
+
/// Move the cursor to the specified row and column on the screen.
/// Change current window if necessary. Returns an integer with the
/// CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
@@ -653,7 +1523,7 @@ void setmouse(void)
// Set orig_topline. Used when jumping to another window, so that a double
// click still works.
-void set_mouse_topline(win_T *wp)
+static void set_mouse_topline(win_T *wp)
{
orig_topline = wp->w_topline;
orig_topfill = wp->w_topfill;
diff --git a/src/nvim/mouse.h b/src/nvim/mouse.h
index 21ff56bbbc..d493a479b1 100644
--- a/src/nvim/mouse.h
+++ b/src/nvim/mouse.h
@@ -4,42 +4,53 @@
#include <stdbool.h>
#include "nvim/buffer_defs.h"
+#include "nvim/normal.h"
#include "nvim/vim.h"
-
-// jump_to_mouse() returns one of first four these values, possibly with
-// some of the other three added.
-#define IN_UNKNOWN 0
-#define IN_BUFFER 1
-#define IN_STATUS_LINE 2 // on status or command line
-#define IN_SEP_LINE 4 // on vertical separator line
-#define IN_OTHER_WIN 8 // in other window but can't go there
-#define CURSOR_MOVED 0x100
-#define MOUSE_FOLD_CLOSE 0x200 // clicked on '-' in fold column
-#define MOUSE_FOLD_OPEN 0x400 // clicked on '+' in fold column
-#define MOUSE_WINBAR 0x800 // in window toolbar
-
-// flags for jump_to_mouse()
-#define MOUSE_FOCUS 0x01 // need to stay in this window
-#define MOUSE_MAY_VIS 0x02 // may start Visual mode
-#define MOUSE_DID_MOVE 0x04 // only act when mouse has moved
-#define MOUSE_SETPOS 0x08 // only set current mouse position
-#define MOUSE_MAY_STOP_VIS 0x10 // may stop Visual mode
-#define MOUSE_RELEASED 0x20 // button was released
-
-// Codes for mouse button events in lower three bits:
-#define MOUSE_LEFT 0x00
-#define MOUSE_MIDDLE 0x01
-#define MOUSE_RIGHT 0x02
-#define MOUSE_RELEASE 0x03
-
-#define MOUSE_X1 0x300 // Mouse-button X1 (6th)
-#define MOUSE_X2 0x400 // Mouse-button X2
-
-// Direction for nv_mousescroll() and ins_mousescroll()
-#define MSCR_DOWN 0 // DOWN must be false
-#define MSCR_UP 1
-#define MSCR_LEFT (-1)
-#define MSCR_RIGHT (-2)
+#include "nvim/window.h"
+
+/// jump_to_mouse() returns one of first five these values, possibly with
+/// some of the other four added.
+enum {
+ IN_UNKNOWN = 0,
+ IN_BUFFER = 1,
+ IN_STATUS_LINE = 2, ///< on status or command line
+ IN_SEP_LINE = 4, ///< on vertical separator line
+ IN_OTHER_WIN = 8, ///< in other window but can't go there
+ CURSOR_MOVED = 0x100,
+ MOUSE_FOLD_CLOSE = 0x200, ///< clicked on '-' in fold column
+ MOUSE_FOLD_OPEN = 0x400, ///< clicked on '+' in fold column
+ MOUSE_WINBAR = 0x800, ///< in window toolbar
+};
+
+/// flags for jump_to_mouse()
+enum {
+ MOUSE_FOCUS = 0x01, ///< need to stay in this window
+ MOUSE_MAY_VIS = 0x02, ///< may start Visual mode
+ MOUSE_DID_MOVE = 0x04, ///< only act when mouse has moved
+ MOUSE_SETPOS = 0x08, ///< only set current mouse position
+ MOUSE_MAY_STOP_VIS = 0x10, ///< may stop Visual mode
+ MOUSE_RELEASED = 0x20, ///< button was released
+};
+
+enum {
+ // Codes for mouse button events in lower three bits:
+ MOUSE_LEFT = 0x00,
+ MOUSE_MIDDLE = 0x01,
+ MOUSE_RIGHT = 0x02,
+ MOUSE_RELEASE = 0x03,
+
+ // mouse buttons that are handled like a key press
+ MOUSE_X1 = 0x300, ///< Mouse-button X1 (6th)
+ MOUSE_X2 = 0x400, ///< Mouse-button X2
+};
+
+/// Direction for nv_mousescroll() and ins_mousescroll()
+enum {
+ MSCR_DOWN = 0, ///< DOWN must be false
+ MSCR_UP = 1,
+ MSCR_LEFT = -1,
+ MSCR_RIGHT = -2,
+};
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "mouse.h.generated.h"
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index a83111136d..6bddd34367 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -45,7 +45,6 @@
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
-#include "nvim/menu.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
@@ -1434,861 +1433,6 @@ static void set_vcount_ca(cmdarg_T *cap, bool *set_prevcount)
*set_prevcount = false; // only set v:prevcount once
}
-/// Move the current tab to tab in same column as mouse or to end of the
-/// tabline if there is no tab there.
-static void move_tab_to_mouse(void)
-{
- int tabnr = tab_page_click_defs[mouse_col].tabnr;
- if (tabnr <= 0) {
- tabpage_move(9999);
- } else if (tabnr < tabpage_index(curtab)) {
- tabpage_move(tabnr - 1);
- } else {
- tabpage_move(tabnr);
- }
-}
-
-/// 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)
-{
- typval_T argv[] = {
- {
- .v_lock = VAR_FIXED,
- .v_type = VAR_NUMBER,
- .vval = {
- .v_number = (varnumber_T)click_defs[col].tabnr
- },
- },
- {
- .v_lock = VAR_FIXED,
- .v_type = VAR_NUMBER,
- .vval = {
- .v_number = ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK
- ? 4
- : ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK
- ? 3
- : ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK
- ? 2
- : 1)))
- },
- },
- {
- .v_lock = VAR_FIXED,
- .v_type = VAR_STRING,
- .vval = {
- .v_string = (which_button == MOUSE_LEFT
- ? "l"
- : (which_button == MOUSE_RIGHT
- ? "r"
- : (which_button == MOUSE_MIDDLE
- ? "m"
- : "?")))
- },
- },
- {
- .v_lock = VAR_FIXED,
- .v_type = VAR_STRING,
- .vval = {
- .v_string = (char[]) {
- (char)(mod_mask & MOD_MASK_SHIFT ? 's' : ' '),
- (char)(mod_mask & MOD_MASK_CTRL ? 'c' : ' '),
- (char)(mod_mask & MOD_MASK_ALT ? 'a' : ' '),
- (char)(mod_mask & MOD_MASK_META ? 'm' : ' '),
- NUL
- }
- },
- }
- };
- typval_T rettv;
- (void)call_vim_function(click_defs[col].func, ARRAY_SIZE(argv), argv, &rettv);
- tv_clear(&rettv);
-}
-
-/// Do the appropriate action for the current mouse click in the current mode.
-/// Not used for Command-line mode.
-///
-/// Normal and Visual Mode:
-/// event modi- position visual change action
-/// fier cursor window
-/// left press - yes end yes
-/// left press C yes end yes "^]" (2)
-/// left press S yes end (popup: extend) yes "*" (2)
-/// left drag - yes start if moved no
-/// left relse - yes start if moved no
-/// middle press - yes if not active no put register
-/// middle press - yes if active no yank and put
-/// right press - yes start or extend yes
-/// right press S yes no change yes "#" (2)
-/// right drag - yes extend no
-/// right relse - yes extend no
-///
-/// Insert or Replace Mode:
-/// event modi- position visual change action
-/// fier cursor window
-/// left press - yes (cannot be active) yes
-/// left press C yes (cannot be active) yes "CTRL-O^]" (2)
-/// left press S yes (cannot be active) yes "CTRL-O*" (2)
-/// left drag - yes start or extend (1) no CTRL-O (1)
-/// left relse - yes start or extend (1) no CTRL-O (1)
-/// middle press - no (cannot be active) no put register
-/// right press - yes start or extend yes CTRL-O
-/// right press S yes (cannot be active) yes "CTRL-O#" (2)
-///
-/// (1) only if mouse pointer moved since press
-/// (2) only if click is in same buffer
-///
-/// @param oap operator argument, can be NULL
-/// @param c K_LEFTMOUSE, etc
-/// @param dir Direction to 'put' if necessary
-/// @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)
-{
- 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
- int jump_flags = 0; // flags for jump_to_mouse()
- pos_T start_visual;
- bool moved; // Has cursor moved?
- bool in_winbar; // mouse in window bar
- 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;
- 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;
-
- for (;;) {
- 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
- // one. Speeds up dragging the status line.
- // Note: Since characters added to the stuff buffer in the code
- // below need to come before the next character, do not do this
- // when the current character was stuffed.
- if (!KeyStuffed && vpeekc() != NUL) {
- int nc;
- int save_mouse_grid = mouse_grid;
- int save_mouse_row = mouse_row;
- int save_mouse_col = mouse_col;
-
- // Need to get the character, peeking doesn't get the actual one.
- nc = safe_vgetc();
- if (c == nc) {
- continue;
- }
- vungetc(nc);
- mouse_grid = save_mouse_grid;
- mouse_row = save_mouse_row;
- mouse_col = save_mouse_col;
- }
- }
- break;
- }
-
- if (c == K_MOUSEMOVE) {
- // Mouse moved without a button pressed.
- return false;
- }
-
- // Ignore drag and release events if we didn't get a click.
- if (is_click) {
- got_click = true;
- } else {
- if (!got_click) { // didn't get click, ignore
- return false;
- }
- if (!is_drag) { // release, reset got_click
- got_click = false;
- if (in_tab_line) {
- in_tab_line = false;
- return false;
- }
- }
- }
-
- // CTRL right mouse button does CTRL-T
- if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT) {
- if (State & MODE_INSERT) {
- stuffcharReadbuff(Ctrl_O);
- }
- if (count > 1) {
- stuffnumReadbuff(count);
- }
- stuffcharReadbuff(Ctrl_T);
- got_click = false; // ignore drag&release now
- return false;
- }
-
- // CTRL only works with left mouse button
- if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT) {
- return false;
- }
-
- // When a modifier is down, ignore drag and release events, as well as
- // multiple clicks and the middle mouse button.
- // Accept shift-leftmouse drags when 'mousemodel' is "popup.*".
- if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT
- | MOD_MASK_META))
- && (!is_click
- || (mod_mask & MOD_MASK_MULTI_CLICK)
- || which_button == MOUSE_MIDDLE)
- && !((mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))
- && mouse_model_popup()
- && which_button == MOUSE_LEFT)
- && !((mod_mask & MOD_MASK_ALT)
- && !mouse_model_popup()
- && which_button == MOUSE_RIGHT)) {
- return false;
- }
-
- // If the button press was used as the movement command for an operator (eg
- // "d<MOUSE>"), or it is the middle button that is held down, ignore
- // drag/release events.
- if (!is_click && which_button == MOUSE_MIDDLE) {
- return false;
- }
-
- if (oap != NULL) {
- regname = oap->regname;
- } else {
- regname = 0;
- }
-
- // Middle mouse button does a 'put' of the selected text
- if (which_button == MOUSE_MIDDLE) {
- if (State == MODE_NORMAL) {
- // If an operator was pending, we don't know what the user wanted to do.
- // Go back to normal mode: Clear the operator and beep().
- if (oap != NULL && oap->op_type != OP_NOP) {
- clearopbeep(oap);
- return false;
- }
-
- // If visual was active, yank the highlighted text and put it
- // before the mouse pointer position.
- // In Select mode replace the highlighted text with the clipboard.
- if (VIsual_active) {
- if (VIsual_select) {
- stuffcharReadbuff(Ctrl_G);
- stuffReadbuff("\"+p");
- } else {
- stuffcharReadbuff('y');
- stuffcharReadbuff(K_MIDDLEMOUSE);
- }
- return false;
- }
- // The rest is below jump_to_mouse()
- } else if ((State & MODE_INSERT) == 0) {
- return false;
- }
-
- // Middle click in insert mode doesn't move the mouse, just insert the
- // contents of a register. '.' register is special, can't insert that
- // with do_put().
- // Also paste at the cursor if the current mode isn't in 'mouse' (only
- // happens for the GUI).
- if ((State & MODE_INSERT)) {
- if (regname == '.') {
- insert_reg(regname, true);
- } else {
- if (regname == 0 && eval_has_provider("clipboard")) {
- regname = '*';
- }
- if ((State & REPLACE_FLAG) && !yank_register_mline(regname)) {
- insert_reg(regname, true);
- } else {
- do_put(regname, NULL, BACKWARD, 1L,
- (fixindent ? PUT_FIXINDENT : 0) | PUT_CURSEND);
-
- // Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r
- AppendCharToRedobuff(Ctrl_R);
- AppendCharToRedobuff(fixindent ? Ctrl_P : Ctrl_O);
- AppendCharToRedobuff(regname == 0 ? '"' : regname);
- }
- }
- return false;
- }
- }
-
- // When dragging or button-up stay in the same window.
- if (!is_click) {
- jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
- }
-
- start_visual.lnum = 0;
-
- // Check for clicking in the tab page line.
- if (mouse_grid <= 1 && mouse_row == 0 && firstwin->w_winrow > 0) {
- if (is_drag) {
- if (in_tab_line) {
- move_tab_to_mouse();
- }
- return false;
- }
-
- // click in a tab selects that tab page
- if (is_click && cmdwin_type == 0 && mouse_col < Columns) {
- in_tab_line = true;
- c1 = tab_page_click_defs[mouse_col].tabnr;
- switch (tab_page_click_defs[mouse_col].type) {
- case kStlClickDisabled:
- break;
- case kStlClickTabClose: {
- tabpage_T *tp;
-
- // Close the current or specified tab page.
- if (c1 == 999) {
- tp = curtab;
- } else {
- tp = find_tabpage(c1);
- }
- if (tp == curtab) {
- if (first_tabpage->tp_next != NULL) {
- tabpage_close(false);
- }
- } else if (tp != NULL) {
- tabpage_close_other(tp, false);
- }
- break;
- }
- case kStlClickTabSwitch:
- if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
- // double click opens new page
- end_visual_mode();
- tabpage_new();
- tabpage_move(c1 == 0 ? 9999 : c1 - 1);
- } else {
- // Go to specified tab page, or next one if not clicking
- // on a label.
- goto_tabpage(c1);
-
- // It's like clicking on the status line of a window.
- if (curwin != old_curwin) {
- end_visual_mode();
- }
- }
- break;
- case kStlClickFuncRun:
- call_click_def_func(tab_page_click_defs, mouse_col, which_button);
- break;
- }
- }
- return true;
- } else if (is_drag && in_tab_line) {
- move_tab_to_mouse();
- return false;
- }
-
- // When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
- // right button up -> pop-up menu
- // shift-left button -> right button
- // alt-left button -> alt-right button
- if (mouse_model_popup()) {
- if (which_button == MOUSE_RIGHT
- && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
- if (!is_click) {
- // Ignore right button release events, only shows the popup
- // menu on the button down event.
- return false;
- }
- jump_flags = 0;
- if (strcmp(p_mousem, "popup_setpos") == 0) {
- // 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) {
- jump_flags = MOUSE_MAY_STOP_VIS;
- } else {
- if (VIsual_mode == 'V') {
- if ((curwin->w_cursor.lnum <= VIsual.lnum
- && (m_pos.lnum < curwin->w_cursor.lnum || VIsual.lnum < m_pos.lnum))
- || (VIsual.lnum < curwin->w_cursor.lnum
- && (m_pos.lnum < VIsual.lnum || curwin->w_cursor.lnum < m_pos.lnum))) {
- jump_flags = MOUSE_MAY_STOP_VIS;
- }
- } else if ((ltoreq(curwin->w_cursor, VIsual)
- && (lt(m_pos, curwin->w_cursor) || lt(VIsual, m_pos)))
- || (lt(VIsual, curwin->w_cursor)
- && (lt(m_pos, VIsual) || lt(curwin->w_cursor, m_pos)))) {
- jump_flags = MOUSE_MAY_STOP_VIS;
- } else if (VIsual_mode == Ctrl_V) {
- getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
- getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
- if (m_pos.col < leftcol || m_pos.col > rightcol) {
- jump_flags = MOUSE_MAY_STOP_VIS;
- }
- }
- }
- } else {
- jump_flags = MOUSE_MAY_STOP_VIS;
- }
- }
- if (jump_flags) {
- jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
- redraw_curbuf_later(VIsual_active ? UPD_INVERTED : UPD_VALID);
- update_screen();
- setcursor();
- ui_flush(); // Update before showing popup menu
- }
- show_popupmenu();
- got_click = false; // ignore release events
- return (jump_flags & CURSOR_MOVED) != 0;
- }
- if (which_button == MOUSE_LEFT
- && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))) {
- which_button = MOUSE_RIGHT;
- mod_mask &= ~MOD_MASK_SHIFT;
- }
- }
-
- if ((State & (MODE_NORMAL | MODE_INSERT))
- && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
- if (which_button == MOUSE_LEFT) {
- if (is_click) {
- // stop Visual mode for a left click in a window, but not when on a status line
- if (VIsual_active) {
- jump_flags |= MOUSE_MAY_STOP_VIS;
- }
- } else {
- jump_flags |= MOUSE_MAY_VIS;
- }
- } else if (which_button == MOUSE_RIGHT) {
- if (is_click && VIsual_active) {
- // Remember the start and end of visual before moving the cursor.
- if (lt(curwin->w_cursor, VIsual)) {
- start_visual = curwin->w_cursor;
- end_visual = VIsual;
- } else {
- start_visual = VIsual;
- end_visual = curwin->w_cursor;
- }
- }
- jump_flags |= MOUSE_FOCUS;
- jump_flags |= MOUSE_MAY_VIS;
- }
- }
-
- // If an operator is pending, ignore all drags and releases until the next mouse click.
- if (!is_drag && oap != NULL && oap->op_type != OP_NOP) {
- got_click = false;
- oap->motion_type = kMTCharWise;
- }
-
- // When releasing the button let jump_to_mouse() know.
- if (!is_click && !is_drag) {
- jump_flags |= MOUSE_RELEASED;
- }
-
- // JUMP!
- jump_flags = jump_to_mouse(jump_flags,
- oap == NULL ? NULL : &(oap->inclusive),
- which_button);
-
- moved = (jump_flags & CURSOR_MOVED);
- in_winbar = (jump_flags & MOUSE_WINBAR);
- in_status_line = (jump_flags & IN_STATUS_LINE);
- in_sep_line = (jump_flags & IN_SEP_LINE);
-
- if ((in_winbar || in_status_line) && is_click) {
- // Handle click event on window bar or status lin
- int click_grid = mouse_grid;
- int click_row = mouse_row;
- int click_col = mouse_col;
- win_T *wp = mouse_find_win(&click_grid, &click_row, &click_col);
- if (wp == NULL) {
- return false;
- }
-
- StlClickDefinition *click_defs = in_status_line ? wp->w_status_click_defs
- : wp->w_winbar_click_defs;
-
- if (in_status_line && global_stl_height() > 0) {
- // global statusline is displayed for the current window,
- // and spans the whole screen.
- click_defs = curwin->w_status_click_defs;
- click_col = mouse_col;
- }
-
- if (click_defs != NULL) {
- switch (click_defs[click_col].type) {
- case kStlClickDisabled:
- break;
- case kStlClickFuncRun:
- call_click_def_func(click_defs, click_col, which_button);
- break;
- default:
- assert(false && "winbar and statusline only support %@ for clicks");
- break;
- }
- }
-
- return false;
- } else if (in_winbar) {
- // A drag or release event in the window bar has no side effects.
- return false;
- }
-
- // When jumping to another window, clear a pending operator. That's a bit
- // friendlier than beeping and not jumping to that window.
- if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP) {
- clearop(oap);
- }
-
- if (mod_mask == 0
- && !is_drag
- && (jump_flags & (MOUSE_FOLD_CLOSE | MOUSE_FOLD_OPEN))
- && which_button == MOUSE_LEFT) {
- // open or close a fold at this line
- if (jump_flags & MOUSE_FOLD_OPEN) {
- openFold(curwin->w_cursor, 1L);
- } else {
- closeFold(curwin->w_cursor, 1L);
- }
- // don't move the cursor if still in the same window
- if (curwin == old_curwin) {
- curwin->w_cursor = save_cursor;
- }
- }
-
- // Set global flag that we are extending the Visual area with mouse dragging;
- // temporarily minimize 'scrolloff'.
- if (VIsual_active && is_drag && get_scrolloff_value(curwin)) {
- // In the very first line, allow scrolling one line
- if (mouse_row == 0) {
- mouse_dragging = 2;
- } else {
- mouse_dragging = 1;
- }
- }
-
- // When dragging the mouse above the window, scroll down.
- if (is_drag && mouse_row < 0 && !in_status_line) {
- scroll_redraw(false, 1L);
- mouse_row = 0;
- }
-
- if (start_visual.lnum) { // right click in visual mode
- // When ALT is pressed make Visual mode blockwise.
- if (mod_mask & MOD_MASK_ALT) {
- VIsual_mode = Ctrl_V;
- }
-
- // In Visual-block mode, divide the area in four, pick up the corner
- // that is in the quarter that the cursor is in.
- if (VIsual_mode == Ctrl_V) {
- getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
- if (curwin->w_curswant > (leftcol + rightcol) / 2) {
- end_visual.col = leftcol;
- } else {
- end_visual.col = rightcol;
- }
- if (curwin->w_cursor.lnum >=
- (start_visual.lnum + end_visual.lnum) / 2) {
- end_visual.lnum = start_visual.lnum;
- }
-
- // move VIsual to the right column
- start_visual = curwin->w_cursor; // save the cursor pos
- curwin->w_cursor = end_visual;
- coladvance(end_visual.col);
- VIsual = curwin->w_cursor;
- curwin->w_cursor = start_visual; // restore the cursor
- } else {
- // If the click is before the start of visual, change the start.
- // If the click is after the end of visual, change the end. If
- // the click is inside the visual, change the closest side.
- if (lt(curwin->w_cursor, start_visual)) {
- VIsual = end_visual;
- } else if (lt(end_visual, curwin->w_cursor)) {
- VIsual = start_visual;
- } else {
- // In the same line, compare column number
- if (end_visual.lnum == start_visual.lnum) {
- if (curwin->w_cursor.col - start_visual.col >
- end_visual.col - curwin->w_cursor.col) {
- VIsual = start_visual;
- } else {
- VIsual = end_visual;
- }
- } else {
- // In different lines, compare line number
- diff = (curwin->w_cursor.lnum - start_visual.lnum) -
- (end_visual.lnum - curwin->w_cursor.lnum);
-
- if (diff > 0) { // closest to end
- VIsual = start_visual;
- } else if (diff < 0) { // closest to start
- VIsual = end_visual;
- } else { // in the middle line
- if (curwin->w_cursor.col <
- (start_visual.col + end_visual.col) / 2) {
- VIsual = end_visual;
- } else {
- VIsual = start_visual;
- }
- }
- }
- }
- }
- } else if ((State & MODE_INSERT) && VIsual_active) {
- // If Visual mode started in insert mode, execute "CTRL-O"
- stuffcharReadbuff(Ctrl_O);
- }
-
- // Middle mouse click: Put text before cursor.
- if (which_button == MOUSE_MIDDLE) {
- if (regname == 0 && eval_has_provider("clipboard")) {
- regname = '*';
- }
- if (yank_register_mline(regname)) {
- if (mouse_past_bottom) {
- dir = FORWARD;
- }
- } else if (mouse_past_eol) {
- dir = FORWARD;
- }
-
- if (fixindent) {
- c1 = (dir == BACKWARD) ? '[' : ']';
- c2 = 'p';
- } else {
- c1 = (dir == FORWARD) ? 'p' : 'P';
- c2 = NUL;
- }
- prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
-
- // Remember where the paste started, so in edit() Insstart can be set to this position
- if (restart_edit != 0) {
- where_paste_started = curwin->w_cursor;
- }
- do_put(regname, NULL, dir, count,
- (fixindent ? PUT_FIXINDENT : 0)| PUT_CURSEND);
- } else if (((mod_mask & MOD_MASK_CTRL) || (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
- && bt_quickfix(curbuf)) {
- // Ctrl-Mouse click or double click in a quickfix window jumps to the
- // error under the mouse pointer.
- if (curwin->w_llist_ref == NULL) { // quickfix window
- do_cmdline_cmd(".cc");
- } else { // location list window
- do_cmdline_cmd(".ll");
- }
- 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
- // under the mouse pointer.
- if (State & MODE_INSERT) {
- stuffcharReadbuff(Ctrl_O);
- }
- stuffcharReadbuff(Ctrl_RSB);
- 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
- if (State & MODE_INSERT || (VIsual_active && VIsual_select)) {
- stuffcharReadbuff(Ctrl_O);
- }
- if (which_button == MOUSE_LEFT) {
- stuffcharReadbuff('*');
- } else { // MOUSE_RIGHT
- stuffcharReadbuff('#');
- }
- } else if (in_status_line || in_sep_line) {
- // Do nothing if on status line or vertical separator
- // Handle double clicks otherwise
- } else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (MODE_NORMAL | MODE_INSERT))) {
- if (is_click || !VIsual_active) {
- if (VIsual_active) {
- orig_cursor = VIsual;
- } else {
- VIsual = curwin->w_cursor;
- orig_cursor = VIsual;
- VIsual_active = true;
- VIsual_reselect = true;
- // start Select mode if 'selectmode' contains "mouse"
- may_start_select('o');
- setmouse();
- }
- if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
- // Double click with ALT pressed makes it blockwise.
- if (mod_mask & MOD_MASK_ALT) {
- VIsual_mode = Ctrl_V;
- } else {
- VIsual_mode = 'v';
- }
- } else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK) {
- VIsual_mode = 'V';
- } else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK) {
- VIsual_mode = Ctrl_V;
- }
- }
- // 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;
- while (gc = gchar_pos(&end_visual), ascii_iswhite(gc)) {
- inc(&end_visual);
- }
- if (oap != NULL) {
- oap->motion_type = kMTCharWise;
- }
- if (oap != NULL
- && VIsual_mode == 'v'
- && !vim_iswordc(gchar_pos(&end_visual))
- && equalpos(curwin->w_cursor, VIsual)
- && (pos = findmatch(oap, NUL)) != NULL) {
- curwin->w_cursor = *pos;
- if (oap->motion_type == kMTLineWise) {
- VIsual_mode = 'V';
- } else if (*p_sel == 'e') {
- if (lt(curwin->w_cursor, VIsual)) {
- VIsual.col++;
- } else {
- curwin->w_cursor.col++;
- }
- }
- }
- }
-
- if (pos == NULL && (is_click || is_drag)) {
- // When not found a match or when dragging: extend to include a word.
- if (lt(curwin->w_cursor, orig_cursor)) {
- find_start_of_word(&curwin->w_cursor);
- find_end_of_word(&VIsual);
- } else {
- find_start_of_word(&VIsual);
- if (*p_sel == 'e' && *get_cursor_pos_ptr() != NUL) {
- curwin->w_cursor.col +=
- utfc_ptr2len(get_cursor_pos_ptr());
- }
- find_end_of_word(&curwin->w_cursor);
- }
- }
- curwin->w_set_curswant = true;
- }
- if (is_click) {
- redraw_curbuf_later(UPD_INVERTED); // update the inversion
- }
- } else if (VIsual_active && !old_active) {
- if (mod_mask & MOD_MASK_ALT) {
- VIsual_mode = Ctrl_V;
- } else {
- VIsual_mode = 'v';
- }
- }
-
- // If Visual mode changed show it later.
- if ((!VIsual_active && old_active && mode_displayed)
- || (VIsual_active && p_smd && msg_silent == 0
- && (!old_active || VIsual_mode != old_mode))) {
- redraw_cmdline = true;
- }
-
- return moved;
-}
-
-/// Move "pos" back to the start of the word it's in.
-static void find_start_of_word(pos_T *pos)
-{
- char_u *line;
- int cclass;
- int col;
-
- line = (char_u *)ml_get(pos->lnum);
- cclass = get_mouse_class(line + pos->col);
-
- while (pos->col > 0) {
- col = pos->col - 1;
- col -= utf_head_off((char *)line, (char *)line + col);
- if (get_mouse_class(line + col) != cclass) {
- break;
- }
- pos->col = col;
- }
-}
-
-/// Move "pos" forward to the end of the word it's in.
-/// When 'selection' is "exclusive", the position is just after the word.
-static void find_end_of_word(pos_T *pos)
-{
- char_u *line;
- int cclass;
- int col;
-
- line = (char_u *)ml_get(pos->lnum);
- if (*p_sel == 'e' && pos->col > 0) {
- pos->col--;
- pos->col -= utf_head_off((char *)line, (char *)line + pos->col);
- }
- cclass = get_mouse_class(line + pos->col);
- while (line[pos->col] != NUL) {
- col = pos->col + utfc_ptr2len((char *)line + pos->col);
- if (get_mouse_class(line + col) != cclass) {
- if (*p_sel == 'e') {
- pos->col = col;
- }
- break;
- }
- pos->col = col;
- }
-}
-
-/// Get class of a character for selection: same class means same word.
-/// 0: blank
-/// 1: punctuation groups
-/// 2: normal word character
-/// >2: multi-byte word character.
-static int get_mouse_class(char_u *p)
-{
- if (MB_BYTE2LEN(p[0]) > 1) {
- return mb_get_class(p);
- }
-
- const int c = *p;
- if (c == ' ' || c == '\t') {
- return 0;
- }
- if (vim_iswordc(c)) {
- return 2;
- }
-
- // There are a few special cases where we want certain combinations of
- // characters to be considered as a single word. These are things like
- // "->", "/ *", "*=", "+=", "&=", "<=", ">=", "!=" etc. Otherwise, each
- // character is in its own class.
- if (c != NUL && vim_strchr("-+*/%<>&|^!=", c) != NULL) {
- return 1;
- }
- return c;
-}
-
/// End Visual mode.
/// This function should ALWAYS be called to end Visual mode, except from
/// do_pending_operator().
@@ -7401,12 +6545,6 @@ static void nv_event(cmdarg_T *cap)
}
}
-/// @return true when 'mousemodel' is set to "popup" or "popup_setpos".
-static bool mouse_model_popup(void)
-{
- return p_mousem[0] == 'p';
-}
-
void normal_cmd(oparg_T *oap, bool toplevel)
{
NormalState s;