All Manuals > Editor User Guide > 6 Advanced Features

6.3 Programming the editor

The editor functions described in this section can be combined and provided with arguments to create new commands.

Existing editor commands can also be used in the creation of new commands. Every editor command documented in this manual is named by a string command which can be used to invoke the command interactively, but there is also associated with this a standard Lisp function (the "command function") named by a symbol exported from the editor package. You can use this symbol to call the command programmatically. For example, the editor command Forward Character is referred to by editor:forward-character-command.

The first argument of any command function is the prefix argument p, and this must therefore be included in any programmatic call, even if the prefix argument is ignored. Some commands have additional optional arguments. For example to insert 42 #\! characters, you would call:

(editor:self-insert-command 42 #\!)

Details of these optional arguments are provided in the command descriptions throughout this manual.

See editor:defcommand for the details of how to create new commands.

Note: code which modifies the contents of a capi:editor-pane (for example a displayed editor buffer) must be run only in the interface process of that pane.

The following sections describe editor functions that are not interactive editor commands.

6.3.1 Calling editor functions

All editor commands and some other editor functions expect to be called within a dynamic context that includes settings for the current buffer and current window. This happens automatically when using the editor interactively.

You can set up the context in a CAPI application by using the function capi:call-editor (see the CAPI User Guide and Reference Manual).

You can also use the following function to call editor commands and functions.

editor:process-character Function

editor:process-character char window

Processes char in a dynamic context where the current window is window and the current buffer is the buffer currently displayed in window.

The char can be one of the following:

There is no return value. The processing may happen in another thread, so may not have competed before this function returns.

6.3.2 Defining commands

editor:defcommand Macro

defcommand name lambda-list command-doc function-doc &body forms => command-function

Defines a new editor command. name is a usually string naming the new editor command which can invoked in the editor via Extended Command, and command-function is a symbol naming the new command function which can be called programmatically. The command-function symbol is interned in the current package.

lambda-list is the lambda list of the new command, which must have at least one argument which is usually denoted p, the prefix argument.

command-doc and function-doc should be strings giving detailed and brief descriptions of the new command respectively.

forms is the Lisp code for the command.

The name of the command must be a string, while the name of the associated command function must be a symbol. There are two ways in which name can be supplied. Most simply, name is given as a string, and the string is taken to be the name of the editor command. The symbol naming the command function is computed from that string: spaces are replaced with hyphens and alphabetic characters are uppercased, but otherwise the symbol name contains the same characters as the string with -COMMAND appended.

If a specific function name, different to the one defcommand derives itself, is required, then this can be supplied explicitly. To do this, name should be a list: its first element is the string used as the name of the command, while its second and last element is the symbol used to name the Lisp command function.

For example the following code defines an editor command, Move Five, which moves the cursor forward in an editor buffer by five characters.

(editor:defcommand "Move Five" (p)
   "Moves the current point forward five characters.
    Any prefix argument is ignored."      
   "Moves five characters forward."
   (editor:forward-character-command 5))
=>
MOVE-FIVE-COMMAND

The prefix argument p is not used, and is there simply because the lambda-list must have at least one element.

Use Meta+X Move Five to invoke the command.

As another example this command changes all the text in a writable buffer to be uppercase:

(editor:defcommand "Uppercase Buffer" (p) 
     "Uppercase the buffer contents" ""
  (declare (ignore p))
  (let* ((buffer (editor:current-buffer))
         (point (editor:buffer-point buffer))
         (start (editor:buffers-start buffer))
         (end (editor:buffers-end buffer)))
    (editor:set-current-mark start)
    (editor:move-point point end)
    (editor:uppercase-region-command nil)))

Having defined your new command, you can invoke it immediately by Meta+X Uppercase Buffer.

You could also call it programmatically:

(uppercase-buffer-command nil)

If you anticipate frequent interactive use of Uppercase Buffer you will want to bind it to a key. You can do this interactively for the current session using Bind Key. Also you can put something like this in your initialization file to establish the key binding for each new session:

(editor:bind-key "Uppercase Buffer" #("Control-x" "Meta-u"))

Then, entering Ctrl+X Meta+U will invoke the command.

Define Command Synonym Editor Command

Arguments: new-name, command-name
Key sequence: None

The command Define Command Synonym prompts for a string and an existing command name, and makes the string be a synonym for the existing command name.

6.3.3 Buffers

Each buffer that you manipulate interactively using editor commands is an object of type editor:buffer that can be used directly when programming the editor. Buffers contain an arbitrary number of editor:point objects, which are used when examining or modifying the text in a buffer (see 6.3.4 Points).

6.3.3.1 Buffer locking

Each buffer contains a lock that is used to prevent more than one thread from modifying the text, text properties or points within the buffer simultaneously. All of the exported editor functions (editor:insert-string, editor:move-point etc) claim this lock implicitly and are therefore atomic with respect to other such functions.

In situations where you want to make several changes as one atomic operation, use one of the macros editor:with-buffer-locked or editor:with-point-locked to lock the buffer for the duration of the operation. For example, if you want to delete the next character and replace it by a space:

(editor:with-buffer-locked ((editor:current-buffer))
  (editor:delete-next-character-command nil)
  (editor:insert-character (editor:current-point)
                           #\Space))

In addition, you sometimes want to examine the text in a buffer without changing it, but ensure that no other thread can modify it in the meantime. This can be achieved by locking the buffer using editor:with-buffer-locked or editor:with-point-locked and passing the for-modification argument as nil. For example, if you are computing the beginning and end of some portion of the text in a buffer and then performing some operation on that text, you may want to lock the buffer to ensure that no other threads can modify the text while your are processing it.

editor:with-buffer-locked Macro

editor:with-buffer-locked (buffer &key for-modification check-file-modification block-interrupts) &body body => values

Evaluates body while holding the lock in buffer. At most one thread can lock a buffer at a time and the macro waits until it can claim the lock.

If for-modification is non-nil (the default), the contents of buffer can be modified by body. If for-modification is nil, the contents of buffer cannot be modified until body returns and trying to do so from within body will signal an error. If the buffer is read-only and for-modification is non-nil, then an editor:editor-error is signaled. The status of the lock can be changed to for-modification (see editor:change-buffer-lock-for-modification). If the buffer is read-only, an editor:editor-error occurs if for-modification is t.

The macro editor:with-buffer-locked can be used recursively, but if the outermost use passed nil as the value of for-modification, then inner uses cannot pass non-nil as the value of for-modification, unless editor:change-buffer-lock-for-modification is used to change the lock status.

If check-file-modification is non-nil (the default) and the buffer is associated with a file and has not already been modified, then the modification time of the file is compared to the time that the file was last read. If the file is newer than the buffer, then the user is asked if they want to re-read the file into the buffer, and if they do then the file is re-read and the operations aborts. Otherwise, there is no check for the file being newer than the buffer.

If block-interrupts is non-nil, the body is evaluated with interrupts blocked. This is useful if the buffer may be modified by an interrupt function, or some interrupt function may end up waiting for another thread that may wait for the buffer lock, which would cause a deadlock. The default is not to block interrupts.

Note that using a non-nil value for block-interrupts is not the same as using the without-interrupts or without-preemption macros. It just stops the current thread from calling interrupt functions, so other threads might run while the body is being evaluated.

The values returned are those of body.

editor:with-point-locked Macro

editor:with-point-locked (point &key for-modification check-file-modification block-interrupts errorp) &body body  =>  values

Evaluates body while holding the lock in the buffer that is associated with point. In addition, the macro checks that point is valid and this check is atomic with respect to calls to the function editor:delete-point. The values of for-modification, check-file-modification and block-interrupts have the same meanings as for editor:with-buffer-locked.

The value of errorp determines the behavior when point is not valid. If errorp is non-nil, an error is signaled, otherwise nil is returned without evaluating body. The point may be invalid because it does not reference any buffer (that is, it has been deleted), or because its buffer was changed by another thread while the current thread was attempting to lock the buffer.

The values returned are those of body, or nil when errorp is nil and point is not valid.

editor:change-buffer-lock-for-modification Function

editor:change-buffer-lock-for-modification buffer &key check-file-modification force-modification => result

Changes the status of the lock in the buffer buffer to allow modification of the text. buffer must already be locked for non-modification by the current thread (that is, it must be dynamically within a editor:with-buffer-locked or editor:with-point-locked form with for-modification nil).

buffer
An editor buffer.
check-file-modification

A boolean.

force-modification

A boolean.

result
:buffer-not-locked, :buffer-out-of-date or :buffer-not-writable.

If check-file-modification is non-nil, the same test as described for editor:with-buffer-locked is performed, and if the file has been modified then :buffer-out-of-date is returned without changing anything (it does not prompt the user to re-read the file).

The default value of check-file-modification is t.

force-modification controls what happens if the buffer is read-only. If force-modification is nil, the function returns :buffer-not-writable and does nothing. If it is non-nil, the status is changed. The buffer remains read-only.

result is nil if the status of the locking was changed to for-modification, or if the status of the buffer lock was already for-modification. Otherwise, result is a keyword indicating why the status could not be changed. When result is non-nil, the status of the locking remains unchanged.

The returned value can be be one of:

:buffer-not-locked

The buffer is not locked by the current thread.

:buffer-not-writable

The buffer is not writable, and force-modification is nil.

:buffer-out-of-date

The file that is associated with the buffer was modified after it was read into the editor, the buffer is not modified, and check-file-modification is non-nil.

6.3.3.2 Buffer operations

editor:*buffer-list* Variable

Contains a list of all the buffers in the editor.

editor:current-buffer Function

editor:current-buffer

Returns the current buffer.

editor:buffer-name Function

editor:buffer-name buffer

Returns the name of buffer.

editor:window-buffer Function

editor:window-buffer window

Returns the buffer currently associated with window.

editor:buffers-start Function

editor:buffers-start buffer

Returns the starting point of buffer.

editor:buffers-end Function

editor:buffers-end buffer

Returns the end point of buffer.

editor:buffer-point Function

editor:buffer-point buffer

Returns the current point in buffer.

editor:use-buffer Macro

editor:use-buffer buffer &body forms

Makes buffer the current buffer during the evaluation of forms.

editor:buffer-from-name Function

editor:buffer-from-name name

Returns the buffer called name (which should be a string). If there is no buffer with that name, nil is returned.

editor:make-buffer Function

make-buffer name &key modes contents temporary base-name name-pattern

Creates or returns an existing buffer.

name should be a string or nil.

modes should be a list of strings naming modes. The first mode must be a major mode, and the rest minor modes. The default value of modes is the value of default-modes.

base-name should be a string or nil. If name and temporary are both nil then base-name must be a string.

contents should be a string, nil or t (default value nil).

temporary is a boolean (default value nil).

name-pattern should be a string (default value "~a<~a>").

When name is non-nil, it is the name of the buffer. If there is already a buffer with this name which is not temporary and the temporary argument is nil, make-buffer returns that buffer. Before doing so, it sets its contents to contents unless contents is t. When contents is nil, the buffer is made empty.

If name is nil or temporary is non-nil or a buffer with the name cannot be found, then a new buffer is made and returned. The buffer's contents is set to contents if contents is a string, and otherwise the buffer is made empty. The name of the buffer is set to name if name is non-nil.

If temporary is nil, the buffer is added to the internal tables of the editor. If name is non-nil, it is used. Otherwise make-buffer tries to use base-name. If there is already a buffer with this name, it constructs another name by:

(format nil name-pattern base-name n)

with different integers n until it constructs an unused name, which it uses as the buffer's name.

If temporary is non-nil, the buffer is not added to the internal tables. It is also marked as temporary, which mainly means that it does not have auto-save and backup files, and avoids calling general hooks when it is modified.

Notes:

Using :temporary t gives you a buffer that is 'yours', that is the editor does not do anything with it except in response to explicit calls from your code. Except when actually editing files, this is the most useful way of using buffers in most cases.

capi:editor-pane with the :buffer :temp initarg uses:

(make-buffer ... :temporary t)

editor:goto-buffer Function

editor:goto-buffer buffer in-same-window

Makes buffer the current buffer. If buffer is currently being shown in a window then the cursor is moved there. If buffer is not currently in a window and in-same-window is non-nil then it is shown in the current window, otherwise a new window is created for it.

editor:clear-undo Function

editor:clear-undo buffer

Clears any undo information in the buffer buffer.

6.3.4 Points

Locations within a buffer are recorded as editor:point objects. Each point remembers a character position within the buffer and all of the editor functions that manipulate the text of a buffer locate the text using one or more point objects (sometimes the current point).

A point's kind controls what happens to the point when text in the buffer is inserted or deleted.

:temporary points are for cases where you need read-only access to the buffer. They are like GNU Emacs "points". They have a lower overhead than the other kinds of point and do not need to be explicitly deleted, but do not use them in cases where you make a point, insert or delete text and then use the point again, since they do not move when the text is changed. Also, do not use them in cases where more than one thread can modify their buffer without locking the buffer first (see 6.3.3.1 Buffer locking).

:before-insert and :after-insert points are for cases where you need to make a point, insert or delete text and still use the point afterwards. They are like GNU Emacs "markers". The difference between these two kinds is what happens when text is inserted. For a point at position n from the start of the buffer, inserting len characters will leave the point at either position n or n+len according to the following table.

Editor point positions after text insertion
kindInsert at < nInsert at = nInsert at > n

:before-insert

n+len

n

n

:after-insert

n+len

n+len

n

When text is deleted, :before-insert and :after-insert points are treated the same: points <= the start of the deletion remain unchanged, points >= the end of the deletion are moved with the text and points within the deleted region are automatically deleted and cannot be used again.

All points with kind other than :temporary are stored within the data structures of the editor buffer so they can be updated when the text changes. A point can be removed from the buffer by editor:delete-point, and point objects are also destroyed if their buffer is killed.

editor:point-kind Function

editor:point-kind point

Returns the kind of the point, which is :temporary, :before-insert or :after-insert.

editor:current-point Function

editor:current-point

Returns the current point. See also editor:buffer-point.

editor:current-mark Function

editor:current-mark &optional pop-p no-error-p

Returns the current mark. If pop-p is t, the mark ring is rotated so that the previous mark becomes the current mark. If no mark is set and no-error-p is t, nil is returned; otherwise an error is signaled. The default for both of these optional arguments is nil.

editor:set-current-mark Function

editor:set-current-mark point

Sets the current mark to be point.

editor:point< Function

editor:point< point1 point2

Returns non-nil if point1 is before point2 in the buffer.

editor:point<= Function

editor:point<= point1 point2

Returns non-nil if point1 is before or at the same offset as point2 in the buffer.

editor:point> Function

editor:point> point1 point2

Returns non-nil if point1 is after point2 in the buffer.

editor:point>= Function

editor:point>= point1 point2

Returns non-nil if point1 is after or at the same offset as point2 in the buffer.

editor:copy-point Function

editor:copy-point point &optional kind new-point

Makes and returns a copy of point. The argument kind can take the value :before, :after, or :temporary. If new-point is supplied, the copied point is bound to that as well as being returned.

editor:delete-point Function

editor:delete-point point

Deletes the point point.

This should be done to any non-temporary point which is no longer needed.

editor:move-point Function

editor:move-point point new-position

Moves point to new-position, which should itself be a point.

editor:start-line-p Function

editor:start-line-p point

Returns t if point is immediately before the first character in a line, and nil otherwise.

editor:end-line-p Function

editor:end-line-p point

Returns t if point is immediately after the last character in a line, and nil otherwise.

editor:same-line-p Function

editor:same-line-p point1 point2

Returns t if point1 and point2 are on the same line, and nil otherwise.

editor:save-excursion Macro

editor:save-excursion &rest body

Saves the location of the point and the mark and restores them after completion of body. This restoration is accomplished even when there is an abnormal exit from body.

editor:with-point Macro

editor:with-point point-bindings &rest body

point-bindings is a list of bindings, each of the form (var point [kind]). Each variable var is bound to a new point which is a copy of the point point though possibly with a different kind, if kind is supplied. If kind is not supplied, then the new point has kind :temporary.

The forms of body are evaluated within the scope of the point bindings, and then the points in each variable var are deleted, as if by editor:delete-point. Each point var is deleted even if there was an error when evaluating body.

The main reason for using with-point to create non-temporary points is to allow body to modify the buffer while keeping these points up to date for later use within body.

6.3.5 Regular expression searching

editor:regular-expression-search Function

regular-expression-search point pattern &key forwardp prompt limit to-end brackets-limits => match-len, brackets-limits-vector

Search for pattern starting from point.

point must be an editor:point object or nil, meaning the result of calling editor:current-point.

pattern can be a string, a lw:precompiled-regexp (the result of lw:precompile-regexp), or nil.

forwardp is a boolean (default value t) specifying the direction to search.

prompt is a string used to prompt for a pattern when pattern is nil.

limit should be nil or an editor:point specifying a limit for the search.

to-end is a boolean (default value t), specifying whether to move the point to the end of the match when searching forward.

brackets-limits is a boolean specifying whether regular-expression-search should return a vector of brackets-limits.

regular-expression-search performs a search starting from point for the pattern, in the direction specified by forwardp, up to to limit if specified, or the buffer's end (when forwardp is non-nil) or the buffer's start (when forwardp is nil). If it succeeds, it then moves the point, either to the end of that match when both forwardp and to-end are non-nil (the default), or to the beginning of the match.

When pattern is non-nil it must be either a string or a precompiled pattern created with lw:precompile-regexp. If pattern is a string, regular-expression-search "precompiles" it before searching, so using a precompiled pattern is more efficient when using the same pattern repeatedly.

If pattern is nil, regular-expression-search first prompts for a pattern in the echo area, using the prompt. If pattern is non-nil, prompt is ignored.

Return values: If regular-expression-search is successful, it returns the length of the string that it matched, and if brackets-limits is non-nil, a second value which is a vector of the limits of the matches of each \( and \) pair in the pattern. The meaning of the vector is described in the manual entry for lw:find-regexp-in-string in the LispWorks® User Guide and Reference Manual.

Compatibility note: regular-expression-search was exported but not documented in LispWorks 6.1 and earlier versions. brackets-limits was introduced in LispWorks 7.0.

See also:
lw:find-regexp-in-string, lw:regexp-find-symbols and lw:precompile-regexp and 28.7 Regular expression syntax in the LispWorks® User Guide and Reference Manual.

6.3.6 The echo area

editor:message Function

editor:message string &rest args

A message is printed in the Echo Area. The argument string must be a string, which may contain formatting characters to be interpreted by format. The argument args consists of arguments to be printed within the string.

editor:clear-echo-area Function

editor:clear-echo-area &optional string force

Clears the Echo Area. The argument string is then printed in the Echo Area. If force is non-nil, the Echo Area is cleared immediately, with no delay. Otherwise, there may be a delay for the user to read any existing message.

6.3.7 Editor errors

Many editor commands and functions signal an error on failure (using editor:editor-error as described below). This causes the current operation to be aborted.

In many cases, the user will not want the operation to abort completely if one of the editor commands it uses is not successful. For example, the operation may involve a search, but some aspects of the operation should continue even if the search is not successful. To achieve this, the user can catch the editor:editor-error using a macro such as handler-case.

For example, one part of an application might involve moving forward 5 forms. If the current point cannot be moved forward five forms, generally the editor would signal an error. However, this error can be caught. The following trivial example shows how a new message could be printed in this situation, replacing the system message.

(editor:defcommand "Five Forms" (p)  
   "Tries to move the current point forward five forms,
    printing out an appropriate message on failure."  
   "Tries to move the current point forward five forms."
   (handler-case
      (editor:forward-form-command 5)
      (editor:editor-error (condition)
         (editor:message "could not move forward five"))))

editor:editor-error Function

editor:editor-error string &rest args

By default this prints a message in the Echo Area, sounds a beep, and exits to the top level of LispWorks, aborting the current operation. The argument string must be a string, which is interpreted as a control string by format. As with editor:message, args can consist of arguments to be processed within the control string.

The behavior is affected by break-on-editor-error.

6.3.8 Files

editor:find-file-buffer Function

editor:find-file-buffer pathname &optional check-function

Returns a buffer associated with the file pathname, reading the file into a new buffer if necessary. The second value returned is t if a new buffer is created, and nil otherwise. If the file already exists in a buffer, its consistency is first checked by means of check-function. If no value is supplied for check-function, editor:check-disk-version-consistent is used.

editor:set-buffer-name-directory-delimiters Function

editor:set-buffer-name-directory-delimiters &key prefix postfix separator display-p

The function editor:set-buffer-name-directory-delimiters controls the naming of buffers that are associated with files with the same name.

For a buffer associated with a file, the editor names the buffer using the file's name. Each buffer must have a unique name, so if you open several files with the same name (in different directories) then the editor has to choose different names for these buffers. By default, the editor resolves this situation by changing the name of all such buffers to be the file name followed by enough directory components to make it unique. The format of these unique names is:

filename prefix comp-1 separator comp-2 separatorpostfix

where comp-1, comp-2 … are directory components. Note that this feature is new in LispWorks 8.0 and in previous versions the editor just added <number> after the filename, where number is an increasing integer.

prefix sets the prefix to use. If it is nil (the default), the prefix is not changed. Otherwise, it must be a string or a character.

postfix sets the postfix to use. If it is nil (the default), the postfix is not changed. Otherwise, it must be a string or a character.

separator sets the directory separator to use. If it is nil (the default), the separator is not changed. Otherwise, it must be a string or a character.

Note that if you want any of prefix, postfix and separator to be empty, you need to pass an empty string.

display-p controls whether this name format is used. If it is non-nil, then the naming uses the format above. If it is nil, the naming uses the pre LispWorks 8.0 format with an integer suffix. If display-p is not supplied, its setting is not changed.

The initial settings are as if editor:set-buffer-name-directory-delimiters was called like this:

(set-buffer-name-directory-delimiters :prefix "<"
                                      :postfix ">"
                                      :separator "/"
                                      :display-p t)

When editor:set-buffer-name-directory-delimiters is called and whenever a buffer is created or deleted, the editor checks if it creates or eliminates a clash, and if it does then the editor recomputes the names of all the buffers that are affected.

For example, if you edit a file in the editor (see Find File ) with path /compa-1/compb-1/filename, then the buffer is named filename. Suppose you then edit another file /compa-1/compb-2/filename. Now the first buffer is renamed as filename<compb-1>, and the second buffer is named filename<compb-2>. If you close the first buffer, then the second buffer is renamed to filename because there is no longer a clash.

editor:fast-save-all-buffers Function

editor:fast-save-all-buffers &optional ask

Saves all modified buffers which are associated with a file. If ask is non-nil then confirmation is asked for before saving each buffer. If ask is not set, all buffers are saved without further prompting.

Unlike the editor command Save All Files this function can be run without any window interaction. It is thus suitable for use in code which does not intend to allow the user to leave any buffers unsaved, and from the console if it is necessary to save buffers without re-entering the full window system.

editor:check-disk-version-consistent Function

editor:check-disk-version-consistent pathname buffer

Checks that the date of the file pathname is not more recent than the last time buffer was saved. If pathname is more recent, the user is prompted on how to proceed. Returns t if there is no need to read the file from disk and nil if it should be read from disk.

editor:buffer-pathname Function

editor:buffer-pathname buffer

Returns the pathname of the file associated with buffer. If no file is associated with buffer, nil is returned.

editor:set-pathname-load-function Function

editor:set-pathname-load-function &key type load-function load-function-finder

Sets the function to use when loading files with type type.

editor:set-pathname-load-function affects what the command Load File does, and what loading in the LispWorks IDE using File > Load does. It does not affect what the Common Lisp load function does.

type is a string specifying the pathname-type of a filename.

In the description below, a load function means a function or a fbound symbol that takes one argument, a pathname designator, and "loads" it in some appropriate way.

If load-function-finder is non-nil, it must be a function that takes one argument, a pathname designator, and returns a load function or nil. If it returns a load function, this function is called to load the file. If it returns nil, the normal processing is done, which means calling load with the pathname designator without the type.

If load-function-finder is non-nil, load-function is ignored. Otherwise, load-function specifies the load function to use.

If both load-function-finder and load-function are nil, any previous setting for type is removed.

Each call to editor:set-pathname-load-function replaces the setting of any previous call with the same type.

For example, the ASDF integration example in (example-edit-file "misc/asdf-integration.lisp") uses the following call to cause the LispWorks IDE to load files with type "asd" by calling asdf:load-asd:

(editor:set-pathname-load-function :type "asd" :load-function 'asdf:load-asd)

Note: editor:set-pathname-load-function was added in LispWorks 8.0.

6.3.8.1 File encodings in the editor

In an application which writes editor buffers to file, you can do this to set the external format of a given buffer:

(setf (editor:buffer-external-format buffer) ef-spec)

You can also set a global default external format for editor buffers:

(setf (editor:variable-value 'editor:output-format-default
                             :global)
      ef-spec)

Then ef-spec will be used when a buffer itself does not have an external format.

See 3.5.3 Unicode and other file encodings for a full description of the editor's file encodings interface.

6.3.9 Inserting text

editor:insert-string Function

editor:insert-string point string &optional start end 

Inserts string at point in the current buffer. The arguments start and end specify the indices within string of the substring to be inserted. The default values for start and end are 0 and (length string) respectively.

editor:kill-ring-string Function

editor:kill-ring-string &optional index

Returns either the topmost string on the kill ring, or the string at index places below the top when index is supplied.

The editor kill ring stores the strings copied by the editor, in order to allow using them later.

editor:points-to-string Function

editor:points-to-string start end

Returns the string between the points start and end.

6.3.10 Indentation

editor:*indent-with-tabs* Variable

Controls whether indentation commands such as Indent and Indent Form insert whitespace using #\Space or #\Tab characters when changing the indentation of a line.

The initial value is nil, meaning that only the #\Space character is inserted.

A true value for editor:*indent-with-tabs* causes the indentation commands to insert #\Tab characters according to the value of spaces-for-tab and then pad with #\Space characters as needed.

6.3.11 Lisp

editor:*find-likely-function-ignores* Variable

Contains a list of symbols likely to be found at the beginning of a form (such as apply, funcall, defun, defmethod, defgeneric).

editor:*source-found-action* Variable

This variable determines how definitions found by the commands Find Source, Find Source for Dspec and Find Tag are shown. The value should be a list of length 2.

The first element controls the positioning of the definition: when t, show it at the top of the editor window; when a non-negative fixnum, position it that many lines from the top; and when nil, position it at the center of the window.

The second element can be :highlight, meaning highlight the definition, or nil, meaning do not highlight it.

The initial value of *source-found-action* is (nil :highlight).

6.3.12 Movement

editor:line-end Function

editor:line-end point

Moves point to be located immediately before the next newline character, or the end of the buffer if there are no following newline characters.

editor:line-start Function

editor:line-start point 

Moves point to be located immediately after the previous newline character, or the start of the buffer if there are no previous newline characters.

editor:character-offset Function

editor:character-offset point n 

Moves point forward n characters. If n is negative, point moves back n characters.

editor:word-offset Function

editor:word-offset point n

Moves point forward n words. If n is negative, point moves back n words.

editor:line-offset Function

editor:line-offset point n &optional to-offset 

Moves point n lines forward, to a location to-offset characters into the line. If n is negative, point moves back n lines. If to-offset is nil (its default value), an attempt is made to retain the current offset. An error is signaled if there are not n further lines in the buffer.

editor:form-offset Function

editor:form-offset point n &optional form depth

Moves point forward n Lisp forms. If n is negative, point moves back n forms. If form is t (its default value) then atoms are counted as forms, otherwise they are ignored. Before point is moved forward n forms, it first jumps out depth levels. The default value for depth is 0.

6.3.13 Prompting the user

The following functions can be used to prompt for some kind of input, which is generally typed into the Echo Area.

The following keyword arguments are common to a number of prompting functions.

:must-exist
Specifies whether the value that is input by the user must be an existing value or not. If :must-exist is non-nil, the user is prompted again if a non-existent value is input.
:default
Defines the default value that is selected if an empty string is input.
:default-string

Specifies the string that may be edited by the user (with Insert Parse Default).

:prompt
Defines the prompt that is written in the Echo Area. Most prompting functions have a default prompt that is used if no value is supplied for :prompt.
:help
Provides a help message that is printed if the user types "?".

editor:prompt-for-file Function

editor:prompt-for-file &key direction must-exist create-directories default default-string prompt help

Prompts for a file name, and returns a pathname.

:direction
You can specify direction :input (when expecting to read the file) or direction :output (when expecting to write the file). This controls the default value of must-exist, which is false for direction :output and true otherwise.
:create-directories

If create-directories is true, then the user is prompted to create any missing directories in the path she enters. The default is false for direction :output and true otherwise.

See above for an explanation of the other arguments.

editor:prompt-for-buffer Function

editor:prompt-for-buffer &key prompt must-exist default default-string help

Prompts for a buffer name, and returns the buffer. See above for an explanation of the keywords.

The default value of must-exist is t. If must-exist is nil and the buffer does not exist, it is created.

editor:prompt-for-integer Function

editor:prompt-for-integer &key prompt must-exist default help

Prompts for an integer. See above for an explanation of the keywords.

editor:prompt-for-string Function

editor:prompt-for-string &key prompt default default-string clear-echo-area help

Prompts for a string. No checking is done on the input. The keyword clear-echo-area controls whether or not the echo area is cleared (that is, whether the text being replaced is visible or not). The default for this keyword is t. See above for an explanation of the remaining keywords.

editor:prompt-for-variable Function

editor:prompt-for-variable &key must-exist prompt default default-string help

Prompts for an editor variable. See above for an explanation of the keywords. The default value of must-exist is t.

6.3.14 In-place completion

editor:complete-in-place Function

editor:complete-in-place complete-func &key extract-func skip-func insert-func

Performs a non-focus completion at the editor current point.

complete-func should be a function designator with signature:

complete-func string &optional user-arg => result

string should be a string to complete. user-arg is the second return value of extract-func, if this is not nil. result should be a list of items to be displayed in the list panel of the non-focus window.

extract-func must be a function designator with signature:

extract-func point => string, user-arg

point should be a Point object.

extract-func needs to move point to the beginning of the text that will be replaced if the user confirms. It should return two values: string is the string to complete, and user-arg can be any Lisp object. string is passed to the function complete-func, and if user-arg is non-nil it is also passed.

The default value of extract-func is a function which searches backwards until it finds a non-alphanumeric character, or the beginning of the buffer. It then moves its point argument forward to the next character. The function returns its first value string the string between this and the original location of the point, and it returns nil as the second value user-arg.

skip-func, if supplied, must be a function designator with signature:

skip-func point

point should be a Point object.

point will be used as the end of the region to replace by the completion. At the call to skip-func, the point is located at the same place as the point that was passed to extract-func (after it moved). skip-func needs to move point forward to the end of the text that should be replaced when the user wants to do the completion. If skip-func is not supplied, the end point is set to the current point.

insert-func, if supplied, must be a function designator with signature:

insert-func result string user-arg => string-to-use

result is the item selected by the user, string is the original string that was returned by extract-func, and user-arg is the second value returned by extract-func (regardless of whether this value is nil). It must return a string, string-to-use, which is inserted as the the completion.

If insert-func is not supplied, the completion item is inserted. If it is not a string it is first converted by prin1-to-string.

When editor:complete-in-place is called, it makes a copy of the current point and passes it to extract-func. It then copies this point and positions it either using skip-func or the current point. These two points define the text to be replaced. editor:complete-in-place then calls complete-func, and use the result to raise a non-focus window next to the current point. The interaction of this window is as described in the CAPI User Guide and Reference Manual.

Note: editor:complete-with-non-focus is a deprecated synonym for editor:complete-in-place.

6.3.15 Variables

editor:define-editor-variable Function

editor:define-editor-variable name value &optional documentation

Defines an editor variable.

name
Symbol naming the variable.
value
The value to assign to the variable.
mode
A string naming a mode.
documentation
A documentation string.

The macro editor:define-editor-variable defines a global editor variable. There is only one global value, so repeated uses of editor:define-editor-variable overwrite each other.

editor:define-editor-variable gives a readable value of defining a variable, and is recognized by the LispWorks source code location system. However variables can also be defined dynamically by calling (setf editor:variable-value). Variable values may be accessed by editor:variable-value.

A variable has only one string of documentation associated with it. editor:variable-value overwrites the existing documentation string, if there is any. You can see the documentation by the command Describe Editor Variable. It can can be accessed programmatically by editor:editor-variable-documentation.

Note: for backwards compatibility name can also be a string, which is converted to a symbol by uppercasing, replacing #\Space by #\-, and interning in the EDITOR package. This may lead to clashes and so you should use a symbol for name, not a string.

editor:define-editor-mode-variable Function

editor:define-editor-mode-variable name mode value &optional documentation

Defines an editor variable in the specified mode.

mode
A string naming a mode.
name, value
documentation
As for editor:define-editor-variable, except that editor:define-editor-mode-variable installs the documentation only if the editor variable does not already have any documentation.

editor:define-editor-mode-variable defines a variable in the specified mode. There is one value per variable per mode.

editor:define-editor-mode-variable gives a readable value of defining a variable in a mode, and is recognized by the LispWorks source code location system. However mode variables can also be defined dynamically by calling (setf editor:variable-value). Mode variable values may be accessed by editor:variable-value.

editor:editor-variable-documentation Function

editor:editor-variable-documentation editor-variable-name
editor-variable-name

A symbol naming an editor variable.

Returns the documentation associated with the editor variable, if any.

Note: For backwards compatibility a string editor-variable-name is also accepted, as described for editor:define-editor-variable.

editor:variable-value Accessor

editor:variable-value name &optional kind where

The reader returns the value of the editor variable name, where name is a symbol. An error is signaled if the variable is undefined. The argument kind can take the value :current, :buffer, :global or :mode. The default value of kind is :current.

When kind is :current the argument where should be nil (the default, meaning the current buffer) or an editor buffer object or the name of a buffer. The variable value for the specified buffer is returned or (if there is no current buffer) then the global variable value is returned.

kind can also be :buffer, and then where should be an editor buffer object.

For example, the code given below will, by default, return the value :ask-user.

(editor:variable-value 
  'editor:add-newline-at-eof-on-writing-file)

The value of variables may also be altered using the setter of this function. For example, the code given below will allow buffers to be saved to file without any prompt for a missing newline.

(setf
   (editor:variable-value 
      'editor:add-newline-at-eof-on-writing-file)
    nil)

editor:variable-value-if-bound Function

editor:variable-value-if-bound name &optional kind where

Returns the value of the variable name. If the variable is not bound, nil is returned. The arguments are as for editor:variable-value.

editor:buffer-value Function

editor:buffer-value buffer name &optional errorp

Accesses the value of the editor variable name in the buffer specified by buffer.

name should be a symbol and buffer should be a point object or a buffer object.

If the editor variable is undefined and errorp is true, an error is signaled. If the variable is undefined and errorp is false, nil is returned. The default value of errorp is nil.

6.3.16 Windows

editor:current-window Function

editor:current-window

Returns the current window.

editor:redisplay Function

editor:redisplay

Redisplays any window that appears to need it. In general, the contents of a window may not be redisplayed until there is an event to provoke it.

Note: editor:redisplay will update a modified editor buffer only when that buffer is the editor:current-buffer. Take care to call editor:redisplay in an appropriate context.

editor:window-text-pane Function

editor:window-text-pane window

Returns the capi:editor-pane associated with an editor window.

6.3.17 Faces

editor:face System Class

An instance of the system class editor:face describes the "face" of some text. It specifies the colors of the text and background, the font, and whether the text is bold, italic or underlined.

A editor:face is created by calling editor:make-face. It is used by various interface functions, for example hcl:code-coverage-set-editor-colors and hcl:write-string-with-properties. Note that in general you can use a face name, that is associated with a editor:face by editor:make-face, instead of the actual editor:face object.

editor:make-face Function

editor:make-face name &key if-exists foreground background font bold-p italic-p underline-p inverse-p documentation => face
name
A symbol.
if-exists
nil, :overwrite or :error.
foreground, background

CAPI colors or nil.

font
A graphics-ports:font object or nil.
bold-p, italic-p, underline-p, inverse-p

Booleans.

documentation
A string or nil.
face
A editor:face object.

The function editor:make-face returns a editor:face , either new or existing, and may associate it with name. editor:face objects are used by some interface function such as hcl:code-coverage-set-editor-colors and hcl:write-string-with-properties.

If name is non-nil, editor:make-face first checks if a editor:face with this name already exists. If it exists, then if-exists controls what happens:

nil
Return the existing editor:face object as it is (the default).
:overwrite
Reset the existing editor:face to default values and set its slots using the supplied keywords. The existing face is returned. This also causes Editor windows to update, and where this face is used the display will change accordingly.
:error
Signal an error.

If there is no existing editor:face, either because name is nil or because it has not been made yet, editor:make-face creates a new editor:face from the supplied keywords. If name is non-nil, the editor:face is associated with name, so future calls to editor:make-face with the same name will find it and name can be used in interface functions.

None of the keywords is required, and they all default to nil. For foreground, background and font, nil means use the default value, that is the color or font that the text would have drawn if the face was not applied.

foreground and background specify the colors to use. When they are non-nil, they must be a CAPI color. See the chapter "The Color System" in the CAPI User Guide and Reference Manual for description of colors.

font specifies the font to use. It must be a graphics-ports:font object, typically the result of graphics-ports:find-best-font. See "Portable font descriptions" in the "Drawing - Graphics Ports" chapter in the CAPI User Guide and Reference Manual for details. Note that the editor does not work properly with fonts of different height.

bold-p, italic-p and underline-p specify whether the text should be bold, italic or underlined respectively.

inverse-p specifies that the foreground and background colors are swapped, which causes the text to be drawn in the current background color using the current foreground color as the background. The effective background color is either the background argument if it is non-nil, or the default otherwise, and the same for the effective foreground color.

documentation is stored in the editor:face, and can be retrieved by calling cl:documentation with editor:face as the doc-type argument. cl:documentation can be called either with a editor:face object or with name.

6.3.18 Examples

6.3.18.1 Example 1

The following simple example creates a new editor command called Current Line.

(editor:defcommand "Current Line" (p)  
    "Computes the line number of the current point and
     prints it in the Echo Area"
    "Prints the line number of the current point"  
    (let* ((cpoint (editor:current-point))
           (svpoint (editor:copy-point cpoint))
           (count 0))
          (editor:beginning-of-buffer-command nil)
          (loop
             (if (editor:point> cpoint svpoint)
                 (return))
             (unless (editor:next-line-command nil)
                 (return))
             (incf count))
          (editor:move-point cpoint svpoint)
          (editor:message "Current Line Number: ~S " count)))
6.3.18.2 Example 2

This example creates a new editor command called Goto Line which moves the current point to the specified line number.

(editor:defcommand "Goto Line" (p)
   "Moves the current point to a specified line number.
    The number can either be supplied via the prefix
    argument, or, if this is nil, it is prompted for."
   "Moves the current point to a specified line number."
   (let ((line-number  
          (or p (editor:prompt-for-integer  
                 :prompt "Line number: "
                 :help "Type in the number of the line to
                  go to"))))
        (editor:beginning-of-buffer-command nil)
        (editor:next-line-command line-number)))
6.3.18.3 Example 3

The following example illustrates how text might be copied between buffers. First, string is set to all the text in from-buf. This text is then copied to the end of to-buf.

(defun copy-string (from-buf to-buf)
   (let ((string (editor:points-to-string  
           (editor:buffers-start from-buf)
           (editor:buffers-end from-buf))))
        (editor:insert-string (editor:buffers-end to-buf) string)))

To test this example, two buffers named t1 and t2 should be created. Then, to copy all the text from t1 to the end of t2:

(copy-string (editor:buffer-from-name "t1") 
             (editor:buffer-from-name "t2"))

Editor User Guide (Macintosh version) - 01 Dec 2021 19:35:12