wiki:HemlockProgrammer/AuxiliarySystems

Version 1 (modified by rme, 6 years ago) (diff)

--

18. Auxiliary Systems

This chapter describes utilities that some implementations of Hemlock may leave unprovided or unsupported.

18.1. Key-events

These routines are defined in the "EXTENSIONS" package since other projects have often used Hemlock's input translations for interfacing to CLX.

18.1.1. Introduction

The canonical representation of editor input is a key-event structure. Users can bind commands to keys (see section 1.3.1), which are non-zero length sequences of key-events. A key-event consists of an identifying token known as a keysym and a field of bits representing modifiers. Users define keysyms, integers between 0 and 65535 inclusively, by supplying names that reflect the legends on their keyboard's keys. Users define modifier names similarly, but the system chooses the bit and mask for recognizing the modifier. You can use keysym and modifier names to textually specify key-events and Hemlock keys in a #k syntax. The following are some examples:

#k"C-u"
#k"Control-u"
#k"c-m-z"
#k"control-x meta-d"
#k"a"
#k"A"
#k"Linefeed"

This is convenient for use within code and in init files containing bind-key calls.

The #k syntax is delimited by double quotes, but the system parses the contents rather than reading it as a Common Lisp string. Within the double quotes, spaces separate multiple key-events. A single key-event optionally starts with modifier names terminated by hyphens. Modifier names are alphabetic sequences of characters which the system uses case-insensitively. Following modifiers is a keysym name, which is case-insensitive if it consists of multiple characters, but if the name consists of only a single character, then it is case-sensitive.

You can escape special characters---hyphen, double quote, open angle bracket, close angle bracket, and space---with a backslash, and you can specify a backslash by using two contiguously. You can use angle brackets to enclose a keysym name with many special characters in it. Between angle brackets appearing in a keysym name position, there are only two special characters, the closing angle bracket and backslash.

18.1.2. Interface

All of the following routines and variables are exported from the "EXTENSIONS" ("EXT") package.

define-keysym keysym preferred-name &rest other-names [Function]

This function establishes a mapping from preferred-name to keysym for purposes of #k syntax. Other-names also map to keysym, but the system uses preferred-name when printing key-events. The names are case-insensitive simple-strings; however, if the string contains a single character, then it is used case-sensitively. Redefining a keysym or re-using names has undefined effects.

You can use this to define unused keysyms, but primarily this defines keysyms defined in the X Window System Protocol, MIT X Consortium Standard, X Version 11, Release 4. translate-key-event uses this knowledge to determine what keysyms are modifier keysyms and what keysym stand for alphabetic key-events.

define-mouse-keysym button keysym name shifted-bit event-key [Function]

This function defines keysym named name for key-events representing the X button cross the X event-key (:button-press or :button-release). Shifted-bit is a defined modifier name that translate-mouse-key-event sets in the key-event it returns whenever the X shift bit is set in an incoming event.

Note, by default, there are distinct keysyms for each button distinguishing whether the user pressed or released the button.

Keysym should be an one unspecified in X Window System Protocol, MIT X Consortium Standard, X Version 11, Release 4.

name-keysym name [Function]

This function returns the keysym named name. If name is unknown, this returns nil.

keysym-names keysym [Function]

This function returns the list of all names for keysym. If keysym is undefined, this returns nil.

keysym-preferred-name keysym [Function]

This returns the preferred name for keysym, how it is typically printed. If keysym is undefined, this returns nil.

define-key-event-modifier long-name short-name [Function]

This establishes long-name and short-name as modifier names for purposes of specifying key-events in #k syntax. The names are case-insensitive simple-strings. If either name is already defined, this signals an error.

The system defines the following default modifiers (first the long name, then the short name):

  • "Hyper", "H"
  • "Super", "S"
  • "Meta", "M"
  • "Control", "C"
  • "Shift", "Shift"
  • "Lock", "Lock"

all-modifier-names [Variable]

This variable holds all the defined modifier names.

define-clx-modifier clx-mask modifier-name [Function]

This function establishes a mapping from clx-mask to a defined key-event modifier-name. translate-key-event and translate-mouse-key-event can only return key-events with bits defined by this routine.

The system defines the following default mappings between CLX modifiers and key-event modifiers:

  • (xlib:make-state-mask :mod-1) --> "Meta"
  • (xlib:make-state-mask :control) --> "Control"
  • (xlib:make-state-mask :lock) --> "Lock"
  • (xlib:make-state-mask :shift) --> "Shift"

make-key-event-bits &rest modifier-names [Function]

This function returns bits suitable for make-key-event from the supplied modifier-names. If any name is undefined, this signals an error.

key-event-modifier-mask modifier-name [Function]

This function returns a mask for modifier-name. This mask is suitable for use with key-event-bits. If modifier-name is undefined, this signals an error.

key-event-bits-modifiers bits [Function]

This returns a list of key-event modifier names, one for each modifier set in bits.

translate-key-event display scan-code bits [Function]

This function translates the X scan-code and X bits to a key-event. First this maps scan-code to an X keysym using xlib:keycode->keysym looking at bits and supplying index as 1 if the X shift bit is on, 0 otherwise.

If the resulting keysym is undefined, and it is not a modifier keysym, then this signals an error. If the keysym is a modifier key, then this returns nil.

If these conditions are satisfied

  • The keysym is defined.
  • The X shift bit is off.
  • The X lock bit is on.
  • The X keysym represents a lowercase letter.

then this maps the scan-code again supplying index as 1 this time, treating the X lock bit as a caps-lock bit. If this results in an undefined keysym, this signals an error. Otherwise, this makes a key-event with the keysym and bits formed by mapping the X bits to key-event bits.

Otherwise, this makes a key-event with the keysym and bits formed by mapping the X bits to key-event bits.

translate-mouse-key-event scan-code bits event-key [Function]

This function translates the X button code, scan-code, and modifier bits, bits, for the X event-key into a key-event. See define-mouse-keysym.

make-key-event object bits [Function]

This function returns a key-event described by object with bits. Object is one of keysym, string, or key-event. When object is a key-event, this uses key-event-keysym. You can form bits with make-key-event-bits or key-event-modifier-mask.

key-event-p object [Function]

This function returns whether object is a key-event.

key-event-bits key-event [Function]

This function returns the bits field of a key-event.

key-event-keysym key-event [Function]

This function returns the keysym field of a key-event.

char-key-event character [Function]

This function returns the key-event associated with character. You can associate a key-event with a character by setf-ing this form.

key-event-char key-event [Function]

This function returns the character associated with key-event. You can associate a character with a key-event by setf'ing this form. The system defaultly translates key-events in some implementation dependent way for text insertion; for example, under an ASCII system, the key-event #k"C-h", as well as #k"backspace" would map to the Common Lisp character that causes a backspace.

key-event-bit-p key-event bit-name [Function]

This function returns whether key-event has the bit set named by bit-name. This signals an error if bit-name is undefined.

do-alpha-key-events (var kind &optional result) form [Macro]

This macro evaluates each form with var bound to a key-event representing an alphabetic character. Kind is one of :lower, :upper, or :both, and this binds var to each key-event in order as specified in X Window System Protocol, MIT X Consortium Standard, X Version 11, Release 4. When :both is specified, this processes lowercase letters first.

print-pretty-key key &optional stream long-names-p [Function]

This prints key, a key-event or vector of key-events, in a user-expected fashion to stream. Long-names-p indicates whether modifiers should print with their long or short name. Stream defaults to standard-output.

print-pretty-key-event key-event &optional stream long-names-p [Function]

This prints key-event to stream in a user-expected fashion. Long-names-p indicates whether modifier names should appear using the long name or short name. Stream defaults to standard-output.

18.2. CLX Interface

18.2.1. Graphics Window Hooks

This section describes a few hooks used by Hemlock's internals to handle graphics windows that manifest Hemlock windows. Some heavy users of Hemlock as a tool have needed these in the past, but typically functions that replace the default values of these hooks must be written in the "HEMLOCK-INTERNALS" package. All of these symbols are internal to this package.

If you need this level of control for your application, consult the current implementation for code fragments that will be useful in correctly writing your own window hook functions.

create-window-hook [Variable]

This holds a function that Hemlock calls when make-window executes under CLX. Hemlock passes the CLX display and the following arguments from make-window: starting mark, ask-user, x, y, width, height, and modelinep. The function returns a CLX window or nil indicating one could not be made.

delete-window-hook [Variable]

This holds a function that Hemlock calls when delete-window executes under CLX. Hemlock passes the CLX window and the Hemlock window to this function.

random-typeout-hook [Variable]

This holds a function that Hemlock calls when random typeout occurs under CLX. Hemlock passes it a Hemlock device, a pre-existing CLX window or nil, and the number of pixels needed to display the number of lines requested in the with-pop-up-display form. It should return a window, and if a new window is created, then a CLX gcontext must be the second value.

create-initial-windows-hook [Variable]

This holds a function that Hemlock calls when it initializes the screen manager and makes the first windows, typically windows for the Main and Echo Area buffers. Hemlock passes the function a Hemlock device.

18.2.2. Entering and Leaving Windows

Enter Window Hook (initial value ) [Variable]

When the mouse enters an editor window, Hemlock invokes the functions in this hook. These functions take a Hemlock window as an argument.

Exit Window Hook (initial value ) [Variable]

When the mouse exits an editor window, Hemlock invokes the functions in this hook. These functions take a Hemlock window as an argument.

18.2.3 How to Lose Up-Events

Often the only useful activity user's design for the mouse is to click on something. Hemlock sees a character representing the down event, but what do you do with the up event character that you know must follow? Having the command eat it would be tasteless, and would inhibit later customizations that make use of it, possibly adding on to the down click command's functionality. Bind the corresponding up character to the command described here.

Do Nothing [Command]

This does nothing as many times as you tell it.

18.3 Slave Lisps

Some implementations of Hemlock feature the ability to manage multiple slave Lisps, each connected to one editor Lisp. The routines discussed here spawn slaves, send evaluation and compilation requests, return the current server, etc. This is very powerful because without it you can lose your editing state when code you are developing causes a fatal error in Lisp.

The routines described in this section are best suited for creating editor commands that interact with slave Lisps, but in the past users implemented several independent Lisps as nodes communicating via these functions. There is a better level on which to write such code that avoids the extra effort these routines take for the editor's sake. See the CMU Common Lisp User's Manual for the remote and wire packages.

18.3.1 The Current Slave

There is a slave-information structure that these return which is suitable for passing to the routines described in the following subsections.

create-slave &optional name [Function]

This creates a slave that tries to connect to the editor. When the slave connects to the editor, this returns a slave-information structure, and the interactive buffer is the buffer named name. This generates a name if name is nil. In case the slave never connects, this will eventually timeout and signal an editor-error.

get-current-eval-server &optional errorp [Function]

Current Eval Server (initial value ) [Variable]

This returns the server-information for the Current Eval Server after making sure it is valid. Of course, a slave Lisp can die at anytime. If this variable is nil, and errorp is non-nil, then this signals an editor-error; otherwise, it tries to make a new slave. If there is no current eval server, then this tries to make a new slave, prompting the user based on a few variables (see the Hemlock User's Manual).

get-current-compile-server [Function]

Current Compile Server (initial value ) [Variable]

This returns the server-information for the Current Compile Server after making sure it is valid. This may return nil. Since multiple slaves may exist, it is convenient to use one for developing code and one for compiling files. The compilation commands that use slave Lisps prefer to use the current compile server but will fall back on the current eval server when necessary. Typically, users only have separate compile servers when the slave Lisp can live on a separate workstation to save cycles on the editor machine, and the Hemlock commands only use this for compiling files.

18.3.2 Asynchronous Operation Queuing

The routines in this section queue requests with an eval server. Requests are always satisfied in order, but these do not wait for notification that the operation actually happened. Because of this, the user can continue editing while his evaluation or compilation occurs. Note, these usually execute in the slave immediately, but if the interactive buffer connected to the slave is waiting for a form to return a value, the operation requested must wait until the slave is free again.

string-eval string [Function]

region-eval region [Function]

region-compile region [Function]

string-eval queues the evaluation of the form read from string on eval server server. Server defaults to the result of get-current-server, and string is a simple-string. The evaluation occurs with package bound in the slave to the package named by package, which defaults to Current Package or the empty string; the empty string indicates that the slave should evaluate the form in its current package. The slave reads the form in string within this context as well. Context is a string to use when reporting start and end notifications in the Echo Area buffer; it defaults to the concatenation of "evaluation of " and string.

region-eval is the same as string-eval, but context defaults differently. If the user leaves this unsupplied, then it becomes a string involving part of the first line of region.

region-compile is the same as the above. Server defaults the same; it does not default to get-current-compile-server since this compiles the region into the slave Lisp's environment, to affect what you are currently working on.

file-compile file [Function]

Remote Compile File (initial value nil) [Variable]

This compiles file in a slave Lisp. When output-file is t, this uses a temporary output file that is publicly writable in case the client is on another machine, which allows for file systems that do not permit remote write access. This renames the temporary file to the appropriate binary name or deletes it after compilation. Setting Remote Compile File to nil, inhibits this. If output-file is non-nil and not t, then it is the name of the binary file to write. The compilation occurs with package bound in the slave to the package named by package, which defaults to Current Package or the empty string; the empty string indicates that the slave should evaluate the form in its current package. Error-file is the file in which to record compiler output, and a nil value inhibits this file's creation. Load indicates whether to load the resulting binary file, defaults to nil. Server defaults to get-current-compile-server, but if this returns nil, then server defaults to get-current-server.

18.3.3. Synchronous Operation Queuing

The routines in this section queue requests with an eval server and wait for confirmation that the evaluation actually occurred. Because of this, the user cannot continue editing while the slave executes the request. Note, these usually execute in the slave immediately, but if the interactive buffer connected to the slave is waiting for a form to return a value, the operation requested must wait until the slave is free again.

eval-form-in-server server-info string &optional package [Function]

This function queues the evaluation of a form in the server associated with server-info and waits for the results. The server read's the form from string with package bound to the package named by package. This returns the results from the slave Lisp in a list of string values. You can read from the strings or simply display them depending on the print'ing of the evaluation results.

Package defaults to Current Package. If this is nil, the server uses the value of package in the server.

While the slave executes the form, it binds terminal-io to a stream that signals errors when read from and dumps output to a bit-bucket. This prevents the editor and slave from dead locking by waiting for each other to reply.

eval-form-in-server-1 server-info string &optional package [Function]

This function calls eval-form-in-server and read's the result in the first string it returns. This result must be read'able in the editor's Lisp.

18.4. Spelling

Hemlock supports spelling checking and correcting commands based on the ITS Ispell dictionary. These commands use the following routines which include adding and deleting entries, reading the Ispell dictionary in a compiled binary format, reading user dictionary files in a text format, and checking and correcting possible spellings.

spell:maybe-read-spell-dictionary [Function]

This reads the default binary Ispell dictionary. Users must call this before the following routines will work.

spell:spell-read-dictionary filename [Function]

This adds entries to the dictionary from the lines in the file filename. Dictionary files contain line oriented records like the following:

entry1/flag1/flag2
entry2
entry3/flag1

The flags are the Ispell flags indicating which endings are appropriate for the given entry root, but these are unnecessary for user dictionary files. You can consult Ispell documentation if you want to know more about them.

spell:spell-add-entry line &optional word-end [Function]

This takes a line from a dictionary file, and adds the entry described by line to the dictionary. Word-end defaults to the position of the first slash character or the length of the line. Line is destructively modified.

spell-remove-entry entry [Function]

This removes entry, a simple-string, from the dictionary, so it will be an unknown word. This destructively modifies entry. If it is a root word, then all words derived with entry and its flags will also be deleted. If entry is a word derived from some root word, then the root and any words derived from it remain known words.

spell:correct-spelling word [Function]

This checks the spelling of word and outputs the results. If this finds word is correctly spelled due to some appropriate suffix on a root, it generates output indicating this. If this finds word as a root entry, it simply outputs that it found word. If this cannot find word at all, then it outputs possibly correct close spellings. This writes to standard-output, and it calls maybe-read-spell-dictionary before attempting any lookups.

spell:spell-try-word word word-len [Function]

max-entry-length [Constant]

This returns an index into the dictionary if it finds word or an appropriate root. Word-len must be inclusively in the range 2 through max-entry-length, and it is the length of word. Word must be uppercase. This returns a second value indicating whether it found word due to a suffix flag, nil if word is a root entry.

spell:spell-root-word index [Function]

This returns a copy of the root word at dictionary entry index. This index is the same as returned by spell-try-word.

spell:spell-collect-close-words word [Function]

This returns a list of words correctly spelled that are close to word. Word must be uppercase, and its length must be inclusively in the range 2 through max-entry-length. Close words are determined by the Ispell rules:

  1. Two adjacent letters can be transposed to form a correct spelling.
  2. One letter can be changed to form a correct spelling.
  3. One letter can be added to form a correct spelling.
  4. One letter can be removed to form a correct spelling.

spell:spell-root-flags index [Function]

This returns a list of suffix flags as capital letters that apply to the dictionary root entry at index. This index is the same as returned by spell-try-word.

18.5. File Utilities

Some implementations of Hemlock provide extensive directory editing commands, Dired, including a single wildcard feature. An asterisk denotes a wildcard.

dired:copy-file spec1 spec2 &key :update :clobber :directory [Function]

This function copies spec1 to spec2. It accepts a single wildcard in the filename portion of the specification, and it accepts directories. This copies files maintaining the source's write date.

If spec1 and spec2 are both directories, this recursively copies the files and subdirectory structure of spec1; if spec2 is in the subdirectory structure of spec1, the recursion will not descend into it. Use "/spec1/*" to copy only the files from spec1 to directory spec2.

If spec2 is a directory, and spec1 is a file, then this copies spec1 into spec2 with the same pathname-name.

When :update is non-nil, then the copying process only copies files if the source is newer than the destination.

When :update and :clobber are nil, and the destination exists, the copying process stops and asks the user whether the destination should be overwritten.

When the user supplies :directory, it is a list of pathnames, directories excluded, and spec1 is a pattern containing one wildcard. This then copies each of the pathnames whose pathname-name matches the pattern. Spec2 is either a directory or a pathname whose pathname-name contains a wildcard.

dired:rename-file spec1 spec2 &key :clobber :directory [Function]

This function renames spec1 to spec2. It accepts a single wildcard in the filename portion of the specification, and spec2 may be a directory with the destination specification resulting in the merging of spec2 with spec1. If :clobber is nil, and spec2 exists, then this asks the user to confirm the renaming. When renaming a directory, end the specification without the trailing slash.

When the user supplies :directory, it is a list of pathnames, directories excluded, and spec1 is a pattern containing one wildcard. This then copies each of the pathnames whose pathname-name matches the pattern. Spec2 is either a directory or a pathname whose pathname-name contains a wildcard.

dired:delete-file spec &key :recursive :clobber [Function]

This function deletes spec. It accepts a single wildcard in the filename portion of the specification, and it asks for confirmation on each file if :clobber is nil. If :recursive is non-nil, then spec may be a directory to recursively delete the entirety of the directory and its subdirectory structure. An empty directory may be specified without :recursive being non-nil. Specify directories with the trailing slash.

dired:find-file name &optional directory find-all [Function]

This function finds the file with file-namestring name, recursively looking in directory. If find-all is non-nil, then this continues searching even after finding a first occurrence of file. Name may contain a single wildcard, which causes find-all to default to t instead of nil.

dired:make-directory name [Function]

This function creates the directory with name. If it already exists, this signals an error.

dired:pathnames-from-pattern pattern files [Function]

This function returns a list of pathnames from the list files whose file-namestring's match pattern. Pattern must be a non-empty string and contain only one asterisk. Files contains no directories.

dired:*update-default* [Variable]

dired:*clobber-default* [Variable]

dired:*recursive-default* [Variable]

These are the default values for the keyword arguments above with corresponding names. These default to nil, t, and nil respectively.

dired:*report-function* [Variable] dired:*error-function* [Variable] dired:*yesp-function* [Variable]

These are the function the above routines call to report progress, signal errors, and prompt for yes or no. These all take format strings and arguments.

merge-relative-pathnames pathname default-directory [Function]

This function merges pathname with default-directory. If pathname is not absolute, this assumes it is relative to default-directory. The result is always a directory pathname.

directoryp pathname [Function]

This function returns whether pathname names a directory: it has no name and no type fields.

18.6. Beeping

hemlock-beep [Function]

Hemlock binds system:*beep-function* to this function to beep the device. It is different for different devices.

Bell Style (initial value :border-flash) [Variable]

Beep Border Width (initial value 20) [Variable]

Bell Style determines what hemlock-beep does in Hemlock under CLX. Acceptable values are :border-flash, :feep, :border-flash-and-feep, :flash, :flash-and-feep, and nil.

Beep Border Width is the width in pixels of the border flashed by border flash beep styles.