Mercurial > vim
view src/getchar.c @ 2894:fe9c7da98b5e v7.3.220
updated for version 7.3.220
Problem: Python 3: vim.error is a 'str' instead of an 'Exception' object,
so 'except' or 'raise' it causes a 'SystemError' exception.
Buffer objects do not support slice assignment.
When exchanging text between Vim and Python, multibyte texts become
gabage or cause Unicode Expceptions, etc.
'py3file' tries to read in the file as Unicode, sometimes causes
UnicodeDecodeException
Solution: Fix the problems. (lilydjwg)
author | Bram Moolenaar <bram@vim.org> |
---|---|
date | Sun, 19 Jun 2011 00:27:51 +0200 |
parents | 930b171ce3fc |
children | 47b6d37b6012 |
line wrap: on
line source
/* vi:set ts=8 sts=4 sw=4: * * 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. */ /* * getchar.c * * functions related with getting a character from the user/mapping/redo/... * * manipulations with redo buffer and stuff buffer * mappings and abbreviations */ #include "vim.h" /* * These buffers are used for storing: * - stuffed characters: A command that is translated into another command. * - redo characters: will redo the last change. * - recorded characters: for the "q" command. * * The bytes are stored like in the typeahead buffer: * - K_SPECIAL introduces a special key (two more bytes follow). A literal * K_SPECIAL is stored as K_SPECIAL KS_SPECIAL KE_FILLER. * - CSI introduces a GUI termcap code (also when gui.in_use is FALSE, * otherwise switching the GUI on would make mappings invalid). * A literal CSI is stored as CSI KS_EXTRA KE_CSI. * These translations are also done on multi-byte characters! * * Escaping CSI bytes is done by the system-specific input functions, called * by ui_inchar(). * Escaping K_SPECIAL is done by inchar(). * Un-escaping is done by vgetc(). */ #define MINIMAL_SIZE 20 /* minimal size for b_str */ static struct buffheader redobuff = {{NULL, {NUL}}, NULL, 0, 0}; static struct buffheader old_redobuff = {{NULL, {NUL}}, NULL, 0, 0}; #if defined(FEAT_AUTOCMD) || defined(FEAT_EVAL) || defined(PROTO) static struct buffheader save_redobuff = {{NULL, {NUL}}, NULL, 0, 0}; static struct buffheader save_old_redobuff = {{NULL, {NUL}}, NULL, 0, 0}; #endif static struct buffheader recordbuff = {{NULL, {NUL}}, NULL, 0, 0}; static int typeahead_char = 0; /* typeahead char that's not flushed */ /* * when block_redo is TRUE redo buffer will not be changed * used by edit() to repeat insertions and 'V' command for redoing */ static int block_redo = 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)) ? (c1) : ((c1) ^ 0x80)) /* * 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; /* * List used for abbreviations. */ static mapblock_T *first_abbr = NULL; /* first entry in abbrlist */ static int KeyNoremap = 0; /* remapping flags */ /* * variables used by vgetorpeek() and flush_buffers() * * typebuf.tb_buf[] contains all characters that are not consumed yet. * typebuf.tb_buf[typebuf.tb_off] is the first valid character. * typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len - 1] is the last valid char. * typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len] must be NUL. * The head of the buffer may contain the result of mappings, abbreviations * and @a commands. The length of this part is typebuf.tb_maplen. * typebuf.tb_silent is the part where <silent> applies. * After the head are characters that come from the terminal. * typebuf.tb_no_abbr_cnt is the number of characters in typebuf.tb_buf that * should not be considered for abbreviations. * Some parts of typebuf.tb_buf may not be mapped. These parts are remembered * in typebuf.tb_noremap[], which is the same length as typebuf.tb_buf and * contains RM_NONE for the characters that are not to be remapped. * typebuf.tb_noremap[typebuf.tb_off] is the first valid flag. * (typebuf has been put in globals.h, because check_termcode() needs it). */ #define RM_YES 0 /* tb_noremap: remap */ #define RM_NONE 1 /* tb_noremap: don't remap */ #define RM_SCRIPT 2 /* tb_noremap: remap local script mappings */ #define RM_ABBR 4 /* tb_noremap: don't remap, do abbrev. */ /* typebuf.tb_buf has three parts: room in front (for result of mappings), the * middle for typeahead and room for new characters (which needs to be 3 * * MAXMAPLEN) for the Amiga). */ #define TYPELEN_INIT (5 * (MAXMAPLEN + 3)) static char_u typebuf_init[TYPELEN_INIT]; /* initial typebuf.tb_buf */ static char_u noremapbuf_init[TYPELEN_INIT]; /* initial typebuf.tb_noremap */ static int last_recorded_len = 0; /* number of last recorded chars */ static char_u *get_buffcont __ARGS((struct buffheader *, int)); static void add_buff __ARGS((struct buffheader *, char_u *, long n)); static void add_num_buff __ARGS((struct buffheader *, long)); static void add_char_buff __ARGS((struct buffheader *, int)); static int read_stuff __ARGS((int advance)); static void start_stuff __ARGS((void)); static int read_redo __ARGS((int, int)); static void copy_redo __ARGS((int)); static void init_typebuf __ARGS((void)); static void gotchars __ARGS((char_u *, int)); static void may_sync_undo __ARGS((void)); static void closescript __ARGS((void)); static int vgetorpeek __ARGS((int)); static void map_free __ARGS((mapblock_T **)); static void validate_maphash __ARGS((void)); static void showmap __ARGS((mapblock_T *mp, int local)); #ifdef FEAT_EVAL static char_u *eval_map_expr __ARGS((char_u *str, int c)); #endif /* * Free and clear a buffer. */ void free_buff(buf) struct buffheader *buf; { struct buffblock *p, *np; for (p = buf->bh_first.b_next; p != NULL; p = np) { np = p->b_next; vim_free(p); } buf->bh_first.b_next = NULL; } /* * Return the contents of a buffer as a single string. * K_SPECIAL and CSI in the returned string are escaped. */ static char_u * get_buffcont(buffer, dozero) struct buffheader *buffer; int dozero; /* count == zero is not an error */ { long_u count = 0; char_u *p = NULL; char_u *p2; char_u *str; struct buffblock *bp; /* compute the total length of the string */ for (bp = buffer->bh_first.b_next; bp != NULL; bp = bp->b_next) count += (long_u)STRLEN(bp->b_str); if ((count || dozero) && (p = lalloc(count + 1, TRUE)) != NULL) { p2 = p; for (bp = buffer->bh_first.b_next; bp != NULL; bp = bp->b_next) for (str = bp->b_str; *str; ) *p2++ = *str++; *p2 = NUL; } return (p); } /* * Return the contents of the record buffer as a single string * and clear the record buffer. * K_SPECIAL and CSI in the returned string are escaped. */ char_u * get_recorded() { char_u *p; size_t len; p = get_buffcont(&recordbuff, TRUE); free_buff(&recordbuff); /* * Remove the characters that were added the last time, these must be the * (possibly mapped) characters that stopped the recording. */ len = STRLEN(p); if ((int)len >= last_recorded_len) { len -= last_recorded_len; p[len] = NUL; } /* * When stopping recording from Insert mode with CTRL-O q, also remove the * CTRL-O. */ if (len > 0 && restart_edit != 0 && p[len - 1] == Ctrl_O) p[len - 1] = NUL; return (p); } /* * Return the contents of the redo buffer as a single string. * K_SPECIAL and CSI in the returned string are escaped. */ char_u * get_inserted() { return get_buffcont(&redobuff, FALSE); } /* * Add string "s" after the current block of buffer "buf". * K_SPECIAL and CSI should have been escaped already. */ static void add_buff(buf, s, slen) struct buffheader *buf; char_u *s; long slen; /* length of "s" or -1 */ { struct buffblock *p; long_u len; if (slen < 0) slen = (long)STRLEN(s); if (slen == 0) /* don't add empty strings */ return; if (buf->bh_first.b_next == NULL) /* first add to list */ { buf->bh_space = 0; buf->bh_curr = &(buf->bh_first); } else if (buf->bh_curr == NULL) /* buffer has already been read */ { EMSG(_("E222: Add to read buffer")); return; } else if (buf->bh_index != 0) mch_memmove(buf->bh_first.b_next->b_str, buf->bh_first.b_next->b_str + buf->bh_index, STRLEN(buf->bh_first.b_next->b_str + buf->bh_index) + 1); buf->bh_index = 0; if (buf->bh_space >= (int)slen) { len = (long_u)STRLEN(buf->bh_curr->b_str); vim_strncpy(buf->bh_curr->b_str + len, s, (size_t)slen); buf->bh_space -= slen; } else { if (slen < MINIMAL_SIZE) len = MINIMAL_SIZE; else len = slen; p = (struct buffblock *)lalloc((long_u)(sizeof(struct buffblock) + len), TRUE); if (p == NULL) return; /* no space, just forget it */ buf->bh_space = (int)(len - slen); vim_strncpy(p->b_str, s, (size_t)slen); p->b_next = buf->bh_curr->b_next; buf->bh_curr->b_next = p; buf->bh_curr = p; } return; } /* * Add number "n" to buffer "buf". */ static void add_num_buff(buf, n) struct buffheader *buf; long n; { char_u number[32]; sprintf((char *)number, "%ld", n); add_buff(buf, number, -1L); } /* * Add character 'c' to buffer "buf". * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters. */ static void add_char_buff(buf, c) struct buffheader *buf; int c; { #ifdef FEAT_MBYTE char_u bytes[MB_MAXBYTES + 1]; int len; int i; #endif char_u temp[4]; #ifdef FEAT_MBYTE if (IS_SPECIAL(c)) len = 1; else len = (*mb_char2bytes)(c, bytes); for (i = 0; i < len; ++i) { if (!IS_SPECIAL(c)) c = bytes[i]; #endif if (IS_SPECIAL(c) || c == K_SPECIAL || c == NUL) { /* translate special key code into three byte sequence */ temp[0] = K_SPECIAL; temp[1] = K_SECOND(c); temp[2] = K_THIRD(c); temp[3] = NUL; } #ifdef FEAT_GUI else if (c == CSI) { /* Translate a CSI to a CSI - KS_EXTRA - KE_CSI sequence */ temp[0] = CSI; temp[1] = KS_EXTRA; temp[2] = (int)KE_CSI; temp[3] = NUL; } #endif else { temp[0] = c; temp[1] = NUL; } add_buff(buf, temp, -1L); #ifdef FEAT_MBYTE } #endif } /* * Get one byte from the stuff buffer. * If advance == TRUE go to the next char. * No translation is done K_SPECIAL and CSI are escaped. */ static int read_stuff(advance) int advance; { char_u c; struct buffblock *curr; if (stuffbuff.bh_first.b_next == NULL) /* buffer is empty */ return NUL; curr = stuffbuff.bh_first.b_next; c = curr->b_str[stuffbuff.bh_index]; if (advance) { if (curr->b_str[++stuffbuff.bh_index] == NUL) { stuffbuff.bh_first.b_next = curr->b_next; vim_free(curr); stuffbuff.bh_index = 0; } } return c; } /* * Prepare the stuff buffer for reading (if it contains something). */ static void start_stuff() { if (stuffbuff.bh_first.b_next != NULL) { stuffbuff.bh_curr = &(stuffbuff.bh_first); stuffbuff.bh_space = 0; } } /* * Return TRUE if the stuff buffer is empty. */ int stuff_empty() { return (stuffbuff.bh_first.b_next == NULL); } /* * Set a typeahead character that won't be flushed. */ void typeahead_noflush(c) int c; { typeahead_char = c; } /* * Remove the contents of the stuff buffer and the mapped characters in the * typeahead buffer (used in case of an error). If 'typeahead' is true, * flush all typeahead characters (used when interrupted by a CTRL-C). */ void flush_buffers(typeahead) int typeahead; { init_typebuf(); start_stuff(); while (read_stuff(TRUE) != NUL) ; if (typeahead) /* remove all typeahead */ { /* * We have to get all characters, because we may delete the first part * of an escape sequence. * In an xterm we get one char at a time and we have to get them all. */ while (inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 10L, typebuf.tb_change_cnt) != 0) ; typebuf.tb_off = MAXMAPLEN; typebuf.tb_len = 0; } else /* remove mapped characters only */ { typebuf.tb_off += typebuf.tb_maplen; typebuf.tb_len -= typebuf.tb_maplen; } typebuf.tb_maplen = 0; typebuf.tb_silent = 0; cmd_silent = FALSE; typebuf.tb_no_abbr_cnt = 0; } /* * The previous contents of the redo buffer is kept in old_redobuffer. * This is used for the CTRL-O <.> command in insert mode. */ void ResetRedobuff() { if (!block_redo) { free_buff(&old_redobuff); old_redobuff = redobuff; redobuff.bh_first.b_next = NULL; } } #if defined(FEAT_AUTOCMD) || defined(FEAT_EVAL) || defined(PROTO) /* * Save redobuff and old_redobuff to save_redobuff and save_old_redobuff. * Used before executing autocommands and user functions. */ static int save_level = 0; void saveRedobuff() { char_u *s; if (save_level++ == 0) { save_redobuff = redobuff; redobuff.bh_first.b_next = NULL; save_old_redobuff = old_redobuff; old_redobuff.bh_first.b_next = NULL; /* Make a copy, so that ":normal ." in a function works. */ s = get_buffcont(&save_redobuff, FALSE); if (s != NULL) { add_buff(&redobuff, s, -1L); vim_free(s); } } } /* * Restore redobuff and old_redobuff from save_redobuff and save_old_redobuff. * Used after executing autocommands and user functions. */ void restoreRedobuff() { if (--save_level == 0) { free_buff(&redobuff); redobuff = save_redobuff; free_buff(&old_redobuff); old_redobuff = save_old_redobuff; } } #endif /* * Append "s" to the redo buffer. * K_SPECIAL and CSI should already have been escaped. */ void AppendToRedobuff(s) char_u *s; { if (!block_redo) add_buff(&redobuff, s, -1L); } /* * Append to Redo buffer literally, escaping special characters with CTRL-V. * K_SPECIAL and CSI are escaped as well. */ void AppendToRedobuffLit(str, len) char_u *str; int len; /* length of "str" or -1 for up to the NUL */ { char_u *s = str; int c; char_u *start; if (block_redo) return; while (len < 0 ? *s != NUL : s - str < len) { /* Put a string of normal characters in the redo buffer (that's * faster). */ start = s; while (*s >= ' ' #ifndef EBCDIC && *s < DEL /* EBCDIC: all chars above space are normal */ #endif && (len < 0 || s - str < len)) ++s; /* Don't put '0' or '^' as last character, just in case a CTRL-D is * typed next. */ if (*s == NUL && (s[-1] == '0' || s[-1] == '^')) --s; if (s > start) add_buff(&redobuff, start, (long)(s - start)); if (*s == NUL || (len >= 0 && s - str >= len)) break; /* Handle a special or multibyte character. */ #ifdef FEAT_MBYTE if (has_mbyte) /* Handle composing chars separately. */ c = mb_cptr2char_adv(&s); else #endif c = *s++; if (c < ' ' || c == DEL || (*s == NUL && (c == '0' || c == '^'))) add_char_buff(&redobuff, Ctrl_V); /* CTRL-V '0' must be inserted as CTRL-V 048 (EBCDIC: xf0) */ if (*s == NUL && c == '0') #ifdef EBCDIC add_buff(&redobuff, (char_u *)"xf0", 3L); #else add_buff(&redobuff, (char_u *)"048", 3L); #endif else add_char_buff(&redobuff, c); } } /* * Append a character to the redo buffer. * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters. */ void AppendCharToRedobuff(c) int c; { if (!block_redo) add_char_buff(&redobuff, c); } /* * Append a number to the redo buffer. */ void AppendNumberToRedobuff(n) long n; { if (!block_redo) add_num_buff(&redobuff, n); } /* * Append string "s" to the stuff buffer. * CSI and K_SPECIAL must already have been escaped. */ void stuffReadbuff(s) char_u *s; { add_buff(&stuffbuff, s, -1L); } void stuffReadbuffLen(s, len) char_u *s; long len; { add_buff(&stuffbuff, s, len); } #if defined(FEAT_EVAL) || defined(PROTO) /* * Stuff "s" into the stuff buffer, leaving special key codes unmodified and * escaping other K_SPECIAL and CSI bytes. * Change CR, LF and ESC into a space. */ void stuffReadbuffSpec(s) char_u *s; { int c; while (*s != NUL) { if (*s == K_SPECIAL && s[1] != NUL && s[2] != NUL) { /* Insert special key literally. */ stuffReadbuffLen(s, 3L); s += 3; } else { #ifdef FEAT_MBYTE c = mb_ptr2char_adv(&s); #else c = *s++; #endif if (c == CAR || c == NL || c == ESC) c = ' '; stuffcharReadbuff(c); } } } #endif /* * Append a character to the stuff buffer. * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters. */ void stuffcharReadbuff(c) int c; { add_char_buff(&stuffbuff, c); } /* * Append a number to the stuff buffer. */ void stuffnumReadbuff(n) long n; { add_num_buff(&stuffbuff, n); } /* * Read a character from the redo buffer. Translates K_SPECIAL, CSI and * multibyte characters. * The redo buffer is left as it is. * if init is TRUE, prepare for redo, return FAIL if nothing to redo, OK * otherwise * if old is TRUE, use old_redobuff instead of redobuff */ static int read_redo(init, old_redo) int init; int old_redo; { static struct buffblock *bp; static char_u *p; int c; #ifdef FEAT_MBYTE int n; char_u buf[MB_MAXBYTES]; int i; #endif if (init) { if (old_redo) bp = old_redobuff.bh_first.b_next; else bp = redobuff.bh_first.b_next; if (bp == NULL) return FAIL; p = bp->b_str; return OK; } if ((c = *p) != NUL) { /* Reverse the conversion done by add_char_buff() */ #ifdef FEAT_MBYTE /* For a multi-byte character get all the bytes and return the * converted character. */ if (has_mbyte && (c != K_SPECIAL || p[1] == KS_SPECIAL)) n = MB_BYTE2LEN_CHECK(c); else n = 1; for (i = 0; ; ++i) #endif { if (c == K_SPECIAL) /* special key or escaped K_SPECIAL */ { c = TO_SPECIAL(p[1], p[2]); p += 2; } #ifdef FEAT_GUI if (c == CSI) /* escaped CSI */ p += 2; #endif if (*++p == NUL && bp->b_next != NULL) { bp = bp->b_next; p = bp->b_str; } #ifdef FEAT_MBYTE buf[i] = c; if (i == n - 1) /* last byte of a character */ { if (n != 1) c = (*mb_ptr2char)(buf); break; } c = *p; if (c == NUL) /* cannot happen? */ break; #endif } } return c; } /* * Copy the rest of the redo buffer into the stuff buffer (in a slow way). * If old_redo is TRUE, use old_redobuff instead of redobuff. * The escaped K_SPECIAL and CSI are copied without translation. */ static void copy_redo(old_redo) int old_redo; { int c; while ((c = read_redo(FALSE, old_redo)) != NUL) stuffcharReadbuff(c); } /* * Stuff the redo buffer into the stuffbuff. * Insert the redo count into the command. * If "old_redo" is TRUE, the last but one command is repeated * instead of the last command (inserting text). This is used for * CTRL-O <.> in insert mode * * return FAIL for failure, OK otherwise */ int start_redo(count, old_redo) long count; int old_redo; { int c; /* init the pointers; return if nothing to redo */ if (read_redo(TRUE, old_redo) == FAIL) return FAIL; c = read_redo(FALSE, old_redo); /* copy the buffer name, if present */ if (c == '"') { add_buff(&stuffbuff, (char_u *)"\"", 1L); c = read_redo(FALSE, old_redo); /* if a numbered buffer is used, increment the number */ if (c >= '1' && c < '9') ++c; add_char_buff(&stuffbuff, c); c = read_redo(FALSE, old_redo); } #ifdef FEAT_VISUAL if (c == 'v') /* redo Visual */ { VIsual = curwin->w_cursor; VIsual_active = TRUE; VIsual_select = FALSE; VIsual_reselect = TRUE; redo_VIsual_busy = TRUE; c = read_redo(FALSE, old_redo); } #endif /* try to enter the count (in place of a previous count) */ if (count) { while (VIM_ISDIGIT(c)) /* skip "old" count */ c = read_redo(FALSE, old_redo); add_num_buff(&stuffbuff, count); } /* copy from the redo buffer into the stuff buffer */ add_char_buff(&stuffbuff, c); copy_redo(old_redo); return OK; } /* * Repeat the last insert (R, o, O, a, A, i or I command) by stuffing * the redo buffer into the stuffbuff. * return FAIL for failure, OK otherwise */ int start_redo_ins() { int c; if (read_redo(TRUE, FALSE) == FAIL) return FAIL; start_stuff(); /* skip the count and the command character */ while ((c = read_redo(FALSE, FALSE)) != NUL) { if (vim_strchr((char_u *)"AaIiRrOo", c) != NULL) { if (c == 'O' || c == 'o') stuffReadbuff(NL_STR); break; } } /* copy the typed text from the redo buffer into the stuff buffer */ copy_redo(FALSE); block_redo = TRUE; return OK; } void stop_redo_ins() { block_redo = FALSE; } /* * Initialize typebuf.tb_buf to point to typebuf_init. * alloc() cannot be used here: In out-of-memory situations it would * be impossible to type anything. */ static void init_typebuf() { if (typebuf.tb_buf == NULL) { typebuf.tb_buf = typebuf_init; typebuf.tb_noremap = noremapbuf_init; typebuf.tb_buflen = TYPELEN_INIT; typebuf.tb_len = 0; typebuf.tb_off = 0; typebuf.tb_change_cnt = 1; } } /* * insert a string in position 'offset' in the typeahead buffer (for "@r" * and ":normal" command, vgetorpeek() and check_termcode()) * * If noremap is REMAP_YES, new string can be mapped again. * If noremap is REMAP_NONE, new string cannot be mapped again. * If noremap is REMAP_SKIP, fist char of new string cannot be mapped again, * but abbreviations are allowed. * If noremap is REMAP_SCRIPT, new string cannot be mapped again, except for * script-local mappings. * If noremap is > 0, that many characters of the new string cannot be mapped. * * If nottyped is TRUE, the string does not return KeyTyped (don't use when * offset is non-zero!). * * If silent is TRUE, cmd_silent is set when the characters are obtained. * * return FAIL for failure, OK otherwise */ int ins_typebuf(str, noremap, offset, nottyped, silent) char_u *str; int noremap; int offset; int nottyped; int silent; { char_u *s1, *s2; int newlen; int addlen; int i; int newoff; int val; int nrm; init_typebuf(); if (++typebuf.tb_change_cnt == 0) typebuf.tb_change_cnt = 1; addlen = (int)STRLEN(str); /* * Easy case: there is room in front of typebuf.tb_buf[typebuf.tb_off] */ if (offset == 0 && addlen <= typebuf.tb_off) { typebuf.tb_off -= addlen; mch_memmove(typebuf.tb_buf + typebuf.tb_off, str, (size_t)addlen); } /* * Need to allocate a new buffer. * In typebuf.tb_buf there must always be room for 3 * MAXMAPLEN + 4 * characters. We add some extra room to avoid having to allocate too * often. */ else { newoff = MAXMAPLEN + 4; newlen = typebuf.tb_len + addlen + newoff + 4 * (MAXMAPLEN + 4); if (newlen < 0) /* string is getting too long */ { EMSG(_(e_toocompl)); /* also calls flush_buffers */ setcursor(); return FAIL; } s1 = alloc(newlen); if (s1 == NULL) /* out of memory */ return FAIL; s2 = alloc(newlen); if (s2 == NULL) /* out of memory */ { vim_free(s1); return FAIL; } typebuf.tb_buflen = newlen; /* copy the old chars, before the insertion point */ mch_memmove(s1 + newoff, typebuf.tb_buf + typebuf.tb_off, (size_t)offset); /* copy the new chars */ mch_memmove(s1 + newoff + offset, str, (size_t)addlen); /* copy the old chars, after the insertion point, including the NUL at * the end */ mch_memmove(s1 + newoff + offset + addlen, typebuf.tb_buf + typebuf.tb_off + offset, (size_t)(typebuf.tb_len - offset + 1)); if (typebuf.tb_buf != typebuf_init) vim_free(typebuf.tb_buf); typebuf.tb_buf = s1; mch_memmove(s2 + newoff, typebuf.tb_noremap + typebuf.tb_off, (size_t)offset); mch_memmove(s2 + newoff + offset + addlen, typebuf.tb_noremap + typebuf.tb_off + offset, (size_t)(typebuf.tb_len - offset)); if (typebuf.tb_noremap != noremapbuf_init) vim_free(typebuf.tb_noremap); typebuf.tb_noremap = s2; typebuf.tb_off = newoff; } typebuf.tb_len += addlen; /* If noremap == REMAP_SCRIPT: do remap script-local mappings. */ if (noremap == REMAP_SCRIPT) val = RM_SCRIPT; else if (noremap == REMAP_SKIP) val = RM_ABBR; else val = RM_NONE; /* * Adjust typebuf.tb_noremap[] for the new characters: * If noremap == REMAP_NONE or REMAP_SCRIPT: new characters are * (sometimes) not remappable * If noremap == REMAP_YES: all the new characters are mappable * If noremap > 0: "noremap" characters are not remappable, the rest * mappable */ if (noremap == REMAP_SKIP) nrm = 1; else if (noremap < 0) nrm = addlen; else nrm = noremap; for (i = 0; i < addlen; ++i) typebuf.tb_noremap[typebuf.tb_off + i + offset] = (--nrm >= 0) ? val : RM_YES; /* tb_maplen and tb_silent only remember the length of mapped and/or * silent mappings at the start of the buffer, assuming that a mapped * sequence doesn't result in typed characters. */ if (nottyped || typebuf.tb_maplen > offset) typebuf.tb_maplen += addlen; if (silent || typebuf.tb_silent > offset) { typebuf.tb_silent += addlen; cmd_silent = TRUE; } if (typebuf.tb_no_abbr_cnt && offset == 0) /* and not used for abbrev.s */ typebuf.tb_no_abbr_cnt += addlen; return OK; } /* * Put character "c" back into the typeahead buffer. * Can be used for a character obtained by vgetc() that needs to be put back. * Uses cmd_silent, KeyTyped and KeyNoremap to restore the flags belonging to * the char. */ void ins_char_typebuf(c) int c; { #ifdef FEAT_MBYTE char_u buf[MB_MAXBYTES]; #else char_u buf[4]; #endif if (IS_SPECIAL(c)) { buf[0] = K_SPECIAL; buf[1] = K_SECOND(c); buf[2] = K_THIRD(c); buf[3] = NUL; } else { #ifdef FEAT_MBYTE buf[(*mb_char2bytes)(c, buf)] = NUL; #else buf[0] = c; buf[1] = NUL; #endif } (void)ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent); } /* * Return TRUE if the typeahead buffer was changed (while waiting for a * character to arrive). Happens when a message was received from a client or * from feedkeys(). * But check in a more generic way to avoid trouble: When "typebuf.tb_buf" * changed it was reallocated and the old pointer can no longer be used. * Or "typebuf.tb_off" may have been changed and we would overwrite characters * that was just added. */ int typebuf_changed(tb_change_cnt) int tb_change_cnt; /* old value of typebuf.tb_change_cnt */ { return (tb_change_cnt != 0 && (typebuf.tb_change_cnt != tb_change_cnt #if defined(FEAT_CLIENTSERVER) || defined(FEAT_EVAL) || typebuf_was_filled #endif )); } /* * Return TRUE if there are no characters in the typeahead buffer that have * not been typed (result from a mapping or come from ":normal"). */ int typebuf_typed() { return typebuf.tb_maplen == 0; } #if defined(FEAT_VISUAL) || defined(PROTO) /* * Return the number of characters that are mapped (or not typed). */ int typebuf_maplen() { return typebuf.tb_maplen; } #endif /* * remove "len" characters from typebuf.tb_buf[typebuf.tb_off + offset] */ void del_typebuf(len, offset) int len; int offset; { int i; if (len == 0) return; /* nothing to do */ typebuf.tb_len -= len; /* * Easy case: Just increase typebuf.tb_off. */ if (offset == 0 && typebuf.tb_buflen - (typebuf.tb_off + len) >= 3 * MAXMAPLEN + 3) typebuf.tb_off += len; /* * Have to move the characters in typebuf.tb_buf[] and typebuf.tb_noremap[] */ else { i = typebuf.tb_off + offset; /* * Leave some extra room at the end to avoid reallocation. */ if (typebuf.tb_off > MAXMAPLEN) { mch_memmove(typebuf.tb_buf + MAXMAPLEN, typebuf.tb_buf + typebuf.tb_off, (size_t)offset); mch_memmove(typebuf.tb_noremap + MAXMAPLEN, typebuf.tb_noremap + typebuf.tb_off, (size_t)offset); typebuf.tb_off = MAXMAPLEN; } /* adjust typebuf.tb_buf (include the NUL at the end) */ mch_memmove(typebuf.tb_buf + typebuf.tb_off + offset, typebuf.tb_buf + i + len, (size_t)(typebuf.tb_len - offset + 1)); /* adjust typebuf.tb_noremap[] */ mch_memmove(typebuf.tb_noremap + typebuf.tb_off + offset, typebuf.tb_noremap + i + len, (size_t)(typebuf.tb_len - offset)); } if (typebuf.tb_maplen > offset) /* adjust tb_maplen */ { if (typebuf.tb_maplen < offset + len) typebuf.tb_maplen = offset; else typebuf.tb_maplen -= len; } if (typebuf.tb_silent > offset) /* adjust tb_silent */ { if (typebuf.tb_silent < offset + len) typebuf.tb_silent = offset; else typebuf.tb_silent -= len; } if (typebuf.tb_no_abbr_cnt > offset) /* adjust tb_no_abbr_cnt */ { if (typebuf.tb_no_abbr_cnt < offset + len) typebuf.tb_no_abbr_cnt = offset; else typebuf.tb_no_abbr_cnt -= len; } #if defined(FEAT_CLIENTSERVER) || defined(FEAT_EVAL) /* Reset the flag that text received from a client or from feedkeys() * was inserted in the typeahead buffer. */ typebuf_was_filled = FALSE; #endif if (++typebuf.tb_change_cnt == 0) typebuf.tb_change_cnt = 1; } /* * Write typed characters to script file. * If recording is on put the character in the recordbuffer. */ static void gotchars(chars, len) char_u *chars; int len; { char_u *s = chars; int c; char_u buf[2]; int todo = len; /* remember how many chars were last recorded */ if (Recording) last_recorded_len += len; buf[1] = NUL; while (todo--) { /* Handle one byte at a time; no translation to be done. */ c = *s++; updatescript(c); if (Recording) { buf[0] = c; add_buff(&recordbuff, buf, 1L); } } may_sync_undo(); #ifdef FEAT_EVAL /* output "debug mode" message next time in debug mode */ debug_did_msg = FALSE; #endif /* Since characters have been typed, consider the following to be in * another mapping. Search string will be kept in history. */ ++maptick; } /* * Sync undo. Called when typed characters are obtained from the typeahead * buffer, or when a menu is used. * Do not sync: * - In Insert mode, unless cursor key has been used. * - While reading a script file. * - When no_u_sync is non-zero. */ static void may_sync_undo() { if ((!(State & (INSERT + CMDLINE)) || arrow_used) && scriptin[curscript] == NULL) u_sync(FALSE); } /* * Make "typebuf" empty and allocate new buffers. * Returns FAIL when out of memory. */ int alloc_typebuf() { typebuf.tb_buf = alloc(TYPELEN_INIT); typebuf.tb_noremap = alloc(TYPELEN_INIT); if (typebuf.tb_buf == NULL || typebuf.tb_noremap == NULL) { free_typebuf(); return FAIL; } typebuf.tb_buflen = TYPELEN_INIT; typebuf.tb_off = 0; typebuf.tb_len = 0; typebuf.tb_maplen = 0; typebuf.tb_silent = 0; typebuf.tb_no_abbr_cnt = 0; if (++typebuf.tb_change_cnt == 0) typebuf.tb_change_cnt = 1; return OK; } /* * Free the buffers of "typebuf". */ void free_typebuf() { if (typebuf.tb_buf == typebuf_init) EMSG2(_(e_intern2), "Free typebuf 1"); else vim_free(typebuf.tb_buf); if (typebuf.tb_noremap == noremapbuf_init) EMSG2(_(e_intern2), "Free typebuf 2"); else vim_free(typebuf.tb_noremap); } /* * When doing ":so! file", the current typeahead needs to be saved, and * restored when "file" has been read completely. */ static typebuf_T saved_typebuf[NSCRIPT]; int save_typebuf() { init_typebuf(); saved_typebuf[curscript] = typebuf; /* If out of memory: restore typebuf and close file. */ if (alloc_typebuf() == FAIL) { closescript(); return FAIL; } return OK; } static int old_char = -1; /* character put back by vungetc() */ static int old_mod_mask; /* mod_mask for ungotten character */ #if defined(FEAT_EVAL) || defined(FEAT_EX_EXTRA) || defined(PROTO) /* * Save all three kinds of typeahead, so that the user must type at a prompt. */ void save_typeahead(tp) tasave_T *tp; { tp->save_typebuf = typebuf; tp->typebuf_valid = (alloc_typebuf() == OK); if (!tp->typebuf_valid) typebuf = tp->save_typebuf; tp->old_char = old_char; tp->old_mod_mask = old_mod_mask; old_char = -1; tp->save_stuffbuff = stuffbuff; stuffbuff.bh_first.b_next = NULL; # ifdef USE_INPUT_BUF tp->save_inputbuf = get_input_buf(); # endif } /* * Restore the typeahead to what it was before calling save_typeahead(). * The allocated memory is freed, can only be called once! */ void restore_typeahead(tp) tasave_T *tp; { if (tp->typebuf_valid) { free_typebuf(); typebuf = tp->save_typebuf; } old_char = tp->old_char; old_mod_mask = tp->old_mod_mask; free_buff(&stuffbuff); stuffbuff = tp->save_stuffbuff; # ifdef USE_INPUT_BUF set_input_buf(tp->save_inputbuf); # endif } #endif /* * Open a new script file for the ":source!" command. */ void openscript(name, directly) char_u *name; int directly; /* when TRUE execute directly */ { if (curscript + 1 == NSCRIPT) { EMSG(_(e_nesting)); return; } #ifdef FEAT_EVAL if (ignore_script) /* Not reading from script, also don't open one. Warning message? */ return; #endif if (scriptin[curscript] != NULL) /* already reading script */ ++curscript; /* use NameBuff for expanded name */ expand_env(name, NameBuff, MAXPATHL); if ((scriptin[curscript] = mch_fopen((char *)NameBuff, READBIN)) == NULL) { EMSG2(_(e_notopen), name); if (curscript) --curscript; return; } if (save_typebuf() == FAIL) return; /* * Execute the commands from the file right now when using ":source!" * after ":global" or ":argdo" or in a loop. Also when another command * follows. This means the display won't be updated. Don't do this * always, "make test" would fail. */ if (directly) { oparg_T oa; int oldcurscript; int save_State = State; int save_restart_edit = restart_edit; int save_insertmode = p_im; int save_finish_op = finish_op; int save_msg_scroll = msg_scroll; State = NORMAL; msg_scroll = FALSE; /* no msg scrolling in Normal mode */ restart_edit = 0; /* don't go to Insert mode */ p_im = FALSE; /* don't use 'insertmode' */ clear_oparg(&oa); finish_op = FALSE; oldcurscript = curscript; do { update_topline_cursor(); /* update cursor position and topline */ normal_cmd(&oa, FALSE); /* execute one command */ vpeekc(); /* check for end of file */ } while (scriptin[oldcurscript] != NULL); State = save_State; msg_scroll = save_msg_scroll; restart_edit = save_restart_edit; p_im = save_insertmode; finish_op = save_finish_op; } } /* * Close the currently active input script. */ static void closescript() { free_typebuf(); typebuf = saved_typebuf[curscript]; fclose(scriptin[curscript]); scriptin[curscript] = NULL; if (curscript > 0) --curscript; } #if defined(EXITFREE) || defined(PROTO) void close_all_scripts() { while (scriptin[0] != NULL) closescript(); } #endif #if defined(FEAT_INS_EXPAND) || defined(PROTO) /* * Return TRUE when reading keys from a script file. */ int using_script() { return scriptin[curscript] != NULL; } #endif /* * This function is called just before doing a blocking wait. Thus after * waiting 'updatetime' for a character to arrive. */ void before_blocking() { updatescript(0); #ifdef FEAT_EVAL if (may_garbage_collect) garbage_collect(); #endif } /* * updatescipt() is called when a character can be written into the script file * or when we have waited some time for a character (c == 0) * * All the changed memfiles are synced if c == 0 or when the number of typed * characters reaches 'updatecount' and 'updatecount' is non-zero. */ void updatescript(c) int c; { static int count = 0; if (c && scriptout) putc(c, scriptout); if (c == 0 || (p_uc > 0 && ++count >= p_uc)) { ml_sync_all(c == 0, TRUE); count = 0; } } /* * Get the next input character. * Can return a special key or a multi-byte character. * Can return NUL when called recursively, use safe_vgetc() if that's not * wanted. * This translates escaped K_SPECIAL and CSI bytes to a K_SPECIAL or CSI byte. * Collects the bytes of a multibyte character into the whole character. * Returns the modifiers in the global "mod_mask". */ int vgetc() { int c, c2; #ifdef FEAT_MBYTE int n; char_u buf[MB_MAXBYTES]; int i; #endif #ifdef FEAT_EVAL /* Do garbage collection when garbagecollect() was called previously and * we are now at the toplevel. */ if (may_garbage_collect && want_garbage_collect) garbage_collect(); #endif /* * If a character was put back with vungetc, it was already processed. * Return it directly. */ if (old_char != -1) { c = old_char; old_char = -1; mod_mask = old_mod_mask; } else { mod_mask = 0x0; last_recorded_len = 0; for (;;) /* this is done twice if there are modifiers */ { if (mod_mask) /* no mapping after modifier has been read */ { ++no_mapping; ++allow_keys; } c = vgetorpeek(TRUE); if (mod_mask) { --no_mapping; --allow_keys; } /* Get two extra bytes for special keys */ if (c == K_SPECIAL #ifdef FEAT_GUI || c == CSI #endif ) { int save_allow_keys = allow_keys; ++no_mapping; allow_keys = 0; /* make sure BS is not found */ c2 = vgetorpeek(TRUE); /* no mapping for these chars */ c = vgetorpeek(TRUE); --no_mapping; allow_keys = save_allow_keys; if (c2 == KS_MODIFIER) { mod_mask = c; continue; } c = TO_SPECIAL(c2, c); #if defined(FEAT_GUI_W32) && defined(FEAT_MENU) && defined(FEAT_TEAROFF) /* Handle K_TEAROFF here, the caller of vgetc() doesn't need to * know that a menu was torn off */ if (c == K_TEAROFF) { char_u name[200]; int i; /* get menu path, it ends with a <CR> */ for (i = 0; (c = vgetorpeek(TRUE)) != '\r'; ) { name[i] = c; if (i < 199) ++i; } name[i] = NUL; gui_make_tearoff(name); continue; } #endif #if defined(FEAT_GUI) && defined(FEAT_GUI_GTK) && defined(FEAT_MENU) /* GTK: <F10> normally selects the menu, but it's passed until * here to allow mapping it. Intercept and invoke the GTK * behavior if it's not mapped. */ if (c == K_F10 && gui.menubar != NULL) { gtk_menu_shell_select_first(GTK_MENU_SHELL(gui.menubar), FALSE); continue; } #endif #ifdef FEAT_GUI /* Handle focus event here, so that the caller doesn't need to * know about it. Return K_IGNORE so that we loop once (needed if * 'lazyredraw' is set). */ if (c == K_FOCUSGAINED || c == K_FOCUSLOST) { ui_focus_change(c == K_FOCUSGAINED); c = K_IGNORE; } /* Translate K_CSI to CSI. The special key is only used to avoid * it being recognized as the start of a special key. */ if (c == K_CSI) c = CSI; #endif } #ifdef MSDOS /* * If K_NUL was typed, it is replaced by K_NUL, 3 in mch_inchar(). * Delete the 3 here. */ else if (c == K_NUL && vpeekc() == 3) (void)vgetorpeek(TRUE); #endif /* a keypad or special function key was not mapped, use it like * its ASCII equivalent */ switch (c) { case K_KPLUS: c = '+'; break; case K_KMINUS: c = '-'; break; case K_KDIVIDE: c = '/'; break; case K_KMULTIPLY: c = '*'; break; case K_KENTER: c = CAR; break; case K_KPOINT: #ifdef WIN32 /* Can be either '.' or a ',', * * depending on the type of keypad. */ c = MapVirtualKey(VK_DECIMAL, 2); break; #else c = '.'; break; #endif case K_K0: c = '0'; break; case K_K1: c = '1'; break; case K_K2: c = '2'; break; case K_K3: c = '3'; break; case K_K4: c = '4'; break; case K_K5: c = '5'; break; case K_K6: c = '6'; break; case K_K7: c = '7'; break; case K_K8: c = '8'; break; case K_K9: c = '9'; break; case K_XHOME: case K_ZHOME: if (mod_mask == MOD_MASK_SHIFT) { c = K_S_HOME; mod_mask = 0; } else if (mod_mask == MOD_MASK_CTRL) { c = K_C_HOME; mod_mask = 0; } else c = K_HOME; break; case K_XEND: case K_ZEND: if (mod_mask == MOD_MASK_SHIFT) { c = K_S_END; mod_mask = 0; } else if (mod_mask == MOD_MASK_CTRL) { c = K_C_END; mod_mask = 0; } else c = K_END; break; case K_XUP: c = K_UP; break; case K_XDOWN: c = K_DOWN; break; case K_XLEFT: c = K_LEFT; break; case K_XRIGHT: c = K_RIGHT; break; } #ifdef FEAT_MBYTE /* For a multi-byte character get all the bytes and return the * converted character. * Note: This will loop until enough bytes are received! */ if (has_mbyte && (n = MB_BYTE2LEN_CHECK(c)) > 1) { ++no_mapping; buf[0] = c; for (i = 1; i < n; ++i) { buf[i] = vgetorpeek(TRUE); if (buf[i] == K_SPECIAL #ifdef FEAT_GUI || buf[i] == CSI #endif ) { /* Must be a K_SPECIAL - KS_SPECIAL - KE_FILLER sequence, * which represents a K_SPECIAL (0x80), * or a CSI - KS_EXTRA - KE_CSI sequence, which represents * a CSI (0x9B), * of a K_SPECIAL - KS_EXTRA - KE_CSI, which is CSI too. */ c = vgetorpeek(TRUE); if (vgetorpeek(TRUE) == (int)KE_CSI && c == KS_EXTRA) buf[i] = CSI; } } --no_mapping; c = (*mb_ptr2char)(buf); } #endif break; } } #ifdef FEAT_EVAL /* * In the main loop "may_garbage_collect" can be set to do garbage * collection in the first next vgetc(). It's disabled after that to * avoid internally used Lists and Dicts to be freed. */ may_garbage_collect = FALSE; #endif return c; } /* * Like vgetc(), but never return a NUL when called recursively, get a key * directly from the user (ignoring typeahead). */ int safe_vgetc() { int c; c = vgetc(); if (c == NUL) c = get_keystroke(); return c; } /* * Like safe_vgetc(), but loop to handle K_IGNORE. * Also ignore scrollbar events. */ int plain_vgetc() { int c; do { c = safe_vgetc(); } while (c == K_IGNORE || c == K_VER_SCROLLBAR || c == K_HOR_SCROLLBAR); return c; } /* * Check if a character is available, such that vgetc() will not block. * If the next character is a special character or multi-byte, the returned * character is not valid!. */ int vpeekc() { if (old_char != -1) return old_char; return vgetorpeek(FALSE); } #if defined(FEAT_TERMRESPONSE) || defined(PROTO) /* * Like vpeekc(), but don't allow mapping. Do allow checking for terminal * codes. */ int vpeekc_nomap() { int c; ++no_mapping; ++allow_keys; c = vpeekc(); --no_mapping; --allow_keys; return c; } #endif #if defined(FEAT_INS_EXPAND) || defined(PROTO) /* * Check if any character is available, also half an escape sequence. * Trick: when no typeahead found, but there is something in the typeahead * buffer, it must be an ESC that is recognized as the start of a key code. */ int vpeekc_any() { int c; c = vpeekc(); if (c == NUL && typebuf.tb_len > 0) c = ESC; return c; } #endif /* * Call vpeekc() without causing anything to be mapped. * Return TRUE if a character is available, FALSE otherwise. */ int char_avail() { int retval; ++no_mapping; retval = vpeekc(); --no_mapping; return (retval != NUL); } void vungetc(c) /* unget one character (can only be done once!) */ int c; { old_char = c; old_mod_mask = mod_mask; } /* * get a character: * 1. from the stuffbuffer * This is used for abbreviated commands like "D" -> "d$". * Also used to redo a command for ".". * 2. from the typeahead buffer * Stores text obtained previously but not used yet. * Also stores the result of mappings. * Also used for the ":normal" command. * 3. from the user * This may do a blocking wait if "advance" is TRUE. * * if "advance" is TRUE (vgetc()): * really get the character. * KeyTyped is set to TRUE in the case the user typed the key. * KeyStuffed is TRUE if the character comes from the stuff buffer. * if "advance" is FALSE (vpeekc()): * just look whether there is a character available. * * When "no_mapping" is zero, checks for mappings in the current mode. * Only returns one byte (of a multi-byte character). * K_SPECIAL and CSI may be escaped, need to get two more bytes then. */ static int vgetorpeek(advance) int advance; { int c, c1; int keylen; char_u *s; mapblock_T *mp; #ifdef FEAT_LOCALMAP mapblock_T *mp2; #endif mapblock_T *mp_match; int mp_match_len = 0; int timedout = FALSE; /* waited for more than 1 second for mapping to complete */ int mapdepth = 0; /* check for recursive mapping */ int mode_deleted = FALSE; /* set when mode has been deleted */ int local_State; int mlen; int max_mlen; int i; #ifdef FEAT_CMDL_INFO int new_wcol, new_wrow; #endif #ifdef FEAT_GUI # ifdef FEAT_MENU int idx; # endif int shape_changed = FALSE; /* adjusted cursor shape */ #endif int n; #ifdef FEAT_LANGMAP int nolmaplen; #endif int old_wcol, old_wrow; int wait_tb_len; /* * This function doesn't work very well when called recursively. This may * happen though, because of: * 1. The call to add_to_showcmd(). char_avail() is then used to check if * there is a character available, which calls this function. In that * case we must return NUL, to indicate no character is available. * 2. A GUI callback function writes to the screen, causing a * wait_return(). * Using ":normal" can also do this, but it saves the typeahead buffer, * thus it should be OK. But don't get a key from the user then. */ if (vgetc_busy > 0 #ifdef FEAT_EX_EXTRA && ex_normal_busy == 0 #endif ) return NUL; local_State = get_real_state(); ++vgetc_busy; if (advance) KeyStuffed = FALSE; init_typebuf(); start_stuff(); if (advance && typebuf.tb_maplen == 0) Exec_reg = FALSE; do { /* * get a character: 1. from the stuffbuffer */ if (typeahead_char != 0) { c = typeahead_char; if (advance) typeahead_char = 0; } else c = read_stuff(advance); if (c != NUL && !got_int) { if (advance) { /* KeyTyped = FALSE; When the command that stuffed something * was typed, behave like the stuffed command was typed. * needed for CTRL-W CTRl-] to open a fold, for example. */ KeyStuffed = TRUE; } if (typebuf.tb_no_abbr_cnt == 0) typebuf.tb_no_abbr_cnt = 1; /* no abbreviations now */ } else { /* * Loop until we either find a matching mapped key, or we * are sure that it is not a mapped key. * If a mapped key sequence is found we go back to the start to * try re-mapping. */ for (;;) { /* * ui_breakcheck() is slow, don't use it too often when * inside a mapping. But call it each time for typed * characters. */ if (typebuf.tb_maplen) line_breakcheck(); else ui_breakcheck(); /* check for CTRL-C */ keylen = 0; if (got_int) { /* flush all input */ c = inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 0L, typebuf.tb_change_cnt); /* * If inchar() returns TRUE (script file was active) or we * are inside a mapping, get out of insert mode. * Otherwise we behave like having gotten a CTRL-C. * As a result typing CTRL-C in insert mode will * really insert a CTRL-C. */ if ((c || typebuf.tb_maplen) && (State & (INSERT + CMDLINE))) c = ESC; else c = Ctrl_C; flush_buffers(TRUE); /* flush all typeahead */ if (advance) { /* Also record this character, it might be needed to * get out of Insert mode. */ *typebuf.tb_buf = c; gotchars(typebuf.tb_buf, 1); } cmd_silent = FALSE; break; } else if (typebuf.tb_len > 0) { /* * Check for a mappable key sequence. * Walk through one maphash[] list until we find an * entry that matches. * * Don't look for mappings if: * - no_mapping set: mapping disabled (e.g. for CTRL-V) * - maphash_valid not set: no mappings present. * - typebuf.tb_buf[typebuf.tb_off] should not be remapped * - in insert or cmdline mode and 'paste' option set * - waiting for "hit return to continue" and CR or SPACE * typed * - waiting for a char with --more-- * - in Ctrl-X mode, and we get a valid char for that mode */ mp = NULL; max_mlen = 0; c1 = typebuf.tb_buf[typebuf.tb_off]; if (no_mapping == 0 && maphash_valid && (no_zero_mapping == 0 || c1 != '0') && (typebuf.tb_maplen == 0 || (p_remap && (typebuf.tb_noremap[typebuf.tb_off] & (RM_NONE|RM_ABBR)) == 0)) && !(p_paste && (State & (INSERT + CMDLINE))) && !(State == HITRETURN && (c1 == CAR || c1 == ' ')) && State != ASKMORE && State != CONFIRM #ifdef FEAT_INS_EXPAND && !((ctrl_x_mode != 0 && vim_is_ctrl_x_key(c1)) || ((compl_cont_status & CONT_LOCAL) && (c1 == Ctrl_N || c1 == Ctrl_P))) #endif ) { #ifdef FEAT_LANGMAP if (c1 == K_SPECIAL) nolmaplen = 2; else { LANGMAP_ADJUST(c1, TRUE); nolmaplen = 0; } #endif #ifdef FEAT_LOCALMAP /* First try buffer-local mappings. */ mp = curbuf->b_maphash[MAP_HASH(local_State, c1)]; mp2 = maphash[MAP_HASH(local_State, c1)]; if (mp == NULL) { mp = mp2; mp2 = NULL; } #else mp = maphash[MAP_HASH(local_State, c1)]; #endif /* * Loop until a partly matching mapping is found or * all (local) mappings have been checked. * The longest full match is remembered in "mp_match". * A full match is only accepted if there is no partly * match, so "aa" and "aaa" can both be mapped. */ mp_match = NULL; mp_match_len = 0; for ( ; mp != NULL; #ifdef FEAT_LOCALMAP mp->m_next == NULL ? (mp = mp2, mp2 = NULL) : #endif (mp = mp->m_next)) { /* * Only consider an entry if the first character * matches and it is for the current state. * Skip ":lmap" mappings if keys were mapped. */ if (mp->m_keys[0] == c1 && (mp->m_mode & local_State) && ((mp->m_mode & LANGMAP) == 0 || typebuf.tb_maplen == 0)) { #ifdef FEAT_LANGMAP int nomap = nolmaplen; int c2; #endif /* find the match length of this mapping */ for (mlen = 1; mlen < typebuf.tb_len; ++mlen) { #ifdef FEAT_LANGMAP c2 = typebuf.tb_buf[typebuf.tb_off + mlen]; if (nomap > 0) --nomap; else if (c2 == K_SPECIAL) nomap = 2; else LANGMAP_ADJUST(c2, TRUE); if (mp->m_keys[mlen] != c2) #else if (mp->m_keys[mlen] != typebuf.tb_buf[typebuf.tb_off + mlen]) #endif break; } #ifdef FEAT_MBYTE /* Don't allow mapping the first byte(s) of a * multi-byte char. Happens when mapping * <M-a> and then changing 'encoding'. */ if (has_mbyte && MB_BYTE2LEN(c1) > (*mb_ptr2len)(mp->m_keys)) mlen = 0; #endif /* * Check an entry whether it matches. * - Full match: mlen == keylen * - Partly match: mlen == typebuf.tb_len */ keylen = mp->m_keylen; if (mlen == keylen || (mlen == typebuf.tb_len && typebuf.tb_len < keylen)) { /* * If only script-local mappings are * allowed, check if the mapping starts * with K_SNR. */ s = typebuf.tb_noremap + typebuf.tb_off; if (*s == RM_SCRIPT && (mp->m_keys[0] != K_SPECIAL || mp->m_keys[1] != KS_EXTRA || mp->m_keys[2] != (int)KE_SNR)) continue; /* * If one of the typed keys cannot be * remapped, skip the entry. */ for (n = mlen; --n >= 0; ) if (*s++ & (RM_NONE|RM_ABBR)) break; if (n >= 0) continue; if (keylen > typebuf.tb_len) { if (!timedout) { /* break at a partly match */ keylen = KEYLEN_PART_MAP; break; } } else if (keylen > mp_match_len) { /* found a longer match */ mp_match = mp; mp_match_len = keylen; } } else /* No match; may have to check for * termcode at next character. */ if (max_mlen < mlen) max_mlen = mlen; } } /* If no partly match found, use the longest full * match. */ if (keylen != KEYLEN_PART_MAP) { mp = mp_match; keylen = mp_match_len; } } /* Check for match with 'pastetoggle' */ if (*p_pt != NUL && mp == NULL && (State & (INSERT|NORMAL))) { for (mlen = 0; mlen < typebuf.tb_len && p_pt[mlen]; ++mlen) if (p_pt[mlen] != typebuf.tb_buf[typebuf.tb_off + mlen]) break; if (p_pt[mlen] == NUL) /* match */ { /* write chars to script file(s) */ if (mlen > typebuf.tb_maplen) gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, mlen - typebuf.tb_maplen); del_typebuf(mlen, 0); /* remove the chars */ set_option_value((char_u *)"paste", (long)!p_paste, NULL, 0); if (!(State & INSERT)) { msg_col = 0; msg_row = Rows - 1; msg_clr_eos(); /* clear ruler */ } showmode(); setcursor(); continue; } /* Need more chars for partly match. */ if (mlen == typebuf.tb_len) keylen = KEYLEN_PART_KEY; else if (max_mlen < mlen) /* no match, may have to check for termcode at * next character */ max_mlen = mlen + 1; } if ((mp == NULL || max_mlen >= mp_match_len) && keylen != KEYLEN_PART_MAP) { int save_keylen = keylen; /* * When no matching mapping found or found a * non-matching mapping that matches at least what the * matching mapping matched: * Check if we have a terminal code, when: * mapping is allowed, * keys have not been mapped, * and not an ESC sequence, not in insert mode or * p_ek is on, * and when not timed out, */ if ((no_mapping == 0 || allow_keys != 0) && (typebuf.tb_maplen == 0 || (p_remap && typebuf.tb_noremap[ typebuf.tb_off] == RM_YES)) && !timedout) { keylen = check_termcode(max_mlen + 1, NULL, 0); /* If no termcode matched but 'pastetoggle' * matched partially it's like an incomplete key * sequence. */ if (keylen == 0 && save_keylen == KEYLEN_PART_KEY) keylen = KEYLEN_PART_KEY; /* * When getting a partial match, but the last * characters were not typed, don't wait for a * typed character to complete the termcode. * This helps a lot when a ":normal" command ends * in an ESC. */ if (keylen < 0 && typebuf.tb_len == typebuf.tb_maplen) keylen = 0; } else keylen = 0; if (keylen == 0) /* no matching terminal code */ { #ifdef AMIGA /* check for window bounds report */ if (typebuf.tb_maplen == 0 && (typebuf.tb_buf[ typebuf.tb_off] & 0xff) == CSI) { for (s = typebuf.tb_buf + typebuf.tb_off + 1; s < typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len && (VIM_ISDIGIT(*s) || *s == ';' || *s == ' '); ++s) ; if (*s == 'r' || *s == '|') /* found one */ { del_typebuf((int)(s + 1 - (typebuf.tb_buf + typebuf.tb_off)), 0); /* get size and redraw screen */ shell_resized(); continue; } if (*s == NUL) /* need more characters */ keylen = KEYLEN_PART_KEY; } if (keylen >= 0) #endif /* When there was a matching mapping and no * termcode could be replaced after another one, * use that mapping (loop around). If there was * no mapping use the character from the * typeahead buffer right here. */ if (mp == NULL) { /* * get a character: 2. from the typeahead buffer */ c = typebuf.tb_buf[typebuf.tb_off] & 255; if (advance) /* remove chars from tb_buf */ { cmd_silent = (typebuf.tb_silent > 0); if (typebuf.tb_maplen > 0) KeyTyped = FALSE; else { KeyTyped = TRUE; /* write char to script file(s) */ gotchars(typebuf.tb_buf + typebuf.tb_off, 1); } KeyNoremap = typebuf.tb_noremap[ typebuf.tb_off]; del_typebuf(1, 0); } break; /* got character, break for loop */ } } if (keylen > 0) /* full matching terminal code */ { #if defined(FEAT_GUI) && defined(FEAT_MENU) if (typebuf.tb_len >= 2 && typebuf.tb_buf[typebuf.tb_off] == K_SPECIAL && typebuf.tb_buf[typebuf.tb_off + 1] == KS_MENU) { /* * Using a menu may cause a break in undo! * It's like using gotchars(), but without * recording or writing to a script file. */ may_sync_undo(); del_typebuf(3, 0); idx = get_menu_index(current_menu, local_State); if (idx != MENU_INDEX_INVALID) { # ifdef FEAT_VISUAL /* * In Select mode and a Visual mode menu * is used: Switch to Visual mode * temporarily. Append K_SELECT to switch * back to Select mode. */ if (VIsual_active && VIsual_select && (current_menu->modes & VISUAL)) { VIsual_select = FALSE; (void)ins_typebuf(K_SELECT_STRING, REMAP_NONE, 0, TRUE, FALSE); } # endif ins_typebuf(current_menu->strings[idx], current_menu->noremap[idx], 0, TRUE, current_menu->silent[idx]); } } #endif /* FEAT_GUI && FEAT_MENU */ continue; /* try mapping again */ } /* Partial match: get some more characters. When a * matching mapping was found use that one. */ if (mp == NULL || keylen < 0) keylen = KEYLEN_PART_KEY; else keylen = mp_match_len; } /* complete match */ if (keylen >= 0 && keylen <= typebuf.tb_len) { #ifdef FEAT_EVAL int save_m_expr; int save_m_noremap; int save_m_silent; char_u *save_m_keys; char_u *save_m_str; #else # define save_m_noremap mp->m_noremap # define save_m_silent mp->m_silent #endif /* write chars to script file(s) */ if (keylen > typebuf.tb_maplen) gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, keylen - typebuf.tb_maplen); cmd_silent = (typebuf.tb_silent > 0); del_typebuf(keylen, 0); /* remove the mapped keys */ /* * Put the replacement string in front of mapstr. * The depth check catches ":map x y" and ":map y x". */ if (++mapdepth >= p_mmd) { EMSG(_("E223: recursive mapping")); if (State & CMDLINE) redrawcmdline(); else setcursor(); flush_buffers(FALSE); mapdepth = 0; /* for next one */ c = -1; break; } #ifdef FEAT_VISUAL /* * In Select mode and a Visual mode mapping is used: * Switch to Visual mode temporarily. Append K_SELECT * to switch back to Select mode. */ if (VIsual_active && VIsual_select && (mp->m_mode & VISUAL)) { VIsual_select = FALSE; (void)ins_typebuf(K_SELECT_STRING, REMAP_NONE, 0, TRUE, FALSE); } #endif #ifdef FEAT_EVAL /* Copy the values from *mp that are used, because * evaluating the expression may invoke a function * that redefines the mapping, thereby making *mp * invalid. */ save_m_expr = mp->m_expr; save_m_noremap = mp->m_noremap; save_m_silent = mp->m_silent; save_m_keys = NULL; /* only saved when needed */ save_m_str = NULL; /* only saved when needed */ /* * Handle ":map <expr>": evaluate the {rhs} as an * expression. Save and restore the typeahead so that * getchar() can be used. Also save and restore the * command line for "normal :". */ if (mp->m_expr) { tasave_T tabuf; int save_vgetc_busy = vgetc_busy; save_typeahead(&tabuf); if (tabuf.typebuf_valid) { vgetc_busy = 0; save_m_keys = vim_strsave(mp->m_keys); save_m_str = vim_strsave(mp->m_str); s = eval_map_expr(save_m_str, NUL); vgetc_busy = save_vgetc_busy; } else s = NULL; restore_typeahead(&tabuf); } else #endif s = mp->m_str; /* * Insert the 'to' part in the typebuf.tb_buf. * If 'from' field is the same as the start of the * 'to' field, don't remap the first character (but do * allow abbreviations). * If m_noremap is set, don't remap the whole 'to' * part. */ if (s == NULL) i = FAIL; else { int noremap; if (save_m_noremap != REMAP_YES) noremap = save_m_noremap; else if ( #ifdef FEAT_EVAL STRNCMP(s, save_m_keys != NULL ? save_m_keys : mp->m_keys, (size_t)keylen) #else STRNCMP(s, mp->m_keys, (size_t)keylen) #endif != 0) noremap = REMAP_YES; else noremap = REMAP_SKIP; i = ins_typebuf(s, noremap, 0, TRUE, cmd_silent || save_m_silent); #ifdef FEAT_EVAL if (save_m_expr) vim_free(s); #endif } #ifdef FEAT_EVAL vim_free(save_m_keys); vim_free(save_m_str); #endif if (i == FAIL) { c = -1; break; } continue; } } /* * get a character: 3. from the user - handle <Esc> in Insert mode */ /* * special case: if we get an <ESC> in insert mode and there * are no more characters at once, we pretend to go out of * insert mode. This prevents the one second delay after * typing an <ESC>. If we get something after all, we may * have to redisplay the mode. That the cursor is in the wrong * place does not matter. */ c = 0; #ifdef FEAT_CMDL_INFO new_wcol = curwin->w_wcol; new_wrow = curwin->w_wrow; #endif if ( advance && typebuf.tb_len == 1 && typebuf.tb_buf[typebuf.tb_off] == ESC && !no_mapping #ifdef FEAT_EX_EXTRA && ex_normal_busy == 0 #endif && typebuf.tb_maplen == 0 && (State & INSERT) && (p_timeout || (keylen == KEYLEN_PART_KEY && p_ttimeout)) && (c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, 3, 25L, typebuf.tb_change_cnt)) == 0) { colnr_T col = 0, vcol; char_u *ptr; if (mode_displayed) { unshowmode(TRUE); mode_deleted = TRUE; } #ifdef FEAT_GUI /* may show different cursor shape */ if (gui.in_use) { int save_State; save_State = State; State = NORMAL; gui_update_cursor(TRUE, FALSE); State = save_State; shape_changed = TRUE; } #endif validate_cursor(); old_wcol = curwin->w_wcol; old_wrow = curwin->w_wrow; /* move cursor left, if possible */ if (curwin->w_cursor.col != 0) { if (curwin->w_wcol > 0) { if (did_ai) { /* * We are expecting to truncate the trailing * white-space, so find the last non-white * character -- webb */ col = vcol = curwin->w_wcol = 0; ptr = ml_get_curline(); while (col < curwin->w_cursor.col) { if (!vim_iswhite(ptr[col])) curwin->w_wcol = vcol; vcol += lbr_chartabsize(ptr + col, (colnr_T)vcol); #ifdef FEAT_MBYTE if (has_mbyte) col += (*mb_ptr2len)(ptr + col); else #endif ++col; } curwin->w_wrow = curwin->w_cline_row + curwin->w_wcol / W_WIDTH(curwin); curwin->w_wcol %= W_WIDTH(curwin); curwin->w_wcol += curwin_col_off(); #ifdef FEAT_MBYTE col = 0; /* no correction needed */ #endif } else { --curwin->w_wcol; #ifdef FEAT_MBYTE col = curwin->w_cursor.col - 1; #endif } } else if (curwin->w_p_wrap && curwin->w_wrow) { --curwin->w_wrow; curwin->w_wcol = W_WIDTH(curwin) - 1; #ifdef FEAT_MBYTE col = curwin->w_cursor.col - 1; #endif } #ifdef FEAT_MBYTE if (has_mbyte && col > 0 && curwin->w_wcol > 0) { /* Correct when the cursor is on the right halve * of a double-wide character. */ ptr = ml_get_curline(); col -= (*mb_head_off)(ptr, ptr + col); if ((*mb_ptr2cells)(ptr + col) > 1) --curwin->w_wcol; } #endif } setcursor(); out_flush(); #ifdef FEAT_CMDL_INFO new_wcol = curwin->w_wcol; new_wrow = curwin->w_wrow; #endif curwin->w_wcol = old_wcol; curwin->w_wrow = old_wrow; } if (c < 0) continue; /* end of input script reached */ typebuf.tb_len += c; /* buffer full, don't map */ if (typebuf.tb_len >= typebuf.tb_maplen + MAXMAPLEN) { timedout = TRUE; continue; } #ifdef FEAT_EX_EXTRA if (ex_normal_busy > 0) { # ifdef FEAT_CMDWIN static int tc = 0; # endif /* No typeahead left and inside ":normal". Must return * something to avoid getting stuck. When an incomplete * mapping is present, behave like it timed out. */ if (typebuf.tb_len > 0) { timedout = TRUE; continue; } /* When 'insertmode' is set, ESC just beeps in Insert * mode. Use CTRL-L to make edit() return. * For the command line only CTRL-C always breaks it. * For the cmdline window: Alternate between ESC and * CTRL-C: ESC for most situations and CTRL-C to close the * cmdline window. */ if (p_im && (State & INSERT)) c = Ctrl_L; else if ((State & CMDLINE) # ifdef FEAT_CMDWIN || (cmdwin_type > 0 && tc == ESC) # endif ) c = Ctrl_C; else c = ESC; # ifdef FEAT_CMDWIN tc = c; # endif break; } #endif /* * get a character: 3. from the user - update display */ /* In insert mode a screen update is skipped when characters * are still available. But when those available characters * are part of a mapping, and we are going to do a blocking * wait here. Need to update the screen to display the * changed text so far. Also for when 'lazyredraw' is set and * redrawing was postponed because there was something in the * input buffer (e.g., termresponse). */ if (((State & INSERT) != 0 || p_lz) && (State & CMDLINE) == 0 && advance && must_redraw != 0 && !need_wait_return) { update_screen(0); setcursor(); /* put cursor back where it belongs */ } /* * If we have a partial match (and are going to wait for more * input from the user), show the partially matched characters * to the user with showcmd. */ #ifdef FEAT_CMDL_INFO i = 0; #endif c1 = 0; if (typebuf.tb_len > 0 && advance && !exmode_active) { if (((State & (NORMAL | INSERT)) || State == LANGMAP) && State != HITRETURN) { /* this looks nice when typing a dead character map */ if (State & INSERT && ptr2cells(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len - 1) == 1) { edit_putchar(typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len - 1], FALSE); setcursor(); /* put cursor back where it belongs */ c1 = 1; } #ifdef FEAT_CMDL_INFO /* need to use the col and row from above here */ old_wcol = curwin->w_wcol; old_wrow = curwin->w_wrow; curwin->w_wcol = new_wcol; curwin->w_wrow = new_wrow; push_showcmd(); if (typebuf.tb_len > SHOWCMD_COLS) i = typebuf.tb_len - SHOWCMD_COLS; while (i < typebuf.tb_len) (void)add_to_showcmd(typebuf.tb_buf[typebuf.tb_off + i++]); curwin->w_wcol = old_wcol; curwin->w_wrow = old_wrow; #endif } /* this looks nice when typing a dead character map */ if ((State & CMDLINE) #if defined(FEAT_CRYPT) || defined(FEAT_EVAL) && cmdline_star == 0 #endif && ptr2cells(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len - 1) == 1) { putcmdline(typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len - 1], FALSE); c1 = 1; } } /* * get a character: 3. from the user - get it */ wait_tb_len = typebuf.tb_len; c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, typebuf.tb_buflen - typebuf.tb_off - typebuf.tb_len - 1, !advance ? 0 : ((typebuf.tb_len == 0 || !(p_timeout || (p_ttimeout && keylen == KEYLEN_PART_KEY))) ? -1L : ((keylen == KEYLEN_PART_KEY && p_ttm >= 0) ? p_ttm : p_tm)), typebuf.tb_change_cnt); #ifdef FEAT_CMDL_INFO if (i != 0) pop_showcmd(); #endif if (c1 == 1) { if (State & INSERT) edit_unputchar(); if (State & CMDLINE) unputcmdline(); setcursor(); /* put cursor back where it belongs */ } if (c < 0) continue; /* end of input script reached */ if (c == NUL) /* no character available */ { if (!advance) break; if (wait_tb_len > 0) /* timed out */ { timedout = TRUE; continue; } } else { /* allow mapping for just typed characters */ while (typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len] != NUL) typebuf.tb_noremap[typebuf.tb_off + typebuf.tb_len++] = RM_YES; #ifdef USE_IM_CONTROL /* Get IM status right after getting keys, not after the * timeout for a mapping (focus may be lost by then). */ vgetc_im_active = im_get_status(); #endif } } /* for (;;) */ } /* if (!character from stuffbuf) */ /* if advance is FALSE don't loop on NULs */ } while (c < 0 || (advance && c == NUL)); /* * The "INSERT" message is taken care of here: * if we return an ESC to exit insert mode, the message is deleted * if we don't return an ESC but deleted the message before, redisplay it */ if (advance && p_smd && msg_silent == 0 && (State & INSERT)) { if (c == ESC && !mode_deleted && !no_mapping && mode_displayed) { if (typebuf.tb_len && !KeyTyped) redraw_cmdline = TRUE; /* delete mode later */ else unshowmode(FALSE); } else if (c != ESC && mode_deleted) { if (typebuf.tb_len && !KeyTyped) redraw_cmdline = TRUE; /* show mode later */ else showmode(); } } #ifdef FEAT_GUI /* may unshow different cursor shape */ if (gui.in_use && shape_changed) gui_update_cursor(TRUE, FALSE); #endif --vgetc_busy; return c; } /* * inchar() - get one character from * 1. a scriptfile * 2. the keyboard * * As much characters as we can get (upto 'maxlen') are put in "buf" and * NUL terminated (buffer length must be 'maxlen' + 1). * Minimum for "maxlen" is 3!!!! * * "tb_change_cnt" is the value of typebuf.tb_change_cnt if "buf" points into * it. When typebuf.tb_change_cnt changes (e.g., when a message is received * from a remote client) "buf" can no longer be used. "tb_change_cnt" is 0 * otherwise. * * If we got an interrupt all input is read until none is available. * * If wait_time == 0 there is no waiting for the char. * If wait_time == n we wait for n msec for a character to arrive. * If wait_time == -1 we wait forever for a character to arrive. * * Return the number of obtained characters. * Return -1 when end of input script reached. */ int inchar(buf, maxlen, wait_time, tb_change_cnt) char_u *buf; int maxlen; long wait_time; /* milli seconds */ int tb_change_cnt; { int len = 0; /* init for GCC */ int retesc = FALSE; /* return ESC with gotint */ int script_char; if (wait_time == -1L || wait_time > 100L) /* flush output before waiting */ { cursor_on(); out_flush(); #ifdef FEAT_GUI if (gui.in_use) { gui_update_cursor(FALSE, FALSE); # ifdef FEAT_MOUSESHAPE if (postponed_mouseshape) update_mouseshape(-1); # endif } #endif } /* * Don't reset these when at the hit-return prompt, otherwise a endless * recursive loop may result (write error in swapfile, hit-return, timeout * on char wait, flush swapfile, write error....). */ if (State != HITRETURN) { did_outofmem_msg = FALSE; /* display out of memory message (again) */ did_swapwrite_msg = FALSE; /* display swap file write error again */ } undo_off = FALSE; /* restart undo now */ /* * Get a character from a script file if there is one. * If interrupted: Stop reading script files, close them all. */ script_char = -1; while (scriptin[curscript] != NULL && script_char < 0 #ifdef FEAT_EVAL && !ignore_script #endif ) { #if defined(FEAT_NETBEANS_INTG) /* Process the queued netbeans messages. */ netbeans_parse_messages(); #endif if (got_int || (script_char = getc(scriptin[curscript])) < 0) { /* Reached EOF. * Careful: closescript() frees typebuf.tb_buf[] and buf[] may * point inside typebuf.tb_buf[]. Don't use buf[] after this! */ closescript(); /* * When reading script file is interrupted, return an ESC to get * back to normal mode. * Otherwise return -1, because typebuf.tb_buf[] has changed. */ if (got_int) retesc = TRUE; else return -1; } else { buf[0] = script_char; len = 1; } } if (script_char < 0) /* did not get a character from script */ { /* * If we got an interrupt, skip all previously typed characters and * return TRUE if quit reading script file. * Stop reading typeahead when a single CTRL-C was read, * fill_input_buf() returns this when not able to read from stdin. * Don't use buf[] here, closescript() may have freed typebuf.tb_buf[] * and buf may be pointing inside typebuf.tb_buf[]. */ if (got_int) { #define DUM_LEN MAXMAPLEN * 3 + 3 char_u dum[DUM_LEN + 1]; for (;;) { len = ui_inchar(dum, DUM_LEN, 0L, 0); if (len == 0 || (len == 1 && dum[0] == 3)) break; } return retesc; } /* * Always flush the output characters when getting input characters * from the user. */ out_flush(); /* * Fill up to a third of the buffer, because each character may be * tripled below. */ len = ui_inchar(buf, maxlen / 3, wait_time, tb_change_cnt); } if (typebuf_changed(tb_change_cnt)) return 0; return fix_input_buffer(buf, len, script_char >= 0); } /* * Fix typed characters for use by vgetc() and check_termcode(). * buf[] must have room to triple the number of bytes! * Returns the new length. */ int fix_input_buffer(buf, len, script) char_u *buf; int len; int script; /* TRUE when reading from a script */ { int i; char_u *p = buf; /* * Two characters are special: NUL and K_SPECIAL. * When compiled With the GUI CSI is also special. * Replace NUL by K_SPECIAL KS_ZERO KE_FILLER * Replace K_SPECIAL by K_SPECIAL KS_SPECIAL KE_FILLER * Replace CSI by K_SPECIAL KS_EXTRA KE_CSI * Don't replace K_SPECIAL when reading a script file. */ for (i = len; --i >= 0; ++p) { #ifdef FEAT_GUI /* When the GUI is used any character can come after a CSI, don't * escape it. */ if (gui.in_use && p[0] == CSI && i >= 2) { p += 2; i -= 2; } /* When the GUI is not used CSI needs to be escaped. */ else if (!gui.in_use && p[0] == CSI) { mch_memmove(p + 3, p + 1, (size_t)i); *p++ = K_SPECIAL; *p++ = KS_EXTRA; *p = (int)KE_CSI; len += 2; } else #endif if (p[0] == NUL || (p[0] == K_SPECIAL && !script #ifdef FEAT_AUTOCMD /* timeout may generate K_CURSORHOLD */ && (i < 2 || p[1] != KS_EXTRA || p[2] != (int)KE_CURSORHOLD) #endif #if defined(WIN3264) && !defined(FEAT_GUI) /* Win32 console passes modifiers */ && (i < 2 || p[1] != KS_MODIFIER) #endif )) { mch_memmove(p + 3, p + 1, (size_t)i); p[2] = K_THIRD(p[0]); p[1] = K_SECOND(p[0]); p[0] = K_SPECIAL; p += 2; len += 2; } } *p = NUL; /* add trailing NUL */ return len; } #if defined(USE_INPUT_BUF) || defined(PROTO) /* * Return TRUE when bytes are in the input buffer or in the typeahead buffer. * Normally the input buffer would be sufficient, but the server_to_input_buf() * or feedkeys() may insert characters in the typeahead buffer while we are * waiting for input to arrive. */ int input_available() { return (!vim_is_input_buf_empty() # if defined(FEAT_CLIENTSERVER) || defined(FEAT_EVAL) || typebuf_was_filled # endif ); } #endif /* * 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 :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(maptype, arg, mode, abbrev) 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 */ char_u *newstr; int hasarg; int haskey; int did_it = FALSE; #ifdef FEAT_LOCALMAP int did_local = FALSE; #endif int round; char_u *keys_buf = NULL; char_u *arg_buf = NULL; int retval = 0; int do_backslash; int hash; int new_hash; mapblock_T **abbr_table; mapblock_T **map_table; int unique = FALSE; int silent = FALSE; int special = FALSE; #ifdef FEAT_EVAL int expr = FALSE; #endif 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 <buffer>, <silent>, <expr> <script> and <unique> in any order. */ for (;;) { #ifdef FEAT_LOCALMAP /* * Check for "<buffer>": mapping local to buffer. */ if (STRNCMP(keys, "<buffer>", 8) == 0) { keys = skipwhite(keys + 8); map_table = curbuf->b_maphash; abbr_table = &curbuf->b_first_abbr; continue; } #endif /* * Check for "<silent>": don't echo commands. */ if (STRNCMP(keys, "<silent>", 8) == 0) { keys = skipwhite(keys + 8); silent = TRUE; continue; } /* * Check for "<special>": accept special keys in <> */ if (STRNCMP(keys, "<special>", 9) == 0) { keys = skipwhite(keys + 9); special = TRUE; continue; } #ifdef FEAT_EVAL /* * Check for "<script>": remap script-local mappings only */ if (STRNCMP(keys, "<script>", 8) == 0) { keys = skipwhite(keys + 8); noremap = REMAP_SCRIPT; continue; } /* * Check for "<expr>": {rhs} is an expression. */ if (STRNCMP(keys, "<expr>", 6) == 0) { keys = skipwhite(keys + 6); expr = TRUE; continue; } #endif /* * Check for "<unique>": don't overwrite an existing mapping. */ if (STRNCMP(keys, "<unique>", 8) == 0) { keys = skipwhite(keys + 8); unique = TRUE; continue; } break; } validate_maphash(); /* * find end of keys and skip CTRL-Vs (and backslashes) in it * Accept backslash like CTRL-V when 'cpoptions' does not contain 'B'. * with :unmap white space is included in the keys, no argument possible */ p = keys; do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL); while (*p && (maptype == 1 || !vim_iswhite(*p))) { if ((p[0] == Ctrl_V || (do_backslash && p[0] == '\\')) && p[1] != NUL) ++p; /* skip CTRL-V or backslash */ ++p; } if (*p != NUL) *p++ = NUL; p = skipwhite(p); rhs = p; hasarg = (*rhs != NUL); haskey = (*keys != NUL); /* check for :unmap without argument */ if (maptype == 1 && !haskey) { retval = 1; goto theend; } /* * If mapping has been given as ^V<C_UP> say, then replace the term codes * with the appropriate two bytes. If it is a shifted special key, unshift * it too, giving another two bytes. * replace_termcodes() may move the result to allocated memory, which * needs to be freed later (*keys_buf and *arg_buf). * replace_termcodes() also removes CTRL-Vs and sometimes backslashes. */ if (haskey) keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, special); orig_rhs = rhs; if (hasarg) { if (STRICMP(rhs, "<nop>") == 0) /* "<Nop>" means nothing */ rhs = (char_u *)""; else rhs = replace_termcodes(rhs, &arg_buf, FALSE, TRUE, special); } #ifdef FEAT_FKMAP /* * When in right-to-left mode and alternate keymap option set, * reverse the character flow in the rhs in Farsi. */ if (p_altkeymap && curwin->w_p_rl) lrswap(rhs); #endif /* * check arguments and translate function keys */ if (haskey) { len = (int)STRLEN(keys); if (len > MAXMAPLEN) /* maximum length of MAXMAPLEN chars */ { retval = 1; goto theend; } if (abbrev && maptype != 1) { /* * If an abbreviation ends in a keyword character, the * rest must be all keyword-char or all non-keyword-char. * Otherwise we won't be able to find the start of it in a * vi-compatible way. */ #ifdef FEAT_MBYTE if (has_mbyte) { int first, last; int same = -1; first = vim_iswordp(keys); last = first; p = keys + (*mb_ptr2len)(keys); n = 1; while (p < keys + len) { ++n; /* nr of (multi-byte) chars */ last = vim_iswordp(p); /* type of last char */ if (same == -1 && last != first) same = n - 1; /* count of same char type */ p += (*mb_ptr2len)(p); } if (last && n > 2 && same >= 0 && same < n - 1) { retval = 1; goto theend; } } else #endif if (vim_iswordc(keys[len - 1])) /* ends in keyword char */ for (n = 0; n < len - 2; ++n) if (vim_iswordc(keys[n]) != vim_iswordc(keys[len - 2])) { retval = 1; goto theend; } /* An abbreviation cannot contain white space. */ for (n = 0; n < len; ++n) if (vim_iswhite(keys[n])) { retval = 1; goto theend; } } } if (haskey && hasarg && abbrev) /* if we will add an abbreviation */ no_abbr = FALSE; /* reset flag that indicates there are no abbreviations */ if (!haskey || (maptype != 1 && !hasarg)) msg_start(); #ifdef FEAT_LOCALMAP /* * Check if a new local mapping wasn't already defined globally. */ if (map_table == curbuf->b_maphash && haskey && hasarg && maptype != 1) { /* need to loop over all global hash lists */ for (hash = 0; hash < 256 && !got_int; ++hash) { if (abbrev) { if (hash != 0) /* there is only one abbreviation list */ break; mp = first_abbr; } else mp = maphash[hash]; for ( ; mp != NULL && !got_int; mp = mp->m_next) { /* check entries with the same mode */ if ((mp->m_mode & mode) != 0 && mp->m_keylen == len && unique && STRNCMP(mp->m_keys, keys, (size_t)len) == 0) { if (abbrev) EMSG2(_("E224: global abbreviation already exists for %s"), mp->m_keys); else EMSG2(_("E225: global mapping already exists for %s"), mp->m_keys); retval = 5; goto theend; } } } } /* * When listing global mappings, also list buffer-local ones here. */ if (map_table != curbuf->b_maphash && !hasarg && maptype != 1) { /* need to loop over all global hash lists */ for (hash = 0; hash < 256 && !got_int; ++hash) { if (abbrev) { if (hash != 0) /* there is only one abbreviation list */ break; mp = curbuf->b_first_abbr; } else mp = curbuf->b_maphash[hash]; for ( ; mp != NULL && !got_int; mp = mp->m_next) { /* check entries with the same mode */ if ((mp->m_mode & mode) != 0) { if (!haskey) /* show all entries */ { showmap(mp, TRUE); did_local = TRUE; } else { n = mp->m_keylen; if (STRNCMP(mp->m_keys, keys, (size_t)(n < len ? n : len)) == 0) { showmap(mp, TRUE); did_local = TRUE; } } } } } } #endif /* * Find an entry in the maphash[] list that matches. * For :unmap we may loop two times: once to try to unmap an entry with a * matching 'from' part, a second time, if the first fails, to unmap an * entry with a matching 'to' part. This was done to allow ":ab foo bar" * to be unmapped by typing ":unab foo", where "foo" will be replaced by * "bar" because of the abbreviation. */ for (round = 0; (round == 0 || maptype == 1) && round <= 1 && !did_it && !got_int; ++round) { /* need to loop over all hash lists */ for (hash = 0; hash < 256 && !got_int; ++hash) { if (abbrev) { if (hash > 0) /* there is only one abbreviation list */ break; mpp = abbr_table; } else mpp = &(map_table[hash]); for (mp = *mpp; mp != NULL && !got_int; mp = *mpp) { if (!(mp->m_mode & mode)) /* skip entries with wrong mode */ { mpp = &(mp->m_next); continue; } if (!haskey) /* show all entries */ { showmap(mp, map_table != maphash); did_it = TRUE; } else /* do we have a match? */ { if (round) /* second round: Try unmap "rhs" string */ { n = (int)STRLEN(mp->m_str); p = mp->m_str; } else { n = mp->m_keylen; p = mp->m_keys; } if (STRNCMP(p, keys, (size_t)(n < len ? n : len)) == 0) { if (maptype == 1) /* delete entry */ { /* Only accept a full match. For abbreviations we * ignore trailing space when matching with the * "lhs", since an abbreviation can't have * trailing space. */ if (n != len && (!abbrev || round || n > len || *skipwhite(keys + n) != NUL)) { mpp = &(mp->m_next); continue; } /* * We reset the indicated mode bits. If nothing is * left the entry is deleted below. */ mp->m_mode &= ~mode; did_it = TRUE; /* remember we did something */ } else if (!hasarg) /* show matching entry */ { showmap(mp, map_table != maphash); did_it = TRUE; } else if (n != len) /* new entry is ambiguous */ { mpp = &(mp->m_next); continue; } else if (unique) { if (abbrev) EMSG2(_("E226: abbreviation already exists for %s"), p); else EMSG2(_("E227: mapping already exists for %s"), p); retval = 5; goto theend; } else /* new rhs for existing entry */ { mp->m_mode &= ~mode; /* remove mode bits */ if (mp->m_mode == 0 && !did_it) /* reuse entry */ { newstr = vim_strsave(rhs); if (newstr == NULL) { retval = 4; /* no mem */ goto theend; } vim_free(mp->m_str); mp->m_str = newstr; vim_free(mp->m_orig_str); mp->m_orig_str = vim_strsave(orig_rhs); mp->m_noremap = noremap; mp->m_silent = silent; mp->m_mode = mode; #ifdef FEAT_EVAL mp->m_expr = expr; mp->m_script_ID = current_SID; #endif did_it = TRUE; } } if (mp->m_mode == 0) /* entry can be deleted */ { map_free(mpp); continue; /* continue with *mpp */ } /* * May need to put this entry into another hash list. */ new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]); if (!abbrev && new_hash != hash) { *mpp = mp->m_next; mp->m_next = map_table[new_hash]; map_table[new_hash] = mp; continue; /* continue with *mpp */ } } } mpp = &(mp->m_next); } } } if (maptype == 1) /* delete entry */ { if (!did_it) retval = 2; /* no match */ goto theend; } if (!haskey || !hasarg) /* print entries */ { if (!did_it #ifdef FEAT_LOCALMAP && !did_local #endif ) { if (abbrev) MSG(_("No abbreviation found")); else MSG(_("No mapping found")); } goto theend; /* listing finished */ } if (did_it) /* have added the new entry already */ goto theend; /* * Get here when adding a new entry to the maphash[] list or abbrlist. */ mp = (mapblock_T *)alloc((unsigned)sizeof(mapblock_T)); if (mp == NULL) { retval = 4; /* no mem */ goto theend; } /* If CTRL-C has been mapped, don't always use it for Interrupting */ if (*keys == Ctrl_C) mapped_ctrl_c = TRUE; 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); retval = 4; /* no mem */ goto theend; } mp->m_keylen = (int)STRLEN(mp->m_keys); mp->m_noremap = noremap; mp->m_silent = silent; mp->m_mode = mode; #ifdef FEAT_EVAL mp->m_expr = expr; mp->m_script_ID = current_SID; #endif /* add the new entry in front of the abbrlist or maphash[] list */ if (abbrev) { mp->m_next = *abbr_table; *abbr_table = mp; } else { n = MAP_HASH(mp->m_mode, mp->m_keys[0]); mp->m_next = map_table[n]; map_table[n] = mp; } theend: vim_free(keys_buf); vim_free(arg_buf); return retval; } /* * 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(mpp) 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); } /* * Initialize maphash[] for first use. */ static void validate_maphash() { if (!maphash_valid) { vim_memset(maphash, 0, sizeof(maphash)); maphash_valid = TRUE; } } /* * Get the mapping mode from the command name. */ int get_map_mode(cmdp, forceit) char_u **cmdp; int forceit; { char_u *p; int modec; int mode; p = *cmdp; modec = *p++; if (modec == 'i') mode = INSERT; /* :imap */ else if (modec == 'l') mode = LANGMAP; /* :lmap */ else if (modec == 'c') mode = CMDLINE; /* :cmap */ else if (modec == 'n' && *p != 'o') /* avoid :noremap */ mode = NORMAL; /* :nmap */ else if (modec == 'v') mode = VISUAL + SELECTMODE; /* :vmap */ else if (modec == 'x') mode = VISUAL; /* :xmap */ else if (modec == 's') mode = SELECTMODE; /* :smap */ else if (modec == 'o') mode = OP_PENDING; /* :omap */ else { --p; if (forceit) mode = INSERT + CMDLINE; /* :map ! */ else mode = VISUAL + SELECTMODE + NORMAL + OP_PENDING;/* :map */ } *cmdp = p; return mode; } /* * Clear all mappings or abbreviations. * 'abbr' should be FALSE for mappings, TRUE for abbreviations. */ void map_clear(cmdp, arg, forceit, abbr) char_u *cmdp; char_u *arg UNUSED; int forceit; int abbr; { int mode; #ifdef FEAT_LOCALMAP int local; local = (STRCMP(arg, "<buffer>") == 0); if (!local && *arg != NUL) { EMSG(_(e_invarg)); return; } #endif mode = get_map_mode(&cmdp, forceit); map_clear_int(curbuf, mode, #ifdef FEAT_LOCALMAP local, #else FALSE, #endif abbr); } /* * Clear all mappings in "mode". */ void map_clear_int(buf, mode, local, abbr) buf_T *buf UNUSED; /* buffer for local mappings */ int mode; /* mode in which to delete */ int local UNUSED; /* TRUE for buffer-local mappings */ int abbr; /* TRUE for abbreviations */ { mapblock_T *mp, **mpp; int hash; int new_hash; validate_maphash(); for (hash = 0; hash < 256; ++hash) { if (abbr) { if (hash > 0) /* there is only one abbrlist */ break; #ifdef FEAT_LOCALMAP if (local) mpp = &buf->b_first_abbr; else #endif mpp = &first_abbr; } else { #ifdef FEAT_LOCALMAP if (local) mpp = &buf->b_maphash[hash]; else #endif mpp = &maphash[hash]; } while (*mpp != NULL) { mp = *mpp; if (mp->m_mode & mode) { mp->m_mode &= ~mode; if (mp->m_mode == 0) /* entry can be deleted */ { map_free(mpp); continue; } /* * May need to put this entry into another hash list. */ new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]); if (!abbr && new_hash != hash) { *mpp = mp->m_next; #ifdef FEAT_LOCALMAP if (local) { mp->m_next = buf->b_maphash[new_hash]; buf->b_maphash[new_hash] = mp; } else #endif { mp->m_next = maphash[new_hash]; maphash[new_hash] = mp; } continue; /* continue with *mpp */ } } mpp = &(mp->m_next); } } } /* * Return characters to represent the map mode in an allocated string. * Returns NULL when out of memory. */ char_u * map_mode_to_chars(mode) 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 & (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(mp, local) mapblock_T *mp; int local; /* TRUE for buffer-local map */ { int len = 1; char_u *mapchars; 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(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); do { msg_putchar(' '); /* padd with blanks */ ++len; } while (len < 12); if (mp->m_noremap == REMAP_NONE) msg_puts_attr((char_u *)"*", hl_attr(HLF_8)); else if (mp->m_noremap == REMAP_SCRIPT) msg_puts_attr((char_u *)"&", hl_attr(HLF_8)); else msg_putchar(' '); if (local) msg_putchar('@'); else msg_putchar(' '); /* Use FALSE below if we only want things like <Up> 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((char_u *)"<Nop>", hl_attr(HLF_8)); else msg_outtrans_special(mp->m_str, FALSE); #ifdef FEAT_EVAL if (p_verbose > 0) last_set_msg(mp->m_script_ID); #endif out_flush(); /* show one line at a time */ } #if defined(FEAT_EVAL) || defined(PROTO) /* * Return TRUE if a map exists that has "str" in the rhs for mode "modechars". * Recognize termcap codes in "str". * Also checks mappings local to the current buffer. */ int map_to_exists(str, modechars, abbr) char_u *str; char_u *modechars; int abbr; { int mode = 0; char_u *rhs; char_u *buf; int retval; rhs = replace_termcodes(str, &buf, FALSE, TRUE, FALSE); if (vim_strchr(modechars, 'n') != NULL) mode |= NORMAL; if (vim_strchr(modechars, 'v') != NULL) mode |= VISUAL + SELECTMODE; if (vim_strchr(modechars, 'x') != NULL) mode |= VISUAL; if (vim_strchr(modechars, 's') != NULL) mode |= SELECTMODE; if (vim_strchr(modechars, 'o') != NULL) mode |= OP_PENDING; if (vim_strchr(modechars, 'i') != NULL) mode |= INSERT; if (vim_strchr(modechars, 'l') != NULL) mode |= LANGMAP; if (vim_strchr(modechars, 'c') != NULL) mode |= CMDLINE; retval = map_to_exists_mode(rhs, mode, abbr); vim_free(buf); return retval; } #endif /* * Return TRUE if a map exists that has "str" in the rhs for mode "mode". * Also checks mappings local to the current buffer. */ int map_to_exists_mode(rhs, mode, abbr) char_u *rhs; int mode; int abbr; { mapblock_T *mp; int hash; # ifdef FEAT_LOCALMAP int expand_buffer = FALSE; validate_maphash(); /* Do it twice: once for global maps and once for local maps. */ for (;;) { # endif for (hash = 0; hash < 256; ++hash) { if (abbr) { if (hash > 0) /* there is only one abbr list */ break; #ifdef FEAT_LOCALMAP if (expand_buffer) mp = curbuf->b_first_abbr; else #endif mp = first_abbr; } # ifdef FEAT_LOCALMAP else if (expand_buffer) mp = curbuf->b_maphash[hash]; # endif else mp = maphash[hash]; for (; mp; mp = mp->m_next) { if ((mp->m_mode & mode) && strstr((char *)mp->m_str, (char *)rhs) != NULL) return TRUE; } } # ifdef FEAT_LOCALMAP if (expand_buffer) break; expand_buffer = TRUE; } # endif return FALSE; } #if defined(FEAT_CMDL_COMPL) || defined(PROTO) /* * Used below when expanding mapping/abbreviation names. */ static int expand_mapmodes = 0; static int expand_isabbrev = 0; #ifdef FEAT_LOCALMAP static int expand_buffer = FALSE; #endif /* * Work out what to complete when doing command line completion of mapping * or abbreviation names. */ char_u * set_context_in_map_cmd(xp, cmd, arg, forceit, isabbrev, isunmap, cmdidx) expand_T *xp; char_u *cmd; char_u *arg; int forceit; /* TRUE if '!' given */ int isabbrev; /* TRUE if abbreviation */ int isunmap; /* TRUE if unmap/unabbrev command */ cmdidx_T cmdidx; { if (forceit && cmdidx != CMD_map && cmdidx != CMD_unmap) xp->xp_context = EXPAND_NOTHING; else { if (isunmap) expand_mapmodes = get_map_mode(&cmd, forceit || isabbrev); else { expand_mapmodes = INSERT + CMDLINE; if (!isabbrev) expand_mapmodes += VISUAL + SELECTMODE + NORMAL + OP_PENDING; } expand_isabbrev = isabbrev; xp->xp_context = EXPAND_MAPPINGS; #ifdef FEAT_LOCALMAP expand_buffer = FALSE; #endif for (;;) { #ifdef FEAT_LOCALMAP if (STRNCMP(arg, "<buffer>", 8) == 0) { expand_buffer = TRUE; arg = skipwhite(arg + 8); continue; } #endif if (STRNCMP(arg, "<unique>", 8) == 0) { arg = skipwhite(arg + 8); continue; } if (STRNCMP(arg, "<silent>", 8) == 0) { arg = skipwhite(arg + 8); continue; } #ifdef FEAT_EVAL if (STRNCMP(arg, "<script>", 8) == 0) { arg = skipwhite(arg + 8); continue; } if (STRNCMP(arg, "<expr>", 6) == 0) { arg = skipwhite(arg + 6); continue; } #endif break; } xp->xp_pattern = arg; } return NULL; } /* * Find all mapping/abbreviation names that match regexp 'prog'. * For command line expansion of ":[un]map" and ":[un]abbrev" in all modes. * Return OK if matches found, FAIL otherwise. */ int ExpandMappings(regmatch, num_file, file) regmatch_T *regmatch; int *num_file; char_u ***file; { mapblock_T *mp; int hash; int count; int round; char_u *p; int i; validate_maphash(); *num_file = 0; /* return values in case of FAIL */ *file = NULL; /* * round == 1: Count the matches. * round == 2: Build the array to keep the matches. */ for (round = 1; round <= 2; ++round) { count = 0; for (i = 0; i < 5; ++i) { if (i == 0) p = (char_u *)"<silent>"; else if (i == 1) p = (char_u *)"<unique>"; #ifdef FEAT_EVAL else if (i == 2) p = (char_u *)"<script>"; else if (i == 3) p = (char_u *)"<expr>"; #endif #ifdef FEAT_LOCALMAP else if (i == 4 && !expand_buffer) p = (char_u *)"<buffer>"; #endif else continue; if (vim_regexec(regmatch, p, (colnr_T)0)) { if (round == 1) ++count; else (*file)[count++] = vim_strsave(p); } } for (hash = 0; hash < 256; ++hash) { if (expand_isabbrev) { if (hash > 0) /* only one abbrev list */ break; /* for (hash) */ mp = first_abbr; } #ifdef FEAT_LOCALMAP else if (expand_buffer) mp = curbuf->b_maphash[hash]; #endif else mp = maphash[hash]; for (; mp; mp = mp->m_next) { if (mp->m_mode & expand_mapmodes) { p = translate_mapping(mp->m_keys, TRUE); if (p != NULL && vim_regexec(regmatch, p, (colnr_T)0)) { if (round == 1) ++count; else { (*file)[count++] = p; p = NULL; } } vim_free(p); } } /* for (mp) */ } /* for (hash) */ if (count == 0) /* no match found */ break; /* for (round) */ if (round == 1) { *file = (char_u **)alloc((unsigned)(count * sizeof(char_u *))); if (*file == NULL) return FAIL; } } /* for (round) */ if (count > 1) { char_u **ptr1; char_u **ptr2; char_u **ptr3; /* Sort the matches */ sort_strings(*file, count); /* Remove multiple entries */ ptr1 = *file; ptr2 = ptr1 + 1; ptr3 = ptr1 + count; while (ptr2 < ptr3) { if (STRCMP(*ptr1, *ptr2)) *++ptr1 = *ptr2++; else { vim_free(*ptr2++); count--; } } } *num_file = count; return (count == 0 ? FAIL : OK); } #endif /* FEAT_CMDL_COMPL */ /* * Check for an abbreviation. * Cursor is at ptr[col]. When inserting, mincol is where insert started. * "c" is the character typed before check_abbr was called. It may have * ABBR_OFF added to avoid prepending a CTRL-V to it. * * Historic vi practice: The last character of an abbreviation must be an id * character ([a-zA-Z0-9_]). The characters in front of it must be all id * characters or all non-id characters. This allows for abbr. "#i" to * "#include". * * Vim addition: Allow for abbreviations that end in a non-keyword character. * Then there must be white space before the abbr. * * return TRUE if there is an abbreviation, FALSE if not */ int check_abbr(c, ptr, col, mincol) int c; char_u *ptr; int col; int mincol; { int len; int scol; /* starting column of the abbr. */ int j; char_u *s; #ifdef FEAT_MBYTE char_u tb[MB_MAXBYTES + 4]; #else char_u tb[4]; #endif mapblock_T *mp; #ifdef FEAT_LOCALMAP mapblock_T *mp2; #endif #ifdef FEAT_MBYTE int clen = 0; /* length in characters */ #endif int is_id = TRUE; int vim_abbr; if (typebuf.tb_no_abbr_cnt) /* abbrev. are not recursive */ return FALSE; if ((KeyNoremap & (RM_NONE|RM_SCRIPT)) != 0) /* no remapping implies no abbreviation */ return FALSE; /* * Check for word before the cursor: If it ends in a keyword char all * chars before it must be keyword chars or non-keyword chars, but not * white space. If it ends in a non-keyword char we accept any characters * before it except white space. */ if (col == 0) /* cannot be an abbr. */ return FALSE; #ifdef FEAT_MBYTE if (has_mbyte) { char_u *p; p = mb_prevptr(ptr, ptr + col); if (!vim_iswordp(p)) vim_abbr = TRUE; /* Vim added abbr. */ else { vim_abbr = FALSE; /* vi compatible abbr. */ if (p > ptr) is_id = vim_iswordp(mb_prevptr(ptr, p)); } clen = 1; while (p > ptr + mincol) { p = mb_prevptr(ptr, p); if (vim_isspace(*p) || (!vim_abbr && is_id != vim_iswordp(p))) { p += (*mb_ptr2len)(p); break; } ++clen; } scol = (int)(p - ptr); } else #endif { if (!vim_iswordc(ptr[col - 1])) vim_abbr = TRUE; /* Vim added abbr. */ else { vim_abbr = FALSE; /* vi compatible abbr. */ if (col > 1) is_id = vim_iswordc(ptr[col - 2]); } for (scol = col - 1; scol > 0 && !vim_isspace(ptr[scol - 1]) && (vim_abbr || is_id == vim_iswordc(ptr[scol - 1])); --scol) ; } if (scol < mincol) scol = mincol; if (scol < col) /* there is a word in front of the cursor */ { ptr += scol; len = col - scol; #ifdef FEAT_LOCALMAP mp = curbuf->b_first_abbr; mp2 = first_abbr; if (mp == NULL) { mp = mp2; mp2 = NULL; } #else mp = first_abbr; #endif for ( ; mp; #ifdef FEAT_LOCALMAP mp->m_next == NULL ? (mp = mp2, mp2 = NULL) : #endif (mp = mp->m_next)) { /* find entries with right mode and keys */ if ( (mp->m_mode & State) && mp->m_keylen == len && !STRNCMP(mp->m_keys, ptr, (size_t)len)) break; } if (mp != NULL) { /* * Found a match: * Insert the rest of the abbreviation in typebuf.tb_buf[]. * This goes from end to start. * * Characters 0x000 - 0x100: normal chars, may need CTRL-V, * except K_SPECIAL: Becomes K_SPECIAL KS_SPECIAL KE_FILLER * Characters where IS_SPECIAL() == TRUE: key codes, need * K_SPECIAL. Other characters (with ABBR_OFF): don't use CTRL-V. * * Character CTRL-] is treated specially - it completes the * abbreviation, but is not inserted into the input stream. */ j = 0; if (c != Ctrl_RSB) { /* special key code, split up */ if (IS_SPECIAL(c) || c == K_SPECIAL) { tb[j++] = K_SPECIAL; tb[j++] = K_SECOND(c); tb[j++] = K_THIRD(c); } else { if (c < ABBR_OFF && (c < ' ' || c > '~')) tb[j++] = Ctrl_V; /* special char needs CTRL-V */ #ifdef FEAT_MBYTE if (has_mbyte) { /* if ABBR_OFF has been added, remove it here */ if (c >= ABBR_OFF) c -= ABBR_OFF; j += (*mb_char2bytes)(c, tb + j); } else #endif tb[j++] = c; } tb[j] = NUL; /* insert the last typed char */ (void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent); } #ifdef FEAT_EVAL if (mp->m_expr) s = eval_map_expr(mp->m_str, c); else #endif s = mp->m_str; if (s != NULL) { /* insert the to string */ (void)ins_typebuf(s, mp->m_noremap, 0, TRUE, mp->m_silent); /* no abbrev. for these chars */ typebuf.tb_no_abbr_cnt += (int)STRLEN(s) + j + 1; #ifdef FEAT_EVAL if (mp->m_expr) vim_free(s); #endif } tb[0] = Ctrl_H; tb[1] = NUL; #ifdef FEAT_MBYTE if (has_mbyte) len = clen; /* Delete characters instead of bytes */ #endif while (len-- > 0) /* delete the from string */ (void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent); return TRUE; } } return FALSE; } #ifdef FEAT_EVAL /* * Evaluate the RHS of a mapping or abbreviations and take care of escaping * special characters. */ static char_u * eval_map_expr(str, c) char_u *str; int c; /* NUL or typed character for abbreviation */ { char_u *res; char_u *p; char_u *save_cmd; pos_T save_cursor; save_cmd = save_cmdline_alloc(); if (save_cmd == NULL) return NULL; /* Forbid changing text or using ":normal" to avoid most of the bad side * effects. Also restore the cursor position. */ ++textlock; #ifdef FEAT_EX_EXTRA ++ex_normal_lock; #endif set_vim_var_char(c); /* set v:char to the typed character */ save_cursor = curwin->w_cursor; p = eval_to_string(str, NULL, FALSE); --textlock; #ifdef FEAT_EX_EXTRA --ex_normal_lock; #endif curwin->w_cursor = save_cursor; restore_cmdline_alloc(save_cmd); if (p == NULL) return NULL; res = vim_strsave_escape_csi(p); vim_free(p); return res; } #endif /* * Copy "p" to allocated memory, escaping K_SPECIAL and CSI so that the result * can be put in the typeahead buffer. * Returns NULL when out of memory. */ char_u * vim_strsave_escape_csi(p) char_u *p; { char_u *res; char_u *s, *d; /* Need a buffer to hold up to three times as much. */ res = alloc((unsigned)(STRLEN(p) * 3) + 1); if (res != NULL) { d = res; for (s = p; *s != NUL; ) { if (s[0] == K_SPECIAL && s[1] != NUL && s[2] != NUL) { /* Copy special key unmodified. */ *d++ = *s++; *d++ = *s++; *d++ = *s++; } else { /* Add character, possibly multi-byte to destination, escaping * CSI and K_SPECIAL. */ d = add_char2buf(PTR2CHAR(s), d); mb_ptr_adv(s); } } *d = NUL; } return res; } /* * Remove escaping from CSI and K_SPECIAL characters. Reverse of * vim_strsave_escape_csi(). Works in-place. */ void vim_unescape_csi(p) char_u *p; { char_u *s = p, *d = p; while (*s != NUL) { if (s[0] == K_SPECIAL && s[1] == KS_SPECIAL && s[2] == KE_FILLER) { *d++ = K_SPECIAL; s += 3; } else if ((s[0] == K_SPECIAL || s[0] == CSI) && s[1] == KS_EXTRA && s[2] == (int)KE_CSI) { *d++ = CSI; s += 3; } else *d++ = *s++; } *d = NUL; } /* * Write map commands for the current mappings to an .exrc file. * Return FAIL on error, OK otherwise. */ int makemap(fd, buf) FILE *fd; buf_T *buf; /* buffer for local mappings or NULL */ { mapblock_T *mp; char_u c1, c2, c3; char_u *p; char *cmd; int abbr; int hash; int did_cpo = FALSE; int i; validate_maphash(); /* * Do the loop twice: Once for mappings, once for abbreviations. * Then loop over all map hash lists. */ for (abbr = 0; abbr < 2; ++abbr) for (hash = 0; hash < 256; ++hash) { if (abbr) { if (hash > 0) /* there is only one abbr list */ break; #ifdef FEAT_LOCALMAP if (buf != NULL) mp = buf->b_first_abbr; else #endif mp = first_abbr; } else { #ifdef FEAT_LOCALMAP if (buf != NULL) mp = buf->b_maphash[hash]; else #endif mp = maphash[hash]; } for ( ; mp; mp = mp->m_next) { /* skip script-local mappings */ if (mp->m_noremap == REMAP_SCRIPT) continue; /* skip mappings that contain a <SNR> (script-local thing), * they probably don't work when loaded again */ for (p = mp->m_str; *p != NUL; ++p) if (p[0] == K_SPECIAL && p[1] == KS_EXTRA && p[2] == (int)KE_SNR) break; if (*p != NUL) continue; /* It's possible to create a mapping and then ":unmap" certain * modes. We recreate this here by mapping the individual * modes, which requires up to three of them. */ c1 = NUL; c2 = NUL; c3 = NUL; if (abbr) cmd = "abbr"; else cmd = "map"; switch (mp->m_mode) { case NORMAL + VISUAL + SELECTMODE + OP_PENDING: break; case NORMAL: c1 = 'n'; break; case VISUAL: c1 = 'x'; break; case SELECTMODE: c1 = 's'; break; case OP_PENDING: c1 = 'o'; break; case NORMAL + VISUAL: c1 = 'n'; c2 = 'x'; break; case NORMAL + SELECTMODE: c1 = 'n'; c2 = 's'; break; case NORMAL + OP_PENDING: c1 = 'n'; c2 = 'o'; break; case VISUAL + SELECTMODE: c1 = 'v'; break; case VISUAL + OP_PENDING: c1 = 'x'; c2 = 'o'; break; case SELECTMODE + OP_PENDING: c1 = 's'; c2 = 'o'; break; case NORMAL + VISUAL + SELECTMODE: c1 = 'n'; c2 = 'v'; break; case NORMAL + VISUAL + OP_PENDING: c1 = 'n'; c2 = 'x'; c3 = 'o'; break; case NORMAL + SELECTMODE + OP_PENDING: c1 = 'n'; c2 = 's'; c3 = 'o'; break; case VISUAL + SELECTMODE + OP_PENDING: c1 = 'v'; c2 = 'o'; break; case CMDLINE + INSERT: if (!abbr) cmd = "map!"; break; case CMDLINE: c1 = 'c'; break; case INSERT: c1 = 'i'; break; case LANGMAP: c1 = 'l'; break; default: EMSG(_("E228: makemap: Illegal mode")); return FAIL; } do /* do this twice if c2 is set, 3 times with c3 */ { /* When outputting <> form, need to make sure that 'cpo' * is set to the Vim default. */ if (!did_cpo) { if (*mp->m_str == NUL) /* will use <Nop> */ did_cpo = TRUE; else for (i = 0; i < 2; ++i) for (p = (i ? mp->m_str : mp->m_keys); *p; ++p) if (*p == K_SPECIAL || *p == NL) did_cpo = TRUE; if (did_cpo) { if (fprintf(fd, "let s:cpo_save=&cpo") < 0 || put_eol(fd) < 0 || fprintf(fd, "set cpo&vim") < 0 || put_eol(fd) < 0) return FAIL; } } if (c1 && putc(c1, fd) < 0) return FAIL; if (mp->m_noremap != REMAP_YES && fprintf(fd, "nore") < 0) return FAIL; if (fputs(cmd, fd) < 0) return FAIL; if (buf != NULL && fputs(" <buffer>", fd) < 0) return FAIL; if (mp->m_silent && fputs(" <silent>", fd) < 0) return FAIL; #ifdef FEAT_EVAL if (mp->m_noremap == REMAP_SCRIPT && fputs("<script>", fd) < 0) return FAIL; if (mp->m_expr && fputs(" <expr>", fd) < 0) return FAIL; #endif if ( putc(' ', fd) < 0 || put_escstr(fd, mp->m_keys, 0) == FAIL || putc(' ', fd) < 0 || put_escstr(fd, mp->m_str, 1) == FAIL || put_eol(fd) < 0) return FAIL; c1 = c2; c2 = c3; c3 = NUL; } while (c1 != NUL); } } if (did_cpo) if (fprintf(fd, "let &cpo=s:cpo_save") < 0 || put_eol(fd) < 0 || fprintf(fd, "unlet s:cpo_save") < 0 || put_eol(fd) < 0) return FAIL; return OK; } /* * write escape string to file * "what": 0 for :map lhs, 1 for :map rhs, 2 for :set * * return FAIL for failure, OK otherwise */ int put_escstr(fd, strstart, what) FILE *fd; char_u *strstart; int what; { char_u *str = strstart; int c; int modifiers; /* :map xx <Nop> */ if (*str == NUL && what == 1) { if (fprintf(fd, "<Nop>") < 0) return FAIL; return OK; } for ( ; *str != NUL; ++str) { #ifdef FEAT_MBYTE char_u *p; /* Check for a multi-byte character, which may contain escaped * K_SPECIAL and CSI bytes */ p = mb_unescape(&str); if (p != NULL) { while (*p != NUL) if (fputc(*p++, fd) < 0) return FAIL; --str; continue; } #endif c = *str; /* * Special key codes have to be translated to be able to make sense * when they are read back. */ if (c == K_SPECIAL && what != 2) { modifiers = 0x0; if (str[1] == KS_MODIFIER) { modifiers = str[2]; str += 3; c = *str; } if (c == K_SPECIAL) { c = TO_SPECIAL(str[1], str[2]); str += 2; } if (IS_SPECIAL(c) || modifiers) /* special key */ { if (fputs((char *)get_special_key_name(c, modifiers), fd) < 0) return FAIL; continue; } } /* * A '\n' in a map command should be written as <NL>. * A '\n' in a set command should be written as \^V^J. */ if (c == NL) { if (what == 2) { if (fprintf(fd, IF_EB("\\\026\n", "\\" CTRL_V_STR "\n")) < 0) return FAIL; } else { if (fprintf(fd, "<NL>") < 0) return FAIL; } continue; } /* * Some characters have to be escaped with CTRL-V to * prevent them from misinterpreted in DoOneCmd(). * A space, Tab and '"' has to be escaped with a backslash to * prevent it to be misinterpreted in do_set(). * A space has to be escaped with a CTRL-V when it's at the start of a * ":map" rhs. * A '<' has to be escaped with a CTRL-V to prevent it being * interpreted as the start of a special key name. * A space in the lhs of a :map needs a CTRL-V. */ if (what == 2 && (vim_iswhite(c) || c == '"' || c == '\\')) { if (putc('\\', fd) < 0) return FAIL; } else if (c < ' ' || c > '~' || c == '|' || (what == 0 && c == ' ') || (what == 1 && str == strstart && c == ' ') || (what != 2 && c == '<')) { if (putc(Ctrl_V, fd) < 0) return FAIL; } if (putc(c, fd) < 0) return FAIL; } return OK; } /* * Check all mappings for the presence of special key codes. * Used after ":set term=xxx". */ void check_map_keycodes() { mapblock_T *mp; char_u *p; int i; char_u buf[3]; char_u *save_name; int abbr; int hash; #ifdef FEAT_LOCALMAP buf_T *bp; #endif validate_maphash(); save_name = sourcing_name; sourcing_name = (char_u *)"mappings"; /* avoids giving error messages */ #ifdef FEAT_LOCALMAP /* This this once for each buffer, and then once for global * mappings/abbreviations with bp == NULL */ for (bp = firstbuf; ; bp = bp->b_next) { #endif /* * Do the loop twice: Once for mappings, once for abbreviations. * Then loop over all map hash lists. */ for (abbr = 0; abbr <= 1; ++abbr) for (hash = 0; hash < 256; ++hash) { if (abbr) { if (hash) /* there is only one abbr list */ break; #ifdef FEAT_LOCALMAP if (bp != NULL) mp = bp->b_first_abbr; else #endif mp = first_abbr; } else { #ifdef FEAT_LOCALMAP if (bp != NULL) mp = bp->b_maphash[hash]; else #endif mp = maphash[hash]; } for ( ; mp != NULL; mp = mp->m_next) { for (i = 0; i <= 1; ++i) /* do this twice */ { if (i == 0) p = mp->m_keys; /* once for the "from" part */ else p = mp->m_str; /* and once for the "to" part */ while (*p) { if (*p == K_SPECIAL) { ++p; if (*p < 128) /* for "normal" tcap entries */ { buf[0] = p[0]; buf[1] = p[1]; buf[2] = NUL; (void)add_termcap_entry(buf, FALSE); } ++p; } ++p; } } } } #ifdef FEAT_LOCALMAP if (bp == NULL) break; } #endif sourcing_name = save_name; } #if defined(FEAT_EVAL) || defined(PROTO) /* * Check the string "keys" against the lhs of all mappings. * Return pointer to rhs of mapping (mapblock->m_str). * NULL when no mapping found. */ char_u * check_map(keys, mode, exact, ign_mod, abbr, mp_ptr, local_ptr) char_u *keys; int mode; int exact; /* require exact match */ int ign_mod; /* ignore preceding modifier */ int abbr; /* do abbreviations */ mapblock_T **mp_ptr; /* return: pointer to mapblock or NULL */ int *local_ptr; /* return: buffer-local mapping or NULL */ { int hash; int len, minlen; mapblock_T *mp; char_u *s; #ifdef FEAT_LOCALMAP int local; #endif validate_maphash(); len = (int)STRLEN(keys); #ifdef FEAT_LOCALMAP for (local = 1; local >= 0; --local) #endif /* loop over all hash lists */ for (hash = 0; hash < 256; ++hash) { if (abbr) { if (hash > 0) /* there is only one list. */ break; #ifdef FEAT_LOCALMAP if (local) mp = curbuf->b_first_abbr; else #endif mp = first_abbr; } #ifdef FEAT_LOCALMAP else if (local) mp = curbuf->b_maphash[hash]; #endif else mp = maphash[hash]; for ( ; mp != NULL; mp = mp->m_next) { /* skip entries with wrong mode, wrong length and not matching * ones */ if ((mp->m_mode & mode) && (!exact || mp->m_keylen == len)) { if (len > mp->m_keylen) minlen = mp->m_keylen; else minlen = len; s = mp->m_keys; if (ign_mod && s[0] == K_SPECIAL && s[1] == KS_MODIFIER && s[2] != NUL) { s += 3; if (len > mp->m_keylen - 3) minlen = mp->m_keylen - 3; } if (STRNCMP(s, keys, minlen) == 0) { if (mp_ptr != NULL) *mp_ptr = mp; if (local_ptr != NULL) #ifdef FEAT_LOCALMAP *local_ptr = local; #else *local_ptr = 0; #endif return mp->m_str; } } } } return NULL; } #endif #if defined(MSDOS) || defined(MSWIN) || defined(OS2) || defined(MACOS) #define VIS_SEL (VISUAL+SELECTMODE) /* abbreviation */ /* * Default mappings for some often used keys. */ static struct initmap { char_u *arg; int mode; } initmappings[] = { #if defined(MSDOS) || defined(MSWIN) || defined(OS2) /* Use the Windows (CUA) keybindings. */ # ifdef FEAT_GUI /* paste, copy and cut */ {(char_u *)"<S-Insert> \"*P", NORMAL}, {(char_u *)"<S-Insert> \"-d\"*P", VIS_SEL}, {(char_u *)"<S-Insert> <C-R><C-O>*", INSERT+CMDLINE}, {(char_u *)"<C-Insert> \"*y", VIS_SEL}, {(char_u *)"<S-Del> \"*d", VIS_SEL}, {(char_u *)"<C-Del> \"*d", VIS_SEL}, {(char_u *)"<C-X> \"*d", VIS_SEL}, /* Missing: CTRL-C (cancel) and CTRL-V (block selection) */ # else {(char_u *)"\316w <C-Home>", NORMAL+VIS_SEL}, {(char_u *)"\316w <C-Home>", INSERT+CMDLINE}, {(char_u *)"\316u <C-End>", NORMAL+VIS_SEL}, {(char_u *)"\316u <C-End>", INSERT+CMDLINE}, /* paste, copy and cut */ # ifdef FEAT_CLIPBOARD # ifdef DJGPP {(char_u *)"\316\122 \"*P", NORMAL}, /* SHIFT-Insert is "*P */ {(char_u *)"\316\122 \"-d\"*P", VIS_SEL}, /* SHIFT-Insert is "-d"*P */ {(char_u *)"\316\122 \022\017*", INSERT}, /* SHIFT-Insert is ^R^O* */ {(char_u *)"\316\222 \"*y", VIS_SEL}, /* CTRL-Insert is "*y */ # if 0 /* Shift-Del produces the same code as Del */ {(char_u *)"\316\123 \"*d", VIS_SEL}, /* SHIFT-Del is "*d */ # endif {(char_u *)"\316\223 \"*d", VIS_SEL}, /* CTRL-Del is "*d */ {(char_u *)"\030 \"-d", VIS_SEL}, /* CTRL-X is "-d */ # else {(char_u *)"\316\324 \"*P", NORMAL}, /* SHIFT-Insert is "*P */ {(char_u *)"\316\324 \"-d\"*P", VIS_SEL}, /* SHIFT-Insert is "-d"*P */ {(char_u *)"\316\324 \022\017*", INSERT}, /* SHIFT-Insert is ^R^O* */ {(char_u *)"\316\325 \"*y", VIS_SEL}, /* CTRL-Insert is "*y */ {(char_u *)"\316\327 \"*d", VIS_SEL}, /* SHIFT-Del is "*d */ {(char_u *)"\316\330 \"*d", VIS_SEL}, /* CTRL-Del is "*d */ {(char_u *)"\030 \"-d", VIS_SEL}, /* CTRL-X is "-d */ # endif # else {(char_u *)"\316\324 P", NORMAL}, /* SHIFT-Insert is P */ {(char_u *)"\316\324 \"-dP", VIS_SEL}, /* SHIFT-Insert is "-dP */ {(char_u *)"\316\324 \022\017\"", INSERT}, /* SHIFT-Insert is ^R^O" */ {(char_u *)"\316\325 y", VIS_SEL}, /* CTRL-Insert is y */ {(char_u *)"\316\327 d", VIS_SEL}, /* SHIFT-Del is d */ {(char_u *)"\316\330 d", VIS_SEL}, /* CTRL-Del is d */ # endif # endif #endif #if defined(MACOS) /* Use the Standard MacOS binding. */ /* paste, copy and cut */ {(char_u *)"<D-v> \"*P", NORMAL}, {(char_u *)"<D-v> \"-d\"*P", VIS_SEL}, {(char_u *)"<D-v> <C-R>*", INSERT+CMDLINE}, {(char_u *)"<D-c> \"*y", VIS_SEL}, {(char_u *)"<D-x> \"*d", VIS_SEL}, {(char_u *)"<Backspace> \"-d", VIS_SEL}, #endif }; # undef VIS_SEL #endif /* * Set up default mappings. */ void init_mappings() { #if defined(MSDOS) || defined(MSWIN) || defined(OS2) || defined(MACOS) int i; for (i = 0; i < sizeof(initmappings) / sizeof(struct initmap); ++i) add_map(initmappings[i].arg, initmappings[i].mode); #endif } #if defined(MSDOS) || defined(MSWIN) || defined(OS2) \ || defined(FEAT_CMDWIN) || defined(MACOS) || defined(PROTO) /* * Add a mapping "map" for mode "mode". * Need to put string in allocated memory, because do_map() will modify it. */ void add_map(map, mode) char_u *map; int mode; { char_u *s; char_u *cpo_save = p_cpo; p_cpo = (char_u *)""; /* Allow <> notation */ s = vim_strsave(map); if (s != NULL) { (void)do_map(0, s, mode, FALSE); vim_free(s); } p_cpo = cpo_save; } #endif