diff options
author | zeertzjq <zeertzjq@outlook.com> | 2022-11-01 09:14:25 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-01 09:14:25 +0800 |
commit | c3aba403c6091a56c517e60beb73d9873d4bb8af (patch) | |
tree | 2d4eeaa33947ce74c4748296e6bcc8c4062d17e4 | |
parent | d8dbf58b4372d1415e3ca69541fc6fe71890ba53 (diff) | |
download | rneovim-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.c | 874 | ||||
-rw-r--r-- | src/nvim/mouse.h | 81 | ||||
-rw-r--r-- | src/nvim/normal.c | 862 |
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; |