/* vi:set ts=8 sts=4 sw=4 noet: * * VIM - Vi IMproved by Bram Moolenaar * * Do ":help uganda" in Vim to read copying and usage conditions. * Do ":help credits" in Vim to see a list of people who contributed. * See README.txt for an overview of the Vim source code. */ /* * map.c: functions for maps and abbreviations */ #include "vim.h" /* * List used for abbreviations. */ static mapblock_T *first_abbr = NULL; // first entry in abbrlist /* * Each mapping is put in one of the 256 hash lists, to speed up finding it. */ static mapblock_T *(maphash[256]); static int maphash_valid = FALSE; /* * Make a hash value for a mapping. * "mode" is the lower 4 bits of the State for the mapping. * "c1" is the first character of the "lhs". * Returns a value between 0 and 255, index in maphash. * Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode. */ #define MAP_HASH(mode, c1) (((mode) & (NORMAL + VISUAL + SELECTMODE + OP_PENDING + TERMINAL)) ? (c1) : ((c1) ^ 0x80)) /* * Get the start of the hashed map list for "state" and first character "c". */ mapblock_T * get_maphash_list(int state, int c) { return maphash[MAP_HASH(state, c)]; } /* * Get the buffer-local hashed map list for "state" and first character "c". */ mapblock_T * get_buf_maphash_list(int state, int c) { return curbuf->b_maphash[MAP_HASH(state, c)]; } int is_maphash_valid(void) { return maphash_valid; } /* * Initialize maphash[] for first use. */ static void validate_maphash(void) { if (!maphash_valid) { CLEAR_FIELD(maphash); maphash_valid = TRUE; } } /* * Delete one entry from the abbrlist or maphash[]. * "mpp" is a pointer to the m_next field of the PREVIOUS entry! */ static void map_free(mapblock_T **mpp) { mapblock_T *mp; mp = *mpp; vim_free(mp->m_keys); vim_free(mp->m_str); vim_free(mp->m_orig_str); *mpp = mp->m_next; vim_free(mp); } /* * Return characters to represent the map mode in an allocated string. * Returns NULL when out of memory. */ static char_u * map_mode_to_chars(int mode) { garray_T mapmode; ga_init2(&mapmode, 1, 7); if ((mode & (INSERT + CMDLINE)) == INSERT + CMDLINE) ga_append(&mapmode, '!'); // :map! else if (mode & INSERT) ga_append(&mapmode, 'i'); // :imap else if (mode & LANGMAP) ga_append(&mapmode, 'l'); // :lmap else if (mode & CMDLINE) ga_append(&mapmode, 'c'); // :cmap else if ((mode & (NORMAL + VISUAL + SELECTMODE + OP_PENDING)) == NORMAL + VISUAL + SELECTMODE + OP_PENDING) ga_append(&mapmode, ' '); // :map else { if (mode & NORMAL) ga_append(&mapmode, 'n'); // :nmap if (mode & OP_PENDING) ga_append(&mapmode, 'o'); // :omap if (mode & TERMINAL) ga_append(&mapmode, 't'); // :tmap if ((mode & (VISUAL + SELECTMODE)) == VISUAL + SELECTMODE) ga_append(&mapmode, 'v'); // :vmap else { if (mode & VISUAL) ga_append(&mapmode, 'x'); // :xmap if (mode & SELECTMODE) ga_append(&mapmode, 's'); // :smap } } ga_append(&mapmode, NUL); return (char_u *)mapmode.ga_data; } static void showmap( mapblock_T *mp, int local) // TRUE for buffer-local map { int len = 1; char_u *mapchars; if (message_filtered(mp->m_keys) && message_filtered(mp->m_str)) return; if (msg_didout || msg_silent != 0) { msg_putchar('\n'); if (got_int) // 'q' typed at MORE prompt return; } mapchars = map_mode_to_chars(mp->m_mode); if (mapchars != NULL) { msg_puts((char *)mapchars); len = (int)STRLEN(mapchars); vim_free(mapchars); } while (++len <= 3) msg_putchar(' '); // Display the LHS. Get length of what we write. len = msg_outtrans_special(mp->m_keys, TRUE, 0); do { msg_putchar(' '); // padd with blanks ++len; } while (len < 12); if (mp->m_noremap == REMAP_NONE) msg_puts_attr("*", HL_ATTR(HLF_8)); else if (mp->m_noremap == REMAP_SCRIPT) msg_puts_attr("&", HL_ATTR(HLF_8)); else msg_putchar(' '); if (local) msg_putchar('@'); else msg_putchar(' '); // Use FALSE below if we only want things like to show up as such on // the rhs, and not M-x etc, TRUE gets both -- webb if (*mp->m_str == NUL) msg_puts_attr("", HL_ATTR(HLF_8)); else { // Remove escaping of CSI, because "m_str" is in a format to be used // as typeahead. char_u *s = vim_strsave(mp->m_str); if (s != NULL) { vim_unescape_csi(s); msg_outtrans_special(s, FALSE, 0); vim_free(s); } } #ifdef FEAT_EVAL if (p_verbose > 0) last_set_msg(mp->m_script_ctx); #endif out_flush(); // show one line at a time } static int map_add( mapblock_T **map_table, mapblock_T **abbr_table, char_u *keys, char_u *rhs, char_u *orig_rhs, int noremap, int nowait, int silent, int mode, int is_abbr, #ifdef FEAT_EVAL int expr, scid_T sid, // -1 to use current_sctx linenr_T lnum, #endif int simplified) { mapblock_T *mp = ALLOC_ONE(mapblock_T); if (mp == NULL) return FAIL; // If CTRL-C has been mapped, don't always use it for Interrupting. if (*keys == Ctrl_C) { if (map_table == curbuf->b_maphash) curbuf->b_mapped_ctrl_c |= mode; else mapped_ctrl_c |= mode; } mp->m_keys = vim_strsave(keys); mp->m_str = vim_strsave(rhs); mp->m_orig_str = vim_strsave(orig_rhs); if (mp->m_keys == NULL || mp->m_str == NULL) { vim_free(mp->m_keys); vim_free(mp->m_str); vim_free(mp->m_orig_str); vim_free(mp); return FAIL; } mp->m_keylen = (int)STRLEN(mp->m_keys); mp->m_noremap = noremap; mp->m_nowait = nowait; mp->m_silent = silent; mp->m_mode = mode; mp->m_simplified = simplified; #ifdef FEAT_EVAL mp->m_expr = expr; if (sid >= 0) { mp->m_script_ctx.sc_sid = sid; mp->m_script_ctx.sc_lnum = lnum; } else { mp->m_script_ctx = current_sctx; mp->m_script_ctx.sc_lnum += SOURCING_LNUM; } #endif // add the new entry in front of the abbrlist or maphash[] list if (is_abbr) { mp->m_next = *abbr_table; *abbr_table = mp; } else { int n = MAP_HASH(mp->m_mode, mp->m_keys[0]); mp->m_next = map_table[n]; map_table[n] = mp; } return OK; } /* * map[!] : show all key mappings * map[!] {lhs} : show key mapping for {lhs} * map[!] {lhs} {rhs} : set key mapping for {lhs} to {rhs} * noremap[!] {lhs} {rhs} : same, but no remapping for {rhs} * unmap[!] {lhs} : remove key mapping for {lhs} * abbr : show all abbreviations * abbr {lhs} : show abbreviations for {lhs} * abbr {lhs} {rhs} : set abbreviation for {lhs} to {rhs} * noreabbr {lhs} {rhs} : same, but no remapping for {rhs} * unabbr {lhs} : remove abbreviation for {lhs} * * maptype: 0 for :map, 1 for :unmap, 2 for noremap. * * arg is pointer to any arguments. Note: arg cannot be a read-only string, * it will be modified. * * for :map mode is NORMAL + VISUAL + SELECTMODE + OP_PENDING * for :map! mode is INSERT + CMDLINE * for :cmap mode is CMDLINE * for :imap mode is INSERT * for :lmap mode is LANGMAP * for :nmap mode is NORMAL * for :vmap mode is VISUAL + SELECTMODE * for :xmap mode is VISUAL * for :smap mode is SELECTMODE * for :omap mode is OP_PENDING * for :tmap mode is TERMINAL * * for :abbr mode is INSERT + CMDLINE * for :iabbr mode is INSERT * for :cabbr mode is CMDLINE * * Return 0 for success * 1 for invalid arguments * 2 for no match * 4 for out of mem * 5 for entry not unique */ int do_map( int maptype, char_u *arg, int mode, int abbrev) // not a mapping but an abbreviation { char_u *keys; mapblock_T *mp, **mpp; char_u *rhs; char_u *p; int n; int len = 0; // init for GCC int hasarg; int haskey; int do_print; int keyround; char_u *keys_buf = NULL; char_u *alt_keys_buf = NULL; char_u *arg_buf = NULL; int retval = 0; int do_backslash; mapblock_T **abbr_table; mapblock_T **map_table; int unique = FALSE; int nowait = FALSE; int silent = FALSE; int special = FALSE; #ifdef FEAT_EVAL int expr = FALSE; #endif int did_simplify = FALSE; int noremap; char_u *orig_rhs; keys = arg; map_table = maphash; abbr_table = &first_abbr; // For ":noremap" don't remap, otherwise do remap. if (maptype == 2) noremap = REMAP_NONE; else noremap = REMAP_YES; // Accept , , ,