/// Pass input keys to Neovim /// /// @param keys to be typed /// @param mode specifies the mapping options /// @see feedkeys() void vim_feedkeys(String keys, String mode) { bool remap = true; bool typed = false; if (keys.size == 0) { return; } for (size_t i = 0; i < mode.size; ++i) { switch (mode.data[i]) { case 'n': remap = false; break; case 'm': remap = true; break; case 't': typed = true; break; } } /* Need to escape K_SPECIAL and CSI before putting the string in the * typeahead buffer. */ char *keys_esc = (char *)vim_strsave_escape_csi((char_u *)keys.data); ins_typebuf((char_u *)keys_esc, (remap ? REMAP_YES : REMAP_NONE), typebuf.tb_len, !typed, false); free(keys_esc); if (vgetc_busy) typebuf_was_filled = true; }
/// Passes input keys to Nvim. /// On VimL error: Does not fail, but updates v:errmsg. /// /// @param keys to be typed /// @param mode mapping options /// @param escape_csi If true, escape K_SPECIAL/CSI bytes in `keys` /// @see feedkeys() /// @see vim_strsave_escape_csi void nvim_feedkeys(String keys, String mode, Boolean escape_csi) { bool remap = true; bool insert = false; bool typed = false; bool execute = false; bool dangerous = false; for (size_t i = 0; i < mode.size; ++i) { switch (mode.data[i]) { case 'n': remap = false; break; case 'm': remap = true; break; case 't': typed = true; break; case 'i': insert = true; break; case 'x': execute = true; break; case '!': dangerous = true; break; } } if (keys.size == 0 && !execute) { return; } char *keys_esc; if (escape_csi) { // Need to escape K_SPECIAL and CSI before putting the string in the // typeahead buffer. keys_esc = (char *)vim_strsave_escape_csi((char_u *)keys.data); } else { keys_esc = keys.data; } ins_typebuf((char_u *)keys_esc, (remap ? REMAP_YES : REMAP_NONE), insert ? 0 : typebuf.tb_len, !typed, false); if (escape_csi) { xfree(keys_esc); } if (vgetc_busy) { typebuf_was_filled = true; } if (execute) { int save_msg_scroll = msg_scroll; /* Avoid a 1 second delay when the keys start Insert mode. */ msg_scroll = false; if (!dangerous) { ex_normal_busy++; } exec_normal(true); if (!dangerous) { ex_normal_busy--; } msg_scroll |= save_msg_scroll; } }
/// Pass input keys to Neovim /// /// @param keys to be typed /// @param replace_tcodes If true replace special keys such as <CR> or <Leader> /// for compatibility with Vim --remote-send expressions /// @param remap If True remap keys /// @param typed Handle keys as if typed; otherwise they are handled as /// if coming from a mapping. This matters for undo, /// opening folds, etc. void vim_feedkeys(String keys, Boolean replace_tcodes, Boolean remap, Boolean typed, Error *err) { char *ptr = NULL; char *cpo_save = (char *)p_cpo; if (replace_tcodes) { // Set 'cpoptions' the way we want it. // B set - backslashes are *not* treated specially // k set - keycodes are *not* reverse-engineered // < unset - <Key> sequences *are* interpreted // The last but one parameter of replace_termcodes() is TRUE so that the // <lt> sequence is recognised - needed for a real backslash. p_cpo = (char_u *)"Bk"; replace_termcodes((char_u *)keys.data, (char_u **)&ptr, false, true, true); p_cpo = (char_u *)cpo_save; } else { ptr = keys.data; } if (ptr == NULL) { set_api_error("Failed to eval expression", err); } else { // Add the string to the input stream. // Can't use add_to_input_buf() here, we now have K_SPECIAL bytes. // // First clear typed characters from the typeahead buffer, there could // be half a mapping there. Then append to the existing string, so // that multiple commands from a client are concatenated. if (typebuf.tb_maplen < typebuf.tb_len) { del_typebuf(typebuf.tb_len - typebuf.tb_maplen, typebuf.tb_maplen); } (void)ins_typebuf((char_u *)ptr, (remap ? REMAP_YES : REMAP_NONE), typebuf.tb_len, !typed, false); // Let input_available() know we inserted text in the typeahead // buffer. */ typebuf_was_filled = true; if (replace_tcodes) { free(ptr); } } }
/// Passes input keys to Neovim /// /// @param keys to be typed /// @param mode specifies the mapping options /// @param escape_csi the string needs escaping for K_SPECIAL/CSI bytes /// @see feedkeys() /// @see vim_strsave_escape_csi void vim_feedkeys(String keys, String mode, Boolean escape_csi) { bool remap = true; bool insert = false; bool typed = false; bool execute = false; if (keys.size == 0) { return; } for (size_t i = 0; i < mode.size; ++i) { switch (mode.data[i]) { case 'n': remap = false; break; case 'm': remap = true; break; case 't': typed = true; break; case 'i': insert = true; break; case 'x': execute = true; break; } } char *keys_esc; if (escape_csi) { // Need to escape K_SPECIAL and CSI before putting the string in the // typeahead buffer. keys_esc = (char *)vim_strsave_escape_csi((char_u *)keys.data); } else { keys_esc = keys.data; } ins_typebuf((char_u *)keys_esc, (remap ? REMAP_YES : REMAP_NONE), insert ? 0 : typebuf.tb_len, !typed, false); if (escape_csi) { xfree(keys_esc); } if (vgetc_busy) { typebuf_was_filled = true; } if (execute) { exec_normal(true); } }
/* * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and * execute it. */ void ex_emenu(exarg_T *eap) { vimmenu_T *menu; char_u *name; char_u *saved_name; char_u *p; int idx; char_u *mode; saved_name = vim_strsave(eap->arg); menu = root_menu; name = saved_name; while (*name) { /* Find in the menu hierarchy */ p = menu_name_skip(name); while (menu != NULL) { if (menu_name_equal(name, menu)) { if (*p == NUL && menu->children != NULL) { EMSG(_("E333: Menu path must lead to a menu item")); menu = NULL; } else if (*p != NUL && menu->children == NULL) { EMSG(_(e_notsubmenu)); menu = NULL; } break; } menu = menu->next; } if (menu == NULL || *p == NUL) break; menu = menu->children; name = p; } free(saved_name); if (menu == NULL) { EMSG2(_("E334: Menu not found: %s"), eap->arg); return; } /* Found the menu, so execute. * Use the Insert mode entry when returning to Insert mode. */ if (restart_edit && !current_SID ) { mode = (char_u *)"Insert"; idx = MENU_INDEX_INSERT; } else if (eap->addr_count) { pos_T tpos; mode = (char_u *)"Visual"; idx = MENU_INDEX_VISUAL; /* GEDDES: This is not perfect - but it is a * quick way of detecting whether we are doing this from a * selection - see if the range matches up with the visual * select start and end. */ if ((curbuf->b_visual.vi_start.lnum == eap->line1) && (curbuf->b_visual.vi_end.lnum) == eap->line2) { /* Set it up for visual mode - equivalent to gv. */ VIsual_mode = curbuf->b_visual.vi_mode; tpos = curbuf->b_visual.vi_end; curwin->w_cursor = curbuf->b_visual.vi_start; curwin->w_curswant = curbuf->b_visual.vi_curswant; } else { /* Set it up for line-wise visual mode */ VIsual_mode = 'V'; curwin->w_cursor.lnum = eap->line1; curwin->w_cursor.col = 1; tpos.lnum = eap->line2; tpos.col = MAXCOL; tpos.coladd = 0; } /* Activate visual mode */ VIsual_active = TRUE; VIsual_reselect = TRUE; check_cursor(); VIsual = curwin->w_cursor; curwin->w_cursor = tpos; check_cursor(); /* Adjust the cursor to make sure it is in the correct pos * for exclusive mode */ if (*p_sel == 'e' && gchar_cursor() != NUL) ++curwin->w_cursor.col; } else { mode = (char_u *)"Normal"; idx = MENU_INDEX_NORMAL; } if (idx != MENU_INDEX_INVALID && menu->strings[idx] != NULL) { /* When executing a script or function execute the commands right now. * Otherwise put them in the typeahead buffer. */ if (current_SID != 0) exec_normal_cmd(menu->strings[idx], menu->noremap[idx], menu->silent[idx]); else ins_typebuf(menu->strings[idx], menu->noremap[idx], 0, TRUE, menu->silent[idx]); } else EMSG2(_("E335: Menu not defined for %s mode"), mode); }