diff options
| author | Stefan Kangas | 2021-10-13 00:04:23 +0200 |
|---|---|---|
| committer | Stefan Kangas | 2021-10-28 22:21:16 +0200 |
| commit | 2671ea0de8e90e20241fe0441f4f8b79eeccdb12 (patch) | |
| tree | 8f7706ebb6178963fefd94131b2de5ddfd3a58c9 /src/keymap.c | |
| parent | 64cc31b5c80ab165c4e565ff8943919d832ebd2f (diff) | |
| download | emacs-2671ea0de8e90e20241fe0441f4f8b79eeccdb12.tar.gz emacs-2671ea0de8e90e20241fe0441f4f8b79eeccdb12.zip | |
Be more allowing when looking for menu-bar items
* src/keymap.c (lookup_key_1): Factor out function from
Flookup_key.
(Flookup_key): Be case insensitive, and treat spaces as dashes,
when looking for Qmenu_bar items. (Bug#50752)
* test/src/keymap-tests.el
(keymap-lookup-key/mixed-case)
(keymap-lookup-key/mixed-case-multibyte)
(keymap-lookup-keymap/with-spaces)
(keymap-lookup-keymap/with-spaces-multibyte)
(keymap-lookup-keymap/with-spaces-multibyte-lang-env): New tests.
Diffstat (limited to 'src/keymap.c')
| -rw-r--r-- | src/keymap.c | 161 |
1 files changed, 140 insertions, 21 deletions
diff --git a/src/keymap.c b/src/keymap.c index 8b521a89dfa..2e98b059192 100644 --- a/src/keymap.c +++ b/src/keymap.c | |||
| @@ -65,6 +65,9 @@ static Lisp_Object exclude_keys; | |||
| 65 | /* Pre-allocated 2-element vector for Fcommand_remapping to use. */ | 65 | /* Pre-allocated 2-element vector for Fcommand_remapping to use. */ |
| 66 | static Lisp_Object command_remapping_vector; | 66 | static Lisp_Object command_remapping_vector; |
| 67 | 67 | ||
| 68 | /* Char table for the backwards-compatibility part in Flookup_key. */ | ||
| 69 | static Lisp_Object unicode_case_table; | ||
| 70 | |||
| 68 | /* Hash table used to cache a reverse-map to speed up calls to where-is. */ | 71 | /* Hash table used to cache a reverse-map to speed up calls to where-is. */ |
| 69 | static Lisp_Object where_is_cache; | 72 | static Lisp_Object where_is_cache; |
| 70 | /* Which keymaps are reverse-stored in the cache. */ | 73 | /* Which keymaps are reverse-stored in the cache. */ |
| @@ -1209,27 +1212,8 @@ remapping in all currently active keymaps. */) | |||
| 1209 | return FIXNUMP (command) ? Qnil : command; | 1212 | return FIXNUMP (command) ? Qnil : command; |
| 1210 | } | 1213 | } |
| 1211 | 1214 | ||
| 1212 | /* Value is number if KEY is too long; nil if valid but has no definition. */ | 1215 | static Lisp_Object |
| 1213 | /* GC is possible in this function. */ | 1216 | lookup_key_1 (Lisp_Object keymap, Lisp_Object key, Lisp_Object accept_default) |
| 1214 | |||
| 1215 | DEFUN ("lookup-key", Flookup_key, Slookup_key, 2, 3, 0, | ||
| 1216 | doc: /* Look up key sequence KEY in KEYMAP. Return the definition. | ||
| 1217 | A value of nil means undefined. See doc of `define-key' | ||
| 1218 | for kinds of definitions. | ||
| 1219 | |||
| 1220 | A number as value means KEY is "too long"; | ||
| 1221 | that is, characters or symbols in it except for the last one | ||
| 1222 | fail to be a valid sequence of prefix characters in KEYMAP. | ||
| 1223 | The number is how many characters at the front of KEY | ||
| 1224 | it takes to reach a non-prefix key. | ||
| 1225 | KEYMAP can also be a list of keymaps. | ||
| 1226 | |||
| 1227 | Normally, `lookup-key' ignores bindings for t, which act as default | ||
| 1228 | bindings, used when nothing else in the keymap applies; this makes it | ||
| 1229 | usable as a general function for probing keymaps. However, if the | ||
| 1230 | third optional argument ACCEPT-DEFAULT is non-nil, `lookup-key' will | ||
| 1231 | recognize the default bindings, just as `read-key-sequence' does. */) | ||
| 1232 | (Lisp_Object keymap, Lisp_Object key, Lisp_Object accept_default) | ||
| 1233 | { | 1217 | { |
| 1234 | bool t_ok = !NILP (accept_default); | 1218 | bool t_ok = !NILP (accept_default); |
| 1235 | 1219 | ||
| @@ -1271,6 +1255,141 @@ recognize the default bindings, just as `read-key-sequence' does. */) | |||
| 1271 | } | 1255 | } |
| 1272 | } | 1256 | } |
| 1273 | 1257 | ||
| 1258 | /* Value is number if KEY is too long; nil if valid but has no definition. */ | ||
| 1259 | /* GC is possible in this function. */ | ||
| 1260 | |||
| 1261 | DEFUN ("lookup-key", Flookup_key, Slookup_key, 2, 3, 0, | ||
| 1262 | doc: /* Look up key sequence KEY in KEYMAP. Return the definition. | ||
| 1263 | A value of nil means undefined. See doc of `define-key' | ||
| 1264 | for kinds of definitions. | ||
| 1265 | |||
| 1266 | A number as value means KEY is "too long"; | ||
| 1267 | that is, characters or symbols in it except for the last one | ||
| 1268 | fail to be a valid sequence of prefix characters in KEYMAP. | ||
| 1269 | The number is how many characters at the front of KEY | ||
| 1270 | it takes to reach a non-prefix key. | ||
| 1271 | KEYMAP can also be a list of keymaps. | ||
| 1272 | |||
| 1273 | Normally, `lookup-key' ignores bindings for t, which act as default | ||
| 1274 | bindings, used when nothing else in the keymap applies; this makes it | ||
| 1275 | usable as a general function for probing keymaps. However, if the | ||
| 1276 | third optional argument ACCEPT-DEFAULT is non-nil, `lookup-key' will | ||
| 1277 | recognize the default bindings, just as `read-key-sequence' does. */) | ||
| 1278 | (Lisp_Object keymap, Lisp_Object key, Lisp_Object accept_default) | ||
| 1279 | { | ||
| 1280 | Lisp_Object found = lookup_key_1 (keymap, key, accept_default); | ||
| 1281 | if (!NILP (found) && !NUMBERP (found)) | ||
| 1282 | return found; | ||
| 1283 | |||
| 1284 | /* Menu definitions might use mixed case symbols (notably in old | ||
| 1285 | versions of `easy-menu-define'), or use " " instead of "-". | ||
| 1286 | The rest of this function is about accepting these variations for | ||
| 1287 | backwards-compatibility. (Bug#50752) */ | ||
| 1288 | |||
| 1289 | /* Just skip everything below unless this is a menu item. */ | ||
| 1290 | if (!VECTORP (key) || !(ASIZE (key) > 0) | ||
| 1291 | || !EQ (AREF (key, 0), Qmenu_bar)) | ||
| 1292 | return found; | ||
| 1293 | |||
| 1294 | /* Initialize the unicode case table, if it wasn't already. */ | ||
| 1295 | if (NILP (unicode_case_table)) | ||
| 1296 | { | ||
| 1297 | unicode_case_table = uniprop_table (intern ("lowercase")); | ||
| 1298 | staticpro (&unicode_case_table); | ||
| 1299 | } | ||
| 1300 | |||
| 1301 | ptrdiff_t key_len = ASIZE (key); | ||
| 1302 | Lisp_Object new_key = make_vector (key_len, Qnil); | ||
| 1303 | |||
| 1304 | /* Try both the Unicode case table, and the buffer local one. | ||
| 1305 | Otherwise, we will fail for e.g. the "Turkish" language | ||
| 1306 | environment where 'I' does not downcase to 'i'. */ | ||
| 1307 | Lisp_Object tables[2] = {unicode_case_table, Fcurrent_case_table ()}; | ||
| 1308 | for (int tbl_num = 0; tbl_num < 2; tbl_num++) | ||
| 1309 | { | ||
| 1310 | /* First, let's try converting all symbols like "Foo-Bar-Baz" to | ||
| 1311 | "foo-bar-baz". */ | ||
| 1312 | for (int i = 0; i < key_len; i++) | ||
| 1313 | { | ||
| 1314 | Lisp_Object key_item = Fsymbol_name (AREF (key, i)); | ||
| 1315 | Lisp_Object new_item; | ||
| 1316 | if (!STRING_MULTIBYTE (key_item)) | ||
| 1317 | new_item = Fdowncase (key_item); | ||
| 1318 | else | ||
| 1319 | { | ||
| 1320 | USE_SAFE_ALLOCA; | ||
| 1321 | ptrdiff_t size = SCHARS (key_item), n; | ||
| 1322 | if (INT_MULTIPLY_WRAPV (size, MAX_MULTIBYTE_LENGTH, &n)) | ||
| 1323 | n = PTRDIFF_MAX; | ||
| 1324 | unsigned char *dst = SAFE_ALLOCA (n); | ||
| 1325 | unsigned char *p = dst; | ||
| 1326 | ptrdiff_t j_char = 0, j_byte = 0; | ||
| 1327 | |||
| 1328 | while (j_char < size) | ||
| 1329 | { | ||
| 1330 | int ch = fetch_string_char_advance (key_item, &j_char, &j_byte); | ||
| 1331 | Lisp_Object ch_conv = CHAR_TABLE_REF (tables[tbl_num], ch); | ||
| 1332 | if (!NILP (ch_conv)) | ||
| 1333 | CHAR_STRING (XFIXNUM (ch_conv), p); | ||
| 1334 | else | ||
| 1335 | CHAR_STRING (ch, p); | ||
| 1336 | p = dst + j_byte; | ||
| 1337 | } | ||
| 1338 | new_item = make_multibyte_string ((char *) dst, | ||
| 1339 | SCHARS (key_item), | ||
| 1340 | SBYTES (key_item)); | ||
| 1341 | SAFE_FREE (); | ||
| 1342 | } | ||
| 1343 | ASET (new_key, i, Fintern (new_item, Qnil)); | ||
| 1344 | } | ||
| 1345 | |||
| 1346 | /* Check for match. */ | ||
| 1347 | found = lookup_key_1 (keymap, new_key, accept_default); | ||
| 1348 | if (!NILP (found) && !NUMBERP (found)) | ||
| 1349 | break; | ||
| 1350 | |||
| 1351 | /* If we still don't have a match, let's convert any spaces in | ||
| 1352 | our lowercased string into dashes, e.g. "foo bar baz" to | ||
| 1353 | "foo-bar-baz". */ | ||
| 1354 | for (int i = 0; i < key_len; i++) | ||
| 1355 | { | ||
| 1356 | Lisp_Object lc_key = Fsymbol_name (AREF (new_key, i)); | ||
| 1357 | |||
| 1358 | /* If there are no spaces in this symbol, just skip it. */ | ||
| 1359 | if (!strstr (SSDATA (lc_key), " ")) | ||
| 1360 | continue; | ||
| 1361 | |||
| 1362 | USE_SAFE_ALLOCA; | ||
| 1363 | ptrdiff_t size = SCHARS (lc_key), n; | ||
| 1364 | if (INT_MULTIPLY_WRAPV (size, MAX_MULTIBYTE_LENGTH, &n)) | ||
| 1365 | n = PTRDIFF_MAX; | ||
| 1366 | unsigned char *dst = SAFE_ALLOCA (n); | ||
| 1367 | |||
| 1368 | /* We can walk the string data byte by byte, because UTF-8 | ||
| 1369 | encoding ensures that no other byte of any multibyte | ||
| 1370 | sequence will ever include a 7-bit byte equal to an ASCII | ||
| 1371 | single-byte character. */ | ||
| 1372 | memcpy (dst, SSDATA (lc_key), SBYTES (lc_key)); | ||
| 1373 | for (int i = 0; i < SBYTES (lc_key); ++i) | ||
| 1374 | { | ||
| 1375 | if (dst[i] == ' ') | ||
| 1376 | dst[i] = '-'; | ||
| 1377 | } | ||
| 1378 | Lisp_Object | ||
| 1379 | new_it = make_multibyte_string ((char *) dst, SCHARS (lc_key), SBYTES (lc_key)); | ||
| 1380 | ASET (new_key, i, Fintern (new_it, Qnil)); | ||
| 1381 | SAFE_FREE (); | ||
| 1382 | } | ||
| 1383 | |||
| 1384 | /* Check for match. */ | ||
| 1385 | found = lookup_key_1 (keymap, new_key, accept_default); | ||
| 1386 | if (!NILP (found) && !NUMBERP (found)) | ||
| 1387 | break; | ||
| 1388 | } | ||
| 1389 | |||
| 1390 | return found; | ||
| 1391 | } | ||
| 1392 | |||
| 1274 | /* Make KEYMAP define event C as a keymap (i.e., as a prefix). | 1393 | /* Make KEYMAP define event C as a keymap (i.e., as a prefix). |
| 1275 | Assume that currently it does not define C at all. | 1394 | Assume that currently it does not define C at all. |
| 1276 | Return the keymap. */ | 1395 | Return the keymap. */ |