Previous
|
Up
|
Next
|
The Main Loop |
Primitives and EEL Subroutines |
Defining Language Modes |
Epsilon User's Manual and Reference >
Primitives and EEL Subroutines >
Input Primitives >
Binding Primitives
Epsilon lets each buffer have a different set of key bindings
appropriate to editing the type of text in that buffer. For
instance, while in a buffer with EEL source code, a certain key could
indent the current function. The same key might indent a paragraph
in a buffer with text.
A key table stores a set of key
bindings. A key table acts like a very large array, with one entry
for each key on the keyboard. Each entry in the array contains an
index into the name table. (See The Name Table.) If the
value of a particular entry is negative or zero, it means the key is
undefined according to that table.
Since the key codes that index a key table can be very large numbers,
with big gaps between entries, Epsilon doesn't actually store key
tables as arrays, but they act like arrays in EEL. (Internally,
Epsilon stores a key table as a type of sparse array.) The
MAXKEYS macro holds a number bigger than the biggest
possible key code.
#define key_t int
set_range(key_t *table, int i, int value, int cnt)
int get_range(key_t *table, int i, int value)
int find_next_entry(key_t *table, int value, int bound)
EEL code that wants to perform some operation on groups
of keys can't simply use a loop like for (i = 0; i < MAXKEYS;
i++) to do so; that would be too slow. Instead, there are primitives
specifically designed for such purposes.
To set a range of key codes entries to a particular value, use the
set_range( ) primitive. It sets the cnt entries starting
at index i in the key table table to the specified value. The
key_t type represents the type of a key table entry.
To find the length of a run of identical values in a key table array,
use the get_range( ) primitive. It returns the number of
entries, starting at index i , with the specified value. For
instance, if k is a key table, and k[5] , k[6] , and
k[7] equal 123, but k[8] equals 456, then get_range(k, 5,
123) would return 3 , and get_range(k, 8,
123) would return 0 .
The find_next_entry( ) primitive searches for the index of
the next member of the key table array k with the specified value.
It starts looking at position bound in the array, skipping past
index entries less than or equal to bound . It returns -1 if
there are no more entries set to the specified value.
The EEL header file oldkeys.h
provides sample implementations of the above primitives. If the
number of possible keys were much smaller, you could use them instead
of the above primitives. If you write EEL source code that must also
work in older versions of Epsilon, you can include that file. Your
EEL source code will use the definitions in oldkeys.h when you
compile it in old versions of Epsilon; under Epsilon 12 or later, it
will use the above primitives.
buffer short *mode_keys;
short *root_keys;
keytable reg_tab, c_tab;
Epsilon uses two key tables in its search for the binding of a key.
First it looks in the key table referenced by the buffer-specific
variable mode_keys. If the entry for the key is negative,
Epsilon considers the command unbound and signals an error. If the
entry for the key is 0 , as it usually is, Epsilon uses the entry
in the key table referenced by the variable root_keys instead.
If the resulting entry is zero or negative, Epsilon considers the key
unbound. If it finds an entry for the key that is a positive number,
Epsilon considers that number the key's binding. The number is
actually an index into the name table.
Most entries in a key table refer to commands, but an entry may also
refer to a subroutine (if it takes no arguments), to a keyboard
macro, or to another key table. For example, the entry for Ctrl-x in
the default key table refers to a key table named cx_tab ,
which contains the Ctrl-x commands. The entry for the
find-file command bound to Ctrl-x Ctrl-f appears in the
cx_tab key table.
Normally in Epsilon the root_keys variable points to the
reg_tab array. The mode_keys variable points to one
of the many mode-specific tables, such as c_tab for C mode.
int new_table(char *name)
int make_anon_keytable() /* control.e */
short *index_table(int index)
Key tables are usually defined with the keytable keyword as
described in Key Tables. If a key table's name is not
known when the routine is compiled, the new_table( ) primitive
can be used. It makes a new key table with the given name. All
entries in it are 0 .
The make_anon_keytable( ) subroutine defined in control.e
calls new_table( ), first choosing an unused name for the table.
The index_table( ) function takes a name table index and
retrieves the key table it refers to.
fix_key_table(short *ftab, int fval, short *ttab, int tval)
copy_key_table(short *from, short *to)
set_list_keys(short *tab)
The fix_key_table( ) subroutine copies selected key table
information from one key table to another. For each key in ftab
bound to the function fval , the subroutine binds that key in
ttab to the function tval . The copy_key_table( )
subroutine copies an entire key table. The set_list_keys( )
subroutine sets the "n" and "p" keys to move up or down by lines.
do_topkey()
run_topkey()
When Epsilon is ready to execute a key in its main loop, it calls the
primitive do_topkey( ). This primitive
searches the key tables for the command bound to the current key, as
described above. When it has found the name table index, it calls
do_command( ), below, to interpret the command.
The run_topkey( ) subroutine provides a wrapper around
do_topkey( ) that resets iter and similar variables like
the main loop does. An EEL subroutine that wants to retrieve keys
itself and execute them as if the user typed them at command level
can call this subroutine.
do_command(int index)
user short last_index;
The do_command( ) primitive executes the command or other
item with the supplied name table index. If the index is invalid,
then the quick_abort( ) primitive is called. Otherwise, the
index is copied to the last_index variable, so the help system
can find the name of the current command (among other uses).
If the name table index refers to a command or subroutine, Epsilon
calls the function. When it returns, Epsilon checks the
iter variable. If it is two or more, Epsilon proceeds to call
the same function repeatedly, decrementing iter each time, so
that it calls the function a total of iter times. See The Main Loop.
key_t *table_keys;
int table_count;
table_prompt() /* control.e */
If the entry in the name table that do_command( ) is to execute
contains another table, Epsilon gets another key. First, Epsilon
updates the primitive array table_keys. It contains the
prefix keys entered so far in the current command, and
table_count contains their number. Next, Epsilon calls the
EEL subroutine table_prompt( ) if it exists to display a prompt
for the new key. The version of this subroutine that's provided with
Epsilon uses mention( ), so the message may not appear
immediately. Epsilon then calls the EEL subroutine getkey( )
to read a new key and clears the echo area of the prompt. Epsilon
then interprets the key just as the do_topkey( ) primitive
would, but using the new key table. If both mode_keys and
root_keys provided a table as the entry for the first key, the
values from each are used as the new mode and root key tables.
do_again()
The do_again( ) primitive reinterprets a key using the same
pair of mode and root tables that were used previously. The value in
the variable key may, of course, be different. Epsilon uses
this primitive in commands such as alt-prefix.
Epsilon handles EEL subroutines without parameters in the name table
in the same way as commands, as described above. If the entry is for
a keyboard macro, the only other legal name table entry, Epsilon goes
into a recursive edit level and begins processing the keys in the
macro. It saves the macro internally so that future requests for a
key will return characters from the macro, as described in Keys. It also saves the value of iter, so the macro
will iterate properly. When the macro runs out of keys, Epsilon
automatically exits the recursive edit level, and returns from the
call to do_again( ). (When macro-runs-immediately is
nonzero, running a macro doesn't enter a recursive edit level, but
returns immediately. Future key requests will still come from the
macro until it's exhausted.)
short ignore_kbd_macro;
Epsilon provides a way for a keyboard macro to
suspend itself and get input from the user, then continue. Set the
ignore_kbd_macro variable nonzero to get keyboard input even
when a macro is running. The pause-macro command uses this
variable.
short *ask_key(char *pr, char *keyname, int flags)
int key_binding[30]; // ask_key() puts key info here
The ask_key( ) subroutine defined in basic.e duplicates the
logic of the main loop in getting the sequence of keys that make up a
command. However, it prompts for the sequence and doesn't run the
command at the end. Commands like bind-to-key
that ask for a key and accept a sequence of key table keys
use it.
The ask_key( ) subroutine returns a pointer to the entry in the
key table that was finally reached. The value pointed to is the name
table index of the command the key sequence invokes.
This subroutine stores the key sequence in the keyname parameter
in text form (as "Ctrl-x f", for example). It also copies the key
sequence into the global variable key_binding . The key
sequence is in macro format, so in the example of Ctrl-x f,
key_binding[1] would hold CTRL('X') , key_binding[2] would
hold 'f' , and key_binding[0] would hold 3, the total number
of entries in the array.
If you pass the 1 flag in flags and the user presses a key
like <Backspace> with both a generic and a specific
interpretation, Epsilon asks the user which one he wants. Without
this flag, Epsilon assumes the specific key is meant.
If you pass the 2 flag and the user presses a key that normally
shouldn't be rebound because it self-inserts (such as letter keys or
<Enter>), the subroutine asks for confirmation.
full_getkey(char *pr, int code) /* basic.e */
/* for full_getkey() */
#define ALTIFY_KEY 1
#define CTRLIFY_KEY 2
The full_getkey( ) subroutine defined in basic.e gets a single
key from the keyboard, but recognizes the prefix keys <Esc> and
Ctrl-^. The ask_key( ) subroutine uses it, as well as the
commands bound to the prefix keys above. It takes a prompt to
display and a bit pattern (from eel.h) to make it act as if certain
of the above keys had already been typed. For example, the
ctrl-prefix command calls this subroutine with the value
CTRLIFY_KEY . It leaves the key that results in the key
primitive.
name_macro(char *name, key_t *keys)
Epsilon has no internal mechanism for capturing keyboard keys to
build a macro (this is done in the getkey( ) subroutine defined
in control.e), but once a macro has been built Epsilon can name it and
make it accessible with the name_macro( ) function. It takes the
name of the macro to create, and the sequence of keys making up
the macro in an array of short ints. This array is in the same
format that get_keycode( ) uses. That is, the first element of
the array contains the number of valid elements in the array
(including the first one). The actual keys in the macro follow. The
name_macro( ) primitive makes a copy of the macro it is given,
so the array can be reused once the macro has been defined.
key_t *get_macro(int index)
The get_macro( ) primitive can retrieve the keys in a defined
keyboard macro. It takes the name table index of a macro, and
returns a pointer to the array containing the macro, in the same
format as name_macro( ).
bind_universally(int k, int f)
The bind_universally( ) subroutine can be useful to bind a
function to a key in all modes. When a key has a generic version, a
mode that binds the generic version will override a global definition
for the non-generic version of the key. For instance, the Tab key has
a generic version, Ctrl-i. By default, each time you press Tab,
Epsilon converts it to Ctrl-i and uses the current mode's binding for
Ctrl-i, unless the current mode has its own definition for the Tab key
too. If you want Ctrl-i to behave in a mode-specific manner, but
force Tab to always run the same command regardless of the mode, you
can call this function. It binds the key k to the function f
in all key tables.
int list_bindings(int start, short *modetable,
short *roottable, int find)
The list_bindings( ) primitive quickly steps through a pair of
key tables, looking for entries that have a certain name table index.
It takes mode and root key tables, the name table index to find, and
either -1 to start at the beginning of the key tables, or the
value it returned on a previous call. It returns the index into the
key table, or -1 if there are no more matching entries. For
each position in the tables, Epsilon looks at the value in the mode
key table, unless it is zero. In that case, it uses the root table.
In addition to the matches, list_bindings( ) also stops on each
name table index corresponding to a key table, since these must
normally be searched also. For example, the following file
defines a command that counts the number of separate bindings of any
command.
#include "eel.h"
command count_bindings()
{
char cmd[80];
get_cmd(cmd, "Count bindings of command", "");
if (*cmd)
say("The %s command has %d bindings", cmd,
find_some(mode_keys,
root_keys, find_index(cmd)));
}
/* count bindings to index in table */
int find_some(modetable, roottable, index)
short *modetable, *roottable;
{
int i, total = 0, found;
i = list_bindings(-1, modetable, roottable, index);
while (i != -1) {
found = (modetable[i]
? modetable[i] : roottable[i]);
if (found == index)
total++;
else
total += find_some(index_table(found),
index_table(found), index);
i = list_bindings(i, modetable, roottable, index);
}
return total;
}
Previous
|
Up
|
Next
|
The Main Loop |
Primitives and EEL Subroutines |
Defining Language Modes |
Epsilon Programmer's Editor 14.04 manual. Copyright (C) 1984, 2021 by Lugaru Software Ltd. All rights reserved.
|