Function: where-is-internal
where-is-internal is a function defined in keymap.c.
Signature
(where-is-internal DEFINITION &optional KEYMAP FIRSTONLY NOINDIRECT NO-REMAP)
Documentation
Return list of keys that invoke DEFINITION.
If KEYMAP is a keymap, search only KEYMAP and the global keymap.
If KEYMAP is nil, search all the currently active keymaps, except
for overriding-local-map (which is ignored).
If KEYMAP is a list of keymaps, search only those keymaps.
If optional 3rd arg FIRSTONLY is non-nil, return the first key sequence found,
rather than a list of all possible key sequences.
If FIRSTONLY is the symbol non-ascii, return the first binding found,
no matter what it is.
If FIRSTONLY has another non-nil value, prefer bindings
that use the modifier key specified in where-is-preferred-modifier
(or their meta variants) and entirely reject menu bindings.
If optional 4th arg NOINDIRECT is non-nil, don't extract the commands inside menu-items. This makes it possible to search for a menu-item itself.
The optional 5th arg NO-REMAP alters how command remapping is handled:
- If another command OTHER-COMMAND is remapped to DEFINITION, normally
search for the bindings of OTHER-COMMAND and include them in the
returned list. But if NO-REMAP is non-nil, include the vector
[remap OTHER-COMMAND] in the returned list instead, without
searching for those other bindings.
- If DEFINITION is remapped to OTHER-COMMAND, normally return the
bindings for OTHER-COMMAND. But if NO-REMAP is non-nil, return the
bindings for DEFINITION instead, ignoring its remapping.
Keys that are represented as events that have a non-key-event non-nil
symbol property are ignored.
Probably introduced at or before Emacs version 18.
Source Code
// Defined in /usr/src/emacs/src/keymap.c
// Skipping highlighting due to helpful-max-highlight.
{
/* The keymaps in which to search. */
Lisp_Object keymaps;
/* Potentially relevant bindings in "shortest to longest" order. */
Lisp_Object sequences = Qnil;
/* Actually relevant bindings. */
Lisp_Object found = Qnil;
/* 1 means ignore all menu bindings entirely. */
bool nomenus = !NILP (firstonly) && !EQ (firstonly, Qnon_ascii);
/* List of sequences found via remapping. Keep them in a separate
variable, so as to push them later, since we prefer
non-remapped binding. */
Lisp_Object remapped_sequences = Qnil;
/* Whether or not we're handling remapped sequences. This is needed
because remapping is not done recursively by Fcommand_remapping: you
can't remap a remapped command. */
bool remapped = false;
/* Refresh the C version of the modifier preference. */
where_is_preferred_modifier
= parse_solitary_modifier (Vwhere_is_preferred_modifier);
/* Find the relevant keymaps. */
if (CONSP (keymap) && KEYMAPP (XCAR (keymap)))
keymaps = keymap;
else if (!NILP (keymap))
keymaps = list2 (keymap, current_global_map);
else
keymaps = Fcurrent_active_maps (Qnil, Qnil);
Lisp_Object tem = Fcommand_remapping (definition, Qnil, keymaps);
/* If `definition' is remapped to `tem', then OT1H no key will run
that command (since they will run `tem' instead), so we should
return nil; but OTOH all keys bound to `definition' (or to `tem')
will run the same command.
So for menu-shortcut purposes, we want to find all the keys bound (maybe
via remapping) to `tem'. But for the purpose of finding the keys that
run `definition', then we'd want to just return nil.
We choose to make it work right for menu-shortcuts, since it's the most
common use.
Known bugs: if you remap switch-to-buffer to toto, C-h f switch-to-buffer
will tell you that switch-to-buffer is bound to C-x b even though C-x b
will run toto instead. And if `toto' is itself remapped to forward-char,
then C-h f toto will tell you that it's bound to C-f even though C-f does
not run toto and it won't tell you that C-x b does run toto. */
if (NILP (no_remap) && !NILP (tem))
definition = tem;
if (SYMBOLP (definition)
&& !NILP (firstonly)
&& !NILP (tem = Fget (definition, QCadvertised_binding)))
{
/* We have a list of advertised bindings. */
/* FIXME: Not sure why we use false for shadow_lookup's remapping,
nor why we use `EQ' here but `Fequal' in the call further down. */
while (CONSP (tem))
if (EQ (shadow_lookup (keymaps, XCAR (tem), Qnil, 0), definition))
return XCAR (tem);
else
tem = XCDR (tem);
if (EQ (shadow_lookup (keymaps, tem, Qnil, 0), definition))
return tem;
}
sequences = Freverse (where_is_internal (definition, keymaps,
!NILP (noindirect), nomenus));
while (CONSP (sequences)
/* If we're at the end of the `sequences' list and we haven't
considered remapped sequences yet, copy them over and
process them. */
|| (!remapped && (sequences = remapped_sequences,
remapped = true,
CONSP (sequences))))
{
Lisp_Object sequence, function;
sequence = XCAR (sequences);
sequences = XCDR (sequences);
/* Verify that this key binding is not shadowed by another
binding for the same key, before we say it exists.
Mechanism: look for local definition of this key and if
it is defined and does not match what we found then
ignore this key.
Either nil or number as value from Flookup_key
means undefined. */
if (NILP (Fequal (shadow_lookup (keymaps, sequence, Qnil, remapped),
definition)))
continue;
/* If the current sequence is a command remapping with
format [remap COMMAND], find the key sequences
which run COMMAND, and use those sequences instead. */
if (NILP (no_remap) && !remapped
&& VECTORP (sequence) && ASIZE (sequence) == 2
&& EQ (AREF (sequence, 0), Qremap)
&& (function = AREF (sequence, 1), SYMBOLP (function)))
{
Lisp_Object seqs = where_is_internal (function, keymaps,
!NILP (noindirect), nomenus);
remapped_sequences = nconc2 (Freverse (seqs), remapped_sequences);
continue;
}
/* Don't annoy user with strings from a menu such as the
entries from the "Edit => Paste from Kill Menu".
Change them all to "(any string)", so that there
seems to be only one menu item to report. */
if (! NILP (sequence))
{
Lisp_Object tem1;
tem1 = Faref (sequence, make_fixnum (ASIZE (sequence) - 1));
if (STRINGP (tem1))
Faset (sequence, make_fixnum (ASIZE (sequence) - 1),
build_string ("(any string)"));
}
/* It is a true unshadowed match. Record it, unless it's already
been seen (as could happen when inheriting keymaps). */
if (NILP (Fmember (sequence, found))
/* Filter out non key events. */
&& !(VECTORP (sequence)
&& ASIZE (sequence) == 1
&& SYMBOLP (AREF (sequence, 0))
&& !NILP (Fget (AREF (sequence, 0), Qnon_key_event))))
found = Fcons (sequence, found);
/* If firstonly is Qnon_ascii, then we can return the first
binding we find. If firstonly is not Qnon_ascii but not
nil, then we should return the first ascii-only binding
we find. */
if (EQ (firstonly, Qnon_ascii))
return sequence;
else if (!NILP (firstonly)
&& 2 == preferred_sequence_p (sequence))
return sequence;
}
found = Fnreverse (found);
/* firstonly may have been t, but we may have gone all the way through
the keymaps without finding an all-ASCII key sequence. So just
return the best we could find. */
if (NILP (firstonly))
return found;
else if (where_is_preferred_modifier == 0)
return Fcar (found);
else
{ /* Maybe we did not find a preferred_modifier binding, but we did find
some ASCII binding. */
Lisp_Object bindings = found;
while (CONSP (bindings))
if (preferred_sequence_p (XCAR (bindings)))
return XCAR (bindings);
else
bindings = XCDR (bindings);
return Fcar (found);
}
}