Presentation Types Contents Index Menu Facilities




24 Input Editing and Completion Facilities

CLIM provides number of facilities to assist in writing presentation type parser functions, such as an interactive input editor and some ``completion'' facilities.

24.1 The Input Editor

An input editing stream ``encapsulates'' an interactive stream, that is, most operations are handled by the encapsulated interactive stream, but some operations are handled directly by the input editing stream itself. (See Appendix Encapsulating Streams for a discussion of encapsulating streams.)

An input editing stream will have the following components:

The input editing stream may also have other components to store internal state, such as a slot to accumulate a numeric argument or remember the most recently used presentation history, and so forth. These other components are explicitly left unspecified.

The high level description of the operation of the input editor is that it reads either ``real'' gestures from the user (such as characters from the keyboard or pointer button events) or input editing commands. The input editing commands can modify the state of the input buffer. When such modifications take place, it is necessary to ``rescan'' the input buffer, that is, reset the scan pointer SP to its original state and reparse the contents of the input editor buffer before reading any other gestures from the user. While this rescanning operation is taking place, the ``rescan in progress'' flag is set to true . The relationship SP <= IP <= FP always holds.

The overall control structure of the input editor is:

(catch 'rescan                  ;thrown to when a rescan is invoked
  (reset-scan-pointer stream)   ;sets STREAM-RESCANNING-P to T
  (loop
     (funcall continuation stream)))
where stream is the input editing stream and continuation is the code supplied by the programmer, and typically contains calls to such functions as accept and read-token (which will eventually call stream-read-gesture ). When a rescan operation is invoked, it has the effect of throwing to the rescan tag in the example above. The loop is terminated when an activation gesture is seen, and at that point the values produced by continuation are returned as values from the input editor.

The important point is that functions such as accept , read-gesture , and unread-gesture read (or restore) the next gesture object from the buffer at the position pointed to by the scan pointer SP. However, insertion and input editing commands take place at the position pointed to by IP. The purpose of the rescanning operation is to eventually ensure that all the input gestures issued by the user (typed characters, pointer button presses, and so forth) have been read by CLIM. During input editing, the input editor should maintain some sort of visible cursor to remind the user of the position of IP.

The overall structure of stream-read-gesture on an input editing stream is:

(progn
  (rescan-if-necessary stream)
  (loop
    ;; If SP is less than FP
    ;;   Then get the next gesture from the input editor buffer at SP
    ;;   and increment SP
    ;;   Else read the next gesture from the encapsulated stream
    ;;   and insert it into the buffer at IP
    ;; Set the "rescan in progress" flag to false
    ;; Call STREAM-PROCESS-GESTURE on the gesture
    ;;   If it was a "real" gesture
    ;;     Then exit with the gesture as the result
    ;;     Else it was an input editing command (which has already been
    ;;     processed), so continue looping
    ))
When a new gesture object is inserted into the input editor buffer, it is inserted at the insertion pointer IP. If IP = FP, this is accomplished by a vector-push-extend -like operation on the input buffer and FP, and then incrementing IP. If IP < FP, CLIM must first ``make room'' for the new gesture in the input buffer, then insert the gesture at IP, then increment both IP and FP.

When the user requests an input editor motion command, only the insertion pointer IP is affected. Motion commands do not need to request a rescan operation.

When the user requests an input editor deletion command, the sequence of gesture objects at IP are removed, and IP and FP must be modified to reflect the new state of the input buffer. Deletion commands (and other commands that modify the input buffer) must arrange for a rescan to occur when they are done modifying the buffer, either by calling queue-rescan or immediate-rescan .

CLIM implementations are free to put special objects in the input editor buffer, such as ``noise strings'' and ``accept results''. A ``noise string'' is used to represent some sort of in-line prompt and is never seen as input; the prompt-for-accept method may insert a noise string into the input buffer. An ``accept result'' is an object in the input buffer that is used to represent some object that was inserted into the input buffer (typically via a pointer gesture) that has no readable representation (in the Lisp sense); presentation-replace-input may create accept results. Noise strings are skipped over by input editing commands, and accept results are treated as a single gesture.

interactive-stream-pobject[Predicate]
Returns true if object is an interactive stream, that is, a bidrectional stream intended for user interactions. Otherwise it returns false . This is exactly the same function as in X3J13 Common Lisp, except that in CLIM it is a generic function.

The input editor need only be fully implemented for interactive streams.

input-editing-stream[Protocol Class]
The protocol class that corresponds to an input editing stream. If you want to create a new class that behaves like an input editing stream, it should be a subclass of input-editing-stream. Subclasses of input-editing-stream must obey the input editing stream protocol.
input-editing-stream-pobject[Predicate]
Returns true if object is an input editing stream (that is, a stream of the sort created by a call to with-input-editing ), otherwise returns false .

standard-input-editing-stream[Class]
The instantiable class that implements CLIM's standard input editor. This is the class of stream created by calling with-input-editing .

Members of this class are mutable.
with-input-editing(&optional stream &key input-sensitizer initial-contents class) &body body[Macro]
Establishes a context in which the user can edit the input typed in on the interactive stream stream . body is then executed in this context, and the values returned by body are returned as the values of with-input-editing . body may have zero or more declarations as its first forms.

The stream argument is not evaluated, and must be a symbol that is bound to an input stream. If stream is t (the default), *standard-input* is used. If stream is a stream that is not an interactive stream, then with-input-editing is equivalent to progn .

input-sensitizer , if supplied, is a function of two arguments, a stream and a continuation function; the function has dynamic extent. The continuation, supplied by CLIM, is responsible for displaying output corresponding to the user's input on the stream. The input-sensitizer function will typically call with-output-as-presentation in order to make the output produced by the continuation sensitive.

If initial-contents is supplied, it must be either a string or a list of two elements, an object and a presentation type. If it is a string, the string will be inserted into the input buffer using replace-input . If it is a list, the printed representation of the object will be inserted into the input buffer using presentation-replace-input .

with-input-editor-typeout(&optional stream &key erase) &body body[Macro]
Establishes a context inside of with-input-editing in which output can be done by body to the input editing stream stream . If erase is true , the area underneath the typeout will be erased before the typeout is done. with-input-editor-typeout should call fresh-line before and after evaluating the body. body may have zero or more declarations as its first forms.

The stream argument is not evaluated, and must be a symbol that is bound to a stream. If stream is t (the default), *standard-input* is used. If stream is a stream that is not an input editing stream, then with-input-editor-typeout is equivalent to calling fresh-line , evaluating the body, and then calling fresh-line again.

input-editor-formatstream format-string &rest format-args[Generic function]
This function is like format , except that it is intended to be called on input editing streams. It arranges to insert ``noise strings'' in the input editor's input buffer. Programmers can use this to display in-line prompts in accept methods.

If stream is a stream that is not an input editing stream, then input-editor-format is equivalent to format .

24.1.1 The Input Editing Stream Protocol

Input editing streams obey both the extended input and extended output stream protocols, and must support the generic functions that comprise those protocols. For the most part, this will simply entail ``trampolining'' those operations to the encapsulated interactive stream. However, some generic functions as stream-read-gesture and stream-unread-gesture will need methods that observe the use of the input editor's scan pointer.

Input editing streams will typically also implement methods for prompt-for-accept (in order to provide in-line prompting that interacts correctly with input editing) and stream-accept (in order to cause accept to obey the scan pointer).

The following generic functions comprise the remainder of the input editing protocol, and must be implemented for all classes that inherit from input-editing-stream .

stream-input-buffer(stream input-editing-stream )[Method]
Returns the input buffer (that is, the string being edited) associated with the input editing stream stream . This must be an unspecialized vector with a fill pointer. The fill pointer of the vector points past the last gesture object in the buffer. During input editing, this buffer is side-effected. The consequences of modifying the input buffer by means other than the specified API (such as replace-input ) are unspecified.

stream-insertion-pointerstream[Generic function]
Returns an integer corresponding to the current input position in the input editing stream stream 's buffer, that is, the point in the buffer at which the next user input gesture will be inserted. The insertion pointer will always be less than (fill-pointer (stream-input-buffer stream )) . The insertion pointer can also be thought of as an editing cursor.

(setf stream-insertion-pointer)pointer stream[Generic function]
Changes the input position of the input editing stream stream to pointer . pointer is an integer, and must be less than (fill-pointer (stream-input-buffer stream )) .

stream-scan-pointerstream[Generic function]
Returns an integer corresponding to the current scan pointer in the input editing stream stream 's buffer, that is, the point in the buffer at which calls to accept have stopped parsing input. The scan pointer will always be less than or equal to (stream-insertion-pointer stream ) .

(setf stream-scan-pointer)pointer stream[Generic function]
Changes the scan pointer of the input editing stream stream to pointer . pointer is an integer, and must be less than or equal to (stream-insertion-pointer stream ) .

stream-rescanning-pstream[Generic function]
Returns the state of the input editing stream stream 's ``rescan in progress'' flag, which is true if stream is performing a rescan operation, otherwise it is false . All extended input streams must implement a method for this, but non-input editing streams will always returns false .

reset-scan-pointerstream &optional (scan-pointer 0 )[Generic function]
Sets the input editing stream stream 's scan pointer to scan-pointer , and sets the state of stream-rescanning-p to true .

immediate-rescanstream[Generic function]
Invokes a rescan operation immediately by ``throwing'' out to the most recent invocation of with-input-editing .

queue-rescanstream[Generic function]
Indicates that a rescan operation on the input editing stream stream should take place after the next non-input editing gesture is read by setting the ``rescan queued'' flag to true .

rescan-if-necessarystream &optional inhibit-activation[Generic function]
Invokes a rescan operation on the input editing stream stream if queue-rescan was called on the same stream and no intervening rescan operation has taken place. Resets the state of the ``rescan queued'' flag to false .

If inhibit-activation is false , the input line will not be activated even if there is an activation character in it.

erase-input-bufferstream &optional (start-position 0 )[Generic function]
Erases the part of the display that corresponds to the input editor's buffer starting at the position start-position .

redraw-input-bufferstream &optional (start-position 0 )[Generic function]
Displays the input editor's buffer starting at the position start-position on the interactive stream that is encapsulated by the input editing stream stream .

stream-process-gesturestream gesture type[Generic function]
If gesture is an input editing command, stream-process-gesture performs the input editing operation on the input editing stream stream and returns nil . Otherwise, it returns the two values gesture and type .

stream-read-gesture(stream standard-input-editing-stream ) &key [Method]
Reads and returns a gesture from the user on the input editing stream stream .

The stream-read-gesture method must call stream-process-gesture , which will either return a ``real'' gesture (such as a typed character, a pointer gesture, or a timeout) or will return nil (indicating that some sort of input editing operation was performed). stream-read-gesture must only return when a real gesture was been read; if an input editing operation was performed, stream-read-gesture will loop until a ``real'' gesture is typed by the user.

stream-unread-gesture(stream standard-input-editing-stream ) gesture[Method]
Inserts the gesture gesture back into the input editor's buffer, maintaining the scan pointer.

24.1.2 Suggestions for Input Editing Commands

An implementation of the input editor should provide a set of generally useful input editing commands. The exact set of these commands is unspecified, and the key bindings for these commands may vary from platform to platform. The following is a suggested minimum set of input editing commands and key bindings, taken roughly from EMACS.

|
|
|
|
Suggested
|
|
Input editor command
|
key binding
|
|
|
|Forward character | control-F|
|Forward word | meta-F |
|Backward character | control-B|
|Backward word | meta-B |
|Beginning of line | control-A|
|End of line | control-E|
|Next line | control-N|
|Previous line | control-P|
|Beginning of buffer | meta-< |
|End of buffer | meta-< |
|Delete next character | control-D|
|Delete next word | meta-D |
|Delete previous character | Rubout |
|Delete previous word | m-Rubout |
|Kill to end of line | control-K|
|Clear input buffer | varies |
|Insert new line | control-O|
|Transpose adjacent characters | control-T |
|Transpose adjacent words | meta-T |
|Yank from kill ring | control-Y |
|Yank from presentation history | control-meta-Y|
|Yank next item | meta-Y |
|Scroll output history forward | control-V|
|Scroll output history backward | meta-V |
|
|
An implementation of the input may also support ``numeric arguments'' (such as control-0, control-1, meta-0, and so forth) that modify the behavior of the input editing commands. For instance, the motion and deletion commands should be repeated as many times as specified by the numeric argument. Furthermore, the accumulated numeric argument should be passed to the command processor in such a way that substitute-numeric-argument-marker can be used to insert the numeric argument into a command that was read via a keystroke accelerator.

add-input-editor-commandgestures function[Function]
Adds an input editing command that causes function to be executed when the specified gesture(s) are typed by the user. gestures is either a single gesture name, or a list of gesture names. When gestures is a sequence of gesture names, the function is executed only after all of the gestures are typed in order with no intervening gestures. (This is used to implement ``prefixed'' commands, such as the control-X control-F command one might fix in EMACS.)

24.2 Activation and Delimiter Gestures

Activation gestures terminate an input ``sentence'', such as a command or anything else being read by accept . When an activation gesture is entered by the user, CLIM will cease reading input and ``execute'' the input that has been entered.

Delimiter gestures terminate an input ``word'', such as a recursive call to accept .

*activation-gestures*[Variable]
The set of currently active activation gestures. The global value of this must be nil . The exact format of *activation-gestures* is unspecified. *activation-gestures* and the elements in it may have dynamic extent.

*standard-activation-gestures*[Variable]
The default set of activation gestures. The exact set of standard activation is unspecified, but must include the gesture that corresponds to the #\Newline character.

with-activation-gestures(gestures &key override) &body body[Macro]
Specifies a list of gestures that terminate input during the execution of body . body may have zero or more declarations as its first forms. gestures must be either a single gesture name or a form that evaluates to a list of gesture names.

If the boolean override is true , then gestures will override the current activation gestures. If it is false (the default), then gestures will be added to the existing set of activation gestures. with-activation-gestures must bind *activation-gestures* to the new set of activation gestures.

See also the :activation-gestures and :additional-activation-gestures options to accept .

activation-gesture-pgesture[Function]
Returns true if the gesture object gesture is an activation gesture, otherwise returns false .

*delimiter-gestures*[Variable]
The set of currently active delimiter gestures. The global value of this must be nil . The exact format of *delimiter-gestures* is unspecified. *delimiter-gestures* and the elements in it may have dynamic extent.

with-delimiter-gestures(gestures &key override) &body body[Macro]
Specifies a list of gestures that terminate an individual token, but not the entire input, during the execution of body . body may have zero or more declarations as its first forms. gestures must be either a single gesture name or a form that evaluates to a list of gesture names.

If the boolean override is true , then gestures will override the current delimiter gestures. If it is false (the default), then gestures will be added to the existing set of delimiter gestures. with-delimiter-gestures must bind *delimiter-gestures* to the new set of delimiter gestures.

See also the :delimiter-gestures and :additional-delimiter-gestures options to accept .

delimiter-gesture-pgesture[Function]
Returns true if the gesture object gesture is a delimiter gesture, otherwise returns false .

24.3 Signalling Errors Inside present Methods

simple-parse-error[Error]
The error that is signalled by simple-parse-error . This is a subclass of parse-error .

This condition handles two initargs, :format-string and :format-arguments , which are used to specify a control string and arguments for a call to format .

simple-parse-errorformat-string &rest format-arguments[Function]
Signals a simple-parse-error error while parsing an input token. Does not return. format-string and format-args are as for format .

input-not-of-required-type[Error]
The error that is signalled by input-not-of-required-type . This is a subclass of parse-error .

This condition handles two initargs, :string and :type , which specify a string to be used in an error message and the expected presentation type.

input-not-of-required-typeobject type[Function]
Reports that input does not satisfy the specified type by signalling an input-not-of-required-type error. object is a parsed object or an unparsed token (a string). type is a presentation type specifier. Does not return.

24.4 Reading and Writing of Tokens

replace-inputstream new-input &key start end buffer-start rescan[Generic function]
Replaces the part of the input editing stream stream 's input buffer that extends from buffer-start to its scan pointer with the string new-input . buffer-start defaults to the current input position of stream . start and end can be supplied to specify a subsequence of new-input ; start defaults to 0 and end defaults to the length of new-input .

replace-input must queue a rescan by calling queue-rescan if the new input does not match the old input, or rescan is true .

The returned value is the position in the input buffer.

All input editing streams must implement a method for this function.

presentation-replace-inputstream object type view &key buffer-start rescan query-identifier for-context-type[Generic function]
Like replace-input , except that the new input to insert into the input buffer is gotten by presenting object with the presentation type type and view view . buffer-start and rescan are as for replace-input , and query-identifier and for-context-type as as for present .

All input editing streams must implement a method for this function. Typically, this will be implemented by calling present-to-string on object , type , view , and for-context-type , and then calling replace-input on the resulting string.

If the object does not have a readable representation (in the Lisp sense), presentation-replace-input may create an ``accept result'' to represent the object, and insert that into the input buffer. For the purposes of input editing, ``accept results'' must be treated as a single input gesture.

read-tokenstream &key input-wait-handler pointer-button-press-handler click-only[Function]
Reads characters from the interactive stream stream until it encounters a delimiter or activation gesture, or a pointer gesture. Returns the accumulated string that was delimited by the delimiter or activation gesture, leaving the delimiter unread.

If the first character of typed input is a quotation mark (#\"), then read-token will ignore delimiter gestures until until another quotation mark is seen. When the closing quotation mark is seen, read-token will proceed as above.

If the boolean click-only is true , then no keyboard input is allowed. In this case read-token will simply ignore any typed characters.

input-wait-handler and pointer-button-press-handler are as for stream-read-gesture .

write-tokentoken stream &key acceptably[Function]
write-token is the opposite of read-token given the string token , it writes it to the interactive stream stream . If acceptably is true and there are any characters in the token that are delimiter gestures (see the macro with-delimiter-gestures ), then write-token will surround the token with quotation marks (#\").

Typically, present methods will use write-token instead of write-string .

24.5 Completion

CLIM provides a completion facility that completes a string provided by a user against some set of possible completions (which are themselves strings). Each completion is associated with some Lisp object. CLIM implementations are encouraged to provide ``chunkwise'' completion, that is, if the user input consists of several tokens separated by ``partial delimiters'', CLIM should complete each token separately against the set of possibilities.

*completion-gestures*[Variable]
A list of the gesture names that cause complete-input to complete the user's input as fully as possible. The exact global contents of this list is unspecified, but must include the :complete gesture name.

*help-gestures*[Variable]
A list of the gesture names that cause accept and complete-input to display a (possibly input context-sensitive) help message, and for some presentation types a list of possibilities as well. The exact global contents of this list is unspecified, but must include the :help gesture name.

*possibilities-gestures*[Variable]
A list of the gesture names that cause complete-input to display a (possibly input context-sensitive) help message and a list of possibilities. The exact global contents of this list is unspecified, but must include the :possibilities gesture name.

complete-inputstream function &key partial-completers allow-any-input possibility-printer (help-displays-possibilities t)[Function]
Reads input from the user from the input editing stream stream , completing over a set of possibilities. complete-input is only required to work on input editing streams, but implementations may extend it to work on interactive streams as well.

function is a function of two arguments. It is called to generate the completion possibilities that match the user's input; it has dynamic extent. Usually, programmers will pass either complete-from-possibilities or complete-from-generator as the value of function . Its first argument is a string containing the user's input ``so far''. Its second argument is the completion mode, one of the following:

function must return five values:

complete-input returns three values: object , success , and string . In addition, the printed representation of the completed input will be inserted into the input buffer of stream in place of the user-supplied string by calling replace-input .

partial-completers is a list of characters that delimit portions of a name that can be completed separately. The default is an empty list.

If the boolean allow-any-input is true , then complete-input will return as soon as the user issues an activation gesture, even if the input is not any of the possibilities. If the input is not one of the possibilities, the three values returned by complete-input will be nil , t , and the string. The default for allow-any-input is false .

If possibility-printer is supplied, it must be a function of three arguments, a possibility, a presentation type, and a stream; it has dynamic extent. The function displays the possibility on the stream. The possibility will be a list of two elements, the first being a string and the second being the object corresponding to the string.

If help-display-possibilities is true (the default), then when the user issues a help gesture (a gesture that matches one of the gesture names in *help-gestures* ), CLIM will display all the matching possibilities. If it is false , then CLIM will not display the possibilities unless the user issues a possibility gesture (a gesture that matches one of the gesture names in *possibilities-gestures* ).

simple-completion-error[Condition]
The error that is signalled by complete-input when no completion is found. This is a subclass of simple-parse-error .

completing-from-suggestions(stream &key partial-completers allow-any-input possibility-printer (help-displays-possibilities t )) &body body[Macro]
Reads input from the input editing stream stream , completing over a set of possibilities generated by calls to suggest within body . body may have zero or more declarations as its first forms.

completing-from-suggestions returns three values, object , success , and string The stream argument is not evaluated, and must be a symbol that is bound to a stream. If stream is t (the default), *standard-input* is used.

partial-completers , allow-any-input , and possibility-printer are as for complete-input .

Implementations will probably use complete-from-generator to implement this.

suggestcompletion object[Function]
Specifies one possibility for completing-from-suggestions . completion is a string, the printed representation of object . object is the internal representation.

It is permitted for this function to have lexical scope, and be defined only within the body of completing-from-suggestions .

complete-from-generatorstring function delimiters &key (action :complete ) predicate[Function]
Given an input string string and a list of delimiter characters delimiters that act as partial completion characters, complete-from-generator completes against the possibilities that are generated by the function generator . generator is a function of two arguments, the string string and another function that it calls in order to process the possibility; it has dynamic extent.

action will be one of :complete , :complete-maximal , :complete-limited , or :possibilities . These are described under the function complete-input .

predicate must be a function of one argument, an object. If the predicate returns true , the possibility corresponding to the object is processed, otherwise it is not. It has dynamic extent.

complete-from-generator returns five values, the completed input string, the success value (true if the completion was successful, otherwise false ), the object matching the completion (or nil if unsuccessful), the number of matches, and a list of possible completions if action was :possibilities .

This function is one that will typically be passed as the second argument to complete-input .

complete-from-possibilitiesstring completions delimiters &key (action :complete ) predicate name-key value-key[Function]
Given an input string string and a list of delimiter characters delimiters that act as partial completion characters, complete-from-possibilities completes against the possibilities in the sequence completions . The completion string is extracted from the possibilities in completions by applying name-key , which is a function of one argument. The object is extracted by applying value-key , which is a function of one argument. name-key defaults to first , and value-key defaults to second .

action will be one of :complete , :complete-maximal , :complete-limited , or :possibilities . These are described under the function complete-input .

predicate must be a function of one argument, an object. If the predicate returns true , the possibility corresponding to the object is processed, otherwise it is not.

predicate , name-key , and value-key have dynamic extent.

complete-from-possibilities returns five values, the completed input string, the success value (true if the completion was successful, otherwise false ), the object matching the completion (or nil if unsuccessful), the number of matches, and a list of possible completions if action was :possibilities .

This function is one that will typically be passed as the second argument to complete-input .

with-accept-helpoptions &body body[Macro]
Binds the dynamic environment to control the documentation produced by help and possibilities gestures during user input in calls to accept with the dynamic scope of body . body may have zero or more declarations as its first forms.

options is a list of option specifications. Each specification is itself a list of the form (help-option help-string) . help-option is either a symbol that is a help-type or a list of the form (help-type mode-flag) .

help-type must be one of:

mode-flag must be one of:

help-string is a string or a function that returns a string. If it is a function, it receives three arguments, the stream, an action (either :help or :possibilities ) and the help string generated so far.

None of the arguments is evaluated.



Presentation Types Contents Index Menu Facilities