Lugaru's Epsilon
Programmer's
Editor

Context:
Epsilon User's Manual and Reference
   Primitives and EEL Subroutines
      . . .
      Control Primitives
         Control Flow
         Character Types
         Strings
         . . .
         Help Subroutines
      Input Primitives
         . . .
         Dialogs
         The Main Loop
         Binding Primitives
      Defining Language Modes
         Language-specific Subroutines

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 is an 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. The file eel.h defines a macro called NUMKEYS that provides the number of bindable keys on the keyboard. A key table, then, is an array of NUMKEYS short ints.

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)
set_case_indirect(short *tab)
set_list_keys(short *tab)

The fix_key_table( ) subroutine copies 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 set_case_indirect( ) subroutine sets the upper case letter keys in a key table to indirect through their lower case equivalents. The set_list_keys( ) subroutine does that, and also 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.

short *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) /* basic.e */
short 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.

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, short *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.

short *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.

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


Lugaru Copyright (C) 1984, 2020 by Lugaru Software Ltd. All rights reserved.