Mercurial > vim
comparison src/getchar.c @ 17576:97a750e8707f v8.1.1785
patch 8.1.1785: map functionality mixed with character input
commit https://github.com/vim/vim/commit/b66bab381c8ba71fd6e92327d1d34c6f8a65f2a7
Author: Bram Moolenaar <Bram@vim.org>
Date: Thu Aug 1 14:28:24 2019 +0200
patch 8.1.1785: map functionality mixed with character input
Problem: Map functionality mixed with character input.
Solution: Move the map functionality to a separate file. (Yegappan
Lakshmanan, closes #4740) Graduate the +localmap feature.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Thu, 01 Aug 2019 14:30:07 +0200 |
parents | e17cbc3e545d |
children | 8d20183f2a8c |
comparison
equal
deleted
inserted
replaced
17575:b2a246caeb25 | 17576:97a750e8707f |
---|---|
50 * when block_redo is TRUE redo buffer will not be changed | 50 * when block_redo is TRUE redo buffer will not be changed |
51 * used by edit() to repeat insertions and 'V' command for redoing | 51 * used by edit() to repeat insertions and 'V' command for redoing |
52 */ | 52 */ |
53 static int block_redo = FALSE; | 53 static int block_redo = FALSE; |
54 | 54 |
55 /* | |
56 * Make a hash value for a mapping. | |
57 * "mode" is the lower 4 bits of the State for the mapping. | |
58 * "c1" is the first character of the "lhs". | |
59 * Returns a value between 0 and 255, index in maphash. | |
60 * Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode. | |
61 */ | |
62 #define MAP_HASH(mode, c1) (((mode) & (NORMAL + VISUAL + SELECTMODE + OP_PENDING + TERMINAL)) ? (c1) : ((c1) ^ 0x80)) | |
63 | |
64 /* | |
65 * Each mapping is put in one of the 256 hash lists, to speed up finding it. | |
66 */ | |
67 static mapblock_T *(maphash[256]); | |
68 static int maphash_valid = FALSE; | |
69 | |
70 /* | |
71 * List used for abbreviations. | |
72 */ | |
73 static mapblock_T *first_abbr = NULL; /* first entry in abbrlist */ | |
74 | |
75 static int KeyNoremap = 0; /* remapping flags */ | 55 static int KeyNoremap = 0; /* remapping flags */ |
76 | 56 |
77 /* | 57 /* |
78 * Variables used by vgetorpeek() and flush_buffers(). | 58 * Variables used by vgetorpeek() and flush_buffers(). |
79 * | 59 * |
111 static int read_readbuf(buffheader_T *buf, int advance); | 91 static int read_readbuf(buffheader_T *buf, int advance); |
112 static void init_typebuf(void); | 92 static void init_typebuf(void); |
113 static void may_sync_undo(void); | 93 static void may_sync_undo(void); |
114 static void closescript(void); | 94 static void closescript(void); |
115 static int vgetorpeek(int); | 95 static int vgetorpeek(int); |
116 static void map_free(mapblock_T **); | |
117 static void validate_maphash(void); | |
118 static void showmap(mapblock_T *mp, int local); | |
119 static int inchar(char_u *buf, int maxlen, long wait_time); | 96 static int inchar(char_u *buf, int maxlen, long wait_time); |
120 #ifdef FEAT_EVAL | |
121 static char_u *eval_map_expr(char_u *str, int c); | |
122 #endif | |
123 | 97 |
124 /* | 98 /* |
125 * Free and clear a buffer. | 99 * Free and clear a buffer. |
126 */ | 100 */ |
127 void | 101 void |
906 typebuf.tb_buflen = TYPELEN_INIT; | 880 typebuf.tb_buflen = TYPELEN_INIT; |
907 typebuf.tb_len = 0; | 881 typebuf.tb_len = 0; |
908 typebuf.tb_off = MAXMAPLEN + 4; | 882 typebuf.tb_off = MAXMAPLEN + 4; |
909 typebuf.tb_change_cnt = 1; | 883 typebuf.tb_change_cnt = 1; |
910 } | 884 } |
885 } | |
886 | |
887 /* | |
888 * Returns TRUE when keys cannot be remapped. | |
889 */ | |
890 int | |
891 noremap_keys(void) | |
892 { | |
893 return KeyNoremap & (RM_NONE|RM_SCRIPT); | |
911 } | 894 } |
912 | 895 |
913 /* | 896 /* |
914 * Insert a string in position 'offset' in the typeahead buffer (for "@r" | 897 * Insert a string in position 'offset' in the typeahead buffer (for "@r" |
915 * and ":normal" command, vgetorpeek() and check_termcode()). | 898 * and ":normal" command, vgetorpeek() and check_termcode()). |
1960 { | 1943 { |
1961 int c, c1; | 1944 int c, c1; |
1962 int keylen; | 1945 int keylen; |
1963 char_u *s; | 1946 char_u *s; |
1964 mapblock_T *mp; | 1947 mapblock_T *mp; |
1965 #ifdef FEAT_LOCALMAP | |
1966 mapblock_T *mp2; | 1948 mapblock_T *mp2; |
1967 #endif | |
1968 mapblock_T *mp_match; | 1949 mapblock_T *mp_match; |
1969 int mp_match_len = 0; | 1950 int mp_match_len = 0; |
1970 int timedout = FALSE; /* waited for more than 1 second | 1951 int timedout = FALSE; /* waited for more than 1 second |
1971 for mapping to complete */ | 1952 for mapping to complete */ |
1972 int mapdepth = 0; /* check for recursive mapping */ | 1953 int mapdepth = 0; /* check for recursive mapping */ |
2110 * - in Ctrl-X mode, and we get a valid char for that mode | 2091 * - in Ctrl-X mode, and we get a valid char for that mode |
2111 */ | 2092 */ |
2112 mp = NULL; | 2093 mp = NULL; |
2113 max_mlen = 0; | 2094 max_mlen = 0; |
2114 c1 = typebuf.tb_buf[typebuf.tb_off]; | 2095 c1 = typebuf.tb_buf[typebuf.tb_off]; |
2115 if (no_mapping == 0 && maphash_valid | 2096 if (no_mapping == 0 && is_maphash_valid() |
2116 && (no_zero_mapping == 0 || c1 != '0') | 2097 && (no_zero_mapping == 0 || c1 != '0') |
2117 && (typebuf.tb_maplen == 0 | 2098 && (typebuf.tb_maplen == 0 |
2118 || (p_remap | 2099 || (p_remap |
2119 && (typebuf.tb_noremap[typebuf.tb_off] | 2100 && (typebuf.tb_noremap[typebuf.tb_off] |
2120 & (RM_NONE|RM_ABBR)) == 0)) | 2101 & (RM_NONE|RM_ABBR)) == 0)) |
2139 (State & (CMDLINE | INSERT)) == 0 | 2120 (State & (CMDLINE | INSERT)) == 0 |
2140 && get_real_state() != SELECTMODE); | 2121 && get_real_state() != SELECTMODE); |
2141 nolmaplen = 0; | 2122 nolmaplen = 0; |
2142 } | 2123 } |
2143 #endif | 2124 #endif |
2144 #ifdef FEAT_LOCALMAP | 2125 // First try buffer-local mappings. |
2145 /* First try buffer-local mappings. */ | 2126 mp = get_buf_maphash_list(local_State, c1); |
2146 mp = curbuf->b_maphash[MAP_HASH(local_State, c1)]; | 2127 mp2 = get_maphash_list(local_State, c1); |
2147 mp2 = maphash[MAP_HASH(local_State, c1)]; | |
2148 if (mp == NULL) | 2128 if (mp == NULL) |
2149 { | 2129 { |
2150 /* There are no buffer-local mappings. */ | 2130 // There are no buffer-local mappings. |
2151 mp = mp2; | 2131 mp = mp2; |
2152 mp2 = NULL; | 2132 mp2 = NULL; |
2153 } | 2133 } |
2154 #else | |
2155 mp = maphash[MAP_HASH(local_State, c1)]; | |
2156 #endif | |
2157 /* | 2134 /* |
2158 * Loop until a partly matching mapping is found or | 2135 * Loop until a partly matching mapping is found or |
2159 * all (local) mappings have been checked. | 2136 * all (local) mappings have been checked. |
2160 * The longest full match is remembered in "mp_match". | 2137 * The longest full match is remembered in "mp_match". |
2161 * A full match is only accepted if there is no partly | 2138 * A full match is only accepted if there is no partly |
2162 * match, so "aa" and "aaa" can both be mapped. | 2139 * match, so "aa" and "aaa" can both be mapped. |
2163 */ | 2140 */ |
2164 mp_match = NULL; | 2141 mp_match = NULL; |
2165 mp_match_len = 0; | 2142 mp_match_len = 0; |
2166 for ( ; mp != NULL; | 2143 for ( ; mp != NULL; |
2167 #ifdef FEAT_LOCALMAP | 2144 mp->m_next == NULL ? (mp = mp2, mp2 = NULL) |
2168 mp->m_next == NULL ? (mp = mp2, mp2 = NULL) : | 2145 : (mp = mp->m_next)) |
2169 #endif | |
2170 (mp = mp->m_next)) | |
2171 { | 2146 { |
2172 /* | 2147 /* |
2173 * Only consider an entry if the first character | 2148 * Only consider an entry if the first character |
2174 * matches and it is for the current state. | 2149 * matches and it is for the current state. |
2175 * Skip ":lmap" mappings if keys were mapped. | 2150 * Skip ":lmap" mappings if keys were mapped. |
3192 || typebuf_was_filled | 3167 || typebuf_was_filled |
3193 # endif | 3168 # endif |
3194 ); | 3169 ); |
3195 } | 3170 } |
3196 #endif | 3171 #endif |
3197 | |
3198 /* | |
3199 * map[!] : show all key mappings | |
3200 * map[!] {lhs} : show key mapping for {lhs} | |
3201 * map[!] {lhs} {rhs} : set key mapping for {lhs} to {rhs} | |
3202 * noremap[!] {lhs} {rhs} : same, but no remapping for {rhs} | |
3203 * unmap[!] {lhs} : remove key mapping for {lhs} | |
3204 * abbr : show all abbreviations | |
3205 * abbr {lhs} : show abbreviations for {lhs} | |
3206 * abbr {lhs} {rhs} : set abbreviation for {lhs} to {rhs} | |
3207 * noreabbr {lhs} {rhs} : same, but no remapping for {rhs} | |
3208 * unabbr {lhs} : remove abbreviation for {lhs} | |
3209 * | |
3210 * maptype: 0 for :map, 1 for :unmap, 2 for noremap. | |
3211 * | |
3212 * arg is pointer to any arguments. Note: arg cannot be a read-only string, | |
3213 * it will be modified. | |
3214 * | |
3215 * for :map mode is NORMAL + VISUAL + SELECTMODE + OP_PENDING | |
3216 * for :map! mode is INSERT + CMDLINE | |
3217 * for :cmap mode is CMDLINE | |
3218 * for :imap mode is INSERT | |
3219 * for :lmap mode is LANGMAP | |
3220 * for :nmap mode is NORMAL | |
3221 * for :vmap mode is VISUAL + SELECTMODE | |
3222 * for :xmap mode is VISUAL | |
3223 * for :smap mode is SELECTMODE | |
3224 * for :omap mode is OP_PENDING | |
3225 * for :tmap mode is TERMINAL | |
3226 * | |
3227 * for :abbr mode is INSERT + CMDLINE | |
3228 * for :iabbr mode is INSERT | |
3229 * for :cabbr mode is CMDLINE | |
3230 * | |
3231 * Return 0 for success | |
3232 * 1 for invalid arguments | |
3233 * 2 for no match | |
3234 * 4 for out of mem | |
3235 * 5 for entry not unique | |
3236 */ | |
3237 int | |
3238 do_map( | |
3239 int maptype, | |
3240 char_u *arg, | |
3241 int mode, | |
3242 int abbrev) /* not a mapping but an abbreviation */ | |
3243 { | |
3244 char_u *keys; | |
3245 mapblock_T *mp, **mpp; | |
3246 char_u *rhs; | |
3247 char_u *p; | |
3248 int n; | |
3249 int len = 0; /* init for GCC */ | |
3250 char_u *newstr; | |
3251 int hasarg; | |
3252 int haskey; | |
3253 int did_it = FALSE; | |
3254 #ifdef FEAT_LOCALMAP | |
3255 int did_local = FALSE; | |
3256 #endif | |
3257 int round; | |
3258 char_u *keys_buf = NULL; | |
3259 char_u *arg_buf = NULL; | |
3260 int retval = 0; | |
3261 int do_backslash; | |
3262 int hash; | |
3263 int new_hash; | |
3264 mapblock_T **abbr_table; | |
3265 mapblock_T **map_table; | |
3266 int unique = FALSE; | |
3267 int nowait = FALSE; | |
3268 int silent = FALSE; | |
3269 int special = FALSE; | |
3270 #ifdef FEAT_EVAL | |
3271 int expr = FALSE; | |
3272 #endif | |
3273 int noremap; | |
3274 char_u *orig_rhs; | |
3275 | |
3276 keys = arg; | |
3277 map_table = maphash; | |
3278 abbr_table = &first_abbr; | |
3279 | |
3280 /* For ":noremap" don't remap, otherwise do remap. */ | |
3281 if (maptype == 2) | |
3282 noremap = REMAP_NONE; | |
3283 else | |
3284 noremap = REMAP_YES; | |
3285 | |
3286 /* Accept <buffer>, <nowait>, <silent>, <expr> <script> and <unique> in | |
3287 * any order. */ | |
3288 for (;;) | |
3289 { | |
3290 #ifdef FEAT_LOCALMAP | |
3291 /* | |
3292 * Check for "<buffer>": mapping local to buffer. | |
3293 */ | |
3294 if (STRNCMP(keys, "<buffer>", 8) == 0) | |
3295 { | |
3296 keys = skipwhite(keys + 8); | |
3297 map_table = curbuf->b_maphash; | |
3298 abbr_table = &curbuf->b_first_abbr; | |
3299 continue; | |
3300 } | |
3301 #endif | |
3302 | |
3303 /* | |
3304 * Check for "<nowait>": don't wait for more characters. | |
3305 */ | |
3306 if (STRNCMP(keys, "<nowait>", 8) == 0) | |
3307 { | |
3308 keys = skipwhite(keys + 8); | |
3309 nowait = TRUE; | |
3310 continue; | |
3311 } | |
3312 | |
3313 /* | |
3314 * Check for "<silent>": don't echo commands. | |
3315 */ | |
3316 if (STRNCMP(keys, "<silent>", 8) == 0) | |
3317 { | |
3318 keys = skipwhite(keys + 8); | |
3319 silent = TRUE; | |
3320 continue; | |
3321 } | |
3322 | |
3323 /* | |
3324 * Check for "<special>": accept special keys in <> | |
3325 */ | |
3326 if (STRNCMP(keys, "<special>", 9) == 0) | |
3327 { | |
3328 keys = skipwhite(keys + 9); | |
3329 special = TRUE; | |
3330 continue; | |
3331 } | |
3332 | |
3333 #ifdef FEAT_EVAL | |
3334 /* | |
3335 * Check for "<script>": remap script-local mappings only | |
3336 */ | |
3337 if (STRNCMP(keys, "<script>", 8) == 0) | |
3338 { | |
3339 keys = skipwhite(keys + 8); | |
3340 noremap = REMAP_SCRIPT; | |
3341 continue; | |
3342 } | |
3343 | |
3344 /* | |
3345 * Check for "<expr>": {rhs} is an expression. | |
3346 */ | |
3347 if (STRNCMP(keys, "<expr>", 6) == 0) | |
3348 { | |
3349 keys = skipwhite(keys + 6); | |
3350 expr = TRUE; | |
3351 continue; | |
3352 } | |
3353 #endif | |
3354 /* | |
3355 * Check for "<unique>": don't overwrite an existing mapping. | |
3356 */ | |
3357 if (STRNCMP(keys, "<unique>", 8) == 0) | |
3358 { | |
3359 keys = skipwhite(keys + 8); | |
3360 unique = TRUE; | |
3361 continue; | |
3362 } | |
3363 break; | |
3364 } | |
3365 | |
3366 validate_maphash(); | |
3367 | |
3368 /* | |
3369 * Find end of keys and skip CTRL-Vs (and backslashes) in it. | |
3370 * Accept backslash like CTRL-V when 'cpoptions' does not contain 'B'. | |
3371 * with :unmap white space is included in the keys, no argument possible. | |
3372 */ | |
3373 p = keys; | |
3374 do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL); | |
3375 while (*p && (maptype == 1 || !VIM_ISWHITE(*p))) | |
3376 { | |
3377 if ((p[0] == Ctrl_V || (do_backslash && p[0] == '\\')) && | |
3378 p[1] != NUL) | |
3379 ++p; /* skip CTRL-V or backslash */ | |
3380 ++p; | |
3381 } | |
3382 if (*p != NUL) | |
3383 *p++ = NUL; | |
3384 | |
3385 p = skipwhite(p); | |
3386 rhs = p; | |
3387 hasarg = (*rhs != NUL); | |
3388 haskey = (*keys != NUL); | |
3389 | |
3390 /* check for :unmap without argument */ | |
3391 if (maptype == 1 && !haskey) | |
3392 { | |
3393 retval = 1; | |
3394 goto theend; | |
3395 } | |
3396 | |
3397 /* | |
3398 * If mapping has been given as ^V<C_UP> say, then replace the term codes | |
3399 * with the appropriate two bytes. If it is a shifted special key, unshift | |
3400 * it too, giving another two bytes. | |
3401 * replace_termcodes() may move the result to allocated memory, which | |
3402 * needs to be freed later (*keys_buf and *arg_buf). | |
3403 * replace_termcodes() also removes CTRL-Vs and sometimes backslashes. | |
3404 */ | |
3405 if (haskey) | |
3406 keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, special); | |
3407 orig_rhs = rhs; | |
3408 if (hasarg) | |
3409 { | |
3410 if (STRICMP(rhs, "<nop>") == 0) /* "<Nop>" means nothing */ | |
3411 rhs = (char_u *)""; | |
3412 else | |
3413 rhs = replace_termcodes(rhs, &arg_buf, FALSE, TRUE, special); | |
3414 } | |
3415 | |
3416 /* | |
3417 * check arguments and translate function keys | |
3418 */ | |
3419 if (haskey) | |
3420 { | |
3421 len = (int)STRLEN(keys); | |
3422 if (len > MAXMAPLEN) /* maximum length of MAXMAPLEN chars */ | |
3423 { | |
3424 retval = 1; | |
3425 goto theend; | |
3426 } | |
3427 | |
3428 if (abbrev && maptype != 1) | |
3429 { | |
3430 /* | |
3431 * If an abbreviation ends in a keyword character, the | |
3432 * rest must be all keyword-char or all non-keyword-char. | |
3433 * Otherwise we won't be able to find the start of it in a | |
3434 * vi-compatible way. | |
3435 */ | |
3436 if (has_mbyte) | |
3437 { | |
3438 int first, last; | |
3439 int same = -1; | |
3440 | |
3441 first = vim_iswordp(keys); | |
3442 last = first; | |
3443 p = keys + (*mb_ptr2len)(keys); | |
3444 n = 1; | |
3445 while (p < keys + len) | |
3446 { | |
3447 ++n; /* nr of (multi-byte) chars */ | |
3448 last = vim_iswordp(p); /* type of last char */ | |
3449 if (same == -1 && last != first) | |
3450 same = n - 1; /* count of same char type */ | |
3451 p += (*mb_ptr2len)(p); | |
3452 } | |
3453 if (last && n > 2 && same >= 0 && same < n - 1) | |
3454 { | |
3455 retval = 1; | |
3456 goto theend; | |
3457 } | |
3458 } | |
3459 else if (vim_iswordc(keys[len - 1])) // ends in keyword char | |
3460 for (n = 0; n < len - 2; ++n) | |
3461 if (vim_iswordc(keys[n]) != vim_iswordc(keys[len - 2])) | |
3462 { | |
3463 retval = 1; | |
3464 goto theend; | |
3465 } | |
3466 /* An abbreviation cannot contain white space. */ | |
3467 for (n = 0; n < len; ++n) | |
3468 if (VIM_ISWHITE(keys[n])) | |
3469 { | |
3470 retval = 1; | |
3471 goto theend; | |
3472 } | |
3473 } | |
3474 } | |
3475 | |
3476 if (haskey && hasarg && abbrev) /* if we will add an abbreviation */ | |
3477 no_abbr = FALSE; /* reset flag that indicates there are | |
3478 no abbreviations */ | |
3479 | |
3480 if (!haskey || (maptype != 1 && !hasarg)) | |
3481 msg_start(); | |
3482 | |
3483 #ifdef FEAT_LOCALMAP | |
3484 /* | |
3485 * Check if a new local mapping wasn't already defined globally. | |
3486 */ | |
3487 if (map_table == curbuf->b_maphash && haskey && hasarg && maptype != 1) | |
3488 { | |
3489 /* need to loop over all global hash lists */ | |
3490 for (hash = 0; hash < 256 && !got_int; ++hash) | |
3491 { | |
3492 if (abbrev) | |
3493 { | |
3494 if (hash != 0) /* there is only one abbreviation list */ | |
3495 break; | |
3496 mp = first_abbr; | |
3497 } | |
3498 else | |
3499 mp = maphash[hash]; | |
3500 for ( ; mp != NULL && !got_int; mp = mp->m_next) | |
3501 { | |
3502 /* check entries with the same mode */ | |
3503 if ((mp->m_mode & mode) != 0 | |
3504 && mp->m_keylen == len | |
3505 && unique | |
3506 && STRNCMP(mp->m_keys, keys, (size_t)len) == 0) | |
3507 { | |
3508 if (abbrev) | |
3509 semsg(_("E224: global abbreviation already exists for %s"), | |
3510 mp->m_keys); | |
3511 else | |
3512 semsg(_("E225: global mapping already exists for %s"), | |
3513 mp->m_keys); | |
3514 retval = 5; | |
3515 goto theend; | |
3516 } | |
3517 } | |
3518 } | |
3519 } | |
3520 | |
3521 /* | |
3522 * When listing global mappings, also list buffer-local ones here. | |
3523 */ | |
3524 if (map_table != curbuf->b_maphash && !hasarg && maptype != 1) | |
3525 { | |
3526 /* need to loop over all global hash lists */ | |
3527 for (hash = 0; hash < 256 && !got_int; ++hash) | |
3528 { | |
3529 if (abbrev) | |
3530 { | |
3531 if (hash != 0) /* there is only one abbreviation list */ | |
3532 break; | |
3533 mp = curbuf->b_first_abbr; | |
3534 } | |
3535 else | |
3536 mp = curbuf->b_maphash[hash]; | |
3537 for ( ; mp != NULL && !got_int; mp = mp->m_next) | |
3538 { | |
3539 /* check entries with the same mode */ | |
3540 if ((mp->m_mode & mode) != 0) | |
3541 { | |
3542 if (!haskey) /* show all entries */ | |
3543 { | |
3544 showmap(mp, TRUE); | |
3545 did_local = TRUE; | |
3546 } | |
3547 else | |
3548 { | |
3549 n = mp->m_keylen; | |
3550 if (STRNCMP(mp->m_keys, keys, | |
3551 (size_t)(n < len ? n : len)) == 0) | |
3552 { | |
3553 showmap(mp, TRUE); | |
3554 did_local = TRUE; | |
3555 } | |
3556 } | |
3557 } | |
3558 } | |
3559 } | |
3560 } | |
3561 #endif | |
3562 | |
3563 /* | |
3564 * Find an entry in the maphash[] list that matches. | |
3565 * For :unmap we may loop two times: once to try to unmap an entry with a | |
3566 * matching 'from' part, a second time, if the first fails, to unmap an | |
3567 * entry with a matching 'to' part. This was done to allow ":ab foo bar" | |
3568 * to be unmapped by typing ":unab foo", where "foo" will be replaced by | |
3569 * "bar" because of the abbreviation. | |
3570 */ | |
3571 for (round = 0; (round == 0 || maptype == 1) && round <= 1 | |
3572 && !did_it && !got_int; ++round) | |
3573 { | |
3574 /* need to loop over all hash lists */ | |
3575 for (hash = 0; hash < 256 && !got_int; ++hash) | |
3576 { | |
3577 if (abbrev) | |
3578 { | |
3579 if (hash > 0) /* there is only one abbreviation list */ | |
3580 break; | |
3581 mpp = abbr_table; | |
3582 } | |
3583 else | |
3584 mpp = &(map_table[hash]); | |
3585 for (mp = *mpp; mp != NULL && !got_int; mp = *mpp) | |
3586 { | |
3587 | |
3588 if (!(mp->m_mode & mode)) /* skip entries with wrong mode */ | |
3589 { | |
3590 mpp = &(mp->m_next); | |
3591 continue; | |
3592 } | |
3593 if (!haskey) /* show all entries */ | |
3594 { | |
3595 showmap(mp, map_table != maphash); | |
3596 did_it = TRUE; | |
3597 } | |
3598 else /* do we have a match? */ | |
3599 { | |
3600 if (round) /* second round: Try unmap "rhs" string */ | |
3601 { | |
3602 n = (int)STRLEN(mp->m_str); | |
3603 p = mp->m_str; | |
3604 } | |
3605 else | |
3606 { | |
3607 n = mp->m_keylen; | |
3608 p = mp->m_keys; | |
3609 } | |
3610 if (STRNCMP(p, keys, (size_t)(n < len ? n : len)) == 0) | |
3611 { | |
3612 if (maptype == 1) /* delete entry */ | |
3613 { | |
3614 /* Only accept a full match. For abbreviations we | |
3615 * ignore trailing space when matching with the | |
3616 * "lhs", since an abbreviation can't have | |
3617 * trailing space. */ | |
3618 if (n != len && (!abbrev || round || n > len | |
3619 || *skipwhite(keys + n) != NUL)) | |
3620 { | |
3621 mpp = &(mp->m_next); | |
3622 continue; | |
3623 } | |
3624 /* | |
3625 * We reset the indicated mode bits. If nothing is | |
3626 * left the entry is deleted below. | |
3627 */ | |
3628 mp->m_mode &= ~mode; | |
3629 did_it = TRUE; /* remember we did something */ | |
3630 } | |
3631 else if (!hasarg) /* show matching entry */ | |
3632 { | |
3633 showmap(mp, map_table != maphash); | |
3634 did_it = TRUE; | |
3635 } | |
3636 else if (n != len) /* new entry is ambiguous */ | |
3637 { | |
3638 mpp = &(mp->m_next); | |
3639 continue; | |
3640 } | |
3641 else if (unique) | |
3642 { | |
3643 if (abbrev) | |
3644 semsg(_("E226: abbreviation already exists for %s"), | |
3645 p); | |
3646 else | |
3647 semsg(_("E227: mapping already exists for %s"), p); | |
3648 retval = 5; | |
3649 goto theend; | |
3650 } | |
3651 else /* new rhs for existing entry */ | |
3652 { | |
3653 mp->m_mode &= ~mode; /* remove mode bits */ | |
3654 if (mp->m_mode == 0 && !did_it) /* reuse entry */ | |
3655 { | |
3656 newstr = vim_strsave(rhs); | |
3657 if (newstr == NULL) | |
3658 { | |
3659 retval = 4; /* no mem */ | |
3660 goto theend; | |
3661 } | |
3662 vim_free(mp->m_str); | |
3663 mp->m_str = newstr; | |
3664 vim_free(mp->m_orig_str); | |
3665 mp->m_orig_str = vim_strsave(orig_rhs); | |
3666 mp->m_noremap = noremap; | |
3667 mp->m_nowait = nowait; | |
3668 mp->m_silent = silent; | |
3669 mp->m_mode = mode; | |
3670 #ifdef FEAT_EVAL | |
3671 mp->m_expr = expr; | |
3672 mp->m_script_ctx = current_sctx; | |
3673 mp->m_script_ctx.sc_lnum += sourcing_lnum; | |
3674 #endif | |
3675 did_it = TRUE; | |
3676 } | |
3677 } | |
3678 if (mp->m_mode == 0) /* entry can be deleted */ | |
3679 { | |
3680 map_free(mpp); | |
3681 continue; /* continue with *mpp */ | |
3682 } | |
3683 | |
3684 /* | |
3685 * May need to put this entry into another hash list. | |
3686 */ | |
3687 new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]); | |
3688 if (!abbrev && new_hash != hash) | |
3689 { | |
3690 *mpp = mp->m_next; | |
3691 mp->m_next = map_table[new_hash]; | |
3692 map_table[new_hash] = mp; | |
3693 | |
3694 continue; /* continue with *mpp */ | |
3695 } | |
3696 } | |
3697 } | |
3698 mpp = &(mp->m_next); | |
3699 } | |
3700 } | |
3701 } | |
3702 | |
3703 if (maptype == 1) /* delete entry */ | |
3704 { | |
3705 if (!did_it) | |
3706 retval = 2; /* no match */ | |
3707 else if (*keys == Ctrl_C) | |
3708 { | |
3709 /* If CTRL-C has been unmapped, reuse it for Interrupting. */ | |
3710 #ifdef FEAT_LOCALMAP | |
3711 if (map_table == curbuf->b_maphash) | |
3712 curbuf->b_mapped_ctrl_c &= ~mode; | |
3713 else | |
3714 #endif | |
3715 mapped_ctrl_c &= ~mode; | |
3716 } | |
3717 goto theend; | |
3718 } | |
3719 | |
3720 if (!haskey || !hasarg) /* print entries */ | |
3721 { | |
3722 if (!did_it | |
3723 #ifdef FEAT_LOCALMAP | |
3724 && !did_local | |
3725 #endif | |
3726 ) | |
3727 { | |
3728 if (abbrev) | |
3729 msg(_("No abbreviation found")); | |
3730 else | |
3731 msg(_("No mapping found")); | |
3732 } | |
3733 goto theend; /* listing finished */ | |
3734 } | |
3735 | |
3736 if (did_it) /* have added the new entry already */ | |
3737 goto theend; | |
3738 | |
3739 /* | |
3740 * Get here when adding a new entry to the maphash[] list or abbrlist. | |
3741 */ | |
3742 mp = ALLOC_ONE(mapblock_T); | |
3743 if (mp == NULL) | |
3744 { | |
3745 retval = 4; /* no mem */ | |
3746 goto theend; | |
3747 } | |
3748 | |
3749 /* If CTRL-C has been mapped, don't always use it for Interrupting. */ | |
3750 if (*keys == Ctrl_C) | |
3751 { | |
3752 #ifdef FEAT_LOCALMAP | |
3753 if (map_table == curbuf->b_maphash) | |
3754 curbuf->b_mapped_ctrl_c |= mode; | |
3755 else | |
3756 #endif | |
3757 mapped_ctrl_c |= mode; | |
3758 } | |
3759 | |
3760 mp->m_keys = vim_strsave(keys); | |
3761 mp->m_str = vim_strsave(rhs); | |
3762 mp->m_orig_str = vim_strsave(orig_rhs); | |
3763 if (mp->m_keys == NULL || mp->m_str == NULL) | |
3764 { | |
3765 vim_free(mp->m_keys); | |
3766 vim_free(mp->m_str); | |
3767 vim_free(mp->m_orig_str); | |
3768 vim_free(mp); | |
3769 retval = 4; /* no mem */ | |
3770 goto theend; | |
3771 } | |
3772 mp->m_keylen = (int)STRLEN(mp->m_keys); | |
3773 mp->m_noremap = noremap; | |
3774 mp->m_nowait = nowait; | |
3775 mp->m_silent = silent; | |
3776 mp->m_mode = mode; | |
3777 #ifdef FEAT_EVAL | |
3778 mp->m_expr = expr; | |
3779 mp->m_script_ctx = current_sctx; | |
3780 mp->m_script_ctx.sc_lnum += sourcing_lnum; | |
3781 #endif | |
3782 | |
3783 /* add the new entry in front of the abbrlist or maphash[] list */ | |
3784 if (abbrev) | |
3785 { | |
3786 mp->m_next = *abbr_table; | |
3787 *abbr_table = mp; | |
3788 } | |
3789 else | |
3790 { | |
3791 n = MAP_HASH(mp->m_mode, mp->m_keys[0]); | |
3792 mp->m_next = map_table[n]; | |
3793 map_table[n] = mp; | |
3794 } | |
3795 | |
3796 theend: | |
3797 vim_free(keys_buf); | |
3798 vim_free(arg_buf); | |
3799 return retval; | |
3800 } | |
3801 | |
3802 /* | |
3803 * Delete one entry from the abbrlist or maphash[]. | |
3804 * "mpp" is a pointer to the m_next field of the PREVIOUS entry! | |
3805 */ | |
3806 static void | |
3807 map_free(mapblock_T **mpp) | |
3808 { | |
3809 mapblock_T *mp; | |
3810 | |
3811 mp = *mpp; | |
3812 vim_free(mp->m_keys); | |
3813 vim_free(mp->m_str); | |
3814 vim_free(mp->m_orig_str); | |
3815 *mpp = mp->m_next; | |
3816 vim_free(mp); | |
3817 } | |
3818 | |
3819 /* | |
3820 * Initialize maphash[] for first use. | |
3821 */ | |
3822 static void | |
3823 validate_maphash(void) | |
3824 { | |
3825 if (!maphash_valid) | |
3826 { | |
3827 vim_memset(maphash, 0, sizeof(maphash)); | |
3828 maphash_valid = TRUE; | |
3829 } | |
3830 } | |
3831 | |
3832 /* | |
3833 * Get the mapping mode from the command name. | |
3834 */ | |
3835 int | |
3836 get_map_mode(char_u **cmdp, int forceit) | |
3837 { | |
3838 char_u *p; | |
3839 int modec; | |
3840 int mode; | |
3841 | |
3842 p = *cmdp; | |
3843 modec = *p++; | |
3844 if (modec == 'i') | |
3845 mode = INSERT; /* :imap */ | |
3846 else if (modec == 'l') | |
3847 mode = LANGMAP; /* :lmap */ | |
3848 else if (modec == 'c') | |
3849 mode = CMDLINE; /* :cmap */ | |
3850 else if (modec == 'n' && *p != 'o') /* avoid :noremap */ | |
3851 mode = NORMAL; /* :nmap */ | |
3852 else if (modec == 'v') | |
3853 mode = VISUAL + SELECTMODE; /* :vmap */ | |
3854 else if (modec == 'x') | |
3855 mode = VISUAL; /* :xmap */ | |
3856 else if (modec == 's') | |
3857 mode = SELECTMODE; /* :smap */ | |
3858 else if (modec == 'o') | |
3859 mode = OP_PENDING; /* :omap */ | |
3860 else if (modec == 't') | |
3861 mode = TERMINAL; /* :tmap */ | |
3862 else | |
3863 { | |
3864 --p; | |
3865 if (forceit) | |
3866 mode = INSERT + CMDLINE; /* :map ! */ | |
3867 else | |
3868 mode = VISUAL + SELECTMODE + NORMAL + OP_PENDING;/* :map */ | |
3869 } | |
3870 | |
3871 *cmdp = p; | |
3872 return mode; | |
3873 } | |
3874 | |
3875 /* | |
3876 * Clear all mappings or abbreviations. | |
3877 * 'abbr' should be FALSE for mappings, TRUE for abbreviations. | |
3878 */ | |
3879 void | |
3880 map_clear( | |
3881 char_u *cmdp, | |
3882 char_u *arg UNUSED, | |
3883 int forceit, | |
3884 int abbr) | |
3885 { | |
3886 int mode; | |
3887 #ifdef FEAT_LOCALMAP | |
3888 int local; | |
3889 | |
3890 local = (STRCMP(arg, "<buffer>") == 0); | |
3891 if (!local && *arg != NUL) | |
3892 { | |
3893 emsg(_(e_invarg)); | |
3894 return; | |
3895 } | |
3896 #endif | |
3897 | |
3898 mode = get_map_mode(&cmdp, forceit); | |
3899 map_clear_int(curbuf, mode, | |
3900 #ifdef FEAT_LOCALMAP | |
3901 local, | |
3902 #else | |
3903 FALSE, | |
3904 #endif | |
3905 abbr); | |
3906 } | |
3907 | |
3908 /* | |
3909 * Clear all mappings in "mode". | |
3910 */ | |
3911 void | |
3912 map_clear_int( | |
3913 buf_T *buf UNUSED, /* buffer for local mappings */ | |
3914 int mode, /* mode in which to delete */ | |
3915 int local UNUSED, /* TRUE for buffer-local mappings */ | |
3916 int abbr) /* TRUE for abbreviations */ | |
3917 { | |
3918 mapblock_T *mp, **mpp; | |
3919 int hash; | |
3920 int new_hash; | |
3921 | |
3922 validate_maphash(); | |
3923 | |
3924 for (hash = 0; hash < 256; ++hash) | |
3925 { | |
3926 if (abbr) | |
3927 { | |
3928 if (hash > 0) /* there is only one abbrlist */ | |
3929 break; | |
3930 #ifdef FEAT_LOCALMAP | |
3931 if (local) | |
3932 mpp = &buf->b_first_abbr; | |
3933 else | |
3934 #endif | |
3935 mpp = &first_abbr; | |
3936 } | |
3937 else | |
3938 { | |
3939 #ifdef FEAT_LOCALMAP | |
3940 if (local) | |
3941 mpp = &buf->b_maphash[hash]; | |
3942 else | |
3943 #endif | |
3944 mpp = &maphash[hash]; | |
3945 } | |
3946 while (*mpp != NULL) | |
3947 { | |
3948 mp = *mpp; | |
3949 if (mp->m_mode & mode) | |
3950 { | |
3951 mp->m_mode &= ~mode; | |
3952 if (mp->m_mode == 0) /* entry can be deleted */ | |
3953 { | |
3954 map_free(mpp); | |
3955 continue; | |
3956 } | |
3957 /* | |
3958 * May need to put this entry into another hash list. | |
3959 */ | |
3960 new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]); | |
3961 if (!abbr && new_hash != hash) | |
3962 { | |
3963 *mpp = mp->m_next; | |
3964 #ifdef FEAT_LOCALMAP | |
3965 if (local) | |
3966 { | |
3967 mp->m_next = buf->b_maphash[new_hash]; | |
3968 buf->b_maphash[new_hash] = mp; | |
3969 } | |
3970 else | |
3971 #endif | |
3972 { | |
3973 mp->m_next = maphash[new_hash]; | |
3974 maphash[new_hash] = mp; | |
3975 } | |
3976 continue; /* continue with *mpp */ | |
3977 } | |
3978 } | |
3979 mpp = &(mp->m_next); | |
3980 } | |
3981 } | |
3982 } | |
3983 | |
3984 /* | |
3985 * Return characters to represent the map mode in an allocated string. | |
3986 * Returns NULL when out of memory. | |
3987 */ | |
3988 char_u * | |
3989 map_mode_to_chars(int mode) | |
3990 { | |
3991 garray_T mapmode; | |
3992 | |
3993 ga_init2(&mapmode, 1, 7); | |
3994 | |
3995 if ((mode & (INSERT + CMDLINE)) == INSERT + CMDLINE) | |
3996 ga_append(&mapmode, '!'); /* :map! */ | |
3997 else if (mode & INSERT) | |
3998 ga_append(&mapmode, 'i'); /* :imap */ | |
3999 else if (mode & LANGMAP) | |
4000 ga_append(&mapmode, 'l'); /* :lmap */ | |
4001 else if (mode & CMDLINE) | |
4002 ga_append(&mapmode, 'c'); /* :cmap */ | |
4003 else if ((mode & (NORMAL + VISUAL + SELECTMODE + OP_PENDING)) | |
4004 == NORMAL + VISUAL + SELECTMODE + OP_PENDING) | |
4005 ga_append(&mapmode, ' '); /* :map */ | |
4006 else | |
4007 { | |
4008 if (mode & NORMAL) | |
4009 ga_append(&mapmode, 'n'); /* :nmap */ | |
4010 if (mode & OP_PENDING) | |
4011 ga_append(&mapmode, 'o'); /* :omap */ | |
4012 if (mode & TERMINAL) | |
4013 ga_append(&mapmode, 't'); /* :tmap */ | |
4014 if ((mode & (VISUAL + SELECTMODE)) == VISUAL + SELECTMODE) | |
4015 ga_append(&mapmode, 'v'); /* :vmap */ | |
4016 else | |
4017 { | |
4018 if (mode & VISUAL) | |
4019 ga_append(&mapmode, 'x'); /* :xmap */ | |
4020 if (mode & SELECTMODE) | |
4021 ga_append(&mapmode, 's'); /* :smap */ | |
4022 } | |
4023 } | |
4024 | |
4025 ga_append(&mapmode, NUL); | |
4026 return (char_u *)mapmode.ga_data; | |
4027 } | |
4028 | |
4029 static void | |
4030 showmap( | |
4031 mapblock_T *mp, | |
4032 int local) /* TRUE for buffer-local map */ | |
4033 { | |
4034 int len = 1; | |
4035 char_u *mapchars; | |
4036 | |
4037 if (message_filtered(mp->m_keys) && message_filtered(mp->m_str)) | |
4038 return; | |
4039 | |
4040 if (msg_didout || msg_silent != 0) | |
4041 { | |
4042 msg_putchar('\n'); | |
4043 if (got_int) /* 'q' typed at MORE prompt */ | |
4044 return; | |
4045 } | |
4046 | |
4047 mapchars = map_mode_to_chars(mp->m_mode); | |
4048 if (mapchars != NULL) | |
4049 { | |
4050 msg_puts((char *)mapchars); | |
4051 len = (int)STRLEN(mapchars); | |
4052 vim_free(mapchars); | |
4053 } | |
4054 | |
4055 while (++len <= 3) | |
4056 msg_putchar(' '); | |
4057 | |
4058 /* Display the LHS. Get length of what we write. */ | |
4059 len = msg_outtrans_special(mp->m_keys, TRUE, 0); | |
4060 do | |
4061 { | |
4062 msg_putchar(' '); /* padd with blanks */ | |
4063 ++len; | |
4064 } while (len < 12); | |
4065 | |
4066 if (mp->m_noremap == REMAP_NONE) | |
4067 msg_puts_attr("*", HL_ATTR(HLF_8)); | |
4068 else if (mp->m_noremap == REMAP_SCRIPT) | |
4069 msg_puts_attr("&", HL_ATTR(HLF_8)); | |
4070 else | |
4071 msg_putchar(' '); | |
4072 | |
4073 if (local) | |
4074 msg_putchar('@'); | |
4075 else | |
4076 msg_putchar(' '); | |
4077 | |
4078 /* Use FALSE below if we only want things like <Up> to show up as such on | |
4079 * the rhs, and not M-x etc, TRUE gets both -- webb */ | |
4080 if (*mp->m_str == NUL) | |
4081 msg_puts_attr("<Nop>", HL_ATTR(HLF_8)); | |
4082 else | |
4083 { | |
4084 /* Remove escaping of CSI, because "m_str" is in a format to be used | |
4085 * as typeahead. */ | |
4086 char_u *s = vim_strsave(mp->m_str); | |
4087 if (s != NULL) | |
4088 { | |
4089 vim_unescape_csi(s); | |
4090 msg_outtrans_special(s, FALSE, 0); | |
4091 vim_free(s); | |
4092 } | |
4093 } | |
4094 #ifdef FEAT_EVAL | |
4095 if (p_verbose > 0) | |
4096 last_set_msg(mp->m_script_ctx); | |
4097 #endif | |
4098 out_flush(); /* show one line at a time */ | |
4099 } | |
4100 | |
4101 #if defined(FEAT_EVAL) || defined(PROTO) | |
4102 /* | |
4103 * Return TRUE if a map exists that has "str" in the rhs for mode "modechars". | |
4104 * Recognize termcap codes in "str". | |
4105 * Also checks mappings local to the current buffer. | |
4106 */ | |
4107 int | |
4108 map_to_exists(char_u *str, char_u *modechars, int abbr) | |
4109 { | |
4110 int mode = 0; | |
4111 char_u *rhs; | |
4112 char_u *buf; | |
4113 int retval; | |
4114 | |
4115 rhs = replace_termcodes(str, &buf, FALSE, TRUE, FALSE); | |
4116 | |
4117 if (vim_strchr(modechars, 'n') != NULL) | |
4118 mode |= NORMAL; | |
4119 if (vim_strchr(modechars, 'v') != NULL) | |
4120 mode |= VISUAL + SELECTMODE; | |
4121 if (vim_strchr(modechars, 'x') != NULL) | |
4122 mode |= VISUAL; | |
4123 if (vim_strchr(modechars, 's') != NULL) | |
4124 mode |= SELECTMODE; | |
4125 if (vim_strchr(modechars, 'o') != NULL) | |
4126 mode |= OP_PENDING; | |
4127 if (vim_strchr(modechars, 'i') != NULL) | |
4128 mode |= INSERT; | |
4129 if (vim_strchr(modechars, 'l') != NULL) | |
4130 mode |= LANGMAP; | |
4131 if (vim_strchr(modechars, 'c') != NULL) | |
4132 mode |= CMDLINE; | |
4133 | |
4134 retval = map_to_exists_mode(rhs, mode, abbr); | |
4135 vim_free(buf); | |
4136 | |
4137 return retval; | |
4138 } | |
4139 #endif | |
4140 | |
4141 /* | |
4142 * Return TRUE if a map exists that has "str" in the rhs for mode "mode". | |
4143 * Also checks mappings local to the current buffer. | |
4144 */ | |
4145 int | |
4146 map_to_exists_mode(char_u *rhs, int mode, int abbr) | |
4147 { | |
4148 mapblock_T *mp; | |
4149 int hash; | |
4150 # ifdef FEAT_LOCALMAP | |
4151 int exp_buffer = FALSE; | |
4152 | |
4153 validate_maphash(); | |
4154 | |
4155 /* Do it twice: once for global maps and once for local maps. */ | |
4156 for (;;) | |
4157 { | |
4158 # endif | |
4159 for (hash = 0; hash < 256; ++hash) | |
4160 { | |
4161 if (abbr) | |
4162 { | |
4163 if (hash > 0) /* there is only one abbr list */ | |
4164 break; | |
4165 #ifdef FEAT_LOCALMAP | |
4166 if (exp_buffer) | |
4167 mp = curbuf->b_first_abbr; | |
4168 else | |
4169 #endif | |
4170 mp = first_abbr; | |
4171 } | |
4172 # ifdef FEAT_LOCALMAP | |
4173 else if (exp_buffer) | |
4174 mp = curbuf->b_maphash[hash]; | |
4175 # endif | |
4176 else | |
4177 mp = maphash[hash]; | |
4178 for (; mp; mp = mp->m_next) | |
4179 { | |
4180 if ((mp->m_mode & mode) | |
4181 && strstr((char *)mp->m_str, (char *)rhs) != NULL) | |
4182 return TRUE; | |
4183 } | |
4184 } | |
4185 # ifdef FEAT_LOCALMAP | |
4186 if (exp_buffer) | |
4187 break; | |
4188 exp_buffer = TRUE; | |
4189 } | |
4190 # endif | |
4191 | |
4192 return FALSE; | |
4193 } | |
4194 | |
4195 #if defined(FEAT_CMDL_COMPL) || defined(PROTO) | |
4196 /* | |
4197 * Used below when expanding mapping/abbreviation names. | |
4198 */ | |
4199 static int expand_mapmodes = 0; | |
4200 static int expand_isabbrev = 0; | |
4201 #ifdef FEAT_LOCALMAP | |
4202 static int expand_buffer = FALSE; | |
4203 #endif | |
4204 | |
4205 /* | |
4206 * Work out what to complete when doing command line completion of mapping | |
4207 * or abbreviation names. | |
4208 */ | |
4209 char_u * | |
4210 set_context_in_map_cmd( | |
4211 expand_T *xp, | |
4212 char_u *cmd, | |
4213 char_u *arg, | |
4214 int forceit, /* TRUE if '!' given */ | |
4215 int isabbrev, /* TRUE if abbreviation */ | |
4216 int isunmap, /* TRUE if unmap/unabbrev command */ | |
4217 cmdidx_T cmdidx) | |
4218 { | |
4219 if (forceit && cmdidx != CMD_map && cmdidx != CMD_unmap) | |
4220 xp->xp_context = EXPAND_NOTHING; | |
4221 else | |
4222 { | |
4223 if (isunmap) | |
4224 expand_mapmodes = get_map_mode(&cmd, forceit || isabbrev); | |
4225 else | |
4226 { | |
4227 expand_mapmodes = INSERT + CMDLINE; | |
4228 if (!isabbrev) | |
4229 expand_mapmodes += VISUAL + SELECTMODE + NORMAL + OP_PENDING; | |
4230 } | |
4231 expand_isabbrev = isabbrev; | |
4232 xp->xp_context = EXPAND_MAPPINGS; | |
4233 #ifdef FEAT_LOCALMAP | |
4234 expand_buffer = FALSE; | |
4235 #endif | |
4236 for (;;) | |
4237 { | |
4238 #ifdef FEAT_LOCALMAP | |
4239 if (STRNCMP(arg, "<buffer>", 8) == 0) | |
4240 { | |
4241 expand_buffer = TRUE; | |
4242 arg = skipwhite(arg + 8); | |
4243 continue; | |
4244 } | |
4245 #endif | |
4246 if (STRNCMP(arg, "<unique>", 8) == 0) | |
4247 { | |
4248 arg = skipwhite(arg + 8); | |
4249 continue; | |
4250 } | |
4251 if (STRNCMP(arg, "<nowait>", 8) == 0) | |
4252 { | |
4253 arg = skipwhite(arg + 8); | |
4254 continue; | |
4255 } | |
4256 if (STRNCMP(arg, "<silent>", 8) == 0) | |
4257 { | |
4258 arg = skipwhite(arg + 8); | |
4259 continue; | |
4260 } | |
4261 if (STRNCMP(arg, "<special>", 9) == 0) | |
4262 { | |
4263 arg = skipwhite(arg + 9); | |
4264 continue; | |
4265 } | |
4266 #ifdef FEAT_EVAL | |
4267 if (STRNCMP(arg, "<script>", 8) == 0) | |
4268 { | |
4269 arg = skipwhite(arg + 8); | |
4270 continue; | |
4271 } | |
4272 if (STRNCMP(arg, "<expr>", 6) == 0) | |
4273 { | |
4274 arg = skipwhite(arg + 6); | |
4275 continue; | |
4276 } | |
4277 #endif | |
4278 break; | |
4279 } | |
4280 xp->xp_pattern = arg; | |
4281 } | |
4282 | |
4283 return NULL; | |
4284 } | |
4285 | |
4286 /* | |
4287 * Find all mapping/abbreviation names that match regexp "regmatch"'. | |
4288 * For command line expansion of ":[un]map" and ":[un]abbrev" in all modes. | |
4289 * Return OK if matches found, FAIL otherwise. | |
4290 */ | |
4291 int | |
4292 ExpandMappings( | |
4293 regmatch_T *regmatch, | |
4294 int *num_file, | |
4295 char_u ***file) | |
4296 { | |
4297 mapblock_T *mp; | |
4298 int hash; | |
4299 int count; | |
4300 int round; | |
4301 char_u *p; | |
4302 int i; | |
4303 | |
4304 validate_maphash(); | |
4305 | |
4306 *num_file = 0; /* return values in case of FAIL */ | |
4307 *file = NULL; | |
4308 | |
4309 /* | |
4310 * round == 1: Count the matches. | |
4311 * round == 2: Build the array to keep the matches. | |
4312 */ | |
4313 for (round = 1; round <= 2; ++round) | |
4314 { | |
4315 count = 0; | |
4316 | |
4317 for (i = 0; i < 7; ++i) | |
4318 { | |
4319 if (i == 0) | |
4320 p = (char_u *)"<silent>"; | |
4321 else if (i == 1) | |
4322 p = (char_u *)"<unique>"; | |
4323 #ifdef FEAT_EVAL | |
4324 else if (i == 2) | |
4325 p = (char_u *)"<script>"; | |
4326 else if (i == 3) | |
4327 p = (char_u *)"<expr>"; | |
4328 #endif | |
4329 #ifdef FEAT_LOCALMAP | |
4330 else if (i == 4 && !expand_buffer) | |
4331 p = (char_u *)"<buffer>"; | |
4332 #endif | |
4333 else if (i == 5) | |
4334 p = (char_u *)"<nowait>"; | |
4335 else if (i == 6) | |
4336 p = (char_u *)"<special>"; | |
4337 else | |
4338 continue; | |
4339 | |
4340 if (vim_regexec(regmatch, p, (colnr_T)0)) | |
4341 { | |
4342 if (round == 1) | |
4343 ++count; | |
4344 else | |
4345 (*file)[count++] = vim_strsave(p); | |
4346 } | |
4347 } | |
4348 | |
4349 for (hash = 0; hash < 256; ++hash) | |
4350 { | |
4351 if (expand_isabbrev) | |
4352 { | |
4353 if (hash > 0) /* only one abbrev list */ | |
4354 break; /* for (hash) */ | |
4355 mp = first_abbr; | |
4356 } | |
4357 #ifdef FEAT_LOCALMAP | |
4358 else if (expand_buffer) | |
4359 mp = curbuf->b_maphash[hash]; | |
4360 #endif | |
4361 else | |
4362 mp = maphash[hash]; | |
4363 for (; mp; mp = mp->m_next) | |
4364 { | |
4365 if (mp->m_mode & expand_mapmodes) | |
4366 { | |
4367 p = translate_mapping(mp->m_keys); | |
4368 if (p != NULL && vim_regexec(regmatch, p, (colnr_T)0)) | |
4369 { | |
4370 if (round == 1) | |
4371 ++count; | |
4372 else | |
4373 { | |
4374 (*file)[count++] = p; | |
4375 p = NULL; | |
4376 } | |
4377 } | |
4378 vim_free(p); | |
4379 } | |
4380 } /* for (mp) */ | |
4381 } /* for (hash) */ | |
4382 | |
4383 if (count == 0) /* no match found */ | |
4384 break; /* for (round) */ | |
4385 | |
4386 if (round == 1) | |
4387 { | |
4388 *file = ALLOC_MULT(char_u *, count); | |
4389 if (*file == NULL) | |
4390 return FAIL; | |
4391 } | |
4392 } /* for (round) */ | |
4393 | |
4394 if (count > 1) | |
4395 { | |
4396 char_u **ptr1; | |
4397 char_u **ptr2; | |
4398 char_u **ptr3; | |
4399 | |
4400 /* Sort the matches */ | |
4401 sort_strings(*file, count); | |
4402 | |
4403 /* Remove multiple entries */ | |
4404 ptr1 = *file; | |
4405 ptr2 = ptr1 + 1; | |
4406 ptr3 = ptr1 + count; | |
4407 | |
4408 while (ptr2 < ptr3) | |
4409 { | |
4410 if (STRCMP(*ptr1, *ptr2)) | |
4411 *++ptr1 = *ptr2++; | |
4412 else | |
4413 { | |
4414 vim_free(*ptr2++); | |
4415 count--; | |
4416 } | |
4417 } | |
4418 } | |
4419 | |
4420 *num_file = count; | |
4421 return (count == 0 ? FAIL : OK); | |
4422 } | |
4423 #endif /* FEAT_CMDL_COMPL */ | |
4424 | |
4425 /* | |
4426 * Check for an abbreviation. | |
4427 * Cursor is at ptr[col]. | |
4428 * When inserting, mincol is where insert started. | |
4429 * For the command line, mincol is what is to be skipped over. | |
4430 * "c" is the character typed before check_abbr was called. It may have | |
4431 * ABBR_OFF added to avoid prepending a CTRL-V to it. | |
4432 * | |
4433 * Historic vi practice: The last character of an abbreviation must be an id | |
4434 * character ([a-zA-Z0-9_]). The characters in front of it must be all id | |
4435 * characters or all non-id characters. This allows for abbr. "#i" to | |
4436 * "#include". | |
4437 * | |
4438 * Vim addition: Allow for abbreviations that end in a non-keyword character. | |
4439 * Then there must be white space before the abbr. | |
4440 * | |
4441 * return TRUE if there is an abbreviation, FALSE if not | |
4442 */ | |
4443 int | |
4444 check_abbr( | |
4445 int c, | |
4446 char_u *ptr, | |
4447 int col, | |
4448 int mincol) | |
4449 { | |
4450 int len; | |
4451 int scol; /* starting column of the abbr. */ | |
4452 int j; | |
4453 char_u *s; | |
4454 char_u tb[MB_MAXBYTES + 4]; | |
4455 mapblock_T *mp; | |
4456 #ifdef FEAT_LOCALMAP | |
4457 mapblock_T *mp2; | |
4458 #endif | |
4459 int clen = 0; /* length in characters */ | |
4460 int is_id = TRUE; | |
4461 int vim_abbr; | |
4462 | |
4463 if (typebuf.tb_no_abbr_cnt) /* abbrev. are not recursive */ | |
4464 return FALSE; | |
4465 | |
4466 /* no remapping implies no abbreviation, except for CTRL-] */ | |
4467 if ((KeyNoremap & (RM_NONE|RM_SCRIPT)) != 0 && c != Ctrl_RSB) | |
4468 return FALSE; | |
4469 | |
4470 /* | |
4471 * Check for word before the cursor: If it ends in a keyword char all | |
4472 * chars before it must be keyword chars or non-keyword chars, but not | |
4473 * white space. If it ends in a non-keyword char we accept any characters | |
4474 * before it except white space. | |
4475 */ | |
4476 if (col == 0) /* cannot be an abbr. */ | |
4477 return FALSE; | |
4478 | |
4479 if (has_mbyte) | |
4480 { | |
4481 char_u *p; | |
4482 | |
4483 p = mb_prevptr(ptr, ptr + col); | |
4484 if (!vim_iswordp(p)) | |
4485 vim_abbr = TRUE; /* Vim added abbr. */ | |
4486 else | |
4487 { | |
4488 vim_abbr = FALSE; /* vi compatible abbr. */ | |
4489 if (p > ptr) | |
4490 is_id = vim_iswordp(mb_prevptr(ptr, p)); | |
4491 } | |
4492 clen = 1; | |
4493 while (p > ptr + mincol) | |
4494 { | |
4495 p = mb_prevptr(ptr, p); | |
4496 if (vim_isspace(*p) || (!vim_abbr && is_id != vim_iswordp(p))) | |
4497 { | |
4498 p += (*mb_ptr2len)(p); | |
4499 break; | |
4500 } | |
4501 ++clen; | |
4502 } | |
4503 scol = (int)(p - ptr); | |
4504 } | |
4505 else | |
4506 { | |
4507 if (!vim_iswordc(ptr[col - 1])) | |
4508 vim_abbr = TRUE; /* Vim added abbr. */ | |
4509 else | |
4510 { | |
4511 vim_abbr = FALSE; /* vi compatible abbr. */ | |
4512 if (col > 1) | |
4513 is_id = vim_iswordc(ptr[col - 2]); | |
4514 } | |
4515 for (scol = col - 1; scol > 0 && !vim_isspace(ptr[scol - 1]) | |
4516 && (vim_abbr || is_id == vim_iswordc(ptr[scol - 1])); --scol) | |
4517 ; | |
4518 } | |
4519 | |
4520 if (scol < mincol) | |
4521 scol = mincol; | |
4522 if (scol < col) /* there is a word in front of the cursor */ | |
4523 { | |
4524 ptr += scol; | |
4525 len = col - scol; | |
4526 #ifdef FEAT_LOCALMAP | |
4527 mp = curbuf->b_first_abbr; | |
4528 mp2 = first_abbr; | |
4529 if (mp == NULL) | |
4530 { | |
4531 mp = mp2; | |
4532 mp2 = NULL; | |
4533 } | |
4534 #else | |
4535 mp = first_abbr; | |
4536 #endif | |
4537 for ( ; mp; | |
4538 #ifdef FEAT_LOCALMAP | |
4539 mp->m_next == NULL ? (mp = mp2, mp2 = NULL) : | |
4540 #endif | |
4541 (mp = mp->m_next)) | |
4542 { | |
4543 int qlen = mp->m_keylen; | |
4544 char_u *q = mp->m_keys; | |
4545 int match; | |
4546 | |
4547 if (vim_strbyte(mp->m_keys, K_SPECIAL) != NULL) | |
4548 { | |
4549 char_u *qe = vim_strsave(mp->m_keys); | |
4550 | |
4551 /* might have CSI escaped mp->m_keys */ | |
4552 if (qe != NULL) | |
4553 { | |
4554 q = qe; | |
4555 vim_unescape_csi(q); | |
4556 qlen = (int)STRLEN(q); | |
4557 } | |
4558 } | |
4559 | |
4560 /* find entries with right mode and keys */ | |
4561 match = (mp->m_mode & State) | |
4562 && qlen == len | |
4563 && !STRNCMP(q, ptr, (size_t)len); | |
4564 if (q != mp->m_keys) | |
4565 vim_free(q); | |
4566 if (match) | |
4567 break; | |
4568 } | |
4569 if (mp != NULL) | |
4570 { | |
4571 /* | |
4572 * Found a match: | |
4573 * Insert the rest of the abbreviation in typebuf.tb_buf[]. | |
4574 * This goes from end to start. | |
4575 * | |
4576 * Characters 0x000 - 0x100: normal chars, may need CTRL-V, | |
4577 * except K_SPECIAL: Becomes K_SPECIAL KS_SPECIAL KE_FILLER | |
4578 * Characters where IS_SPECIAL() == TRUE: key codes, need | |
4579 * K_SPECIAL. Other characters (with ABBR_OFF): don't use CTRL-V. | |
4580 * | |
4581 * Character CTRL-] is treated specially - it completes the | |
4582 * abbreviation, but is not inserted into the input stream. | |
4583 */ | |
4584 j = 0; | |
4585 if (c != Ctrl_RSB) | |
4586 { | |
4587 /* special key code, split up */ | |
4588 if (IS_SPECIAL(c) || c == K_SPECIAL) | |
4589 { | |
4590 tb[j++] = K_SPECIAL; | |
4591 tb[j++] = K_SECOND(c); | |
4592 tb[j++] = K_THIRD(c); | |
4593 } | |
4594 else | |
4595 { | |
4596 if (c < ABBR_OFF && (c < ' ' || c > '~')) | |
4597 tb[j++] = Ctrl_V; /* special char needs CTRL-V */ | |
4598 if (has_mbyte) | |
4599 { | |
4600 /* if ABBR_OFF has been added, remove it here */ | |
4601 if (c >= ABBR_OFF) | |
4602 c -= ABBR_OFF; | |
4603 j += (*mb_char2bytes)(c, tb + j); | |
4604 } | |
4605 else | |
4606 tb[j++] = c; | |
4607 } | |
4608 tb[j] = NUL; | |
4609 /* insert the last typed char */ | |
4610 (void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent); | |
4611 } | |
4612 #ifdef FEAT_EVAL | |
4613 if (mp->m_expr) | |
4614 s = eval_map_expr(mp->m_str, c); | |
4615 else | |
4616 #endif | |
4617 s = mp->m_str; | |
4618 if (s != NULL) | |
4619 { | |
4620 /* insert the to string */ | |
4621 (void)ins_typebuf(s, mp->m_noremap, 0, TRUE, mp->m_silent); | |
4622 /* no abbrev. for these chars */ | |
4623 typebuf.tb_no_abbr_cnt += (int)STRLEN(s) + j + 1; | |
4624 #ifdef FEAT_EVAL | |
4625 if (mp->m_expr) | |
4626 vim_free(s); | |
4627 #endif | |
4628 } | |
4629 | |
4630 tb[0] = Ctrl_H; | |
4631 tb[1] = NUL; | |
4632 if (has_mbyte) | |
4633 len = clen; /* Delete characters instead of bytes */ | |
4634 while (len-- > 0) /* delete the from string */ | |
4635 (void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent); | |
4636 return TRUE; | |
4637 } | |
4638 } | |
4639 return FALSE; | |
4640 } | |
4641 | |
4642 #ifdef FEAT_EVAL | |
4643 /* | |
4644 * Evaluate the RHS of a mapping or abbreviations and take care of escaping | |
4645 * special characters. | |
4646 */ | |
4647 static char_u * | |
4648 eval_map_expr( | |
4649 char_u *str, | |
4650 int c) /* NUL or typed character for abbreviation */ | |
4651 { | |
4652 char_u *res; | |
4653 char_u *p; | |
4654 char_u *expr; | |
4655 pos_T save_cursor; | |
4656 int save_msg_col; | |
4657 int save_msg_row; | |
4658 | |
4659 /* Remove escaping of CSI, because "str" is in a format to be used as | |
4660 * typeahead. */ | |
4661 expr = vim_strsave(str); | |
4662 if (expr == NULL) | |
4663 return NULL; | |
4664 vim_unescape_csi(expr); | |
4665 | |
4666 /* Forbid changing text or using ":normal" to avoid most of the bad side | |
4667 * effects. Also restore the cursor position. */ | |
4668 ++textlock; | |
4669 ++ex_normal_lock; | |
4670 set_vim_var_char(c); /* set v:char to the typed character */ | |
4671 save_cursor = curwin->w_cursor; | |
4672 save_msg_col = msg_col; | |
4673 save_msg_row = msg_row; | |
4674 p = eval_to_string(expr, NULL, FALSE); | |
4675 --textlock; | |
4676 --ex_normal_lock; | |
4677 curwin->w_cursor = save_cursor; | |
4678 msg_col = save_msg_col; | |
4679 msg_row = save_msg_row; | |
4680 | |
4681 vim_free(expr); | |
4682 | |
4683 if (p == NULL) | |
4684 return NULL; | |
4685 /* Escape CSI in the result to be able to use the string as typeahead. */ | |
4686 res = vim_strsave_escape_csi(p); | |
4687 vim_free(p); | |
4688 | |
4689 return res; | |
4690 } | |
4691 #endif | |
4692 | |
4693 /* | |
4694 * Copy "p" to allocated memory, escaping K_SPECIAL and CSI so that the result | |
4695 * can be put in the typeahead buffer. | |
4696 * Returns NULL when out of memory. | |
4697 */ | |
4698 char_u * | |
4699 vim_strsave_escape_csi( | |
4700 char_u *p) | |
4701 { | |
4702 char_u *res; | |
4703 char_u *s, *d; | |
4704 | |
4705 /* Need a buffer to hold up to three times as much. Four in case of an | |
4706 * illegal utf-8 byte: | |
4707 * 0xc0 -> 0xc3 0x80 -> 0xc3 K_SPECIAL KS_SPECIAL KE_FILLER */ | |
4708 res = alloc(STRLEN(p) * 4 + 1); | |
4709 if (res != NULL) | |
4710 { | |
4711 d = res; | |
4712 for (s = p; *s != NUL; ) | |
4713 { | |
4714 if (s[0] == K_SPECIAL && s[1] != NUL && s[2] != NUL) | |
4715 { | |
4716 /* Copy special key unmodified. */ | |
4717 *d++ = *s++; | |
4718 *d++ = *s++; | |
4719 *d++ = *s++; | |
4720 } | |
4721 else | |
4722 { | |
4723 /* Add character, possibly multi-byte to destination, escaping | |
4724 * CSI and K_SPECIAL. Be careful, it can be an illegal byte! */ | |
4725 d = add_char2buf(PTR2CHAR(s), d); | |
4726 s += MB_CPTR2LEN(s); | |
4727 } | |
4728 } | |
4729 *d = NUL; | |
4730 } | |
4731 return res; | |
4732 } | |
4733 | |
4734 /* | |
4735 * Remove escaping from CSI and K_SPECIAL characters. Reverse of | |
4736 * vim_strsave_escape_csi(). Works in-place. | |
4737 */ | |
4738 void | |
4739 vim_unescape_csi(char_u *p) | |
4740 { | |
4741 char_u *s = p, *d = p; | |
4742 | |
4743 while (*s != NUL) | |
4744 { | |
4745 if (s[0] == K_SPECIAL && s[1] == KS_SPECIAL && s[2] == KE_FILLER) | |
4746 { | |
4747 *d++ = K_SPECIAL; | |
4748 s += 3; | |
4749 } | |
4750 else if ((s[0] == K_SPECIAL || s[0] == CSI) | |
4751 && s[1] == KS_EXTRA && s[2] == (int)KE_CSI) | |
4752 { | |
4753 *d++ = CSI; | |
4754 s += 3; | |
4755 } | |
4756 else | |
4757 *d++ = *s++; | |
4758 } | |
4759 *d = NUL; | |
4760 } | |
4761 | |
4762 /* | |
4763 * Write map commands for the current mappings to an .exrc file. | |
4764 * Return FAIL on error, OK otherwise. | |
4765 */ | |
4766 int | |
4767 makemap( | |
4768 FILE *fd, | |
4769 buf_T *buf) /* buffer for local mappings or NULL */ | |
4770 { | |
4771 mapblock_T *mp; | |
4772 char_u c1, c2, c3; | |
4773 char_u *p; | |
4774 char *cmd; | |
4775 int abbr; | |
4776 int hash; | |
4777 int did_cpo = FALSE; | |
4778 int i; | |
4779 | |
4780 validate_maphash(); | |
4781 | |
4782 /* | |
4783 * Do the loop twice: Once for mappings, once for abbreviations. | |
4784 * Then loop over all map hash lists. | |
4785 */ | |
4786 for (abbr = 0; abbr < 2; ++abbr) | |
4787 for (hash = 0; hash < 256; ++hash) | |
4788 { | |
4789 if (abbr) | |
4790 { | |
4791 if (hash > 0) /* there is only one abbr list */ | |
4792 break; | |
4793 #ifdef FEAT_LOCALMAP | |
4794 if (buf != NULL) | |
4795 mp = buf->b_first_abbr; | |
4796 else | |
4797 #endif | |
4798 mp = first_abbr; | |
4799 } | |
4800 else | |
4801 { | |
4802 #ifdef FEAT_LOCALMAP | |
4803 if (buf != NULL) | |
4804 mp = buf->b_maphash[hash]; | |
4805 else | |
4806 #endif | |
4807 mp = maphash[hash]; | |
4808 } | |
4809 | |
4810 for ( ; mp; mp = mp->m_next) | |
4811 { | |
4812 /* skip script-local mappings */ | |
4813 if (mp->m_noremap == REMAP_SCRIPT) | |
4814 continue; | |
4815 | |
4816 /* skip mappings that contain a <SNR> (script-local thing), | |
4817 * they probably don't work when loaded again */ | |
4818 for (p = mp->m_str; *p != NUL; ++p) | |
4819 if (p[0] == K_SPECIAL && p[1] == KS_EXTRA | |
4820 && p[2] == (int)KE_SNR) | |
4821 break; | |
4822 if (*p != NUL) | |
4823 continue; | |
4824 | |
4825 /* It's possible to create a mapping and then ":unmap" certain | |
4826 * modes. We recreate this here by mapping the individual | |
4827 * modes, which requires up to three of them. */ | |
4828 c1 = NUL; | |
4829 c2 = NUL; | |
4830 c3 = NUL; | |
4831 if (abbr) | |
4832 cmd = "abbr"; | |
4833 else | |
4834 cmd = "map"; | |
4835 switch (mp->m_mode) | |
4836 { | |
4837 case NORMAL + VISUAL + SELECTMODE + OP_PENDING: | |
4838 break; | |
4839 case NORMAL: | |
4840 c1 = 'n'; | |
4841 break; | |
4842 case VISUAL: | |
4843 c1 = 'x'; | |
4844 break; | |
4845 case SELECTMODE: | |
4846 c1 = 's'; | |
4847 break; | |
4848 case OP_PENDING: | |
4849 c1 = 'o'; | |
4850 break; | |
4851 case NORMAL + VISUAL: | |
4852 c1 = 'n'; | |
4853 c2 = 'x'; | |
4854 break; | |
4855 case NORMAL + SELECTMODE: | |
4856 c1 = 'n'; | |
4857 c2 = 's'; | |
4858 break; | |
4859 case NORMAL + OP_PENDING: | |
4860 c1 = 'n'; | |
4861 c2 = 'o'; | |
4862 break; | |
4863 case VISUAL + SELECTMODE: | |
4864 c1 = 'v'; | |
4865 break; | |
4866 case VISUAL + OP_PENDING: | |
4867 c1 = 'x'; | |
4868 c2 = 'o'; | |
4869 break; | |
4870 case SELECTMODE + OP_PENDING: | |
4871 c1 = 's'; | |
4872 c2 = 'o'; | |
4873 break; | |
4874 case NORMAL + VISUAL + SELECTMODE: | |
4875 c1 = 'n'; | |
4876 c2 = 'v'; | |
4877 break; | |
4878 case NORMAL + VISUAL + OP_PENDING: | |
4879 c1 = 'n'; | |
4880 c2 = 'x'; | |
4881 c3 = 'o'; | |
4882 break; | |
4883 case NORMAL + SELECTMODE + OP_PENDING: | |
4884 c1 = 'n'; | |
4885 c2 = 's'; | |
4886 c3 = 'o'; | |
4887 break; | |
4888 case VISUAL + SELECTMODE + OP_PENDING: | |
4889 c1 = 'v'; | |
4890 c2 = 'o'; | |
4891 break; | |
4892 case CMDLINE + INSERT: | |
4893 if (!abbr) | |
4894 cmd = "map!"; | |
4895 break; | |
4896 case CMDLINE: | |
4897 c1 = 'c'; | |
4898 break; | |
4899 case INSERT: | |
4900 c1 = 'i'; | |
4901 break; | |
4902 case LANGMAP: | |
4903 c1 = 'l'; | |
4904 break; | |
4905 case TERMINAL: | |
4906 c1 = 't'; | |
4907 break; | |
4908 default: | |
4909 iemsg(_("E228: makemap: Illegal mode")); | |
4910 return FAIL; | |
4911 } | |
4912 do /* do this twice if c2 is set, 3 times with c3 */ | |
4913 { | |
4914 /* When outputting <> form, need to make sure that 'cpo' | |
4915 * is set to the Vim default. */ | |
4916 if (!did_cpo) | |
4917 { | |
4918 if (*mp->m_str == NUL) /* will use <Nop> */ | |
4919 did_cpo = TRUE; | |
4920 else | |
4921 for (i = 0; i < 2; ++i) | |
4922 for (p = (i ? mp->m_str : mp->m_keys); *p; ++p) | |
4923 if (*p == K_SPECIAL || *p == NL) | |
4924 did_cpo = TRUE; | |
4925 if (did_cpo) | |
4926 { | |
4927 if (fprintf(fd, "let s:cpo_save=&cpo") < 0 | |
4928 || put_eol(fd) < 0 | |
4929 || fprintf(fd, "set cpo&vim") < 0 | |
4930 || put_eol(fd) < 0) | |
4931 return FAIL; | |
4932 } | |
4933 } | |
4934 if (c1 && putc(c1, fd) < 0) | |
4935 return FAIL; | |
4936 if (mp->m_noremap != REMAP_YES && fprintf(fd, "nore") < 0) | |
4937 return FAIL; | |
4938 if (fputs(cmd, fd) < 0) | |
4939 return FAIL; | |
4940 if (buf != NULL && fputs(" <buffer>", fd) < 0) | |
4941 return FAIL; | |
4942 if (mp->m_nowait && fputs(" <nowait>", fd) < 0) | |
4943 return FAIL; | |
4944 if (mp->m_silent && fputs(" <silent>", fd) < 0) | |
4945 return FAIL; | |
4946 #ifdef FEAT_EVAL | |
4947 if (mp->m_noremap == REMAP_SCRIPT | |
4948 && fputs("<script>", fd) < 0) | |
4949 return FAIL; | |
4950 if (mp->m_expr && fputs(" <expr>", fd) < 0) | |
4951 return FAIL; | |
4952 #endif | |
4953 | |
4954 if ( putc(' ', fd) < 0 | |
4955 || put_escstr(fd, mp->m_keys, 0) == FAIL | |
4956 || putc(' ', fd) < 0 | |
4957 || put_escstr(fd, mp->m_str, 1) == FAIL | |
4958 || put_eol(fd) < 0) | |
4959 return FAIL; | |
4960 c1 = c2; | |
4961 c2 = c3; | |
4962 c3 = NUL; | |
4963 } while (c1 != NUL); | |
4964 } | |
4965 } | |
4966 | |
4967 if (did_cpo) | |
4968 if (fprintf(fd, "let &cpo=s:cpo_save") < 0 | |
4969 || put_eol(fd) < 0 | |
4970 || fprintf(fd, "unlet s:cpo_save") < 0 | |
4971 || put_eol(fd) < 0) | |
4972 return FAIL; | |
4973 return OK; | |
4974 } | |
4975 | |
4976 /* | |
4977 * write escape string to file | |
4978 * "what": 0 for :map lhs, 1 for :map rhs, 2 for :set | |
4979 * | |
4980 * return FAIL for failure, OK otherwise | |
4981 */ | |
4982 int | |
4983 put_escstr(FILE *fd, char_u *strstart, int what) | |
4984 { | |
4985 char_u *str = strstart; | |
4986 int c; | |
4987 int modifiers; | |
4988 | |
4989 /* :map xx <Nop> */ | |
4990 if (*str == NUL && what == 1) | |
4991 { | |
4992 if (fprintf(fd, "<Nop>") < 0) | |
4993 return FAIL; | |
4994 return OK; | |
4995 } | |
4996 | |
4997 for ( ; *str != NUL; ++str) | |
4998 { | |
4999 char_u *p; | |
5000 | |
5001 /* Check for a multi-byte character, which may contain escaped | |
5002 * K_SPECIAL and CSI bytes */ | |
5003 p = mb_unescape(&str); | |
5004 if (p != NULL) | |
5005 { | |
5006 while (*p != NUL) | |
5007 if (fputc(*p++, fd) < 0) | |
5008 return FAIL; | |
5009 --str; | |
5010 continue; | |
5011 } | |
5012 | |
5013 c = *str; | |
5014 /* | |
5015 * Special key codes have to be translated to be able to make sense | |
5016 * when they are read back. | |
5017 */ | |
5018 if (c == K_SPECIAL && what != 2) | |
5019 { | |
5020 modifiers = 0x0; | |
5021 if (str[1] == KS_MODIFIER) | |
5022 { | |
5023 modifiers = str[2]; | |
5024 str += 3; | |
5025 c = *str; | |
5026 } | |
5027 if (c == K_SPECIAL) | |
5028 { | |
5029 c = TO_SPECIAL(str[1], str[2]); | |
5030 str += 2; | |
5031 } | |
5032 if (IS_SPECIAL(c) || modifiers) /* special key */ | |
5033 { | |
5034 if (fputs((char *)get_special_key_name(c, modifiers), fd) < 0) | |
5035 return FAIL; | |
5036 continue; | |
5037 } | |
5038 } | |
5039 | |
5040 /* | |
5041 * A '\n' in a map command should be written as <NL>. | |
5042 * A '\n' in a set command should be written as \^V^J. | |
5043 */ | |
5044 if (c == NL) | |
5045 { | |
5046 if (what == 2) | |
5047 { | |
5048 if (fprintf(fd, IF_EB("\\\026\n", "\\" CTRL_V_STR "\n")) < 0) | |
5049 return FAIL; | |
5050 } | |
5051 else | |
5052 { | |
5053 if (fprintf(fd, "<NL>") < 0) | |
5054 return FAIL; | |
5055 } | |
5056 continue; | |
5057 } | |
5058 | |
5059 /* | |
5060 * Some characters have to be escaped with CTRL-V to | |
5061 * prevent them from misinterpreted in DoOneCmd(). | |
5062 * A space, Tab and '"' has to be escaped with a backslash to | |
5063 * prevent it to be misinterpreted in do_set(). | |
5064 * A space has to be escaped with a CTRL-V when it's at the start of a | |
5065 * ":map" rhs. | |
5066 * A '<' has to be escaped with a CTRL-V to prevent it being | |
5067 * interpreted as the start of a special key name. | |
5068 * A space in the lhs of a :map needs a CTRL-V. | |
5069 */ | |
5070 if (what == 2 && (VIM_ISWHITE(c) || c == '"' || c == '\\')) | |
5071 { | |
5072 if (putc('\\', fd) < 0) | |
5073 return FAIL; | |
5074 } | |
5075 else if (c < ' ' || c > '~' || c == '|' | |
5076 || (what == 0 && c == ' ') | |
5077 || (what == 1 && str == strstart && c == ' ') | |
5078 || (what != 2 && c == '<')) | |
5079 { | |
5080 if (putc(Ctrl_V, fd) < 0) | |
5081 return FAIL; | |
5082 } | |
5083 if (putc(c, fd) < 0) | |
5084 return FAIL; | |
5085 } | |
5086 return OK; | |
5087 } | |
5088 | |
5089 /* | |
5090 * Check all mappings for the presence of special key codes. | |
5091 * Used after ":set term=xxx". | |
5092 */ | |
5093 void | |
5094 check_map_keycodes(void) | |
5095 { | |
5096 mapblock_T *mp; | |
5097 char_u *p; | |
5098 int i; | |
5099 char_u buf[3]; | |
5100 char_u *save_name; | |
5101 int abbr; | |
5102 int hash; | |
5103 #ifdef FEAT_LOCALMAP | |
5104 buf_T *bp; | |
5105 #endif | |
5106 | |
5107 validate_maphash(); | |
5108 save_name = sourcing_name; | |
5109 sourcing_name = (char_u *)"mappings"; /* avoids giving error messages */ | |
5110 | |
5111 #ifdef FEAT_LOCALMAP | |
5112 /* This this once for each buffer, and then once for global | |
5113 * mappings/abbreviations with bp == NULL */ | |
5114 for (bp = firstbuf; ; bp = bp->b_next) | |
5115 { | |
5116 #endif | |
5117 /* | |
5118 * Do the loop twice: Once for mappings, once for abbreviations. | |
5119 * Then loop over all map hash lists. | |
5120 */ | |
5121 for (abbr = 0; abbr <= 1; ++abbr) | |
5122 for (hash = 0; hash < 256; ++hash) | |
5123 { | |
5124 if (abbr) | |
5125 { | |
5126 if (hash) /* there is only one abbr list */ | |
5127 break; | |
5128 #ifdef FEAT_LOCALMAP | |
5129 if (bp != NULL) | |
5130 mp = bp->b_first_abbr; | |
5131 else | |
5132 #endif | |
5133 mp = first_abbr; | |
5134 } | |
5135 else | |
5136 { | |
5137 #ifdef FEAT_LOCALMAP | |
5138 if (bp != NULL) | |
5139 mp = bp->b_maphash[hash]; | |
5140 else | |
5141 #endif | |
5142 mp = maphash[hash]; | |
5143 } | |
5144 for ( ; mp != NULL; mp = mp->m_next) | |
5145 { | |
5146 for (i = 0; i <= 1; ++i) /* do this twice */ | |
5147 { | |
5148 if (i == 0) | |
5149 p = mp->m_keys; /* once for the "from" part */ | |
5150 else | |
5151 p = mp->m_str; /* and once for the "to" part */ | |
5152 while (*p) | |
5153 { | |
5154 if (*p == K_SPECIAL) | |
5155 { | |
5156 ++p; | |
5157 if (*p < 128) /* for "normal" tcap entries */ | |
5158 { | |
5159 buf[0] = p[0]; | |
5160 buf[1] = p[1]; | |
5161 buf[2] = NUL; | |
5162 (void)add_termcap_entry(buf, FALSE); | |
5163 } | |
5164 ++p; | |
5165 } | |
5166 ++p; | |
5167 } | |
5168 } | |
5169 } | |
5170 } | |
5171 #ifdef FEAT_LOCALMAP | |
5172 if (bp == NULL) | |
5173 break; | |
5174 } | |
5175 #endif | |
5176 sourcing_name = save_name; | |
5177 } | |
5178 | |
5179 #if defined(FEAT_EVAL) || defined(PROTO) | |
5180 /* | |
5181 * Check the string "keys" against the lhs of all mappings. | |
5182 * Return pointer to rhs of mapping (mapblock->m_str). | |
5183 * NULL when no mapping found. | |
5184 */ | |
5185 char_u * | |
5186 check_map( | |
5187 char_u *keys, | |
5188 int mode, | |
5189 int exact, /* require exact match */ | |
5190 int ign_mod, /* ignore preceding modifier */ | |
5191 int abbr, /* do abbreviations */ | |
5192 mapblock_T **mp_ptr, /* return: pointer to mapblock or NULL */ | |
5193 int *local_ptr) /* return: buffer-local mapping or NULL */ | |
5194 { | |
5195 int hash; | |
5196 int len, minlen; | |
5197 mapblock_T *mp; | |
5198 char_u *s; | |
5199 #ifdef FEAT_LOCALMAP | |
5200 int local; | |
5201 #endif | |
5202 | |
5203 validate_maphash(); | |
5204 | |
5205 len = (int)STRLEN(keys); | |
5206 #ifdef FEAT_LOCALMAP | |
5207 for (local = 1; local >= 0; --local) | |
5208 #endif | |
5209 /* loop over all hash lists */ | |
5210 for (hash = 0; hash < 256; ++hash) | |
5211 { | |
5212 if (abbr) | |
5213 { | |
5214 if (hash > 0) /* there is only one list. */ | |
5215 break; | |
5216 #ifdef FEAT_LOCALMAP | |
5217 if (local) | |
5218 mp = curbuf->b_first_abbr; | |
5219 else | |
5220 #endif | |
5221 mp = first_abbr; | |
5222 } | |
5223 #ifdef FEAT_LOCALMAP | |
5224 else if (local) | |
5225 mp = curbuf->b_maphash[hash]; | |
5226 #endif | |
5227 else | |
5228 mp = maphash[hash]; | |
5229 for ( ; mp != NULL; mp = mp->m_next) | |
5230 { | |
5231 /* skip entries with wrong mode, wrong length and not matching | |
5232 * ones */ | |
5233 if ((mp->m_mode & mode) && (!exact || mp->m_keylen == len)) | |
5234 { | |
5235 if (len > mp->m_keylen) | |
5236 minlen = mp->m_keylen; | |
5237 else | |
5238 minlen = len; | |
5239 s = mp->m_keys; | |
5240 if (ign_mod && s[0] == K_SPECIAL && s[1] == KS_MODIFIER | |
5241 && s[2] != NUL) | |
5242 { | |
5243 s += 3; | |
5244 if (len > mp->m_keylen - 3) | |
5245 minlen = mp->m_keylen - 3; | |
5246 } | |
5247 if (STRNCMP(s, keys, minlen) == 0) | |
5248 { | |
5249 if (mp_ptr != NULL) | |
5250 *mp_ptr = mp; | |
5251 if (local_ptr != NULL) | |
5252 #ifdef FEAT_LOCALMAP | |
5253 *local_ptr = local; | |
5254 #else | |
5255 *local_ptr = 0; | |
5256 #endif | |
5257 return mp->m_str; | |
5258 } | |
5259 } | |
5260 } | |
5261 } | |
5262 | |
5263 return NULL; | |
5264 } | |
5265 #endif | |
5266 | |
5267 #if defined(MSWIN) || defined(MACOS_X) | |
5268 | |
5269 # define VIS_SEL (VISUAL+SELECTMODE) /* abbreviation */ | |
5270 | |
5271 /* | |
5272 * Default mappings for some often used keys. | |
5273 */ | |
5274 struct initmap | |
5275 { | |
5276 char_u *arg; | |
5277 int mode; | |
5278 }; | |
5279 | |
5280 # ifdef FEAT_GUI_MSWIN | |
5281 /* Use the Windows (CUA) keybindings. (GUI) */ | |
5282 static struct initmap initmappings[] = | |
5283 { | |
5284 /* paste, copy and cut */ | |
5285 {(char_u *)"<S-Insert> \"*P", NORMAL}, | |
5286 {(char_u *)"<S-Insert> \"-d\"*P", VIS_SEL}, | |
5287 {(char_u *)"<S-Insert> <C-R><C-O>*", INSERT+CMDLINE}, | |
5288 {(char_u *)"<C-Insert> \"*y", VIS_SEL}, | |
5289 {(char_u *)"<S-Del> \"*d", VIS_SEL}, | |
5290 {(char_u *)"<C-Del> \"*d", VIS_SEL}, | |
5291 {(char_u *)"<C-X> \"*d", VIS_SEL}, | |
5292 /* Missing: CTRL-C (cancel) and CTRL-V (block selection) */ | |
5293 }; | |
5294 # endif | |
5295 | |
5296 # if defined(MSWIN) && (!defined(FEAT_GUI) || defined(VIMDLL)) | |
5297 /* Use the Windows (CUA) keybindings. (Console) */ | |
5298 static struct initmap cinitmappings[] = | |
5299 { | |
5300 {(char_u *)"\316w <C-Home>", NORMAL+VIS_SEL}, | |
5301 {(char_u *)"\316w <C-Home>", INSERT+CMDLINE}, | |
5302 {(char_u *)"\316u <C-End>", NORMAL+VIS_SEL}, | |
5303 {(char_u *)"\316u <C-End>", INSERT+CMDLINE}, | |
5304 | |
5305 /* paste, copy and cut */ | |
5306 # ifdef FEAT_CLIPBOARD | |
5307 {(char_u *)"\316\324 \"*P", NORMAL}, /* SHIFT-Insert is "*P */ | |
5308 {(char_u *)"\316\324 \"-d\"*P", VIS_SEL}, /* SHIFT-Insert is "-d"*P */ | |
5309 {(char_u *)"\316\324 \022\017*", INSERT}, /* SHIFT-Insert is ^R^O* */ | |
5310 {(char_u *)"\316\325 \"*y", VIS_SEL}, /* CTRL-Insert is "*y */ | |
5311 {(char_u *)"\316\327 \"*d", VIS_SEL}, /* SHIFT-Del is "*d */ | |
5312 {(char_u *)"\316\330 \"*d", VIS_SEL}, /* CTRL-Del is "*d */ | |
5313 {(char_u *)"\030 \"*d", VIS_SEL}, /* CTRL-X is "*d */ | |
5314 # else | |
5315 {(char_u *)"\316\324 P", NORMAL}, /* SHIFT-Insert is P */ | |
5316 {(char_u *)"\316\324 \"-dP", VIS_SEL}, /* SHIFT-Insert is "-dP */ | |
5317 {(char_u *)"\316\324 \022\017\"", INSERT}, /* SHIFT-Insert is ^R^O" */ | |
5318 {(char_u *)"\316\325 y", VIS_SEL}, /* CTRL-Insert is y */ | |
5319 {(char_u *)"\316\327 d", VIS_SEL}, /* SHIFT-Del is d */ | |
5320 {(char_u *)"\316\330 d", VIS_SEL}, /* CTRL-Del is d */ | |
5321 # endif | |
5322 }; | |
5323 # endif | |
5324 | |
5325 # if defined(MACOS_X) | |
5326 static struct initmap initmappings[] = | |
5327 { | |
5328 /* Use the Standard MacOS binding. */ | |
5329 /* paste, copy and cut */ | |
5330 {(char_u *)"<D-v> \"*P", NORMAL}, | |
5331 {(char_u *)"<D-v> \"-d\"*P", VIS_SEL}, | |
5332 {(char_u *)"<D-v> <C-R>*", INSERT+CMDLINE}, | |
5333 {(char_u *)"<D-c> \"*y", VIS_SEL}, | |
5334 {(char_u *)"<D-x> \"*d", VIS_SEL}, | |
5335 {(char_u *)"<Backspace> \"-d", VIS_SEL}, | |
5336 }; | |
5337 # endif | |
5338 | |
5339 # undef VIS_SEL | |
5340 #endif | |
5341 | |
5342 /* | |
5343 * Set up default mappings. | |
5344 */ | |
5345 void | |
5346 init_mappings(void) | |
5347 { | |
5348 #if defined(MSWIN) || defined(MACOS_X) | |
5349 int i; | |
5350 | |
5351 # if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) | |
5352 # ifdef VIMDLL | |
5353 if (!gui.starting) | |
5354 # endif | |
5355 { | |
5356 for (i = 0; | |
5357 i < (int)(sizeof(cinitmappings) / sizeof(struct initmap)); ++i) | |
5358 add_map(cinitmappings[i].arg, cinitmappings[i].mode); | |
5359 } | |
5360 # endif | |
5361 # if defined(FEAT_GUI_MSWIN) || defined(MACOS_X) | |
5362 for (i = 0; i < (int)(sizeof(initmappings) / sizeof(struct initmap)); ++i) | |
5363 add_map(initmappings[i].arg, initmappings[i].mode); | |
5364 # endif | |
5365 #endif | |
5366 } | |
5367 | |
5368 #if defined(MSWIN) || defined(FEAT_CMDWIN) || defined(MACOS_X) \ | |
5369 || defined(PROTO) | |
5370 /* | |
5371 * Add a mapping "map" for mode "mode". | |
5372 * Need to put string in allocated memory, because do_map() will modify it. | |
5373 */ | |
5374 void | |
5375 add_map(char_u *map, int mode) | |
5376 { | |
5377 char_u *s; | |
5378 char_u *cpo_save = p_cpo; | |
5379 | |
5380 p_cpo = (char_u *)""; /* Allow <> notation */ | |
5381 s = vim_strsave(map); | |
5382 if (s != NULL) | |
5383 { | |
5384 (void)do_map(0, s, mode, FALSE); | |
5385 vim_free(s); | |
5386 } | |
5387 p_cpo = cpo_save; | |
5388 } | |
5389 #endif |