aboutsummaryrefslogtreecommitdiffstats
path: root/src/keymap.c
diff options
context:
space:
mode:
authorStefan Kangas2021-10-13 00:04:23 +0200
committerStefan Kangas2021-10-28 22:21:16 +0200
commit2671ea0de8e90e20241fe0441f4f8b79eeccdb12 (patch)
tree8f7706ebb6178963fefd94131b2de5ddfd3a58c9 /src/keymap.c
parent64cc31b5c80ab165c4e565ff8943919d832ebd2f (diff)
downloademacs-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.c161
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. */
66static Lisp_Object command_remapping_vector; 66static Lisp_Object command_remapping_vector;
67 67
68/* Char table for the backwards-compatibility part in Flookup_key. */
69static 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. */
69static Lisp_Object where_is_cache; 72static 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. */ 1215static Lisp_Object
1213/* GC is possible in this function. */ 1216lookup_key_1 (Lisp_Object keymap, Lisp_Object key, Lisp_Object accept_default)
1214
1215DEFUN ("lookup-key", Flookup_key, Slookup_key, 2, 3, 0,
1216 doc: /* Look up key sequence KEY in KEYMAP. Return the definition.
1217A value of nil means undefined. See doc of `define-key'
1218for kinds of definitions.
1219
1220A number as value means KEY is "too long";
1221that is, characters or symbols in it except for the last one
1222fail to be a valid sequence of prefix characters in KEYMAP.
1223The number is how many characters at the front of KEY
1224it takes to reach a non-prefix key.
1225KEYMAP can also be a list of keymaps.
1226
1227Normally, `lookup-key' ignores bindings for t, which act as default
1228bindings, used when nothing else in the keymap applies; this makes it
1229usable as a general function for probing keymaps. However, if the
1230third optional argument ACCEPT-DEFAULT is non-nil, `lookup-key' will
1231recognize 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
1261DEFUN ("lookup-key", Flookup_key, Slookup_key, 2, 3, 0,
1262 doc: /* Look up key sequence KEY in KEYMAP. Return the definition.
1263A value of nil means undefined. See doc of `define-key'
1264for kinds of definitions.
1265
1266A number as value means KEY is "too long";
1267that is, characters or symbols in it except for the last one
1268fail to be a valid sequence of prefix characters in KEYMAP.
1269The number is how many characters at the front of KEY
1270it takes to reach a non-prefix key.
1271KEYMAP can also be a list of keymaps.
1272
1273Normally, `lookup-key' ignores bindings for t, which act as default
1274bindings, used when nothing else in the keymap applies; this makes it
1275usable as a general function for probing keymaps. However, if the
1276third optional argument ACCEPT-DEFAULT is non-nil, `lookup-key' will
1277recognize 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. */