From 7faf76c8c8633972c55f71737fa57ced4d56d0b9 Mon Sep 17 00:00:00 2001 From: Raymond Toy Date: Wed, 1 Apr 2020 16:34:42 -0700 Subject: [PATCH 1/3] Add original hemlock Scribe docs --- docs/hem/cim/aux-sys.mss | 694 ++++++ docs/hem/cim/cim.mss | 4039 +++++++++++++++++++++++++++++++ docs/hem/user/commands.mss | 822 +++++++ docs/hem/user/intro.mss | 1127 +++++++++ docs/hem/user/lisp.mss | 822 +++++++ docs/hem/user/mail.mss | 1343 ++++++++++ docs/hem/user/netnews.mss | 488 ++++ docs/hem/user/special-modes.mss | 738 ++++++ docs/hem/user/user.mss | 2003 +++++++++++++++ 9 files changed, 12076 insertions(+) create mode 100644 docs/hem/cim/aux-sys.mss create mode 100644 docs/hem/cim/cim.mss create mode 100644 docs/hem/user/commands.mss create mode 100644 docs/hem/user/intro.mss create mode 100644 docs/hem/user/lisp.mss create mode 100644 docs/hem/user/mail.mss create mode 100644 docs/hem/user/netnews.mss create mode 100644 docs/hem/user/special-modes.mss create mode 100644 docs/hem/user/user.mss diff --git a/docs/hem/cim/aux-sys.mss b/docs/hem/cim/aux-sys.mss new file mode 100644 index 0000000..945aa7a --- /dev/null +++ b/docs/hem/cim/aux-sys.mss @@ -0,0 +1,694 @@ +@comment{-*- Dictionary: /usr/lisp/scribe/hem/hem; Mode: spell; Package: Hemlock; Log: /usr/lisp/scribe/hem/hem-docs.log -*-} +@chapter (Auxiliary Systems) +This chapter describes utilities that some implementations of @hemlock may +leave unprovided or unsupported. + + +@section[Key-events] +@index(I/O) +@index[keyboard input] +@index[input, keyboard] +@index[mouse input] +@index[input, mouse] +@label[key-events] + +These routines are defined in the @f["EXTENSIONS"] package since other projects +have often used @hemlock's input translations for interfacing to CLX. + + +@subsection[Introduction] + +The canonical representation of editor input is a key-event structure. Users +can bind commands to keys (see section @ref[key-bindings]), which are non-zero +length sequences of key-events. A key-event consists of an identifying token +known as a @i[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 @f[#k] syntax. The following are some +examples: +@begin[programexample] + #k"C-u" + #k"Control-u" + #k"c-m-z" + #k"control-x meta-d" + #k"a" + #k"A" + #k"Linefeed" +@end[programexample] +This is convenient for use within code and in init files containing +@f[bind-key] calls. + +The @f[#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 @dash hyphen, double quote, open angle +bracket, close angle bracket, and space @dash 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. + + + +@subsection[Interface] + +All of the following routines and variables are exported from the "EXTENSIONS" +("EXT") package. + + +@defun[fun {define-keysym}, args {@i[keysym] @i[preferred-name] @rest @i[other-names]}] +This function establishes a mapping from @i[preferred-name] to @i[keysym] for +purposes of @f[#k] syntax. @i[Other-names] also map to @i[keysym], but the +system uses @i[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 @i[X Window System Protocol, MIT X Consortium Standard, X +Version 11, Release 4]. @f[translate-key-event] uses this knowledge to +determine what keysyms are modifier keysyms and what keysym stand for +alphabetic key-events. +@enddefun + + +@defun[fun {define-mouse-keysym}, args {@i[button] @i[keysym] @i[name] @i[shifted-bit] @i[event-key]}] +This function defines @i[keysym] named @i[name] for key-events representing the +X @i[button] cross the X @i[event-key] (@kwd[button-press] or +@kwd[button-release]). @i[Shifted-bit] is a defined modifier name that +@f[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. + +@i[Keysym] should be an one unspecified in @i[X Window System Protocol, MIT X +Consortium Standard, X Version 11, Release 4]. +@enddefun + + +@defun[fun {name-keysym}, args {@i[name]}] +This function returns the keysym named @i[name]. If @i[name] is unknown, this +returns @nil. +@enddefun + +@defun[fun {keysym-names}, args {@i[keysym]}] +This function returns the list of all names for @i[keysym]. If @i[keysym] is +undefined, this returns @nil. +@enddefun + +@defun[fun {keysym-preferred-name}, args {@i[keysym]}] +This returns the preferred name for @i[keysym], how it is typically printed. +If @i[keysym] is undefined, this returns @nil. +@enddefun + +@defun[fun {define-key-event-modifier}, args {@i[long-name] @i[short-name]}] +This establishes @i[long-name] and @i[short-name] as modifier names for +purposes of specifying key-events in @f[#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): +@begin[Itemize] +@f["Hyper"], @f["H"] + +@f["Super"], @f["S"] + +@f["Meta"], @f["M"] + +@f["Control"], @f["C"] + +@f["Shift"], @f["Shift"] + +@f["Lock"], @f["Lock"] +@end[Itemize] +@enddefun + + +@defvar[var {all-modifier-names}] +This variable holds all the defined modifier names. +@enddefvar + + +@defun[fun {define-clx-modifier}, args {@i[clx-mask] @i[modifier-name]}] +This function establishes a mapping from @i[clx-mask] to a defined key-event +@i[modifier-name]. @f[translate-key-event] and @f[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: +@begin[Itemize] +@f[(xlib:make-state-mask :mod-1) --> "Meta"] + +@f[(xlib:make-state-mask :control) --> "Control"] + +@f[(xlib:make-state-mask :lock) --> "Lock"] + +@f[(xlib:make-state-mask :shift) --> "Shift"] +@end[Itemize] +@enddefun + + +@defun[fun {make-key-event-bits}, args {@rest @i[modifier-names]}] +This function returns bits suitable for @f[make-key-event] from the supplied +@i[modifier-names]. If any name is undefined, this signals an error. +@enddefun + +@defun[fun {key-event-modifier-mask}, args {@i[modifier-name]}] +This function returns a mask for @i[modifier-name]. This mask is suitable for +use with @f[key-event-bits]. If @i[modifier-name] is undefined, this signals +an error. +@enddefun + +@defun[fun {key-event-bits-modifiers}, args {@i[bits]}] +This returns a list of key-event modifier names, one for each modifier +set in @i[bits]. +@enddefun + + +@defun[fun {translate-key-event}, args {@i[display] @i[scan-code] @i[bits]}] +This function translates the X @i[scan-code] and X @i[bits] to a key-event. +First this maps @i[scan-code] to an X keysym using @f[xlib:keycode->keysym] +looking at @i[bits] and supplying index as @f[1] if the X shift bit is on, +@f[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 +@begin[Itemize] +The keysym is defined. + +The X shift bit is off. + +The X lock bit is on. + +The X keysym represents a lowercase letter. +@end[Itemize] +then this maps the @i[scan-code] again supplying index as @f[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. +@enddefun + + +@defun[fun {translate-mouse-key-event}, args {@i[scan-code] @i[bits] @i[event-key]}] +This function translates the X button code, @i[scan-code], and modifier bits, +@i[bits], for the X @i[event-key] into a key-event. See +@f[define-mouse-keysym]. +@enddefun + +@defun[fun {make-key-event}, args {@i[object] @i[bits]}] +This function returns a key-event described by @i[object] with @i[bits]. +@i[Object] is one of keysym, string, or key-event. When @i[object] is a +key-event, this uses @f[key-event-keysym]. You can form @i[bits] with +@f[make-key-event-bits] or @f[key-event-modifier-mask]. +@enddefun + +@defun[fun {key-event-p}, args {@i[object]}] +This function returns whether @i[object] is a key-event. +@enddefun + +@defun[fun {key-event-bits}, args {@i[key-event]}] +This function returns the bits field of a @i[key-event]. +@enddefun + +@defun[fun {key-event-keysym}, args {@i[key-event]}] +This function returns the keysym field of a @i[key-event]. +@enddefun + +@defun[fun {char-key-event}, args {@i[character]}] +This function returns the key-event associated with @i[character]. You can +associate a key-event with a character by @f[setf]'ing this form. +@enddefun + +@defun[fun {key-event-char}, args {@i[key-event]}] +This function returns the character associated with @i[key-event]. You can +associate a character with a key-event by @f[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 @f[#k"C-h"], as +well as @f[#k"backspace"] would map to the Common Lisp character that causes a +backspace. +@enddefun + +@defun[fun {key-event-bit-p}, args {@i[key-event] @i[bit-name]}] +This function returns whether @i[key-event] has the bit set named by +@i[bit-name]. This signals an error if @i[bit-name] is undefined. +@enddefun + +@defmac[fun {do-alpha-key-events}, args +{(@i[var] @i[kind] @optional @i[result]) @mstar<@i[form]>}] + This macro evaluates each @i[form] with @i[var] bound to a key-event +representing an alphabetic character. @i[Kind] is one of @kwd[lower], +@kwd[upper], or @kwd[both], and this binds @i[var] to each key-event in order +as specified in @i[X Window System Protocol, MIT X Consortium Standard, X +Version 11, Release 4]. When @kwd[both] is specified, this processes lowercase +letters first. +@enddefmac + +@defun[fun {print-pretty-key}, args {@i[key] @optional @i[stream] @i[long-names-p]}] +This prints @i[key], a key-event or vector of key-events, in a user-expected +fashion to @i[stream]. @i[Long-names-p] indicates whether modifiers should +print with their long or short name. @i[Stream] defaults to +@var[standard-output]. +@enddefun + +@defun[fun {print-pretty-key-event}, args {@i[key-event] @optional @i[stream] @i[long-names-p]}] +This prints @i[key-event] to @i[stream] in a user-expected fashion. +@i[Long-names-p] indicates whether modifier names should appear using the long +name or short name. @i[Stream] defaults to @var[standard-output]. +@enddefun + + + +@section (CLX Interface) + +@subsection (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 "@f[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. + +@defvar[var {create-window-hook}] +This holds a function that @Hemlock calls when @f[make-window] executes under +CLX. @Hemlock passes the CLX display and the following arguments from +@f[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. +@enddefvar + +@defvar[var {delete-window-hook}] +This holds a function that @hemlock calls when @f[delete-window] executes under +CLX. @hemlock passes the CLX window and the @hemlock window to this function. +@enddefvar + +@defvar[var {random-typeout-hook}] +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 +@f[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. +@enddefvar + +@defvar[var {create-initial-windows-hook}] +This holds a function that @hemlock calls when it initializes the screen +manager and makes the first windows, typically windows for the @hid[Main] and +@hid[Echo Area] buffers. @hemlock passes the function a @hemlock device. +@enddefvar + + +@subsection (Entering and Leaving Windows) + +@defhvar[var "Enter Window Hook"] +When the mouse enters an editor window, @hemlock invokes the functions in this +hook. These functions take a @Hemlock window as an argument. +@enddefhvar + +@defhvar[var "Exit Window Hook"] +When the mouse exits an editor window, @hemlock invokes the functions in this +hook. These functions take a @Hemlock window as an argument. +@enddefhvar + + +@subsection (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. + +@defcom[com "Do Nothing"] +This does nothing as many times as you tell it. +@enddefcom + + +@section (Slave Lisps) +@index (Slave lisp interface functions) +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 @i[CMU Common Lisp User's Manual] +for the @f[remote] and @f[wire] packages. + + +@subsection (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. + +@defun[fun {create-slave}, args {@optional @i[name]}] +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 @i[name]. This generates a name if +@i[name] is @nil. In case the slave never connects, this will eventually +timeout and signal an editor-error. +@enddefun + +@defun[fun {get-current-eval-server}, args {@optional @i[errorp]}] +@defhvar1[var {Current Eval Server}] +This returns the server-information for the @hid[Current Eval Server] after +making sure it is valid. Of course, a slave Lisp can die at anytime. If this +variable is @nil, and @i[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 @i[Hemlock User's Manual]). +@enddefun + +@defun[fun {get-current-compile-server}] +@defhvar1[var {Current Compile Server}] +This returns the server-information for the @hid[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. +@enddefun + + +@subsection (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. + +@defun[fun {string-eval}, args {@i[string]}, keys {[server][package][context]}] +@defun1[fun {region-eval}, args {@i[region]}, keys {[server][package][context]}] +@defun1[fun {region-compile}, args {@i[region]}, keys {[server][package]}] +@f[string-eval] queues the evaluation of the form read from @i[string] on eval +server @i[server]. @i[Server] defaults to the result of +@f[get-current-server], and @i[string] is a simple-string. The evaluation +occurs with @var[package] bound in the slave to the package named by +@i[package], which defaults to @hid[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 @i[string] within this context as well. +@i[Context] is a string to use when reporting start and end notifications in +the @hid[Echo Area] buffer; it defaults to the concatenation of @f["evaluation +of "] and @i[string]. + +@f[region-eval] is the same as @f[string-eval], but @i[context] defaults +differently. If the user leaves this unsupplied, then it becomes a string +involving part of the first line of region. + +@f[region-compile] is the same as the above. @i[Server] defaults the same; it +does not default to @f[get-current-compile-server] since this compiles the +region into the slave Lisp's environment, to affect what you are currently +working on. +@enddefun + +@defun[fun {file-compile}, args {@i[file]}, + keys {[output-file][error-file][load][server]}, + morekeys {[package]}] +@defhvar1[var {Remote Compile File}, val {nil}] +This compiles @i[file] in a slave Lisp. When @i[output-file] is @true (the +default), 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 @hid[Remote Compile File] +to @nil, inhibits this. If @i[output-file] is non-@nil and not @true, then it +is the name of the binary file to write. The compilation occurs with +@var[package] bound in the slave to the package named by @i[package], which +defaults to @hid[Current Package] or the empty string; the empty string +indicates that the slave should evaluate the form in its current package. +@i[Error-file] is the file in which to record compiler output, and a @nil value +inhibits this file's creation. @i[Load] indicates whether to load the +resulting binary file, defaults to @nil. @i[Server] defaults to +@f[get-current-compile-server], but if this returns nil, then @i[server] +defaults to @f[get-current-server]. +@enddefun + +@subsection (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. + +@defun[fun {eval-form-in-server}, + args {@i[server-info] @i[string] @optional @i[package]}] + This function queues the evaluation of a form in the server associated with +@i[server-info] and waits for the results. The server @f[read]'s the form from +@i[string] with @var[package] bound to the package named by @i[package]. This +returns the results from the slave Lisp in a list of string values. You can +@f[read] from the strings or simply display them depending on the @f[print]'ing +of the evaluation results. + +@i[Package] defaults to @hid[Current Package]. If this is @nil, the server +uses the value of @var[package] in the server. + +While the slave executes the form, it binds @var[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. +@enddefun + +@defun[fun {eval-form-in-server-1}, + args {@i[server-info] @i[string] @optional @i[package]}] + This function calls @f[eval-form-in-server] and @f[read]'s the result in the +first string it returns. This result must be @f[read]'able in the editor's +Lisp. +@enddefun + + +@section (Spelling) +@index (Spelling checking) +@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. + +@defun[fun {maybe-read-spell-dictionary}, package {spell}] +This reads the default binary Ispell dictionary. Users must call this before +the following routines will work. +@enddefun + +@defun[fun {spell-read-dictionary}, package {spell}, args {@i[filename]}] +This adds entries to the dictionary from the lines in the file @i[filename]. +Dictionary files contain line oriented records like the following: +@begin[programexample] +entry1/flag1/flag2 +entry2 +entry3/flag1 +@end[programexample] +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. +@enddefun + +@defun[fun {spell-add-entry}, package {spell}, + args {@i[line] @optional @i[word-end]}] +This takes a line from a dictionary file, and adds the entry described by +@i[line] to the dictionary. @i[Word-end] defaults to the position of the first +slash character or the length of the line. @i[Line] is destructively modified. +@enddefun + +@defun[fun {spell-remove-entry}, package {spell}, args {@i[entry]}] +This removes entry, a simple-string, from the dictionary, so it will be an +unknown word. This destructively modifies @i[entry]. If it is a root word, +then all words derived with @i[entry] and its flags will also be deleted. If +@i[entry] is a word derived from some root word, then the root and any words +derived from it remain known words. +@enddefun + +@defun[fun {correct-spelling}, package {spell}, args {@i[word]}] +This checks the spelling of @i[word] and outputs the results. If this finds +@i[word] is correctly spelled due to some appropriate suffix on a root, it +generates output indicating this. If this finds @i[word] as a root entry, it +simply outputs that it found @i[word]. If this cannot find @i[word] at all, +then it outputs possibly correct close spellings. This writes to +@var[standard-output], and it calls @f[maybe-read-spell-dictionary] before +attempting any lookups. +@enddefun + +@defun[fun {spell-try-word}, package {spell}, args {@i[word] @i[word-len]}] +@defcon1[var {max-entry-length}, val {31}] +This returns an index into the dictionary if it finds @i[word] or an +appropriate root. @i[Word-len] must be inclusively in the range 2 through +@f[max-entry-length], and it is the length of @i[word]. @i[Word] must be +uppercase. This returns a second value indicating whether it found @i[word] +due to a suffix flag, @nil if @i[word] is a root entry. +@enddefun + +@defun[fun {spell-root-word}, package {spell}, args {@i[index]}] +This returns a copy of the root word at dictionary entry @i[index]. This index +is the same as returned by @f[spell-try-word]. +@enddefun + +@defun[fun {spell-collect-close-words}, package {spell}, args {@i[word]}] +This returns a list of words correctly spelled that are @i[close] to @i[word]. +@i[Word] must be uppercase, and its length must be inclusively in the range 2 +through @f[max-entry-length]. Close words are determined by the Ispell rules: +@begin[enumerate] +Two adjacent letters can be transposed to form a correct spelling. + +One letter can be changed to form a correct spelling. + +One letter can be added to form a correct spelling. + +One letter can be removed to form a correct spelling. +@end[enumerate] +@enddefun + +@defun[fun {spell-root-flags}, package {spell}, args {@i[index]}] +This returns a list of suffix flags as capital letters that apply to the +dictionary root entry at @i[index]. This index is the same as returned by +@f[spell-try-word]. +@enddefun + + +@section (File Utilities) +Some implementations of @hemlock provide extensive directory editing commands, +@hid[Dired], including a single wildcard feature. An asterisk denotes a +wildcard. + +@defun[fun {copy-file}, package {dired}, + args {@i[spec1] @i[spec2]}, keys {[update][clobber][directory]}] + This function copies @i[spec1] to @i[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 @i[spec1] and @i[spec2] are both directories, this recursively copies the +files and subdirectory structure of @i[spec1]; if @i[spec2] is in the +subdirectory structure of @i[spec1], the recursion will not descend into it. +Use @f["/spec1/*"] to copy only the files from @i[spec1] to directory +@i[spec2]. + +If @i[spec2] is a directory, and @i[spec1] is a file, then this copies +@i[spec1] into @i[spec2] with the same @f[pathname-name]. + +When @kwd[update] is non-@nil, then the copying process only copies files if the +source is newer than the destination. + +When @kwd[update] and @kwd[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 @kwd[directory], it is a list of pathnames, directories +excluded, and @i[spec1] is a pattern containing one wildcard. This then copies +each of the pathnames whose @f[pathname-name] matches the pattern. @i[Spec2] +is either a directory or a pathname whose @f[pathname-name] contains a +wildcard. +@enddefun + +@defun[fun {rename-file}, package {dired}, + args {@i[spec1] @i[spec2]}, keys {[clobber][directory]}] + This function renames @i[spec1] to @i[spec2]. It accepts a single wildcard in +the filename portion of the specification, and @i[spec2] may be a directory +with the destination specification resulting in the merging of @i[spec2] with +@i[spec1]. If @kwd[clobber] is @nil, and @i[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 @kwd[directory], it is a list of pathnames, directories +excluded, and @i[spec1] is a pattern containing one wildcard. This then copies +each of the pathnames whose @f[pathname-name] matches the pattern. @i[Spec2] +is either a directory or a pathname whose @f[pathname-name] contains a +wildcard. +@enddefun + +@defun[fun {delete-file}, package {dired}, + args {@i[spec]}, keys {[recursive][clobber]}] + This function deletes @i[spec]. It accepts a single wildcard in the filename +portion of the specification, and it asks for confirmation on each file if +@kwd[clobber] is @nil. If @kwd[recursive] is non-@nil, then @i[spec] may be a +directory to recursively delete the entirety of the directory and its +subdirectory structure. An empty directory may be specified without +@kwd[recursive] being non-@nil. Specify directories with the trailing +slash. +@enddefun + +@defun[fun {find-file}, package {dired}, + args {@i[name] @optional @i[directory] @i[find-all]}] + This function finds the file with @f[file-namestring] @i[name], recursively +looking in @i[directory]. If @i[find-all] is non-@nil (defaults to @nil), then +this continues searching even after finding a first occurrence of file. +@i[Name] may contain a single wildcard, which causes @i[find-all] to default to +@true instead of @nil. +@enddefun + +@defun[fun {make-directory}, package {dired}, args {@i[name]}] +This function creates the directory with @i[name]. If it already exists, this +signals an error. +@enddefun + +@defun[fun {pathnames-from-pattern}, package {dired}, + args {@i[pattern] @i[files]}] +This function returns a list of pathnames from the list @i[files] whose +@f[file-namestring]'s match @i[pattern]. @i[Pattern] must be a non-empty +string and contain only one asterisk. @i[Files] contains no directories. +@enddefun + +@defvar[var {update-default}, package {dired}] +@defvar1[var {clobber-default}, package {dired}] +@defvar1[var {recursive-default}, package {dired}] +These are the default values for the keyword arguments above with corresponding +names. These default to @nil, @true, and @nil respectively. +@enddefvar + +@defvar[var {report-function}, package {dired}] +@defvar1[var {error-function}, package {dired}] +@defvar1[var {yesp-function}, package {dired}] +These are the function the above routines call to report progress, signal +errors, and prompt for @i[yes] or @i[no]. These all take format strings and +arguments. +@enddefvar + + +@defun[fun {merge-relative-pathnames}, args {@i[pathname] @i[default-directory]}] +This function merges @i[pathname] with @i[default-directory]. If @i[pathname] +is not absolute, this assumes it is relative to @i[default-directory]. The +result is always a directory pathname. +@enddefun + +@defun[fun {directoryp}, args {@i[pathname]}] +This function returns whether @i[pathname] names a directory: it has no name +and no type fields. +@enddefun + + +@section (Beeping) + +@defun[fun {hemlock-beep}] +@Hemlock binds @f[system:*beep-function*] to this function to beep the device. +It is different for different devices. +@enddefvar + +@defhvar[var "Bell Style", val {:border-flash}] +@defhvar1[var "Beep Border Width", val {20}] +@hid[Bell Style] determines what @var[hemlock-beep] does in @hemlock under CLX. +Acceptable values are @kwd[border-flash], @kwd[feep], +@kwd[border-flash-and-feep], @kwd[flash], @kwd[flash-and-feep], and @nil (do +nothing). + +@hid[Beep Border Width] is the width in pixels of the border flashed by border +flash beep styles. +@enddefhvar diff --git a/docs/hem/cim/cim.mss b/docs/hem/cim/cim.mss new file mode 100644 index 0000000..03ae31f --- /dev/null +++ b/docs/hem/cim/cim.mss @@ -0,0 +1,4039 @@ +@make[Manual] @comment{-*- Dictionary: /afs/cs/project/clisp/docs/hem/hem; Mode: spell; Package: Hemlock; Log: /usr/lisp/scribe/hem/hem-docs.log -*-} +@Device[postscript] +@style(FontFamily = TimesRoman) +@Style(Spacing = 1.2 lines) +@Style(StringMax = 5000) +@style(Hyphenation = On) +@style(Date="March 1952") +@use(database "/afs/cs/project/clisp/docs/database/") +@Style [DoubleSided] +@Libraryfile[ArpaCredit] +@Libraryfile[Hem] +@Libraryfile[Spice] +@Libraryfile[Uttir] + +@String(ReportTitle "Hemlock Command Implementor's Manual") + +@comment< +@begin[TitlePage] +@begin[TitleBox] +> +@blankspace(1.3inches) +@heading[Hemlock Command Implementor's Manual] + +@center[ +@b +@b + +@b<@value[date]> + +@b +] +@comment<@end[TitleBox]> +@blankspace(2lines) +@begin[Center] +School of Computer Science +Carnegie Mellon University +Pittsburgh, PA 15213 +@end[Center] + +@blankspace(2lines) +@begin[Center] +This is a revised version of Technical Report CMU-CS-87-159. +@end[Center] +@heading[Abstract] +@begin(Text, indent 0) +This document describes how to write commands for the @Hemlock text editor, as +of version M3.2. @Hemlock is a customizable, extensible text editor whose +initial command set closely resembles that of ITS/TOPS-20 @Emacs. @Hemlock is +written in the CMU Common Lisp and has been ported to other implementations. +@end(Text) + +@blankspace(0.5in) +@begin[ResearchCredit] +@arpacredit[Contract=Basic87-90] +@end[ResearchCredit] +@comment<@end[TitlePage]> + + +@commandstring(dash = "@Y[M]") + + +@Tabclear + +@chapter(Introduction) + + @hemlock is a text editor which follows in the tradition of editors +such as EMACS and the Lisp Machine editor ZWEI. In its basic form, +@hemlock has almost the same command set as EMACS, and similar +features such as multiple buffers and windows, extended commands, +and built in documentation. + +Both user extensions and the original commands are written in Lisp, +therefore a command implementor will have a working knowledge of this +language. Users not familiar with Lisp need not despair however. Many +users of Multics EMACS, another text editor written in Lisp, came to learn +Lisp simply for the purpose of writing their own editor extensions, and +found, to their surprise, that it was really pretty easy to write simple +commands. + +This document describes the Common Lisp functions, macros and data structures +that are used to implement new commands. The basic editor consists of a set of +Lisp utility functions for manipulating buffers and the other data structures +of the editor as well as handling the display. All user level commands are +written in terms of these functions. To find out how to define commands see +chapter @ref[commands]. + +@chapter(Representation of Text) +@index (Lines) +@section(Lines) +In @hemlock all text is in some @i[line]. Text is broken into lines wherever +it contains a newline character; newline characters are never stored, but are +assumed to exist between every pair of lines. The implicit newline character +is treated as a single character by the text primitives. + +@defun[fun {linep}, args {@i[line]}] +This function returns @true if @i[line] is a @f[line] object, otherwise @nil. +@enddefun + +@defun[fun {line-string}, args {@i[line]}] +Given a @i(line), this function returns as a simple string the characters in +the line. This is @f[setf]'able to set the @f[line-string] to any string that +does not contain newline characters. It is an error to destructively modify +the result of @f[line-string] or to destructively modify any string after the +@f[line-string] of some line has been set to that string. +@enddefun + +@defun[fun {line-previous}, args {@i[line]}] +@defun1[fun {line-next}, args {@i[line]}] +Given a @i(line), @f[line-previous] returns the previous line or @nil if there +is no previous line. Similarly, @f[line-next] returns the line following +@i[line] or @nil. +@enddefun + +@defun[fun {line-buffer}, args {@i[line]}] +This function returns the buffer which contains this @i(line). Since a +line may not be associated with any buffer, in which case @f[line-buffer] +returns @nil. +@enddefun + +@defun[fun {line-length}, args {@i[line]}] +This function returns the number of characters in the @i(line). This excludes +the newline character at the end. +@enddefun + +@defun[fun {line-character}, args {@i[line] @i[index]}] +This function returns the character at position @i[index] within @i[line]. It +is an error for @i[index] to be greater than the length of the line or less +than zero. If @i[index] is equal to the length of the line, this returns a +@f[#\newline] character. +@enddefun + +@defun[fun {line-plist}, args {@i[line]}] +This function returns the property-list for @i[line]. @f[setf], @f[getf], +@f[putf] and @f[remf] can be used to change properties. This is typically used +in conjunction with @f[line-signature] to cache information about the line's +contents. +@enddefun + +@defun[fun {line-signature}, args {@i[line]}] +This function returns an object that serves as a signature for a @i[line]'s +contents. It is guaranteed that any modification of text on the line will +result in the signature changing so that it is not @f[eql] to any previous +value. The signature may change even when the text remains unmodified, but +this does not happen often. +@enddefun + + +@section(Marks) +@label[marks] +@index (Marks) +A mark indicates a specific position within the text represented by a line and +a character position within that line. Although a mark is sometimes loosely +referred to as pointing to some character, it in fact points between +characters. If the @f[charpos] is zero, the previous character is the newline +character separating the previous line from the mark's @f[line]. If the +charpos is equal to the number of characters in the line, the next character is +the newline character separating the current line from the next. If the mark's +line has no previous line, a mark with @f[charpos] of zero has no previous +character; if the mark's line has no next line, a mark with @f[charpos] equal +to the length of the line has no next character. + +This section discusses the very basic operations involving marks, but a lot of +@hemlock programming is built on altering some text at a mark. For more +extended uses of marks see chapter @ref[doing-stuff]. + + +@subsection(Kinds of Marks) +@index (Permanent marks) +@index (Temporary marks) +A mark may have one of two lifetimes: @i[temporary] or @i[permanent]. +Permanent marks remain valid after arbitrary operations on the text; temporary +marks do not. Temporary marks are used because less bookkeeping overhead is +involved in their creation and use. If a temporary mark is used after the text +it points to has been modified results will be unpredictable. Permanent marks +continue to point between the same two characters regardless of insertions and +deletions made before or after them. + +There are two different kinds of permanent marks which differ only in their +behavior when text is inserted @i(at the position of the mark); text is +inserted to the left of a @i[left-inserting] mark and to the right of +@i[right-inserting] mark. + + +@subsection(Mark Functions) +@defun[fun {markp}, args {@i[mark]}] +This function returns @true if @i[mark] is a @f[mark] object, otherwise @nil. +@enddefun + +@defun[fun {mark-line}, args {@i[mark]}] +This function returns the line to which @i(mark) points. +@enddefun + +@defun[fun {mark-charpos}, args {@i[mark]}] +This function returns the character position of the character after @i(mark). +If @i[mark]'s line has no next line, this returns the length of the line as +usual; however, there is actually is no character after the mark. +@enddefun + +@defun[fun {mark-kind}, args {@i[mark]}] +This function returns one of @kwd[right-inserting], @kwd[left-inserting] or +@kwd[temporary] depending on the mark's kind. A corresponding @f[setf] form +changes the mark's kind. +@enddefun + +@defun[fun {previous-character}, args {@i[mark]}] +@defun1[fun {next-character}, args {@i[mark]}] +This function returns the character immediately before (after) the position of +the @i[mark], or @nil if there is no previous (next) character. These +characters may be set with @f[setf] when they exist; the @f[setf] methods for +these forms signal errors when there is no previous or next character. +@enddefun + + +@subsection(Making Marks) +@defun[fun {mark}, args {@i[line] @i[charpos] @optional @i[kind]}] +This function returns a mark object that points to the @i(charpos)'th character +of the @i(line). @i(Kind) is the kind of mark to create, one of +@kwd[temporary], @kwd[left-inserting], or @kwd[right-inserting]. The default +is @kwd[temporary]. +@enddefun + +@defun[fun {copy-mark}, args {@i[mark] @optional @i[kind]}] +This function returns a new mark pointing to the same position and of the same +kind, or of kind @i[kind] if it is supplied. +@enddefun + +@defun[fun {delete-mark}, args {@i[mark]}] +This function deletes @i(mark). Delete any permanent marks when you are +finished using it. +@enddefun + +@Defmac[Fun {with-mark}, Args + {(@Mstar<(@i[mark] @i[pos] @mopt[@i(kind)])>) @Mstar<@i[form]>}] + This macro binds to each variable @i[mark] a mark of kind @i[kind], which +defaults to @kwd[temporary], pointing to the same position as the mark @i[pos]. +On exit from the scope the mark is deleted. The value of the last @i[form] is +the value returned. +@enddefmac + + +@subsection(Moving Marks) +@index(Moving marks) +These functions destructively modify marks to point to new positions. Other +sections of this document describe mark moving routines specific to higher +level text forms than characters and lines, such as words, sentences, +paragraphs, Lisp forms, etc. + +@defun[fun {move-to-position}, args {@i[mark] @i[charpos] @optional @i[line]}] +This function changes the @i(mark) to point to the given character position on +the line @i(line). @i(Line) defaults to @i[mark]'s line. +@enddefun + +@defun[fun {move-mark}, args {@i[mark] @i[new-position]}] +This function moves @i[mark] to the same position as the mark @i[new-position] +and returns it. +@enddefun + +@defun[fun {line-start}, args {@i[mark] @optional @i[line]}] +@defun1[fun {line-end}, args {@i[mark] @optional @i[line]}] +This function changes @i[mark] to point to the beginning or the end of @i(line) +and returns it. @i[Line] defaults to @i[mark]'s line. +@enddefun + +@defun[fun {buffer-start}, args {@i[mark] @optional @i[buffer]}] +@defun1[fun {buffer-end}, args {@i[mark] @optional @i[buffer]}] +These functions change @i[mark] to point to the beginning or end of @i[buffer], +which defaults to the buffer @i[mark] currently points into. If @i[buffer] is +unsupplied, then it is an error for @i[mark] to be disassociated from any +buffer. +@enddefun + +@defun[fun {mark-before}, args {@i[mark]}] +@defun1[fun {mark-after}, args {@i[mark]}] +These functions change @i[mark] to point one character before or after the +current position. If there is no character before/after the current position, +then they return @nil and leave @i[mark] unmodified. +@enddefun + +@defun[fun {character-offset}, args {@i[mark] @i[n]}] +This function changes @i[mark] to point @i[n] characters after (@i[n] before if +@i[n] is negative) the current position. If there are less than @i[n] +characters after (before) the @i[mark], then this returns @nil and @i[mark] is +unmodified. +@enddefun + +@defun[fun {line-offset}, args {@i[mark] @i[n] @optional @i[charpos]}] +This function changes @i[mark] to point @i[n] lines after (@i[n] before if +@i[n] is negative) the current position. The character position of the +resulting mark is +@lisp +(min (line-length @i(resulting-line)) (mark-charpos @i(mark))) +@endlisp +if @i[charpos] is unspecified, or +@lisp +(min (line-length @i(resulting-line)) @i(charpos)) +@endlisp +if it is. As with @t(character-offset), if there are not @i[n] lines then +@nil is returned and @i[mark] is not modified. +@enddefun + + +@section(Regions) +@index (Regions) +A region is simply a pair of marks: a starting mark and an ending mark. +The text in a region consists of the characters following the starting +mark and preceding the ending mark (keep in mind that a mark points between +characters on a line, not at them). + +By modifying the starting or ending mark in a region it is possible to +produce regions with a start and end which are out of order or even in +different buffers. The use of such regions is undefined and may +result in arbitrarily bad behavior. + + +@subsection(Region Functions) +@defun[fun {region}, args {@i[start] @i[end]}] +This function returns a region constructed from the marks @i[start] and +@i[end]. It is an error for the marks to point to non-contiguous lines or for +@i(start) to come after @i(end). +@enddefun + +@defun[fun {regionp}, args {@i[region]}] +This function returns @true if @i[region] is a @f[region] object, otherwise +@nil. +@enddefun + +@defun[fun {make-empty-region}] +This function returns a region with start and end marks pointing to the start +of one empty line. The start mark is a @kwd[right-inserting] mark, and the end +is a @kwd[left-inserting] mark. +@enddefun + +@defun[fun {copy-region}, args {@i[region]}] +This function returns a region containing a copy of the text in the specified +@i[region]. The resulting region is completely disjoint from @i[region] with +respect to data references @dash marks, lines, text, etc. +@enddefun + +@defun[fun {region-to-string}, args {@i[region]}] +@defun1[fun {string-to-region}, args {@i[string]}] +These functions coerce regions to Lisp strings and vice versa. Within the +string, lines are delimited by newline characters. +@enddefun + +@defun[fun {line-to-region}, args {@i[line]}] +This function returns a region containing all the characters on @i[line]. The +first mark is @kwd[right-inserting] and the last is @kwd[left-inserting]. +@enddefun + +@defun[fun {region-start}, args {@i[region]}] +@defun1[fun {region-end}, args {@i[region]}] +This function returns the start or end mark of @i(region). +@enddefun + +@defun[fun {region-bounds}, args {@i[region]}] +This function returns as multiple-values the starting and ending marks of +@i[region]. +@enddefun + +@defun[fun {set-region-bounds}, args {@i[region] @i[start] @i[end]}] +This function sets the start and end of region to @i[start] and @i[end]. It is +an error for @i[start] to be after or in a different buffer from @i[end]. +@enddefun + +@index(Counting lines and characters) +@defun[fun {count-lines}, args {@i[region]}] +This function returns the number of lines in the @i(region), first and last +lines inclusive. A newline is associated with the line it follows, thus a +region containing some number of non-newline characters followed by one newline +is one line, but if a newline were added at the beginning, it would be two +lines. +@enddefun + +@defun[fun {count-characters}, args {@i[region]}] +This function returns the number of characters in a given @i(region). This +counts line breaks as one character. +@enddefun + +@defun[fun {check-region-query-size}, args {@i[region]}] +@defhvar1[var {Region Query Size}, val {30}] +@f[check-region-query-size] counts the lines in @i[region], and if their number +exceeds the @hid[Region Query Size] threshold, it prompts the user for +confirmation. This should be used in commands that perform destructive +operations and are not undoable. If the user responds negatively, then this +signals an editor-error, aborting whatever command was in progress. +@enddefun + + + +@chapter(Buffers) +@index (Buffers) +@label[buffers] +A buffer is an environment within @hemlock consisting of: +@begin(enumerate) +A name. + +A piece of text. + +A current focus of attention, the point. + +An associated file (optional). + +A write protect flag. + +Some variables (page @pageref[variables]). + +Some key bindings (page @pageref[key-bindings]). + +Some collection of modes (page @pageref[modes]). + +Some windows in which it is displayed (page @pageref[windows]). + +A list of modeline fields (optional). +@end(enumerate) + + +@section (The Current Buffer) +@index (Current buffer) +@defun[fun {current-buffer}] +@defhvar1[var {Set Buffer Hook}] +@defhvar1[var {After Set Buffer Hook}] +@f[current-buffer] returns the current buffer object. Usually this is the +buffer that @funref[current-window] is displaying. This value may be changed +with @f[setf], and the @f[setf] method invokes @hid[Set Buffer Hook] before the +change occurs with the new value. After the change occurs, the method invokes +@hid[After Set Buffer Hook] with the old value. +@enddefun + +@defun[fun {current-point}] +This function returns the @f[buffer-point] of the current buffer. +This is such a common idiom in commands that it is defined despite +its trivial implementation. +@enddefun + +@defun[fun {current-mark}] +@defun1[fun {pop-buffer-mark}] +@defun1[fun {push-buffer-mark}, args {@i[mark] @optional @i[activate-region]}] +@index(Buffer mark stack) +@index(Mark stack) +@label(mark-stack) +@f[current-mark] returns the top of the current buffer's mark stack. There +always is at least one mark at the beginning of the buffer's region, and all +marks returned are right-inserting. + +@f[pop-buffer-mark] pops the current buffer's mark stack, returning the mark. +If the stack becomes empty, this pushes a new mark on the stack pointing to the +buffer's start. This always deactivates the current region (see section +@ref[active-regions]). + +@f[push-buffer-mark] pushes @i[mark] into the current buffer's mark stack, +ensuring that the mark is right-inserting. If @i[mark] does not point into the +current buffer, this signals an error. Optionally, the current region is made +active, but this never deactivates the current region (see section +@ref[active-regions]). @i[Mark] is returned. +@enddefun + +@defvar[var {buffer-list}] +This variable holds a list of all the buffer objects made with @f[make-buffer]. +@enddefvar + +@defvar[var {buffer-names}] +This variable holds a @f[string-table] (page @pageref(string-tables)) of all the +names of the buffers in @var[buffer-list]. The values of the entries are the +corresponding buffer objects. +@enddefvar + +@defvar[var {buffer-history}] +This is a list of buffer objects ordered from those most recently selected to +those selected farthest in the past. When someone makes a buffer, an element +of @hid[Make Buffer Hook] adds this buffer to the end of this list. When +someone deletes a buffer, an element of @hid[Delete Buffer Hook] removes the +buffer from this list. Each buffer occurs in this list exactly once, but it +never contains the @var[echo-area-buffer]. +@enddefvar + +@defun[fun {change-to-buffer}, args {@i[buffer]}] +This switches to @i[buffer] in the @f[current-window] maintaining +@f[buffer-history]. +@enddefun + +@defun[fun {previous-buffer}] +This returns the first buffer from @var[buffer-history] that is not the +@f[current-buffer]. If none can be found, then this returns @nil. +@enddefun + + +@section(Buffer Functions) +@defun[fun {make-buffer}, args {@i[name]}, keys {[modes][modeline-fields][delete-hook]}] +@defhvar1[var {Make Buffer Hook}] +@defhvar1[var {Default Modeline Fields}] +@f[make-buffer] creates and returns a buffer with the given @i(name). If a +buffer named @i[name] already exists, @nil is returned. @i[Modes] is a list of +modes which should be in effect in the buffer, major mode first, followed by +any minor modes. If this is omitted then the buffer is created with the list +of modes contained in @hvarref[Default Modes]. @i[Modeline-fields] is a list +of modeline-field objects (see section @ref[modelines]) which may be @nil. +@f[delete-hook] is a list of delete hooks specific to this buffer, and +@f[delete-buffer] invokes these along with @hid[Delete Buffer Hook]. + +Buffers created with @f[make-buffer] are entered into the list +@var[buffer-list], and their names are inserted into the +string-table @var[buffer-names]. When a buffer is created the hook +@hid[Make Buffer Hook] is invoked with the new buffer. +@enddefun + +@defun[fun {bufferp}, args {@i[buffer]}] +Returns @true if @i[buffer] is a @f[buffer] object, otherwise @nil. +@enddefun + +@defun[fun {buffer-name}, args {@i[buffer]}] +@defhvar1[var {Buffer Name Hook}] +@f[buffer-name] returns the name, which is a string, of the given @i(buffer). +The corresponding @f[setf] method invokes @hid[Buffer Name Hook] with +@i[buffer] and the new name and then sets the buffer's name. When the user +supplies a name for which a buffer already exists, the @f[setf] method signals +an error. +@enddefun + +@defun[fun {buffer-region}, args {@i[buffer]}] +Returns the @i[buffer]'s region. This can be set with @f[setf]. Note, this +returns the region that contains all the text in a buffer, not the +@funref[current-region]. +@enddefun + +@defun[fun {buffer-pathname}, args {@i[buffer]}] +@defhvar1[var {Buffer Pathname Hook}] +@f[buffer-pathname] returns the pathname of the file associated with +the given @i(buffer), or nil if it has no associated file. This is +the truename of the file as of the most recent time it was read or +written. There is a @f[setf] form to change the pathname. When the +pathname is changed the hook @hid[Buffer Pathname Hook] is invoked +with the buffer and new value. +@enddefun + +@defun[fun {buffer-write-date}, args {@i[buffer]}] +Returns the write date for the file associated with the buffer in universal +time format. When this the @f[buffer-pathname] is set, use @f[setf] to set +this to the corresponding write date, or to @nil if the date is unknown or +there is no file. +@enddefun + +@defun[fun {buffer-point}, args {@i[buffer]}] +Returns the mark which is the current location within @i[buffer]. To +move the point, use @f[move-mark] or @funref[move-to-position] rather +than setting @f[buffer-point] with @f[setf]. +@enddefun + +@defun[fun {buffer-mark}, args {@i[buffer]}] +@index(Buffer mark stack) +@index(Mark stack) +This function returns the top of @i[buffer]'s mark stack. There always +is at least one mark at the beginning of @i[buffer]'s region, and all marks +returned are right-inserting. +@enddefun + +@defun[fun {buffer-start-mark}, args {@i[buffer]}] +@defun1[fun {buffer-end-mark}, args {@i[buffer]}] +These functions return the start and end marks of @i[buffer]'s region: +@Begin[ProgramExample] +(buffer-start-mark buffer) <==> + (region-start (buffer-region buffer)) +and +(buffer-end-mark buffer) <==> + (region-end (buffer-region buffer)) +@End[ProgramExample] +@enddefun + +@defun[fun {buffer-writable}, args {@i[buffer]}] +@defhvar1[var "Buffer Writable Hook"] +This function returns @true if you can modify the @i(buffer), @nil if you +cannot. If a buffer is not writable, then any attempt to alter text in the +buffer results in an error. There is a @f[setf] method to change this value. + +The @f[setf] method invokes the functions in @hid[Buffer Writable Hook] on the +buffer and new value before storing the new value. +@enddefun + +@defun[fun {buffer-modified}, args {@i[buffer]}] +@defhvar1[var "Buffer Modified Hook"] +@f[buffer-modified] returns @true if the @i[buffer] has been modified, @nil if +it hasn't. This attribute is set whenever a text-altering operation is +performed on a buffer. There is a @f[setf] method to change this value. + +The @f[setf] method invokes the functions in @hid[Buffer Modified Hook] with +the buffer whenever the value of the modified flag changes. +@enddefun + +@defmac[fun {with-writable-buffer}, args {(@i[buffer]) @rest @i[forms]}] +This macro executes @i[forms] with @i[buffer]'s writable status set. After +@i[forms] execute, this resets the @i[buffer]'s writable and modified status. +@enddefmac + +@defun[fun {buffer-signature}, args {@i[buffer]}] +This function returns an arbitrary number which reflects the buffer's current +@i[signature]. The result is @f[eql] to a previous result if and only if the +buffer has not been modified between the calls. +@enddefun + +@defun[fun {buffer-variables}, args {@i[buffer]}] +This function returns a string-table (page @pageref[string-tables]) containing +the names of the buffer's local variables. See chapter @ref[variables]. +@enddefun + +@defun[fun {buffer-modes}, args {@i[buffer]}] +This function returns the list of the names of the modes active in @i[buffer]. +The major mode is first, followed by any minor modes. See chapter @ref[modes]. +@enddefun + +@defun[fun {buffer-windows}, args {@i[buffer]}] +This function returns the list of all the windows in which the buffer may be +displayed. This list may include windows which are not currently visible. See +page @pageref[windows] for a discussion of windows. +@enddefun + +@defun[fun {buffer-delete-hook}, args {@i[buffer]}] +This function returns the list of buffer specific functions @f[delete-buffer] +invokes when deleting a buffer. This is @f[setf]'able. +@enddefun + +@defun[fun {delete-buffer}, args {@i[buffer]}] +@defhvar1[var {Delete Buffer Hook}] +@f[delete-buffer] removes @i[buffer] from @varref[buffer-list] and its name +from @varref[buffer-names]. Before @i[buffer] is deleted, this invokes the +functions on @i[buffer] returned by @f[buffer-delete-hook] and those found in +@hid[Delete Buffer Hook]. If @i[buffer] is the @f[current-buffer], or if it is +displayed in any windows, then this function signals an error. +@enddefun + +@defun[fun {delete-buffer-if-possible}, args {@i[buffer]}] +This uses @f[delete-buffer] to delete @i[buffer] if at all possible. If +@i[buffer] is the @f[current-buffer], then this sets the @f[current-buffer] to +the first distinct buffer in @f[buffer-history]. If @i[buffer] is displayed in +any windows, then this makes each window display the same distinct buffer. +@enddefun + + +@section(Modelines) +@index(Modelines) +@label(modelines) + +A Buffer may specify a modeline, a line of text which is displayed across the +bottom of a window to indicate status information. Modelines are described as +a list of @f[modeline-field] objects which have individual update functions and +are optionally fixed-width. These have an @f[eql] name for convenience in +referencing and updating, but the name must be unique for all created +modeline-field objects. When creating a modeline-field with a specified width, +the result of the update function is either truncated or padded on the right to +meet the constraint. All modeline-field functions must return simple strings +with standard characters, and these take a buffer and a window as arguments. +Modeline-field objects are typically shared amongst, or aliased by, different +buffers' modeline fields lists. These lists are unique allowing fields to +behave the same wherever they occur, but different buffers may display these +fields in different arrangements. + +Whenever one of the following changes occurs, all of a buffer's modeline fields +are updated: +@Begin[Itemize] +A buffer's major mode is set. + +One of a buffer's minor modes is turned on or off. + +A buffer is renamed. + +A buffer's pathname changes. + +A buffer's modified status changes. + +A window's buffer is changed. +@End[Itemize] + +The policy is that whenever one of these changes occurs, it is guaranteed that +the modeline will be updated before the next trip through redisplay. +Furthermore, since the system cannot know what modeline-field objects the +user has added whose update functions rely on these values, or how he has +changed @hid[Default Modeline Fields], we must update all the fields. When any +but the last occurs, the modeline-field update function is invoked once for +each window into the buffer. When a window's buffer changes, each +modeline-field update function is invoked once; other windows' modeline +fields should not be affected due to a given window's buffer changing. + +The user should note that modelines can be updated at any time, so update +functions should be careful to avoid needless delays (for example, waiting for +a local area network to determine information). + +@defun[fun {make-modeline-field}, keys {[name][width][function]}] +@defun1[fun {modeline-field-p}, args @i(modeline-field)] +@defun1[fun {modeline-field-name}, args @i(modeline-field)] +@f[make-modeline-field] returns a modeline-field object with @i[name], +@i[width], and @i[function]. @i[Width] defaults to @nil meaning that the field +is variable width; otherwise, the programmer must supply this as a positive +integer. @i[Function] must take a buffer and window as arguments and return a +@f[simple-string] containing only standard characters. If @i[name] already +names a modeline-field object, then this signals an error. + +@f[modeline-field-name] returns the name field of a modeline-field object. If +this is set with @f[setf], and the new name already names a modeline-field, +then the @f[setf] method signals an error. + +@f[modeline-field-p] returns @true or @nil, depending on whether its argument +is a @f[modeline-field] object. +@enddefun + +@defun[fun {modeline-field}, args {@i[name]}] +This returns the modeline-field object named @i[name]. If none exists, this +returns nil. +@enddefun + +@defun[fun {modeline-field-function}, args {@i[modeline-field]}] +Returns the function called when updating the @i[modeline-field]. When this is +set with @f[setf], the @f[setf] method updates @i[modeline-field] for all +windows on all buffers that contain the given field, so the next trip through +redisplay will reflect the change. All modeline-field functions must return +simple strings with standard characters, and they take a buffer and a window +as arguments. +@enddefun + +@defun[fun {modeline-field-width}, args {@i[modeline-field]}] +Returns the width to which @i[modeline-field] is constrained, or @nil +indicating that it is variable width. When this is set with @f[setf], the +@f[setf] method updates all modeline-fields for all windows on all buffers that +contain the given field, so the next trip through redisplay will reflect the +change. All the fields for any such modeline display must be updated, which is +not the case when setting a modeline-field's function. +@enddefun + +@defun[fun {buffer-modeline-fields}, args {@i[buffer]}] +Returns a copy of the list of @i[buffer]'s modeline-field objects. This list +can be destructively modified without affecting display of @i[buffer]'s +modeline, but modifying any particular field's components (for example, width +or function) causes the changes to be reflected the next trip through redisplay +in every modeline display that uses the modified modeline-field. When this is +set with @f[setf], @f[update-modeline-fields] is called for each window into +@i[buffer]. +@enddefun + +@defun[fun {buffer-modeline-field-p}, args {@i[buffer] @i[field]}] +If @i[field], a modeline-field or the name of one, is in buffer's list of +modeline-field objects, it is returned; otherwise, this returns nil. +@enddefun + +@defun[fun {update-modeline-fields}, args {@i[buffer] @i[window]}] +This invokes each modeline-field object's function from @i[buffer]'s list, +passing @i[buffer] and @i[window]. The results are collected regarding each +modeline-field object's width as appropriate, and the window is marked so +the next trip through redisplay will reflect the changes. If window does not +display modelines, then no computation occurs. +@enddefun + +@defun[fun {update-modeline-field}, args {@i[buffer] @i[window] @i[field-or-name}] +This invokes the modeline-field object's function for @i[field-or-name], which +is a modeline-field object or the name of one for @i[buffer]. This passes +@i[buffer] and @i[window] to the update function. The result is applied to the +@i[window]'s modeline display using the modeline-field object's width, and the +window is marked so the next trip through redisplay will reflect the changes. +If the window does not display modelines, then no computation occurs. If +@i[field-or-name] is not found in @i[buffer]'s list of modeline-field objects, +then this signals an error. See @f[buffer-modeline-field-p] above. +@enddefun + + + +@chapter(Altering and Searching Text) +@label[doing-stuff] + +@section(Altering Text) +@index(Altering text) +@index(Inserting) +@index(Deleting) +A note on marks and text alteration: @kwd[temporary] marks are invalid +after any change has been made to the text the mark points to; it is an +error to use a temporary mark after such a change has been made. If +text is deleted which has permanent marks pointing into it then they +are left pointing to the position where the text was. + +@defun[fun {insert-character}, args {@i[mark] @i[character]}] +@defun1[fun {insert-string}, args {@i[mark] @i[string]}] +@defun1[fun {insert-region}, args {@i[mark] @i[region]}] +Inserts @i[character], @i[string] or @i[region] at @i[mark]. +@f[insert-character] signals an error if @i[character] is not +@f[string-char-p]. If @i[string] or @i[region] is empty, and @i[mark] is in +some buffer, then @hemlock leaves @f[buffer-modified] of @i[mark]'s buffer +unaffected. +@enddefun + +@defun[fun {ninsert-region}, args {@i[mark] @i[region]}] +Like @f[insert-region], inserts the @i[region] at the @i[mark]'s position, +destroying the source region. This must be used with caution, since if anyone +else can refer to the source region bad things will happen. In particular, one +should make sure the region is not linked into any existing buffer. If +@i[region] is empty, and @i[mark] is in some buffer, then @hemlock leaves +@f[buffer-modified] of @i[mark]'s buffer unaffected. +@enddefun + +@defun[fun {delete-characters}, args {@i[mark] @i[n]}] +This deletes @i[n] characters after the @i[mark] (or -@i[n] before if @i[n] is +negative). If @i[n] characters after (or -@i[n] before) the @i[mark] do not +exist, then this returns @nil; otherwise, it returns @true. If @i[n] is zero, +and @i[mark] is in some buffer, then @hemlock leaves @f[buffer-modified] of +@i[mark]'s buffer unaffected. +@enddefun + +@defun[fun {delete-region}, args {@i[region]}] +This deletes @i[region]. This is faster than @f[delete-and-save-region] +(below) because no lines are copied. If @i[region] is empty and contained in +some buffer's @f[buffer-region], then @hemlock leaves @f[buffer-modified] of +the buffer unaffected. +@enddefun + +@defun[fun {delete-and-save-region}, args {@i[region]}] +This deletes @i[region] and returns a region containing the original +@i[region]'s text. If @i[region] is empty and contained in some buffer's +@f[buffer-region], then @hemlock leaves @f[buffer-modified] of the buffer +unaffected. In this case, this returns a distinct empty region. +@enddefun + +@defun[fun {filter-region}, args {@i[function] @i[region]}] +Destructively modifies @i[region] by replacing the text +of each line with the result of the application of @i[function] to a +string containing that text. @i[Function] must obey the following +restrictions: +@begin[enumerate] +The argument may not be destructively modified. + +The return value may not contain newline characters. + +The return value may not be destructively modified after it is +returned from @i[function]. +@end[enumerate] +The strings are passed in order, and are always simple strings. + +Using this function, a region could be uppercased by doing: +@lisp +(filter-region #'string-upcase region) +@endlisp +@enddefun + + +@section(Text Predicates) +@defun[fun {start-line-p}, args {@i[mark]}] +Returns @true if the @i(mark) points before the first character in a line, +@nil otherwise. +@enddefun + +@defun[fun {end-line-p}, args {@i[mark]}] +Returns @true if the @i(mark) points after the last character in a line and +before the newline, @nil otherwise. +@enddefun + +@defun[fun {empty-line-p}, args {@i[mark]}] +Return @true of the line which @i[mark] points to contains no characters. +@enddefun + +@defun[fun {blank-line-p}, args {@i[line]}] +Returns @true if @i[line] contains only characters with a +@hid[Whitespace] attribute of 1. See chapter @ref[character-attributes] for +discussion of character attributes. +@enddefun + +@defun[fun {blank-before-p}, args {@i[mark]}] +@defun1[fun {blank-after-p}, args {@i[mark]}] +These functions test if all the characters preceding or following +@i[mark] on the line it is on have a @hid[Whitespace] attribute of @f[1]. +@enddefun + +@defun[fun {same-line-p}, args {@i[mark1] @i[mark2]}] +Returns @true if @i(mark1) and @i(mark2) point to the same line, or @nil +otherwise; That is, +@example[(same-line-p a b) <==> (eq (mark-line a) (mark-line b))] +@enddefun + +@defun[fun {mark<}, funlabel {mark-LSS}, args {@i[mark1] @i[mark2]}] +@defun1[fun {mark<=}, funlabel {mark-LEQ}, args {@i[mark1] @i[mark2]}] +@defun1[fun {mark=}, funlabel {mark-EQL}, args {@i[mark1] @i[mark2]}] +@defun1[fun {mark/=}, funlabel {mark-NEQ}, args {@i[mark1] @i[mark2]}] +@defun1[fun {mark>=}, funlabel {mark-GEQ}, args {@i[mark1] @i[mark2]}] +@defun1[fun {mark>}, funlabel {mark-GTR}, args {@i[mark1] @i[mark2]}] +These predicates test the relative ordering of two marks in a piece of +text, that is a mark is @f[mark>] another if it points to a position +after it. If the marks point into different, non-connected pieces of +text, such as different buffers, then it is an error to test their +ordering; for such marks @f[mark=] is always false and @f[mark/=] is +always true. +@enddefun + +@defun[fun {line<}, funlabel {line-LSS}, args {@i[line1] @i[line2]}] +@defun1[fun {line<=}, funlabel {line-LEQ}, args {@i[line1] @i[line2]}] +@defun1[fun {line>=}, funlabel {line-GEQ}, args {@i[line1] @i[line2]}] +@defun1[fun {line>}, funlabel {line-GTR}, args {@i[line1] @i[line2]}] +These predicates test the ordering of @i[line1] and @i[line2]. If the +lines are in unconnected pieces of text it is an error to test their +ordering. +@enddefun + +@defun[fun {lines-related}, args {@i[line1] @i[line2]}] +This function returns @true if @i[line1] and @i[line2] are in the same +piece of text, or @nil otherwise. +@enddefun + +@defun[fun {first-line-p}, args {@i[mark]}] +@defun1[fun {last-line-p}, args {@i[mark]}] +@f[first-line-p] returns @true if there is no line before the line +@i[mark] is on, and @nil otherwise. @i[Last-line-p] similarly tests +tests whether there is no line after @i[mark]. +@enddefun + + +@section(Kill Ring) +@index(Kill ring) +@label(kill-ring) + +@defvar[var {kill-ring}] +This is a ring (see section @ref[rings]) of regions deleted from buffers. +Some commands save affected regions on the kill ring before performing +modifications. You should consider making the command undoable (see section +@ref[undo]), but this is a simple way of achieving a less satisfactory means +for the user to recover. +@enddefvar + +@defun[fun {kill-region}, args {@i[region] @i[current-type]}] +This kills @i[region] saving it in @var[kill-ring]. @i[Current-type] is either +@kwd[kill-forward] or @kwd[kill-backward]. When the @funref[last-command-type] +is one of these, this adds @i[region] to the beginning or end, respectively, of +the top of @var[kill-ring]. The result of calling this is undoable using the +command @hid[Undo] (see the @i[Hemlock User's Manual]). This sets +@f[last-command-type] to @i[current-type], and it interacts with +@f[kill-characters]. +@enddefun + +@defun[fun {kill-characters}, args {@i[mark] @i[count]}] +@defhvar1[var {Character Deletion Threshold}, val {5}] +@f[kill-characters] kills @i[count] characters after @i[mark] if @i[count] is +positive, otherwise before @i[mark] if @i[count] is negative. When @i[count] +is greater than or equal to @hid[Character Deletion Threshold], the killed +characters are saved on @var[kill-ring]. This may be called multiple times +contiguously (that is, without @funref[last-command-type] being set) to +accumulate an effective count for purposes of comparison with the threshold. + +This sets @f[last-command-type], and it interacts with @f[kill-region]. When +this adds a new region to @var[kill-ring], it sets @f[last-command-type] to +@kwd[kill-forward] (if @i[count] is positive) or @kwd[kill-backward] (if +@i[count] is negative). When @f[last-command-type] is @kwd[kill-forward] or +@kwd[kill-backward], this adds the killed characters to the beginning (if +@i[count] is negative) or the end (if @i[count] is positive) of the top of +@var[kill-ring], and it sets @f[last-command-type] as if it added a new region +to @var[kill-ring]. When the kill ring is unaffected, this sets +@f[last-command-type] to @kwd[char-kill-forward] or @kwd[char-kill-backward] +depending on whether @i[count] is positive or negative, respectively. + +This returns mark if it deletes characters. If there are not @i[count] +characters in the appropriate direction, this returns nil. +@enddefun + + +@section(Active Regions) +@index(Active regions) +@label(active-regions) + +Every buffer has a mark stack (page @pageref[mark-stack]) and a mark known as +the point where most text altering nominally occurs. Between the top of the +mark stack, the @f[current-mark], and the @f[current-buffer]'s point, the +@f[current-point], is what is known as the @f[current-region]. Certain +commands signal errors when the user tries to operate on the @f[current-region] +without its having been activated. If the user turns off this feature, then +the @f[current-region] is effectively always active. + +When writing a command that marks a region of text, the programmer should make +sure to activate the region. This typically occurs naturally from the +primitives that you use to mark regions, but sometimes you must explicitly +activate the region. These commands should be written this way, so they do not +require the user to separately mark an area and then activate it. Commands +that modify regions do not have to worry about deactivating the region since +modifying a buffer automatically deactivates the region. Commands that insert +text often activate the region ephemerally; that is, the region is active for +the immediately following command, allowing the user wants to delete the region +inserted, fill it, or whatever. + +Once a marking command makes the region active, it remains active until: +@begin[itemize] +a command uses the region, + +a command modifies the buffer, + +a command changes the current window or buffer, + +a command signals an editor-error, + +or the user types @binding[C-g]. +@end[itemize] + +@defhvar[var "Active Regions Enabled", val {t}] +When this variable is non-@nil, some primitives signal an editor-error if +the region is not active. This may be set to @nil for more traditional @emacs +region semantics. +@enddefhvar + +@defvar[var {ephemerally-active-command-types}] +This is a list of command types (see section @ref[command-types]), and its +initial value is the list of @kwd[ephemerally-active] and @kwd[unkill]. When +the previous command's type is one of these, the @f[current-region] is active +for the currently executing command only, regardless of whether it does +something to deactivate the region. However, the current command may activate +the region for future commands. @kwd[ephemerally-active] is a default command +type that may be used to ephemerally activate the region, and @kwd[unkill] is +the type used by two commands, @hid[Un-kill] and @hid[Rotate Kill Ring] (what +users typically think of as @binding[C-y] and @binding[M-y]). +@enddefvar + +@defun[fun {activate-region}] +This makes the @f[current-region] active. +@enddefun + +@defun[fun {deactivate-region}] +After invoking this the @f[current-region] is no longer active. +@enddefun + +@defun[fun {region-active-p}] +Returns whether the @f[current-region] is active, including ephemerally. This +ignores @hid[Active Regions Enabled]. +@enddefun + +@defun[fun {check-region-active}] +This signals an editor-error when active regions are enabled, and the +@f[current-region] is not active. +@enddefun + +@defun[fun {current-region}, + args {@optional @i[error-if-not-active] @i[deactivate-region]}] +This returns a region formed with @f[current-mark] and @f[current-point], +optionally signaling an editor-error if the current region is not active. +@i[Error-if-not-active] defaults to @true. Each call returns a distinct region +object. Depending on @i[deactivate-region] (defaults to @true), fetching the +current region deactivates it. @hemlock primitives are free to modify text +regardless of whether the region is active, so a command that checks for this +can deactivate the region whenever it is convenient. +@enddefun + + +@section(Searching and Replacing) +@index(Searching) +@index(Replacing) + +Before using any of these functions to do a character search, look at character +attributes (page @pageref[character-attributes]). They provide a facility +similar to the syntax table in real EMACS. Syntax tables are a powerful, +general, and efficient mechanism for assigning meanings to characters in +various modes. + +@defcon[var {search-char-code-limit}] +An exclusive upper limit for the char-code of characters given to the searching +functions. The result of searches for characters with a char-code greater than +or equal to this limit is ill-defined, but it is @i[not] an error to do such +searches. +@enddefcon + +@defun[fun {new-search-pattern}, +args {@i[kind] @i[direction] @i[pattern] @optional @i[result-search-pattern]}] + +Returns a @i[search-pattern] object which can be given to the @f[find-pattern] +and @f[replace-pattern] functions. A search-pattern is a specification of a +particular sort of search to do. @i[direction] is either @kwd[forward] or +@kwd[backward], indicating the direction to search in. @i[kind] specifies the +kind of search pattern to make, and @i[pattern] is a thing which specifies what +to search for. + +The interpretation of @i[pattern] depends on the @i[kind] of pattern being +made. Currently defined kinds of search pattern are: +@begin(description) +@kwd[string-insensitive]@\Does a case-insensitive string search, +@i[pattern] being the string to search for. + +@kwd[string-sensitive]@\Does a case-sensitive string search for +@i[pattern]. + +@kwd[character]@\Finds an occurrence of the character @i[pattern]. +This is case sensitive. + +@kwd[not-character]@\Find a character which is not the character +@i[pattern]. + +@kwd[test]@\Finds a character which satisfies the function @i[pattern]. +This function may not be applied an any particular fashion, so it +should depend only on what its argument is, and should have no +side-effects. + +@kwd[test-not]@\Similar to as @kwd[test], except it finds a character that +fails the test. + +@kwd[any]@\Finds a character that is in the string @i[pattern]. + +@kwd[not-any]@\Finds a character that is not in the string @i[pattern]. +@end(description) + +@i[result-search-pattern], if supplied, is a search-pattern to +destructively modify to produce the new pattern. Where reasonable +this should be supplied, since some kinds of search patterns may +involve large data structures. +@enddefun + +@defun[fun {search-pattern-p}, args {@i[search-pattern]}] +Returns @true if @i[search-pattern] is a @f[search-pattern] object, otherwise +@nil. +@enddefun + +@defun[fun {get-search-pattern}, args {@i[string] @i[direction]}] +@defvar1[var {last-search-pattern}] +@defvar1[var {last-search-string}] +@f[get-search-pattern] interfaces to a default search string and pattern that +search and replacing commands can use. These commands then share a default +when prompting for what to search or replace, and save on consing a search +pattern each time they execute. This uses @hid[Default Search Kind] (see the +@i[Hemlock User's Manual]) when updating the pattern object. This returns the +pattern, so you probably don't need to refer to @var[last-search-pattern], but +@var[last-search-string] is useful when prompting. +@enddefun + +@defun[fun {find-pattern}, args {@i[mark] @i[search-pattern]}] +Find the next match of @i[search-pattern] starting at @i[mark]. If a +match is found then @i[mark] is altered to point before the matched text +and the number of characters matched is returned. If no match is +found then @nil is returned and @i[mark] is not modified. +@enddefun + +@defun[fun {replace-pattern}, args + {@i[mark] @i[search-pattern] @i[replacement] @optional @i[n]}] +Replace @i[n] matches of @i[search-pattern] with the string +@i[replacement] starting at @i[mark]. If @i[n] is @nil (the default) +then replace all matches. A mark pointing before the last replacement +done is returned. +@enddefun + + + +@Chapter(The Current Environment) +@label(current-environment) +@index(Current environment) + +@section(Different Scopes) + In @hemlock the values of @i[variables] (page @pageref[variables]), +@i[key-bindings] (page @pageref(key-bindings)) and +@i[character-attributes] (page @pageref[character-attributes]) may +depend on the @funref(current-buffer) and the modes +active in it. There are three possible scopes for +@hemlock values: +@begin(description) +@i[buffer local]@\The value is present only if the buffer it is local +to is the @f[current-buffer]. + +@i[mode local]@\The value is present only when the mode it is local to +is active in the @f[current-buffer]. + +@i[global]@\The value is always present unless shadowed by a buffer or +mode local value. +@end(description) + + +@section(Shadowing) + It is possible for there to be a conflict between different values +for the same thing in different scopes. For example, there be might a +global binding for a given variable and also a local binding in the +current buffer. Whenever there is a conflict shadowing occurs, +permitting only one of the values to be visible in the current +environment. + + The process of resolving such a conflict can be described as a +search down a list of places where the value might be defined, returning +the first value found. The order for the search is as follows: +@begin(enumerate) +Local values in the current buffer. + +Mode local values in the minor modes of the current buffer, in order +from the highest precedence mode to the lowest precedence mode. The +order of minor modes with equal precedences is undefined. + +Mode local values in the current buffer's major mode. + +Global values. +@end(enumerate) + + + +@chapter(Hemlock Variables) +@index (Hemlock variables) +@label(variables) +@hemlock implements a system of variables separate from normal Lisp variables +for the following reasons: +@begin(enumerate) +@hemlock has different scoping rules which are useful in an editor. @hemlock +variables can be local to a @i(buffer) (page @pageref[buffers]) or a @i(mode) +(page @pageref[modes]). + +@hemlock variables have @i(hooks) (page @pageref[hooks]), lists of functions +called when someone sets the variable. See @f[variable-value] for the +arguments @hemlock passes to these hook functions. + +There is a database of variable names and documentation which makes it easier +to find out what variables exist and what their values mean. +@end(enumerate) + + +@section(Variable Names) +To the user, a variable name is a case insensitive string. This +string is referred to as the @i[string name] of the variable. A +string name is conventionally composed of words separated by spaces. + +In Lisp code a variable name is a symbol. The name of this symbol is +created by replacing any spaces in the string name with hyphens. This +symbol name is always interned in the @hemlock package and referring +to a symbol with the same name in the wrong package is an error. + +@defvar[var {global-variable-names}] +This variable holds a string-table of the names of all the global @hemlock +variables. The value of each entry is the symbol name of the variable. +@enddefvar + +@defun[fun {current-variable-tables}] +This function returns a list of variable tables currently established, +globally, in the @f[current-buffer], and by the modes of the +@f[current-buffer]. This list is suitable for use with +@f[prompt-for-variable]. +@enddefun + + +@section(Variable Functions) +In the following descriptions @i[name] is the symbol name of the variable. + +@defun[fun {defhvar}, args {@i[string-name] @i[documentation]}, + keys {[mode][buffer][hooks][value]}] + This function defines a @hemlock variable. Functions that take a variable +name signal an error when the variable is undefined. +@begin(description) +@i[string-name]@\The string name of the variable to define. + +@i[documentation]@\The documentation string for the variable. + +@multiple{ +@kwd[mode], +@kwd[buffer]}@\ + If @i[buffer] is supplied, the variable is local to that buffer. If @i[mode] +is supplied, it is local to that mode. If neither is supplied, it is global. + +@kwd[value]@\ + This is the initial value for the variable, which defaults to @nil. + +@kwd[hooks]@\ + This is the initial list of functions to call when someone sets the variable's +value. These functions execute before @hemlock establishes the new value. See +@f[variable-value] for the arguments passed to the hook functions. +@end(description) +If a variable with the same name already exists in the same place, then +@f[defhvar] sets its hooks and value from @i[hooks] and @i[value] if the user +supplies these keywords. +@enddefun + +@defun[fun {variable-value}, args {@i[name] @optional @i[kind] @i[where]}] +This function returns the value of a @hemlock variable in some place. +The following values for @i[kind] are defined: +@begin[description] +@kwd[current]@\ + Return the value present in the current environment, taking into consideration +any mode or buffer local variables. This is the default. + +@kwd[global]@\ + Return the global value. + +@kwd[mode]@\ + Return the value in the mode named @i[where]. + +@kwd[buffer]@\ + Return the value in the buffer @i[where]. +@end[description] +When set with @f[setf], @hemlock sets the value of the specified variable and +invokes the functions in its hook list with @i[name], @i[kind], @i[where], and +the new value. +@enddefun + +@defun[fun {variable-documentation}, args + {@i[name] @optional @i[kind] @i[where]}] +@defun1[fun {variable-hooks}, args + {@i[name] @optional @i[kind] @i[where]}] +@defun1[fun {variable-name}, args + {@i[name] @optional @i[kind] @i[where]}] +These function return the documentation, hooks and string name of a +@hemlock variable. The @i[kind] and @i[where] arguments are the same +as for @f[variable-value]. The documentation and hook list may be set +using @f[setf]. +@enddefun + +@defun[fun {string-to-variable}, args {@i[string]}] +This function converts a string into the corresponding variable symbol +name. @i[String] need not be the name of an actual @hemlock variable. +@enddefun + +@defmac[fun {value}, args {@i[name]}] +@defmac1[fun {setv}, args {@i[name] @i[new-value]}] +These macros get and set the current value of the @hemlock variable +@i[name]. @i[Name] is not evaluated. There is a @f[setf] form for +@f[value]. +@enddefmac + +@Defmac[Fun {hlet}, Args {(@Mstar<(@i[var] @i[value])>) @Mstar<@i[form]>}] +This macro is very similar to @f[let] in effect; within its scope each +of the @hemlock variables @i[var] have the respective @i[value]s, but +after the scope is exited by any means the binding is removed. This +does not cause any hooks to be invoked. The value of the last +@i[form] is returned. +@enddefmac + +@defun[fun {hemlock-bound-p}, args {@i[name] @optional @i[kind] @i[where]}] +Returns @true if @i[name] is defined as a @hemlock variable in the +place specified by @i[kind] and @i[where], or @nil otherwise. +@enddefun + +@defun[fun {delete-variable}, args {@i(name) @optional @i[kind] @i[where]}] +@defhvar1[var {Delete Variable Hook}] +@f[delete-variable] makes the @hemlock variable @i[name] no longer +defined in the specified place. @i[Kind] and @i[where] have the same +meanings as they do for @f[variable-value], except that @kwd[current] +is not available, and the default for @i[kind] is @kwd[global] + +An error will be signaled if no such variable exists. The hook, +@hid[Delete Variable Hook] is invoked with the same arguments before the +variable is deleted. +@enddefun + + +@section(Hooks) +@index(Hooks) +@label[hooks] +@hemlock actions such as setting variables, changing buffers, changing windows, +turning modes on and off, etc., often have hooks associated with them. A hook +is a list of functions called before the system performs the action. The +manual describes the object specific hooks with the rest of the operations +defined on these objects. + +Often hooks are stored in @hemlock variables, @hid[Delete Buffer Hook] and +@hid[Set Window Hook] for example. This leads to a minor point of confusion +because these variables have hooks that the system executes when someone +changes their values. These hook functions @hemlock invokes when someone sets +a variable are an example of a hook stored in an object instead of a @hemlock +variable. These are all hooks for editor activity, but @hemlock keeps them in +different kinds of locations. This is why some of the routines in this section +have a special interpretation of the hook @i[place] argument. + +@defmac[fun {add-hook}, args {@i[place] @i[hook-fun]}] +@defmac1[fun {remove-hook}, args {@i[place] @i[hook-fun]}] +These macros add or remove a hook function in some @i[place]. If @i[hook-fun] +already exists in @i[place], this call has no effect. If @i[place] is a +symbol, then it is a @hemlock variable; otherwise, it is a generalized variable +or storage location. Here are two examples: +@Begin[ProgramExample] +(add-hook delete-buffer-hook 'remove-buffer-from-menu) + +(add-hook (variable-hooks 'check-mail-interval) + 'reschedule-mail-check) +@End[ProgramExample] +@enddefmac + +@defmac[fun {invoke-hook}, args {@i[place] @rest @i[args]}] +This macro calls all the functions in @i[place]. If @i[place] is a symbol, +then it is a @hemlock variable; otherwise, it is a generalized variable. +@enddefun + + + +@chapter(Commands) +@index (Commands) +@label[commands] + + +@section(Introduction) +The way that the user tells @hemlock to do something is by invoking a +@i(command). Commands have three attributes: +@begin(description) +@i[name]@\A command's name provides a way to refer to it. Command +names are usually capitalized words separated by spaces, such as +@hid[Forward Word]. + +@i[documentation]@\The documentation for a command is used by +on-line help facilities. + +@i[function]@\A command is implemented by a Lisp function, which is callable +from Lisp. +@end(description) + +@defvar[var {command-names}] +Holds a string-table (page @pageref[string-tables]) associating +command names to command objects. Whenever a new command is defined +it is entered in this table. +@enddefvar + + +@subsection(Defining Commands) + +@defmac[fun {defcommand}, args +{@^@mgroup<@i[command-name] @MOR (@i[command-name] @i[function-name])> @i[lambda-list] +@\@i[command-doc] @i[function-doc] @mstar<@i[form]>}] + +Defines a command named @i[name]. @f[defcommand] creates a function to +implement the command from the @i[lambda-list] and @i[form]'s supplied. The +@i[lambda-list] must specify one required argument, see section +@ref[invoking-commands-as-functions], which by convention is typically named +@f[p]. If the caller does not specify @i[function-name], @f[defcommand] +creates the command name by replacing all spaces with hyphens and appending +"@f[-command]". @i[Function-doc] becomes the documentation for the function +and should primarily describe issues involved in calling the command as a +function, such as what any additional arguments are. @i[Command-doc] becomes +the command documentation for the command. @enddefmac + +@defun[fun {make-command}, args + {@i[name] @i[documentation] @i[function]}] +Defines a new command named @i[name], with command documentation +@I[documentation] and function @i[function]. The command in entered +in the string-table @varref[command-names], with the command object as +its value. Normally command implementors will use the @f[defcommand] +macro, but this permits access to the command definition mechanism at +a lower level, which is occasionally useful. +@enddefun + +@defun[fun {commandp}, args {@i[command]}] +Returns @true if @i[command] is a @f[command] object, otherwise @nil. +@enddefun + +@defun[fun {command-documentation}, args {@i[command]}] +@defun1[fun {command-function}, args {@i[command]}] +@defun1[fun {command-name}, args {@i[command]}] +Returns the documentation, function, or name for @i[command]. These +may be set with @f[setf]. +@enddefun + + +@subsection(Command Documentation) +@i[Command documentation] is a description of what the command does +when it is invoked as an extended command or from a key. Command +documentation may be either a string or a function. If the +documentation is a string then the first line should briefly summarize +the command, with remaining lines filling the details. Example: +@lisp +(defcommand "Forward Character" (p) + "Move the point forward one character. + With prefix argument move that many characters, with negative + argument go backwards." + "Move the point of the current buffer forward p characters." + . . .) +@endlisp + +Command documentation may also be a function of one argument. The +function is called with either @kwd[short] or @kwd[full], indicating +that the function should return a short documentation string or do +something to document the command fully. + + +@section(The Command Interpreter) +@index[Interpreter, command] +@index[Invocation, command] +@index[Command interpreter] + +The @i[command interpreter] is a function which reads key-events (see section +@ref[key-events-intro]) from the keyboard and dispatches to different commands +on the basis of what the user types. When the command interpreter executes a +command, we say it @i[invokes] the command. The command interpreter also +provides facilities for communication between commands contiguously running +commands, such as a last command type register. It also takes care of +resetting communication mechanisms, clearing the echo area, displaying partial +keys typed slowly by the user, etc. + +@defvar[var {invoke-hook}] +This variable contains a function the command interpreter calls when it wants +to invoke a command. The function receives the command and the prefix argument +as arguments. The initial value is a function which simply funcalls the +@f[command-function] of the command with the supplied prefix argument. This is +useful for implementing keyboard macros and similar things. +@enddefhvar + +@defhvar[var "Command Abort Hook"] +The command interpreter invokes the function in this variable whenever someone +aborts a command (for example, if someone called @f[editor-error]). +@enddefhvar + +When @hemlock initially starts the command interpreter is in control, but +commands may read from the keyboard themselves and assign whatever +interpretation they will to the key-events read. Commands may call the command +interpreter recursively using the function @funref[recursive-edit]. + + +@subsection(Editor Input) +@label[key-events-intro] +@index[key-events] + +The canonical representation of editor input is a key-event structure. Users +can bind commands to keys (see section @ref[key-bindings]), which are non-zero +length sequences of key-events. A key-event consists of an identifying token +known as a @i[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 @f[#k] syntax. The following are some +examples: +@begin[programexample] + #k"C-u" + #k"Control-u" + #k"c-m-z" + #k"control-x meta-d" + #k"a" + #k"A" + #k"Linefeed" +@end[programexample] +This is convenient for use within code and in init files containing +@f[bind-key] calls. + +The @f[#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 @dash hyphen, double quote, open angle +bracket, close angle bracket, and space @dash 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. + +For more information on key-events see section @ref[key-events]. + + + +@subsection(Binding Commands to Keys) +@label[Key-Bindings] +@Index[Key Bindings] + +The command interpreter determines which command to invoke on the basis of +@i[key bindings]. A key binding is an association between a command and a +sequence of key-events (see section @ref[key-events-intro]. A sequence of +key-events is called a @i[key] and is represented by a single key-event or a +sequence (list or vector) of key-events. + +Since key bindings may be local to a mode or buffer, the current environment +(page @pageref[current-environment]) determines the set of key bindings in +effect at any given time. When the command interpreter tries to find the +binding for a key, it first checks if there is a local binding in the +@w[@funref[current-buffer]], then if there is a binding in each of the minor +modes and the major mode for the current buffer @w[(page @pageref[modes])], and +finally checks to see if there is a global binding. If no binding is found, +then the command interpreter beeps or flashes the screen to indicate this. + +@defun[fun {bind-key}, args + {@i(name) @i(key) @optional @i[kind] @i[where]}] + This function associates command @i[name] and @i[key] in some environment. +@i[Key] is either a key-event or a sequence of key-events. There are three +possible values of @i[kind]: +@begin(description) +@kwd[global]@\ + The default, make a global key binding. + +@kwd[mode]@\ + Make a mode specific key binding in the mode whose name is @i[where]. + +@kwd[buffer]@\ + Make a binding which is local to buffer @i[where]. +@end(description) + +This processes @i[key] for key translations before establishing the binding. +See section @ref[key-trans]. + +If the key is some prefix of a key binding which already exists in the +specified place, then the new one will override the old one, effectively +deleting it. + +@f[ext:do-alpha-key-events] is useful for setting up bindings in certain new +modes. +@enddefun + +@defun[fun {command-bindings}, args {@i[command]}] +This function returns a list of the places where @i[command] is bound. A place +is specified as a list of the key (always a vector), the kind of binding, and +where (either the mode or buffer to which the binding is local, or @nil if it +is a global). +@enddefun + +@defun[fun {delete-key-binding}, args {@i[key] @optional @i[kind] @i[where]}] +This function removes the binding of @i[key] in some place. @i[Key] is either +a key-event or a sequence of key-events. @i[kind] is the kind of binding to +delete, one of @kwd[global] (the default), @kwd[mode] or @kwd[buffer]. If +@i[kind] is @kwd[mode], @i[where] is the mode name, and if @i[kind] is +@kwd[buffer], then @i[where] is the buffer. + +This function signals an error if @i[key] is unbound. + +This processes @i[key] for key translations before deleting the binding. See +section @ref[key-trans]. +@enddefun + +@defun[fun {get-command}, args {@i[key] @optional @i[kind] @i[where]}] +This function returns the command bound to @i[key], returning @nil if it is +unbound. @i[Key] is either a key-event or a sequence of key-events. If +@i[key] is an initial subsequence of some keys, then this returns the keyword +@kwd[prefix]. There are four cases of @i[kind]: +@begin(description) +@kwd[current]@\ + Return the current binding of @i[key] using the current buffer's search list. +If there are any transparent key bindings for @i[key], then they are returned +in a list as a second value. + +@kwd[global]@\ + Return the global binding of @i[key]. This is the default. + +@kwd[mode]@\ + Return the binding of @i[key] in the mode named @i[where]. + +@kwd[buffer]@\ + Return the binding of @i[key] local to the buffer @i[where]. +@end(description) + +This processes @i[key] for key translations before looking for any binding. +See section @ref[key-trans]. +@enddefun + +@defun[fun {map-bindings}, Args {@i[function] @i[kind] @optional @i[where]}] +This function maps over the key bindings in some place. For each binding, this +passes @i[function] the key and the command bound to it. @i[Kind] and +@i[where] are the same as in @f[bind-key]. The key is not guaranteed to remain +valid after a given iteration. +@enddefmac + + +@subsection[Key Translation] +@index[bit-prefix keys] +@index[key translation] +@index[translating keys] +@label[key-trans] +Key translation is a process that the command interpreter applies to keys +before doing anything else. There are two kinds of key translations: +substitution and bit-prefix. In either case, the command interpreter +translates a key when a specified key-event sequence appears in a key. + +In a substitution translation, the system replaces the matched subsequence with +another key-event sequence. Key translation is not recursively applied to the +substituted key-events. + +In a bit-prefix translation, the system removes the matched subsequence and +effectively sets the specified bits in the next key-event in the key. + +While translating a key, if the system encounters an incomplete final +subsequence of key-events, it aborts the translation process. This happens +when those last key-events form a prefix of some translation. It also happens +when they translate to a bit-prefix, but there is no following key-event to +which the system can apply the indicated modifier. If there is a binding for +this partially untranslated key, then the command interpreter will invoke that +command; otherwise, it will wait for the user to type more key-events. + +@defun[fun {key-translation}, args {@i[key]}] +This form is @f[setf]'able and allows users to register key translations that +the command interpreter will use as users type key-events. + +This function returns the key translation for @i[key], returning @nil if there +is none. @i[Key] is either a key-event or a sequence of key-events. If +@i[key] is a prefix of a translation, then this returns @kwd[prefix]. + +A key translation is either a key or modifier specification. The bits +translations have a list form: @w<@f[(:bits {]@i[bit-name]@f[}*)]>. + +Whenever @i[key] appears as a subsequence of a key argument to the binding +manipulation functions, that portion will be replaced with the translation. +@enddefun + + + +@subsection[Transparent Key Bindings] +@label[transparent-key-bindings] +@index[Transparent key bindings] + +Key bindings local to a mode may be @i[transparent]. A transparent key +binding does not shadow less local key bindings, but rather indicates that +the bound command should be invoked before the first normal key binding. +Transparent key bindings are primarily useful for implementing minor modes +such as auto fill and word abbreviation. There may be several transparent +key bindings for a given key, in which case all of the commands bound are +invoked in the order they were found. If there no normal key binding for a +key typed, then the command interpreter acts as though the key is unbound +even if there are transparent key bindings. + +The @kwd[transparent-p] argument to @funref[defmode] determines whether the +key bindings in a mode are transparent or not. + + +@subsection (Interactive) +@index (Keyboard macro vs. interactive) +@index (Interactive vs. keyboard macro) +@Hemlock supports keyboard macros. A user may enter a mode where the editor +records his actions, and when the user exits this mode, the command @hid[Last +Keyboard Macro] plays back the actions. Some commands behave differently when +invoked as part of the definition of a keyboard macro. For example, when used +in a keyboard macro, a command that @f[message]'s useless user confirmation +will slow down the repeated invocations of @hid[Last Keyboard Macro] because +the command will pause on each execution to make sure the user sees the +message. This can be eliminated with the use of @f[interactive]. As another +example, some commands conditionally signal an editor-error versus simply +beeping the device depending on whether it executes on behalf of the user or a +keyboard macro. + +@defun[fun {interactive}] +This returns @true when the user invoked the command directly. +@enddefun + + +@section(Command Types) +@index(Command types) +@label(command-types) +In many editors the behavior of a command depends on the kind of command +invoked before it. @hemlock provides a mechanism to support this known as +@i(command type). + +@defun[fun {last-command-type}] +This returns the command type of the last command invoked. If this is set with +@f[setf], the supplied value becomes the value of @f[last-command-type] until +the next command completes. If the previous command did not set +@f[last-command-type], then its value is @nil. Normally a command type is a +keyword. The command type is not cleared after a command is invoked due to a +transparent key binding. +@enddefun + + +@section(Command Arguments) +@label[invoking-commands-as-functions] +There are three ways in which a command may be invoked: It may be bound to a +key which has been typed, it may be invoked as an extended command, or it may +be called as a Lisp function. Ideally commands should be written in such a way +that they will behave sensibly no matter which way they are invoked. The +functions which implement commands must obey certain conventions about argument +passing if the command is to function properly. + + +@subsection(The Prefix Argument) +@index(Prefix arguments) +Whenever a command is invoked it is passed as its first argument what +is known as the @i[prefix argument]. The prefix argument is always +either an integer or @nil. When a command uses this value it is +usually as a repeat count, or some conceptually similar function. + +@defun[fun {prefix-argument}] +This function returns the current value of the prefix argument. When +set with @f[setf], the new value becomes the prefix argument for the +next command. +@enddefun + +If the prefix argument is not set by the previous command then the +prefix argument for a command is @nil. The prefix argument is not cleared +after a command is invoked due to a transparent key binding. + + +@subsection(Lisp Arguments) +It is often desirable to call commands from Lisp code, in which case +arguments which would otherwise be prompted for are passed as optional +arguments following the prefix argument. A command should prompt for +any arguments not supplied. + + +@section(Recursive Edits) +@index(Recursive edits) +@defmac[fun {use-buffer}, args {@i[buffer] @mstar<@i[form]>}] +The effect of this is similar to setting the current-buffer to @i[buffer] +during the evaluation of @i[forms]. There are restrictions placed on what the +code can expect about its environment. In particular, the value of any global +binding of a @hemlock variable which is also a mode local variable of some mode +is ill-defined; if the variable has a global binding it will be bound, but the +value may not be the global value. It is also impossible to nest +@f[use-buffer]'s in different buffers. The reason for using @f[use-buffer] is +that it may be significantly faster than changing @f[current-buffer] to +@i[buffer] and back. +@enddefmac + +@defun[fun {recursive-edit}, args {@optional @i[handle-abort]}] +@defhvar1[var {Enter Recursive Edit Hook}] +@index[aborting] +@f[recursive-edit] invokes the command interpreter. The command interpreter +will read from the keyboard and invoke commands until it is terminated with +either @f[exit-recursive-edit] or @f[abort-recursive-edit]. + +Normally, an editor-error or @bf[C-g] aborts the command in progress and +returns control to the top-level command loop. If @f[recursive-edit] is used +with @i[handle-abort] true, then @f[editor-error] or @bf[C-g] will only abort +back to the recursive command loop. + +Before the command interpreter is entered the hook +@hid[Enter Recursive Edit Hook] is invoked. +@enddefun + +@defun[fun {in-recursive-edit}] +This returns whether the calling point is dynamically within a recursive edit +context. +@enddefun + +@defun[fun {exit-recursive-edit}, args {@optional @i[values-list]}] +@defhvar1[var {Exit Recursive Edit Hook}] +@f[exit-recursive-edit] exits a recursive edit returning as multiple values +each element of @i[values-list], which defaults to @nil. This invokes +@hid[Exit Recursive Edit Hook] after exiting the command interpreter. If no +recursive edit is in progress, then this signals an error. +@enddefun + +@defun[fun {abort-recursive-edit}, args {@rest @i[args]}] +@defhvar1[var {Abort Recursive Edit Hook}] +@f[abort-recursive-edit] terminates a recursive edit by applying +@funref[editor-error] to @i[args] after exiting the command interpreter. This +invokes @hid[Abort Recursive Edit Hook] with @i[args] before aborting the +recursive edit . If no recursive edit is in progress, then this signals an +error. +@enddefun + + + +@Chapter(Modes) +@label[modes] +@index (Modes) +A mode is a collection of @hemlock values which may be present in the current +environment @w<(page @pageref(current-environment))> depending on the editing +task at hand. Examples of typical modes are @hid[Lisp], for editing Lisp code, +and @hid[Echo Area], for prompting in the echo area. + + +@section(Mode Hooks) + When a mode is added to or removed from a buffer, its @i[mode hook] +is invoked. The hook functions take two arguments, the buffer +involved and @true if the mode is being added or @nil if it is being +removed. + +Mode hooks are typically used to make a mode do something additional to +what it usually does. One might, for example, make a text mode hook +that turned on auto-fill mode when you entered. + + +@section(Major and Minor Modes) +There are two kinds of modes, @i[major] modes and @i[minor] modes. A buffer +always has exactly one major mode, but it may have any number of minor modes. +Major modes may have mode character attributes while minor modes may not. + +A major mode is usually used to change the environment in some major way, such +as to install special commands for editing some language. Minor modes +generally change some small attribute of the environment, such as whether lines +are automatically broken when they get too long. A minor mode should work +regardless of what major mode and minor modes are in effect. + +@defhvar[var {Default Modes}, val {("Fundamental" "Save")}] +This variable contains a list of mode names which are instantiated in a +buffer when no other information is available. +@enddefhvar + +@defvar[var {mode-names}] +Holds a string-table of the names of all the modes. +@enddefvar + +@defcom[com "Illegal"] +This is a useful command to bind in modes that wish to shadow global bindings +by making them effectively illegal. Also, although less likely, minor modes +may shadow major mode bindings with this. This command calls @f[editor-error]. +@enddefcom + + +@section(Mode Functions) + +@defun[fun {defmode}, args {@i[name]}, + keys {[setup-function][cleanup-function][major-p]}, + morekeys {[precedence][transparent-p][documentation]}] +This function defines a new mode named @i[name], and enters it in +@varref[mode-names]. If @i[major-p] is supplied and is not @nil +then the mode is a major mode; otherwise it is a minor mode. + +@i[Setup-function] and @i[cleanup-function] are functions which are +invoked with the buffer affected, after the mode is turned on, and +before it is turned off, respectively. These functions typically are +used to make buffer-local key or variable bindings and to remove them +when the mode is turned off. + +@i[Precedence] is only meaningful for a minor mode. The precedence of a +minor mode determines the order in which it in a buffer's list of modes. +When searching for values in the current environment, minor modes are +searched in order, so the precedence of a minor mode determines which value +is found when there are several definitions. + +@i[Transparent-p] determines whether key bindings local to the defined mode +are transparent. Transparent key bindings are invoked in addition to the +first normal key binding found rather than shadowing less local key bindings. + +@i[Documentation] is some introductory text about the mode. Commands such as +@hid[Describe Mode] use this. +@enddefun + +@defun[fun {mode-documentation}, args {@i[name]}] +This function returns the documentation for the mode named @i[name]. +@enddefun + +@defun[fun {buffer-major-mode}, args {@i[buffer]}] +@defhvar1[var {Buffer Major Mode Hook}] +@f[buffer-major-mode] returns the name of @i[buffer]'s major mode. +The major mode may be changed with @f[setf]; then + @hid[Buffer Major Mode Hook] is invoked with +@i[buffer] and the new mode. +@enddefun + +@defun[fun {buffer-minor-mode}, args {@i[buffer] @i[name]}] +@defhvar1[var {Buffer Minor Mode Hook}] +@f[buffer-minor-mode] returns @true if the minor mode @i[name] is active +in @i[buffer], @nil otherwise. A minor mode may be turned on or off +by using @f[setf]; then @hid[Buffer Minor Mode Hook] is +invoked with @i[buffer], @i[name] and the new value. +@enddefun + +@defun[fun {mode-variables}, args {@i[name]}] +Returns the string-table of mode local variables. +@enddefun + +@defun[fun {mode-major-p}, args {@i[name]}] +Returns @true if @i[name] is the name of a major mode, or @nil if +it is the name of a minor mode. It is an error for @i[name] not to be +the name of a mode. +@enddefun + + + +@chapter(Character Attributes) +@label(character-attributes) +@index(Character attributes) +@index(Syntax tables) + +@section(Introduction) +Character attributes provide a global database of information about characters. +This facility is similar to, but more general than, the @i[syntax tables] of +other editors such as @f[EMACS]. For example, you should use character +attributes for commands that need information regarding whether a character is +@i[whitespace] or not. Use character attributes for these reasons: +@begin(enumerate) +If this information is all in one place, then it is easy the change the +behavior of the editor by changing the syntax table, much easier than it would +be if character constants were wired into commands. + +This centralization of information avoids needless duplication of effort. + +The syntax table primitives are probably faster than anything that can be +written above the primitive level. +@end(enumerate) + +Note that an essential part of the character attribute scheme is that +@i[character attributes are global and are there for the user to change.] +Information about characters which is internal to some set of commands (and +which the user should not know about) should not be maintained as a character +attribute. For such uses various character searching abilities are provided by +the function @funref[find-pattern]. + +@defcon[var {syntax-char-code-limit}] +The exclusive upper bound on character codes which are significant in +the character attribute functions. Font and bits are always ignored. +@enddefcon + + +@section(Character Attribute Names) + +As for @hemlock variables, character attributes have a user visible +string name, but are referred to in Lisp code as a symbol. The string +name, which is typically composed of capitalized words separated by +spaces, is translated into a keyword by replacing all spaces with +hyphens and interning this string in the keyword package. The +attribute named @hid[Ada Syntax] would thus become @kwd[ada-syntax]. + +@defvar[var {character-attribute-names}] +Whenever a character attribute is defined, its name is entered in +this string table (page @pageref[string-tables]), with the +corresponding keyword as the value. +@enddefvar + + +@section(Character Attribute Functions) + +@defun[fun {defattribute}, args + {@i[name] @i[documentation] @optional @i[type] @i[initial-value]}] + This function defines a new character attribute with @i[name], a +simple-string. Character attribute operations take attribute arguments as a +keyword whose name is @i[name] uppercased with spaces replaced by hyphens. + +@i[Documentation] describes the uses of the character attribute. + +@i[Type], which defaults to @w<@f[(mod 2)]>, specifies what type the values of +the character attribute are. Values of a character attribute may be of any +type which may be specified to @f[make-array]. @i[Initial-value] (default +@f[0]) is the value which all characters will initially have for this +attribute. +@enddefun + +@defun[fun {character-attribute-name}, args {@i[attribute]}] +@defun1[fun {character-attribute-documentation}, args {@i[attribute]}] +These functions return the name or documentation for @i[attribute]. +@enddefun + +@defun[fun {character-attribute}, args {@i[attribute] @i[character]}] +@defhvar1[var {Character Attribute Hook}] +@f[character-attribute] returns the value of @i[attribute] for @i[character]. +This signals an error if @i[attribute] is undefined. + +@f[setf] will set a character's attributes. This @f[setf] method invokes the +functions in @hid[Character Attribute Hook] on the attribute and character +before it makes the change. + +If @i[character] is @nil, then the value of the attribute for the beginning or +end of the buffer can be accessed or set. The buffer beginning and end thus +become a sort of fictitious character, which simplifies the use of character +attributes in many cases. +@enddefun + +@defun[fun {character-attribute-p}, args {@i[symbol]}] +This function returns @true if @i[symbol] is the name of a character attribute, +@nil otherwise. +@enddefun + +@defun[fun {shadow-attribute}, args +{@i[attribute] @i[character] @i[value] @i[mode]}] +@defhvar1[var {Shadow Attribute Hook}] +This function establishes @i[value] as the value of @i[character]'s +@i[attribute] attribute when in the mode @i[mode]. @i[Mode] must be the name +of a major mode. @hid[Shadow Attribute Hook] is invoked with the same +arguments when this function is called. If the value for an attribute is set +while the value is shadowed, then only the shadowed value is affected, not the +global one. +@enddefun + +@defun[fun {unshadow-attribute}, args {@i[attribute] @i[character] @i[mode]}] +@defhvar1[var {Unshadow Attribute Hook}] +Make the value of @i[attribute] for @i[character] no longer be shadowed in +@i[mode]. @hid[Unshadow Attribute Hook] is invoked with the same arguments +when this function is called. +@enddefun + +@defun[fun {find-attribute}, + args {@i[mark] @i[attribute] @optional @i[test]}] +@defun1[fun {reverse-find-attribute}, + args {@i[mark] @i[attribute] @optional @i[test]}] + These functions find the next (or previous) character with some value for the +character attribute @i[attribute] starting at @i[mark]. They pass @i[Test] one +argument, the value of @i[attribute] for the character tested. If the test +succeeds, then these routines modify @i[mark] to point before (after for +@f[reverse-find-attribute]) the character which satisfied the test. If no +characters satisfy the test, then these return @nil, and @i[mark] remains +unmodified. @i[Test] defaults to @f[not zerop]. There is no guarantee that +the test is applied in any particular fashion, so it should have no side +effects and depend only on its argument. +@enddefun + + +@section(Character Attribute Hooks) + +It is often useful to use the character attribute mechanism as an abstract +interface to other information about characters which in fact is stored +elsewhere. For example, some implementation of @hemlock might decide to define +a @hid[Print Representation] attribute which controls how a character is +displayed on the screen. + +To make this easy to do, each attribute has a list of hook functions +which are invoked with the attribute, character and new value whenever +the current value changes for any reason. + +@defun[fun {character-attribute-hooks}, args {@i[attribute]}] +Return the current hook list for @i[attribute]. This may be set with +@f[setf]. The @f[add-hook] and @macref[remove-hook] macros should +be used to manipulate these lists. +@enddefun + + +@section (System Defined Character Attributes) +@label(sys-def-chars) +These are predefined in @hemlock: +@begin[description] +@hid[Whitespace]@\ +A value of @f[1] indicates the character is whitespace. + +@hid[Word Delimiter]@\ +A value of @f[1] indicates the character separates words (see section +@ref[text-functions]). + +@hid[Digit]@\ +A value of @f[1] indicates the character is a base ten digit. This may be +shadowed in modes or buffers to mean something else. + +@hid[Space]@\ +This is like @hid[Whitespace], but it should not include @binding[Newline]. +@hemlock uses this primarily for handling indentation on a line. + +@hid[Sentence Terminator]@\ +A value of @f[1] indicates these characters terminate sentences (see section +@ref[text-functions]). + +@hid[Sentence Closing Char]@\ +A value of @f[1] indicates these delimiting characters, such as @binding["] +or @binding[)], may follow a @hid[Sentence Terminator] (see section +@ref[text-functions]). + +@hid[Paragraph Delimiter]@\ +A value of @f[1] indicates these characters delimit paragraphs when they begin +a line (see section @ref[text-functions]). + +@hid[Page Delimiter]@\ +A value of @f[1] indicates this character separates logical pages (see section +@ref[logical-pages]) when it begins a line. + +@hid[Scribe Syntax]@\ +This uses the following symbol values: +@begin[multiple] +@begin[description] +@nil@\These characters have no interesting properties. + +@kwd[escape]@\This is @binding[@@] for the Scribe formatting language. + +@kwd[open-paren]@\These characters begin delimited text. + +@kwd[close-paren]@\These characters end delimited text. + +@kwd[space]@\These characters can terminate the name of a formatting command. + +@kwd[newline]@\These characters can terminate the name of a formatting command. +@end[description] +@end[multiple] + + +@hid[Lisp Syntax]@\ +This uses symbol values from the following: +@begin[multiple] +@begin[description] +@nil@\These characters have no interesting properties. + +@kwd[space]@\These characters act like whitespace and should not include +@binding[Newline]. + +@kwd[newline]@\This is the @binding[Newline] character. + +@kwd[open-paren]@\This is @binding[(] character. + +@kwd[close-paren]@\This is @binding[)] character. + +@kwd[prefix]@\This is a character that is a part of any form it precedes @dash +for example, the single quote, @binding[']. + +@kwd[string-quote]@\This is the character that quotes a string literal, +@binding["].@comment["] + +@kwd[char-quote]@\This is the character that escapes a single character, +@binding[\]. + +@kwd[comment]@\This is the character that makes a comment with the rest of the +line, @binding[;]. + +@kwd[constituent]@\These characters are constitute symbol names. +@end[description] +@end[multiple] + +@end[description] + + + +@chapter (Controlling the Display) +@section (Windows) +@tag[windows] +@index(Windows) +@index(modelines) + +A window is a mechanism for displaying part of a buffer on some physical +device. A window is a way to view a buffer but is not synonymous with one; a +buffer may be viewed in any number of windows. A window may have a +@i[modeline] which is a line of text displayed across the bottom of a window to +indicate status information, typically related to the buffer displayed. + + +@section (The Current Window) +@index (Current window) +@defun[fun {current-window}, args {}] +@defhvar1[var {Set Window Hook}] +@f[current-window] returns the window in which the cursor is currently +displayed. The cursor always tracks the buffer-point of the corresponding +buffer. If the point is moved to a position which would be off the screen the +recentering process is invoked. Recentering shifts the starting point of the +window so that the point is once again displayed. The current window may be +changed with @f[setf]. Before the current window is changed, the hook @hid[Set +Window Hook] is invoked with the new value. +@enddefun + +@defvar[var {window-list}] +Holds a list of all the window objects made with @funref[make-window]. +@enddefvar + + +@section(Window Functions) + +@defun[fun {make-window}, args {@i[mark]}, + keys {[modelinep][window][ask-user]}, + morekeys {[x][y][width][height]}, + morekeys {[proportion]}] +@defhvar1[var {Default Window Width}] +@defhvar1[var {Default Window Height}] +@defhvar1[var {Make Window Hook}] + +@comment[NOTE, we purposefully do not document the font-family or device + arguments since we don't officially support fonts or devices.] + +@f[make-window] returns a window displaying text starting at @i[mark], which +must point into a buffer. If it could not make a window on the device, it +returns nil. The default action is to make the new window a proportion of the +@f[current-window]'s height to make room for the new window. + +@i[Modelinep] specifies whether the window should display buffer modelines. + +@i[Window] is a device dependent window to be used with the Hemlock window. +The device may not support this argument. @i[Window] becomes the parent window +for a new group of windows that behave in a stack orientation as windows do on +the terminal. + +If @i[ask-user] is non-@nil, @hemlock prompts the user for the missing +dimensions (@i[x], @i[y], @i[width], and @i[height]) to make a new group of +windows, as with the @i[window] argument. The device may not support this +argument. Non-null values other than @f[t] may have device dependent meanings. +@i[X] and @i[y] are in pixel units, but @i[width] and @i[height] are characters +units. @hid[Default Window Width] and @hid[Default Window Height] are the +default values for the @i[width] and @i[height] arguments. + +@i[Proportion] determines what proportion of the @f[current-window]'s height +the new window will use. The @f[current-window] retains whatever space left +after accommodating the new one. The default is to split the window in half. + +This invokes @hid[Make Window Hook] with the new window. +@enddefun + +@defun[fun {windowp}, args {@i[window]}] +This function returns @true if @i[window] is a @f[window] object, otherwise +@nil. +@enddefun + +@defun[fun {delete-window}, args {@i[window]}] +@defhvar1[var {Delete Window Hook}] +@f[delete-window] makes @i[window] go away, first invoking @hid[Delete Window +Hook] with @i[window]. +@enddefun + +@defun[fun {window-buffer}, args {@i[window]}] +@defhvar1[var {Window Buffer Hook}] +@f[window-buffer] returns the buffer from which the window displays +text. This may be changed with @f[setf], in which case the hook +@hid[Window Buffer Hook] is invoked beforehand with the window and the +new buffer. +@enddefun + +@defun[fun {window-display-start}, args {@i[window]}] +@defun1[fun {window-display-end}, args {@i[window]}] +@f[window-display-start] returns the mark that points before the first +character displayed in @i[window]. Note that if @i[window] is the current +window, then moving the start may not prove much, since recentering may move it +back to approximately where it was originally. + +@f[window-display-end] is similar, but points after the last character +displayed. Moving the end is meaningless, since redisplay always moves it to +after the last character. +@enddefun + +@defun[fun {window-display-recentering}, args {@i[window]}] +This function returns whether redisplay will ensure the buffer's point of +@i[window]'s buffer is visible after redisplay. This is @f[setf]'able, and +changing @i[window]'s buffer sets this to @nil via @hid[Window Buffer Hook]. +@enddefun + +@defun[fun {window-point}, args {@i[window]}] +This function returns as a mark the position in the buffer where the cursor is +displayed. This may be set with @f[setf]. If @i[window] is the current +window, then setting the point will have little effect; it is forced to track +the buffer point. When the window is not current, the window point is the +position that the buffer point will be moved to when the window becomes +current. +@enddefun + +@defun[fun {center-window}, args {@i[window] @i[mark]}] +This function attempts to adjust window's display start so the that @i[mark] is +vertically centered within the window. +@enddefun + +@defun[fun {scroll-window}, args {@i[window] @i[n]}] +This function scrolls the window down @i[n] display lines; if @i[n] is negative +scroll up. Leave the cursor at the same text position unless we scroll it off +the screen, in which case the cursor is moved to the end of the window closest +to its old position. +@enddefun + +@defun[fun {displayed-p}, args {@i[mark] @i[window]}] +Returns @true if either the character before or the character after @i[mark] +is being displayed in @i[window], or @nil otherwise. +@enddefun + +@defun[fun {window-height}, args {@i[window]}] +@defun1[fun {window-width}, args {@i[window]}] +Height or width of the area of the window used for displaying the +buffer, in character positions. These values may be changed with +@f[setf], but the setting attempt may fail, in which case nothing is done. +@enddefun + +@defun[fun {next-window}, args {@i[window]}] +@defun1[fun {previous-window}, args {@i[window]}] +Return the next or previous window of @i[window]. The exact meaning of next +and previous depends on the device displaying the window. It should be +possible to cycle through all the windows displayed on a device using either +next or previous (implying that these functions wrap around.) +@enddefun + + +@section(Cursor Positions) +@index(Cursor positions) +A cursor position is an absolute position within a window's coordinate +system. The origin is in the upper-left-hand corner and the unit +is character positions. + +@defun[fun {mark-to-cursorpos}, args {@i[mark] @i[window]}] +Returns as multiple values the @f[X] and @f[Y] position on which +@i[mark] is being displayed in @i[window], or @nil if it is not within the +bounds displayed. +@enddefun + +@defun[fun {cursorpos-to-mark}, args {@i[X] @i[Y] @i[window]}] +Returns as a mark the text position which corresponds to the given +(@i[X], @i[Y]) position within window, or @nil if that +position does not correspond to any text within @i[window]. +@enddefun + +@defun[fun {last-key-event-cursorpos}] +Interprets mouse input. It returns as multiple values the (@i[X], @i[Y]) +position and the window where the pointing device was the last time some key +event happened. If the information is unavailable, this returns @nil. +@enddefun + +@defun[fun {mark-column}, args {@i[mark]}] +This function returns the @i[X] position at which @i[mark] would be displayed, +supposing its line was displayed on an infinitely wide screen. This takes into +consideration strange characters such as tabs. +@enddefun + +@defun[fun {move-to-column}, args {@i[mark] @i[column] @optional @i[line]}] +This function is analogous to @funref[move-to-position], except that +it moves @i[mark] to the position on @i[line] which corresponds to the +specified @i[column]. @i[Line] defaults to the line that @i[mark] is +currently on. If the line would not reach to the specified column, +then @nil is returned and @i[mark] is not modified. Note that since a +character may be displayed on more than one column on the screen, +several different values of @i[column] may cause @i[mark] to be moved +to the same position. +@enddefun + +@defun[fun {show-mark}, args {@i[mark] @i[window] @i[time]}] +This function highlights the position of @i[mark] within @i[window] for +@i[time] seconds, possibly by moving the cursor there. The wait may be aborted +if there is pending input. If @i[mark] is positioned outside the text +displayed by @i[window], then this returns @nil, otherwise @true. +@enddefun + + +@section(Redisplay) +Redisplay translates changes in the internal representation of text into +changes on the screen. Ideally this process finds the minimal transformation +to make the screen correspond to the text in order to maximize the speed of +redisplay. + +@defun[fun {redisplay}] +@defhvar1[var "Redisplay Hook"] +@f[redisplay] executes the redisplay process, and @hemlock typically invokes +this whenever it looks for input. The redisplay process frequently checks for +input, and if it detects any, it aborts. The return value is interpreted as +follows: +@begin[description] +@false@\No update was needed. + +@true@\Update was needed, and completed successfully. + +@kwd[editor-input]@\Update is needed, but was aborted due to pending input. +@end[description] + +This function invokes the functions in @hid[Redisplay Hook] on the current +window after computing screen transformations but before executing them. After +invoking the hook, this recomputes the redisplay and then executes it on the +current window. + +For the current window and any window with @f[window-display-recentering] set, +@f[redisplay] ensures the buffer's point for the window's buffer is visible +after redisplay. +@enddefun + +@defun[fun {redisplay-all}] +This causes all editor windows to be completely redisplayed. For the current +window and any window with @f[window-display-recentering] set, this ensures the +buffer's point for the window's buffer is visible after redisplay. The return +values are the same as for redisplay, except that @false is never returned. +@enddefun + +@defun[fun {editor-finish-output}, args {@i[window]}] +This makes sure the editor is synchronized with respect to redisplay output to +@i[window]. This may do nothing on some devices. +@enddefun + + + +@chapter(Logical Key-Events) +@label[logical-key-events] +@index[Logical key-events] + + +@section[Introduction] +Some primitives such as @funref[prompt-for-key] and commands such as EMACS +query replace read key-events directly from the keyboard instead of using the +command interpreter. To encourage consistency between these commands and to +make them portable and easy to customize, there is a mechanism for defining +@i[logical key-events]. + +A logical key-event is a keyword which stands for some set of key-events. The +system globally interprets these key-events as indicators a particular action. +For example, the @kwd[help] logical key-event represents the set of key-events +that request help in a given @hemlock implementation. This mapping is a +many-to-many mapping, not one-to-one, so a given logical key-event may have +multiple corresponding actual key-events. Also, any key-event may represent +different logical key-events. + + +@section[Logical Key-Event Functions] + +@defvar[var {logical-key-event-names}] +This variable holds a string-table mapping all logical key-event names to the +keyword identifying the logical key-event. +@enddefvar + +@defun[fun {define-logical-key-event}, args {@i[string-name] @i[documentation]}] + This function defines a new logical key-event with name @i[string-name], a +simple-string. Logical key-event operations take logical key-events arguments +as a keyword whose name is @i[string-name] uppercased with spaces replaced by +hyphens. + +@i[Documentation] describes the action indicated by the logical key-event. +@enddefun + +@defun[fun {logical-key-event-key-events}, args {@i[keyword]}] +This function returns the list of key-events representing the logical key-event +@i[keyword]. +@enddefun + +@defun[fun {logical-key-event-name}, args {@i[keyword]}] +@defun1[fun {logical-key-event-documentation}, args {@i[keyword]}] +These functions return the string name and documentation given to +@f[define-logical-key-event] for logical key-event @i[keyword]. +@enddefun + +@defun[fun {logical-key-event-p}, args {@i[key-event] @i[keyword]}] +This function returns @f[t] if @i[key-event] is the logical key-event +@i[keyword]. This is @f[setf]'able establishing or disestablishing key-events +as particular logical key-events. It is a error for @i[keyword] to be an +undefined logical key-event. +@enddefun + + +@section[System Defined Logical Key-Events] +There are many default logical key-events, some of which are used by functions +documented in this manual. If a command wants to read a single key-event +command that fits one of these descriptions then the key-event read should be +compared to the corresponding logical key-event instead of explicitly +mentioning the particular key-event in the code. In many cases you can use the +@macref[command-case] macro. It makes logical key-events easy to use and takes +care of prompting and displaying help messages. + +@begin[description] +@kwd[yes]@\ + Indicates the prompter should take the action under consideration. + +@kwd[no]@\ + Indicates the prompter should NOT take the action under consideration. + +@kwd[do-all]@\ + Indicates the prompter should repeat the action under consideration as many +times as possible. + +@kwd[do-once]@\ + Indicates the prompter should execute the action under consideration once and +then exit. + +@kwd[exit]@\ + Indicates the prompter should terminate its activity in a normal fashion. + +@kwd[abort]@\ + Indicates the prompter should terminate its activity without performing any +closing actions of convenience, for example. + +@kwd[keep]@\ + Indicates the prompter should preserve something. + +@kwd[help]@\ + Indicates the prompter should display some help information. + +@kwd[confirm]@\ + Indicates the prompter should take any input provided or use the default if +the user entered nothing. + +@kwd[quote]@\ + Indicates the prompter should take the following key-event as itself without +any sort of command interpretation. + +@kwd[recursive-edit]@\ + Indicates the prompter should enter a recursive edit in the current context. + +@kwd[cancel]@\ + Indicates the prompter should cancel the effect of a previous key-event input. + +@kwd[forward-search]@\ + Indicates the prompter should search forward in the current context. + +@kwd[backward-search]@\ + Indicates the prompter should search backward in the current context. +@end[description] + +@blankspace(1 line) +Define a new logical key-event whenever: +@begin[enumerate] +The key-event concerned represents a general class of actions, and +several commands may want to take a similar action of this type. + +The exact key-event a command implementor chooses may generate violent taste +disputes among users, and then the users can trivially change the command in +their init files. + +You are using @f[command-case] which prevents implementors from specifying +non-standard characters for dispatching in otherwise possibly portable code, +and you can define and set the logical key-event in a site dependent file where +you can mention implementation dependent characters. +@end[enumerate] + + + +@chapter(The Echo Area) + +@hemlock provides a number of facilities for displaying information and +prompting the user for it. Most of these work through a small window displayed +at the bottom of the screen. This is called the echo area and is supported by +a buffer and a window. This buffer's modeline (see section @ref[modelines]) is +referred to as the status line, which, unlike other buffers' modelines, is used +to show general status about the editor, Lisp, or world. + +@defhvar[var {Default Status Line Fields}] +This is the initial list of modeline-field objects stored in the echo area +buffer. +@enddefhvar + +@defhvar[var "Echo Area Height", val {3}] +This variable determines the initial height in lines of the echo area window. +@enddefhvar + + +@section(Echo Area Functions) +It is considered poor taste to perform text operations on the echo area buffer +to display messages; the @f[message] function should be used instead. A +command must use this function or set @funref[buffer-modified] for the +@hid[Echo Area] buffer to @nil to cause @hemlock to leave text in the echo area +after the command's execution. + +@defun[fun {clear-echo-area}] +Clears the echo area. +@enddefun + +@defun[fun {message}, args {@i[control-string] @rest @i[format-arguments]}] +@defun1[fun {loud-message}, args {@i[control-string] @rest @i[format-arguments]}] +@defhvar1[var {Message Pause}, val {0.5}] +Displays a message in the echo area. The message is always displayed on a +fresh line. @f[message] pauses for @hid[Message Pause] seconds before +returning to assure that messages are not displayed too briefly to be seen. +Because of this, @f[message] is the best way to display text in the echo area. + +@f[loud-message] is like @f[message], but it first clears the echo area and +beeps. +@enddefun + +@defvar[var {echo-area-window}] +@defvar1[var {echo-area-buffer}] +@f[echo-area-buffer] contains the buffer object for the echo area, which is +named @hid[Echo Area]. This buffer is usually in @hid[Echo Area] mode. +@f[echo-area-window] contains a window displaying @f[echo-area-buffer]. Its +modeline is the status line, see the beginning of this chapter. +@enddefvar + +@defvar[var {echo-area-stream}] +@index (Echo area) +This is a buffered @hemlock output stream +(@pageref[make-hemlock-output-stream-fun]) which inserts text written to it at +the point of the echo area buffer. Since this stream is buffered a +@f[force-output] must be done when output is complete to assure that it is +displayed. +@enddefvar + + +@section(Prompting Functions) +@index(Prompting functions) +Most of the prompting functions accept the following keyword arguments: +@begin(description) +@kwd[must-exist] @\If @kwd[must-exist] has a non-@nil value then the +user is prompted until a valid response is obtained. If +@kwd[must-exist] is @nil then return as a string whatever is input. +The default is @true. + +@kwd[default] @\If null input is given when the user is prompted +then this value is returned. If no default is given then +some input must be given before anything interesting will happen. + +@kwd[default-string] @\If a @kwd[default] is given then this is a +string to be printed to indicate what the default is. The default is +some representation of the value for @kwd[default], for example for a +buffer it is the name of the buffer. + +@kwd[prompt] @\This is the prompt string to display. + +@kwd[help] @\@multiple{ +This is similar to @kwd[prompt], except that it is displayed when +the help command is typed during input. @comment{If there is some known number +of options as in keyword parses, then they may be displayed, depending +on the setting of @hvarref[Help Show Options].} + +This may also be a function. When called with no arguments, it should either +return a string which is the help text or perform some action to help the user, +returning @Nil.} +@end(description) + +@defun[fun {prompt-for-buffer}, keys {[prompt][help][must-exist][default]}, + morekeys {[default-string]}] +Prompts with completion for a buffer name and returns the corresponding buffer. +If @i[must-exist] is @nil, then it returns the input string if it is not a +buffer name. This refuses to accept the empty string as input when +@kwd[default] and @kwd[default-string] are @nil. @kwd[default-string] may be +used to supply a default buffer name when @kwd[default] is @nil, but when +@kwd[must-exist] is non-@nil, it must name an already existing buffer. +@enddefun + +@defmac[fun {command-case}, Args {(@mstar<@i[key] @i[value]>) @Mstar<(@Mgroup"(@MSTAR'@i[tag]') @MOR @i[tag]" @i[help] @MSTAR'@i[form]')>}] + This macro is analogous to the Common Lisp @f[case] macro. Commands such as +@hid[Query Replace] use this to get a key-event, translate it to a character, +and then to dispatch on the character to some case. In addition to character +dispatching, this supports logical key-events @w<(page +@pageref[logical-key-events])> by using the input key-event directly without +translating it to a character. Since the description of this macro is rather +complex, first consider the following example: +@lisp +(defcommand "Save All Buffers" (p) + "Give the User a chance to save each modified buffer." + "Give the User a chance to save each modified buffer." + (dolist (b *buffer-list*) + (select-buffer-command () b) + (when (buffer-modified b) + (command-case (:prompt "Save this buffer: [Y] " + :help "Save buffer, or do something else:") + ((:yes :confirm) + "Save this buffer and go on to the next." + (save-file-command () b)) + (:no "Skip saving this buffer, and go on to the next.") + (:recursive-edit + "Go into a recursive edit in this buffer." + (do-recursive-edit) (reprompt)) + ((:exit #\p) "Punt this silly loop." + (return nil)))))) +@endlisp + +@f[command-case] prompts for a key-event and then executes the code in the +first branch with a logical key-event or a character (called @i[tags]) matching +the input. Each character must be a standard-character, one that satisfies the +Common Lisp @f[standard-char-p] predicate, and the dispatching mechanism +compares the input key-event to any character tags by mapping the key-event to +a character with @f[ext:key-event-char]. If the tag is a logical key-event, +then the search for an appropriate case compares the key-event read with the +tag using @f[logical-key-event-p]. + +All uses of @f[command-case] have two default cases, @kwd[help] and +@kwd[abort]. You can override these easily by specifying your own branches +that include these logical key-event tags. The @kwd[help] branch displays in a +pop-up window the a description of the valid responses using the variously +specified help strings. The @kwd[abort] branch signals an editor-error. + +The @i[key]/@i[value] arguments control the prompting. The following are valid +values: +@begin[description] +@kwd[help]@\ + The default @kwd[help] case displays this string in a pop-up window. In +addition it formats a description of the valid input including each case's +@i[help] string. + +@kwd[prompt]@\ + This is the prompt used when reading the key-event. + +@kwd[change-window]@\ + If this is non-nil (the default), then the echo area window becomes the +current window while the prompting mechanism reads a key-event. Sometimes it +is desirable to maintain the current window since it may be easier for users to +answer the question if they can see where the current point is. + +@kwd[bind]@\ + This specifies a variable to which the prompting mechanism binds the input +key-event. Any case may reference this variable. If you wish to know what +character corresponds to the key-event, use @f[ext:key-event-char]. +@end(description) + +Instead of specifying a tag or list of tags, you may use @true. This becomes +the default branch, and its forms execute if no other branch is taken, +including the default @kwd[help] and @kwd[abort] cases. This option has no +@i[help] string, and the default @kwd[help] case does not describe the default +branch. Every @f[command-case] has a default branch; if none is specified, the +macro includes one that @f[system:beep]'s and @f[reprompt]'s (see below). + +Within the body of @f[command-case], there is a defined @f[reprompt] macro. +It causes the prompting mechanism and dispatching mechanism to immediately +repeat without further execution in the current branch. +@enddefmac + + +@defun[fun {prompt-for-key-event}, keys {[prompt][change-window]}] +This function prompts for a key-event returning immediately when the user types +the next key-event. @macref[command-case] is more useful for most purposes. +When appropriate, use logical key-events @w<(page +@pageref[logical-key-events])>. +@enddefun + +@defun[fun {prompt-for-key}, keys {[prompt][help][must-exist][default]}, + morekeys {[default-string]}] + This function prompts for a @i[key], a vector of key-events, suitable for +passing to any of the functions that manipulate key bindings @w<(page +@pageref[key-bindings])>. If @i[must-exist] is true, then the key must be +bound in the current environment, and the command currently bound is returned +as the second value. +@enddefun + +@defun[fun {prompt-for-file}, keys {[prompt][help][must-exist][default]}, + morekeys {[default-string]}] + This function prompts for an acceptable filename in some system dependent +fashion. "Acceptable" means that it is a legal filename, and it exists if +@i[must-exist] is non-@nil. @f[prompt-for-file] returns a Common Lisp +pathname. + +If the file exists as entered, then this returns it, otherwise it is merged +with @i[default] as by @f[merge-pathnames]. +@enddefun + +@defun[fun {prompt-for-integer}, keys {[prompt][help][must-exist][default]}, + morekeys {[default-string]}] + This function prompts for a possibly signed integer. If @i[must-exist] is +@nil, then @f[prompt-for-integer] returns the input as a string if it is not a +valid integer. +@enddefun + +@defun[fun {prompt-for-keyword}, args {@i[string-tables]}, + keys {[prompt][help][must-exist]}, + morekeys {[default][default-string]}] + This function prompts for a keyword with completion, using the string tables +in the list @i[string-tables]. If @I[must-exist] is non-@nil, then the result +must be an unambiguous prefix of a string in one of the @i[string-tables], and +the returns the complete string even if only a prefix of the full string was +typed. In addition, this returns the value of the corresponding entry in the +string table as the second value. + +If @i[must-exist] is @nil, then this function returns the string exactly as +entered. The difference between @f[prompt-for-keyword] with @i[must-exist] +@nil, and @f[prompt-for-string], is the user may complete the input using the +@hid and @hid commands. +@enddefun + +@defun[fun {prompt-for-expression}, + keys {[prompt][help][must-exist][default]}, + morekeys {[default-string]}] + This function reads a Lisp expression. If @i[must-exist] is @nil, and a read +error occurs, then this returns the string typed. +@enddefun + +@defun[fun {prompt-for-string}, keys +{[prompt][help][default][default-string]}] + This function prompts for a string; this cannot fail. +@enddefun + +@defun[fun {prompt-for-variable}, keys {[prompt][help][must-exist][default]}, + morekeys {[default-string]}] + This function prompts for a variable name. If @i[must-exist] is non-@nil, +then the string must be a variable @i[defined in the current environment], in +which case the symbol name of the variable found is returned as the second +value. +@enddefun + +@defun[fun {prompt-for-y-or-n}, keys {[prompt][help][must-exist][default]}, + morekeys {[default-string]}] + This prompts for @binding[y], @binding[Y], @binding[n], or @binding[N], +returning @true or @nil without waiting for confirmation. When the user types +a confirmation key, this returns @i[default] if it is supplied. If +@i[must-exist] is @nil, this returns whatever key-event the user first types; +however, if the user types one of the above key-events, this returns @true or +@nil. This is analogous to the Common Lisp function @f[y-or-n-p]. +@enddefun + +@defun[fun {prompt-for-yes-or-no}, keys {[prompt][help][must-exist][default]}, + morekeys {[default-string]}] + This function is to @f[prompt-for-y-or-n] as @f[yes-or-no-p] is to +@f[y-or-n-p]. "Yes" or "No" must be typed out in full and +confirmation must be given. +@enddefun + + +@section(Control of Parsing Behavior) + +@defhvar[var {Beep On Ambiguity}, val {@true}] +If this variable is true, then an attempt to complete a parse which is +ambiguous will result in a "beep". +@enddefhvar + + +@begin(comment) +@hemlock provides for limited control of parsing routine behaviour The +character attribute @hid[Parse Field Separator] is a boolean attribute, a value +of @f[1] indicating that the character is a field separator recognized by the +@hid command. +@end(comment) + +@begin(comment) +@defhvar[var {Help Show Options}] +During a keyword or similar parse, typing the help command may cause a +list of options to be displayed. If displaying the help would take up +more lines than the value of this variable then confirmation will be +asked for before they will be displayed. +@enddefhvar +@end(comment) + + + +@section(Defining New Prompting Functions) +Prompting functions are implemented as a recursive edit in the +@hid[Echo Area] buffer. Completion, help, and other parsing features +are implemented by commands which are bound in @hid[Echo Area Mode]. + +A prompting function passes information down into the recursive edit +by binding a collection of special variables. + +@defvar[var {parse-verification-function}] +The system binds this to a function that @comref[Confirm Parse] calls. It does +most of the work when parsing prompted input. @comref[Confirm Parse] passes +one argument, which is the string that was in @var when the +user invokes the command. The function should return a list of values which +are to be the result of the recursive edit, or @nil indicating that the parse +failed. In order to return zero values, a non-@nil second value may be +returned along with a @nil first value. +@enddefvar + +@defvar[var {parse-string-tables}] +This is the list of @f[string-table]s, if any, that pertain to this parse. +@enddefvar + +@defvar[var {parse-value-must-exist}] +This is bound to the value of the @kwd[must-exist] argument, and is +referred to by the verification function, and possibly some of the +commands. +@enddefvar + +@defvar[var {parse-default}] +When prompting the user, this is bound to a string representing the default +object, the value supplied as the @kwd[default] argument. @hid +supplies this to the parse verification function when the +@var is empty. +@enddefvar + +@defvar[var {parse-default-string}] +When prompting the user, if @var[parse-default] is @nil, @hemlock displays this +string as a representation of the default object; for example, when prompting +for a buffer, this variable would be bound to the buffer name. +@enddefvar + +@defvar[var {parse-type}] +The kind of parse in progress, one of @kwd[file], @kwd[keyword] or +@kwd[string]. This tells the completion commands how to do completion, with +@kwd[string] disabling completion. +@enddefvar + +@defvar[var {parse-prompt}] +The prompt being used for the current parse. +@enddefvar + +@defvar[var {parse-help}] +The help string or function being used for the current parse. +@enddefvar + +@defvar[var {parse-starting-mark}] +This variable holds a mark in the @varref[echo-area-buffer] which +is the position at which the parse began. +@enddefvar + +@defvar[var {parse-input-region}] +This variable holds a region with @var[parse-starting-mark] as its +start and the end of the echo-area buffer as its end. When +@hid[Confirm Parse] is called, the text in this region is the text +that will be parsed. +@enddefvar + + +@section(Some Echo Area Commands) + +These are some of the @hid[Echo Area] commands that coordinate with the +prompting routines. @Hemlock binds other commands specific to the @hid[Echo +Area], but they are uninteresting to mention here, such as deleting to the +beginning of the line or deleting backwards a word. + +@defcom[com {Help On Parse}, + stuff (bound to @bf[Home, C-_] in @hid[Echo Area] mode)] +Display the help text for the parse currently in progress. +@enddefcom + +@defcom[com {Complete Keyword}, + stuff (bound to @bf[Escape] in @hid[Echo Area] mode)] +This attempts to complete the current region as a keyword in +@var[string-tables]. It signals an editor-error if the input is ambiguous +or incorrect. +@enddefcom + +@defcom[com {Complete Field}, + stuff (bound to @bf[Space] in @hid[Echo Area] mode)] +Similar to @hid[Complete Keyword], but only attempts to complete up to and +including the first character in the keyword with a non-zero +@kwd[parse-field-separator] attribute. If +there is no field separator then attempt to complete the entire keyword. +If it is not a keyword parse then just self-insert. +@enddefcom + +@defcom[com {Confirm Parse}, + stuff (bound to @bf[Return] in @hid[Echo Area] mode)] +If @var[string-tables] is non-@nil find the string in the region in +them. Call @var[parse-verification-function] with the current input. +If it returns a non-@nil value then that is returned as the value of +the parse. A parse may return a @nil value if the verification +function returns a non-@nil second value. +@enddefcom + + + +@chapter (Files) +@index (Files) +This chapter discusses ways to read and write files at various levels @dash at +marks, into regions, and into buffers. This also treats automatic mechanisms +that affect the state of buffers in which files are read. + +@section (File Options and Type Hooks) +@index (File options) +@index (Type hooks) +@index (File type hooks) +The user specifies file options with a special syntax on the first line of a +file. If the first line contains the string "@f[-*-]", then @hemlock +interprets the text between the first such occurrence and the second, which +must be contained in one line , as a list of @w{"@f<@i[option]: @i[value]>"} +pairs separated by semicolons. The following is a typical example: +@begin[programexample] +;;; -*- Mode: Lisp, Editor; Package: Hemlock -*- +@end[programexample] +See the @i[Hemlock User's Manual] for more details and predefined options. + +File type hooks are executed when @hemlock reads a file into a buffer based on +the type of the pathname. When the user specifies a @hid[Mode] file option +that turns on a major mode, @hemlock ignores type hooks. This mechanism is +mostly used as a simple means for turning on some appropriate default major +mode. + +@defmac[fun {define-file-option}, args +{@i[name] (@i[buffer] @i[value]) @mstar<@i[declaration]> @mstar<@i[form]>}] +This defines a new file option with the string name @i[name]. @i[Buffer] and +@i[value] specify variable names for the buffer and the option value string, +and @i[form]'s are evaluated with these bound. +@enddefmac + +@defmac[fun {define-file-type-hook}, args +{@i[type-list] (@i[buffer] @i[type]) @mstar<@i[declaration]> @mstar<@i[form]>}] + +This defines some code that @f[process-file-options] (below) executes when the +file options fail to set a major mode. This associates each type, a +@f[simple-string], in @i[type-list] with a routine that binds @i[buffer] to the +buffer the file is in and @i[type] to the type of the pathname. +@enddefmac + +@defun[fun {process-file-options}, args {@i[buffer] @optional @i[pathname]}] +This checks for file options in buffer and invokes handlers if there are any. +@i[Pathname] defaults to @i[buffer]'s pathname but may be @nil. If there is no +@hid[Mode] file option that specifies a major mode, and @i[pathname] has a +type, then this tries to invoke the appropriate file type hook. +@f[read-buffer-file] calls this. +@enddefun + + +@section (Pathnames and Buffers) +There is no good way to uniquely identify buffer names and pathnames. However, +@hemlock has one way of mapping pathnames to buffer names that should be used +for consistency among customizations and primitives. Independent of this, +@hemlock provides a means for consistently generating prompting defaults when +asking the user for pathnames. + +@defun[fun {pathname-to-buffer-name}, args {@i[pathname]}] +This function returns a string of the form "@f[file-namestring] +@f[directory-namestring]". +@enddefun + +@defhvar[var "Pathname Defaults", val {(pathname "gazonk.del")}] +@defhvar1[var "Last Resort Pathname Defaults Function"] +@defhvar1[var "Last Resort Pathname Defaults", val {(pathname "gazonk")}] +These variables control the computation of default pathnames when needed for +promting the user. @hid[Pathname Defaults] is a @i[sticky] default. +See the @i[Hemlock User's Manual] for more details. +@enddefhvar + +@defun[fun {buffer-default-pathname}, args {@i[buffer]}] +This returns @hid[Buffer Pathname] if it is bound. If it is not bound, and +@i[buffer]'s name is composed solely of alphnumeric characters, then return a +pathname formed from @i[buffer]'s name. If @i[buffer]'s name has other +characters in it, then return the value of @hid[Last Resort Pathname Defaults +Function] called on @i[buffer]. +@enddefun + +@section (File Groups) +@index (File groups) +File groups provide a simple way of collecting the files that compose a system +and naming that collection. @Hemlock supports commands for searching, +replacing, and compiling groups. + +@defvar[var {active-file-group}] +This is the list of files that constitute the currently selected file group. +If this is @nil, then there is no current group. +@enddefvar + +@defmac[fun {do-active-group}, args {@mstar<@i[form]>}] +@defhvar1[var "Group Find File", val {nil}] +@defhvar1[var "Group Save File Confirm", val {t}] +@f[do-active-group] iterates over @var[active-file-group] executing the forms +once for each file. While the forms are executing, the file is in the current +buffer, and the point is at the beginning. If there is no active group, this +signals an editor-error. + +This reads each file into its own buffer using @f[find-file-buffer]. Since +unwanted buffers may consume large amounts of memory, @hid[Group Find File] +controls whether to delete the buffer after executing the forms. When the +variable is false, this deletes the buffer if it did not previously exist; +however, regardless of this variable, if the user leaves the buffer modified, +the buffer persists after the forms have completed. Whenever this processes a +buffer that already existed, it saves the location of the buffer's point before +and restores it afterwards. + +After processing a buffer, if it is modified, @f[do-active-group] tries to save +it. If @hid[Group Save File Confirm] is non-@nil, it asks for confirmation. +@enddefmac + + +@section (File Reading and Writing) +Common Lisp pathnames are used by the file primitives. For probing, checking +write dates, and so forth, all of the Common Lisp file functions are available. + +@defun[fun {read-file}, args {@i[pathname] @i[mark]}] +This inserts the file named by @i[pathname] at @i[mark]. +@enddefun + +@defun[fun {write-file}, args {@i[region] @i[pathname]}, + keys {[keep-backup][access][append]}] +@defhvar1[var {Keep Backup Files}, val {@nil}] +This function writes the contents of @i[region] to the file named by +@i[pathname]. This writes @i[region] using a stream as if it were opened with +@kwd[if-exists] supplied as @kwd[rename-and-delete]. + +When @i[keep-backup], which defaults to the value of @hid[Keep Backup Files], +is non-@nil, this opens the stream as if @kwd[if-exists] were @kwd[rename]. If +@i[append] is non-@nil, this writes the file as if it were opened with +@kwd[if-exists] supplied as @kwd[append]. + +This signals an error if both @i[append] and @i[keep-backup] are supplied as +non-@nil. + +@i[Access] is an implementation dependent value that is suitable for setting +@i[pathname]'s access or protection bits. +@enddefun + + +@defun[fun {write-buffer-file}, args {@i[buffer] @i[pathname]}] +@defhvar1[var {Write File Hook}] +@defhvar1[var {Add Newline at EOF on Writing File}, val {@kwd[ask-user]}] +@f[write-buffer-file] writes @i[buffer] to the file named by @i[pathname] +including the following: +@begin[itemize] +It assumes pathname is somehow related to @i[buffer]'s pathname: if the +@i[buffer]'s write date is not the same as @i[pathname]'s, then this prompts +the user for confirmation before overwriting the file. + +It consults @hid[Add Newline at EOF on Writing File] (see @i[Hemlock User's +Manual] for possible values) and interacts with the user if necessary. + +It sets @hid[Pathname Defaults], and after using @f[write-file], marks +@i[buffer] unmodified. + +It updates @i[Buffer]'s pathname and write date. + +It renames the buffer according to the new pathname if possible. + +It invokes @hid[Write File Hook]. +@end[itemize] + +@hid[Write File Hook] is a list of functions that take the newly written buffer +as an argument. +@enddefun + + +@defun[fun {read-buffer-file}, args {@i[pathname] @i[buffer]}] +@defhvar1[var {Read File Hook}] +@f[read-buffer-file] deletes @i[buffer]'s region and uses @f[read-file] to read +@i[pathname] into it, including the following: +@begin[itemize] +It sets @i[buffer]'s write date to the file's write date if the file exists; +otherwise, it @f[message]'s that this is a new file and sets @i[buffer]'s write +date to @nil. + +It moves @i[buffer]'s point to the beginning. + +It sets @i[buffer]'s unmodified status. + +It sets @i[buffer]'s pathname to the result of probing @i[pathname] if the file +exists; otherwise, this function sets @i[buffer]'s pathname to the result of +merging @i[pathname] with @f[default-directory]. + +It sets @hid[Pathname Defaults] to the result of the previous item. + +It processes the file options. + +It invokes @hid[Read File Hook]. +@end[itemize] + +@hid[Read File Hook] is a list functions that take two arguments @dash the +buffer read into and whether the file existed, @true if so. +@enddefun + + +@defun[fun {find-file-buffer}, args {@i[pathname]}] +This returns a buffer assoicated with the @i[pathname], reading the file into a +new buffer if necessary. This returns a second value indicating whether a new +buffer was created, @true if so. If the file has already been read, this +checks to see if the file has been modified on disk since it was read, giving +the user various recovery options. This is the basis of the @hid[Find File] +command. +@enddefun + + + +@chapter (Hemlock's Lisp Environment) + +@index (Lisp environment) +This chapter is sort of a catch all for any functions and variables +which concern @hemlock's interaction with the outside world. + +@section(Entering and Leaving the Editor) + +@defun[fun {ed}, args {@optional @i[x]}] +@defhvar1[var "Entry Hook"] +@f[ed] enters the editor. It is basically as specified in Common Lisp. If +@i[x] is supplied and is a symbol, the definition of @i[x] is put into a +buffer, and that buffer is selected. If @i[x] is a pathname, the file +specified by @i[x] is visited in a new buffer. If @i[x] is not supplied or +@nil, the editor is entered in the same state as when last exited. + +The @hid[Entry Hook] is invoked each time the editor is entered. +@enddefhvar + +@defun[fun {exit-hemlock}, args {@optional @i[value]}] +@defhvar1[var {Exit Hook}] +@f[exit-hemlock] leaves @hemlock and return to Lisp; @i[value] is the +value to return, which defaults to @true. The hook +@hvarref[Exit Hook] is invoked before this is done. +@enddefun + +@defun[fun {pause-hemlock}] +@f[pause-hemlock] suspends the editor process and returns control to the shell. +When the process is resumed, it will still be running @hemlock. +@enddefun + + +@section(Keyboard Input) +@index(I/O) +@index[keyboard input] +@index[input, keyboard] + +Keyboard input interacts with a number of other parts of the editor. Since the +command loop works by reading from the keyboard, keyboard input is the initial +cause of everything that happens. Also, @hemlock redisplays in the low-level +input loop when there is no available input from the user. + + +@defvar[var {editor-input}] +@defvar1[var {real-editor-input}] +@defhvar1[var "Input Hook"] +@defhvar1[var "Abort Hook"] +@index[aborting] +@var[editor-input] is an object on which @hemlock's I/O routines operate. You +can get input, clear input, return input, and listen for input. Input appears +as key-events. + +@var[real-editor-input] holds the initial value of @var[editor-input]. This is +useful for reading from the user when @var[editor-input] is rebound (such as +within a keyboard macro.) + +@Hemlock invokes the functions in @hid[Input Hook] each time someone reads a +key-event from @var[real-editor-input]. These take no arguments. +@enddefvar + +@defun[fun {get-key-event}, args {@i[editor-input] @optional @i[ignore-abort-attempts-p]}] +This function returns a key-event as soon as it is available on +@i[editor-input]. @i[Editor-input] is either @var[editor-input] or +@var[real-editor-input]. @i[Ignore-abort-attempts-p] indicates whether +@binding[C-g] and @binding[C-G] throw to the editor's top-level command loop; +when this is non-nil, this function returns those key-events when the user +types them. Otherwise, it aborts the editor's current state, returning to the +command loop. + +When the user aborts, @Hemlock invokes the functions in @hid[Abort Hook]. +These functions take no arguments. When aborting, @Hemlock ignores the +@hid[Input Hook]. +@enddefun + + +@defun[fun {unget-key-event}, args {@i[key-event] @i[editor-input]}] +This function returns @i[key-event] to @i[editor-input], so the next invocation +of @f[get-key-event] will return @i[key-event]. If @i[key-event] is +@f[#k"C-g"] or @f[#k"C-G"], then whether @f[get-key-event] returns it depends +on that function's second argument. @i[Editor-input] is either +@var[editor-input] or @var[real-editor-input]. +@enddefun + +@defun[fun {clear-editor-input}, args {@i[editor-input]}] +This function flushes any pending input on @i[editor-input]. @i[Editor-input] +is either @var[editor-input] or @var[real-editor-input]. +@enddefun + +@defun[fun {listen-editor-input}, args {@i[editor-input]}] +This function returns whether there is any input available on @i[editor-input]. +@i[Editor-input] is either @var[editor-input] or @var[real-editor-input]. +@enddefun + +@defun[fun {editor-sleep}, args {@i[time]}] +Return either after @i[time] seconds have elapsed or when input is available on +@var[editor-input]. +@enddefun + +@defvar[var {key-event-history}] +This is a @hemlock ring buffer (see page @pageref[rings]) that holds the last +60 key-events read from the keyboard. +@enddefvar + +@defvar[var {last-key-event-typed}] +Commands use this variable to realize the last key-event the user typed to +invoke the commands. Before @hemlock ever reads any input, the value is @nil. +This variable usually holds the last key-event read from the keyboard, but it +is also maintained within keyboard macros allowing commands to behave the same +on each repetition as they did in the recording invocation. +@enddefvar + +@defvar[var {input-transcript}] +If this is non-@nil then it should be an adjustable vector with a fill-pointer. +When it is non-@nil, @hemlock pushes all input read onto this vector. +@enddefvar + + + +@section(Hemlock Streams) +It is possible to create streams which output to or get input from a buffer. +This mechanism is quite powerful and permits easy interfacing of @hemlock to +Lisp. + +@defun[fun {make-hemlock-output-stream}, args + {@i[mark] @optional @i[buffered]}] +@defun1[fun {hemlock-output-stream-p}, args {@i[object]}] +@f[make-hemlock-output-stream] returns a stream that inserts at the permanent +mark @i[mark] all output directed to it. @i[Buffered] controls whether the +stream is buffered or not, and its valid values are the following keywords: +@begin[description] +@kwd[none]@\No buffering is done. This is the default. + +@kwd[line]@\The buffer is flushed whenever a newline is written or +when it is explicitly done with @f[force-output]. + +@kwd[full]@\The screen is only brought up to date when it is +explicitly done with @f[force-output] +@end[description] + +@f[hemlock-output-stream-p] returns @true if @i[object] is a +@f[hemlock-output-stream] object. +@enddefun + +@defun[fun {make-hemlock-region-stream}, args {@i[region]}] +@defun1[fun {hemlock-region-stream-p}, args {@i[object]}] +@f[make-hemlock-region-stream] returns a stream from which the text in +@i[region] can be read. @f[hemlock-region-stream-p] returns @true if +@i[object] is a @f[hemlock-region-stream] object. +@enddefun + +@defmac[fun {with-input-from-region}, args +{(@i[var] @i[region]) @mstar<@i[declaration]> @mstar<@i[form]>}] +While evaluating @i[form]s, binds @i[var] to a stream which returns input +from @i[region]. +@enddefmac + +@defmac[fun {with-output-to-mark}, args +{(@i[var] @i[mark] @mopt<@i"buffered">) @mstar<@i[declaration]> @mstar<@i[form]>}] + During the evaluation of the @i[form]s, binds @i[var] to a stream which +inserts output at the permanent @i[mark]. @i[Buffered] has the same meaning as +for @f[make-hemlock-output-stream]. +@enddefmac + +@defmac[fun {with-pop-up-display}, args {(@i[var] @key @i[height name]) @mstar<@i[declaration]> @mstar<@i[form]>}] +@defvar1[var {random-typeout-buffers}] + This macro executes @i[forms] in a context with @i[var] bound to a stream. +@Hemlock collects output to this stream and tries to pop up a display of the +appropriate height containing all the output. When @i[height] is supplied, +@Hemlock creates the pop-up display immediately, forcing output on line breaks. +The system saves the output in a buffer named @i[name], which defaults to +@hid[Random Typeout]. When the window is the incorrect height, the display +mechanism will scroll the window with more-style prompting. This is useful +for displaying information of temporary interest. + +When a buffer with name @i[name] already exists and was not previously created +by @f[with-pop-up-display], @Hemlock signals an error. + +@var[random-typeout-buffers] is an association list mapping random typeout +buffers to the streams that operate on the buffers. +@enddefmac + + +@section (Interface to the Error System) +The error system interface is minimal. There is a simple editor-error +condition which is a subtype of error and a convenient means for signaling +them. @Hemlock also provides a standard handler for error conditions while in +the editor. + +@defun[fun {editor-error-format-string}, args {@i[condition]}] +@defun1[fun {editor-error-format-arguments}, args {@i[condition]}] +Handlers for editor-error conditions can access the condition object with +these. +@enddefun + +@defun[fun {editor-error}, args {@rest @i[args]}] +This function is called to signal minor errors within Hemlock; these are errors +that a normal user could encounter in the course of editing such as a search +failing or an attempt to delete past the end of the buffer. This function +@f[signal]'s an editor-error condition formed from @i[args], which are @nil or +a @f[format] string possibly followed by @f[format] arguments. @Hemlock +invokes commands in a dynamic context with an editor-error condition handler +bound. This default handler beeps or flashes (or both) the display. If the +condition passed to the handler has a non-@nil string slot, the handler also +invokes @f[message] on it. The command in progress is always aborted, and this +function never returns. +@enddefun + +@defmac[fun {handle-lisp-errors}, args {@mstar<@i[form]>}] +Within the body of this macro any Lisp errors that occur are handled in some +fashion more gracefully than simply dumping the user in the debugger. This +macro should be wrapped around code which may get an error due to some action +of the user @dash for example, evaluating code fragments on the behalf of and +supplied by the user. Using this in a command allows the established handler +to shadow the default editor-error handler, so commands should take care to +signal user errors (calls to @f[editor-errors]) outside of this context. +@enddefmac + + +@section (Definition Editing) +@index (Definition editing) +@hemlock provides commands for finding the definition of a function, macro, or +command and placing the user at the definition in a buffer. This, of course, +is implementation dependent, and if an implementation does not associate a +source file with a routine, or if @hemlock cannot get at the information, then +these commands do not work. If the Lisp system does not store an absolute +pathname, independent of the machine on which the maintainer built the system, +then users need a way of translating a source pathname to one that will be able +to locate the source. + +@defun[fun {add-definition-dir-translation}, args {@i[dir1] @i[dir2]}] +This maps directory pathname @i[dir1] to @i[dir2]. Successive invocations +using the same @i[dir1] push into a translation list. When @hemlock seeks a +definition source file, and it has a translation, then it tries the +translations in order. This is useful if your sources are on various machines, +some of which may be down. When @hemlock tries to find a translation, it first +looks for translations of longer directory pathnames, finding more specific +translations before shorter, more general ones. +@enddefun + +@defun[fun {delete-definition-dir-translation}, args {@i[dir]}] +This deletes the mapping of @i[dir] to all directories to which it has been +mapped. +@enddefun + + +@section (Event Scheduling) +@index (Event scheduling) +@index (Scheduling events) +The mechanism described in this chapter is only operative when the Lisp process +is actually running inside of @hemlock, within the @f[ed] function. The +designers intended its use to be associated with the editor, such as with +auto-saving files, reminding the user, etc. + +@defun[fun {schedule-event}, args {@i[time] @i[function] @optional @i[repeat]}] +This causes @hemlock to call @i[function] after @i[time] seconds have passed, +optionally repeating every @i[time] seconds. @i[Repeat] defaults to @true. +This is a rough mechanism since commands can take an arbitrary amount of time +to run; @hemlock invokes @i[function] at the first possible moment after +@i[time] has elapsed. @i[Function] takes the time in seconds that has elapsed +since the last time it was called (or since it was scheduled for the first +invocation). +@enddefun + +@defun[fun {remove-scheduled-event}, args {@i[function]}] +This removes @i[function] from the scheduling queue. @i[Function] does not +have to be in the queue. +@enddefun + + +@section (Miscellaneous) + +@defun[fun {in-lisp}, args {@mstar<@i[form]>}] +@index[Evaluating Lisp code] +This evaluates @i[form]'s inside @f[handle-lisp-errors]. It also binds +@var[package] to the package named by @hid[Current Package] if it is non-@nil. +Use this when evaluating Lisp code on behalf of the user. +@enddefun + +@defmac[fun {do-alpha-chars}, args {(@i[var] @i[kind] [@i[result]]) @mstar<@i[form]>}] +This iterates over alphabetic characters in Common Lisp binding @i[var] to each +character in order as specified under character relations in @i[Common Lisp the +Language]. @i[Kind] is one of @kwd[lower], @kwd[upper], or @kwd[both]. When +the user supplies @kwd[both], lowercase characters are processed first. +@enddefmac + + + +@chapter (High-Level Text Primitives) +This chapter discusses primitives that operate on higher level text forms than +characters and words. For English text, there are functions that know about +sentence and paragraph structures, and for Lisp sources, there are functions +that understand this language. This chapter also describes mechanisms for +organizing file sections into @i[logical pages] and for formatting text forms. + + +@section (Indenting Text) +@index (Indenting) +@label(indenting) + +@defhvar[var "Indent Function", val {tab-to-tab-stop}] +The value of this variable determines how indentation is done, and it is a +function which is passed a mark as its argument. The function should indent +the line that the mark points to. The function may move the mark around on +the line. The mark will be @f[:left-inserting]. The default simply inserts a +@binding[tab] character at the mark. A function for @hid[Lisp] mode probably +moves the mark to the beginning of the line, deletes horizontal whitespace, and +computes some appropriate indentation for Lisp code. +@enddefhvar + +@defhvar[var "Indent with Tabs", val {indent-using-tabs}] +@defhvar1[var "Spaces per Tab", val {8}] +@hid[Indent with Tabs] holds a function that takes a mark and a number of +spaces. The function will insert a maximum number of tabs and a minimum number +of spaces at mark to move the specified number of columns. The default +definition uses @hid[Spaces per Tab] to determine the size of a tab. @i[Note,] +@hid[Spaces per Tab] @i[is not used everywhere in @hemlock yet, so changing +this variable could have unexpected results.] +@enddefhvar + +@defun[fun {indent-region}, args {@i[region]}] +@defun1[fun {indent-region-for-commands}, args {@i[region]}] +@f[indent-region] invokes the value of @hid[Indent Function] on every line of +region. @f[indent-region-for-commands] uses @f[indent-region] but first saves +the region for the @hid[Undo] command. +@enddefun + +@defun[fun {delete-horizontal-space}, args {@i[mark]}] +This deletes all characters with a @hid[Space] attribute (see section +@ref[sys-def-chars]) of @f[1]. +@enddefun + + +@section (Lisp Text Buffers) +@index (Lisp text functions) +@hemlock bases its Lisp primitives on parsing a block of the buffer and +annotating lines as to what kind of Lisp syntax occurs on the line or what kind +of form a mark might be in (for example, string, comment, list, etc.). These +do not work well if the block of parsed forms is exceeded when moving marks +around these forms, but the block that gets parsed is somewhat programmable. + +There is also a notion of a @i[top level form] which this documentation often +uses synonymously with @i[defun], meaning a Lisp form occurring in a source +file delimited by parentheses with the opening parenthesis at the beginning of +some line. The names of the functions include this inconsistency. + +@defun[fun {pre-command-parse-check}, args {@i[mark] @i[for-sure]}] +@defhvar1[var {Parse Start Function}, val {start-of-parse-block}] +@defhvar1[var {Parse End Function}, val {end-of-parse-block}] +@defhvar1[var {Minimum Lines Parsed}, val {50}] +@defhvar1[var {Maximum Lines Parsed}, val {500}] +@defhvar1[var {Defun Parse Goal}, val {2}] +@f[pre-command-parse-check] calls @hid[Parse Start Function] and @hid[Parse End +Function] on @i[mark] to get two marks. It then parses all the lines between +the marks including the complete lines they point into. When @i[for-sure] is +non-@nil, this parses the area regardless of any cached information about the +lines. Every command that uses the following routines calls this before doing +so. + +The default values of the start and end variables use @hid[Minimum Lines +Parsed], @hid[Maximum Lines Parsed], and @hid[Defun Parse Goal] to determine +how big a region to parse. These two functions always include at least the +minimum number of lines before and after the mark passed to them. They try to +include @hid[Defun Parse Goal] number of top level forms before and after the +mark passed them, but these functions never return marks that include more than +the maximum number of lines before or after the mark passed to them. +@enddefun + +@defun[fun {form-offset}, args {@i[mark] @i[count]}] +This tries to move @i[mark] @i[count] forms forward if positive or -@i[count] +forms backwards if negative. @i[Mark] is always moved. If there were enough +forms in the appropriate direction, this returns @i[mark], otherwise nil. +@enddefun + +@defun[fun {top-level-offset}, args {@i[mark] @i[count]}] +This tries to move @i[mark] @i[count] top level forms forward if positive or +-@i[count] top level forms backwards if negative. If there were enough top +level forms in the appropriate direction, this returns @i[mark], otherwise nil. +@i[Mark] is moved only if this is successful. +@enddefun + +@defun[fun {mark-top-level-form}, args {@i[mark1] @i[mark2]}] +This moves @i[mark1] and @i[mark2] to the beginning and end, respectively, of +the current or next top level form. @i[Mark1] is used as a reference to start +looking. The marks may be altered even if unsuccessful. If successful, return +@i[mark2], else nil. @i[Mark2] is left at the beginning of the line following +the top level form if possible, but if the last line has text after the closing +parenthesis, this leaves the mark immediately after the form. +@enddefun + +@defun[fun {defun-region}, args {@i[mark]}] +This returns a region around the current or next defun with respect to +@i[mark]. @i[Mark] is not used to form the region. If there is no appropriate +top level form, this signals an editor-error. This calls +@f[pre-command-parse-check] first. +@enddefun + +@defun[fun {inside-defun-p}, args {@i[mark]}] +@defun1[fun {start-defun-p}, args {@i[mark]}] +These return, respectively, whether @i[mark] is inside a top level form or at +the beginning of a line immediately before a character whose @hid[Lisp Syntax] +(see section @ref[sys-def-chars]) value is @kwd[opening-paren]. +@enddefun + +@defun[fun {forward-up-list}, args {@i[mark]}] +@defun1[fun {backward-up-list}, args {@i[mark]}] +Respectively, these move @i[mark] immediately past a character whose @hid[Lisp +Syntax] (see section @ref[sys-def-chars]) value is @kwd[closing-paren] or +immediately before a character whose @hid[Lisp Syntax] value is +@kwd[opening-paren]. +@enddefun + +@defun[fun {valid-spot}, args {@i[mark] @i[forwardp]}] +This returns @true or @nil depending on whether the character indicated by +@i[mark] is a valid spot. When @i[forwardp] is set, use the character after +mark and vice versa. Valid spots exclude commented text, inside strings, and +character quoting. +@enddefun + +@defun[fun {defindent}, args {@i[name] @i[count]}] +This defines the function with @i[name] to have @i[count] special arguments. +@f[indent-for-lisp], the value of @hid[Indent Function] (see section +@ref[indenting]) in @hid[Lisp] mode, uses this to specially indent these +arguments. For example, @f[do] has two, @f[with-open-file] has one, etc. +There are many of these defined by the system including definitions for special +@hemlock forms. @i[Name] is a simple-string, case insensitive and purely +textual (that is, not read by the Lisp reader); therefore, @f["with-a-mumble"] +is distinct from @f["mumble:with-a-mumble"]. +@enddefun + + +@section (English Text Buffers) +@index (English text functions) +@label(text-functions) +This section describes some routines that understand basic English language +forms. + +@defun[fun {word-offset}, args {@i[mark] @i[count]}] +This moves @i[mark] @i[count] words forward (if positive) or backwards (if +negative). If @i[mark] is in the middle of a word, that counts as one. If +there were @i[count] (-@i[count] if negative) words in the appropriate +direction, this returns @i[mark], otherwise nil. This always moves @i[mark]. +A word lies between two characters whose @hid[Word Delimiter] attribute value +is @f[1] (see section @ref[sys-def-chars]). +@enddefun + +@defun[fun {sentence-offset}, args {@i[mark] @i[count]}] +This moves @i[mark] @i[count] sentences forward (if positive) or backwards (if +negative). If @i[mark] is in the middle of a sentence, that counts as one. If +there were @i[count] (-@i[count] if negative) sentences in the appropriate +direction, this returns @i[mark], otherwise nil. This always moves @i[mark]. + +A sentence ends with a character whose @hid[Sentence Terminator] attribute is +@f[1] followed by two spaces, a newline, or the end of the buffer. The +terminating character is optionally followed by any number of characters whose +@hid[Sentence Closing Char] attribute is @f[1]. A sentence begins after a +previous sentence ends, at the beginning of a paragraph, or at the beginning of +the buffer. +@enddefun + +@defun[fun {paragraph-offset}, args {@i[mark] @i[count] @optional @i[prefix]}] +@defhvar1[var {Paragraph Delimiter Function}, var {default-para-delim-function}] +This moves @i[mark] @i[count] paragraphs forward (if positive) or backwards (if +negative). If @i[mark] is in the middle of a paragraph, that counts as one. +If there were @i[count] (-@i[count] if negative) paragraphs in the appropriate +direction, this returns @i[mark], otherwise nil. This only moves @i[mark] if +there were enough paragraphs. + +@hid[Paragraph Delimiter Function] holds a function that takes a mark, +typically at the beginning of a line, and returns whether or not the current +line should break the paragraph. @f[default-para-delim-function] returns @true +if the next character, the first on the line, has a @hid[Paragraph Delimiter] +attribute value of @f[1]. This is typically a space, for an indented +paragraph, or a newline, for a block style. Some modes require a more +complicated determinant; for example, @hid[Scribe] modes adds some characters +to the set and special cases certain formatting commands. + +@i[Prefix] defaults to @hid[Fill Prefix] (see section @ref[filling]), and the +right prefix is necessary to correctly skip paragraphs. If @i[prefix] is +non-@nil, and a line begins with @i[prefix], then the scanning process skips +the prefix before invoking the @hid[Paragraph Delimiter Function]. +Note, when scanning for paragraph bounds, and @i[prefix] is non-@nil, lines are +potentially part of the paragraph regardless of whether they contain the prefix; +only the result of invoking the delimiter function matters. + +The programmer should be aware of an idiom for finding the end of the current +paragraph. Assume @f[paragraphp] is the result of moving @f[mark] one +paragraph, then the following correctly determines whether there actually is a +current paragraph: +@begin[programexample] +(or paragraphp + (and (last-line-p mark) + (end-line-p mark) + (not (blank-line-p (mark-line mark))))) +@end[programexample] +In this example @f[mark] is at the end of the last paragraph in the buffer, and +there is no last newline character in the buffer. @f[paragraph-offset] would +have returned @nil since it could not skip any paragraphs since @f[mark] was at +the end of the current and last paragraph. However, you still have found a +current paragraph on which to operate. @f[mark-paragraph] understands this +problem. +@enddefun + +@defun[fun {mark-paragraph}, args {@f[mark1] @f[mark2]}] +This marks the next or current paragraph, setting @i[mark1] to the beginning +and @i[mark2] to the end. This uses @hid[Fill Prefix] (see section +@ref[filling]). @i[Mark1] is always on the first line of the paragraph, +regardless of whether the previous line is blank. @i[Mark2] is typically at +the beginning of the line after the line the paragraph ends on, this returns +@i[mark2] on success. If this cannot find a paragraph, then the marks are left +unmoved, and @nil is returned. +@enddefun + + +@section (Logical Pages) +@index (Logical pages) +@index (Page functions) +@label(logical-pages) +Logical pages are a way of dividing a file into coarse divisions. This is +analogous to dividing a paper into sections, and @hemlock provides primitives +for moving between the pages of a file and listing a directory of the page +titles. Pages are separated by @hid[Page Delimiter] characters (see section +@ref[sys-def-chars]) that appear at the beginning of a line. + +@defun[fun {goto-page}, args {@i[mark] @i[n]}] +This moves @i[mark] to the absolute page numbered @i[n]. If there are less +than @i[n] pages, it signals an editor-error. If it returns, it returns +@i[mark]. @hemlock numbers pages starting with one for the page delimited by +the beginning of the buffer and the first @hid[Page Delimiter] (or the end of +the buffer). +@enddefun + +@defun[fun {page-offset}, args {@i[mark] @i[n]}] +This moves mark forward @i[n] (-@i[n] backwards, if @i[n] is negative) +@hid[Page Delimiter] characters that are in the zero'th line position. If a +@hid[Page Delimiter] is the immediately next character after mark (or before +mark, if @i[n] is negative), then skip it before starting. This always moves +@i[mark], and if there were enough pages to move over, it returns @i[mark]; +otherwise, it returns @nil. +@enddefun + +@defun[fun {page-directory}, args {@i[buffer]}] +This returns a list of each first non-blank line in @i[buffer] that follows a +@hid[Page Delimiter] character that is in the zero'th line position. This +includes the first line of the @i[buffer] as the first page title. If a page +is empty, then its title is the empty string. +@enddefun + +@defun[fun {display-page-directory}, args {@i[stream] @i[directory]}] +This writes the list of strings, @i[directory], to @i[stream], enumerating them +in a field three wide. The number and string are separated by two spaces, and +the first line contains headings for the page numbers and title strings. +@enddefun + + +@section (Filling) +@index (filling) +@label(filling) +Filling is an operation on text that breaks long lines at word boundaries +before a given column and merges shorter lines together in an attempt to make +each line roughly the specified length. This is different from justification +which tries to add whitespace in awkward places to make each line exactly the +same length. @Hemlock's filling optionally inserts a specified string at the +beginning of each line. Also, it eliminates extra whitespace between lines and +words, but it knows two spaces follow sentences (see section +@ref[text-functions]). + +@defhvar[var "Fill Column", val {75}] +@defhvar1[var "Fill Prefix", val {nil}] +These variables hold the default values of the prefix and column arguments to +@hemlock's filling primitives. If @hid[Fill Prefix] is @nil, then there is no +fill prefix. +@enddefhvar + +@defun[fun {fill-region}, args {@i[region] @optional @i[prefix] @i[column]}] +This deletes any blank lines in region and fills it according to prefix and +column. @i[Prefix] and @i[column] default to @hid[Fill Prefix] and @hid[Fill +Column]. +@enddefun + +@defun[fun {fill-region-by-paragraphs}, + args {@i[region] @optional @i[prefix] @i[column]}] +This finds paragraphs (see section @ref[text-functions]) within region and +fills them with @f[fill-region]. This ignores blank lines between paragraphs. +@i[Prefix] and @i[column] default to @hid[Fill Prefix] and @hid[Fill Column]. +@enddefun + + + +@chapter (Utilities) +@index (Utilities) +This chapter describes a number of utilities for manipulating some types of +objects @hemlock uses to record information. String-tables are used to store +names of variables, commands, modes, and buffers. Ring lists can be used to +provide a kill ring, recent command history, or other user-visible features. + + +@section(String-table Functions) +@index (String-tables) +@label(string-tables) + +String tables are similar to Common Lisp hash tables in that they associate a +value with an object. There are a few useful differences: in a string table +the key is always a case insensitive string, and primitives are provided to +facilitate keyword completion and recognition. Any type of string may be added +to a string table, but the string table functions always return +@f[simple-string]'s. + +A string entry in one of these tables may be thought of as being separated into +fields or keywords. The interface provides keyword completion and recognition +which is primarily used to implement some @hid[Echo Area] commands. These +routines perform a prefix match on a field-by-field basis allowing the +ambiguous specification of earlier fields while going on to enter later fields. +While string tables may use any @f[string-char] as a separator, the use of +characters other than @binding[space] may make the @hid[Echo Area] commands +fail or work unexpectedly. + +@defun[fun {make-string-table}, keys {[separator][initial-contents]}] +This function creates an empty string table that uses @i[separator] as the +character, which must be a @f[string-char], that distinguishes fields. +@i[Initial-contents] specifies an initial set of strings and their values in +the form of a dotted @f[a-list], for example: +@Begin[ProgramExample] +'(("Global" . t) ("Mode" . t) ("Buffer" . t)) +@End[ProgramExample] +@enddefun + +@defun[fun {string-table-p}, args {@i[string-table]}] +This function returns @true if @i[string-table] is a @f[string-table] object, +otherwise @nil. +@enddefun + +@defun[fun {string-table-separator}, args {@i[string-table]}] +This function returns the separator character given to @f[make-string-table]. +@enddefun + +@defun[fun {delete-string}, args {@i[string] @i[table]}] +@defun1[fun {clrstring}, args {@i[table]}] +@f[delete-string] removes any entry for @i[string] from the @f[string-table] +@i[table], returning @true if there was an entry. @f[clrstring] removes all +entries from @i[table]. +@enddefun + +@defun[fun {getstring}, args {@i[string] @i[table]}] +This function returns as multiple values, first the value corresponding to the +string if it is found and @nil if it isn't, and second @true if it is found and +@nil if it isn't. + +This may be set with @f[setf] to add a new entry or to store a new value for a +string. It is an error to try to insert a string with more than one +field separator character occurring contiguously. +@enddefun + +@defun[fun {complete-string}, args {@i[string] @i[tables]}] +This function completes @i[string] as far as possible over the list of +@i[tables], returning five values. It is an error for @i[tables] to have +different separator characters. The five return values are as follows: +@begin[itemize] +The maximal completion of the string or @nil if there is none. + +An indication of the usefulness of the returned string: +@begin[description] +@kwd[none]@\ +There is no completion of @i[string]. + +@kwd[complete]@\ +The completion is a valid entry, but other valid completions exist too. This +occurs when the supplied string is an entry as well as initial substring of +another entry. + +@kwd[unique]@\ +The completion is a valid entry and unique. + +@kwd[ambiguous]@\ +The completion is invalid; @f[get-string] would return @nil and @nil if given +the returned string. +@end[description] + +The value of the string when the completion is @kwd[unique] or @kwd[complete], +otherwise @nil. + +An index, or nil, into the completion returned, indicating where the addition +of a single field to @i[string] ends. The command @hid[Complete Field] uses +this when the completion contains the addition to @i[string] of more than one +field. + +An index to the separator following the first ambiguous field when the +completion is @kwd[ambiguous] or @kwd[complete], otherwise @nil. +@end[itemize] +@enddefun + +@defun[fun {find-ambiguous}, args {@i[string] @i[table]}] +@defun1[fun {find-containing}, args {@i[string] @i[table]}] +@f[find-ambiguous] returns a list in alphabetical order of all the +strings in @i[table] matching @i[string]. This considers an entry as matching +if each field in @i[string], taken in order, is an initial substring of the +entry's fields; entry may have fields remaining. + +@f[find-containing] is similar, but it ignores the order of the fields in +@i[string], returning all strings in @i[table] matching any permutation of the +fields in @i[string]. +@enddefun + +@defmac[fun {do-strings}, args {(@i[string-var] @i[value-var] @i[table] @MOPT<@i[result]>) @mstar<@i[declaration]> @mstar<@i[tag] @MOR @i[statement]>}] +This macro iterates over the strings in @i[table] in alphabetical order. On +each iteration, it binds @i[string-var] to an entry's string and @i[value-var] +to an entry's value. +@enddefmac + + +@section (Ring Functions) +@index (Rings) +@label[rings] +There are various purposes in an editor for which a ring of values can be used, +so @hemlock provides a general ring buffer type. It is used for maintaining a +ring of killed regions (see section @ref[kill-ring]), a ring of marks (see +section @ref[mark-stack]), or a ring of command strings which various modes and +commands maintain as a history mechanism. + +@defun[fun {make-ring}, args {@i[length] @optional @i[delete-function]}] +Makes an empty ring object capable of holding up to @i[length] Lisp objects. +@i[Delete-function] is a function that each object is passed to before it falls +off the end. @i[Length] must be greater than zero. +@enddefun + +@defun[fun {ringp}, args {@i[ring]}] +Returns @true if @i[ring] is a @f[ring] object, otherwise @nil. +@enddefun + +@defun[fun {ring-length}, args {@i[ring]}] +Returns as multiple-values the number of elements which @i[ring] +currently holds and the maximum number of elements which it may hold. +@enddefun + +@defun[fun {ring-ref}, args {@i[ring] @i[index]}] +Returns the @i[index]'th item in the @i[ring], where zero is the index +of the most recently pushed. This may be set with @f[setf]. +@enddefun + +@defun[fun {ring-push}, args {@i[object] @i[ring]}] +Pushes @i[object] into @i[ring], possibly causing the oldest item to +go away. +@enddefun + +@defun[fun {ring-pop}, args {@i[ring]}] +Removes the most recently pushed object from @i[ring] and returns it. +If the ring contains no elements then an error is signalled. +@enddefun + +@defun[fun {rotate-ring}, args {@i[ring] @i[offset]}] +With a positive @i[offset], rotates @i[ring] forward that many times. +In a forward rotation the index of each element is reduced by one, +except the one which initially had a zero index, which is made the +last element. A negative offset rotates the ring the other way. +@enddefun + + +@section (Undoing commands) +@index (Undo functions) +@label(undo) + +@defun[fun {save-for-undo}, args {@i[name] @i[method] @optional @i[cleanup] @i[method-undo] @i[buffer]}] +This saves information to undo a command. @i[Name] is a string to display when +prompting the user for confirmation when he invokes the @hid[Undo] command (for +example, @f["kill"] or @f["Fill Paragraph"]). @i[Method] is the function to +invoke to undo the effect of the command. @i[Method-undo] is a function that +undoes the undo function, or effectively re-establishes the state immediately +after invoking the command. If there is any existing undo information, this +invokes the @i[cleanup] function; typically @i[method] closes over or uses +permanent marks into a buffer, and the @i[cleanup] function should delete such +references. @i[Buffer] defaults to the @f[current-buffer], and the @hid[Undo] +command only invokes undo methods when they were saved for the buffer that is +current when the user invokes @hid[Undo]. +@enddefun + +@defun[fun {make-region-undo}, args {@i[kind] @i[name] @i[region] @optional @i[mark-or-region]}] +This handles three common cases that commands fall into when setting up undo +methods, including cleanup and method-undo functions (see @f[save-for-undo]). +These cases are indicated by the @i[kind] argument: +@begin[description] +@kwd[twiddle]@\ +Use this kind when a command modifies a region, and the undo information +indicates how to swap between two regions @dash the one before any modification +occurs and the resulting region. @i[Region] is the resulting region, and it +has permanent marks into the buffer. @i[Mark-or-region] is a region without +marks into the buffer (for example, the result of @f[copy-region]). As a +result of calling this, a first invocation of @hid[Undo] deletes @i[region], +saving it, and inserts @i[mark-or-region] where @i[region] used to be. The +undo method sets up for a second invocation of @hid[Undo] that will undo the +effect of the undo; that is, after two calls, the buffer is exactly as it was +after invoking the command. This activity is repeatable any number of times. +This establishes a cleanup method that deletes the two permanent marks into the +buffer used to locate the modified region. + +@kwd[insert]@\ +Use this kind when a command has deleted a region, and the undo information +indicates how to re-insert the region. @i[Region] is the deleted and saved +region, and it does not contain marks into any buffer. @i[Mark-or-region] is a +permanent mark into the buffer where the undo method should insert @i[region]. +As a result of calling this, a first invocation of @hid[Undo] inserts +@i[region] at @i[mark-or-region] and forms a region around the inserted text +with permanent marks into the buffer. This allows a second invocation of +@hid[Undo] to undo the effect of the undo; that is, after two calls, the buffer +is exactly as it was after invoking the command. This activity is repeatable +any number of times. This establishes a cleanup method that deletes either the +permanent mark into the buffer or the two permanent marks of the region, +depending on how many times the user used @hid[Undo]. + +@kwd[delete]@\ +Use this kind when a command has inserted a block of text, and the undo +information indicates how to delete the region. @i[Region] has permanent marks +into the buffer and surrounds the inserted text. Leave @i[Mark-or-region] +unspecified. As a result of calling this, a first invocation of @hid[Undo] +deletes @i[region], saving it, and establishes a permanent mark into the buffer +to remember where the @i[region] was. This allows a second invocation of +@hid[Undo] to undo the effect of the undo; that is, after two calls, the buffer +is exactly as it was after invoking the command. This activity is repeatable +any number of times. This establishes a cleanup method that deletes either the +permanent mark into the buffer or the two permanent marks of the region, +depending on how many times the user used @hid[Undo]. +@end[description] + +@blankspace(1 line) +@i[Name] in all cases is an appropriate string indicating what the command did. +This is used by @hid[Undo] when prompting the user for confirmation before +calling the undo method. The string used by @hid[Undo] alternates between this +argument and something to indicate that the user is undoing an undo. +@enddefun + + + +@chapter (Miscellaneous) +This chapter is somewhat of a catch-all for comments and features that don't +fit well anywhere else. + + +@section (Generic Pointer Up) +@hid[Generic Pointer Up] is a @hemlock command bound to mouse up-clicks. It +invokes a function supplied with the interface described in this section. This +command allows different commands to be bound to the same down-click in various +modes with one command bound to the corresponding up-click. + +@defun[fun {supply-generic-pointer-up-function}, args {@i[function]}] +@index[Generic Pointer Up] +This function supplies a function that @hid[Generic Pointer Up] invokes the +next time it executes. +@enddefun + + +@section (Using View Mode) +@hid[View] mode supports scrolling through files automatically terminating the +buffer at end-of-file as well as commands for quitting the mode and popping +back to the buffer that spawned the @hid[View] mode buffer. Modes such as +@hid[Dired] and @hid[Lisp-Lib] use this to view files and description of +library entries. + +Modes that want similar commands should use @f[view-file-command] to view a +file and get a handle on the view buffer. To allow the @hid[View Return] and +@hid[View Quit] commands to return to the originating buffer, you must set the +variable @hid[View Return Function] in the viewing buffer to a function that +knows how to do this. Furthermore, since you now have a reference to the +originating buffer, you must add a buffer local delete hook to it that will +clear the view return function's reference. This needs to happen for two +reasons in case the user deletes the originating buffer: +@Begin[Enumerate] +You don't want the return function to go to a non-existing, invalid buffer. + +Since the viewing buffer still exists, its @hid[View Return Function] buffer +local variable still exists. This means the function still references the +deleted originating buffer, and garbage collection cannot reclaim the memory +locked down by the deleted buffer. +@End[Enumerate] + +The following is a piece of code that could implement part of @hid[Dired View +File] that uses two closures to accomplish that described above: +@Begin[ProgramExample] +(let* ((dired-buf (current-buffer)) + (buffer (view-file-command nil pathname))) + (push #'(lambda (buffer) + (declare (ignore buffer)) + (setf dired-buf nil)) + (buffer-delete-hook dired-buf)) + (setf (variable-value 'view-return-function :buffer buffer) + #'(lambda () + (if dired-buf + (change-to-buffer dired-buf) + (dired-from-buffer-pathname-command nil))))) +@End[ProgramExample] + +The @hid[Dired] buffer's delete hook clears the return function's reference to +the @hid[Dired] buffer. The return function tests the variable to see if it +still holds a buffer when the function executes. + + + +@comment[@chapter (Auxiliary Systems)] +@include(aux-sys) diff --git a/docs/hem/user/commands.mss b/docs/hem/user/commands.mss new file mode 100644 index 0000000..7d547bd --- /dev/null +++ b/docs/hem/user/commands.mss @@ -0,0 +1,822 @@ +@comment{-*- Dictionary: hem; Mode: spell; Package: Hemlock -*-} +@chap[Basic Commands] +@section[Motion Commands] + +@index[commands, basic]@index[motion]There is a fairly small number of +basic commands for moving around in the buffer. While there are many other +more complex motion commands, these are by far the most commonly used and +the easiest to learn. + +@defcom[com "Forward Character", bind (C-f, Rightarrow)] +@defcom1[com "Backward Character", bind (C-b, Leftarrow)] +@index[character, motion] +@hid[Forward Character] moves the point forward by one character. If a prefix +argument is supplied, then the point is moved by that many characters. +@hid[Backward Character] is identical, except that it moves the point +backwards. +@enddefcom + +@defcom[com "Forward Word", bind {M-f}] +@defcom1[com "Backward Word", bind {M-b}] +@index[word, motion]These commands move the point forward and backward +over words. The point is always left between the last word and first +non-word character in the direction of motion. This means that after moving +backward the cursor appears on the first character of the word, while after +moving forward, the cursor appears on the delimiting character. Supplying +a prefix argument moves the point by that many words. +@enddefcom + +@defcom[com "Next Line", bind (C-n, Downarrow)] +@defcom1[com "Previous Line", bind (C-p, Uparrow)] +@defcom1[com "Goto Absolute Line"] +@index[line, motion] +@hid[Next Line] and @hid[Previous Line] move to adjacent lines, while remaining +the same distance within a line. Note that this motion is by logical lines, +each of which may take up many lines on the screen if it wraps. If a prefix +argument is supplied, then the point is moved by that many lines. + +The position within the line at the start is recorded, and each successive +use of @binding[C-p] or @binding[C-n] attempts to move the point to that +position on the new line. If it is not possible to move to the recorded +position because the line is shorter, then the point is left at the end of +the line. + +@hid[Goto Absolute Line] moves to the indicated line, as if you counted them +starting at the beginning of the buffer with number one. If the user supplies +a prefix argument, it is the line number; otherwise, @Hemlock prompts the user +for the line. +@enddefcom + +@defcom[com "End of Line", bind {C-e}] +@defcom1[com "Beginning of Line", bind {C-a}] +@hid[End of Line] moves the point to the end of the current line, while +@hid[Beginning of Line] moves to the beginning. If a prefix argument is +supplied, then the point is moved to the end or beginning of the line that +many lines below the current one. +@enddefcom + +@defcom[com "Scroll Window Down", bind {C-v}] +@defcom1[com "Scroll Window Up", bind {M-v}] +@index[scrolling] +@hid[Scroll Window Down] moves forward in the buffer by one screenful of text, +the exact amount being determined by the size of the window. If a prefix +argument is supplied, then this scrolls the screen that many lines. When this +action scrolls the line with the point off the screen, it this command moves +the point to the vertical center of the window. @hid[Scroll Window Up] is +identical to @hid[Scroll Window Down], except that it moves backwards. +@enddefcom + +@defhvar[var "Scroll Overlap", val {2}] +This variable is used by @hid[Scroll Window Down] and @hid[Scroll Window Up] to +determine the number of lines by which the new and old screen should overlap. +@enddefhvar + +@defcom[com "End of Buffer", bind (M-<)] +@defcom1[com "Beginning of Buffer", bind (@bf[M->])] +These commands are used to conveniently get to the very beginning and end of the +text in a buffer. Before the point is moved, its position is saved by +pushing it on the mark stack (see page @pageref[marks]). +@enddefcom + +@defcom[com "Top of Window", bind (M-,)] +@defcom1[com "Bottom of Window", bind (M-.)] +@index[window, motion]@hid[Top of Window] moves the point to the beginning of +the first line displayed in the current window. @hid[Bottom of Window] moves +to the beginning of the last line displayed. +@enddefcom + + +@section[The Mark and The Region] + +@label[marks]@index[marks]@index[region]@index[selection]Each buffer has a +distinguished position known as the @i[mark]. The mark initially points to the +beginning of the buffer. The area between the mark and the point is known as +the @i[region]. Many @hemlock commands which manipulate large pieces of text +use the text in the region. To use these commands, one must first use some +command to mark the region. + +@index[active regions]Although the mark is always pointing somewhere (initially +to the beginning of the buffer), region commands insist that the region be made +@i[active] before it can be used. This prevents accidental use of a region +command from mysteriously mangling large amounts of text. + +@defhvar[var "Active Regions Enabled", val {t}] +When this variable is true, region commands beep unless the region is active. +This may be set to @false for more traditional @emacs region semantics. +@enddefhvar + +Once a marking command makes the region active, it remains active until: +@begin[itemize] +a command uses the region, + +a command modifies the buffer, + +a command changes the current window or buffer, + +a command signals an editor error, + +or the user types @binding[C-g]. +@end[itemize] +Motion commands have the effect of redefining the region, since they move the +point and leave the region active. + +@index[ephemerally active regions]Commands that insert a large chunk of +text into the buffer usually set an @i[ephemerally active] region around +the inserted text. An ephemerally active region is always deactivated by +the next command, regardless of the kind of command. The ephemerally +active region allows an immediately following region command to manipulate +the inserted text, but doesn't persist annoyingly. This is also very +useful with active region highlighting, since it visibly marks the inserted +text. + + +@defhvar[var "Highlight Active Region", val {t}] +@defhvar1[var "Active Region Highlighting Font", val {nil}] +When @hid[Highlight Active Region] is true, @hemlock displays the text in the +region in a different font whenever the region is active. This provides a +visible indication of what text will be manipulated by a region command. +Active region highlighting is only supported under @windows. + +@hid[Active Region Highlighting Font] is the name of the font to use for active +region highlighting. If unspecified, @hemlock uses an underline font. +@enddefhvar + + +@defcom[com "Set/Pop Mark", bind (C-@@)] +This command moves the mark to the point (saving the old mark on the mark +stack) and activates the region. After using this command to mark one end of +the region, use motion commands to move to the other end, then do the region +command. This is the traditional @emacs marking command; when running under a +windowing system with mouse support, it is usually easier to use the mouse with +the @comref[Point to Here] and @comref[Generic Pointer Up]. + +For historical reasons, the prefix argument causes this command to do things +that are distinct commands in @Hemlock. A prefix argument of four does +@hid[Pop and Goto Mark], and a prefix argument of @f[16] does +@hid[Pop Mark]. +@enddefcom + +@defcom[com "Mark Whole Buffer", bind (C-x h)] +@defcom1[com "Mark to Beginning of Buffer", bind (C-<)] +@defcom1[com "Mark to End of Buffer", bind (C->)] +@hid[Mark Whole Buffer] sets the region around the whole buffer, with the point +at the beginning and the mark at the end. If a prefix argument is supplied, +then the mark is put at the beginning and the point at the end. The mark is +pushed on the mark stack beforehand, so popping the stack twice will restore +it. + +@hid[Mark to Beginning of Buffer] sets the current region from point to the +beginning of the buffer. + +@hid[Mark to End of Buffer] sets the current region from the end of the buffer +to point. +@enddefcom + +@defcom[com "Activate Region", bind (C-x C-Space, C-x C-@@)] +This command makes the region active, using whatever the current position of +the mark happens to be. This is useful primarily when the region is +accidentally deactivated. +@enddefcom + + +@subsection[The Mark Stack] + +@index[mark stack]As was hinted at earlier, each buffer has a @i[mark stack], +providing a history of positions in that buffer. The current mark is the mark +on the top of the stack; earlier values are recovered by popping the stack. +Since commands that move a long distance save the old position on the mark +stack, the mark stack commands are useful for jumping to interesting places in +a buffer without having to do a search. + +@defcom[com "Pop Mark", bind (C-M-Space)] +@defcom1[com "Pop and Goto Mark", bind (M-@@, M-Space)] +@hid[Pop Mark] pops the mark stack, restoring the current mark to the next most +recent value. @hid[Pop and Goto Mark] also pops the mark stack, but instead of +discarding the current mark, it moves the point to that position. Both +commands deactivate the region. +@enddefcom + +@defcom[com "Exchange Point and Mark", bind (C-x C-x)] +This command interchanges the position of the point and the mark, thus moving +to where the mark was, and leaving the mark where the point was. This command +can be used to switch between two positions in a buffer, since repeating it +undoes its effect. The old mark isn't pushed on the mark stack, since it is +saved in the point. +@enddefcom + + +@subsection[Using The Mouse] + +@index[mouse]It can be convenient to use the mouse to point to positions in +text, especially when moving large distances. @hemlock defines several +commands for using the mouse. These commands can only be used when running +under @windows (see page @pageref[using-x].) + +@defcom[com "Here to Top of Window", bind (Rightdown)] +@defcom1[com "Top Line to Here", bind (Leftdown)] +@index[window, motion]@hid[Here to Top of Window] scrolls the window so as to +move the line which is under the mouse cursor to the top of the window. This +has the effect of moving forward in the buffer by the distance from the top of +the window to the mouse cursor. @hid[Top Line to Here] is the inverse +operation, it scrolls backward, moving current the top line underneath the +mouse. + +If the mouse is near the left edge of a window, then these commands do smooth +scrolling. @hid[Here To Top of Window] repeatedly scrolls the window up by one +line until the mouse button is released. Similarly, @hid[Top Line to Here] +smoothly scrolls down. +@enddefcom + +@defcom[com "Point to Here", bind (Middledown, S-Leftdown)] +This command moves the point to the position of the mouse, changing to a +different window if necessary. + +When used in a window's modeline, this moves the point of the window's buffer +to the position within the file that is the same percentage, start to end, as +the horizontal position of the mouse within the modeline. This also makes this +window current if necessary. + +This command supplies a function @hid[Generic Pointer Up] invokes if it runs +without any intervening generic pointer up predecessors executing. If the +position of the pointer is different than the current point when the user +invokes @hid[Generic Pointer Up], then this function pushes a buffer mark at +point and moves point to the pointer's position. This allows the user to mark +off a region with the mouse. +@enddefcom + +@defcom[com "Generic Pointer Up", bind (Middleup, S-Leftup)] +Other commands determine this command's action by supplying functions that +this command invokes. The following built-in commands supply the following +generic up actions: +@Begin[Description] +@hid[Point to Here]@\ + When the position of the pointer is different than the current point, the +action pushes a buffer mark at point and moves point to the pointer's position. + +@hid[Bufed Goto and Quit]@\ + The action is a no-op. +@End[Description] +@enddefcom + +@defcom[com "Insert Kill Buffer", bind (S-Rightdown)] +This command is a combination of @hid[Point to Here] and @comref[Un-Kill]. It +moves the point to the mouse location and inserts the most recently killed +text. +@enddefcom + + +@section[Modification Commands] +@index[commands, modification] + +There is a wide variety of basic text-modification commands, but once again the +simplest ones are the most often used. + +@subsection[Inserting Characters] +@index[character, insertion] +@index[insertion, character] + +In @hemlock, you can insert characters with graphic representations by typing +the corresponding key-event which you normally generate with the obvious +keyboard key. You can only insert characters whose codes correspond to ASCII +codes. To insert those without graphic representations, use @hid[Quoted +Insert]. + +@defcom[com "Self Insert"] +@hid[Self Insert] inserts into the buffer the character corresponding to the +key-event typed to invoke the command. This command is normally bound to all +such key-events @binding[Space]. If a prefix argument is supplied, then this +inserts the character that many times. +@enddefcom + +@defcom[com "New Line", bind (Return)] +This command, which has roughly the same effect as inserting a @bf[Newline], +is used to move onto a new blank line. If there are at least two blank +lines beneath the current one then @binding[Return] cleans off any +whitespace on the next line and uses it, instead of inserting a newline. +This behavior is desirable when inserting in the middle of text, because +the bottom half of the screen does not scroll down each time @hid[New Line] +is used. +@enddefcom + +@defcom[com "Quoted Insert", bind {C-q}] +Many key-events have corresponding ASCII characters, but these key-events are +bound to commands other than @hid[Self Insert]. Sometimes they are otherwise +encumbered such as with @binding[C-g]. @hid[Quoted Insert] prompts for a +key-event, without any command interpretation semantics, and inserts the +corresponding character. If the appropriate character has some code other than +an ASCII code, this will beep and abort the command. A common use for this +command is inserting a @bf[Formfeed] by typing @binding[C-q C-l]. If a prefix +argument is supplied, then the character is inserted that many times. +@enddefcom + +@defcom[com "Open Line", bind {C-o}] +This command inserts a newline into the buffer without moving the point. +This command may also be given a prefix argument to insert a number of +newlines, thus opening up some room to work in the middle of a screen of +text. See also @comref[Delete Blank Lines]. +@enddefcom + + +@subsection[Deleting Characters] +@index[deletion, character] +@index[character, deletion] +There are a number of commands for deleting characters as well. + +@defhvar[var "Character Deletion Threshold", val {5}] +If more than this many characters are deleted by a character deletion command, +then the deleted text is placed in the kill ring. +@enddefhvar + +@defcom[com "Delete Next Character", bind {C-d}] +@defcom1[com "Delete Previous Character", bind (Delete, Backspace)] +@hid[Delete Next Character] deletes the character immediately following the +point, that is, the character which appears under the cursor. When given a +prefix argument, @binding[C-d] deletes that many characters after the +point. @hid[Delete Previous Character] is identical, except that it +deletes characters before the point. +@enddefcom + +@defcom[com "Delete Previous Character Expanding Tabs"] +@hid[Delete Previous Character Expanding Tabs] is identical to +@hid[Delete Previous Character], except that it treats tabs as the +equivalent number of spaces. Various language modes that use tabs for +indentation bind @binding[Delete] to this command. +@enddefcom + + +@subsection[Killing and Deleting] + +@index[killing]@index[cutting]@index[pasting]@index[kill ring]@hemlock has many +commands which kill text. Killing is a variety of deletion which saves the +deleted text for later retrieval. The killed text is saved in a ring buffer +known as the @i[kill ring]. Killing has two main advantages over deletion: +@begin[enumerate] +If text is accidentally killed, a not uncommon occurrence, then it can be +restored. + +Text can be moved from one place to another by killing it and then +restoring it in the new location. +@end[enumerate] + +Killing is not the same as deleting. When a command is said to delete +text, the text is permanently gone and is not pushed on the kill ring. +Commands which delete text generally only delete things of little +importance, such as single characters or whitespace. + +@subsection[Kill Ring Manipulation] +@defcom[com "Un-Kill", bind {C-y}] +@index[kill ring, manipulation]This command "yanks" back the most +recently killed piece of text, leaving the mark before the inserted text +and the point after. If a prefix argument is supplied, then the text that +distance back in the kill ring is yanked. +@enddefcom + +@defcom[com "Rotate Kill Ring", bind {M-y}] +This command rotates the kill ring forward, replacing the most recently +yanked text with the next most recent text in the kill ring. @binding[M-y] +may only be used immediately after a use of @binding[C-y] or a previous +use of @binding[M-y]. This command is used to step back through the text +in the kill ring if the desired text was not the most recently killed, and +thus could not be retrieved directly with a @binding[C-y]. If a prefix +argument is supplied, then the kill ring is rotated that many times. +@enddefcom + +@defcom[com "Kill Region", bind {C-w}] +@index[region, killing]This command kills the text between the point and +mark, pushing it onto the kill ring. This command is usually the best way +to move or remove large quantities of text. +@enddefcom + +@defcom[com "Save Region", bind {M-w}] +This command pushes the text in the region on the kill ring, but doesn't +actually kill it, giving an effect similar to typing @binding[C-w C-y]. +This command is useful for duplicating large pieces of text. +@enddefcom + +@subsection[Killing Commands] + +@index[commands, killing]Most commands which kill text append into the +kill ring, meaning that consecutive uses of killing commands will insert +all text killed into the top entry in the kill ring. This allows large +pieces of text to be killed by repeatedly using a killing command. + +@defcom[com "Kill Line", bind {C-k}] +@defcom1[com "Backward Kill Line"] +@index[line, killing]@hid[Kill Line] kills the text from the point to the +end of the current line, deleting the line if it is empty. If a prefix +argument is supplied, then that many lines are killed. Note that a prefix +argument is not the same as a repeat count. + +@hid[Backward Kill Line] is similar, except that it kills from the point to the +beginning of the line. If it is called at the beginning of the line, it kills +the newline and any trailing whitespace on the previous line. With a prefix +argument, this command is the same as @hid[Kill Line] with a negated argument. +@enddefcom + +@defcom[com "Kill Next Word", bind {M-d}] +@defcom1[com "Kill Previous Word", bind (M-Backspace, M-Delete)] +@index[word, killing]@hid[Kill Next Word] kills from the point to the end +of the current or next word. If a prefix argument is supplied, then that +many words are killed. @hid[Kill Previous Word] is identical, except that +it kills backward. +@enddefcom + +@subsection[Case Modification Commands] + +@index[case modification]@hemlock provides a few case modification +commands, which are often useful for correcting typos. + +@defcom[com "Capitalize Word", bind {M-c}] +@defcom1[com "Lowercase Word", bind {M-l}] +@defcom1[com "Uppercase Word", bind {M-u}] +@index[word, case modification]These commands modify the case of the +characters from the point to the end of the current or next word, leaving +the point after the end of the word affected. A positive prefix argument +modifies that many words, moving forward. A negative prefix argument +modifies that many words before the point, but leaves the point unmoved. +@enddefcom + +@defcom[com "Lowercase Region", bind (C-x C-l)] +@defcom1[com "Uppercase Region", bind (C-x C-u)] +@index[region, case modification]These commands case-fold the text in the +region. Since these commands can damage large amounts of text, they ask for +confirmation before modifying large regions and can be undone with @hid[Undo]. +@enddefcom + +@subsection[Transposition Commands] + +@index[transposition]@index[commands, transposition]@hemlock provides a +number of transposition commands. A transposition command swaps the +"things" before and after the point and moves forward one "thing". Just +how a "thing" is defined depends on the particular transposition command. +Transposition commands, particularly +@hid[Transpose Characters] and @hid[Transpose Words], are useful for +correcting typos. More obscure transposition commands can be used to amaze +your friends and demonstrate your immense knowledge of exotic @emacs +commands. + +To the uninitiated, the behavior of transposition commands may seem mysterious; +this has led some implementors to attempt to improve the definition of +transposition, but right-thinking people will accept no substitutes. The +@emacs transposition definition used in @hemlock has two useful properties: +@begin[enumerate] +Repeated applications of a transposition command have a useful effect. The +way to visualize this effect is that each use of the transposition command +drags the previous thing over the next thing. It is possible to correct +double transpositions easily using @hid[Transpose Characters]. + +Transposition commands move backward with a negative prefix argument, thus +undoing the effect of the equivalent positive argument. +@end[enumerate] + +@defcom[com "Transpose Characters", bind {C-t}] +@index[character, transposition]This command exchanges the characters on +either side of the point and moves forward, unless at the end of a line, in +which case it transposes the previous two characters without moving. +@enddefcom + +@defcom[com "Transpose Lines", bind (C-x C-t)] +@index[line, transposition]This command transposes the previous and +current line, moving down to the next line. With a zero argument, it +transposes the current line and the line the mark is on. +@enddefcom + +@defcom[com "Transpose Words", bind {M-t}] +@index[word, transposition]This command transposes the previous word and +the current or next word. +@enddefcom + + +@defcom[com "Transpose Regions", bind (C-x t)] +This command transposes two regions with endpoints defined by the mark stack +and point. To use this command, place three marks (in order) at the start and +end of the first region, and at the start of the second region, then place the +point at the end of the second region. Unlike the other transposition +commands, a second use will simply undo the effect of the first use, and to do +even this, you must reactivate the current region. +@enddefcom + + +@subsection[Whitespace Manipulation] +These commands change the amount of space between words. See also the +indentation commands in section @ref[indentation]. + +@defcom[com "Just One Space", bind (M-|)] +@index[whitespace, manipulation]@index[indentation, manipulation]This +command deletes all whitespace characters before and after the point and then +inserts one space. If a prefix argument is supplied, then that number of +spaces is inserted. +@enddefcom + +@defcom[com "Delete Horizontal Space", bind (M-\)] +This command deletes all blank characters around the point. +@enddefcom + +@defcom[com "Delete Blank Lines", bind (C-x C-o)] +This command deletes all blank lines surrounding the current line, leaving the +point on a single blank line. If the point is already on a single blank line, +then that line is deleted. If the point is on a non-blank line, then all blank +lines immediately following that line are deleted. This command is often used +to clean up after @comref[Open Line]. +@enddefcom + +@section[Filtering] + +@i[Filtering] is a simple way to perform a fairly arbitrary transformation +on text. Filtering text replaces the string in each line with the result +of applying a @llisp function of one argument to that string. The function must +neither destructively modify the argument nor the return value. It is an +error for the function to return a string containing newline characters. + +@defcom[com "Filter Region"] +This function prompts for an expression which is evaluated to obtain a +function to be used to filter the text in the region. For example, to +capitalize all the words in the region one could respond: +@begin[programexample] +Function: #'@comment<>string-capitalize +@end[programexample] +Since the function may be called many times, it should probably be +compiled. Functions for one-time use can be compiled using the compile +function as in the following example which removes all the semicolons on any line +which contains the string "@f[PASCAL]": +@begin[programexample] +Function: (compile nil '(lambda (s) + (if (search "PASCAL" s) + (remove #\; s) + s))) +@end[programexample] +@enddefcom + +@section[Searching and Replacing] +@index[searching]@index[replacing] +Searching for some string known to appear in the text is a commonly used method +of moving long distances in a file. Replacing occurrences of one pattern with +another is a useful way to make many simple changes to text. @hemlock provides +powerful commands for doing both of these operations. + +@defhvar[var "String Search Ignore Case", val {t}] +@index[case sensitivity] +This variable determines the kind of search done by searching and replacing +commands. +@enddefhvar + +@defcom[com "Incremental Search", bind {C-s}] +@defcom1[com "Reverse Incremental Search", bind {C-r}] +@hid[Incremental Search] searches for an occurrence of a string after the +current point. It is known as an incremental search because it reads +key-events form the keyboard one at a time and immediately searches for the +pattern of corresponding characters as you type. This is useful because +it is possible to initially type in a very short pattern and then add more +characters if it turns out that this pattern has too many spurious matches. + +This command dispatches on the following key-events as sub-commands: +@begin[description] +@binding[C-s]@\ + Search forward for an occurrence of the current pattern. This can be used +repeatedly to skip from one occurrence of the pattern to the next, or it can be +used to change the direction of the search if it is currently a reverse search. +If @binding[C-s] is typed when the search string is empty, then a search is +done for the string that was used by the last searching command. + +@binding[C-r]@\ + Similar to @binding[C-s], except that it searches backwards. + +@binding[Delete, Backspace]@\ + Undoes the effect of the last key-event typed. If that key-event simply added +to the search pattern, then this removes the character from the pattern, moving +back to the last match found before entering the removed character. If the +character was a @binding[C-s] or @binding[C-r], then this moves back to the +previous match and possibly reverses the search direction. + +@binding[C-g]@\ + If the search is currently failing, meaning that there is no occurrence of the +search pattern in the direction of search, then @binding[C-g] deletes enough +characters off the end of the pattern to make it successful. If the search +is currently successful, then @binding[C-g] causes the search to be aborted, +leaving the point where it was when the search started. Aborting the search +inhibits the saving of the current search pattern as the last search string. + +@binding[Escape]@\ + Exit at the current position in the text, unless the search string is empty, +in which case a non-incremental string search is entered. + +@binding[C-q]@\ + Search for the character corresponding to the next key-event, rather than +treating it as a command. +@end[description] +Any key-event not corresponding to a graphic character, except those just +described, causes the search to exit. @hemlock then uses the key-event in it +normal command interpretation. + +For example, typing @binding[C-a] will exit the search @i[and] go to the +beginning of the current line. When either of these commands successfully +exits, they push the starting position (before the search) on the mark stack. +If the current region was active when the search started, this foregoes pushing +a mark. +@enddefcom + +@defcom[com "Forward Search", bind (M-s)] +@defcom1[com "Reverse Search", bind (M-r)] +These commands do a normal dumb string search, prompting for the search +string in a normal dumb fashion. One reason for using a non-incremental +search is that it may be faster since it is possible to specify a long +search string from the very start. Since @hemlock uses the Boyer--Moore +search algorithm, the speed of the search increases with the size of the +search string. +When either of these commands successfully exits, they push the starting +position (before the search) on the mark stack. This is inhibited when the +current region is active. +@enddefcom + +@defcom[com "Query Replace", bind (M-%)] +This command prompts in the echo area for a target string and a replacement +string. It then searches for an occurrence of the target after the point. +When it finds a match, it prompts for a key-event indicating what action to +take. The following are valid responses: +@begin[description] +@binding[Space, y]@\ + Replace this occurrence of the target with the replacement string, and search +again. + +@binding[Delete, Backspace, n]@\ + Do not replace this occurrence, but continue the search. + +@binding[!]@\ + Replace this and all remaining occurrences without prompting again. + +@binding[.]@\ + Replace this occurrence and exit. + +@binding[C-r]@\ + Go into a recursive edit (see page @pageref[recursive-edits]) at the current +location. The search will be continued from wherever the point is left when +the recursive edit is exited. This is useful for handling more complicated +cases where a simple replacement will not achieve the desired effect. + +@binding[Escape]@\ + Exit without doing any replacement. + +@binding[Home, C-_, ?, h]@\ + Print a list of all the options available. +@end[description] +Any other key-event causes the command to exit, returning the key-event to the +input stream; thus, @hemlock will interpret it normally for a command binding. + +When the current region is active, this command uses it instead of the region +from point to the end of the buffer. This is especially useful when you expect +to use the @binding[!] option. + +If the replacement string is all lowercase, then a heuristic is used that +attempts to make the case of the replacement the same as that of the +particular occurrence of the target pattern. If "@f[foo]" is being +replaced with "@f[bar]" then "@f[Foo]" is replaced with "@f[Bar]" and +"@f[FOO]" with "@f[BAR]". + +This command may be undone with @hid[Undo], but its undoing may not be undone. +On a successful exit from this command, the starting position (before the +search) is pushed on the mark stack. +@enddefcom + +@defhvar[var "Case Replace", val {t}] +@index[case sensitivity] +If this variable is true then the case preserving heuristic in +@hid[Query Replace] is enabled, otherwise all replacements are done with +the replacement string exactly as specified. +@enddefhvar + +@defcom[com "Replace String"] +This command is the same as @hid[Query Replace] except it operates without ever +querying the user before making replacements. After prompting for a target and +replacement string, it replaces all occurrences of the target string following +the point. If a prefix argument is specified, then only that many occurrences +are replaced. When the current region is active, this command uses it instead +of the region from point to the end of the buffer. +@enddefcom + +@defcom[com "List Matching Lines"] +This command prompts for a search string and displays in a pop-up window all +the lines containing the string that are after the point. If a prefix argument +is specified, then this displays that many lines before and after each matching +line. When the current region is active, this command uses it instead of the +region from point to the end of the buffer. +@enddefcom + +@defcom[com "Delete Matching Lines"] +@defcom1[com "Delete Non-Matching Lines"] +@hid[Delete Matching Lines] prompts for a search string and deletes all lines +containing the string that are after the point. Similarly, @hid[Delete +Non-Matching Lines] deletes all lines following the point that do not contain +the specified string. When the current region is active, these commands uses +it instead of the region from point to the end of the buffer. +@enddefcom + + +@section[Page Commands] +@index[page commands] +Another unit of text recognized by @hemlock is the page. A @i[page] is a piece +of text delimited by formfeeds (@f[^L]'s.) The first non-blank line after the +page marker is the @i[page title]. The page commands are quite useful when +logically distinct parts of a file are put on separate pages. See also +@comref[Count Lines Page]. These commands only recognize @f[^L]'s at the +beginning of a lines, so those quoted in string literals do not get in the way. + +@defcom[com "Previous Page", bind (C-x @bf<]>)] +@defcom1[com "Next Page", bind (C-x [)] +@hid[Previous Page] moves the point to the previous page delimiter, while +@hid[Next Page] moves to the next one. Any page delimiters next to the point +are skipped. The prefix argument is a repeat count. +@enddefcom + +@defcom[com "Mark Page", bind (C-x C-p)] +This command puts the point at the beginning of the current page and the mark +at the end. If given a prefix argument, marks the page that many pages from the +current one. +@enddefcom + +@defcom[com "Goto Page"] +This command does various things, depending on the prefix argument: +@begin[description] +@i[no argument]@\goes to the next page. + +@i[positive argument]@\goes to an absolute page number, moving that many pages +from the beginning of the file. + +@i[zero argument]@\prompts for string and goes to the page with that string in +its title. Repeated invocations in this manner continue searching from the +point of the last find, and a first search with a particular pattern pushes a +buffer mark. + +@i[negative argument]@\moves backward by that many pages, if possible. +@end[description] +@enddefcom + +@defcom[com "View Page Directory"] +@defcom1[com "Insert Page Directory"] +@hid[View Page Directory] uses a pop-up window to display the number and title +of each page in the current buffer. @hid[Insert Page Directory] is the same +except that it inserts the text at the beginning of the buffer. With a prefix +argument, @hid[Insert Page Directory] inserts at the point. +@enddefcom + + +@section[Counting Commands] + +@defcom[com "Count Words"] +This command counts the number of words from the current point to the end of +the buffer, displaying a message in the echo area. When the current region is +active, this uses it instead of the region from the point to the end of the +buffer. Word delimiters are determined by the current major mode. +@enddefcom + +@defcom[com "Count Lines"] +This command counts the number of lines from the current point to the end of +the buffer, displaying a message in the echo area. When the current region is +active, this uses it instead of the region from the point to the end of the +buffer. +@enddefcom + +@defcom[com "Count Lines Page", bind (C-x l)] +This command displays the number of lines in the current page and the number of +lines before and after the point within that page. If given a prefix argument, +the entire buffer is counted instead of just the current page. +@enddefcom + +@defcom[com "Count Occurrences"] +This command prompts for a search string and displays the number of occurrences +of that string in the text from the point to the end of the buffer. When the +current region is active, this uses it instead of the region from the point to +the end of the buffer. +@enddefcom + + +@section[Registers] +@index[registers] +Registers allow you to save a text position or chunk of text associated with a +key-event. This is a convenient way to repeatedly access a commonly-used +location or text fragment. The concept and key bindings should be familiar to +TECO users. + +@defcom[com "Save Position", bind (C-x s)] +@defcom1[com "Jump to Saved Position", bind (C-x j)] +These commands manipulate registers containing textual positions. +@hid[Save Position] prompts for a register and saves the location of the +current point in that register. @hid[Jump to Saved Position] prompts for a +register and moves the point to the position saved in that register. If the +saved position is in a different buffer, then that buffer is made current. +@enddefcom + +@defcom[com "Put Register", bind (C-x x)] +@defcom1[com "Get Register", bind (C-x g)] +These commands manipulate registers containing text. @hid[Put Register] +prompts for a register and puts the text in the current region into the +register. @hid[Get Register] prompts for a register and inserts the text in +that register at the current point. +@enddefcom + +@defcom[com "List Registers"] +@defcom1[com "Kill Register"] +@hid[List Registers] displays a list of all the currently defined registers in +a pop-up window, along with a brief description of their contents. +@hid[Kill Register] prompts for the name of a register and deletes that +register. +@enddefcom diff --git a/docs/hem/user/intro.mss b/docs/hem/user/intro.mss new file mode 100644 index 0000000..3ee6cee --- /dev/null +++ b/docs/hem/user/intro.mss @@ -0,0 +1,1127 @@ +@comment{-*- Dictionary: target:scribe/hem/hem; Mode: spell; Package: Hemlock -*-} +@chap[Introduction] + +@hemlock is a text editor which follows in the tradition of @emacs +and the Lisp Machine editor ZWEI. In its basic form, @hemlock has almost +the same command set as ITS/TOPS-20 @emacs@foot[In this document, "Emacs" +refers to this, the original version, rather than to any of the large +numbers of text editors inspired by it which may go by the same name.], +and similar features such as multiple windows and extended commands, as +well as built in documentation features. The reader should bear in mind +that whenever some powerful feature of @hemlock is described, it has +probably been directly inspired by @emacs. + +This manual describes @hemlock@comment{}'s commands and other user visible +features and then goes on to tell how to make simple customizations. For +complete documentation of the @hemlock primitives with which commands are +written, the @i[Hemlock Command Implementor's Manual] is also available. + + + +@section[The Point and The Cursor] + +@index[point] +@index[cursor] +The @i[point] is the current focus of editing activity. Text typed in by the +user is inserted at the point. Nearly all commands use the point as a +indication of what text to examine or modify. Textual positions in @hemlock +are between characters. This may seem a bit curious at first, but it is +necessary since text must be inserted between characters. Although the point +points between characters, it is sometimes said to point @i[at] a character, in +which case the character after the point is referred to. + +The @i[cursor] is the visible indication of the current focus of attention: a +rectangular blotch under @windows, or the hardware cursor on a terminal. The +cursor is usually displayed on the character which is immediately after the +point, but it may be displayed in other places. Wherever the cursor is +displayed it indicates the current focus of attention. When input is being +prompted for in the echo area, the cursor is displayed where the input is to +go. Under @windows the cursor is only displayed when @hemlock is waiting +for input. + + +@section[Notation] + +There are a number of notational conventions used in this manual which need +some explanation. + + +@subsection[Key-events] + +@label[key-events] +@index[key-events, notation] +@index[bits, key-event] +@index[modifiers, key-event] +The canonical representation of editor input is a @i[key-event]. When you type +on the keyboard, @hemlock receives key-events. Key-events have names for their +basic form, and we refer to this name as a @i[keysym]. This manual displays +keysyms in a @bf[Bold] font. For example, @bf[a] and @bf[b] are the keys that +normally cause the editor to insert the characters @i[a] and @i[b]. + +Key-events have @i[modifiers] or @i[bits] indicating a special interpretation +of the root key-event. Although the keyboard places limitations on what +key-events you can actually type, @hemlock understands arbitrary combinations +of the following modifiers: @i[Control], @i[Meta], @i[Super], @i[Hyper], +@i[Shift], and @i[Lock]. This manual represents the bits in a key-event by +prefixing the keysym with combinations of @bf[C-], @bf[M-], @bf[S-], @bf[H-], +@bf[Shift-], and @bf[Lock]. For example, @bf[a] with both the control and meta +bits set appears as @bf[C-M-a]. In general, ignore the shift and lock +modifiers since this manual never talks about keysyms that explicitly have +these bits set; that is, it may talk about the key-event @bf[A], but it would +never mention @bf[Shift-a]. These are actually distinct key-events, but +typical input coercion turns presents @hemlock with the former, not the latter. + +Key-event modifiers are totally independent of the keysym. This may be new to +you if you are used to thinking in terms of ASCII character codes. For +example, with key-events you can distinctly identify both uppercase and +lowercase keysyms with the control bit set; therefore, @bf[C-a] and @bf[C-A] +may have different meanings to @hemlock. + +Some keysyms' names consist of more than a single character, and these usually +correspond to the legend on the keyboard. For example, some keyboards let you +enter @bf[Home], @bf[Return], @bf[F9], etc. + +In addition to a keyboard, you may have a mouse or pointer device. Key-events +also represent this kind of input. For example, the down and up transitions of +the @i[left button] correspond to the @bf[Leftdown] and @bf[Leftup] keysyms. + +See sections @ref[key-bindings], @ref[using-x], @ref[using-terminals] + + +@subsection[Commands] + +@index[commands]@label[commands]Nearly everything that can be done in +@hemlock is done using a command. Since there are many things worth +doing, @hemlock provides many commands, currently nearly two hundred. +Most of this manual is a description of what commands exist, how they are +invoked, and what they do. This is the format of a command's +documentation: + +@defcom[com "Sample Command", bind (C-M-q, C-`)] +@begin[quotation, facecode i, leftmargin 8ems, rightmargin 3.5ems, +below 0.8 lines] +This command's name is @hid[Sample Command], and it is bound to +@w(@bf(C-M-q)) and @bf[C-`], meaning that typing either of these will +invoke it. After this header comes a description of what the command does: +@end[quotation] + +This command replaces all occurrences following the point of the string +"@f[Pascal]" with the string "@f[Lisp]". If a prefix argument is supplied, +then it is interpreted as the maximum number of occurrences to replace. If +the prefix argument is negative then the replacements are done backwards +from the point. +@comment< +@begin[quotation, facecode i, leftmargin 8ems, rightmargin 3.5ems, +above 0.8 lines, below 0.8 lines] +Toward the end of the description there may be information primarily of +interest to customizers and command implementors. If you don't understand +this information, don't worry, the writer probably forgot to speak English. +@end[quotation] + +@b[Arguments:] +@begin[description] +@i[target]@\The string to replace with "@f[Lisp]". + +@i[buffer]@\The buffer to do the replacement in. If this is @f[:all] then +the replacement is done in all buffers. +@end[description]> +@enddefcom + + +@subsection[Hemlock Variables] + +@index[variables, hemlock]@hemlock variables supply a simple +customization mechanism by permitting commands to be parameterized. For +details see page @pageref[vars]. + +@defhvar[var "Sample Variable", val {36}] +@begin[quotation, facecode i, leftmargin 8ems, below 0.8 lines] +The name of this variable is @hid[Sample Variable] and its initial value is +36. +@end[quotation] +This variable sets a lower limit on the number of replacements that be done +by @hid[Sample Command]. If the prefix argument is supplied, and smaller +in absolute value than @hid[Sample Variable], then the user is prompted as +to whether that small a number of occurrences should be replaced, so as to +avoid a possibly disastrous error. +@enddefhvar + + +@section[Invoking Commands] +@index[invocation, command] +In order to get a command to do its thing, it must be invoked. The user can do +this two ways, by typing the @i[key] to which the command is @i[bound] or by +using an @i[extended command]. Commonly used commands are invoked via their +key bindings since they are faster to type, while less used commands are +invoked as extended commands since they are easier to remember. + + +@subsection[Key Bindings] +@index[bindings, key] +@index[key bindings] +@label[key-bindings] +A key is a sequence of key-events (see section @ref[key-events]) typed on the +keyboard, usually only one or two in length. Sections @ref[using-x] and +@ref[using-terminals] contain information on particular input devices. + +When a command is bound to a key, typing the key causes @hemlock to invoke the +command. When the command completes its job, @hemlock returns to reading +another key, and this continually repeats. + +Some commands read key-events interpreting them however each command desires. +When commands do this, key bindings have no effect, but you can usually abort +@hemlock whenever it is waiting for input by typing @binding[C-g] (see section +@ref[aborting]). You can usually find out what options are available by typing +@binding[C-_] or @binding[Home] (see section @ref[help]). + +The user can easily rebind keys to different commands, bind new keys to +commands, or establish bindings for commands never bound before (see section +@ref[binding-keys]). + +In addition to the key bindings explicitly listed with each command, there are +some implicit bindings created by using key translations@foot[Key translations +are documented in the @i[Hemlock Command Implementor's Manual].]. These +bindings are not displayed by documentation commands such as @hid[Where Is]. +By default, there are only a few key translations. The modifier-prefix +characters @bf[C-^], @bf[Escape], @bf[C-z], or @bf[C-c] may be used when typing +keys to convert the following key-event to a control, meta, control-meta, or +hyper key-event. For example, @bf[C-x Escape b] invokes the same commands as +@bf[C-x M-b], and @bf[C-z u] is the same as @bf[C-M-u]. This allows user to +type more interesting keys on limited keyboards that lack control, meta, and +hyper keys. +@index[bit-prefix key-events] + + +@defhvar[var "Key Echo Delay", val {1.0}] +A key binding may be composed of several key-events, especially when you enter +it using modifier-prefix key-events. @hemlock provides feedback for partially +entered keys by displaying the typed key-events in the echo area. In order to +avoid excessive output and clearing of the echo area, this display is delayed +by @hid[Key Echo Delay] seconds. If this variable is set to @nil, then +@hemlock foregoes displaying initial subsequences of keys. +@enddefhvar + + +@subsection[Extended Commands] + +@index[commands, extended]A command is invoked as an extended command by +typing its name to the @hid[Extended Command] command, which is invoked +using its key binding, @binding[M-x]. + +@defcom[com "Extended Command", bind {M-x}] +This command prompts in the echo area for the name of a command, and then +invokes that command. The prefix argument is passed through to the command +invoked. The command name need not be typed out in full, as long as enough +of its name is supplied to uniquely identify it. Completion is available +using @binding[Escape] and @binding[Space], and a list of possible completions +is given by @binding[Home] or @binding[C-_]. +@enddefcom + + +@section[The Prefix Argument] + +@index[prefix argument]The prefix argument is an integer argument which +may be supplied to a command. It is known as the prefix argument because +it is specified by invoking some prefix argument setting command +immediately before the command to be given the argument. The following +statements about the interpretation of the prefix argument are true: +@begin[itemize] +When it is meaningful, most commands interpret the prefix argument as a +repeat count, causing the same effect as invoking the command that many +times. + +When it is meaningful, most commands that use the prefix argument interpret +a negative prefix argument as meaning the same thing as a positive +argument, but the action is done in the opposite direction. + +Most commands treat the absence of a prefix argument as meaning the same +thing as a prefix argument of one. + +Many commands ignore the prefix argument entirely. + +Some commands do none of the above. +@end[itemize] +The following commands are used to set the prefix argument: + +@defcom[com "Argument Digit", stuff (bound to all control or meta digits)] +Typing a number using this command sets the prefix argument to that number, +for example, typing @binding[M-1 M-2] sets the prefix argument to twelve. +@enddefcom + +@defcom[com "Negative Argument", bind {M--}] +This command negates the prefix argument, or if there is none, sets it to +negative one. For example, typing @binding[M-- M-7] sets the prefix +argument to negative seven. +@enddefcom + +@defcom[com "Universal Argument", bind {C-u}] +@defhvar1[var "Universal Argument Default", val {4}] +This command sets the prefix argument or multiplies it by four. If digits +are typed immediately afterward, they are echoed in the echo area, and the +prefix argument is set to the specified number. If no digits are typed +then the prefix argument is multiplied by four. @binding[C-u - 7] sets the +prefix argument to negative seven. @binding[C-u C-u] sets the prefix +argument to sixteen. @binding[M-4 M-2 C-u] sets the prefix argument to one +hundred and sixty-eight. @binding[C-u M-0] sets the prefix argument to +forty. + +@hid[Universal Argument Default] determines the default value and multiplier +for the @hid[Universal Argument] command. +@enddefcom + + +@section[Modes] + +@label[modes]@index[modes]A mode provides a way to change @hemlock@comment{}'s +behavior by specifying a modification to current key bindings, values of +variables, and other things. Modes are typically used to adjust @hemlock +to suit a particular editing task, e.g. @hid[Lisp] mode is used for editing +@llisp code. + +Modes in @hemlock are not like modes in most text editors; @hemlock is really a +"modeless" editor. There are two ways that the @hemlock mode concept differs +from the conventional one: +@begin[enumerate] +Modes do not usually alter the environment in a very big way, i.e. replace +the set of commands bound with another totally disjoint one. When a mode +redefines what a key does, it is usually redefined to have a slightly +different meaning, rather than a totally different one. For this reason, +typing a given key does pretty much the same thing no matter what modes are +in effect. This property is the distinguishing characteristic of a +modeless editor. + +Once the modes appropriate for editing a given file have been chosen, they +are seldom, if ever, changed. One of the advantages of modeless editors is +that time is not wasted changing modes. +@end[enumerate] + +@index[major mode]A @i[major mode] is used to make some big change in the +editing environment. Language modes such as @hid[Pascal] mode are major +modes. A major mode is usually turned on by invoking the command +@i{mode-name}@hid[ Mode] as an extended command. There is only one major +mode present at a time. Turning on a major mode turns off the one that is +currently in effect. + +@index[minor mode]A @i[minor mode] is used to make a small change in the +environment, such as automatically breaking lines if they get too long. +Unlike major modes, any number of minor modes may be present at once. +Ideally minor modes should do the "right thing" no matter what major and +minor modes are in effect, but this is may not be the case when key +bindings conflict. + +Modes can be envisioned as switches, the major mode corresponding to one big +switch which is thrown into the correct position for the type of editing being +done, and each minor mode corresponding to an on-off switch which controls +whether a certain characteristic is present. + +@defcom[com "Fundamental Mode"] +This command puts the current buffer into @hid[Fundamental] mode. +@hid[Fundamental] mode is the most basic major mode: it's the next best thing +to no mode at all. +@enddefcom + + +@section[Display Conventions] +@index[display conventions] +There are two ways that @hemlock displays information on the screen; one is +normal @i[buffer display], in which the text being edited is shown on the +screen, and the other is a @i[pop-up window]. + + +@subsection[Pop-Up Windows] +@index[pop-up windows] +@index[random typeout] +@label[pop-up] +Some commands print out information that is of little permanent value, and +these commands use a @i[pop-up] window to display the information. It is known +as a @i[pop-up] window because it temporarily appears on the screen overlaying +text already displayed. Most commands of this nature can generate their output +quickly, but in case there is a lot of output, or the user wants to repeatedly +refer to the same output while editing, @hemlock saves the output in a buffer. +Different commands may use different buffers to save their output, and we refer +to these as @i[random typeout] buffers. + +If the amount of output exceeds the size of the pop-up window, @Hemlock +displays the message @w<"@f[--More--]"> after each window full. The following +are valid responses to this prompt: +@Begin[Description] +@bf[Space], @bf[y]@\ + Display the next window full of text. + +@bf[Delete], @bf[Backspace], @bf[n]@\ + Abort any further output. + +@bf[Escape], @bf[!]@\ + Remove the window and continue saving any further output in the buffer. + +@bf[k]@\ + This is the same as @bf[!] or @bf[escape], but @hemlock makes a normal window +over the pop-up window. This only works on bitmap devices. +@End[Description] +Any other input causes the system to abort using the key-event to determine +the next command to execute. + +When the output is complete, @hemlock displays the string @w<"@f[--Flush--]"> +in the pop-up window's modeline, indicating that the user may flush the +temporary display. Typing any of the key-events described above removes the +pop-up window, but typing @bf[k] still produces a window suitable for normal +editing. Any other input also flushes the display, but @hemlock uses the +key-event to determine the next command to invoke. + +@defcom[com "Select Random Typeout Buffer", bind {H-t}] +This command makes the most recently used random typeout buffer the current +buffer in the current window. +@enddefcom + +Random typeout buffers are always in @hid[Fundamental] mode. + + +@subsection[Buffer Display] +@index[buffer, display] +@index[display, buffer] + +If a line of text is too long to fit within the screen width it is @i[wrapped], +with @hemlock displaying consecutive pieces of the text line on as many screen +lines as needed to hold the text. @hemlock indicates a wrapped line by placing +a line-wrap character in the last column of each screen line. Currently, the +line-wrap character is an exclamation point (@f[!]). It is possible for a line +to wrap off the bottom of the screen or on to the top. + +@hemlock wraps screen lines when the line is completely full regardless of the +line-wrap character. Most editors insert the line-wrap character and wrap a +single character when a screen line would be full if the editor had avoided +wrapping the line. In this situation, @hemlock would leave the screen line +full. This means there are always at least two characters on the next screen +line if @hemlock wraps a line of display. When the cursor is at the end of a +line which is the full width of the screen, it is displayed in the last column, +since it cannot be displayed off the edge. + +@hemlock displays most characters as themselves, but it treats some +specially: +@begin[itemize] +Tabs are treated as tabs, with eight character tab-stops. + +Characters corresponding to ASCII control characters are printed as +@f[^]@i[char]; for example, a formfeed is @f[^L]. + +Characters with the most-significant bit on are displayed as +@f[<]@i[hex-code]@f[>]; for example, @f[]. +@end[itemize] +Since a character may be displayed using more than one printing character, +there are some positions on the screen which are in the middle of a character. +When the cursor is on a character with a multiple-character representation, +@hemlock always displays the cursor on the first character. + + +@subsection[Recentering Windows] +@index[recentering windows] +@index[windows, recentering] + +When redisplaying the current window, @hemlock makes sure the current point is +visible. This is the behavior you see when you are entering text near the +bottom of the window, and suddenly redisplay shifts your position to the +window's center. + +Some buffers receive input from streams and other processes, and you might have +windows displaying these. However, if those windows are not the current +window, the output will run off the bottom of the windows, and you won't be +able to see the output as it appears in the buffers. You can change to a +window in which you want to track output and invoke the following command to +remedy this situation. + +@defcom[com "Track Buffer Point"] +This command makes the current window track the buffer's point. This means +that each time Hemlock redisplays, it will make sure the buffer's point is +visible in the window. This is useful for windows that are not current and +that display buffer's that receive output from streams coming from other +processes. +@enddefcom + + +@subsection[Modelines] +@label[modelines] +@index[modeline] +A modeline is the line displayed at the bottom of each window where @hemlock +shows information about the buffer displayed in that window. Here is a typical +modeline: +@begin[programexample] +Hemlock USER: (Fundamental Fill) /usr/slisp/hemlock/user.mss +@end[programexample] +This tells us that the file associated with this buffer is +"@f[/usr/slisp/hemlock/user.mss]", and the @hid[Current Package] for Lisp +interaction commands is the @f["USER"] package. The modes currently present +are @hid[Fundamental] and @hid[Fill]; the major mode is always displayed first, +followed by any minor modes. If the buffer has no associated file, then the +buffer name will be present instead: +@begin[programexample] +Hemlock PLAY: (Lisp) Silly: +@end[programexample] +In this case, the buffer is named @hid[Silly] and is in @hid[Lisp] mode. The +user has set @hid[Current Package] for this buffer to @f["PLAY"]. + +@defhvar[var "Maximum Modeline Pathname Length", val {nil}] +This variable controls how much of a pathname @hemlock displays in a modeline. +Some distributed file systems can have very long pathnames which leads to the +more particular information in a pathname running off the end of a modeline. +When set, the system chops off leading directories until the name is less than +the integer value of this variable. Three dots, @f[...], indicate a truncated +name. The user can establish this variable buffer locally with the +@hid[Defhvar] command. +@enddefhvar + +If the user has modified the buffer since the last time it was read from or +save to a file, then the modeline contains an asterisk (@f[*]) between the +modes list and the file or buffer name: +@begin[programexample] +Hemlock USER: (Fundamental Fill) * /usr/slisp/hemlock/user.mss +@end[programexample] +This serves as a reminder that the buffer should be saved eventually. + +@index[status line] +There is a special modeline known as the @i[status line] which appears as the +@hid[Echo Area]'s modeline. @Hemlock and user code use this area to display +general information not particular to a buffer @dash recursive edits, whether +you just received mail, etc. + + +@section[Use with X Windows] +@label[using-x] +@index[X windows, use with] +You should use @hemlock on a workstation with a bitmap display and a windowing +system since @hemlock makes good use of a non-ASCII device, mouse, and the +extra modifier keys typically associated with workstations. This section +discusses using @hemlock under X windows, the only supported windowing system. + + +@subsection[Window Groups] +@index[window management] +@label[groups] +@hemlock manages windows under X in groups. This allows @hemlock to be more +sophisticated in its window management without being rude in the X paradigm of +screen usage. With window groups, @hemlock can ignore where the groups are, +but within a group, it can maintain the window creation and deletion behavior +users expect in editors without any interference from window managers. + +Initially there are two groups, a main window and the @hid[Echo Area]. If you +keep a pop-up display, see section @ref[pop-up], @hemlock puts the window it +creates in its own group. There are commands for creating new groups. + +@hemlock only links windows within a group for purposes of the @hid[Next +Window], @hid[Previous Window], and @hid[Delete Next Window] commands. To move +between groups, you must use the @hid[Point to Here] command bound to the +mouse. + +Window manager commands can reshape and move groups on the screen. + + +@subsection[Event Translation] +@index[keyboard use under X] +@index[translation of keys under X] +Each X key event is translated into a canonical input representation, a +key-event. The X key event consists of a scan-code and modifier bits, and +these translate to an X keysym. This keysym and the modifier bits map to a +key-event. + +If you type a key with a shift key held down, this typically maps to a distinct +X keysym. For example, the shift of @bf[3] is @bf[#], and these have different +X keysyms. Some keys map to the same X keysym regardless of the shift bit, +such as @bf[Tab], @bf[Space], @bf[Return], etc. When the X lock bit is on, the +system treats this as a caps-lock, only mapping keysyms for lowercase letters +to shifted keysyms. + +The key-event has a keysym and a field of bits. The X keysyms map directly to +the key-event keysyms. There is a distinct mapping for each CLX modifier bit +to a key-event bit. This tends to eliminate shift and lock modifiers, so +key-events usually only have control, meta, hyper, and super bits on. Hyper +and super usually get turned on with prefix key-events that set them on the +following key-event, but you can turn certain keys on the keyboard into hyper +and super keys. See the X manuals and the @i[Hemlock Command Implementor's +Manual] for details. + +The system also maps mouse input to key-events. Each mouse button has distinct +key-event keysyms for whether the user pressed or released it. For +convenience, @hemlock makes use of an odd property of converting mouse events +to key-events. If you enter a mouse event with the shift key held down, +@hemlock sees the key-event keysym for the mouse event, but the key-event has +the super bit turned on. For example, if you press the left button with the +shift key pressed, @hemlock sees @bf[S-Leftdown]. + +Note that with the two button mouse on the IBM RT PC, the only way to to send +@bf[Middledown] is to press both the left and right buttons simultaneously. +This is awkward, and it often confuses the X server. For this reason, the +commands bound to the middle button are also bound to the shifted left button, +@bf[S-Leftdown], which is much easier to type. + + +@subsection[Cut Buffer Commands] +@index[cutting]@index[pasting] These commands allow the X cut buffer to be +used from @hemlock . Although @hemlock can cut arbitrarily large regions, +a bug in the standard version 10 xterm prevents large regions from being +pasted into an xterm window. + +@defcom[com "Region to Cut Buffer", bind {M-Insert}] +@defcom1[com "Insert Cut Buffer", bind {Insert}] +These commands manipulate the X cut buffer. @hid[Region to Cut Buffer] puts +the text in the region into the cut buffer. @hid[Insert Cut Buffer] inserts +the contents of the cut buffer at the point. +@enddefcom + +@subsection[Redisplay and Screen Management] + +These variables control a number of the characteristics of @hemlock bitmap +screen management. + +@defhvar[var "Bell Style", val {:border-flash}] +@defhvar1[var "Beep Border Width", val {20}] +@hid[Bell Style] determines what beeps do in @hemlock. Acceptable values are +@kwd[border-flash], @kwd[feep], @kwd[border-flash-and-feep], @kwd[flash], +@kwd[flash-and-feep], and @nil (do nothing). + +@hid[Beep Border Width] is the width in pixels of the border flashed by border +flash beep styles. +@enddefhvar + +@defhvar[var "Reverse Video", val {nil}] +If this variable is true, then @hemlock paints white on black in window +bodies, black on white in modelines. +@enddefhvar + +@defhvar[var "Thumb Bar Meter", val {t}] +If this variable is true, then windows will be created to be displayed with a +ruler in the bottom border of the window. +@enddefhvar + +@defhvar[var "Set Window Autoraise", val {:echo-only}] +When true, changing the current window will automatically raise the new current +window. If the value is @kwd[echo-only], then only the echo area window will +be raised automatically upon becoming current. +@enddefhvar + +@defhvar[var "Default Initial Window Width", val {80}] +@defhvar1[var "Default Initial Window Height", val {24}] +@defhvar1[var "Default Initial Window X"] +@defhvar1[var "Default Initial Window Y"] +@defhvar1[var "Default Window Height", val {24}] +@defhvar1[var "Default Window Width", val {80}] +@index[window placement] +@Hemlock uses the variables with "@hid[Initial]" in their names when it first +starts up to make its first window. The width and height are specified in +character units, but the x and y are specified in pixels. The other variables +determine the width and height for interactive window creation, such as making +a window with @comref[New Window]. +@enddefhvar + +@defhvar[var "Cursor Bitmap File", val {"library:hemlock.cursor"}] +This variable determines where the mouse cursor bitmap is read from when +@hemlock starts up. The mask is found by merging this name with "@f[.mask]". +This has to be a full pathname for the C routine. +@enddefhvar + + +@defhvar[var "Default Font"] +This variable holds the string name of the font to be used for normal text +display: buffer text, modelines, random typeout, etc. The font is loaded at +initialization time, so this variable must be set before entering @hemlock. +When @nil, the display type is used to choose a font. +@enddefhvar + + +@section[Use With Terminals] +@label[using-terminals]@index[terminals, use with] @hemlock can also be used +with ASCII terminals and terminal emulators. Capabilities that depend on +@windows (such as mouse commands) are not available, but nearly everything else +can be done. + +@subsection[Terminal Initialization] + +@index[terminal speed] +@index[speed, terminal] +@index[slow terminals] +@index[incremental redisplay] +For best redisplay performance, it is very important to set the terminal speed: +@lisp +stty 2400 +@endlisp +Often when running @hemlock using TTY redisplay, Hemlock will actually be +talking to a PTY whose speed is initialized to infinity. In reality, the +terminal will be much slower, resulting in @hemlock's output getting way ahead +of the terminal. This prevents @hemlock from briefly stopping redisplay to +allow the terminal to catch up. See also @hvarref. + +The terminal control sequences are obtained from the termcap database using the +normal Unix conventions. The @f["TERM"] environment variable holds the +terminal type. The @f["TERMCAP"] environment variable can be used to override +the default termcap database (in @f["/etc/termcap"]). The size of the terminal +can be altered from the termcap default through the use of: +@lisp +stty rows @i{height} columns @i{width} +@endlisp + +@subsection[Terminal Input] +@index[ASCII keyboard translation] +@index[bit-prefix key-events] +@index[prefix key-events] +@index[key-event, prefix] +The most important limitation of a terminal is its input capabilities. On a +workstation with function keys and independent control, meta, and shift +modifiers, it is possible to type 800 or so distinct single keystrokes. +Although by default, @hemlock uses only a fraction of these combinations, there +are many more than the 128 key-events available in ASCII. + +On a terminal, @hemlock attempts to translate ASCII control characters into the +most useful key-event: +@begin[itemize] +On a terminal, control does not compose with shift. If the control key is down +when you type a letter keys, the terminal always sends one code regardless of +whether the shift key is held. Since @hemlock primarily binds commands to +key-events with keysyms representing lowercase letters regardless of what bits +are set in the key-event, the system translates the ASCII control codes to a +keysym representing the appropriate lowercase characters. This keysym then +forms a key-event with the control bit set. Users can type @bf[C-c] followed +by an uppercase character to form a key-event with a keysym representing an +uppercase character and bits with the control bit set. + +On a terminal, some of the named keys generate an ASCII control code. For +example, @f[Return] usually sends a @f[C-m]. The system translates these ASCII +codes to a key-event with an appropriate keysym instead of the keysym named by +the character which names the ASCII code. In the above example, typing the +@f[Return] key would generate a key-event with the @bf[Return] keysym and no +bits. It would NOT translate to a key-event with the @bf[m] keysym and the +control bit. +@end[itemize] + +Since terminals have no meta key, you must use the @bf[Escape] and @bf[C-Z] +modifier-prefix key-events to invoke commands bound to key-events with the meta +bit or meta and control bits set. ASCII terminals cannot generate all +key-events which have the control bit on, so you can use the @bf[C-^] +modifier-prefix. The @bf[C-c] prefix sets the hyper bit on the next key-event +typed. + +When running @hemlock from a terminal @f[^\] is the interrupt key-event. +Typing this will place you in the Lisp debugger. + +When using a terminal, pop-up output windows cannot be retained after the +completion of the command. + + +@subsection[Terminal Redisplay] + +Redisplay is substantially different on a terminal. @Hemlock uses different +algorithms, and different parameters control redisplay and screen management. + +Terminal redisplay uses the Unix termcap database to find out how to use a +terminal. @hemlock is useful with terminals that lack capabilities for +inserting and deleting lines and characters, and some terminal emulators +implement these operations very inefficiently (such as xterm). +If you realize poor performance when scrolling, create a termcap entry that +excludes these capabilities. + +@defhvar[var "Scroll Redraw Ratio", val {nil}] +This is a ratio of "inserted" lines to the size of a window. When this ratio +is exceeded, insert/delete line terminal optimization is aborted, and every +altered line is simply redrawn as efficiently as possible. For example, +setting this to 1/4 will cause scrolling commands to redraw the entire window +instead of moving the bottom two lines of the window to the top (typically 3/4 +of the window is being deleted upward and inserted downward, hence a redraw); +however, commands like @hid[New Line] and @hid[Open Line] will still work +efficiently, inserting a line and moving the rest of the window's text +downward. +@enddefhvar + + +@section[The Echo Area] + +@index[echo area] +@index[prompting] +The echo area is the region which occupies the bottom few lines on the screen. +It is used for two purposes: displaying brief messages to the user and +prompting. + +When a command needs some information from the user, it requests it by +displaying a @i[prompt] in the echo area. The following is a typical prompt: +@begin[programexample] +Select Buffer: [hemlock-init.lisp /usr/foo/] +@end[programexample] +The general format of a prompt is a one or two word description of the input +requested, possibly followed by a @i[default] in brackets. The default is a +standard response to the prompt that @hemlock uses if you type @bf[Return] +without giving any other input. + +There are four general kinds of prompts: @comment +@begin[description] +@i[key-event]@\ + The response is a single key-event and no confirming @binding[Return] is +needed. + +@i[keyword]@\ + The response is a selection from one of a limited number of choices. +Completion is available using @binding[Space] and @binding[Escape], and you +only need to supply enough of the keyword to distinguish it from any other +choice. In some cases a keyword prompt accepts unknown input, indicating the +prompter should create a new entry. If this is the case, then you must enter +the keyword fully specified or completed using @binding[Escape]; this +distinguishes entering an old keyword from making a new keyword which is a +prefix of an old one since the system completes partial input automatically. + +@i[file]@\ + The response is the name of a file, which may have to exist. Unlike other +prompts, the default has some effect even after the user supplies some input: +the system @i[merges] the default with the input filename. See page +@pageref(merging) for a description of filename merging. @bf[Escape] and +@bf[Space] complete the input for a file parse. + +@i[string]@\ + The response is a string which must satisfy some property, such as being the +name of an existing file. +@end[description] + +@index[history, echo area] +These key-events have special meanings when prompting: +@begin[description] +@binding[Return]@\ + Confirm the current parse. If no input has been entered, then use the +default. If for some reason the input is unacceptable, @hemlock does two +things: +@Begin[enumerate] +beeps, if the variable @hid[Beep on Ambiguity] set, and + +moves the point to the end of the first word requiring disambiguation. +@End[enumerate] +This allows you to add to the input before confirming the it again. + +@binding[Home, C-_]@\ + Print some sort of help message. If the parse is a keyword parse, then print +all the possible completions of the current input in a pop-up window. + +@binding[Escape]@\ + Attempt to complete the input to a keyword or file parse as far as possible, +beeping if the result is ambiguous. When the result is ambiguous, @hemlock +moves the point to the first ambiguous field, which may be the end of the +completed input. + +@binding[Space]@\ + In a keyword parse, attempt to complete the input up to the next space. This +is useful for completing the names of @hemlock commands and similar things +without beeping a lot, and you can continue entering fields while leaving +previous fields ambiguous. For example, you can invoke @hid[Forward Word] as +an extended command by typing @binding[M-X f Space w Return]. Each time the +user enters space, @Hemlock attempts to complete the current field and all +previous fields. + +@binding[C-i, Tab]@\ + In a string or keyword parse, insert the default so that it may be edited. + +@binding[C-p]@\ + Retrieve the text of the last string input from a history of echo area inputs. +Repeating this moves to successively earlier inputs. + +@binding[C-n]@\ + Go the other way in the echo area history. + +@binding[C-q]@\ + Quote the next key-event so that it is not interpreted as a command. +@end[description] + +@defhvar[var "Ignore File Types"] +This variable is a list of file types (or extensions), represented as a string +without the dot, e.g. @f["fasl"]. Files having any of the specified types will +be considered nonexistent for completion purposes, making an unambiguous +completion more likely. The initial value contains most common binary and +output file types. +@enddefhvar + + +@section[Online Help] + +@label[help] +@index[online help] +@index[documentation, hemlock] +@hemlock has a fairly good online documentation facility. You can get brief +documentation for every command, variable, character attribute, and key +by typing a key. + +@defcom[com "Help", bind (Home, C-_)] +This command prompt for a key-event indicating one of a number of other +documentation commands. The following are valid responses: +@begin[description] +@bf[a]@\ + List commands and other things whose names contain a specified keyword. + +@bf[d]@\ + Give the documentation and bindings for a specified command. + +@bf[g]@\ + Give the documentation for any @hemlock thing. + +@bf[v]@\ + Give the documentation for a @hemlock variable and its values. + +@bf[c]@\ + Give the documentation for a command bound to some key. + +@bf[l]@\ + List the last sixty key-events typed. + +@bf[m]@\ + Give the documentation for a mode followed by a short description of its +mode-specific bindings. + +@bf[p]@\ + Give the documentation and bindings for commands that have at least one +binding involving a mouse/pointer key-event. + +@bf[w]@\ + List all the key bindings for a specified command. + +@bf[t]@\ + Describe a @llisp object. + +@binding[q]@\ + Quit without doing anything. + +@binding[Home, C-_, ?, h]@\ + List all of the options and what they do. +@end[description] +@enddefcom + +@defcom[com "Apropos", bind (Home a, C-_ a)] +This command prints brief documentation for all commands, variables, and +character attributes whose names match the input. This performs a prefix match +on each supplied word separately, intersecting the names in each word's result. +For example, giving @hid[Apropos] "@f[f m]" causes it to tersely describe +following commands and variables: +@Begin[Itemize] +@hid[Auto Fill Mode] + +@hid[Fundamental Mode] + +@hid[Mark Form] + +@hid[Default Modeline Fields] + +@hid[Fill Mode Hook] + +@hid[Fundamental Mode Hook] +@End[Itemize] +Notice @hid[Mark Form] demonstrates that the "@f[f]" words may follow the +"@f[m]" order of the fields does not matter for @hid[Apropos]. + +The bindings of commands and values of variables are printed with the +documentation. +@enddefcom + +@defcom[com "Describe Command", bind (Home d, C-_ d)] +This command prompts for a command and prints its full documentation and all +the keys bound to it. +@enddefcom + +@defcom[com "Describe Key", bind (Home c, C-_ c, M-?)] +This command prints full documentation for the command which is bound to +the specified key in the current environment. +@enddefcom + +@defcom[com "Describe Mode", bind (Home m, C-_ m)] +This command prints the documentation for a mode followed by a short +description of each of its mode-specific bindings. +@enddefcom + +@defcom[com "Show Variable"] +@defcom1[com "Describe and Show Variable"] +@hid[Show Variable] prompts for the name of a variable and displays +the global value of the variable, the value local to the current buffer (if +any), and the value of the variable in all defined modes that have it as a +local variable. @hid[Describe and Show Variable] displays the variable's +documentation in addition to the values. +@enddefcom + +@defcom[com "What Lossage", bind (Home l, C-_ l)] +This command displays the last sixty key-events typed. This can be useful +if, for example, you are curious what the command was that you typed by +accident. +@enddefcom + +@defcom[com "Describe Pointer"] +This command displays the documentation and bindings for commands that have +some binding involving a mouse/pointer key-event. It will not show the +documentation for the @hid[Illegal] command regardless of whether it has a +pointer binding. +@enddefcom + +@defcom[com "Where Is", bind (Home w, C-_ w)] +This command prompts for the name of a command and displays its key +bindings in a pop-up window. If a key binding is not global, the +environment in which it is available is displayed. +@enddefcom + +@defcom[com "Generic Describe", bind (Home g, C-_ g)] +This command prints full documentation for any thing that has +documentation. It first prompts for the kind of thing to document, the +following options being available: +@begin[description] +@i[attribute]@\Describe a character attribute, given its name. + +@i[command]@\Describe a command, given its name. + +@i[key]@\Describe a command, given a key to which it is bound. + +@i[variable]@\Describe a variable, given its name. This is the default. +@end[description] +@enddefcom + + +@section[Entering and Exiting] + +@index[entering hemlock]@hemlock is entered by using the @clisp @f[ed] +function. Simply typing @f[(ed)] will enter @hemlock, leaving you in the state +that you were in when you left it. If @hemlock has never been entered before +then the current buffer will be @hid[Main]. The @f[-edit] command-line switch +may also be used to enter @hemlock: see page @pageref[edit-switch]. + +@f[ed] may optionally be given a file name or a symbol argument. Typing +@f[(ed @i[filename])] will cause the specified file to be read into @hemlock, +as though by @hid[Find File]. Typing @w<@f[(ed @i[symbol])]> will pretty-print +the definition of the symbol into a buffer whose name is obtained by adding +"@f[Edit ]" to the beginning of the symbol's name. + +@defcom[com "Exit Hemlock", bind (C-c, C-x C-z)] +@defcom1[com "Pause Hemlock"] +@index[exiting hemlock]@hid[Exit Hemlock] exits @hemlock, returning @f[t]. +@hid[Exit Hemlock] does not by default save modified buffers, or do +anything else that you might think it should do; it simply exits. At any time +after exiting you may reenter by typing @f[(ed)] to @llisp without losing +anything. Before you quit from @llisp using @f[(quit)], you should +save any modified files that you want to be saved. + +@hid[Pause Hemlock] is similar, but it suspends the @llisp process and returns +control to the shell. When the process is resumed, it will still be running +@hemlock. +@enddefcom + + +@section[Helpful Information] + +@label[aborting] +@index[aborting] +@index[undoing] +@index[error recovery] +This section contains assorted helpful information which may be useful in +staying out of trouble or getting out of trouble. + +@begin[itemize] +It is possible to get some sort of help nearly everywhere by typing +@binding[Home] or @binding[C-_]. + +Various commands take over the keyboard and insist that you type the key-events +that they want as input. If you get in such a situation and want to get out, +you can usually do so by typing @bf[C-g] some small number of times. If this +fails you can try typing @binding[C-x C-z] to exit @hemlock and then "@f[(ed)]" +to re-enter it. + +Before you quit, make sure you have saved all your changes. @binding[C-u C-x +C-b] will display a list of all modified buffers. If you exit using @bf[C-x +M-z], then @hemlock will save all modified buffers with associated files. + +If you lose changes to a file due to a crash or accidental failure to save, +look for backup ("@i[file]@f[.BAK]") or checkpoint ("@i[file]@f[.CKP]") files +in the same directory where the file was. + +If the screen changes unexpectedly, you may have accidentally typed an +incorrect command. Use @binding[Home l] to see what it was. If you are +not familiar with the command, use @binding[Home c] to see what it is so that +you know what damage has been done. Many interesting commands can be found +in this fashion. This is an example of the much-underrated learning +technique known as "Learning by serendipitous malcoordination". Who would +ever think of looking for a command that deletes all files in the current +directory? + +If you accidentally type a "killing" command such as @binding[C-w], you can +get the lost text back using @binding[C-y]. The @hid[Undo] command is also +useful for recovering from this sort of problem. +@end[itemize] + +@defhvar[var "Region Query Size", val {30}] +@index[large region] +Various commands ask for confirmation before modifying a region containing more +than this number of lines. If this is @nil, then these commands refrain from +asking, no matter how large the region is. +@enddefhvar + +@defcom[com "Undo"] +This command undoes the last major modification. Killing commands and some +other commands save information about their modifications, so accidental uses +may be retracted. This command displays the name of the operation to be undone +and asks for confirmation. If the affected text has been modified between the +invocations of @hid[Undo] and the command to be undone, then the result may be +somewhat incorrect but useful. Often @hid[Undo] itself can be undone by +invoking it again. +@enddefcom + + +@section[Recursive Edits] +@label[recursive-edits] +@index[recursive edits] +Some sophisticated commands, such as @hid[Query Replace], can place you in a +@i[recursive edit]. A recursive edit is simply a recursive invocation of +@hemlock done within a command. A recursive edit is useful because it allows +arbitrary editing to be done during the execution of a command without losing +any state that the command might have. When the user exits a recursive edit, +the command that entered it proceeds as though nothing happened. @Hemlock +notes recursive edits in the @hid[Echo Area] modeline, or status line. A +counter reflects the number of pending recursive edits. + +@defcom[com "Exit Recursive Edit", bind (C-M-z)] +This command exits the current recursive edit, returning @nil. If invoked when +not in a recursive edit, then this signals an user error. +@enddefcom + +@defcom[com "Abort Recursive Edit", bind (@bf)] +This command causes the command which invoked the recursive edit to get an +error. If not in a recursive edit, this signals an user error. +@enddefcom + + +@section[User Errors] +@index[beeping] +@index[errors, user] +When in the course of editing, @hemlock is unable to do what it thinks you want +to do, then it brings this to your attention by a beep or a screen flash +(possibly accompanied by an explanatory echo area message such as @w<"@f[No +next line.]">.) Although the exact attention-getting mechanism may vary on the +output device and variable settings, this is always called @i[beeping]. + +Whatever the circumstances, you had best try something else since @hemlock, +being far more stupid than you, is far more stubborn. @hemlock is an +extensible editor, so it is always possible to change the command that +complained to do what you wanted it to do. + +@section[Internal Errors] + +@index[errors, internal]A message of this form may appear in the echo +area, accompanied by a beep: +@begin[programexample] +Internal error: +Wrong type argument, NIL, should have been of type SIMPLE-VECTOR. +@end[programexample] +If the error message is a file related error such as the following, then +you have probably done something illegal which @hemlock did not catch, +but was detected by the file system: +@begin[programexample] +Internal error: +No access to "/lisp2/emacs/teco.mid" +@end[programexample] +Otherwise, you have found a bug. Try to avoid the behavior that resulted +in the error and report the problem to your system maintainer. Since @llisp +has fairly robust error recovery mechanisms, probably no damage has been +done. + +If a truly abominable error from which @hemlock cannot recover occurs, +then you will be thrown into the @llisp debugger. At this point it would be +a good idea to save any changes with @f[save-all-buffers] and then start +a new @llisp. + +@index[save-all-buffers, function]The @llisp function @f[save-all-buffers] may +be used to save modified buffers in a seriously broken @hemlock. To use this, +type "@f[(save-all-buffers)]" to the top-level ("@f[* ]") or debugger +("@f<1] >") prompt and confirm saving of each buffer that should be saved. +Since this function will prompt in the "@f[Lisp]" window, it isn't very useful +when called inside of @hemlock. diff --git a/docs/hem/user/lisp.mss b/docs/hem/user/lisp.mss new file mode 100644 index 0000000..fc749ca --- /dev/null +++ b/docs/hem/user/lisp.mss @@ -0,0 +1,822 @@ +@comment{-*- Dictionary: /afs/cs/project/clisp/scribe/hem/hem; Mode: spell; Package: Hemlock -*-} +@chap[Interacting With Lisp] +@index[lisp, interaction with] + +Lisp encourages highly interactive programming environments by requiring +decisions about object type and function definition to be postponed until run +time. @hemlock supports interactive programming in @llisp by providing +incremental redefinition and environment examination commands. @hemlock also +uses Unix TCP sockets to support multiple Lisp processes, each of which may be +on any machine. + + +@section[Eval Servers] +@label[eval-servers] +@index[eval servers] + +@hemlock runs in the editor process and interacts with other Lisp processes +called @i[eval servers]. A user's Lisp program normally runs in an eval +server process. The separation between editor and eval server has several +advantages: +@begin[itemize] +The editor is protected from any bad things which may happen while debugging a +Lisp program. + +Editing may occur while running a Lisp program. + +The eval server may be on a different machine, removing the load from the +editing machine. + +Multiple eval servers allow the use of several distinct Lisp environments. +@end[itemize] +Instead of providing an interface to a single Lisp environment, @hemlock +coordinates multiple Lisp environments. + + +@subsection[The Current Eval Server] +@index[current eval server] +Although @hemlock can be connected to several eval servers simultaneously, one +eval server is designated as the @i[current eval server]. This is the eval +server used to handle evaluation and compilation requests. Eval servers are +referred to by name so that there is a convenient way to discriminate between +servers when the editor is connected to more than one. The current eval server +is normally globally specified, but it may also be shadowed locally in specific +buffers. + +@defcom[com "Set Eval Server"] +@defcom1[com "Set Buffer Eval Server"] +@defcom1[com "Current Eval Server"] +@hid[Set Eval Server] prompts for the name of an eval server and makes it the +the current eval server. @hid[Set Buffer Eval Server] is the same except that +is sets the eval server for the current buffer only. @hid[Current Eval Server] +displays the name of the current eval server in the echo area, taking any +buffer eval server into consideration. See also @comref[Set Compile Server]. +@enddefcom + + +@subsection[Slaves] +@index[slave buffers] +@index[slaves] +For now, all eval servers are @i[slaves]. A slave is a Lisp process that uses +a typescript (see page @pageref[typescripts]) to run its top-level +@f[read-eval-print] loop in a @hemlock buffer. We refer to the buffer that a +slave uses for I/O as its @i[interactive] or @i[slave] buffer. The name of the +interactive buffer is the same as the eval server's name. + +@index[background buffers] +@hemlock creates a @i[background] buffer for each eval server. The background +buffer's name is @w<@hid[Background ]@i{name}>, where @i[name] is the name of +the eval server. Slaves direct compiler warning output to the background +buffer to avoid cluttering up the interactive buffer. + +@hemlock locally sets @hid[Current Eval Server] in interactive and background +buffers to their associated slave. When in a slave or background buffer, eval +server requests will go to the associated slave, regardless of the global value +of @hid[Current Eval Server]. + +@defcom[com "Select Slave", bind (C-M-c)] +This command changes the current buffer to the current eval server's +interactive buffer. If the current eval server is not a slave, then it beeps. +If there is no current eval server, then this creates a slave (see section +@ref[slave-creation]). If a prefix argument is supplied, then this creates a +new slave regardless of whether there is a current eval server. This command +is the standard way to create a slave. + +The slave buffer is a typescript (see page @pageref[typescripts]) the slave +uses for its top-level @f[read-eval-print] loop. +@enddefcom + +@defcom[com "Select Background", bind (C-M-C)] +This command changes the current buffer to the current eval server's background +buffer. If there is no current eval server, then it beeps. +@enddefcom + + +@subsection[Slave Creation and Destruction] +@label[slave-creation] +When @hemlock first starts up, there is no current eval server. If there is no +a current eval server, commands that need to use the current eval server will +create a slave as the current eval server. + +If an eval server's Lisp process terminates, then we say the eval server is +dead. @hemlock displays a message in the echo area, interactive, and +background buffers whenever an eval server dies. If the user deletes an +interactive or background buffer, the associated eval server effectively +becomes impotent, but @hemlock does not try to kill the process. If a command +attempts to use a dead eval server, then the command will beep and display a +message. + +@defhvar[var "Confirm Slave Creation", val {t}] +If this variable is true, then @hemlock always prompts the user for +confirmation before creating a slave. +@enddefhvar + +@defhvar[var "Ask About Old Servers", val {t}] +If this variable is true, and some slave already exists, @hemlock prompts the +user for the name of an existing server when there is no current server, +instead of creating a new one. +@enddefhvar + +@defcom[com "Editor Server Name"] +This command echos the editor server's name, the machine and port of the +editor, which is suitable for use with the Lisp processes -slave switch. +See section @ref[slave-switch]. +@enddefcom + +@defcom[com "Accept Slave Connections"] +This command cause @hemlock to accept slave connections, and it displays the +editor server's name, which is suitable for use with the Lisp processes -slave +switch. See section @ref[slave-switch]. Supplying an argument causes this +command to inhibit slave connections. +@enddefcom + +@defhvar[var "Slave Utility", val {"/usr/misc/.lisp/bin/lisp"}] +@defhvar1[var "Slave Utility Switches"] +A slave is started by running the program @hid[Slave Utility Name] with +arguments specified by the list of strings @hid[Slave Utility Switches]. This +is useful primarily when running customized Lisp systems. For example, +setting @hid[Slave Utility Switches] to @f[("-core" "my.core")] will cause +"@f[/usr/hqb/my.core]" to be used instead of the default core image. + +The @f[-slave] switch and the editor name are always supplied as arguments, and +should remain unspecified in @hid[Slave Utility Switches]. +@enddefhvar + +@defcom[com "Kill Slave"] +@defcom1[com "Kill Slave and Buffers"] +@hid[Kill Slave] prompts for a slave name, aborts any operations in the slave, +tells the slave to @f[quit], and shuts down the connection to the specified +eval server. This makes no attempt to assure the eval server actually dies. + +@hid[Kill Slave and Buffers] is the same as @hid[Kill Slave], but it also +deletes the interactive and background buffers. +@enddefcom + + +@subsection[Eval Server Operations] + +@label[operations] +@index[eval server operations]@index[operations, eval server] +@hemlock handles requests for compilation or evaluation by queuing an +@i[operation] on the current eval server. Any number of operations may be +queued, but each eval server can only service one operation at a time. +Information about the progress of operations is displayed in the echo area. + +@defcom[com "Abort Operations", bind (C-c a)] +This command aborts all operations on the current eval server, either queued or +in progress. Any operations already in the @f[Aborted] state will be flushed. +@enddefcom + +@defcom[com "List Operations", bind (C-c l)] +This command lists all operations which have not yet completed. Along with a +description of the operation, the state and eval server is displayed. The +following states are used: +@begin[description] +@f[Unsent]@\The operation is in local queue in the editor, and hasn't been sent +yet. + +@f[Pending]@\The operation has been sent, but has not yet started execution. + +@f[Running]@\The operation is currently being processed. + +@f[Aborted]@\The operation has been aborted, but the eval server has not yet +indicated termination. +@end[description] +@enddefcom + + +@section[Typescripts] +@label[typescripts] +@index[typescripts] + +Both slave buffers and background buffers are typescripts. The typescript +protocol allows other processes to do stream-oriented interaction in a @hemlock +buffer similar to that of a terminal. When there is a typescript in a buffer, +the @hid[Typescript] minor mode is present. Some of the commands described in +this section are also used by @hid[Eval] mode (page @pageref[eval-mode].) + +Typescripts are simple to use. @hemlock inserts output from the process into +the buffer. To give the process input, use normal editing to insert the input +at the end of the buffer, and then type @bf[Return] to confirm sending the +input to the process. + +@defcom[com "Confirm Typescript Input", + stuff (bound to @bf[Return] in @hid[Typescript] mode)] +@defhvar1[var "Unwedge Interactive Input Confirm", val {t}] +This command sends text that has been inserted at the end of the current buffer +to the process reading on the buffer's typescript. Before sending the text, +@hemlock moves the point to the end of the buffer and inserts a newline. + +Input may be edited as much as is desired before it is confirmed; the result +of editing input after it has been confirmed is unpredictable. For this reason, +it is desirable to postpone confirming of input until it is actually complete. +The @hid[Indent New Line] command is often useful for inserting newlines +without confirming the input. + +If the process reading on the buffer's typescript is not waiting for input, +then the text is queued instead of being sent immediately. Any number of +inputs may be typed ahead in this fashion. @hemlock makes sure that the inputs +and outputs get interleaved correctly so that when all input has been read, the +buffer looks the same as it would have if the input had not been typed ahead. + +If the buffer's point is before the start of the input area, then various +actions can occur. When set, @hid[Unwedge Interactive Input Confirm] causes +@hemlock to ask the user if it should fix the input buffer which typically +results in ignoring any current input and refreshing the input area at the end +of the buffer. This also has the effect of throwing the slave Lisp to top +level, which aborts any pending operations or queued input. This is the only +way to be sure the user is cleanly set up again after messing up the input +region. When this is @nil, @hemlock simply beeps and tells the user in the +@hid[Echo Area] that the input area is invalid. +@enddefcom + +@defcom[com "Kill Interactive Input", + stuff (bound to @bf[M-i] in @hid[Typescript] and @hid[Eval] modes)] +This command kills any input that would have been confirmed by @bf[Return]. +@enddefcom + +@defcom[com "Next Interactive Input", + stuff (bound to @bf[M-n] in @hid[Typescript] and @hid[Eval] modes)] +@defcom1[com "Previous Interactive Input", + stuff (bound to @bf[M-p] in @hid[Typescript] and @hid[Eval] modes)] +@defcom1[com "Search Previous Interactive Input", + stuff (bound to @bf[M-P] in @hid[Typescript] and @hid[Eval] modes)] +@defhvar1[var "Interactive History Length", val {10}] +@defhvar1[var "Minimum Interactive Input Length", val {2}] +@index[history, typescript] +@Hemlock maintains a history of interactive inputs. @hid[Next Interactive +Input] and @hid[Previous Interactive Input] step forward and backward in the +history, inserting the current entry in the buffer. The prefix argument is +used as a repeat count. + +@hid[Search Previous Interactive Input] searches backward through the +interactive history using the current input as a search string. Consecutive +invocations repeat the previous search. + +@hid[Interactive History Length] determines the number of entries with which +@hemlock creates the buffer-specific histories. @Hemlock only adds an input +region to the history if its number of characters exceeds @hid[Minimum +Interactive Input Length]. +@enddefcom + +@defcom[com "Reenter Interactive Input", + stuff (bound to @bf[C-Return] in @hid[Typescript] and @hid[Eval] modes)] + This copies to the end of the buffer the form to the left of the buffer's +point. When the current region is active, this copies it instead. This is +sometimes easier to use to get a previous input that is either so far back that +it has fallen off the history or is visible and more readily @i[yanked] than +gotten with successive invocations of the history commands. +@enddefcom + +@defcom[com "Interactive Beginning of Line", + stuff (bound to @bf[C-a] in @hid[Typescript] and @hid[Eval] modes)] +This command is identical to @hid[Beginning of Line] unless there is no +prefix argument and the point is on the same line as the start of the current +input; then it moves to the beginning of the input. This is useful since it +skips over any prompt which may be present. +@enddefcom + +@defhvar[var "Input Wait Alarm", val {:loud-message}] +@defhvar1[var "Slave GC Alarm", val {:message}] +@hid[Input Wait Alarm] determines what action to take when a slave Lisp goes +into an input wait on a typescript that isn't currently displayed in any +window. @hid[Slave GC Alarm] determines what action to take when a slave +notifies that it is GC'ing. + +The following are legal values: +@begin[description] +@kwd[loud-message]@\Beep and display a message in the echo area indicating +which buffer is waiting for input. + +@kwd[message]@\Display a message, but don't beep. + +@nil@\Don't do anything. +@end[description] +@enddefhvar + +@defcom[com "Typescript Slave BREAK", bind (Typescript: H-b)] +@defcom1[com "Typescript Slave to Top Level", bind (Typescript: H-g)] +@defcom1[com "Typescript Slave Status", bind (Typescript: H-s)] +Some typescripts have associated information which these commands access +allowing @hemlock to control the process which uses the typescript. + +@hid[Typescript Slave BREAK] puts the current process in a break loop so that +you can be debug it. This is similar in effect to an interrupt signal (@f[^C] +or @f[^\] in the editor process). + +@hid[Typescript Slave to Top Level] causes the current process to throw to the +top-level @f[read-eval-print] loop. This is similar in effect to a quit signal +(@f[^\]). + +@hid[Typescript Slave Status] causes the current process to print status +information on @var[error-output]: +@lisp +; Used 0:06:03, 3851 faults. In: SYSTEM:SERVE-EVENT +@endlisp +The message displays the process run-time, the total number of page faults and +the name of the currently running function. This command is useful for +determining whether the slave is in an infinite loop, waiting for input, or +whatever. +@enddefcom + + +@section[The Current Package] +@label[lisp-package] +@index[package] +The current package is the package which Lisp interaction commands use. The +current package is specified on a per-buffer basis, and defaults to "@f[USER]". +If the current package does not exist in the eval server, then it is created. +If evaluation is being done in the editor process and the current package +doesn't exist, then the value of @f[*package*] is used. The current package is +displayed in the modeline (see section @ref[modelines].) Normally the package +for each file is specified using the @f[Package] file option (see page +@pageref[file-options].) + +When in a slave buffer, the current package is controlled by the value of +@var[package] in that Lisp process. Modeline display of the current package +is inhibited in this case. + +@defcom[com "Set Buffer Package"] +This command prompts for the name of a package to make the local package in the +current buffer. If the current buffer is a slave, background, or eval buffer, +then this sets the current package in the associated eval server or editor +Lisp. When in an interactive buffer, do not use @f[in-package]; use this +command instead. +@enddefcom + + +@section[Compiling and Evaluating Lisp Code] + +@index[compilation]@index[evaluation]These commands can greatly speed up +the edit/debug cycle since they enable incremental reevaluation or +recompilation of changed code, avoiding the need to compile and load an +entire file. + +@defcom[com "Evaluate Expression", bind (M-Escape)] +This command prompts for an expression and prints the result of its evaluation +in the echo area. If an error happens during evaluation, the evaluation is +simply aborted, instead of going into the debugger. This command doesn't +return until the evaluation is complete. +@enddefcom + +@defcom[com "Evaluate Defun", bind (C-x C-e)] +@defcom1[com "Evaluate Region"] +@defcom1[com "Evaluate Buffer"] +These commands evaluate text out of the current buffer, reading the current +defun, the region and the entire buffer, respectively. The result of the +evaluation of each form is displayed in the echo area. If the region is +active, then @hid[Evaluate Defun] evaluates the current region, just like +@hid[Evaluate Region]. +@enddefcom + +@defcom[com "Macroexpand Expression", bind (C-M)] +This command shows the macroexpansion of the next expression in the null +environment in a pop-up window. With an argument, it uses @f[macroexpand] +instead of @f[macroexpand-1]. +@enddefcom + +@defcom[com "Re-evaluate Defvar"] +This command is similar to @hid[Evaluate Defun]. It is used for force the +re-evaluation of a @f[defvar] init form. If the current top-level form is a +@f[defvar], then it does a @f[makunbound] on the variable, and evaluates the +form. +@enddefcom + +@defcom[com "Compile Defun", bind (C-x C-c)] +@defcom1[com "Compile Region"] +These commands compile the text in the current defun and the region, +respectively. If the region is active, then @hid[Compile Defun] compiles the +current region, just like @hid[Compile Region]. +@enddefcom + +@defcom[com "Load File"] +@defhvar1[var "Load Pathname Defaults", val {nil}] +This command prompts for a file and loads it into the current eval server using +@f[load]. @hid[Load Pathname Defaults] contains the default pathname for this +command. This variable is set to the file loaded; if it is @nil, then there is +no default. This command also uses the @hid[Remote Compile File] variable. +@enddefcom + + +@section[Compiling Files] +These commands are used to compile source ("@f[.lisp]") files, producing binary +("@f[.fasl]") output files. Note that unlike the other compiling and evalating +commands, this does not have the effect of placing the definitions in the +environment; to do so, the binary file must be loaded. + +@defcom[com "Compile Buffer File", bind (C-x c)] +@defhvar1[var "Compile Buffer File Confirm", val {t}] +This command asks for confirmation, then saves the current buffer (when +modified) and compiles the associated file. The confirmation prompt indicates +intent to save and compile or just compile. If the buffer wasn't modified, and +a comparison of the write dates for the source and corresponding binary +("@f[.fasl]") file suggests that recompilation is unnecessary, the confirmation +also indicates this. A prefix argument overrides this test and forces +recompilation. Since there is a complete log of output in the background +buffer, the creation of the normal error output ("@f[.err]") file is inhibited. + +Setting @hid[Compile Buffer File Confirm] to @nil inhibits confirmation, except +when the binary is up to date and a prefix argument is not supplied. +@enddefcom + +@defcom[com "Compile File"] +This command prompts for a file and compiles that file, providing a convenient +way to compile a file that isn't in any buffer. Unlike +@hid[Compile Buffer File], this command doesn't do any consistency checks such +as checking whether the source is in a modified buffer or the binary is up to +date. +@enddefcom + +@defcom[com "Compile Group"] +@defcom1[com "List Compile Group"] +@label[compile-group-command]@index[group, compilation]@hid[Compile Group] does +a @hid[Save All Files] and then compiles every "@f[.lisp]" file for which the +corresponding "@f[.fasl]" file is older or nonexistent. The files are compiled +in the order in which they appear in the group definition. A prefix argument +forces compilation of all "@f[.lisp]" files. + +@hid[List Compile Group] lists any files that would be compiled by +@hid[Compile Group]. All Modified files are saved before checking to generate +a consistent list. +@enddefcom + +@defcom[com "Set Compile Server"] +@defcom1[com "Set Buffer Compile Server"] +@defcom1[com "Current Compile Server"] +These commands are analogous to @hid[Set Eval Server], @comref[Set Buffer Eval +Server] and @hid[Current Eval Server], but they determine the eval server used +for file compilation requests. If the user specifies a compile server, then +the file compilation commands send compilation requests to that server instead +of the current eval server. + +Having a separate compile server makes it easy to do compilations in the +background while continuing to interact with your eval server and editor. The +compile server can also run on a remote machine relieving your active +development machine of the compilation effort. +@enddefcom + +@defcom[com "Next Compiler Error", bind (H-n)] +@defcom1[com "Previous Compiler Error", bind (H-p)] +These commands provides a convenient way to inspect compiler errors. First it +splits the current window if there is only one window present. @hemlock +positions the current point in the first window at the erroneous source code +for the next (or previous) error. Then in the second window, it displays the +error beginning at the top of the window. Given an argument, this command +skips that many errors. +@enddefcom + +@defcom[com "Flush Compiler Error Information"] +This command relieves the current eval server of all infomation about errors +encountered while compiling. This is convenient if you have been compiling a +lot, but you were ignoring errors and warnings. You don't want to step through +all the old errors, so you can use this command immediately before compiling a +file whose errors you intend to edit. +@enddefcom + + +@defhvar[var "Remote Compile File", val {nil}] +When true, this variable causes file compilations to be done using the RFS +remote file system mechanism by prepending "@f[/../]@i[host]" to the file being +compiled. This allows the compile server to be run on a different machine, but +requires that the source be world readable. If false, commands use source +filenames directly. Do NOT use this to compile files in AFS. +@enddefhvar + + +@section[Querying the Environment] +@index[documentation, lisp] +These commands are useful for obtaining various random information from the +Lisp environment. + +@defcom[com "Describe Function Call", bind (C-M-A)] +@defcom1[com "Describe Symbol", bind (C-M-S)] +@hid[Describe Function Call] uses the current eval server to describe the +symbol found at the head of the currently enclosing list, displaying the output +in a pop-up window. @hid[Describe Symbol] is the same except that it describes +the symbol at or before the point. These commands are primarily useful for +finding the documentation for functions and variables. If there is no +currently valid eval server, then this command uses the editor Lisp's +environment instead of trying to spawn a slave. +@enddefcom + + +@section[Editing Definitions] +The Lisp compiler annotates each compiled function object with the source +file that the function was originally defined from. The definition editing +commands use this information to locate and edit the source for functions +defined in the environment. + +@defcom[com "Edit Definition"] +@defcom1[com "Goto Definition", bind (C-M-F)] +@defcom1[com "Edit Command Definition"] +@hid[Edit Definition] prompts for the name of a function, and then uses the +current eval server to find out in which file the function is defined. If +something other than @f[defun] or @f[defmacro] defined the function, then this +simply reads in the file, without trying to find its definition point within +the file. If the function is uncompiled, then this looks for it in the current +buffer. If there is no currently valid eval server, then this command uses the +editor Lisp's environment instead of trying to spawn a slave. + +@hid[Goto Definition] edits the definition of the symbol at the beginning of +the current list. + +@hid[Edit Command Definition] edits the definition of a @hemlock command. By +default, this command does a keyword prompt for the command name (as in an +extended command). If a prefix argument is specified, then instead prompt for +a key and edit the definition of the command bound to that key. +@enddefcom + +@defcom[com "Add Definition Directory Translation"] +@defcom1[com "Delete Definition Directory Translation"] +The defining file is recorded as an absolute pathname. The definition editing +commands have a directory translation mechanism that allow the sources to be +found when they are not in the location where compilation was originally done. +@hid[Add Definition Directory Translation] prompts for two directory +namestrings and causes the first to be mapped to the second. Longer (more +specific) directory specifications are matched before shorter (more general) +ones. + +@hid[Delete Definition Directory Translation] prompts for a directory +namestring and deletes it from the directory translation table. +@enddefcom + +@defhvar[var "Editor Definition Info", val {nil}] +When this variable is true, the editor Lisp is used to determine definition +editing information, otherwise the current eval server is used. This variable +is true in @hid[Eval] and @hid[Editor] modes. +@enddefhvar + + +@section[Debugging] +These commands manipulate the slave when it is in the debugger and provide +source editing based on the debugger's current frame. These all affect the +@hid[Current Eval Server]. + + +@subsection[Changing Frames] + +@defcom[com "Debug Down", bind (C-M-H-d)] +This command moves down one debugger frame. +@enddefcom + +@defcom[com "Debug Up", bind (C-M-H-u)] +This command moves up one debugger frame. +@enddefcom + +@defcom[com "Debug Top", bind (C-M-H-t)] +This command moves to the top of the debugging stack. +@enddefcom + +@defcom[com "Debug Bottom", bind (C-M-H-b)] +This command moves to the bottom of the debugging stack. +@enddefcom + +@defcom[com "Debug Frame", bind (C-M-H-f)] +This command moves to the absolute debugger frame number indicated by the +prefix argument. +@enddefcom + + +@subsection[Getting out of the Debugger] + +@defcom[com "Debug Quit", bind (C-M-H-q)] +This command throws to top level out of the debugger in the @hid[Current Eval +Server]. +@enddefcom + +@defcom[com "Debug Go", bind (C-M-H-g)] +This command tries the @f[continue] restart in the @hid[Current Eval Server]. +@enddefcom + +@defcom[com "Debug Abort", bind (C-M-H-a)] +This command executes the ABORT restart in the @hid[Current Eval Server]. +@enddefcom + +@defcom[com "Debug Restart", bind (C-M-H-r)] +This command executes the restart indicated by the prefix argument in the +@hid[Current Eval Server]. The debugger enumerates the restart cases upon +entering it. +@enddefcom + + +@subsection[Getting Information] + +@defcom[com "Debug Help", bind (C-M-H-h)] +This command in prints the debugger's help text. +@enddefcom + +@defcom[com "Debug Error", bind (C-M-H-e)] +This command prints the error condition and restart cases displayed upon +entering the debugger. +@enddefcom + +@defcom[com "Debug Backtrace", bind (C-M-H-B)] +This command executes the debugger's @f[backtrace] command. +@enddefcom + +@defcom[com "Debug Print", bind (C-M-H-p)] +This command prints the debugger's current frame in the same fashion as the +frame motion commands. +@enddefcom + +@defcom[com "Debug Verbose Print", bind (C-M-H-P)] +This command prints the debugger's current frame without elipsis. +@enddefcom + +@defcom[com "Debug Source", bind (C-M-H-s)] +This command prints the source form for the debugger's current frame. +@enddefcom + +@defcom[com "Debug Verbose Source"] +This command prints the source form for the debugger's current frame with +surrounding forms for context. +@enddefcom + +@defcom[com "Debug List Locals", bind (C-M-H-l)] +This prints the local variables for the debugger's current frame. +@enddefcom + + +@subsection[Editing Sources] + +@defcom[com "Debug Edit Source", bind (C-M-H-S)] +This command attempts to place you at the source location of the debugger's +current frame. Not all debugger frames represent function's that were compiled +with the appropriate debug-info policy. This beeps with a message if it is +unsuccessful. +@enddefcom + + +@subsection[Miscellaneous] + +@defcom[com "Debug Flush Errors", bind (C-M-H-F)] +This command toggles whether the debugger ignores errors or recursively enters +itself. +@enddefcom + + + + +@section[Manipulating the Editor Process] +When developing @hemlock customizations, it is useful to be able to manipulate +the editor Lisp environment from @hemlock. + +@defcom[com "Editor Describe", bind (Home t, C-_ t)] +This command prompts for an expression, and then evaluates and describes it +in the editor process. +@enddefcom + +@defcom[com "Room"] +Call the @f[room] function in the editor process, displaying information +about allocated storage in a pop-up window. +@enddefcom + +@defcom[com "Editor Load File"] +This command is analogous to @comref[Load File], but loads the file into the +editor process. +@enddefcom + + +@subsection[Editor Mode] +When @hid[Editor] mode is on, alternate versions of the Lisp interaction +commands are bound in place of the eval server based commands. These commands +manipulate the editor process instead of the current eval server. Turning on +editor mode in a buffer allows incremental development of code within the +running editor. + +@defcom[com "Editor Mode"] +This command turns on @hid[Editor] minor mode in the current buffer. If it is +already on, it is turned off. @hid[Editor] mode may also be turned on using +the @f[Mode] file option (see page @pageref[file-options].) +@enddefcom + +@defcom[com "Editor Compile Defun", + stuff (bound to @bf[C-x C-c] in @hid[Editor] mode)] +@defcom1[com "Editor Compile Region"] +@defcom1[com "Editor Evaluate Buffer"] +@defcom1[com "Editor Evaluate Defun", + stuff (bound to @bf[C-x C-e] in @hid[Editor] mode)] +@defcom1[com "Editor Evaluate Region"] +@defcom1[com "Editor Macroexpand Expression", bind (Editor: C-M)] +@defcom1[com "Editor Re-evaluate Defvar"] +@defcom1[com "Editor Describe Function Call", + stuff (bound to @bf[C-M-A] in @hid[Editor] mode)] +@defcom1[com "Editor Describe Symbol", + stuff (bound to @bf[C-M-S] in @hid[Editor] mode)] +These commands are similar to the standard commands, but modify or examine the +Lisp process that @hemlock is running in. Terminal I/O is done on the +initial window for the editor's Lisp process. Output is directed to a pop-up +window or the editor's window instead of to the background buffer. +@enddefcom + +@defcom[com "Editor Compile Buffer File"] +@defcom1[com "Editor Compile File"] +@defcom1[com "Editor Compile Group"] +In addition to compiling in the editor process, these commands differ from the +eval server versions in that they direct output to the the +@hid[Compiler Warnings] buffer. +@enddefcom + +@defcom[com "Editor Evaluate Expression", + stuff (bound to @bf[M-Escape] in @hid[Editor] mode and @bf[C-M-Escape])] +This command prompts for an expression and evaluates it in the editor process. +The results of the evaluation are displayed in the echo area. +@enddefcom + + +@subsection[Eval Mode] +@label[eval-mode] +@index[modes, eval]@hid[Eval] mode is a minor mode that simulates a @f[read] +@f[eval] @f[print] loop running within the editor process. Since Lisp +program development is usually done in a separate eval server process (see page +@pageref[eval-servers]), @hid[Eval] mode is used primarily for debugging code +that must run in the editor process. @hid[Eval] mode shares some commands with +@hid[Typescript] mode: see section @ref[typescripts]. + +@hid[Eval] mode doesn't completely support terminal I/O: it binds +@var[standard-output] to a stream that inserts into the buffer and +@var[standard-input] to a stream that signals an error for all operations. +@hemlock cannot correctly support the interactive evaluation of forms that read +from the @hid[Eval] interactive buffer. + +@defcom[com "Select Eval Buffer"] +This command changes to the @hid[Eval] buffer, creating one if it doesn't +already exist. The @hid[Eval] buffer is created with @hid[Lisp] as the major +mode and @hid[Eval] and @hid[Editor] as minor modes. +@enddefcom + +@defcom[com "Confirm Eval Input", + stuff (bound to @bf[Return] in @hid[Eval] mode)] +This command evaluates all the forms between the end of the last output and +the end of the buffer, inserting the results of their evaluation in the buffer. +This beeps if the form is incomplete. Use @binding[Linefeed] to insert line +breaks in the middle of a form. + +This command uses @hid[Unwedge Interactive Input Confirm] in the same way +@hid[Confirm Interactive Input] does. +@enddefcom + +@defcom[com "Abort Eval Input", + stuff (bound to @bf[M-i] in @hid[Eval] mode)] +This command moves the the end of the buffer and prompts, ignoring any +input already typed in. +@enddefcom + + +@subsection[Error Handling] +@index[error handling] +When an error happens inside of @hemlock, @hemlock will trap the error and +display the error message in the echo area, possibly along with the +"@f[Internal error:]" prefix. If you want to debug the error, type @bf[?]. +This causes the prompt "@f[Debug:]" to appear in the echo area. The following +commands are recognized: +@begin[description] +@bf[d]@\Enter a break-loop so that you can use the Lisp debugger. +Proceeding with "@f[go]" will reenter @hemlock and give the "@f[Debug:]" +prompt again. + +@bf[e]@\Display the original error message in a pop-up window. + +@bf[b]@\Show a stack backtrace in a pop-up window. + +@bf[q, Escape]@\Quit from this error to the nearest command loop. + +@bf[r]@\Display a list of the restart cases and prompt for the number of a +@f[restart-case] with which to continue. Restarting may result in prompting in +the window in which Lisp started. +@end[description] + +Only errors within the editor process are handled in this way. Errors during +eval server operations are handled using normal terminal I/O on a typescript in +the eval server's slave buffer or background buffer (see page +@pageref[operations]). Errors due to interaction in a slave buffer will cause +the debugger to be entered in the slave buffer. + + +@section[Command Line Switches] +@label[slave-switch] +Two command line switches control the initialization of editor and eval servers +for a Lisp process: +@begin[description] +@f<-edit>@\ +@label[edit-switch] +This switch starts up @hemlock. If there is a non-switch command line word +immediately following the program name, then the system interprets it as a file +to edit. For example, given +@Begin[ProgramExample] +lisp file.txt -edit +@End[ProgramExample] +Lisp will go immediately into @hemlock finding the file @f[file.txt]. + +@f<-slave [>@i[name]@f<]>@\ + This switch causes the Lisp process to become a slave of the editor process +@i[name]. An editor Lisp determines @i[name] when it allows connections from +slaves. Once the editor chooses a name, it keeps the same name until the +editor's Lisp process terminates. Since the editor can automatically create +slaves on its own machine, this switch is useful primarily for creating slaves +that run on a different machine. @f[hqb]'s machine is @f[ME.CS.CMU.EDU], and +he wants want to run a slave on @f[SLAVE.CS.CMU.EDU], then he should use the +@hid[Accept Slave Connections] command, telnet to the machine, and invoke Lisp +supplying @f[-slave] and the editor's name. The command displays the editor's +name. +@end[description] diff --git a/docs/hem/user/mail.mss b/docs/hem/user/mail.mss new file mode 100644 index 0000000..6d1c7a7 --- /dev/null +++ b/docs/hem/user/mail.mss @@ -0,0 +1,1343 @@ +@comment{-*- Dictionary: /afs/cs/project/clisp/scribe/hem/hem; Mode: spell; Package: Hemlock -*-} +@chap[The Mail Interface] +@section[Introduction to Mail in Hemlock] + +@index[MH interface]@label[introduction] +@hemlock provides an electronic mail handling facility via an interface to the +public domain @i[Rand MH Message Handling System]. This chapter assumes that +the user is familiar with the basic features and operation of @mh, but it +attempts to make allowances for beginners. Later sections of this chapter +discuss setting up @mh, profile components and special files for formatting +outgoing mail headers, and backing up protected mail directories on a +workstation. For more information on @mh, see the @i[Rand MH Message Handling +System Tutorial] and the @i[Rand MH Message Handling System Manual]. + +The @hemlock interface to @mh provides a means for generating header (@f[scan]) +lines for messages and displaying these headers in a @hid[Headers] buffer. +This allows the user to operate on the @i[current message] as indicated by the +position of the cursor in the @hid[Headers] buffer. The user can read, reply +to, forward, refile, or perform various other operations on the current +message. A user typically generates a @hid[Headers] buffer with the commands +@hid[Message Headers] or @hid[Incorporate and Read New Mail], and multiple such +buffers may exist simultaneously. + +Reading a message places its text in a @hid[Message] buffer. In a manner +similar to a @hid[Headers] buffer, this allows the user to operate on that +message. Most @hid[Headers] buffer commands behave the same in a @hid[Message] +buffer. For example, the @hid[Reply to Message] command has the same effect in +both @hid[Headers] mode and @hid[Message] mode. It creates a @hid[Draft] +buffer and makes it the current buffer so that the user may type a reply to the +current message. + +The @hid[Send Message] command originates outgoing mail. It generates a +@hid[Draft] buffer in which the user composes a mail message. Each @hid[Draft] +buffer has an associated pathname, so the user can save the buffer to a file as +necessary. Invoking @hid[Send Message] in a @hid[Headers] or @hid[Message] +buffer associates the @hid[Draft] buffer with a @hid[Message] buffer. This +allows the user to easily refer to the message being replied to with the +command @hid[Goto Message Buffer]. After the user composes a draft message, he +can deliver the message by invoking the @hid[Deliver Message] command in the +@hid[Draft] buffer (which deletes both the this buffer and any associated +@hid[Message] buffer), or he can delay this action. Invoking @hid[Deliver +Message] when not in a @hid[Draft] buffer causes it to prompt for a draft +message ID, allowing previously composed and saved messages to be delivered +(even across distinct Lisp invocations). + +@index[virtual message deletion] +The @hemlock mail system provides a mechanism for @i[virtual message deletion]. +That is, the @hid[Delete Message] command does not immediately delete a message +but merely flags the message for future deletion. This allows the user to +undelete the messages with the @hid[Undelete Message] command. The +@hid[Expunge Messages] command actually removes messages flagged for deletion. +After expunging a deleted message, @hid[Undelete Messages] can no longer +retrieve it. Commands that read messages by sequencing through a @hid[Headers] +buffer typically ignore those marked for deletion, which makes for more fluid +reading if a first pass has been made to delete uninteresting messages. + +After handling messages in a @hid[Headers] buffer, there may be messages +flagged for deletion and possibly multiple @hid[Message] buffers lying around. +There is a variety of commands that help @i[terminate] a mail session. +@hid[Expunge Messages] will flush the messages to be deleted, leaving the +buffer in an updated state. @hid[Delete Headers Buffer and Message Buffers] +will delete the @hid[Headers] buffer and its corresponding @hid[Message] +buffers. @hid[Quit Headers] is a combination of these two commands in that it +first expunges messages and then deletes all the appropriate buffers. + +One does not have to operate only on messages represented in a @hid[Headers] +buffer. This is merely the nominal mode of interaction. There are commands +that prompt for a folder, an @mh message specification (for example, "@f[1 3 6 +last]", "@f[1-3 5 6]", "@f[all]", "@f[unseen]"), and possibly a @f[pick] +expression. @f[Pick] expressions allow messages to be selected based on header +field pattern matching, body text searching, and date comparisons; these can be +specified using either a Unix shell-like/switch notation or a Lisp syntax, +according to one's preference. See section @ref[scanning] for more details. + +A @i[mail-drop] is a file where a Unix-based mail system stores all messages a +user receives. The user's mail handling program then fetches these from the +mail-drop, allowing the user to operate on them. Traditionally one locates his +mail-drop and mail directory on a mainframe machine because the information on +mainframes is backed up on magnetic tape at least once per day. Since @hemlock +only runs under CMU @clisp on workstations, and one's mail directory is not +usually world writable, it is not possible to adhere to a standard arrangement. +Since @mh provides for a remote mail-drop, and CMU's Remote File System has a +feature allowing authentication across a local area network, one can use +@hemlock to fetch his mail from a mainframe mail-drop (where it is backed up +before @hemlock grabs it) and store it on his workstation. Reading mail on a +workstation is often much faster and more comfortable because typically it is a +single user machine. Section @ref[backing-up] describes how to back up one's +mail directory from a workstation to a mainframe. + + +@section[Constraints on MH to use Hemlock's Interface] + +@index[constraints for mail interface]@label[constraints] +There are a couple constaints placed on the user of the @hemlock interface to +@mh. The first is that there must be a draft folder specified in one's @mh +profile to use any command that sends mail. Also, to read new mail, there must +be an @f[Unseen-Sequence:] component in one's @mh profile. The default @mh +profile does not specify these components, so they must be added by the user. +The next section of this chapter describes how to add these components. +Another constraint is that @hemlock requires its own @f[scan] line format to +display headers lines in a @hid[Headers] buffer. See the description of the +variable @hid[MH Scan Line Form] for details. + + +@section[Setting up MH] + +@index[setting up the mail interface]@label[setting-up] +@index[mail profile]@index[MH profile] +Get an @mh default profile and mail directory by executing the @mh @f[folder] +utility in a Unix shell. When it asks if it should make the "@f[inbox]" +folder, answer "@b[yes]". This creates a file called "@f[.mh_profile]" in the +user's home directory and a directory named "@f[Mail]". + +Edit the "@f[.mh_profile]" file inserting two additional lines. To send mail +in @hemlock, the user must indicate a draft folder by adding a +@f[Draft-Folder:] line with a draft folder name @dash "@f[drafts]" is a common +name: +@begin[example] +Draft-Folder: drafts +@end[example] + +Since the mail-drop exists on a remote machine, the following line must +also be added: +@begin[example] +MailDrop: /..//usr/spool/mail/ +@end[example] + +Since the user's mail-drop is on a separate machine from his mail directory +(and where the user runs @hemlock), it is necessary to issue the following +command from the Unix shell (on the workstation). This only needs to be done +once. +@begin[programexample] +/usr/cs/etc/rfslink -host /usr/spool/mail/ +@end[programexample] +Note that @b[] is not a full ARPANET domain-style name. Use an +abbreviated CMU host name (for example, "@b[spice]" not +"@b[spice.cs.cmu.edu]"). + + +@section[Profile Components and Customized Files] + +@subsection[Profile Components] + +@label[Profile] +The following are short descriptions about profile components that are either +necessary to using @hemlock's interface to @mh or convenient for using @mh in +general: + +@begin[description] +@f[Path:]@\ +This specifies the user's mail directory. It can be either a full pathname or +a pathname relative to the user's home directory. This component is +@i[necessary] for using @mh. + +@f[MailDrop:]@\ +This is used to specify one's remote mail-drop. It is @i[necessary] for +@hemlock only when using a mail-drop other than "@f[/usr/spool/mail/]" on +the local machine. + +@f[Folder-Protect:], @f[Msg-Protect:]@\ +These are set to 700 and 600 respectively to keep others from reading one's +mail. At one time the default values were set for public visibility of mail +folders. Though this is no longer true, these can be set for certainty. The +700 protection allows only user read, write, and execute (list access for +directories), and 600 allows only user read and write. These are not necessary +for either @mh or the @hemlock interface. + +@f[Unseen-Sequence:]@\ +When mail is incorporated, new messages are added to this sequence, and as +these messages are read they are removed from it. This allows the user at any +time to invoke an @mh program on all the unseen messges of a folder easily. An +example definition is: +@begin[example] +Unseen-Sequence: unseen +@end[example] +Specifying an unseen-sequence is @i[necessary] to use @hemlock's +interface to @mh. + +@f[Alternate-Mailboxes:]@\ +This is not necessary for either @mh or the @hemlock interface. This +component tells @mh which addresses that it should recognize as the user. This +is used for @f[scan] output formatting when the mail was sent by the user. It +is also used by @f[repl] when it sets up headers to know who the user is for +inclusion or exclusion from @b[cc]: lists. This is case sensitive and takes +wildcards. One example is: +@begin[example] +Alternate-Mailboxes: *FRED*, *Fred*, *fred* +@end[example] + +@f[Draft-Folder:]@\ +This makes multiple draft creation possible and trivial to use. Just supply a +folder name (for example, "@f[drafts]"). Specifying a draft-folder is +@i[necessary] to use @hemlock's interface to @mh. + +@f[repl: -cc all -nocc me -fcc out-copy]@\ +This tells the @f[repl] utility to include everyone but the user in the +@b[cc:] list when replying to mail. It also makes @f[repl] keep an copy of the +message the user sends. This is mentioned because one probably wants to reply +to everyone receiving a piece of mail except oneself. Unlike other utilities +that send mail, @f[repl] stores personal copies of outgoing mail based on a +command line switch. Other @mh utilities use different mechanisms. This line +is not necessary to use either @mh or the @hemlock interface. + +@f[rmmproc: /usr/cs/bin/rm]@\ +This is not necessary to use @hemlock's interface to @mh, but due to +@hemlock's virtual message deletion feature, this causes messages to be deleted +from folder directories in a cleaner fashion when they actually get removed. +Note that setting this makes @f[rmm] more treacherous if used in the Unix +shell. +@end[description] +@; + + +@subsection[Components Files] +@index[components] +@label[components-files] +@i[Components] files are templates for outgoing mail header fields that specify +position and sometimes values for specified fields. Example files are shown +for each one discussed here. These should exist in the user's mail directory. + +For originating mail there is a components file named "@f[components]", and it +is used by the @mh utility @f[comp]. An example follows: +@begin[example] + To: + cc: + fcc: out-copy + Subject: + -------- +@end[example] +This example file differs from the default by including the @f[fcc:] line. +This causes @mh to keep a copy of the outgoing draft message. Also, though it +isn't visible here, the @f[To:], @f[cc:], and @f[Subject:] lines have a space +at the end. + +@index[forwarding components] +The "@f[forwcomps]" components file is a template for the header fields of any +forwarded message. Though it may be different, our example is the same as the +previous one. These are distinct files for @mh's purposes, and it is more +flexible since the user might not want to keep copies of forwarded messages. + +@index[reply components] +The "@f[replcomps]" components file is a template for the header fields of any +draft message composed when replying to a message. An example +follows: +@begin[example] + %(lit)%(formataddr %<{reply-to}%|%<{from}%|%{sender}%>%>)\ + %<(nonnull)%(void(width))%(putaddr To: )\n%>\ + %(lit)%(formataddr{to})%(formataddr{cc})%(formataddr(me))\ + %(formataddr{resent-to})\ + %<(nonnull)%(void(width))%(putaddr cc: )\n%>\ + %<{fcc}Fcc: %{fcc}\n%>\ + %<{subject}Subject: Re: %{subject}\n%>\ + %<{date}In-reply-to: Your message of \ + %<(nodate{date})%{date}%|%(tws{date})%>.%<{message-id} + %{message-id}%>\n%>\ + -------- +@end[example] +This example file differs from the default by including the @b[resent-to:] +field (in addition to the @b[to:] and @b[cc:] fields) of the message being +replied to in the @b[cc:] field of the draft. This is necessary for replying +to all recipients of a distributed message. Keeping a copy of the outgoing +draft message works a little differently with reply components. @mh expects a +switch which the user can put in his profile (see section @ref[Profile] of this +chapter), and using the @mh formatting language, this file tests for the +@f[fcc] value as does the standard file. + + +@section[Backing up the Mail Directory] +@index[backing up mail directories] +@label[backing-up] +The easiest method of backing up a protected mail directory is to copy it into +an Andrew File System (AFS) directory since these are backed up daily as with +mainframes. The only problem with this is that the file servers may be down +when one wants to copy his mail directory since, at the time of this writing, +these servers are still under active development; however, they are becoming +more robust daily. One can read about the current AFS status in the file +@f[/../fac/usr/gripe/doc/vice/status]. + +Using AFS, one could keep his actual mail directory (not a copy thereof) in his +AFS home directory which eliminates the issue of backing it up. This is +additionally beneficial if the user does not use the same workstation everyday +(that is, he does not have his own but shares project owned machines). Two +problems with this arrangement result from the AFS being a distributed file +system. Besides the chance that the server will be down when the user wants to +read mail, performance degrades since messages must always be referenced across +the local area network. + +Facilities' official mechanism for backing up protected directories is called +@f[sup]. This is awkward to use and hard to set up, but a subsection here +describes a particular arrangement suitable for the user's mail directory. + + +@subsection[Andrew File System] +If the user choses to use AFS, he should get copies of @i[Getting Started with +the Andrew File System] and @i[Protecting AFS files and directories]. To use +AFS, send mail to Gripe requesting an account. When Gripe replies with a +password, change it to be the same as the account's password on the +workstation. This causes the user to be authenticated into AFS when he logs +into his workstation (that is, he is automatically logged into his AFS +account). To change the password, first log into the AFS account: +@begin[programexample] +log +@end[programexample] +Then issue the @f[vpasswd] command. + +All of the example command lines in this section assume the user has +@f[/usr/misc/bin] on his Unix shell @f[PATH] environment variable. + +@paragraph[Copy into AFS:] + +Make an AFS directory to copy into: +@begin[programexample] +mkdir /afs/cs.cmu.edu/user//mail-backup +@end[programexample] + +This will be readable by everyone, so protect it with the following: +@begin[programexample] +fs sa /afs/cs.cmu.edu/user//mail-backup System:AnyUser none +@end[programexample] + +Once the AFS account and directory to backup into have been established, the +user needs a means to recursively copy his mail directory updating only those +file that have changed and deleting those that no longer exist. To do this, +issue the following command: +@begin[programexample] +copy -2 -v -R +@end[programexample] +Do not terminate either of these directory specifications with a @f[/]. The +@f[-v] switch causes @f[copy] to output a line for copy and deletion, so this +may be eliminated if the user desires. + +@paragraph[Mail Directory Lives in AFS:] + +Assuming the AFS account has been established, and the user has followed the +directions in @ref[setting-up], now make an AFS directory to serve as the mail +directory: +@begin[programexample] +mkdir /afs/cs.cmu.edu/user//Mail +@end[programexample] + +This will be readable by everyone, so protect it with the following: +@begin[programexample] +fs sa /afs/cs.cmu.edu/user//Mail System:AnyUser none +@end[programexample] + +Tell @mh where the mail directory is by modifying the profile's +"@f[.mh_profile]" (see section @ref[setting-up]) @f[Path:] component (see +section @ref[Profile]): +@begin[programexample] +Path: /afs/cs.cmu.edu/user//Mail +@end[programexample] + + +@subsection[Sup to a Mainframe] +To use @f[sup] the user must set up a directory named "@f[sup]" on the +workstation in the user's home directory. This contains different directories +for the various trees that will be backed up, so there will be a "@f[Mail]" +directory. This directory will contain two files: "@f[crypt]" and "@f[list]". +The "@f[crypt]" file contains one line, terminated with a new line, that +contains a single word @dash an encryption key. "@f[list]" contains one line, +terminated with a new line, that contains two words @dash "@b[upgrade Mail]". + +On the user's mainframe, a file must be created that will be supplied to the +@f[sup] program. It should contain the following line to backup the mail +directory: + +@begin[example] +Mail delete host= hostbase=/usr/ base=/usr/ \ +crypt=WordInCryptFile login= password=LoginPasswordOnWorkstation +@end[example] +Warning: @i[This file contains the user's password and should be +protected appropriately.] + +The following Unix shell command issued on the mainframe will backup the +mail directory: + +@begin[programexample] + sup +@end[programexample] + +As a specific example, assume user "@f[fred]" has a workstation called +"@f[fred]", and his mainframe is the "@f[gpa]" machine where he has another +user account named "@f[fred]". The password on his workstation is +"@f[purple]". On his workstation, he creates the directory +"@f[/usr/fred/sup/Mail/]" with the two files "@f[crypt]" and "@f[list]". +The file "@f[/usr/fred/sup/Mail/crypt]" contains only the encryption key: +@programexample[steppenwolf] +The file "@f[/usr/fred/sup/Mail/list]" contains the command to upgrade the +"@f[Mail]" directory: +@programexample[upgrade Mail] + +On the "@f[gpa]" machine, the file "@f[/usr/fred/supfile]" contains the +following line: +@begin[programexample] +Mail delete host=fred hostbase=/usr/fred base=/usr/fred \ +crypt=steppenwolf login=fred password=purple +@end[programexample] +This file is protected on "@f[gpa]", so others cannot see @f[fred's] password +on his workstation. + +On the gpa-vax, issuing +@begin[programexample] + sup /usr/fred/supfile +@end[programexample] +to the Unix shell will update the @mh mail directory from @f[fred's] +workstation deleting any files that exist on the gpa that do not exist on the +workstation. + +For a more complete description of the features of @f[sup], see the @i[UNIX +Workstation Owner's Guide] and @i[The SUP Software Upgrade Protocol]. + +@section[Introduction to Commands and Variables] + +@index[mail commands]@index[mail variables]@label[mhcommands] +Unless otherwise specified, any command which prompts for a folder name will +offer the user a default. Usually this is @mh's idea of the current folder, +but sometimes it is the folder name associated with the current buffer if there +is one. When prompting for a message, any valid @mh message expression may be +entered (for example, "@f[1 3 6]", "@f[1-3 5 6]", "@f[unseen]", "@f[all]"). +Unless otherwise specified, a default will be offered (usually the current +message). + +Some commands mention specific @mh utilities, so the user knows how the +@hemlock command affects the state of @mh and what profile components and +special formatting files will be used. @hemlock runs the @mh utility programs +from a directory indicated by the following variable: + +@defhvar[var "MH Utility Pathname", val {"/usr/misc/.mh/bin/"}] +@mh utility names are merged with this pathname to find the executable +files. +@enddefhvar + + +@section[Scanning and Picking Messages] +@label[scanning] +As pointed out in the introduction of this chapter, users typically generate +headers or @f[scan] listings of messages with @hid[Message Headers], using +commands that operate on the messages represented by the headers. @hid[Pick +Headers] (bound to @bf[h] in @hid[Headers] mode) can be used to narrow down (or +further select over) the headers in the buffer. + +A @f[pick] expression may be entered using either a Lisp syntax or a Unix +shell-like/switch notation as described in the @mh documentation. The Lisp +syntax is as follows: + +@begin[example] + ::= {(not ) | (and *) | (or *) + | (cc ) | (date ) + | (from ) | (search ) + | (subject ) | (to ) + | (-- ) + | (before ) | (after ) + | (datefield )} + + ::= { | } + + ::= { | } + + ::= { | | } + + ::= +@end[example] + +Anywhere the user enters a @f[], its symbol name is used as a string. +Since @hemlock @f[read]s the expression without evaluating it, single quotes +("@bf[']") are unnecessary. From the @mh documentation, + +@begin[itemize] + A @f[] is a Unix @f[ed] regular expression. When using a string to + input these, remember that @f[\] is an escape character in Common Lisp. + + A @f[] is a header field name (for example, @b[reply-to] or + @b[resent-to]). + + A @f[] is an @i[822]-style specification, a day of the week, + "@b[today]", "@b[yesterday]", "@b[tomorrow]", or a number indicating @i[n] + days ago. The @i[822] standard is basically: + @begin[example] + dd mmm yy hh:mm:ss zzz + @end[example] + which is a two digit day, three letter month (first letter capitalized), two + digit year, two digit hour (@f[00] through @f[23]), two digit minute, two + digit second (this is optional), and a three letter zone (all capitalized). + For + example: + @begin[example] + 21 Mar 88 16:00 EST + @end[example] + + A @f[] is an alternate @f[Date:] field to use with @f[(before + )] and @f[(after )] such as @f[BB-Posted:] or + @f[Delivery-Date:]. + + Using @f[(before )] and @f[(after )] causes date field parsing, + while @f[(date )] does string pattern matching. +@end[itemize] + +Since a @f[] may be a symbol or string, it should be noted that the +symbol name is probably all uppercase characters, and @mh will match these +only against upper case. @mh will match lowercase characters against lower +and upper case. Some examples are: +@begin[example] + ;;; All messages to Gripe. + (to "gripe") + + ;;; All messages to Gripe or about Hemlock. + (or (to "gripe") (subject "hemlock")) + + ;;; All messages to Gripe with "Hemlock" in the body. + (and (to "gripe") (search "hemlock")) +@end[example] + +Matching of @f[] fields is case sensitive, so this example will +@f[pick] over all messages that have been replied to. +@example[(or (-- "replied" "") (-- "Replied" ""))] + + +@defhvar[var "MH Scan Line Form", val {"library:mh-scan"}] +This is a pathname of a file containing an @mh format expression used for +header lines. + +The header line format must display the message ID as the first non-whitespace +item. If the user uses the virtual message deletion feature which is on by +default, there must be a space three characters to the right of the message ID. +This location is used on header lines to note that a message is flagged for +deletion. The second space after the message ID is used for notating answered +or replied-to messages. +@enddefhvar + +@defcom[com "Message Headers", bind (C-x r)] +This command prompts for a folder, message (defaulting to "@b[all]"), and an +optional @f[pick] expression. Typically this will simply be used to generate +headers for an entire folder or sequence, and the @f[pick] expression will not +be used. A new @hid[Headers] buffer is made, and the output of @f[scan] on the +messages indicated is inserted into the buffer. The current window is used, +the buffer's point is moved to the first header, and the @hid[Headers] buffer +becomes current. The current value of the @hemlock @hid[Fill Column] variable +is supplied to @f[scan] as the @f[-width] switch. The buffer name is set to a +string of the form @w<"@f[Headers ]">, so the +modeline will show what is in the buffer. If no @f[pick] expression was +supplied, none will be shown in the buffer's name. As described in the +introduction to this section, the expression may be entered using either a Lisp +syntax or a Unix shell-like/switch notation. +@enddefcom + +@defhvar[var "MH Lisp Expression", val {t}] +When this is set, @mh expression prompts are read in a Lisp syntax. Otherwise, +the input is of the form of a Unix shell-like/switch notation as described in +the @mh documentation. +@enddefhvar + +@defcom[com "Pick Headers", stuff (bound to @bf[h] in @hid[Headers] mode) ] +This command is only valid in a @hid[Headers] buffer. It prompts for a +@f[pick] expression, and the messages shown in the buffer are supplied to +@f[pick] with the expression. The resulting messages are @f[scan]'ed, deleting +the previous contents of the buffer. The current value of @hid[Fill Column] is +used for the @f[scan]'ing. The buffer's point is moved to the first header. +The buffer's name is set to a string of the form @w<"@f[Headers ]">, so the modeline will show what is in the +buffer. As described in the introduction to this section, the expression may +be entered using either a Lisp syntax or a Unix shell-like/switch notation. +@enddefcom + +@defcom[com "Headers Help", bind (Headers: ?)] +This command displays documentation on @hid[Headers] mode. +@enddefcom + + +@section[Reading New Mail] + +@index[reading new mail]@label[reading-new-mail] + +@defcom[com "Incorporate and Read New Mail", stuff (bound to @bf[C-x i] globally and @bf[i] in @hid[Headers] and @hid[Message] modes) ] +This command incorporates new mail into @hid[New Mail Folder] and creates a +@hid[Headers] buffer with the new messages. An unseen-sequence must be define +in the user's @mh profile to use this. Any headers generated due to +@hid[Unseen Headers Message Spec] are inserted as well. The buffer's point is +positioned on the headers line representing the first unseen message of the +newly incorporated mail. +@enddefcom + +@defcom[com "Incorporate New Mail" ] +This command incorporates new mail into @hid[New Mail Folder], displaying +@f[inc] output in a pop-up window. This is similar to @hid[Incorporate and +Read New Mail] except that no @hid[Headers] buffer is generated. +@enddefcom + +@defhvar[var "New Mail Folder", val {"+inbox"}] +This is the folder into which @mh incorporates new mail. +@enddefhvar + +@defhvar[var "Unseen Headers Message Spec", val {nil}] +This is an @mh message specification that is suitable for any message prompt. +When incorporating new mail and after expunging messages, @hemlock uses this +specification in addition to the unseen-sequence name that is taken from the +user's @mh profile to generate headers for the unseen @hid[Headers] buffer. +This value is a string. +@enddefhvar + +@defhvar[var "Incorporate New Mail Hook", val {nil}] +This is a list of functions which are invoked immediately after new mail is +incorporated. The functions should take no arguments. +@enddefhvar + +@defhvar[var "Store Password", val {nil}] +When this is set, the user is only prompted once for his password, and the +password is stored for future use. +@enddefhvar + +@defhvar[var "Authenticate Incorporation", val {nil}] +@defhvar1[var "Authentication User Name", val {nil}] +When @hid[Authenticate Incorporation] is set, incorporating new mail prompts +for a password to access a remote mail-drop. + +When incorporating new mail accesses a remote mail-drop, @hid[Authentication +User Name] is the user name supplied for authentication on the remote machine. +If this is @nil, @hemlock uses the local name. +@enddefhvar + + +@section[Reading Messages] +@index[reading messages] +@label[reading-messages] +This section describes basic commands that show the current, next, and previous +messages, as well as a couple advanced commands. @hid[Show Message] (bound to +@bf[SPACE] in @hid[Headers] mode) will display the message represented by the +@f[scan] line the @hemlock cursor is on. Deleted messages are considered +special, and the more conveniently bound commands for viewing the next and +previous messages (@hid[Next Undeleted Message] bound to @bf[n] and +@hid[Previous Undeleted Message] bound to @bf[p], both in @hid[Headers] and +@hid[Message] modes) will ignore them. @hid[Next Message] and @hid[Previous +Message] (bound to @bf[M-n] and @bf[M-p] in @hid[Headers] and @hid[Message] +modes) may be invoked if reading a message is desired regardless of whether it +has been deleted. + + +@defcom[com "Show Message", stuff (bound to @bf[SPACE] and @bf[.] in @hid[Headers] mode) ] + This command, when invoked in a @hid[Headers] buffer, displays the current +message (the message the cursor is on), by replacing any previous message that +has not been preserved with @hid[Keep Message]. The current message is also +removed from the unseen sequence. The @hid[Message] buffer becomes the current +buffer using the current window. The buffer's point will be moved to the +beginning of the buffer, and the buffer's name will be set to a string of the +form @w<"@f[Message ]">. + +The @hid[Message] buffer is read-only and may not be modified. The command +@hid[Goto Headers Buffer] issued in the @hid[Message] buffer makes the +associated @hid[Headers] buffer current. + +When not in a @hid[Headers] buffer, this command prompts for a folder and +message. A unique @hid[Message] buffer is obtained, and its name is set to a +string of the form @w<"@f[Message ]">. The buffer's point is +moved to the beginning of the buffer, and the current window is used to display +the message. + +Specifying multiple messages inserts all the messages into the same buffer. If +the user wishes to show more than one message, it is expected that he will +generate a @hid[headers] buffer with the intended messages, and then use the +message sequencing commands described below. +@enddefcom + +@defcom[com "Next Message", stuff (bound to @bf[M-n] in @hid[Headers] and @hid[Message] modes) ] + This command is only meaningful in a @hid[Headers] buffer or a @hid[Message] +buffer associated with a @hid[Headers] buffer. In a @hid[Headers] buffer, the +point is moved to the next message, and if there is one, it is shown as +described in the @hid[Show Message] command. + +In a @hid[Message] buffer, the message after the currently visible message is +displayed. This clobbers the buffer's contents. Note, if the @hid[Message] +buffer is associated with a @hid[Draft] buffer, invoking this command breaks +that association. Using @hid[Keep Message] preserves the @hid[Message] buffer +and any association with a @hid[Draft] buffer. + +The @hid[Message] buffer's name is set as described in the @hid[Show Message] +command. +@enddefcom + +@defcom[com "Previous Message", stuff (bound to @bf[M-p] in @hid[Headers] and @hid[Message] modes) ] + This command is only meaningful in a @hid[Headers] buffer or a @hid[Message] +buffer associated with a @hid[Headers] buffer. In a @hid[Headers] buffer, the +point is moved to the previous message, and if there is one, it is shown as +described in the @hid[Show Message] command. + +In a @hid[Message] buffer, the message before the currently visible message is +displayed. This clobbers the buffer's contents. Note, if the @hid[Message] +buffer is associated with a @hid[Draft] buffer, invoking this command breaks +that association. Using @hid[Keep Message] preserves the @hid[Message] buffer +and any association with a @hid[Draft] buffer. + +The @hid[Message] buffer's name is set as described in the @hid[Show Message] +command. +@enddefcom + +@defcom[com "Next Undeleted Message", stuff (bound to @bf[n] in @hid[Headers] and @hid[Message] modes) ] + This command is only meaningful in a @hid[Headers] buffer or a @hid[Message] +buffer associated with a @hid[Headers] buffer. In a @hid[Headers] buffer, the +point is moved to the next undeleted message, and if there is one, it is shown +as described in the @hid[Show Message] command. + +In a @hid[Message] buffer, the first undeleted message after the currently +visible message is displayed. This clobbers the buffer's contents. Note, if +the @hid[Message] buffer is associated with a @hid[Draft] buffer, invoking this +command breaks that association. The @hid[Keep Message] command preserves the +@hid[Message] buffer and any association with a @hid[Draft] buffer. + +The @hid[Message] buffer's name is set as described in the @hid[Show Message] +command. +@enddefcom + +@defcom[com "Previous Undeleted Message", stuff (bound to @bf[p] in @hid[Headers] and @hid[Message] modes) ] + This command is only meaningful in a @hid[Headers] buffer or a @hid[Message] +buffer associated with a @hid[Headers] buffer. In a @hid[Headers] buffer, the +point is moved to the previous undeleted message, and if there is one, it is +shown as described in the @hid[Show Message] command. + +In a @hid[Message] buffer, the first undeleted message before the currently +visible message is displayed. This clobbers the buffer's contents. Note, if +the @hid[Message] buffer is associated with a @hid[Draft] buffer, invoking this +command breaks that association. The @hid[Keep Message] command preserves the +@hid[Message] buffer and any association with a @hid[Draft] buffer. + +The @hid[Message] buffer's name is set as described in the @hid[Show Message] +command. +@enddefcom + +@defcom[com "Scroll Message", stuff (bound to @bf[SPACE] and @bf[C-v] in @hid[Message] mode) ] +@defhvar1[var "Scroll Message Showing Next", val {t}] + This command scrolls the current window down through the current message. If +the end of the message is visible and @hid[Scroll Message Showing Next] is not +@nil, then show the next undeleted message. +@enddefcom + +@defcom[com "Keep Message" ] +This command can only be invoked in a @hid[Message] buffer. It causes the +@hid[Message] buffer to continue to exist when the user invokes commands to +view other messages either within the kept @hid[Message] buffer or its +associated @hid[Headers] buffer. This is useful for getting two messages into +different buffers. It is also useful for retaining @hid[Message] buffers which +would otherwise be deleted when an associated draft message is delivered. +@enddefcom + +@defcom[com "Message Help", bind (Message: ?)] +This command displays documentation on @hid[Message] mode. +@enddefcom + + +@section[Sending Messages] +@index[sending messages] +@label[sending-messages] +The most useful commands for sending mail are @hid[Send Message] (bound to +@bf[m] and @bf[s] in @hid[Headers] and @hid[Message] modes), @hid[Reply to +Message] (bound to @bf[r] in @hid[Headers] mode), and @hid[Reply to Message in +Other Window] (bound to @bf[r] in @hid[Message] mode). These commands set up a +@hid[Draft] buffer and associate a @hid[Message] buffer with the draft when +possible. To actually deliver the message to its recipient(s), use +@hid[Deliver Message] (bound to @bf[H-s] in @hid[Draft] mode). To abort +sending mail, use @hid[Delete Draft and Buffer] (bound to @bf[H-q] in +@hid[Draft] mode). If one wants to temporarily stop composing a draft with the +intention of finishing it later, then the @hid[Save File] command (bound to +@bf[C-x C-s]) will save the draft to the user's draft folder. + +@hid[Draft] buffers have a special @hemlock minor mode called @hid[Draft] mode. +The major mode of a @hid[Draft] buffer is taken from the @hid[Default Modes] +variable. The user may wish to arrange that @hid[Text] mode (and possibly +@hid[Fill] mode or @hid[Save] mode) be turned on whenever @hid[Draft] mode is +set. For a further description of how to manipulate modes in @hemlock see the +@i[Hemlock Command Implementor's Manual]. + + +@defcom[com "Send Message", stuff (bound to @bf[s] and @bf[m] in @hid[Headers] and @hid[Message] modes and @bf[C-x m] globally) ] + This command, when invoked in a @hid[Headers] buffer, creates a unique +@hid[Draft] buffer and a unique @hid[Message] buffer. The current message is +inserted in the @hid[Message] buffer, and the @hid[Draft] buffer is displayed +in the current window. The @hid[Draft] buffer's point is moved to the end of +the line containing @f[To:] if it exists. The name of the draft message file +is used to produce the buffer's name. A pathname is associated with the +@hid[Draft] buffer so that @hid[Save File] can be used to incrementally save a +composition before delivering it. The @f[comp] utility will be used to +allocate a draft message in the user's @mh draft folder and to insert the +proper header components into the draft message. Both the @hid[Draft] and +@hid[Message] buffers are associated with the @hid[Headers] buffer, and the +@hid[Draft] buffer is associated with the @hid[Message] buffer. + +When invoked in a @hid[Message] buffer, a unique @hid[Draft] buffer is created, +and these two buffers are associated. If the @hid[Message] buffer is +associated with a @hid[Headers] buffer, this association is propagated to the +@hid[Draft] buffer. Showing other messages while in this @hid[Headers] buffer +will not affect this @hid[Message] buffer. + +When not in a @hid[Headers] or @hid[Message] buffer, this command does the same +thing as described in the previous two cases, but there are no @hid[Message] or +@hid[Headers] buffer manipulations. + +@hid[Deliver Message] will deliver the draft to its intended recipient(s). + +The @hid[Goto Headers Buffer] command, when invoked in a @hid[Draft] or +@hid[Message] buffer, makes the associated @hid[Headers] buffer current. The +@hid[Goto Message Buffer] command, when invoked in a @hid[Draft] buffer, makes +the associated @hid[Message] buffer current. +@enddefcom + +@defcom[com "Reply to Message", stuff (bound to @bf[r] in @hid[Headers] mode) ] +@defcom1[com "Reply to Message in Other Window", stuff (bound to @bf[r] in @hid[Message] mode) ] +@defhvar1[var "Reply to Message Prefix Action"] + @hid[Reply to Message], when invoked in a @hid[Headers] buffer, creates a +unique @hid[Draft] buffer and a unique @hid[Message] buffer. The current +message is inserted in the @hid[Message] buffer, and the @hid[Draft] buffer is +displayed in the current window. The draft components are set up in reply to +the message, and the @hid[Draft] buffer's point is moved to the end of the +buffer. The name of the draft message file is used to produce the buffer's +name. A pathname is associated with the @hid[Draft] buffer so that @hid[Save +File] can be used to incrementally save a composition before delivering it. +The @f[repl] utility will be used to allocate a draft message file in the +user's @mh draft folder and to insert the proper header components into the +draft message. Both the @hid[Draft] and @hid[Message] buffers are associated +with the @hid[Headers] buffer, and the @hid[Draft] buffer is associated with +the @hid[Message] buffer. + +When invoked in a @hid[Message] buffer, a unique @hid[Draft] buffer is set up +using the message in the buffer as the associated message. Any previous +association between the @hid[Message] buffer and a @hid[Draft] buffer is +removed. Any association of the @hid[Message] buffer with a @hid[Headers] +buffer is propagated to the @hid[Draft] buffer. + +When not in a @hid[Headers] buffer or @hid[Message] buffer, this command +prompts for a folder and message to reply to. This message is inserted into a +unique @hid[Message] buffer, and a unique @hid[Draft] buffer is created as in +the previous two cases. There is no association of either the @hid[Message] +buffer or the @hid[Draft] buffer with a @hid[Headers] buffer. + +When a prefix argument is supplied, @hid[Reply to Message Prefix Action] is +considered with respect to supplying carbon copy switches to @f[repl]. This +variable's value is one of @b[:cc-all], :@b[no-cc-all], or @nil. See section +@ref[Styles] for examples of how to use this. + +@hid[Reply to Message in Other Window] is identical to @hid[Reply to Message], +but the current window is split showing the @hid[Draft] buffer in the new +window. The split window displays the @hid[Message] buffer. + +@hid[Deliver Message] will deliver the draft to its intended recipient(s). + +The @hid[Goto Headers Buffer] commmand, when invoked in a @hid[Draft] or +@hid[Message] buffer, makes the associated @hid[Headers] buffer current. The +@hid[Goto Message Buffer] command, when invoked in a @hid[Draft] buffer, makes +the associated @hid[Message] buffer current. +@enddefcom + +@defcom[com "Forward Message", stuff (bound to @bf[f] in @hid[Headers] and @hid[Message] modes) ] + This command, when invoked in a @hid[Headers] buffer, creates a unique +@hid[Draft] buffer. The current message is inserted in the draft by using the +@f[forw] utility, and the @hid[Draft] buffer is shown in the current window. +The name of the draft message file is used to produce the buffer's name. A +pathname is associated with the @hid[Draft] buffer so that @hid[Save File] can +be used to incrementally save a composition before delivering it. The +@hid[Draft] buffer is associated with the @hid[Headers] buffer, but no +@hid[Message] buffer is created since the message is already a part of the +draft. + +When invoked in a @hid[Message] buffer, a unique @hid[Draft] buffer is set up +inserting the message into the @hid[Draft] buffer. The @hid[Message] buffer is +not associated with the @hid[Draft] buffer because the message is already a +part of the draft. However, any association of the @hid[Message] buffer with a +@hid[Headers] buffer is propagated to the @hid[Draft] buffer. + +When not in a @hid[Headers] buffer or @hid[Message] buffer, this command +prompts for a folder and message to forward. A @hid[Draft] buffer is created +as described in the previous two cases. + +@hid[Deliver Message] will deliver the draft to its intended recipient(s). +@enddefcom + +@defcom[com "Deliver Message", stuff (bound to @bf[H-s] and @bf[H-c] in @hid[Draft] mode) ] +@defhvar1[var "Deliver Message Confirm", val {nil}] + This command, when invoked in a @hid[Draft] buffer, saves the file and uses +the @mh @f[send] utility to deliver the draft. If the draft is a reply to some +message, then @f[anno] is used to annotate that message with a "@f[replied]" +component. Any @hid[Headers] buffers containing the replied-to message are +updated with an "@b[A]" placed in the appropriate headers line two characters +after the message ID. Before doing any of this, confirmation is asked for +based on @hid[Deliver Message Confirm]. + +When not in a @hid[Draft] buffer, this prompts for a draft message ID and +invokes @f[send] on that draft message to deliver it. Sending a draft in this +way severs any association that draft may have had with a message being replied +to, so no annotation will occur. +@enddefcom + +@defcom[com "Delete Draft and Buffer", stuff (bound to @bf[H-q] in @hid[Draft] mode) ] +This command, when invoked in a @hid[Draft] buffer, deletes the draft message +file and the buffer. This also deletes any associated message buffer unless +the user preserved it with @hid[Keep Message]. +@enddefcom + +@defcom[com "Remail Message", stuff (bound to @bf[H-r] in @hid[Headers] and @hid[Message] modes) ] + This command, when invoked in a @hid[Headers] or @hid[Message] buffer, prompts +for resend @f[To:] and resend @f[Cc:] addresses, remailing the current message. +When invoked in any other kind of buffer, this command prompts for a folder and +message as well. @mh's @f[dist] sets up a draft folder message which is then +modified. The above mentioned addresses are inserted on the @f[Resent-To:] and +@f[Resent-Cc:] lines. Then the message is delivered. + +There is no mechanism for annotating messages as having been remailed. +@enddefcom + +@defcom[com "Draft Help", bind (Draft: H-?)] +This command displays documentation on @hid[Draft] mode. +@enddefcom + + +@section[Convenience Commands for Message and Draft Buffers] +@index[message buffer commands] +@index[draft buffer commands] +@index[convenience commands for mail interface] +@label[convenience-coms] +This section describes how to switch from a @hid[Message] or @hid[Draft] buffer +to its associated @hid[Headers] buffer, or from a @hid[Draft] buffer to its +associated @hid[Message] buffer. There are also commands for various styles of +inserting text from a @hid[Message] buffer into a @hid[Draft] buffer. + +@defcom[com "Goto Headers Buffer", stuff (bound to @bf[^] in @hid[Message] mode and @bf[H-^] in @hid[Draft] mode) ] +This command, when invoked in a @hid[Message] or @hid[Draft] buffer with an +associated @hid[Headers] buffer, places the associated @hid[Headers] buffer in +the current window. + +The cursor is moved to the headers line of the associated message. +@enddefcom + +@defcom[com "Goto Message Buffer", stuff (bound to @bf[H-m] in @hid[Draft] mode) ] +This command, when invoked in a @hid[Draft] buffer with an associated +@hid[Message] buffer, places the associated @hid[Message] buffer in the current +window. +@enddefcom + +@defcom[com "Insert Message Region", stuff (bound to @bf[H-y] in appropriate modes) ] +@defhvar1[var "Message Insertion Prefix", val {" "}] +@defhvar1[var "Message Insertion Column", val {75}] +This command, when invoked in a @hid[Message] or @hid[News-Message] (where it +is bound) buffer that has an associated @hid[Draft] or @hid[Post] buffer, +copies the current active region into the @hid[Draft] or @hid[Post] buffer. It +is filled using @hid[Message Insertion Prefix] (which defaults to three spaces) +and @hid[Message Insertion Column]. If an argument is supplied, the filling is +inhibited. +@enddefcom + +@defcom[com "Insert Message Buffer", stuff (bound to @bf[H-y] in appropriate modes) ] +@defhvar1[var "Message Buffer Insertion Prefix", val {" "}] +This command, when invoked in a @hid[Draft] or @hid[Post] (where it is bound) +buffer with an associated @hid[Message] or @hid[News-Message] buffer, or when +in a @hid[Message] (or @hid[News-Message]) buffer that has an associated +@hid[Draft] buffer, inserts the @hid[Message] buffer into the @hid[Draft] (or +@hid[Post]) buffer. Each inserted line is modified by prefixing it with +@hid[Message Buffer Insertion Prefix] (which defaults to four spaces) . If an +argument is supplied, the prefixing is inhibited. +@enddefcom + +@defcom[com "Edit Message Buffer", stuff (bound to @bf[e] in @hid[Message] mode) ] +This command puts the current @hid[Message] buffer in @hid[Text] mode and makes +it writable (@hid[Message] buffers are normally read-only). The pathname of +the file which the message is in is associated with the buffer making saving +possible. A recursive edit is entered, and the user is allowed to make changes +to the message. When the recursive edit is exited, if the buffer is modified, +the user is asked if the changes should be saved. The buffer is marked +unmodified, and the pathname is disassociated from the buffer. The buffer +otherwise returns to its previous state as a @hid[Message] buffer. If the +recursive edit is aborted, the user is not asked to save the file, and the +buffer remains changed though it is marked unmodified. +@enddefcom + + +@section[Deleting Messages] +@index[deleting messages] +@label[deleting] +The main command described in this section is @hid[Headers Delete Message] +(bound to @bf[k] in @hid[Headers] and @hid[Message] modes). A useful command +for reading new mail is @hid[Delete Message and Show Next] (bound to @bf[d] in +@hid[Message] mode) which deletes the current message and shows the next +undeleted message. + +Since messages are by default deleted using a virtual message deletion +mechanism, @hid[Expunge Messages] (bound to @bf[!] in @hid[Headers] mode) +should be mentioned here. This is described in section @ref[terminating]. + + +@defhvar[var "Virtual Message Deletion", val {t}] +When set, @hid[Delete Message] adds a message to the "@f[hemlockdeleted]" +sequence; otherwise, @f[rmm] is invoked on the message immediately. +@enddefhvar + +@defcom[com "Delete Message" ] +This command prompts for a folder, messages, and an optional @f[pick] +expression. When invoked in a @hid[Headers] buffer of the specified folder, +the prompt for a message specification will default to the those messages in +that @hid[Headers] buffer. + +When the variable @hid[Virtual Message Deletion] is set, this command merely +flags the messages for deletion by adding them to the "@f[hemlockdeleted]" +sequence. Then this updates any @hid[Headers] buffers representing the folder. +It notates each headers line referring to a deleted message with a "@b[D]" in +the third character position after the message ID. + +When @hid[Virtual Message Deletion] is not set, @f[rmm] is invoked on the +message, and each headers line referring to the deleted message is deleted from +its buffer +@enddefcom + +@defcom[com "Headers Delete Message", stuff (bound to @bf[k] in @hid[Headers] and @hid[Message] modes) ] +This command, when invoked in a @hid[Headers] buffer, deletes the message on +the current line as described in @hid[Delete Message]. + +When invoked in a @hid[Message] buffer, the message displayed in it is deleted +as described in @hid[Delete Message]. +@enddefcom + +@defcom[com "Delete Message and Show Next", stuff (bound to @bf[k] in @hid[Headers] and @hid[Message] modes) ] +This command is only valid in a @hid[Headers] buffer or a @hid[Message] buffer +associated with some @hid[Headers] buffer. The current message is deleted as +with the @hid[Delete Message] command. Then the next message is shown as with +@hid[Next Undeleted Message]. +@enddefcom + +@defcom[com "Delete Message and Down Line", stuff (bound to @bf[d] in @hid[Headers mode])] +This command, when invoked in a @hid[Headers] buffer, deletes the message on +the current line. Then the point is moved to the next non-blank line. +@enddefcom + +@defcom[com "Undelete Message" ] +This command is only meaningful when @hid[Virtual Message Deletion] is set. +This prompts for a folder, messages, and an optional @f[pick] expression. When +in a @hid[Headers] buffer of the specified folder, the messages prompt defaults +to those messages in the buffer. All @hid[Headers] buffers representing the +folder are updated. Each headers line referring to an undeleted message is +notated by replacing the "@b[D]" in the third character position after the +message ID with a space. +@enddefcom + +@defcom[com "Headers Undelete Message", stuff (bound to @bf[u] in @hid[Headers] and @hid[Message] modes) ] +This command is only meaningful when @hid[Virtual Message Deletion] is set. +When invoked in a @hid[Headers] buffer, the message on the current line is +undeleted as described in @hid[Undelete Message]. + +When invoked in a @hid[Message] buffer, the message displayed in it is +undeleted as described in @hid[Undelete Message]. +@enddefcom + + +@section[Folder Operations] + +@index[folder operations]@label[folder] +@defcom[com "List Folders" ] +This command displays a list of all current mail folders in the user's +top-level mail directory in a @hemlock pop-up window. +@enddefcom + +@defcom[com "Create Folder"] +This command prompts for and creates a folder. If the folder already exists, +an error is signaled. +@enddefcom + +@defcom[com "Delete Folder" ] +This command prompts for a folder and uses @f[rmf] to delete it. Note that no +confirmation is asked for. +@enddefcom + + +@section[Refiling Messages] + +@index[refiling messages]@label[refiling] +@defcom[com "Refile Message" ] +This command prompts for a folder, messages, an optional @f[pick] expression, +and a destination folder. When invoked in a @hid[Headers] buffer of the +specified folder, the message prompt offers a default of those messages in the +buffer. If the destination folder does not exist, the user is asked to create +it. The resulting messages are refiled with the @f[refile] utility. All +@hid[Headers] buffers for the folder are updated. Each line referring to a +refiled message is deleted from its buffer. +@enddefcom + +@defcom[com "Headers Refile Message", stuff (bound to @bf[o] in @hid[Headers] and @hid[Message] modes) ] +This command, when invoked in a @hid[Headers] buffer, prompts for a destination +folder, refiling the message on the current line with @f[refile]. If the +destination folder does not exist, the user is asked to create it. Any +@hid[Headers] buffers containing messages for that folder are updated. Each +headers line referring to the refiled message is deleted from its buffer. + +When invoked in a @hid[Message] buffer, that message is refiled as described +above. +@enddefcom + + +@section[Marking Messages] +@index[marking messages] +@label[marking] +@defcom[com "Mark Message" ] +This command prompts for a folder, message, and sequence and adds (deletes) the +message specification to (from) the sequence. By default this adds the +message, but if an argument is supplied, this deletes the message. When +invoked in a @hid[Headers] buffer or @hid[Message] buffer, this only prompts +for a sequence and uses the current message. +@enddefcom + + +@section[Terminating Headers Buffers] +@label[terminating] +The user never actually @i[exits] the mailer. He can leave mail buffers lying +around while conducting other editing tasks, selecting them and continuing his +mail handling whenever. There still is a need for various methods of +terminating or cleaning up @hid[Headers] buffers. The two most useful commands +in this section are @hid[Expunge Messages] and @hid[Quit Headers]. + + +@defhvar[var "Expunge Messages Confirm", val {t}] +When this is set, @hid[Quit Headers] and @hid[Expunge Messages] will ask for +confirmation before expunging messages and packing the folder's message ID's. +@enddefhvar + +@defhvar[var "Temporary Draft Folder", val {nil}] +This is a folder name where @mh @f[fcc:] messages are kept with the intention +that this folder's messages will be deleted and expunged whenever messages from +any folder are expunged (for example, when @hid[Expunge Messages] or @hid[Quit +Headers] is invoked. +@enddefhvar + +@defcom[com "Expunge Messages", stuff (bound to @bf[!] in @hid[Headers] mode) ] +This command deletes messages @f[mark]'ed for deletion, and compacts the +folder's message ID's. If there are messages to expunge, ask the user for +confirmation, displaying the folder name. This can be inhibited by setting +@hid[Expunge Messages Confirm] to @nil. When @hid[Temporary Draft Folder] is +not @nil, this command deletes and expunges that folder's messages regardless +of the folder in which the user invokes it, and a negative response to the +request for confirmation inhibits this. + +When invoked in a @hid[Headers] buffer, the messages in that folder's +"@f[hemlockdeleted]" sequence are deleted by invoking @f[rmm]. Then the ID's +of the folder's remaining messages are compacted using the @f[folder] utility. +Since headers must be regenerated due to renumbering or reassigning message +ID's, and because @hid[Headers] buffers become inconsistent after messages are +deleted, @hemlock must regenerate all the headers for the folder. Multiple +@hid[Headers] buffers for the same folder are then collapsed into one buffer, +deleting unnecessary duplicates. Any @hid[Message] buffers associated with +these @hid[Headers] buffers are deleted. + +If there is an unseen @hid[Headers] buffer for the folder, it is handled +separately from the @hid[Headers] buffers described above. @hemlock tries to +update it by filling it only with remaining unseen message headers. +Additionally, any headers generated due to @hid[Unseen Headers Message Spec] +are inserted. If there are no headers, unseen or otherwise, the buffer is left +blank. + +Any @hid[Draft] buffer set up as a reply to a message in the folder is affected +as well since the associated message has possibly been deleted. When a draft +of this type is delivered, no message will be annotated as having been replied +to. + +When invoked in a @hid[Message] buffer, this uses its corresponding folder as +the folder argument. The same updating as described above occurs. + +In any other type of buffer, a folder is prompted for. +@enddefcom + +@defcom[com "Quit Headers", stuff (bound to @bf[q] in @hid[Headers] and @hid[Message] modes) ] +This command affects the current @hid[Headers] buffer. When there are deleted +messages, ask the user for confirmation on expunging the messages and packing +the folder's message ID's. This prompting can be inhibited by setting +@hid[Expunge Messages Confirm] to @nil. After deleting and packing, this +deletes the buffer and all its associated @hid[Message] buffers. + +Other @hid[Headers] buffers regarding the same folder are handled as described +in @hid[Expunge Messages], but the buffer this command is invoked in is always +deleted. + +When @hid[Temporary Draft Folder] is not @nil, this folder's messages are +deleted and expunged regardless of the folder in which the user invokes this +command. A negative response to the above mentioned request for confirmation +inhibits this. +@enddefcom + +@defcom[com "Delete Headers Buffer and Message Buffers" ] +This command prompts for a @hid[Headers] buffer to delete along with its +associated @hid[Message] buffers. Any associated @hid[Draft] buffers are left +intact, but their corresponding @hid[Message] buffers will be deleted. When +invoked in a @hid[Headers] buffer or a @hid[Message] buffer associated with a +@hid[Headers] buffer, that @hid[Headers] buffer is offered as a default. +@enddefcom + + +@section[Miscellaneous Commands] +@label[miscellaneous mail commands] +@label[miscellaneous] + +@defcom[com "List Mail Buffers", stuff (bound to @bf[l] in @hid[Headers] and @hid[Message] modes @bf[H-l] in @hid[Draft] mode) ] +This command shows a list of all mail @hid[Message], @hid[Headers], and +@hid[Draft] buffers. + +If a @hid[Message] buffer has an associated @hid[Headers] buffer, it is +displayed to the right of the @hid[Message] buffer's name. + +If a @hid[Draft] buffer has an associated @hid[Message] buffer, it is displayed +to the right of the @hid[Draft] buffer's name. If a @hid[Draft] buffer has no +associated @hid[Message] buffer, but it is associated with a @hid[Headers] +buffer, then the name of the @hid[Headers] buffer is displayed to the right of +the @hid[Draft] buffer. + +For each buffer listed, if it is modified, then an asterisk is displayed before +the name of the buffer. +@enddefcom + + +@section[Styles of Usage] +@index[styles of mail interface usage] +@label[Styles] +This section discusses some styles of usage or ways to make use of some of the +features of @hemlock's interface to @mh that might not be obvious. In each +case, setting some variables and/or remembering an extra side effect of a +command will lend greater flexibility and functionality to the user. + +@subsection[Unseen Headers Message Spec] +The unseen @hid[Headers] buffer by default only shows unseen headers which is +adequate for one folder, simple mail handling. Some people use their @hid[New +Mail Folder] only for incoming mail, refiling or otherwise dispatching a +message immediately. Under this mode it is easy to conceive of the user not +having time to respond to a message, but he would like to leave it in this +folder to remind him to take care of it. Using the @hid[Unseen Headers Message +Spec] variable, the user can cause all the messages the @hid[New Mail Folder] to +be inserted into the unseen @hid[Headers] buffer whenever just unseen headers +would be. This way he sees all the messages that require immediate attention. + +To achieve the above effect, @hid[Unseen Headers Message Spec] should be set to +the string @f["all"]. This variable can be set to any general @mh message +specification (see section @ref[mhcommands] of this chapter), so the user can +include headers of messages other than those that have not been seen without +having to insert all of them. For example, the user could set the variable to +@f["flagged"] and use the @hid[Mark Message] command to add messages he's +concerned about to the @f["flagged"] sequence. Then the user would see new +mail and interesting mail in his unseen @hid[Headers] buffer, but he doesn't +have to see everything in his @hid[New Mail Folder]. + + +@subsection[Temporary Draft Folder] +Section @ref[components-files] of this chapter discusses how to make @mh keep +personal copies of outgoing mail. The method described will cause a copy of +every outgoing message to be saved forever and requires the user to go through +his @f[Fcc:] folder, weeding out those he does not need. The @hid[Temporary +Draft Folder] variable can name a folder whose messages will be deleted and +expunged whenever any folder's messages are expunged. By naming this folder in +the @mh profile and components files, copies of outgoing messages can be saved +temporarily. They will be cleaned up automatically, but the user still has a +time frame in which he can permanently save a copy of an outgoing message. +This folder can be visited with @hid[Message Headers], and messages can be +refiled just like any other folder. + + +@subsection[Reply to Message Prefix Action] +Depending on the kinds of messages one tends to handle, the user may find +himself usually replying to everyone who receives a certain message, or he may +find that this is only desired occasionally. In either case, the user +can set up his @mh profile to do one thing by default, using the @hid[Reply +to Message Prefix Action] variable in combination with a prefix argument to the +@hid[Reply to Message] command to get the other effect. + +For example, the following line in one's @mh profile will cause @mh to reply to +everyone receiving a certain message (except for the user himself since he +saves personal copies with the @f[-fcc] switch): +@begin[programexample] +repl: -cc all -nocc me -fcc out-copy +@end[programexample] +This user can set @hid[Reply to Message Prefix Action] to be @f[:no-cc-all]. +Then whenever he invokes @hid[Reply to Message] with a prefix argument, instead +of replying to everyone, the draft will be set up in reply only to the person +who sent the mail. + +As an alternative example, not specifying anything in one's @mh profile and +setting this variable to @f[:cc-all] will have a default effect of replying +only to the sender of a piece of mail. Then invoking @hid[Reply to Message] +with a prefix argument will cause everyone who received the mail to get a copy +of the reply. If the user does not want a @f[cc:] copy, then he can add +@f[-nocc me] as a default switch and value in his @mh profile. + + +@newpage +@section[Wallchart] + +@tabclear +@tabdivide(3) + +@begin[format, spacing 1.5] + +@Begin[Center] @b[Global bindings:] @End[Center] + +@hid[Incorporate and Read New Mail]@\@\@bf[C-x i] +@hid[Send Message]@\@\@bf[C-x m] +@hid[Message Headers]@\@\@bf[C-x r] + + +@Begin[Center] @b[Headers and Message modes bindings:] @End[Center] + +@hid[Next Undeleted Message]@\@\@bf[n] +@hid[Previous Undeleted Message]@\@\@bf[p] +@hid[Send Message]@\@\@bf[s], @bf[m] +@hid[Forward Message]@\@\@bf[f] +@hid[Headers Delete Message]@\@\@bf[k] +@hid[Headers Undelete Message]@\@\@bf[u] +@hid[Headers Refile Message]@\@\@bf[o] +@hid[List Mail Buffers]@\@\@bf[l] +@hid[Quit Headers]@\@\@bf[q] +@hid[Incorporate and Read New Mail]@\@\@bf[i] +@hid[Next Message]@\@\@bf[M-n] +@hid[Previous Message]@\@\@bf[M-p] +@hid[Beginning of Buffer]@\@\@bf[<] +@hid[End of Buffer]@\@\@bf[>] + + +@Begin[Center] @b[Headers mode bindings:] @End[Center] + +@hid[Delete Message and Down Line]@\@\@bf[d] +@hid[Pick Headers]@\@\@bf[h] +@hid[Show Message]@\@\@bf[space], @bf[.] +@hid[Reply to Message]@\@\@bf[r] +@hid[Expunge Messages]@\@\@bf[!] + + +@Begin[Center] @b[Message mode bindings:] @End[Center] + +@hid[Delete Message and Show Next]@\@\@bf[d] +@hid[Goto Headers Buffer]@\@\@bf[^] +@hid[Scroll Message]@\@\@bf[space] +@hid[Scroll Message]@\@\@bf[C-v] +@hid[Scroll Window Up]@\@\@bf[backspace], @bf[delete] +@hid[Reply to Message in Other Window]@\@bf[r] +@hid[Edit Message Buffer]@\@\@bf[e] +@hid[Insert Message Region]@\@\@bf[H-y] + + +@Begin[Center] @b[Draft mode bindings:] @End[Center] + +@hid[Goto Headers Buffer]@\@\@bf[H-^] +@hid[Goto Message Buffer]@\@\@bf[H-m] +@hid[Deliver Message]@\@\@bf[H-s], @bf[H-c] +@hid[Insert Message Buffer]@\@\@bf[H-y] +@hid[Delete Draft and Buffer]@\@\@bf[H-q] +@hid[List Mail Buffers]@\@\@bf[H-l] + +@end[format] +@tabclear diff --git a/docs/hem/user/netnews.mss b/docs/hem/user/netnews.mss new file mode 100644 index 0000000..cc3b2d5 --- /dev/null +++ b/docs/hem/user/netnews.mss @@ -0,0 +1,488 @@ +@comment{-*- Dictionary: /afs/cs/project/clisp/docs/hem/hem; Mode: spell; Package: Hemlock -*-} + +@chap[The Hemlock Netnews Interface] +@section[Introduction to Netnews in Hemlock] + + +@hemlock provides a facility for reading bulletin boards through the +NetNews Transfer Protocol (NNTP). You can easily read Netnews, reply to +news posts, post messages, etc. The news reading interface is consistent +with that of the @hemlock mailer, and most Netnews commands function in the +same manner as their mailer counterparts. + +Netnews can be read in one of two different modes. The first mode, invoked +by the @hid(Netnews) command, allows the user to read new messages in +groups which the user has specified. This method of reading netnews will +track the highest numbered message in each newsgroup and only show new +messages which have arrived since then. The @hid(Netnews Browse) command +invokes the other method of reading netnews. This mode displays a list of +all newsgroups, and the user may choose to read messages in any of them. +By default, the news reader will not track the latest message read when +browsing, and it will always display the last few messages. + + +@section[Setting Up Netnews] + +To start reading bulletin boards from @hemlock you probably need to create a +file containing the newsgroups you want to read. + +@defhvar[var "Netnews Group File", val {".hemlock-groups"}] + When you invoke the @hid(Netnews) command, @hemlock merges the value of + this variable with your home directory and looks there for a list of + groups (one per line) to read. +@enddefhvar + +@defhvar[var "Netnews Database File", val{".hemlock-netnews"}] +When you invoke the @hid(Netnews) command, @hemlock merges the value of +this variable with your home directory. This file maintains a pointer to +the highest numbered message read in each group in @hid(Netnews Group +File). +@enddefhvar + +@defcom[com "List All Groups"] + When you invoke this command, @hemlock creates a buffer called + @hid(Netnews Groups) and inserts the names of all accessible Netnews + groups into it alphabetically. You may find this useful if you choose to set + up your @hid(Netnews Group File) manually. +@enddefcom + +@defhvar[var "Netnews NNTP Server", val{"netnews.srv.cs.cmu.edu"}] +This variable stores the host name of the machine which @hemlock will use +as the NNTP server. +@enddefhvar + +@defhvar[var "Netnews NNTP Timeout Period", val{30}] +This is the number of seconds @hemlock will wait trying to connect to the +NNTP server. If a connection is not made within this time period, the +connection will time out and an error will be signalled. +@enddefhvar + +@subsection[News-Browse Mode] + + @hid(News-Browse) mode provides an easy method of adding groups to + your @hid(Netnews Group File). + +@defcom[com "Netnews Browse"] + This command sets up a buffer in @hid{News-Browse} mode with all + available groups listed one per line. Groups may be read or added + to your group file using various commands in this mode. +@enddefcom + +@defcom[com "Netnews Browse Add Group To File", stuff (bound to @bf[a] in @hid[News-Browse] mode)] +@defcom1[com "Netnews Browse Pointer Add Group to File"] +@hid(Netnews Browse Add Group to File) adds the group under the point to +your group file, and @hid(Netnews Browse Pointer Add Group To File) adds +the group under the mouse pointer without moving the point. +@enddefcom + +@defcom[com "Netnews Browse Read Group", stuff (bound to @bf[space] in @hid[News-Browse] mode)] +@defcom1[com "Netnews Browse Pointer Read Group"] +@hid(Netnews Browse Read Group) and @hid(Netnews Browse Pointer Read Group) +read the group under the cursor and the group under the mouse pointer, +respectively. These commands neither use nor modify the contents of your +@hid(Netnews Database File); they will always present the last few messages +in the newsgroup, regardless of the last message read. @hid(Netnews Browse +Pointer Read Group) does not modify the position of the point. +@enddefcom + +@defcom[com "Netnews Quit Browse"] + This command exits @hid(News-Browse) mode. +@enddefcom + +The @hid(Next Line) and @hid(Previous Line) commands are conveniently bound to +@bf[n] and @bf[p] in this mode. + +@section[Starting Netnews] + +Once your @hid(Netnews Group File) is set up, you may begin reading netnews. + +@defcom[com "Netnews"] + This command is the main entry point for reading bulletin boards in + @hemlock. Without an argument, the system looks for what bulletin boards to + read in the value of @hid(Netnews Group File) and reads each of them in + succession. @hemlock keeps a pointer to the last message you read in each + of these groups in your @hid(Netnews Database File). Bulletin boards may + be added to your @hid(Netnews Group File) manually or by using + the @hid(Netnews Browse) facility. With an argument, @hemlock prompts the + user for the name of a bulletin board and reads it. +@enddefcom + +@defcom[com "Netnews Look at Group"] + This command prompts for a group and reads it, ignoring the information + in your @hid(Netnews Database File). +@enddefcom + +When you read a group, @hemlock creates a buffer that contains important +header information for the messages in that group. There are four fields +in each header, one each for the @i(date), @i(lines), @i(from), and +@i(subject). The @i(date) field shows when the message was sent, the +@i(lines) field displays how long the message is in lines, the @i(from) +field shows who sent the message, and the @i(subject) field displays the +subject of this message. If a field for a message is not available, @f(NA) +will appear instead. You may alter the length of each of these fields by +modifying the following @hemlock variables: + +@defhvar[var "Netnews Before Date Field Pad", val 1] + How many spaces should be inserted before the date in @hid(News-Headers) + buffers. +@enddefhvar + +@defhvar[var "Netnews Date Field Length", val 6] +@defhvar1[var "Netnews Line Field Length", val 3] +@defhvar1[var "Netnews From Field Length", val 20] +@defhvar1[var "Netnews Subject Field Length", val 43] + These variables control how long the @i(date), @i(line), @i(from), and + @i(subject) fields should be in @hid{News-Headers} buffers. +@enddefhvar + +@defhvar[var "Netnews Field Padding", val 2] + How many spaces should be left between the Netnews @i(date), @i(from), + @i(lines), and @i(subject) fields after padding to the required length. +@enddefhvar + +For increased speed, @hemlock only inserts headers for a subset of the +messages in each group. If you have never read a certain group, and the +value of @hid(Netnews New Group Style) is @f(:from-end) (the default), +@hemlock inserts some number of the last messages in the group, determined +by the value of @hid(Netnews Batch Count). If the value of @hid(Netnews +New Group Style) is @f(:from-start), @hemlock will insert the first batch +of messages in the group. If you have read a group before, @hemlock will +insert the batch of messages following the highest numbered message that +you had read previously. + +@defhvar[var "Netnews Start Over Threshold", val {350}] + If the number of new messages in a group exceeds the value of this + variable and @hid(Netnews New Group Style) is @f(:from-end), @hemlock asks + if you would like to start reading this group from the end. +@enddefhvar + +You may at any time go beyond the messages that are visible using the +@hid(Netnews Next Line), @hid(Netnews Previous Line), +@hid(Netnews Headers Scroll Window Up), and +@hid(Netnews Headers Scroll Down) commands in @hid(News-Headers) mode, +or the @hid(Netnews Next Article) and @hid(Netnews Previous Article) +commands in @hid(News-Message) mode. + +@defhvar[var "Netnews Fetch All Headers", val {nil}] +This variable determines whether Netnews will fetch all headers immediately +upon entering a new group. +@enddefhvar + +@defhvar[var "Netnews Batch Count", val {50}] + This variable determines how many headers the Netnews facility will fetch + at a time. +@enddefhvar + +@defhvar[var "Netnews New Group Style", val {:from-end}] +This variable determines what happens when you read a group that you have +never read before. When it is @f(:from-start), the @hid(Netnews) command +will read from the beginning of a new group forward. When it is @f(:from-end), +the default, @hid(Netnews) will read the group from the end backward. +@enddefhvar + +@section[Reading Messages] + +From a @hid{News-Headers} buffer, you may read messages, reply to messages +via the @hemlock mailer, or reply to messages via post. Some commands are +also bound to ease getting from one header to another. + +@defcom[com "Netnews Show Article", stuff (bound to @bf[space] in @hid{News-Headers} mode)] +@defhvar1[var "Netnews Read Style", val {:multiple}] +@defhvar1[var "Netnews Headers Proportion", val {0.25}] +This command puts the body of the message header under the current point +into a @hid{News-Message} buffer. If the value of @hid(Netnews Read +Style) is @f(:single), @hemlock changes to the @hid{News-Message} +buffer. If it is @f(:multiple), then @hemlock splits the current window +into two windows, one for headers and one for message bodies. The headers +window takes up a proportion of the current window based on the value of +@hid(Netnews Headers Proportion). If the window displaying the +@hid(News-Headers) buffer has already been split, and the message +currently displayed in the @hid(News-Message) window is the same as the +one under the current point, this command behaves just like @hid(Netnews +Message Scroll Down). +@enddefcom + +@defhvar[var "Netnews Message Header Fields", val {nil}] + When this variable is @nil, all available fields are displayed in the + header of a message. Otherwise, this variable should containt a list of + fields to include in message headers. If an element of this + list is an atom, then it should be the string name of a field. If it is + a cons, then the car should be the string name of a field, and the cdr + should be the length to which this field should be limited. Any string + name is acceptable, and fields that do not exist are ignored. +@enddefhvar + +@defcom[com "Netnews Show Whole Header", stuff (bound to @bf[w] in @hid{News-Headers} and @hid{News-Message} modes.)] +This command displays the entire header for the message currently being +read. This is to undo the effects of @hid{Netnews Message Header Fields} +for the current message. +@enddefcom + +@defcom[com "Netnews Next Line", stuff (bound to @bf[C-n] and @bf[Downarrow] in @hid{News-Headers} mode)] +@defhvar1[var "Netnews Last Header Style", val {:next-headers}] +This command moves the current point to the next line. If you are on the +last visible message, and there are more in the current group, headers for +these messages will be inserted. If you are on the last header and there +are no more messages in this group, then @hemlock will take some action +based on the value of @hid(Netnews Last Header Style). If the value of +this variable is @f(:feep), @hemlock feeps you indicating there are no +more messages. If the value is @f(:next-headers), @hemlock reads in the +headers for the next group in your @hid(Netnews Group File). If the value +is @f(:next-article), @hemlock goes on to the next group and shows you +the first unread message. +@enddefcom + +@defcom[com "Netnews Previous Line", stuff (bound to @bf[C-p] and @bf[Uparrow] in @hid{News-Headers} mode)] +This command moves the current point to the previous line. If you are on +the first visible header, and there are more previous messages, @hemlock +inserts the headers for these messages. +@enddefcom + +@defcom[com "Netnews Headers Scroll Window Down", stuff (bound to @bf[C-v] in @hid{News-Headers} mode)] +@defcom1[com "Netnews Headers Scroll Window Up", stuff (bound to @bf[M-v] in @hid{News-Headers} mode)] + These commands scroll the headers window up or down one screenfull. If the + end of the buffer is visible, @hemlock inserts the next batch of headers. +@enddefcom + +@defcom[com "Netnews Next Article", stuff (bound to @bf[n] in @hid{News-Message} and @hid{News-Headers} modes)] +@defcom1[com "Netnews Previous Article", stuff (bound to @bf[p] in @hid{News-Message} and @hid{News-Headers} modes)] + These commands insert the next or previous message into a message buffer. +@enddefcom + +@defcom[com "Netnews Message Scroll Down", stuff (bound to @bf[space] in @hid{News-Message} mode)] +@defhvar1[var "Netnews Scroll Show Next Message", val {t}] +If the end of the current message is visible, @hemlock feeps the user if +the value of @hid(Netnews Scroll Show Next Message) is non-@nil, or it +inserts the next message into this message buffer if that variable is @nil. +If the end of the message is not visible, then @hemlock shows the next +screenfull of the current message. +@enddefcom + +@defcom[com "Netnews Message Quit", stuff (bound to @bf[q] in @hid{News-Message} mode)] + This command deletes the current message buffer and makes the associated + @hid{News-Headers} buffer current. +@enddefcom + +@defcom[com "Netnews Goto Headers Buffer", stuff (bound to @bf[H-h] in @hid{News-Message} mode)] + This command, when invoked from a @hid(News-Message) buffer with an + associated @hid(News-Headers) buffer, places the associated + @hid(News-Headers) buffer into the current window. +@enddefcom + +@defcom[com "Netnews Message Keep Buffer", stuff (bound to @bf[k] in @hid{News-Message} mode)] + By default, @hemlock uses one buffer to display all messages in a group, + one at a time. This command tells @hemlock to keep the current message + buffer intact and start reading messages in another buffer. +@enddefcom + +@defcom[com "Netnews Select Message Buffer", stuff (bound to @bf[H-m] in @hid{News-Headers} and @hid{Post} modes.)] + In @hid{News-Headers} mode, this command selects the buffer + containing the last message read. In @hid{Post} mode, it selects the + associated @hid{News-Message} buffer, if there is one. +@enddefcom + +@defcom[com "Netnews Append to File", stuff (bound to @bf[a] in @hid{News-Headers} and @hid{News-Message} modes.)] +@defhvar1[var "Netnews Message File", val {"netnews-messages.txt"}] +This command prompts for a file which the current message will be appended +to. The default file is the value of @hid(Netnews Message File) merged +with your home directory. +@enddefcom + +@defcom[com "Netnews Headers File Message", stuff (bound to @bf[o] in @hid{News-Headers} mode)] +This command prompts for a mail folder and files the message under the +point into it. If the folder does not exist, @hemlock will ask if it should +be created. +@enddefcom + +@defcom[com "Netnews Message File Message", stuff (bound to @bf[o] in @hid{News-Message} mode)] +This command prompts for a mail folder and files the current message there. +If the folder does not exist, @hemlock will ask if it should be created. +@enddefcom + +@defcom[com "Fetch All Headers", stuff (bound to @bf[f] in @hid{Netnews Headers} mode)] + In a forward reading @hid(Netnews headers) buffer, this command inserts + all headers after the last visible one into the headers buffer. If + @hemlock is reading this group backward, the system inserts all headers + before the first visible one into the headers buffer. +@enddefcom + +@defcom[com "Netnews Go to Next Group", stuff (bound to @bf[g] in @hid{News-Headers} and @hid{News-Message} modes.)] +This command goes to the next group in your @hid(Netnews Group File). +Before going on, it sets the group pointer in @hid(Netnews Database +Filename) to the last message you read. With an argument, the command does +not modify the group pointer for the current group. +@enddefcom + +@defcom[com "Netnews Quit Starting Here", stuff (bound to @bf[.] in @hid{News-Headers} and @hid{News-Message} modes)] + This command goes to the next group in your @hid(Netnews Group File), + setting the netnews pointer for this group to the message before the one + under the current point, so the next time you read this group, the message + indicated by the point will appear first. +@enddefcom + +@defcom[com "Netnews Group Punt Messages", stuff (bound to @bf[G] in @hid{News-Headers} mode)] + This command goes on to the next bulletin board in your group + file. Without an argument, the system sets the pointer for the current + group to the last message. With an argument, @hemlock sets the + pointer to the last visible message in the group. +@enddefcom + +@defcom[com "Netnews Exit", stuff (bound to @bf[q] in @hid{News-Headers} mode)] +@defhvar1[var "Netnews Exit Confirm", val {t}] + This command cleans up and deletes the @hid(News-Headers) buffer and + all associated @hid(News-Message) buffers. If the value of + @hid(Netnews Exit Confirm) is @nil, then @hemlock will not prompt before + exiting. +@enddefcom + +@section[Replying to Messages] + +The @hemlock Netnews interface also provides an easy way of replying to +messages through the @hemlock Mailer or via @hid{Post} mode. + +@defcom[com "Netnews Reply to Sender"] + When you invoke this command, @hemlock creates a @hid(Draft) buffer and + tries to fill in the @i(to) and @i(subject) fields of the draft. For + the @i(to) field, @hemlock looks at the @i(reply-to) field of the + message you are replying to, or failing that, the @i(from) field. If + the @i(subject) field does not start with @f(Re:), @hemlock inserts this + string, signifying that this is a reply. +@enddefcom + +@defcom[com "Netnews Reply to Sender in Other Window", stuff (bound to @bf[r] in @hid{News-Headers} and @hid{News-Message}.)] +This command splits the current window, placing the message you are +replying to in the top window and a new @hid{Draft} buffer in the bottom +one. This command fills in the header fields in the same manner as +@hid(Netnews Reply to Sender). +@enddefcom + +@defcom[com "Netnews Reply to Group"] +This command creates a @hid{Post} buffer with the @i(newsgroups) field set +to the current group and the @i(subject) field constructed in the same way +as in @hid(Netnews Reply to Sender). +@enddefcom + +@defcom[com "Netnews Reply to Group in Other Window", stuff (bound to @bf[R] in @hid{News-Headers} and @hid{News-Message}.)] + This command splits the current window, placing the message you are + replying to in the top window and a new @hid{Post} buffer in the bottom + one. This command will fill in the header fields in the same manner as + @hid(Netnews Reply to Group). +@enddefcom + +@defcom[com "Netnews Post Message", stuff (bound to @bf[C-x P])] + This command creates a @hid{Post} buffer. If you are in a + @hid(News-Headers) or @hid{News-Message} buffer, @hemlock fills in the + @i(newsgroups) field with the current group. +@enddefcom + +@defcom[com "Netnews Forward Message", stuff (bound to @bf[f] in @hid{News-Headers} and @hid{News-Message} modes.)] +This command creates a @hid{Post} buffer. If you are in a @hid{Netnews +Headers} or @hid{News-Message} buffer, @hemlock will put the text of the +current message into the buffer along with lines delimiting the forwarded +message. +@enddefcom + +@defcom[com "Netnews Goto Post Buffer", stuff (bound to @bf[H-p] in @hid{News-Message} mode)] + This command, when invoked in a @hid(News-Message) or @hid(Draft) buffer + with an associated @hid(News-Headers) buffer, places the associated + @hid(News-Headers) buffer into the current window. +@enddefcom + +@defcom[com "Netnews Goto Draft Buffer", stuff (bound to @bf[H-d] in @hid{News-Message} mode)] + This command, when invoked in a @hid(News-Message) buffer with an + associated @hid(Draft) buffer, places the @hid(Draft) buffer into the + current window. +@enddefcom + +@section[Posting Messages] + +@defcom[com "Netnews Deliver Post", stuff (bound to @bf[H-s] in @hid{Post} mode)] +@defhvar1[var "Netnews Deliver Post Confirm", val "t"] +This command delivers the contents of a @hid(Post) buffer to the NNTP +server. If @hid(Netnews Deliver Post Confirm) is @f(t), @hemlock will ask for +confirmation before posting the message. @hemlock feeps you if NNTP does +not accept the message. +@enddefcom + +@defcom[com "Netnews Abort Post", stuff (bound to @bf[H-q] in @hid{Post} mode)] + This command deletes the current @hid(Post) buffer. +@enddefcom + + +As in the mailer, when replying to a message you can be excerpt sections of +it using @hid(Insert Message Buffer) and @hid(Insert Message Region) in +@hid(Post) and @hid(News-Message) modes, respectively. You can also use +these commands when replying to a message via mail in a @hid(Draft) buffer. +In all cases, the same binding is used: @bf[H-y]. + +@newpage +@section[Wallchart] + +@tabclear +@tabdivide(5) + +@begin[format, spacing 1.5] + + +@Begin[Center] @b[Global bindings:] @End[Center] + +@hid[Netnews Post Message]@\@\@bf[C-x P] + + +@Begin[Center] @b[News-Headers and News-Message modes bindings:] @End[Center] + +@hid[Netnews Next Article]@\@\@\@bf[n] +@hid[Netnews Previous Article]@\@\@bf[p] +@hid[Netnews Go to Next Group]@\@\@bf[g] +@hid[Netnews Group Punt Messages]@\@\@bf[G] +@hid[List All Groups]@\@\@\@bf[l] +@hid[Netnews Append to File]@\@\@bf[a] +@hid[Netnews Forward Message]@\@\@bf[f] +@hid[Netnews Reply to Sender in Other Window]@\@\@bf[r] +@hid[Netnews Reply to Group in Other Window]@\@\@bf[R] +@hid[Netnews Quit Starting Here]@\@\@bf[.] + +@Begin[Center] @b[News-Headers mode bindings:] @End[Center] + +@hid[Netnews Show Article]@\@\@bf[Space] +@hid[Netnews Previous Line]@\@\@bf[C-p], @bf[Uparrow] +@hid[Netnews Next Line]@\@\@\@bf[C-n], @bf[Downarrow] +@hid[Netnews Headers Scroll Window Down]@\@\@bf[C-v] +@hid[Netnews Headers Scroll Window Up]@\@\@bf[M-v] +@hid[Netnews Select Message Buffer]@\@\@bf[H-m] +@hid[Netnews Exit]@\@\@\@bf[q] +@hid[Netnews Headers File Message]@\@\@bf[o] + + +@Begin[Center] @b[News-Message mode bindings:] @End[Center] + +@hid[Netnews Message Scroll Down]@\@\@bf[Space] +@hid[Scroll Window Up]@\@\@\@bf[Backspace] +@hid[Netnews Goto Headers Buffer]@\@\@bf[H-h], @bf[^] +@hid[Netnews Message Keep Buffer]@\@\@bf[k] +@hid[Netnews Message Quit]@\@\@bf[q] +@hid[Netnews Message File Message]@\@\@bf[o] +@hid[Netnews Goto Post Buffer]@\@\@bf[H-p] +@hid[Netnews Goto Draft Buffer]@\@\@bf[H-d] +@hid[Insert Message Region]@\@\@bf[H-y] + + +@Begin[Center] @b[Post mode bindings:] @End[Center] + +@hid[Netnews Select Message Buffer]@\@\@bf[H-m] +@hid[Netnews Deliver Post]@\@\@bf[H-s] +@hid[Netnews Abort Post]@\@\@\@bf[H-q] +@hid[Insert Message Buffer]@\@\@bf[H-y] + + +@Begin[Center] @b[News-Browse mode bindings:] @End[Center] + +@hid[Netnews Quit Browse]@\@\@bf[q] +@hid[Netnews Browse Add Group To File]@\@\@bf[a] +@hid[Netnews Browse Read Group]@\@\@bf[Space] +@hid[Next Line]@\@\@\@bf[n] +@hid[Previous Line]@\@\@\@bf[p] + + +@end[format] +@tabclear diff --git a/docs/hem/user/special-modes.mss b/docs/hem/user/special-modes.mss new file mode 100644 index 0000000..262b44b --- /dev/null +++ b/docs/hem/user/special-modes.mss @@ -0,0 +1,738 @@ +@comment{-*- Dictionary: bld:scribe/hem/hem; Mode: spell; Package: Hemlock -*-} +@chap[Special Modes] + +@section[Dired Mode] +@label[dired] +@index[directory editing] + +@hemlock provides a directory editing mechanism. The user can flag files and +directories for deletion, undelete flagged files, and with a keystroke read in +files and descend into directories. In some implementations, it also supports +copying, renaming, and a simple wildcard feature. + + +@subsection[Inspecting Directories] +@defcom[com "Dired", bind (C-x C-M-d)] +This command prompts for a directory and fills a buffer with a verbose listing +of that directory. When the prefix argument is supplied, this includes Unix +dot files. If a dired buffer already exists for the directory, this switches +to the buffer and makes sure it displays dot files if appropriate. +@enddefcom + +@defcom[com "Dired with Pattern", bind (C-x C-M-d)] +This command prompts for a directory and a pattern that may contain at most one +wildcard, an asterisk, and it fills a buffer with a verbose listing of the +files in the directory matching the pattern. When the prefix argument is +supplied, this includes Unix dot files. If a dired buffer already exists for +this directory, this switches to the buffer and makes sure it displays dot +files if appropriate. +@enddefcom + +@defcom[com "Dired from Buffer Pathname"] +This command invokes @hid[Dired] on the directory name of the current buffer's +pathname. +@enddefcom + +@defcom[com "Dired Help", bind (Dired: ?)] +This command pops up a help window listing the various @hid[Dired] commands. +@enddefcom + +@defcom[com "Dired View File", bind (Dired: Space)] +@defcom1[com "Dired Edit File", bind (Dired: e)] +These command read in the file on the current line with the point. If the line +describes a directory instead of a file, then this command effectively invokes +@hid[Dired] on the specification. This associates the file's buffer with the +@hid[Dired] buffer. + +@hid[Dired View File] reads in the file as if by @hid[View File], and +@hid[Dired Edit File] as if by @hid[Find File]. + +@hid[Dired View File] always reads into a newly created buffer, warning if the +file already exists in some buffer. +@enddefcom + +@defcom[com "Dired Up Directory", bind (Dired: ^)] +This command invokes @hid[Dired] on the directory up one level from the current +@hid[Dired] buffer. This is useful for going backwards after repeatedly +invoking @hid[Dired View File] and descending into a series of subdirectories. +Remember, @hid[Dired] only generates directory listings when no buffer contains +a dired for the specified directory. +@enddefcom + +@defcom[com "Dired Update Buffer", bind (Dired: H-u)] +This command is useful when the user knows the directory in the current +@hid[Dired] buffer has changed. @hemlock cannot know the directory structure +has changed, but the user can explicitly update the buffer with this command +instead of having to delete it and invoke @hid[Dired] again. +@enddefcom + +@defcom[com "Dired Next File"] +@defcom1[com "Dired Previous File"] +These commands move to next or previous undeleted file. +@enddefcom + + +@subsection[Deleting Files] +@defcom[com "Dired Delete File and Down Line", bind (Dired: d)] +This command marks for deletion the file on the current line with the point and +moves point down a line. +@enddefcom + +@defcom[com "Dired Delete File with Pattern", bind (Dired: D)] +This command prompts for a name pattern that may contain at most one wildcard, +an asterisk, and marks for deletion all the names matching the pattern. +@enddefcom + +@defcom[com "Dired Delete File", bind (Dired: C-d)] +This command marks for deletion the file on the current line with the point +without moving the point. +@enddefcom + + +@subsection[Undeleting Files] +@defcom[com "Dired Undelete File and Down Line", bind (Dired: u)] +This command unmarks for deletion the file on the current line with the point +and moves point down a line. +@enddefcom + +@defcom[com "Dired Undelete File with Pattern", bind (Dired: U)] +This command prompts for a name pattern that may contain at most one wildcard, +an asterisk, and unmarks for deletion all the names matching the pattern. +@enddefcom + +@defcom[com "Dired Undelete File", bind (Dired: C-u)] +This command unmarks for deletion the file on the current line with the point +without moving the point. +@enddefcom + + +@subsection[Expunging and Quitting] +@defcom[com "Dired Expunge Files", bind (Dired: !)] +@defhvar1[var "Dired File Expunge Confirm", val {t}] +@defhvar1[var "Dired Directory Expunge Confirm", val {t}] +This command deletes files marked for deletion, asking the user for +confirmation once for all the files flagged. It recursively deletes any marked +directories, asking the user for confirmation once for all those marked. +@hid[Dired File Expunge Confirm] and @hid[Dired Directory Expunge Confirm] when +set to @nil individually inhibit the confirmation prompting for the appropriate +deleting. +@enddefcom + +@defcom[com "Dired Quit", bind (Dired: q)] +This command expunges any marked files or directories as if by @hid[Expunge +Dired Files] before deleting the @hid[Dired] buffer. +@enddefcom + + +@subsection[Copying Files] +@defcom[com "Dired Copy File", bind (Dired: c)] +This command prompts for a destination specification and copies the file on the +line with the point. When prompting, the current line's specification is the +default, which provides some convenience in supplying the destination. The +destination is either a directory specification or a file name, and when it is +the former, the source is copied into the directory under its current file name +and extension. +@enddefcom + +@defcom[com "Dired Copy with Wildcard", bind (Dired: C)] +This command prompts for a name pattern that may contain at most one wildcard, +an asterisk, and copies all the names matching the pattern. When prompting for +a destination, this provides the @hid[Dired] buffer's directory as a default. +The destination is either a directory specification or a file name with a +wildcard. When it is the former, all the source files are copied into the +directory under their current file names and extensions. When it is the later, +each sources file's substitution for the wildcard causing it to match the first +pattern replaces the wildcard in the destination pattern; for example, you +might want to copy @f["*.txt"] to @f["*.text"]. +@enddefcom + +@defhvar[var "Dired Copy File Confirm", val {t}] +@label[copy-confirm] +This variable controls interaction with the user when it is not obvious what +the copying process should do. This takes one of the following values: +@Begin[Description] +@true@\ +When the destination specification exists, the copying process stops and asks +the user if it should overwrite the destination. + +@nil@\ +The copying process always copies the source file to the destination +specification without interacting with the user. + +@kwd[update]@\ +When the destination specification exists, and its write date is newer than +the source's write date, then the copying process stops and asks the user if it +should overwrite the destination. +@End[Description] +@enddefhvar + + +@subsection[Renaming Files] +@defcom[com "Dired Rename File", bind (Dired: r)] +Rename the file or directory under the point +@enddefcom + +@defcom[com "Dired Rename with Wildcard", bind (Dired: R)] +Rename files that match a pattern containing ONE wildcard. +@enddefcom + +@defhvar[var "Dired Rename File Confirm", val {t}] +When non-nil, @hid[Dired] will query before clobbering an existing file. +@enddefhvar + + +@section[View Mode] +@hid[View] mode provides for scrolling through a file read-only, terminating +the buffer upon reaching the end. + +@defcom[com "View File"] +This command reads a file into a new buffer as if by "Visit File", but +read-only. Bindings exist for scrolling and backing up in a single key stroke. +@enddefcom + +@defcom[com "View Help", bind (View: ?)] +This command shows a help message for @hid[View] mode. +@enddefcom + +@defcom[com "View Edit File", bind (View: e)] +This commands makes a buffer in @hid[View] mode a normal editing buffer, +warning if the file exists in another buffer simultaneously. +@enddefcom + +@defcom[com "View Scroll Down", bind (View: Space)] +@defhvar1[var "View Scroll Deleting Buffer", val {t}] +This command scrolls the current window down through its buffer. If the end of +the file is visible, then this deletes the buffer if @hid[View Scroll Deleting +Buffer] is set. If the buffer is associated with a @hid[Dired] buffer, this +returns there instead of to the previous buffer. +@enddefcom + +@defcom[com "View Return", bind (View: ^)] +@defcom1[com "View Quit", bind (View: q)] +These commands invoke a function that returns to the buffer that created the +current buffer in @hid[View] mode. Sometimes this function does nothing, but +it is useful for returning to @hid[Dired] buffers and similar @hemlock +features. + +After invoking the viewing return function if there is one, @hid[View Quit] +deletes the buffer that is current when the user invokes it. +@enddefcom + +Also, bound in @hid[View] mode are the following commands: +@Begin[Description] +@binding[backspace], @binding[delete]@\Scrolls the window up. + +@binding[<]@\Goes to the beginning of the buffer. + +@binding[>]@\Goes to the end of the buffer. +@End[Description] + + +@section[Process Mode] +@Label[process] +@Index[shells] +@Index[processes] + +@hid[Process] mode allows the user to execute a Unix process within a @hemlock +buffer. These commands and default bindings cater to running Unix shells in +buffers. For example, @hid[Stop Buffer Subprocess] is bound to @binding[H-z] +to stop the process you are running in the shell instead of binding @hid[Stop +Main Process] to this key which would stop the shell itself. + +@defcom[com "Shell", bind (C-M-s)] +@defhvar1[var "Shell Utility", val {"/bin/csh"}] +@defhvar1[var "Shell Utility Switches", val {@nil}] +@defhvar1[var "Current Shell"] +@defhvar1[var "Ask about Old Shells"] +This command executes the process determined by the values of @hid(Shell +Utility) and @hid(Shell Utility Switches) in a new buffer named @f["Shell n"] +where @f["n"] is some distinguishing integer. + +@hid[Current Shell] is a @hemlock variable that holds to the current shell +buffer. When @hid[Shell] is invoked, if there is a @hid[Current Shell], the +command goes to that buffer. + +When there is no @hid[Current Shell], but shell buffers do exist, if @hid[Ask +about Old Shells] is set, the @hid[Shell] command prompts for one of them, +setting @hid[Current Shell] to the indicated shell, and goes to the buffer. + +Invoking @hid[Shell] with an argument forces the creation of a new shell +buffer. + +@hid[Shell Utility] is the string name of the process to execute. + +@hid[Shell Utility Switches] is a string containing the default command line +arguments to @hid[Shell Utility]. This is a string since the utility is +typically @f["/bin/csh"], and this string can contain I/O redirection and other +shell directives. +@enddefcom + +@defcom[com "Shell Command Line in Buffer"] +This command prompts for a buffer and a shell command line. It then runs a +shell, giving it the command line, in the buffer. +@enddefcom + +@defcom[com "Set Current Shell"] +This command sets the value of @hid[Current Shell]. +@enddefcom + +@defcom[com "Stop Main Process"] +This command stops the process running in the current buffer by sending a +@f[:SIGTSTP] to that process. With an argument, stops the process using +@f[:SIGSTOP]. +@enddefcom + +@defcom[com "Continue Main Process"] +If the process in the current buffer is stopped, this command continues it. +@enddefcom + +@defcom[com "Kill Main Process"] +@defhvar1[var "Kill Process Confirm", val {t}] +This command prompts for confirmation and kills the process running in the +current buffer. + +Setting this variable to @nil inhibits @hemlock's prompting for confirmation. +@enddefcom + +@defcom[com "Stop Buffer Subprocess", stuff (bound to @bf[H-z] in @hid[Process] mode)] +This command stops the foreground subprocess of the process in the current +buffer, similar to the effect of @binding[C-Z] in a shell. +@enddefcom + +@defcom[com "Kill Buffer Subprocess"] +This command kills the foreground subprocess of the process in the current +buffer. +@enddefcom + +@defcom[com "Interrupt Buffer Subprocess", stuff (bound to @bf[H-c] in @hid[Process] mode)] +This command interrupts the foreground subprocess of the process in the +current buffer, similar to the effect of @binding[C-C] in a shell. +@enddefcom + +@defcom[com "Quit Buffer Subprocess", stuff (bound to @bf[H-\] in @hid[Process] mode)] +This command dumps the core of the foreground subprocess of the processs in +the current buffer, similar to the effect of @binding[C-\] in a shell. +@enddefcom + +@defcom[com "Send EOF to Process", stuff (bound to @bf[H-d] in @hid[Process] mode)] +This command sends the end of file character to the process in the current +buffer, similar to the effect of @binding[C-D] in a shell. +@enddefcom + +@defcom[com "Confirm Process Input", stuff (bound to @bf[Return] in @hid[Process] mode)] +This command sends the text the user has inserted at the end of a process +buffer to the process in that buffer. Resulting output is inserted at the end +of the process buffer. +@enddefcom + +The user may edit process input using commands that are shared with +@hid[Typescript] mode, see section @ref[typescripts]. + + +@section[Bufed Mode] +@hemlock provides a mechanism for managing buffers as an itemized list. +@hid[Bufed] supports conveniently deleting several buffers at once, saving +them, going to one, etc., all in a key stroke. + +@defcom[com "Bufed", bind (C-x C-M-b)] +This command creates a list of buffers in a buffer supporting operations such +as deletion, saving, and selection. If there already is a @hid[Bufed] buffer, +this just goes to it. +@enddefcom + +@defcom[com "Bufed Help"] +This command pops up a display of @hid[Bufed] help. +@enddefcom + +@defcom[com "Bufed Delete", bind (Bufed: C-d, C-D, D, d)] +@defhvar1[var "Virtual Buffer Deletion", val {t}] +@defhvar1[var "Bufed Delete Confirm", val {t}] +@hid[Bufed Delete] deletes the buffer on the current line. + +When @hid[Virtual Buffer Deletion] is set, this merely flags the buffer for +deletion until @hid[Bufed Expunge] or @hid[Bufed Quit] executes. + +Whenever these commands actually delete a buffer, if @hid[Bufed Delete Confirm] +is set, then @hemlock prompts the user for permission; if more than one buffer +is flagged for deletion, this only prompts once. For each modified buffer, +@hemlock asks to save the buffer before deleting it. +@enddefcom + +@defcom[com "Bufed Undelete", bind (Bufed: U, u)] +This command undeletes the buffer on the current line. +@enddefcom + +@defcom[com "Bufed Expunge", bind (Bufed: !)] +This command expunges any buffers marked for deletion regarding @hid[Bufed +Delete Confirm]. +@enddefcom + +@defcom[com "Bufed Quit", bind (Bufed: q)] +This command kills the @hid[Bufed] buffer, expunging any buffers marked for +deletion. +@enddefcom + +@defcom[com "Bufed Goto", bind (Bufed: Space)] +This command selects the buffer on the current line, switching to it. +@enddefcom + +@defcom[com "Bufed Goto and Quit", bind (Bufed: S-leftdown)] +This command goes to the buffer under the pointer, quitting @hid[Bufed]. It +supplies a function for @hid[Generic Pointer Up] which is a no-op. +@enddefcom + +@defcom[com "Bufed Save File", bind (Bufed: s)] +This command saves the buffer on the current line. +@enddefcom + + +@section[Completion] +This is a minor mode that saves words greater than three characters in length, +allowing later completion of those words. This is very useful for the often +long identifiers used in Lisp programs. As you type a word, such as a Lisp +symbol when in @hid[Lisp] mode, and you progress to typing the third letter, +@hemlock displays a possible completion in the status line. You can then +rotate through the possible completions or type some more letters to narrow +down the possibilities. If you choose a completion, you can also rotate +through the possibilities in the buffer instead of in the status line. +Choosing a completion or inserting a character that delimits words moves the +word forward in the ring of possible completions, so the next time you enter +its initial characters, @hemlock will prefer it over less recently used +completions. + +@defcom[com "Completion Mode"] +This command toggles @hid[Completion] mode in the current buffer. +@enddefcom + +@defcom[com "Completion Self Insert"] +This command is like @hid[Self Insert], but it also checks for possible +completions displaying any result in the status line. This is bound to most of +the key-events with corresponding graphic characters. +@enddefcom + +@defcom[com "Completion Complete Word", bind (Completion: End)] +This command selects the currently displayed completion if there is one, +guessing the case of the inserted text as with @hid[Query Replace]. Invoking +this immediately in succession rotates through possible completions in the +buffer. If there is no currently displayed completion on a first invocation, +this tries to find a completion from text immediately before the point and +displays the completion if found. +@enddefcom + +@defcom[com "Completion Rotate Completions", bind (Completion: M-End)] +This command displays the next possible completion in the status line. If +there is no currently displayed completion, this tries to find a completion +from text immediately before the point and displays the completion if found. +@enddefcom + +@defcom[com "List Possible Completions"] +This command lists all the possible completions for the text immediately before +the point in a pop-up display. Sometimes this is more useful than rotating +through several completions to see if what you want is available. +@enddefcom + +@defhvar[var "Completion Bucket Size", val {20}] +Completions are stored in buckets determined by the first three letters of a +word. This variable limits the number of completions saved for each combination +of the first three letters of a word. If you have many identifier in some +module beginning with the same first three letters, you'll need increase this +variable to accommodate all the names. +@enddefhvar + + +@defcom[com "Save Completions"] +@defcom1[com "Read Completions"] +@defhvar1[var "Completion Database Filename", val {nil}] +@hid[Save Completions] writes the current completions to the file +@hid[Completion Database Filename]. It writes them, so @hid[Read Completions] +can read them back in preserving the most-recently-used order. If the user +supplies an argument, then this prompts for a pathname. + +@hid[Read Completions] reads completions saved in @hid[Completion Database +Filename]. It moves any current completions to a less-recently-used status, +and it removes any in a given bucket that exceed the limit @hid[Completion +Bucket Size]. +@enddefcom + +@defcom[com "Parse Buffer for Completions"] +This command passes over the current buffer putting each valid completion word +into the database. This is a good way of picking up many useful completions +upon visiting a new file for which there are no saved completions. +@enddefcom + + +@section[CAPS-LOCK Mode] + +@hid[CAPS-LOCK] is a minor mode in which @hemlock that inserts all alphabetic +characters as uppercase letters. + +@defcom[com "Caps Lock Mode"] +This command toggles @hid[CAPS-LOCK] mode for the current buffer; it is most +useful when bound to a key, so you can enter and leave @hid[CAPS-LOCK] mode +casually. +@enddefcom + +@defcom[com "Self Insert Caps Lock"] +This command inserts the uppercase version of the character corresponding to +the last key-event typed. +@enddefcom + + + +@section[Overwrite Mode] + +@hid[Overwrite] mode is a minor mode which is useful for creating figures and +tables out of text. In this mode, typing a key-event with a corresponding +graphic character replaces the character at the point instead of inserting the +character. @hid[Quoted Insert] can be used to insert characters normally. + +@defcom[com "Overwrite Mode"] +This command turns on @hid[Overwrite] mode in the current buffer. If it is +already on, then it is turned off. A positive argument turns @hid[Overwrite] +mode on, while zero or a negative argument turns it off. +@enddefcom + +@defcom[com "Self Overwrite"] +This command replaces the next character with the character corresponding to +the key-event used to invoke the command. After replacing the character, this +moves past it. If the next character is a tab, this first expands the tab into +the appropriate number of spaces, replacing just the next space character. +At the end of the line, it inserts the +character instead of clobbering the newline. + +This is bound to key-events with corresponding graphic characters in +@hid[Overwrite] mode. +@enddefcom + +@defcom[com "Overwrite Delete Previous Character", + stuff (bound to @bf[Delete] and @bf[Backspace] in @hid[Overwrite] mode)] +This command replaces the previous character with a space and moves backwards. +This deletes tabs and newlines. +@enddefcom + + +@section[Word Abbreviation] +@index[word abbreviation] +Word abbreviation provides a way to speed the typing of frequently used words +and phrases. When in @hid[Abbrev] mode, typing a word delimiter causes the +previous word to be replaced with its @i[expansion] if there is one currently +defined. The expansion for an abbrev may be any string, so this mode can be +used for abbreviating programming language constructs and other more obscure +uses. For example, @hid[Abbrev] mode can be used to automatically correct +common spelling mistakes and to enforce consistent capitalization of +identifiers in programs. + +@i[Abbrev] is an abbreviation for @i[abbreviation], which is used for +historical reasons. Obviously the original writer of @hid[Abbrev] mode hated +to type long words and could hardly use @hid[Abbrev] mode while writing +@hid[Abbrev] mode. + +A word abbrev can be either global or local to a major mode. A global word +abbrev is defined no matter what the current major mode is, while a mode word +abbrev is only defined when its mode is the major mode in the current buffer. +Mode word abbrevs can be used to prevent abbrev expansion in inappropriate +contexts. + + +@subsection[Basic Commands] + +@defcom[com "Abbrev Mode"] +This command turns on @hid[Abbrev] mode in the current buffer. If @hid[Abbrev] +mode is already on, it is turned off. @hid[Abbrev] mode must be on for the +automatic expansion of word abbrevs to occur, but the abbreviation commands are +bound globally and may be used at any time. +@enddefcom + +@defcom[com "Abbrev Expand Only", + stuff (bound to word-delimiters in @hid[Abbrev] mode)] +This is the word abbrev expansion command. If the word before the point is a +defined word abbrev, then it is replaced with its expansion. The replacement +is done using the same case-preserving heuristic as is used by +@hid[Query Replace]. This command is globally bound to @binding[M-Space] so +that abbrevs can be expanded when @hid[Abbrev] mode is off. An undesirable +expansion may be inhibited by using @binding[C-q] to insert the delimiter. +@enddefcom + +@defcom[com "Inverse Add Global Word Abbrev", bind (C-x -)] +@defcom1[com "Inverse Add Mode Word Abbrev", bind (C-x C-h, C-x Backspace)] +@hid[Inverse Add Global Word Abbrev] prompts for a string and makes it the +global word abbrev expansion for the word before the point. + +@hid[Inverse Add Mode Word Abbrev] is identical to +@hid[Inverse Add Global Word Abbrev] except that it defines an expansion which +is local to the current major mode. +@enddefcom + +@defcom[com "Make Word Abbrev"] +This command defines an arbitrary word abbreviation. It prompts for the mode, +abbreviation and expansion. If the mode @f["Global"] is specified, then it +makes a global abbrev. +@enddefcom + +@defcom[com "Add Global Word Abbrev", bind (C-x +)] +@defcom1[com "Add Mode Word Abbrev", bind (C-x C-a)] +@hid[Add Global Word Abbrev] prompts for a word and defines it to be a global +word abbreviation. The prefix argument determines which text is used as the +expansion: +@begin[description] +@i[no prefix argument]@\The word before the point is used as the expansion of +the abbreviation. + +@i[zero prefix argument]@\The text in the region is used as the expansion of the +abbreviation. + +@i[positive prefix argument]@\That many words before the point are made the +expansion of the abbreviation. + +@i[negative prefix argument]@\Do the same thing as +@hid[Delete Global Word Abbrev] instead of defining an abbreviation. +@end[description] + +@hid[Add Mode Word Abbrev] is identical to @hid[Add Global Word Abbrev] except +that it defines or deletes mode word abbrevs in the current major mode. +@enddefcom + +@defcom[com "Word Abbrev Prefix Mark", bind (M-")] +This command allows @hid[Abbrev Expand Only] to recognize abbreviations when +they have prefixes attached. First type the prefix, then use this command. A +hyphen (@f[-]) will be inserted in the buffer. Now type the abbreviation and +the word delimiter. @hid[Abbrev Expand Only] will expand the abbreviation and +remove the hyphen. + +Note that there is no need for a suffixing command, since +@hid[Abbrev Expand Only] may be used explicitly by typing @binding[M-Space]. +@enddefcom + +@defcom[com "Unexpand Last Word", bind (C-x u)] +This command undoes the last word abbrev expansion. If repeated, undoes its +own effect. +@enddefcom + + +@subsection[Word Abbrev Files] +A word abbrev file is a file which holds word abbrev definitions. Word abbrev +files allow abbrevs to be saved so that they may be used across many editing +sessions. + +@defhvar[var "Abbrev Pathname Defaults", val {(pathname "abbrev.defns")}] +This is sticky default for the following commands. When they prompt for a file +to write, they offer this and set it for the next time one of them executes. +@enddefhvar + +@defcom[com "Read Word Abbrev File"] +This command reads in a word abbrev file, adding all the definitions to those +currently defined. If a definition in the file is different from the current +one, the current definition is replaced. +@enddefcom + +@defcom[com "Write Word Abbrev File"] +This command prompts for a file and writes all currently defined word abbrevs +out to it. +@enddefcom + +@defcom[com "Append to Word Abbrev File"] +This command prompts for a word abbrev file and appends any new definitions to +it. An abbrev is new if it has been defined or redefined since the last use of +this command. Definitions made by reading word abbrev files are not +considered. +@enddefcom + + +@subsection[Listing Word Abbrevs] +@defcom[com "List Word Abbrevs"] +@defcom1[com "Word Abbrev Apropos"] +@hid[List Word Abbrevs] displays a list of each defined word abbrev, with its +mode and expansion. + +@hid[Word Abbrev Apropos] is similar, except that it only displays abbrevs +which contain a specified string, either in the definition, expansion or mode. +@enddefcom + +@subsection[Editing Word Abbrevs] +Word abbrev definition lists are edited by editing the text representation +of the definitions. Word abbrev files may be edited directly, like any other +text file. The set of abbrevs currently defined in @hemlock may be edited +using the commands described in this section. + +The text representation of a word abbrev is fairly simple. Each definition +begins at the beginning of a line. Each line has three fields which are +separated by ASCII tab characters. The fields are the abbreviation, the mode +of the abbreviation and the expansion. The mode is represented as the mode +name inside of parentheses. If the abbrev is global, then the mode field is +empty. The expansion is represented as a quoted string since it may contain +any character. The string is quoted with double-quotes (@f["]); double-quotes +in the expansion are represented by doubled double-quotes. The expansion may +contain newline characters, in which case the definition will take up more than +one line. + +@defcom[com "Edit Word Abbrevs"] +This command inserts the current word abbrev definitions into the +@hid[Edit Word Abbrevs] buffer and then enters a recursive edit on the buffer. +When the recursive edit is exited, the definitions in the buffer become the new +current abbrev definitions. +@enddefcom + +@defcom[com "Insert Word Abbrevs"] +This command inserts at the point the text representation of the currently +defined word abbrevs. +@enddefcom + +@defcom[com "Define Word Abbrevs"] +This command interprets the text of the current buffer as a word abbrev +definition list, adding all the definitions to those currently defined. +@enddefcom + + +@subsection[Deleting Word Abbrevs] +The user may delete word abbrevs either individually or collectively. +Individual abbrev deletion neutralizes single abbrevs which have outlived their +usefulness; collective deletion provides a clean slate from which to initiate +abbrev definitions. + +@defcom[com "Delete All Word Abbrevs"] +This command deletes all word abbrevs which are currently defined. +@enddefcom + +@defcom[com "Delete Global Word Abbrev"] +@defcom1[com "Delete Mode Word Abbrev"] +@hid[Delete Global Word Abbrev] prompts for a word abbreviation and deletes its +global definition. If given a prefix argument, deletes all global abbrev +definitions. + +@hid[Delete Mode Word Abbrev] is identical to @hid[Delete Global Word Abbrev] +except that it deletes definitions in the current major mode. +@enddefcom + + +@section[Lisp Library] +This is an implementation dependent feature. The Lisp library is a collection +of local hacks that users can submit and share that is maintained by the Lisp +group. These commands help peruse the catalog or description files and figure +out how to load the entries. + +@defcom[com "Lisp Library"] +This command finds all the library entries and lists them in a buffer. The +following commands describe and load those entries. +@enddefcom + +@defcom[com "Describe Library Entry", bind (Lisp-Lib: space)] +@defcom1[com "Describe Pointer Library Entry", bind (Lisp-Lib: leftdown)] +@defcom1[com "Load Library Entry", bind (Lisp-Lib: rightdown)] +@defcom1[com "Load Pointer Library Entry", bind (Lisp-Lib: l)] +@defcom1[com "Editor Load Library Entry"] +@defcom1[com "Editor Load Pointer Library Entry"] +@hid[Load Library Entry] and @hid[Load Pointer Library Entry] load the library +entry indicated by the line on which the point lies or where the user clicked +the pointer, respectively. These load the entry into the current slave Lisp. + +@hid[Editor Load Library Entry] and @hid[Editor Load Pointer Library Entry] are +the same, but they load the entry into the editor Lisp. +@enddefcom + +@defcom[com "Exit Lisp Library", bind (Lisp-Lib: q)] +This command deletes the @hid[Lisp Library] buffer. +@enddefcom + +@defcom[com "Lisp Library Help", bind (Lisp-Lib: ?)] +This command pops up a help window listing @hid[Lisp-Lib] commands. +@enddefcom diff --git a/docs/hem/user/user.mss b/docs/hem/user/user.mss new file mode 100644 index 0000000..b7c2bd6 --- /dev/null +++ b/docs/hem/user/user.mss @@ -0,0 +1,2003 @@ +@make[Manual] @comment{-*- Dictionary: /afs/cs/project/clisp/scribe/hem/hem; Mode: spell; Package: Hemlock -*-} +@Device[postscript] +@Style(Spacing = 1.2 lines) +@Style(StringMax = 5000) +@Use(Database "/afs/cs/project/clisp/docs/database/") +@Style(FontFamily=TimesRoman) +@Style(Date="March 1952") +@style(DOUBLESIDED) +@Libraryfile[ArpaCredit] +@libraryfile[hem] +@Libraryfile[Spice] +@Libraryfile[Uttir] + +@String(REPORTTITLE "Hemlock User's Manual") + +@comment<@begin[TitlePage] +@begin[TitleBox] +> +@blankspace(1.3inches) +@heading[Hemlock User's Manual] + +@center[@b +@b + + +@b<@value[date]> + +@b +] +@comment<@end[TitleBox]> +@blankspace(2lines) +@begin[Center] +School of Computer Science +Carnegie Mellon University +Pittsburgh, PA 15213 +@end[Center] +@blankspace[2lines] + +@begin[Center] +This is a revised version of Technical Report CMU-CS-87-158. +@end[Center] + +@heading +@begin(Text, indent 0) +This document describes the @Hemlock text editor, version M3.2. @Hemlock is a +customizable, extensible text editor whose initial command set closely +resembles that of ITS/TOPS-20 @Emacs. @Hemlock is written in CMU Common Lisp +and has been ported to other implementations. +@end(Text) + +@begin[ResearchCredit] +@ArpaCredit[Contract=Basic87-90] +@end[ResearchCredit] +@comment<@end[TitlePage]> + + +@commandstring(mh = "@f1(MH)") +@commandstring(dash = "@Y[M]") + +@comment[This tabclear is necessary since the definition macros don't + take care of the own tabbing needs] +@tabclear + + +@comment[@chap (Introduction)] +@include(intro) + + +@comment[@chap (Basic Commands)] +@include(commands) + + + +@chap[Files, Buffers, and Windows] + +@section[Introduction] + +@index[files] +@index[buffers] +@index[windows] +@hemlock provides three different abstractions which are used in combination to +solve the text-editing problem, while other editors tend to mash these ideas +together into two or even one. +@begin[description] +File@\A file provides permanent storage of text. @hemlock has commands +to read files into buffers and write buffers out into files. + +Buffer@\A buffer provides temporary storage of text and a capability to +edit it. A buffer may or may not have a file associated with it; if it +does, the text in the buffer need bear no particular relation to the text +in the file. In addition, text in a buffer may be displayed in any number +of windows, or may not be displayed at all. + +Window@\A window displays some portion of a buffer on the screen. There +may be any number of windows on the screen, each of which may display any +position in any buffer. It is thus possible, and often useful, to have +several windows displaying different places in the same buffer. +@end[description] + + +@section[Buffers] +In addition to some text, a buffer has several other user-visible attributes: +@begin[description] +A name@\ +A buffer is identified by its name, which allows it to be selected, destroyed, +or otherwise manipulated. + +A collection of modes@\ +The modes present in a buffer alter the set of commands available and +otherwise alter the behavior of the editor. For details see page +@pageref[modes]. + +A modification flag @\ +This flag is set whenever the text in a buffer is modified. It is often +useful to know whether a buffer has been changed, since if it has it should +probably be saved in its associated file eventually. + +A write-protect flag @\ +If this flag is true, then any attempt to modify the buffer will result in an +error. +@end[description] + +@defcom[com "Select Buffer", bind (C-x b)] +This command prompts for the name of a existing buffer and makes that buffer +the @i[current buffer]. The newly selected buffer is displayed in the +current window, and editing commands now edit the text in that buffer. +Each buffer has its own point, thus the point will be in the place it was +the last time the buffer was selected. When prompting for the buffer, the +default is the buffer that was selected before the current one. +@enddefcom + +@defcom[com "Select Previous Buffer", bind (C-M-l)] +@defcom1[com "Circulate Buffers", bind (C-M-L)] +With no prefix argument, @hid[Select Previous Buffer] selects the buffer that +has been selected most recently, similar to @binding[C-x b Return]. If given a +prefix argument, then it does the same thing as @hid[Circulate Buffers]. + +@hid[Circulate Buffers] moves back into successively earlier buffers in the +buffer history. If the previous command was not @hid[Circulate Buffers] or +@hid[Select Previous Buffer], then it does the same thing as +@hid[Select Previous Buffer], otherwise it moves to the next most recent +buffer. The original buffer at the start of the excursion is made the previous +buffer, so @hid[Select Previous Buffer] will always take you back to where you +started. + +These commands are generally used together. Often @hid[Select Previous Buffer] +will take you where you want to go. If you don't end up there, then using +@hid[Circulate Buffers] will do the trick. +@enddefcom + +@defcom[com "Create Buffer", bind (C-x M-b)] +This command is very similar to @hid[Select Buffer], but the buffer need not +already exist. If the buffer does not exist, a new empty buffer is created +with the specified name. +@enddefcom + +@defcom[com "Kill Buffer", bind (C-x k)] +This command is used to make a buffer go away. There is no way to restore +a buffer that has been accidentally deleted, so the user is given a chance +to save the hapless buffer if it has been modified. This command is poorly +named, since it has nothing to do with killing text. +@enddefcom + +@defcom[com "List Buffers", bind (C-x C-b)] +This command displays a list of all existing buffers in a pop-up window. A +"@f[*]" is displayed before the name of each modified buffer. A buffer with no +associated file is represented by the buffer name followed by the number of +lines in the buffer. A buffer with an associated file are is represented by +the name and type of the file, a space, and the device and directory. If the +buffer name doesn't match the associated file, then the buffer name is also +displayed. When given a prefix argument, this command lists only the modified +buffers. +@enddefcom + +@defcom[com "Buffer Not Modified", bind (M-~)] +This command resets the current buffer's modification flag @dash @i[it does not +save any changes]. This is primarily useful in cases where a user accidentally +modifies a buffer and then undoes the change. Resetting the modified flag +indicates that the buffer has no changes that need to be written out. +@enddefcom + +@defcom[com "Check Buffer Modified", bind (C-x ~)] +This command displays a message indicating whether the current buffer is modified. +@enddefcom + +@defcom[com "Set Buffer Read-Only"] +This command changes the flag that allows the current buffer to be modified. +If a buffer is read-only, any attempt to modify it will result in an error. The +buffer may be made writable again by repeating this command. +@enddefcom + +@defcom[com "Set Buffer Writable"] +This command ensures the current buffer is modifiable. +@enddefcom + +@defcom[com "Insert Buffer"] +This command prompts for the name of a buffer and inserts its contents at the +point, pushing a buffer mark before inserting. The buffer inserted is +unaffected. +@enddefcom + +@defcom[com "Rename Buffer"] +This command prompts for a new name for the current buffer, which defaults +to a name derived from the associated filename. +@enddefcom + + +@section[Files] +@index[files] +These commands either read a file into the current buffer or write it out to +some file. Various other bookkeeping operations are performed as well. + +@defcom[com "Find File", bind (C-x C-f)] +This is the command normally used to get a file into @hemlock. It prompts +for the name of a file, and if that file has already been read in, selects +that buffer; otherwise, it reads file into a new buffer whose name is +derived from the name of the file. If the file does not exist, then the +buffer is left empty, and @w<"@f[(New File)]"> is displayed in the echo area; +the file may then be created by saving the buffer. + +The buffer name created is in the form @w<"@i[name] @i[type] @i[directory]">. +This means that the filename "@f[/sys/emacs/teco.mid]" has +@w<"@f[Teco Mid /Sys/Emacs/]"> as its the corresponding buffer name. The +reason for rearranging the fields in this fashion is that it facilitates +recognition since the components most likely to differ are placed first. If +the buffer cannot be created because it already exists, but has another file in +it (an unlikely occurrence), then the user is prompted for the buffer to use, +as by @hid[Create Buffer]. + +@hid[Find File] takes special action if the file has been modified on disk +since it was read into @hemlock. This usually happens when several people are +simultaneously editing a file, an unhealthy circumstance. If the buffer is +unmodified, @hid[Find File] just asks for confirmation before reading in the +new version. If the buffer is modified, then @hid[Find File] beeps and prompts +for a single key-event to indicate what action to take. It recognizes +the following key-events: +@begin[description] +@binding[Return, Space, y]@\ + Prompt for a file in which to save the current buffer and then read in the +file found to be modified on disk. + +@binding[Delete, Backspace, n]@\ + Forego reading the file. + +@binding[r]@\ + Read the file found to be modified on disk into the buffer containing the +earlier version with modifications. This loses all changes you had in the +buffer. +@end[description] +@enddefcom + +@defcom[com "Save File", bind (C-x C-s)] +This command writes the current buffer out to its associated file and +resets the buffer modification flag. If there is no associated file, then +the user is prompted for a file, which is made the associated file. If +the buffer is not modified, then the user is asked whether to actually +write it or not. + +If the file has been modified on disk since the last time it was read, +@hid[Save File] prompts for confirmation before overwriting the file. +@enddefcom + +@defcom[com "Save All Files", bind (C-x C-m)] +@defcom1[com "Save All Files and Exit", bind (C-x M-z)] +@defhvar1[var "Save All Files Confirm", val {t}] +@hid[Save All Files] does a @hid[Save File] on all buffers which have an +associated file. @hid[Save All Files and Exit] does the same thing and then +exits @hemlock. + +When @hid[Save All Files Confirm] is true, these commands will ask for +confirmation before saving a file. +@enddefcom + +@defcom[com "Visit File", bind (C-x C-v)] +This command prompts for a file and reads it into the current buffer, +setting the associated filename. Since the old contents of the buffer are +destroyed, the user is given a chance to save the buffer if it is modified. +As for @hid[Find File], the file need not actually exist. This command warns +if some other buffer also contains the file. +@enddefcom + +@defcom[com "Write File", bind (C-x C-w)] This command prompts for a file +and writes the current buffer out to it, changing the associated filename +and resetting the modification flag. When the buffer's associated file is +specified this command does the same thing as @hid[Save File]. @enddefcom + +@defcom[com "Backup File"] +This command is similar to @hid[Write File], but it neither sets the +associated filename nor clears the modification flag. This is useful for +saving the current state somewhere else, perhaps on a reliable machine. + +Since @hid[Backup File] doesn't update the write date for the buffer, +@hid[Find File] and @hid[Save File] will get all upset if you back up +a buffer on any file that has been read into @hemlock. +@enddefcom + +@defcom[com "Revert File"] +@defhvar1[var "Revert File Confirm", val {t}] +This command replaces the text in the current buffer with the contents of the +associated file or the checkpoint file for that file, whichever is more recent. +The point is put in approximately the same place that it was before the file +was read. If the original file is reverted to, then clear the modified flag, +otherwise leave it set. If a prefix argument is specified, then always revert +to the original file, ignoring any checkpoint file. + +If the buffer is modified and @hid[Revert File Confirm] is true, then the user +is asked for confirmation. +@enddefcom + +@defcom[com "Insert File", bind (C-x C-r)] +This command prompts for a file and inserts it at the point, pushing a buffer +mark before inserting. +@enddefcom + +@defcom[com "Write Region"] +This command prompts for a file and writes the text in the region out to it. +@enddefcom + +@defhvar[var "Add Newline at EOF on Writing File", val {:ask-user}] +This variable controls whether some file writing commands add a newline at the +end of the file if the last line is non-empty. +@begin[description] +@f[:ask-user]@\Ask the user whether to add a newline. + +@f[t]@\Automatically add a newline and inform the user. + +@nil@\Never add a newline and do not ask. +@end[description] +Some programs will lose the text on the last line or get an +error when the last line does not have a newline at the end. +@enddefhvar + +@defhvar[var "Keep Backup Files", val {nil}] +Whenever a file is written by @hid[Save File] and similar commands, the old +file is renamed by appending "@f[.BAK]" to the name, ensuring that some version +of the file will survive a system crash during the write. If set to true, this +backup file will not deleted even when the write successfully completes. +@enddefhvar + + +@subsection[Auto Save Mode] + +@hid[Save] mode protects against loss of work in system crashes by periodically +saving modified buffers in checkpoint files. + +@defcom[com "Auto Save Mode"] +This command turns on @hid[Save] mode if it is not on, and turns off when it is +on. @hid[Save] mode is on by default. +@enddefcom + +@defhvar[var "Auto Save Checkpoint Frequency", val {120}] +@defhvar1[var "Auto Save Key Count Threshold", val {256}] +These variables determine how often modified buffers in @hid[Save] mode will be +checkpointed. Checkpointing is done after +@hid[Auto Save Checkpoint Frequency] seconds, or after +@hid[Auto Save Key Count Threshold] keystrokes that modify the buffer +(whichever comes first). Either kind of checkpointing may be disabled by +setting the corresponding variable to @nil. +@enddefhvar + +@defhvar[var "Auto Save Cleanup Checkpoints", val {t}] +If this variable is true, then any checkpoint file for a buffer will be deleted +when the buffer is successfully saved in its associated file. +@enddefhvar + +@defhvar[var "Auto Save Filename Pattern", val {"~A~A.CKP"}] +@defhvar1[var "Auto Save Pathname Hook", val {make-unique-save-pathname}] +These variables determine the naming of checkpoint files. +@hid[Auto Save Filename Pattern] is a format string used to name the checkpoint +files for buffers with associated files. Format is called with two arguments: +the directory and file namestrings of the associated file. + +@hid[Auto Save Pathname Hook] is a function called by @hid[Save] mode to get a +checkpoint pathname when there is no pathname associated with a buffer. It +should take a buffer as its argument and return either a pathname or @nil. If +a pathname is returned, then it is used as the name of the checkpoint file. If +the function returns @nil, or if the hook variable is @nil, then @hid[Save] +mode is turned off in the buffer. The default value for this variable returns +a pathname in the default directory of the form "@w<@f[save-]@i[number]>", +where @i[number] is a number used to make the file unique. +@enddefhvar + + +@subsection[Filename Defaulting and Merging] +@index[merging, filename] +@index[defaulting, filename] +@index[filename defaulting] +@label[merging] +@index[pathnames] +When @hemlock prompts for the name of a file, it always offers a default. +Except for a few commands that have their own defaults, filename defaults are +computed in a standard way. If it exists, the associated file for the current +buffer is used as the default, otherwise a more complex mechanism creates a +default. + +@defhvar[var "Pathname Defaults", val {(pathname "gazonk.del")}] +@defhvar1[var "Last Resort Pathname Defaults Function"] +@defhvar1[var "Last Resort Pathname Defaults", val {(pathname "gazonk")}] +These variables control the computation of default filename defaults when the +current buffer has no associated file. + +@hid[Pathname Defaults] holds a "sticky" filename default. Commands that +prompt for files set this to the file specified, and the value is used as a +basis for filename defaults. It is undesirable to offer the unmodified value +as a default, since it is usually the name of an existing file that we don't +want to overwrite. If the current buffer's name is all alphanumeric, then the +default is computed by substituting the buffer name for the the name portion of +@hid[Pathname Defaults]. Otherwise, the default is computed by calling +@hid[Last Resort Pathname Defaults Function] with the buffer as an argument. + +The default value of @hid[Last Resort Pathname Defaults Function] merges +@hid[Last Resort Pathname Defaults] with @hid[Pathname Defaults]. +Unlike @hid[Pathname Defaults], @hid[Last Resort Pathname Defaults] is not +modified by file commands, so setting it to a silly name ensures that real +files aren't inappropriately offered as defaults. +@enddefhvar + +When a default is present in the prompt for a file, @hemlock @i[merges] the +given input with the default filename. The semantics of merging, described in +the Common Lisp manual, is somewhat involved, but @hemlock has a few rules it +uses: +@begin[enumerate] +If @hemlock can find the user's input as a file on the @f["default:"] search +list, then it forgoes merging with the displayed default. Basically, the +system favors the files in your current working directory over those found by +merging with the defaults offered in the prompt. + +Merging comes in two flavors, just merge with the displayed default's directory +or just merge with the displayed default's @f[file-namestring]. If the user +only responds with a directory specification, without any name or type +information, then @hemlock merges the default's @f[file-namestring]. If the +user responds with any name or type information, then @hemlock only merges with +the default's directory. Specifying relative directories in this second +situation coordinates with the displayed defaults, not the current working +directory. +@end[enumerate] + + +@subsection[Type Hooks and File Options] +@index[mode comment] +@index[type hooks] +When a file is read either by @hid[Find File] or @hid[Visit File], @hemlock +attempts to guess the correct mode in which to put the buffer, based on the +file's @i[type] (the part of the filename after the last dot). Any default +action may be overridden by specifying the mode in the file's @i[file +options].@index[modes]@index[package] + +@label[file-options]@index[file options] +The user specifies file options with a special syntax on the first line of a +file. If the first line contains the string "@f[-*-]", then @hemlock +interprets the text between the first such occurrence and the second, which +must be contained in one line , as a list of @w{"@f<@i[option]: @i[value]>"} +pairs separated by semicolons. The following is a typical example: +@begin[programexample] +;;; -*- Mode: Lisp, Editor; Package: Hemlock -*- +@end[programexample] + +These options are currently defined: +@begin[description] +Dictionary@\The argument is the filename of a spelling dictionary associated +with this file. The handler for this option merges the argument with the +name of this file. See @comref[Set Buffer Spelling Dictionary]. + +Log@\The argument is the name of the change log file associated with this file +(see page @pageref[log-files]). The handler for this option merges the +argument with the name of this file. + +Mode@\The argument is a comma-separated list of the names of modes to turn on +in the buffer that the file is read into. + +Package@\The argument is the name of the package to be used for reading code in +the file. This is only meaningful for Lisp code (see page +@pageref[lisp-package].) + +Editor@\The handler for this option ignores its argument and turns on +@hid[Editor] mode (see @comref[Editor Mode]). + +@end[description] +If the option list contains no "@f[:]" then the entire string is used as +the name of the major mode for the buffer. + +@defcom[com "Process File Options"] +This command processes the file options in the current buffer as described +above. This is useful when the options have been changed or when a file is +created. +@enddefcom + + +@section[Windows] +@index[windows] + +@hemlock windows display a portion of a buffer's text. See the section on +@i[window groups], @ref[groups], for a discussion of managing windows on bitmap +device. + +@defcom[com "New Window", bind (C-x C-n)] +This command prompts users for a new window which they can place anywhere on +the screen. This window is in its own group. This only works with bitmap +devices. +@enddefcom + +@defcom[com "Split Window", bind (C-x 2)] +This command splits the current window roughly in half to make two windows. If +the current window is too small to be split, the command signals a user error. +@enddefcom + +@defcom[com "Next Window", bind (C-x n)] +@defcom1[com "Previous Window", bind (C-x p)] +These commands make the next or previous window the new current window, often +changing the current buffer in the process. When a window is created, it is +arbitrarily made the next window of the current window. The location of the +next window is, in general, unrelated to that of the current window. +@enddefcom + +@defcom[com "Delete Window", bind (C-x C-d, C-x d)] +@defcom1[com "Delete Next Window", bind (C-x 1)] +@hid[Delete Window] makes the current window go away, making the next window +current. @hid[Delete Next Window] deletes the next window, leaving the current +window unaffected. + +On bitmap devices, if there is only one window in the group, either command +deletes the group, making some window in another group the current window. If +there are no other groups, they signal a user error. +@enddefcom + +@defcom[com "Go to One Window"] +This command deletes all window groups leaving one with the @hid[Default +Initial Window X], @hid[Default Initial Window Y], @hid[Default Initial Window +Width], and @hid[Default Initial Window Height]. This remaining window +retains the contents of the current window. +@enddefcom + +@defcom[com "Line to Top of Window", bind (M-!)] +@defcom1[com "Line to Center of Window", bind (M-#)] +@index[scrolling]@hid[Line to Top of Window] scrolls the current window up +until the current line is at the top of the screen. + +@hid[Line to Center of Window] attempts to scroll the current window so that +the current line is vertically centered. +@enddefcom + +@defcom[com "Scroll Next Window Down", bind (C-M-v)] +@defcom1[com "Scroll Next Window Up", bind (C-M-V)] +These commands are the same as @hid[Scroll Window Up] and +@hid[Scroll Window Down] except that they operate on the next window. +@enddefcom + +@defcom[com "Refresh Screen", bind {C-l}] +This command refreshes all windows, which is useful if the screen got trashed, +centering the current window about the current line. When the user supplies a +positive argument, it scrolls that line to the top of the window. When the +argument is negative, the line that far from the bottom of the window is moved +to the bottom of the window. In either case when an argument is supplied, this +command only refreshes the current window. +@enddefcom + + +@chap[Editing Documents] +@index[documents, editing] +Although @hemlock is not dedicated to editing documents as word processing +systems are, it provides a number of commands for this purpose. If @hemlock is +used in conjunction with a text-formatting program, then its lack of complex +formatting commands is no liability. + + +@defcom[com "Text Mode"] +This commands puts the current buffer into "Text" mode. +@enddefcom + + +@section[Sentence Commands] +@index[sentence commands] +A sentence is defined as a sequence of characters ending with a period, +question mark or exclamation point, followed by either two spaces or a newline. +A sentence may also be terminated by the end of a paragraph. Any number of +closing delimiters, such as brackets or quotes, may be between the punctuation +and the whitespace. This somewhat complex definition of a sentence is used so +that periods in abbreviations are not misinterpreted as sentence ends. + +@defcom[com "Forward Sentence", bind {M-a}] +@defcom1[com "Backward Sentence", bind {M-e}] +@index[motion, sentence]@hid[Forward Sentence] moves the point forward +past the next sentence end. @hid[Backward Sentence] moves to the beginning +of the current sentence. A prefix argument may be used as a repeat count. +@enddefcom + +@defcom[com "Forward Kill Sentence", bind {M-k}] +@defcom1[com "Backward Kill Sentence", bind (C-x Delete, C-x Backspace)] +@index[killing, sentence]@hid[Forward Kill Sentence] kills text from the +point through to the end of the current sentence. @hid[Backward Kill Sentence] +kills from the point to the beginning of the current sentence. A +prefix argument may be used as a repeat count. +@enddefcom + +@defcom[com "Mark Sentence"] +This command puts the point at the beginning and the mark at the end of the +next or current sentence. +@enddefcom + + +@section[Paragraph Commands] + +@index[paragraph commands]A paragraph may be delimited by a blank line or a +line beginning with "@f[']" or "@f[.]", in which case the delimiting line is +not part of the paragraph. Other characters may be paragraph delimiters in +some modes. A line with at least one leading whitespace character may also +introduce a paragraph and is considered to be part of the paragraph. Any +fill-prefix which is present on a line is disregarded for the purpose of +locating a paragraph boundary. + +@defcom[com "Forward Paragraph", bind (@bf)] +@defcom1[com "Backward Paragraph", bind (M-[)] +@index[motion, paragraph]@index[paragraph, motion]@hid[Forward Paragraph] +moves to the end of the current or next paragraph. @hid[Backward Paragraph] +moves to the beginning of the current or previous paragraph. A prefix +argument may be used as a repeat count. +@enddefcom + +@defcom[com "Mark Paragraph", bind {M-h}] +This command puts the point at the beginning and the mark at the end of the +current paragraph. +@enddefcom + +@defhvar[var "Paragraph Delimiter Function", val {default-para-delim-function}] +This variable holds a function that takes a mark as its argument and returns +true when the line it points to should break the paragraph. +@enddefhvar + +@section[Filling] + +@index[filling]@index[formatting]Filling is a coarse text-formatting +process which attempts to make all the lines roughly the same length, but +doesn't vary the amount of space between words. Editing text may leave +lines with all sorts of strange lengths; filling this text will return it +to a moderately aesthetic form. + +@defcom[com "Set Fill Column", bind (C-x f)] +This command sets the fill column to the column that the point is currently at, +or the one specified by the absolute value of prefix argument, if it is +supplied. The fill column is the column past which no text is permitted to +extend. +@enddefcom + +@defcom[com "Set Fill Prefix", bind (C-x .)] +This command sets the fill prefix to the text from the beginning of the +current line to the point. The fill-prefix is a string which filling commands +leave at the beginning of every line filled. This feature is useful for +filling indented text or comments. +@enddefcom + +@defhvar[var "Fill Column", val {75}] +@defhvar1[var "Fill Prefix", val {nil}] +These variables hold the value of the fill prefix and fill column, thus +setting these variables will change the way filling is done. If +@hid[Fill Prefix] is @nil, then there is no fill prefix. +@enddefcom + +@defcom[com "Fill Paragraph", bind {M-q}] +@index[paragraph, filling]This command fills the text in the current or next +paragraph. The point is not moved. +@enddefcom + +@defcom[com "Fill Region", bind {M-g}] +@index[region, filling]This command fills the text in the region. Since +filling can mangle a large quantity of text, this command asks for confirmation +before filling a large region (see @hid[Region Query Size].) +@enddefcom + + +@defcom[com "Auto Fill Mode"] +@index[modes, auto fill]This command turns on or off the @hid[Fill] +minor mode in the current buffer. When in @hid[Fill] mode, @bf[Space], +@bf[Return] and @bf[Linefeed] are rebound to commands that check whether +the point is past the fill column and fill the current line if it is. +This enables typing text without having to break the lines manually. + +If a prefix argument is supplied, then instead of toggling, the sign +determines whether @hid[Fill] mode is turned off; a positive argument +argument turns in on, and a negative one turns it off. +@enddefcom + +@defcom[com "Auto Fill Linefeed", stuff (bound to @bf[Linefeed] in @hid[Fill] mode)] +@defcom1[com "Auto Fill Return", stuff (bound to @bf[Return] in @hid[Fill] mode)] +@hid[Auto Fill Linefeed] fills the current line if it needs it and then goes to +a new line and inserts the fill prefix. @hid[Auto Fill Return] is similar, but +does not insert the fill prefix on the new line. +@enddefcom + +@defcom[com "Auto Fill Space", stuff (bound to @bf[Space] in @hid[Fill] mode)] +If no prefix argument is supplied, this command inserts a space and +fills the current line if it extends past the fill column. If the argument is +zero, then it fills the line if needed, but does not insert a space. If the +argument is positive, then that many spaces are inserted without filling. +@enddefcom + +@defhvar[var "Auto Fill Space Indent", val {nil}] +This variable determines how lines are broken by the auto fill commands. If it +is true, new lines are created using the @hid[Indent New Comment Line] command, +otherwise the @hid[New Line] command is used. Language modes should define +this variable to be true so that auto fill mode can be used on code. +@enddefhvar + + +@section[Scribe Mode] + +@hid[Scribe] mode provides a number of facilities useful for editing Scribe +documents. It is also sufficiently parameterizable to be adapted to other +similar syntaxes. + +@defcom[com "Scribe Mode"] +@index[modes, scribe]This command puts the current buffer in @hid[Scribe] mode. +Except for special Scribe commands, the only difference between @hid[Scribe] +mode and @hid[Text] mode is that the rules for determining paragraph breaks are +different. In @hid[Scribe] mode, paragraphs delimited by Scribe commands are +normally placed on their own line, in addition to the normal paragraph breaks. +The main reason for doing this is that it prevents @hid[Fill Paragraph] from +mashing these commands into the body of a paragraph. +@enddefcom + +@defcom[com "Insert Scribe Directive", stuff (@bf[C-h] in @hid[Scribe] mode)] +This command prompts for a key-event to determine which Scribe directive to +insert. Directives are inserted differently depending on their kind: +@begin[description] +@i[environment]@\ +The current or next paragraph is enclosed in a begin-end pair: +@f<@@begin[@i{directive}]> @i[paragraph] @f<@@end[@i{directive}]>. If the +current region is active, then this command encloses the region instead of the +paragraph it would otherwise chose. + +@i[command]@\ +The previous word is enclosed by @f<@@@i[directive][@i[word]]>. If the +previous word is already enclosed by a use of the same command, then the +beginning of the command is extended backward by one word. +@end[description] + +Typing @bf[Home] or @bf[C-_] to this command's prompt will display a list of +all the defined key-events on which it dispatches. +@enddefcom + +@defcom[com "Add Scribe Directive"] +This command adds to the database of directives recognized by the +@hid[Insert Scribe Directive] command. It prompts for the directive's name, +the kind of directive (environment or command) and the key-event on which to +dispatch. +@enddefcom + +@defcom[com "Add Scribe Paragraph Delimiter"] +@defcom1[com "List Scribe Paragraph Delimiters"] +@hid[Add Scribe Paragraph Delimiter] prompts for a string to add to the list of +formatting commands that delimit paragraphs in @hid[Scribe] mode. If the user +supplies a prefix argument, then this command removes the string as a +delimiter. + +@hid[List Scribe Paragraph Delimiters] displays in a pop-up window the Scribe +commands that delimit paragraphs. +@enddefcom + +@defhvar[var "Escape Character", val {#\@@}] +@defhvar1[var "Close Paren Character", val {#\]}] +@defhvar1[var "Open Paren Character", val {#\[}] +These variables determine the characters used when a Scribe directive is +inserted. +@enddefhvar + +@defcom[com "Scribe Insert Bracket"] +@defhvar1[var "Scribe Bracket Table"] +@hid[Scribe Insert Bracket] inserts a bracket (@bf[>], @bf[}], @bf[)], or +@bf<]>), that caused its invocation, and then shows the matching bracket. + +@hid[Scribe Bracket Table] holds a @f[simple-vector] indexed by character +codes. If a character is a bracket, then the entry for its @f[char-code] +should be the opposite bracket. If a character is not a bracket, then the +entry should be @nil. +@enddefcom + + +@section[Spelling Correction] +@index[spelling correction] +@hemlock has a spelling correction facility based on the dictionary for the ITS +spell program. This dictionary is fairly small, having only 45,000 word or so, +which means it fits on your disk, but it also means that many reasonably common +words are not in the dictionary. A correct spelling for a misspelled word will +be found if the word is in the dictionary and is only erroneous in that it has +a wrong character, a missing character, an extra character or a transposition +of two characters. + + +@defcom[com "Check Word Spelling", bind (M-$)] +This command looks up the previous or current word in the dictionary and +attempts to correct the spelling if it is misspelled. There are four possible +results of invoking this command: +@begin[enumerate] +This command displays the message "@f[Found it.]" in the echo area. This means +it found the word in the dictionary exactly as given. + +This command displays the message "@f[Found it because of @i[word].]", where +@i[word] is some other word with the same root but a different ending. The +word is no less correct than if the first message is given, but an additional +piece of useless information is supplied to make you feel like you are using a +computer. + +The command prompts with "@f[Correction choice:]" in the echo area and lists +possible correct spellings associated with numbers in a pop-up display. Typing +a number selects the corresponding correction, and the command replaces the +erroneous word, preserving case as though by @hid[Query Replace]. Typing +anything else rejects all the choices. + +This commands displays the message "@f[Word not found.]". The word is not in +the dictionary and possibly spelled correctly anyway. Furthermore, no +similarly spelled words were found to offer as possible corrections. If this +happens, it is worth trying some alternate spellings since one of them might +be close enough to some known words that this command could display. +@end[enumerate] +@enddefcom + +@defcom[com "Correct Buffer Spelling"] +This command scans the entire buffer looking for misspelled words and offers to +correct them. It creates a window into the @hid[Spell Corrections] buffer, and +in this buffer it maintains a log of any actions taken by the user. When this +finds an unknown word, it prompts for a key-event. The user has the following +options: +@begin[description] +@bf[a]@\ + Ignore this word. If the command finds the word again, it will prompt again. + +@bf[i]@\ + Insert this word in the dictionary. + +@bf[c]@\ + Choose one of the corrections displayed in the @hid[Spell Corrections] window +by specifying the correction number. If the same misspelling is encountered +again, then the command will make the same correction automatically, leaving a +note in the log window. + +@bf[r]@\ + Prompt for a word to use instead of the misspelled one, remembering the +correction as with @bf[c]. + +@binding[C-r]@\ + Go into a recursive edit at the current position, and resume checking when the +recursive edit is exited. +@end[description] +After this command completes, it deletes the log window leaving the buffer +around for future reference. +@enddefcom + +@defhvar[var "Spell Ignore Uppercase", val {nil}] +@index[case sensitivity] +If this variable is true, then @hid[Auto Check Word Spelling] and @hid[Correct +Buffer Spelling] will ignore unknown words that are all uppercase. This is +useful for acronyms and cryptic formatter directives. +@enddefhvar + +@defcom[com "Add Word to Spelling Dictionary", bind (C-x $)] +This command adds the previous or current word to the spelling dictionary. +@enddefcom + +@defcom[com "Remove Word from Spelling Dictionary"] +This command prompts for a word to remove from the spelling dictionary. Due to +the dictionary representation, removal of a word in the initial spelling +dictionary will remove all words with the same root. The user is asked for +confirmation before removing a root word with valid suffix flags. +@enddefcom + +@defcom[com "List Incremental Spelling Insertions"] +This command displays the incremental spelling insertions for the current +buffer's associated spelling dictionary file. +@enddefcom + +@defcom[com "Read Spelling Dictionary"] +This command adds some words from a file to the spelling dictionary. The +format of the file is a list of words, one on each line. +@enddefcom + +@defcom[com "Save Incremental Spelling Insertions"] +This command appends incremental dictionary insertions to a file. Any words +added to the dictionary since the last time this was done will be appended to +the file. Except for @hid[Augment Spelling Dictionary], all the commands that +add words to the dictionary put their insertions in this list. The file is +prompted for unless @hid[Set Buffer Spelling Dictionary] has been executed in +the buffer. +@enddefcom + +@defcom[com "Set Buffer Spelling Dictionary"] +This command Prompts for the dictionary file to associate with the current +buffer. If the specified dictionary file has not been read for any other +buffer, then it is read. Incremental spelling insertions from this buffer +can be appended to this file with @hid[Save Incremental Spelling +Insertions]. If a buffer has an associated spelling dictionary, then +saving the buffer's associated file also saves any incremental dictionary +insertions. The @w<"@f[Dictionary: ]@i[file]"> file option may also be +used to specify the dictionary for a buffer (see section +@ref[file-options]). +@enddefcom + +@defhvar[var "Default User Spelling Dictionary", val {nil}] +This variable holds the pathname of a dictionary to read the first time +@hid[Spell] mode is entered in a given editing session. When +@hid[Set Buffer Spelling Dictionary] or the "@f[dictionary]" file option is +used to specify a dictionary, this default one is read also. It defaults to +nil. +@enddefhvar + + +@subsection[Auto Spell Mode] +@hid[Auto Spell Mode] checks the spelling of each word as it is typed. +When an unknown word is typed the user is notified and allowed to take a +number of actions to correct the word. + +@defcom[com "Auto Spell Mode"] +This command turns @hid[Spell] mode on or off in the current buffer. +@enddefcom + +@defcom[com "Auto Check Word Spelling", + stuff (bound to word delimiters in @hid[Spell] mode)] +@defhvar1[var "Check Word Spelling Beep", val {t}] +@defhvar1[var "Correct Unique Spelling Immediately", val {t}] +This command checks the spelling of the word before the point, doing nothing if +the word is in the dictionary. If the word is misspelled but has a known +correction previously supplied by the user, then this command corrects the +spelling. If there is no correction, then this displays a message in the echo +area indicating the word is unknown. An unknown word detected by this command +may be corrected using the @hid[Correct Last Misspelled Word] command. This +command executes in addition to others bound to the same key; for example, if +@hid[Fill] mode is on, any of its commands bound to the same keys as this +command also run. + +If @hid[Check Word Spelling Beep] is true, then this command will beep when an +unknown word is found. If @hid[Correct Unique Spelling Immediately] is true, +then this command will immediately attempt to correct any unknown word, +automatically making the correction if there is only one possible. +@enddefhvar + +@defcom[com "Undo Last Spelling Correction", bind (C-x a)] +@defhvar1[var "Spelling Un-Correct Prompt for Insert", val {nil}] +@hid[Undo Last Spelling Correction] undoes the last incremental spelling +correction. The "correction" is replaced with the old word, and the old word +is inserted in the dictionary. Any automatic replacement for the old word is +eliminated. When @hid[Spelling Un-Correct Prompt for Insert] is true, the user +is asked to confirm the insertion into the dictionary. +@enddefcom + +@defcom[com "Correct Last Misspelled Word", bind (M-:)] +This command places the cursor after the last misspelled word detected by the +@hid[Auto Check Word Spelling] command and then prompts for a key-event on +which it dispatches: +@begin[description] +@bf[c]@\ + Display possible corrections in a pop-up window, and prompt for the one to +make according to the corresponding displayed digit or letter. + +@i[any digit]@\ + Similar to @bf[c] @i[digit], but immediately makes the correction, dispensing +with display of the possible corrections. This is shorter, but only works when +there are less than ten corrections. + +@bf[i]@\ + Insert the word in the dictionary. + +@bf[r]@\ + Replace the word with another. + +@binding[Backspace, Delete, n]@\ + Skip this word and try again on the next most recently misspelled word. + +@binding[C-r]@\ + Enter a recursive edit at the word, exiting the command when the recursive +edit is exited. + +@binding[Escape]@\ + Exit and forget about this word. +@end[description] +As in @hid[Correct Buffer Spelling], the @bf[c] and @bf[r] commands add the +correction to the known corrections. +@enddefcom + + + +@chap[Managing Large Systems] + +@hemlock provides three tools which help to manage large systems: +@begin[enumerate] +File groups, which provide several commands that operate on all the files +in a possibly large collection, instead of merely on a single buffer. + +A source comparison facility with semi-automatic merging, which can be used +to compare and merge divergent versions of a source file. + +A change log facility, which maintains a single file containing a record of the +edits done on a system. +@end[enumerate] + + +@section[File Groups] + +@index[file groups]@index[searching, group]@index[replacing, group] +A file group is a set of files, upon which various editing operations can be +performed. The files in a group are specified by a file in the following +format: +@begin[itemize] +Any line which begins with one "@f[@@]" is ignored. + +Any line which does not begin with an "@f[@@]" is the name of a file in the +group. + +A line which begins with "@f[@@@@]" specifies another file having this +syntax, which is recursively examined to find more files in the group. +@end[itemize] +This syntax is used for historical reasons. Although any number of file groups +may be read into @hemlock, there is only one @i[active group], which is the +file group implicitly used by all of the file group commands. +Page @pageref[compile-group-command] describes the @hid[Compile Group] command. + +@defcom[com "Select Group"] +This command prompts for the name of a file group to make the active group. +If the name entered is not the name of a group whose definition has been +read, then the user is prompted for the name of a file to read the group +definition from. The name of the default pathname is the name of the +group, and the type is "@f[upd]". +@enddefcom + +@defcom[com "Group Query Replace"] +This command prompts for target and replacement strings and then executes an +interactive string replace on each file in the active group. This reads in +each file as if @hid[Find File] were used and processes it as if @hid[Query +Replace] were executing. +@enddefcom + +@defcom[com "Group Replace"] +This is like @hid[Group Query Replace] except that it executes a +non-interactive replacement, similar to @hid[Replace String]. +@enddefcom + +@defcom[com "Group Search"] +This command prompts for a string and then searches for it in each file in the +active group. This reads in each file as if @hid[Find File] were used. When +it finds an occurrence, it prompts the user for a key-event indicating what +action to take. The following commands are defined: +@begin[description] +@binding[Escape, Space, y]@\ + Exit @hid[Group Search]. + +@binding[Delete, Backspace, n]@\ + Continue searching for the next occurrence of the string. + +@binding[!]@\ + Continue the search at the beginning of the next file, skipping the remainder +of the current file. + +@binding[C-r]@\ + Go into a recursive edit at the current location, and continue the search when +it is exited. +@end[description] +@enddefcom + +@defhvar[var "Group Find File", val {nil}] +The group searching and replacing commands read each file into its own buffer +using @hid[Find File]. Since this may result in large amounts of memory being +consumed by unwanted buffers, this variable controls whether to delete the +buffer after processing it. When this variable is false, the default, the +commands delete the buffer if it did not previously exist; however, regardless +of this variable, if the user leaves the buffer modified, the commands will not +delete it. +@enddefhvar + +@defhvar[var "Group Save File Confirm", val {t}] +If this variable is true, the group searching and replacing commands ask for +confirmation before saving any modified file. The commands attempt to save +each file processed before going on to the next one in the group. +@enddefhvar + + +@section[Source Comparison] +@index[buffer, merging] +@index[buffer, comparison] +@index[source comparison] + +These commands can be used to find exactly how the text in two buffers differs, +and to generate a new version that combines features of both versions. + +@defhvar[var "Source Compare Default Destination", val {"Differences"}] +This is a sticky default buffer name to offer when comparison commands prompt +for a buffer in which to insert the results. +@enddefhvar + +@defcom[com "Compare Buffers"] +This command prompts for three buffers and then does a buffer comparison. +The first two buffers must exist, as they are the buffers to be compared. +The last buffer, which is created if it does not exist, is the buffer to +which output is directed. The output buffer is selected during the +comparison so that its progress can be monitored. There are various variables +that control exactly how the comparison is done. + +If a prefix argument is specified, then only only the lines in the the regions +of the two buffers are compared. +@enddefcom + +@defcom[com "Buffer Changes"] +This command compares the contents of the current buffer with the disk version +of the associated file. It reads the file into the buffer +@hid[Buffer Changes File], and generates the comparison in the buffer +@hid[Buffer Changes Result]. As with @hid[Compare Buffers], the output buffer +is displayed in the current window. +@enddefcom + +@defcom[com "Merge Buffers"] +This command functions in a very similar fashion to @hid[Compare Buffers], the +difference being that a version which is a combination of the two buffers being +compared is generated in the output buffer. This copies text that is identical +in the two comparison buffers to the output buffer. When it encounters a +difference, it displays the two differing sections in the output buffer and +prompts the user for a key-event indicating what action to take. The following +commands are defined: +@begin[description] +@bf[1]@\ + Use the first version of the text. + +@bf[2]@\ + Use the second version. + +@bf[b]@\ + Insert the string @w<"@f[**** MERGE LOSSAGE ****]"> followed by both versions. +This is useful if the differing sections are too complex, or it is unclear +which is the correct version. If you cannot make the decision conveniently at +this point, you can later search for the marking string above. + +@binding[C-r]@\ + Do a recursive edit and ask again when the edit is exited. +@end[description] +@enddefcom + + +@defhvar[var "Source Compare Ignore Case", val {nil}] +@index[case sensitivity] +If this variable is non-@nil, @hid[Compare Buffers] and @hid[Merge Buffers] +will do comparisons case-insensitively. +@enddefhvar + +@defhvar[var "Source Compare Ignore Indentation", val {nil}] +If this variable is non-@nil, @hid[Compare Buffers] and @hid[Merge Buffers] +ignore initial whitespace when comparing lines. +@enddefhvar + +@defhvar[var "Source Compare Ignore Extra Newlines", val {t}] +If this variable is true, @hid[Compare Buffers] and @hid[Merge Buffers] +will treat all groups of newlines as if they were a single newline. +@enddefhvar + +@defhvar[var "Source Compare Number of Lines", val {3}] +This variable controls the number of lines @hid[Compare Buffers] and +@hid[Merge Buffers] will compare when resynchronizing after a difference +has been encountered. +@enddefhvar + + +@section[Change Logs] +@label[log-files] +@index[edit history] +@index[change log] + +The @hemlock change log facility encourages the recording of changes to a +system by making it easy to do so. The change log is kept in a separate file +so that it doesn't clutter up the source code. The name of the log for a file +is specified by the @f[Log] file option (see page @pageref[file-options].) + +@defcom[com "Log Change"] +@defhvar1[var "Log Entry Template"] +@hid[Log Change] makes a new entry in the change log associated with the file. +Any changes in the current buffer are saved, and the associated log file is +read into its own buffer. The name of the log file is determined by merging +the name specified in the @f[Log] option with the current buffer's file name, +so it is not usually necessary to put the full name there. After inserting a +template for the log entry at the beginning of the buffer, the command enters a +recursive edit (see page @pageref[recursive-edits]) so that the text of the +entry may be filled in. When the user exits the recursive edit, the log file +is saved. + +The variable @hid[Log Entry Template] determines the format of the change log +entry. Its value is a @clisp @f[format] control string. The format string is +passed three string arguments: the full name of the file, the creation date for +the file and the name of the file author. If the creation date is not +available, the current date is used. If the author is not available then @nil +is passed. If there is an @f[@@] in the template, then it is deleted and the +point is left at that position. +@enddefcom + + + +@comment[@chap (Special Modes)] +@include(special-modes) + + + +@chap[Editing Programs] + + +@section[Comment Manipulation] +@index[comment manipulation] +@hemlock has commenting commands which can be used in almost any language. The +behavior of these commands is determined by several @hemlock variables which +language modes should define appropriately. + +@defcom[com "Indent for Comment", bind (M-;)] +@index[indentation, comment]@label[comment-indentation] +This is the most basic commenting command. If there is already a comment on +the current line, then this moves the point to the start of the comment. If +there no comment, this creates an empty one. + +This command normally indents the comment to start at @hid[Comment Column]. +The comment indents differently in the following cases: +@begin[enumerate] +If the comment currently starts at the beginning of the line, or if the last +character in the @hid[Comment Start] appears three times, then the comment +remains unmoved. + +If the last character in the @hid[Comment Start] appears two times, then the +comment is indented like a line of code. + +If text on the line prevents the comment occurring in the desired position, +this places the comment at the end of the line, separated from the text by a +space. +@end[enumerate] +Although the rules about replication in the comment start are oriented toward +Lisp commenting styles, you can exploit these properties in other languages. + +When given a prefix argument, this command indents any existing comment on that +many consecutive lines. This is useful for fixing up the indentation of a +group of comments. +@enddefcom + +@defcom[com "Indent New Comment Line", bind {M-j, M-Linefeed}] +This commend ends the current comment and starts a new comment on a blank line, +indenting the comment the same way that @hid[Indent for Comment] does. +When not in a comment, this command is the same as @hid[Indent New Line]. +@enddefcom + +@defcom[com "Up Comment Line", bind {M-p}] +@defcom1[com "Down Comment Line", bind {M-n}] +These commands are similar to @hid[Previous Line] or @hid[Next Line] +followed by @hid[Indent for Comment]. Any empty comment on the current line is +deleted before moving to the new line. +@enddefcom + +@defcom[com "Kill Comment", bind (C-M-;)] +This command kills any comment on the current line. When given a prefix +argument, it kills comments on that many consecutive lines. @hid[Undo] will +restore the unmodified text. +@enddefcom + +@defcom[com "Set Comment Column", bind (C-x ;)] +This command sets the comment column to its prefix argument. If used without a +prefix argument, it sets the comment column to the column the point is at. +@enddefcom + +@defhvar[var "Comment Start", val {nil}] +@defhvar1[var "Comment End", val {nil}] +@defhvar1[var "Comment Begin", val {nil}] +@defhvar1[var "Comment Column", val {0}] +These variables determine the behavior of the comment commands. +@begin[description] +@hid[Comment Start]@\The string which indicates the start of a comment. If +this is @nil, then there is no defined comment syntax. + +@hid[Comment End]@\The string which ends a comment. If this is @nil, then +the comment is terminated by the end of the line. + +@hid[Comment Begin]@\The string inserted to begin a new comment. + +@hid[Comment Column]@\The column that normal comments start at. +@end[description] +@enddefcom + + +@section[Indentation] +@label[indentation] +@index[indentation] +Nearly all programming languages have conventions for indentation or leading +whitespace at the beginning of lines. The @hemlock indentation facility is +integrated into the command set so that it interacts well with other features +such as filling and commenting. + +@defcom[com "Indent", bind (Tab, C-i)] +This command indents the current line. With a prefix argument, indents that +many lines and moves down. Exactly what constitutes indentation depends on the +current mode (see @hid[Indent Function]). +@enddefcom + +@defcom[com "Indent New Line", bind (Linefeed)] +This command starts a new indented line. Deletes any whitespace before the +point and inserts indentation on a blank line. The effect of this is similar +to @binding[Return] followed by @binding[Tab]. The prefix argument is passed +to @hid[New Line], which is used to insert the blank line. +@enddefcom + +@defcom[com "Indent Region", bind (C-M-\)] +This command indents every line in the region. It may be undone with +@hid[Undo]. +@enddefcom + +@defcom[com "Back to Indentation", bind {M-m, C-M-m}] +@index[motion, indentation] +This command moves point to the first non-whitespace character on the current +line. +@enddefcom + +@defcom[com "Delete Indentation", bind (M-^, C-M-^)] +@hid[Delete Indentation] joins the current line with the previous one, deleting +excess whitespace. This operation is the inverse of the @bf[Linefeed] command +in most modes. Usually this leaves one space between the two joined lines, but +there are several exceptions. + +The non-whitespace immediately surrounding the deleted line break determine the +amount of space inserted. +@begin[enumerate] +If the preceding character is an "@f[(]" or the following character is a +"@f[)]", then this inserts no space. + +If the preceding character is a newline, then this inserts no space. This will +happen if the previous line was blank. + +If the preceding character is a sentence terminator, then this inserts two +spaces. +@end[enumerate] + +When given a prefix argument, this command joins the current and next lines, +rather than the previous and current lines. +@enddefcom + +@defcom[com "Quote Tab", bind (M-Tab)] +This command inserts a tab character. +@enddefcom + +@defcom[com "Indent Rigidly", bind (C-x Tab, C-x C-i)] +This command changes the indentation of all the lines in the region. Each +line is moved to the right by the number of spaces specified by the prefix +argument, which defaults to eight. A negative prefix argument moves lines +left. +@enddefcom + +@defcom[com "Center Line"] +This indents the current line so that it is centered between the left margin +and @hvarref[Fill Column]. If a prefix argument is supplied, then it is used +as the width instead of @hid[Fill Column]. +@enddefcom + +@defhvar[var "Indent Function", val {tab-to-tab-stop}] +The value of this variable determines how indentation is done, and it is a +function which is passed a mark as its argument. The function should indent +the line which the mark points to. The function may move the mark around on +the line. The mark will be @f[:left-inserting]. The default simply inserts a +tab character at the mark. +@enddefhvar + +@defhvar[var "Indent with Tabs", val {indent-using-tabs}] +@defhvar1[var "Spaces per Tab", val {8}] +@hid[Indent with Tabs] holds a function that takes a mark and a number of +spaces. The function will insert a maximum number of tabs and a minimum number +of spaces at mark to move the specified number of columns. The default +definition uses @hid[Spaces per Tab] to determine the size of a tab. @i[Note,] +@hid[Spaces per Tab] @i[is not used everywhere in @hemlock yet, so changing +this variable could have unexpected results.] +@enddefhvar + + +@section[Language Modes] + +@hemlock@comment{}'s language modes are currently fairly crude, but probably +provide better programming support than most non-extensible editors. + +@defcom[com "Pascal Mode"] +@index[indentation, pascal]@index[modes, pascal]This command sets the current +buffer's major mode to @hid[Pascal]. @hid[Pascal] mode borrows parenthesis +matching from Scribe mode and indents lines under the previous line. +@enddefcom + + +@chap[Editing Lisp] +@index[lisp, editing] +@hemlock provides a large number of powerful commands for editing Lisp code. +It is possible for a text editor to provide a much higher level of support for +editing Lisp than ordinary programming languages, since its syntax is much +simpler. + + +@section[Lisp Mode] +@index[lisp mode] +@index[modes, lisp] +@hid[Lisp] mode is a major mode used for editing Lisp code. Although most +Lisp specific commands are globally bound, @hid[Lisp] mode is necessary to +enable Lisp indentation, commenting, and parenthesis-matching. Whenever the +user or some @hemlock mechanism turns on @hid[Lisp] mode, the mode's setup +includes locally setting @hid[Current Package] (see section @ref[lisp-package]) +in that buffer if its value is non-existent there; the value used is +@f["USER"]. + +@defcom[com "Lisp Mode"] +This command sets the major mode of the current buffer to @hid[Lisp]. +@enddefcom + + +@section[Form Manipulation] +@index[form manipulation] +These commands manipulate Lisp forms, the printed representations of Lisp +objects. A form is either an expression balanced with respect to parentheses +or an atom such as a symbol or string. + +@defcom[com "Forward Form", bind (C-M-f)] +@defcom1[com "Backward Form", bind (C-M-b)] +@index[motion, form]@hid[Forward Form] moves to the end of the current or +next form, while @hid[Backward Form] moves to the beginning of the current +or previous form. A prefix argument is treated as a repeat count. +@enddefcom + +@defcom[com "Forward Kill Form", bind (C-M-k)] +@defcom1[com "Backward Kill Form", bind (C-M-Delete, C-M-Backspace)] +@index[killing, form]@hid[Forward Kill Form] kills text from the point to +the end of the current form. If at the end of a list, but inside the close +parenthesis, then kill the close parenthesis. @hid[Backward Kill Form] is +the same, except it goes in the other direction. A prefix argument is +treated as a repeat count. +@enddefcom + +@defcom[com "Mark Form", bind (C-M-@@)] +This command sets the mark at the end of the current or next form. +@enddefcom + +@defcom[com "Transpose Forms", bind (C-M-t)] +This command transposes the forms before and after the point and moves +forward. A prefix argument is treated as a repeat count. If the prefix +argument is negative, then the point is moved backward after the +transposition is done, reversing the effect of the equivalent positive +argument. +@enddefcom + +@defcom[com "Insert ()", bind {M-(}] +This command inserts an open and a close parenthesis, leaving the point +inside the open parenthesis. If a prefix argument is supplied, then the +close parenthesis is put at the end of the form that many forms from the +point. +@enddefcom + +@defcom[com "Extract Form"] +This command replaces the current containing list with the next form. The +entire affected area is pushed onto the kill ring. If an argument is supplied, +that many upward levels of list nesting is replaced by the next form. This is +similar to @hid[Extract List], but this command is more generally useful since +it works on any kind of form; it is also more intuitive since it operates on +the next form as many @hid[Lisp] mode commands do. +@enddefcom + + +@section[List Manipulation] + +@index[list manipulation]List commands are similar to form commands, but +they only pay attention to lists, ignoring any atomic objects that may +appear. These commands are useful because they can skip over many symbols +and move up and down in the list structure. + +@defcom[com "Forward List", bind (C-M-n)] +@defcom1[com "Backward List", bind (C-M-p)] +@index[motion, list]@hid[Forward List] moves the point to immediately +after the end of the next list at the current level of list structure. If +there is not another list at the current level, then it moves up past +the end of the containing list. +@hid[Backward List] is identical, except that it moves backward and leaves +the point at the beginning of the list. The prefix argument is used as a +repeat count. +@enddefcom + +@defcom[com "Forward Up List", bind {C-M-@bf<)>}] +@defcom1[com "Backward Up List", bind (C-M-@bf<(>, C-M-u)] +@hid[Forward Up List] moves to after the end of the enclosing list. +@hid[Backward Up List] moves to the beginning. The prefix argument is used +as a repeat count. +@enddefcom + +@defcom[com "Down List", bind (C-M-d)] +This command moves to just after the beginning of the next list. The +prefix argument is used as a repeat count. +@enddefcom + +@defcom[com "Extract List", bind (C-M-x)] +This command "extracts" the current list from the list which contains it. +The outer list is deleted, leaving behind the current list. The entire +affected area is pushed on the kill ring, so that this possibly catastrophic +operation can be undone. The prefix argument is used as a repeat count. +@enddefcom + + + +@section[Defun Manipulation] + +@index[defun manipulation]A @i[defun] is a list whose open parenthesis is +against the left margin. It is called this because an occurrence of the +@f[defun] top level form usually satisfies this definition, but +other top level forms such as a @f[defstruct] and @f[defmacro] work just as +well. + +@defcom[com "End of Defun", bind (@bf)] +@defcom1[com "Beginning of Defun", bind (C-M-a, C-M-[)] +@index[motion, defun]@hid[End of Defun] moves to the end of the current +or next defun. @hid[Beginning of Defun] moves to the beginning of the +current or previous defun. @hid[End of Defun] will not work if the +parentheses are not balanced. +@enddefcom + +@defcom[com "Mark Defun", bind (C-M-h)] +This command puts the point at the beginning and the mark at the end of the +current or next defun. +@enddefcom + + + +@section[Indentation] + +@index[indentation, lisp] +One of the most important features provided by @hid[Lisp] mode is automatic +indentation of Lisp code. Since unindented Lisp is unreadable, poorly indented +Lisp is hard to manage, and inconsistently indented Lisp is subtly misleading. +See section @ref[indentation] for a description of the general-purpose +indentation commands. @hid[Lisp] mode uses these indentation rules: +@begin[itemize] +If in a semicolon (@f[;]) comment, then use the standard comment indentation +rules. See page @pageref[comment-indentation]. + +If in a quoted string, then indent to the column one greater than the column +containing the opening double quote. This is exactly what you want in function +documentation strings and wrapping @f[error] strings. + +If there is no enclosing list, then use no indentation. + +If enclosing list resembles a call to a known macro or special-form, then the +first few arguments are given greater indentation and the first body form is +indented two spaces. If the first special argument is on the same line as the +beginning of the form, then following special arguments will be indented to the +start of the first special argument, otherwise all special arguments are +indented four spaces. + +If the previous form starts on its own line, then the indentation is copied +from that form. This rule allows the default indentation to be overridden: +once a form has been manually indented to the user's satisfaction, subsequent +forms will be indented in the same way. + +If the enclosing list has some arguments on the same line as the form start, +then subsequent arguments will be indented to the start of the first argument. + +If the enclosing list has no argument on the same line as the form start, then +arguments will be indented one space. +@end[itemize] + + +@defcom[com "Indent Form", bind (C-M-q)] +This command indents all the lines in the current form, leaving the point +unmoved. This is undo-able. +@enddefcom + +@defcom[com "Fill Lisp Comment Paragraph", + stuff ] +@defhvar1[var "Fill Lisp Comment Paragraph Confirm", val {t}] +This fills a flushleft or indented Lisp comment. This also fills Lisp string +literals using the proper indentation as a filling prefix. When invoked +outside of a comment or string, this tries to fill all contiguous lines +beginning with the same initial, non-empty blankspace. When filling a comment, +the current line is used to determine a fill prefix by taking all the initial +whitespace on the line, the semicolons, and any whitespace following the +semicolons. + +When invoked outside of a comment or string, this command prompts for +confirmation before filling. It is useful to use this for filling long +@f[export] lists or other indented text or symbols, but since this is a less +common use, this command tries to make sure that is what you wanted. Setting +@hid[Fill Lisp Comment Paragraph Confirm] to @nil inhibits the confirmation +prompt. +@enddefcom + +@defcom[com "Defindent", bind (C-M-#)] +This command prompts for the number of special arguments to associate with +the symbol at the beginning of the current or containing list. +@enddefcom + +@defhvar[var "Indent Defanything", val {2}] +This is the number of special arguments implicitly assumed to be supplied in +calls to functions whose names begin with "@f[def]". If set to @nil, this +feature is disabled. +@enddefhvar + +@defcom[com "Move Over )", bind {M-)}] +This command moves past the next close parenthesis and then does the equivalent +of @hid[Indent New Line]. +@enddefcom + + +@section[Parenthesis Matching] + +@index[parenthesis matching]Another very important facility provided by +@hid[Lisp] mode is @i[parenthesis matching]. Two different styles of +parenthesis matching are supported: highlighting and pausing. + +@defhvar[var "Highlight Open Parens", val {t}] +@defhvar1[var "Open Paren Highlighting Font", val {nil}] +When @hid[Highlight Open Parens] is true, and a close paren is immediately +before the point, then @hemlock displays the matching open paren in @hid[Open +Paren Highlighting Font]. + +@hid[Open Paren Highlighting Font] is the string name of the font used for +paren highlighting. Only the "@f[(]" character is used in this font. If null, +then a reasonable default is chosen. The highlighting font is read at +initialization time, so this variable must be set before the editor is first +entered to have any effect. +@enddefhvar + +@defcom[com "Lisp Insert )", stuff ] +@defhvar1[var "Paren Pause Period", val {0.5}] +This command inserts a close parenthesis and then attempts to display the +matching open parenthesis by placing the cursor on top of it for +@hid[Paren Pause Period] seconds. If there is no matching parenthesis then +beep. If the matching parenthesis is off the top of the screen, then the line +on which it appears is displayed in the echo area. Paren pausing may be +disabled by setting @hid[Paren Pause Period] to @nil. +@enddefcom + +The initial values shown for @hid[Highlight Open Parens] and @hid[Paren Pause +Period] are only approximately correct. Since paren highlighting is only +meaningful in Lisp mode, @hid[Highlight Open Parens] is false globally, and +has a mode-local value of @true in Lisp mode. It it redundant to do both +kinds of paren matching, so there is also a binding of @hid[Paren Pause Period] +to @false in Lisp mode. + +Paren highlighting is only supported under @windows, so the above defaults are +conditional on the device type. If @hemlock is started on a terminal, the +initialization code makes Lisp mode bindings of @false and @f[0.5] for +@hid[Highlight Open Parens] and @hid[Paren Pause Period]. Since these +alternate default bindings are made at initialization time, the only way to +affect them is to use the @f[after-editor-initializations] macro. + + +@section[Parsing Lisp] +Lisp mode has a fairly complete knowledge of Lisp syntax, but since it does +not use the reader, and must work incrementally, it can be confused by legal +constructs. Lisp mode totally ignores the read-table, so user-defined read +macros have no effect on the editor. In some cases, the values the @hid[Lisp +Syntax] character attribute can be changed to get a similar effect. + +Lisp commands consistently treat semicolon (@f[;]) style comments as +whitespace when parsing, so a Lisp command used in a comment will affect the +next (or previous) form outside of the comment. Since @f[#| ... |#] comments +are not recognized, they can used to comment out code, while still allowing +Lisp editing commands to be used. + +Strings are parsed similarly to symbols. When within a string, the next form +is after the end of the string, and the previous form is the beginning of the +string. + + +@defhvar[var "Defun Parse Goal", val {2}] +@defhvar1[var "Maximum Lines Parsed", val {500}] +@defhvar1[var "Minimum Lines Parsed", val {50}] +In order to save time, Lisp mode does not parse the entire buffer every time +a Lisp command is used. Instead, it uses a heuristic to guess the region of +the buffer that is likely to be interesting. These variables control the +heuristic. + +Normally, parsing begins and ends on defun boundaries (an open parenthesis at +the beginning of a line). @hid[Defun Parse Goal] specifies the number of +defuns before and after the point to parse. If this parses fewer lines than +@hid[Minimum Lines Parsed], then parsing continues until this lower limit is +reached. If we cannot find enough defuns within @hid[Maximum Lines Parsed] +lines then we stop on the farthest defun found, or at the point where we +stopped if no defuns were found. + +When the heuristic fails, and does not parse enough of the buffer, then +commands usually act as though a syntax error was detected. If the parse +starts in a bad place (such as in the middle of a string), then Lisp commands +will be totally confused. Such problems can usually be eliminated by +increasing the values of some of these variables. +@enddefhvar + +@defhvar[var "Parse Start Function", val {start-of-parse-block}] +@defhvar1[var "Parse End Function", val {end-of-parse-block}] +These variables determine the region of the buffer parsed. The values are +functions that take a mark and move it to the start or end of the parse region. +The default values implement the heuristic described above. +@enddefhvar + + + +@comment[@chap(Interacting With Lisp)] +@include(lisp) + + +@comment[@chap(Mail Interface)] +@include(mail) + + +@comment[@chap(Netnews Interface)] +@include(netnews) + + + +@chap[System Interface] + +@hemlock provides a number of commands that access operating system resources +such as the filesystem and print servers. These commands offer an alternative +to leaving the editor and using the normal operating system command language +(such as the Unix shell), but they are implementation dependent. Therefore, +they might not even exist in some implementations. + + +@section[File Utility Commands] +This section describes some general file operation commands and quick directory +commands. + +See section @ref[dired] for a description @hemlock's directory editing +mechanism, @hid[Dired] mode. + +@defcom[com "Copy File"] +This command copies a file, allowing one wildcard in the filename. It prompts +for source and destination specifications. + +If these are both directories, then the copying process is recursive on the +source, and if the destination is in the subdirectory structure of the source, +the recursion excludes this portion of the directory tree. Use +@f[dir-spec-1/*] to copy only the files in a source directory without +recursively descending into subdirectories. + +If the destination specification is a directory, and the source is a file, then +it is copied into the destination with the same filename. + +The copying process copies files maintaining the source's write date. + +See the description of @hid[Dired Copy File Confirm], page +@pageref[copy-confirm], for controlling user interaction when the destination +exists. +@enddefcom + +@defcom[com "Rename File"] +This command renames a file, allowing one wildcard in the filename. It prompts +for source and destination specifications. + +If the destination is a directory, then the renaming process moves file(s) +indicated by the source into the directory with their original filenames. + +For Unix-based implementations, if you want to rename a directory, do not +specify the trailing slash in the source specification. +@enddefcom + +@defcom[com "Delete File"] +This command prompts for the name of a file and deletes it. +@enddefcom + +@defcom[com "Directory", bind (C-x C-d)] +@defcom1[com "Verbose Directory", bind (C-x C-D)] +These commands prompt for a pathname (which may contain wildcards), and display +a directory listing in a pop-up window. If a prefix argument is supplied, then +normally hidden files such as Unix dot-files will also be displayed. +@hid[Directory] uses a compact, multiple-column format; +@hid[Verbose Directory] displays one file on a line, with information about +protection, size, etc. +@enddefcom + + +@section[Printing] + +@defcom[com "Print Region"] +@defcom1[com "Print Buffer"] +@defcom1[com "Print File"] +@hid[Print Region] and @hid[Print Buffer] print the contents of the current +region and the current buffer, respectively. @hid[Print File] prompts for a +the name of a file and prints that file. Any error messages will be displayed +in the echo area. +@enddefcom + +@defhvar[var "Print Utility", val {"/usr/cs/bin/lpr"}] +@defhvar1[var "Print Utility Switches", val {()}] +@hid[Print Utility] is the program the print commands use to send files to the +printer. The program should act like @f[lpr]: if a filename is given as an +argument, it should print that file, and if no name appears, standard input +should be assumed. @hid[Print Utility Switches] is a list of strings +specifying the options to pass to the program. +@enddefhvar + + +@section[Scribe] +@defcom[com "Scribe Buffer File", + stuff (bound to @bf[C-x c] in @hid[Scribe] mode)] +@defhvar1[var "Scribe Buffer File Confirm", val {t}] +@defcom1[com "Scribe File"] +@hid[Scribe Buffer File] invokes @hid[Scribe Utility] on the file associated +with the current buffer. That process's default directory is the directory of +the file. The process sends its output to the @hid[Scribe Warnings] buffer. +Before doing anything, this asks the user to confirm saving and formatting the +file. This prompting can be inhibited with "Scribe Buffer File Confirm". + +@hid[Scribe File] invokes @hid[Scribe Utility] on a file supplied by the user +in the same manner as describe above. +@enddefcom + +@defhvar[var "Scribe Utility", val {"/usr/misc/bin/scribe"}] +@defhvar1[var "Scribe Utility Switches"] +@hid[Scribe Utility] is the program the Scribe commands use to compile the text +formatting. @hid[Scribe Utility Switches] is a list of strings whose contents +would be contiguous characters, other than space, had the user invoked this +program on a command line outside of @hemlock. Do not include the name of the +file to compile in this variable; the Scribe commands supply this. +@enddefhvar + +@defcom[com "Select Scribe Warnings", bind (Scribe: C-M-C)] +This command makes the @hid[Scribe Warnings] buffer current if it exists. +@enddefcom + + +@section[Miscellaneous] + +@defcom[com "Manual Page"] +This command displays a Unix manual page in a buffer which is in @hid[View] +mode. When given an argument, this puts the manual page in a pop-up display. +@enddefcom + +@defcom[com "Unix Filter Region"] +This command prompts for a UNIX program and then passes the current region to +the program as standard input. The standard output from the program is used to +replace the region. This command is undoable. +@enddefcom + + + +@chap[Simple Customization] + +@index[customization]@hemlock can be customized and extended to a very +large degree, but in order to do much of this a knowledge of Lisp is +required. These advanced aspects of customization are discussed in the +@i[Hemlock Command Implementor's Manual], while simpler methods of +customization are discussed here. + + +@section[Keyboard Macros] +@index[keyboard macros] +Keyboard macros provide a facility to turn a sequence of commands into one +command. + +@defcom[com "Define Keyboard Macro", bind {C-x (}] +@defcom1[com "End Keyboard Macro", bind {C-x )}] +@hid[Define Keyboard Macro] starts the definition of a keyboard macro. The +commands which are invoked up until @hid[End Keyboard Macro] is invoked +become the definition for the keyboard macro, thus replaying the keyboard +macro is synonymous with invoking that sequence of commands. +@enddefcom + +@defcom[com "Last Keyboard Macro", bind (C-x e)] +This command is the keyboard macro most recently defined; invoking it will +replay the keyboard macro. The prefix argument is used as a repeat count. +@enddefcom + +@defcom[com "Define Keyboard Macro Key", bind (C-x M-(; )] +@defhvar1[var "Define Keyboard Macro Key Confirm", val {t}] +This command prompts for a key before going into a mode for defining keyboard +macros. After defining the macro @hemlock binds it to the key. If the key is +already bound, @hemlock asks for confirmation before clobbering the binding; +this prompting can be inhibited by setting @hid[Define Keyboard Macro Key +Confirm] to @nil. +@enddefcom + +@defcom[com "Keyboard Macro Query", bind (C-x q)] +This command conditionalizes the execution of a keyboard macro. When invoked +during the definition of a macro, it does nothing. When the macro replays, it +prompts the user for a key-event indicating what action to take. The following +commands are defined: +@begin[description] +@binding[Escape]@\ + Exit all repetitions of this keyboard macro. More than one may have been +specified using a prefix argument. + +@binding[Space, y]@\ + Proceed with the execution of the keyboard macro. + +@binding[Delete, Backspace, n]@\ + Skip the remainder of the keyboard macro and go on to the next repetition, if +any. + +@binding[!]@\ + Do all remaining repetitions of the keyboard macro without prompting. + +@binding[.]@\ + Complete this repetition of the macro and then exit without doing any of the +remaining repetitions. + +@binding[C-r]@\ + Do a recursive edit and then prompt again. +@end[description] +@enddefcom + +@defcom[com "Name Keyboard Macro"] +This command prompts for the name of a command and then makes the +definition for that command the same as @hid[Last Keyboard Macro]'s current +definition. The command which results is not clobbered when another +keyboard macro is defined, so it is possible to keep several keyboard +macros around at once. The resulting command may also be bound to a key +using @hid[Bind Key], in the same way any other command is. +@enddefcom + +Many keyboard macros are not for customization, but rather for one-shot +use, a typical example being performing some operation on each line of a file. +To add "@f[del ]" to the beginning and "@f[.*]" to the end of every line in +in a buffer, one could do this: +@begin[programexample] +C-x ( d e l Space C-e . * C-n C-a C-x ) C-u 9 9 9 C-x e +@end[programexample] +First a keyboard macro is defined which performs the desired operation on +one line, and then the keyboard macro is invoked with a large prefix +argument. The keyboard macro will not actually execute that many times; +when the end of the buffer is reached the @binding[C-n] will get an error +and abort the execution. + + +@section[Binding Keys] +@index[key bindings] +@label[binding-keys] + +@defcom[com "Bind Key"] +This command prompts for a command, a key and a kind of binding to make, +and then makes the specified binding. The following kinds of bindings are +allowed: +@begin[description] +@i[buffer]@\Prompts for a buffer and then makes a key binding which is +only present when that buffer is the current buffer. + +@i[mode]@\Prompts for the name of a mode and then makes a key binding which +is only in present when that mode is active in the current buffer. + +@i[global]@\Makes a global key binding which is in effect when there is +no applicable mode or buffer key binding. This is the default. +@end[description] +@enddefcom + +@defcom[com "Delete Key Binding"] +This command prompts for a key binding the same way that @hid[Bind Key] +does and makes the specified binding go away. +@enddefcom + +@section[Hemlock Variables] + +@label[vars]@index[variables, hemlock]@index[hemlock variables]A number +of commands use @hemlock variables as flags to control their behavior. Often +you can get a command to do what you want by setting a variable. Generally the +default value for a variable is chosen to be the safest value for novice users. + +@defcom[com "Set Variable"] +This command prompts for the name of a @hemlock variable and an expression, +then sets the current value of the variable to the result of the evaluation of +the expression. +@enddefcom + + +@defcom[com "Defhvar"] +Like @hid[Set Variable], this command prompts for the name of a @hemlock +variable and an expression. Like @hid[Bind Key], this command prompts for a +place: mode, buffer or local. The result of evaluating the expression is +defined to be the value of the named variable in the specified place. + +This command is most useful for making mode or buffer local bindings of +variables. Redefining a variable in a mode or buffer will create a +customization that takes effect only when in that mode or buffer. + +Unlike @hid[Set Variable], the variable name need not be the name of an +existing variable: new variables may be defined. If the variable is already +defined in the current environment, @hemlock copies the documentation and hooks +to the new definition. +@enddefcom + + +@section[Init Files] +@index[init files] +@hemlock customizations are normally put in @hemlock's initialization file, +"@f[hemlock-init.lisp]", or when compiled "@f[hemlock-init.fasl]". When +starting up Lisp, use the @f[-hinit] switch to indicate a particular file. The +contents of the init file must be Lisp code, but there is a fairly +straightforward correspondence between the basic customization commands and the +equivalent Lisp code. Rather than describe these functions in depth here, a +brief example follows: +@begin[programexample] +;;; -*- Mode: Lisp; Package: Hemlock -*- + +;;; It is necessary to specify that the customizations go in +;;; the hemlock package. +(in-package 'hemlock) + +;;; Bind @hid[Kill Previous Word] to @binding[M-h]. +(bind-key "Kill Previous Word" '#(#\m-h)) +;;; +;;; Bind @hid[Extract List] to @binding[C-M-?] when in @hid[Lisp] mode. +(bind-key "Extract List" '#(#\c-m-?) :mode "Lisp") + +;;; Make @binding[C-w] globally unbound. +(delete-key-binding '#(#\c-w)) + +;;; Make string searches case-sensitive. +(setv string-search-ignore-case nil) +;;; +;;; Make "Query Replace" replace strings literally. +(setv case-replace nil) +@end[programexample] +For a detailed description of these functions, see the @i[Hemlock Command +Implementor's Manual]. -- GitLab From f2e5930e0150c05870b52ef1b2513f364a060e43 Mon Sep 17 00:00:00 2001 From: Raymond Toy Date: Wed, 1 Apr 2020 21:52:51 -0700 Subject: [PATCH 2/3] A start of the ocnversion to texi. --- docs/hem/cim/cim.texi | 52 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 docs/hem/cim/cim.texi diff --git a/docs/hem/cim/cim.texi b/docs/hem/cim/cim.texi new file mode 100644 index 0000000..e342fc5 --- /dev/null +++ b/docs/hem/cim/cim.texi @@ -0,0 +1,52 @@ +\input texinfo + +@titlepage +@title Hemlock Command Implementor's Manual +@author Bill Chiles +@author Rob MacLachlan +@subtitle February 1992 +@subtitle CMU-CS-89-132-R1 + +@center +School of Computer Science +Carnegie Mellon University +Pittsburgh, PA 15213 +@end center + +@center +This is a revised version of Technical Report CMU-CS-87-159. +@end center + +@abstract +This document describes how to write commands for the @Hemlock text editor, as +of version M3.2. @Hemlock is a customizable, extensible text editor whose +initial command set closely resembles that of ITS/TOPS-20 @Emacs. @Hemlock is +written in the CMU Common Lisp and has been ported to other implementations. +@end abstract + +This research was supported by the Defense Advanced Research Projects +Agency (DOD), and monitored by the Avionics Laborotory, Air Force +Wright Aeronatutical Laboratories, Aeronautical Systems Division +(AFSC), Wright-Patterson AFB, Ohio 45344-6543 under Contract +F33615-87-C-1499, ARPA Order No. 4976, Amendment 20. + +The views and conclusions contained in this document are those of the +authros and should not be interpreted as respresnting the official +policies, either rexpressed or implied, of the Defense Advanced +Research Projects Agency or the U.S. government. + +@chapter +@section Introduction +Some primitives such as @funref[prompt-for-key] and commands such as EMACS +query replace read key-events directly from the keyboard instead of using the +command interpreter. To encourage consistency between these commands and to +make them portable and easy to customize, there is a mechanism for defining +@i[logical key-events]. + +A logical key-event is a keyword which stands for some set of key-events. The +system globally interprets these key-events as indicators a particular action. +For example, the @kwd[help] logical key-event represents the set of key-events +that request help in a given @hemlock implementation. This mapping is a +many-to-many mapping, not one-to-one, so a given logical key-event may have +multiple corresponding actual key-events. Also, any key-event may represent +different logical key-events. -- GitLab From f34f1c553021e9bf41146ce7ee06ac14229463fa Mon Sep 17 00:00:00 2001 From: Raymond Toy Date: Wed, 1 Apr 2020 22:19:49 -0700 Subject: [PATCH 3/3] Add meta descriptions for the main pages --- cmucl-www/www/FAQ.html | 25 ++++++++-------- cmucl-www/www/benchmarks/index.html | 9 ++++-- cmucl-www/www/credits.html | 23 ++++++++------- cmucl-www/www/doc/index.html | 7 +++-- cmucl-www/www/download.html | 44 ++++++++++++++--------------- cmucl-www/www/hemlock/index.html | 21 +++++++------- cmucl-www/www/index.html | 31 ++++++++++---------- cmucl-www/www/install.html | 24 ++++++++-------- cmucl-www/www/news/index.html | 24 ++++++++-------- cmucl-www/www/platforms.html | 24 ++++++++-------- cmucl-www/www/ports.html | 7 +++-- cmucl-www/www/projects.html | 23 ++++++++------- cmucl-www/www/support.html | 30 ++++++++++---------- 13 files changed, 148 insertions(+), 144 deletions(-) diff --git a/cmucl-www/www/FAQ.html b/cmucl-www/www/FAQ.html index ee4a128..2f76f35 100644 --- a/cmucl-www/www/FAQ.html +++ b/cmucl-www/www/FAQ.html @@ -3,16 +3,21 @@ + content="HTML Tidy for HTML5 for Linux version 5.7.28"> CMUCL FAQ - + + + @@ -32,15 +37,13 @@ })(); - +
- - + +
FAQ: Questions and Answers @@ -447,10 +450,8 @@ or get more memory+swap.
- - -Last modified: Sat Feb 29 22:36:21 PST 2020 + +Last modified: Wed Apr 1 22:06:33 PDT 2020
diff --git a/cmucl-www/www/benchmarks/index.html b/cmucl-www/www/benchmarks/index.html index 6932204..7db191e 100644 --- a/cmucl-www/www/benchmarks/index.html +++ b/cmucl-www/www/benchmarks/index.html @@ -3,7 +3,7 @@ + content="HTML Tidy for HTML5 for Linux version 5.7.28"> CMUCL performance benchmarks @@ -14,6 +14,8 @@ + @@ -58,7 +60,8 @@
  • hashtable exercising
  • -
  • READ-LINE exercising +
  • + READ-LINE exercising
  • mathematical functions: factorial, fibonnaci, ackermann's number
  • @@ -243,7 +246,7 @@
    -Last modified: Sat Mar 14 07:55:00 PDT 2020 +Last modified: Wed Apr 1 22:10:27 PDT 2020
    diff --git a/cmucl-www/www/credits.html b/cmucl-www/www/credits.html index 27ce346..e1230c7 100644 --- a/cmucl-www/www/credits.html +++ b/cmucl-www/www/credits.html @@ -3,16 +3,19 @@ + content="HTML Tidy for HTML5 for Linux version 5.7.28"> CMUCL credits - + + @@ -32,15 +35,13 @@ })(); - +
    - - + +
    Credits: project history & who's who @@ -235,10 +236,8 @@

    - - -Last modified: Sat Feb 29 22:36:28 PST 2020 + +Last modified: Wed Apr 1 22:08:06 PDT 2020
    diff --git a/cmucl-www/www/doc/index.html b/cmucl-www/www/doc/index.html index 36a37ec..5d87418 100644 --- a/cmucl-www/www/doc/index.html +++ b/cmucl-www/www/doc/index.html @@ -3,7 +3,7 @@ + content="HTML Tidy for HTML5 for Linux version 5.7.28"> @@ -16,6 +16,9 @@ <link rel="author" href="https://gitlab.common-lisp.net/cmucl/cmucl-site/issues" title="CMUCL site issues tracker"> + <meta name="description" + content= + "Links to documentation including the CMUCL User's manual, the CLX manual, the CMUCL Motif Toolkit, and the CMUCL Internals manual. Also includes links to tips and tricks, as well as developer information, general information on Common Lisp and other resources."> <link rel="stylesheet" href="../cmucl.css" type="text/css"><!-- For Google analytics --> @@ -366,7 +369,7 @@ </main> <footer> <div id="footer"></div><!-- hhmts start --> -Last modified: Sun Mar 15 17:39:37 PDT 2020 +Last modified: Wed Apr 1 22:04:06 PDT 2020 <!-- hhmts end --> </footer> </div> diff --git a/cmucl-www/www/download.html b/cmucl-www/www/download.html index a0b3bd1..1933be0 100644 --- a/cmucl-www/www/download.html +++ b/cmucl-www/www/download.html @@ -3,16 +3,19 @@ <head> <meta charset="utf-8"> <meta name="generator" - content="HTML Tidy for HTML5 for Apple macOS version 5.6.0"> + content="HTML Tidy for HTML5 for Linux version 5.7.28"> <title> CMUCL downloads - + + @@ -42,15 +45,13 @@ })(); - +
    - - + +
    Download: how to get CMUCL @@ -96,8 +97,8 @@
    A mirror of the downloads directory, located in Germany with good - bandwidth across Europe and the USA. Only has snapshots - up to 2018-10. + bandwidth across Europe and the USA. Only has snapshots up to + 2018-10.
    -Last modified: Sun Mar 15 10:03:09 PDT 2020 + +Last modified: Wed Apr 1 22:09:42 PDT 2020
    diff --git a/cmucl-www/www/hemlock/index.html b/cmucl-www/www/hemlock/index.html index 2e11d09..c7b6ae3 100644 --- a/cmucl-www/www/hemlock/index.html +++ b/cmucl-www/www/hemlock/index.html @@ -3,7 +3,7 @@ + content="HTML Tidy for HTML5 for Linux version 5.7.28"> The Hemlock editor @@ -14,6 +14,9 @@ + @@ -33,15 +36,13 @@ })(); - +
    - - + +
    Hemlock: an extensible Emacs-like editor @@ -102,10 +103,8 @@

    - - -Last modified: Tue Mar 3 19:25:15 PST 2020 + +Last modified: Wed Apr 1 22:11:56 PDT 2020
    diff --git a/cmucl-www/www/index.html b/cmucl-www/www/index.html index 6afc92f..41b4e8f 100644 --- a/cmucl-www/www/index.html +++ b/cmucl-www/www/index.html @@ -3,13 +3,14 @@ + content="HTML Tidy for HTML5 for Linux version 5.7.28"> CMUCL Home Page - + @@ -20,7 +21,8 @@ content="CMUCL, Common Lisp, CLOS"> + "CMUCL is a high-performance, free Common Lisp implementation that runs on most major Unix platforms. It mostly conforms to the ANSI Common Lisp standard."> + - +
    - - + +
    Home: a high-performance, free Common Lisp @@ -172,9 +172,10 @@
    common-lisp.net announced - that Gitlab would replace the - current Trac system. The - pages from Trac are slowing being migrated to CMUCL's gitlab page. + that Gitlab would replace + the current Trac system. + The pages from Trac are slowing being migrated to CMUCL's gitlab + page.
    CMUCL 20e released @@ -210,10 +211,8 @@
    - - -Last modified: Sat Feb 29 22:36:36 PST 2020 + +Last modified: Wed Apr 1 22:03:30 PDT 2020
    diff --git a/cmucl-www/www/install.html b/cmucl-www/www/install.html index 1e9b4ff..ffbced4 100644 --- a/cmucl-www/www/install.html +++ b/cmucl-www/www/install.html @@ -3,16 +3,20 @@ + content="HTML Tidy for HTML5 for Linux version 5.7.28"> Installing CMUCL - + + @@ -32,15 +36,13 @@ })(); - +
    - - + +
    Install: installing CMUCL @@ -54,10 +56,8 @@

    - - -Last modified: Sat Feb 29 22:36:40 PST 2020 + +Last modified: Wed Apr 1 22:13:15 PDT 2020
    diff --git a/cmucl-www/www/news/index.html b/cmucl-www/www/news/index.html index 16a9d45..96e9ae4 100644 --- a/cmucl-www/www/news/index.html +++ b/cmucl-www/www/news/index.html @@ -3,16 +3,20 @@ + content="HTML Tidy for HTML5 for Linux version 5.7.28"> CMUCL News - + + @@ -32,15 +36,13 @@ })(); - +
    - - + +
    News @@ -549,10 +551,8 @@ the short-hand syntax above, you can also use
    - - -Last modified: Sat Feb 29 22:30:00 PST 2020 + +Last modified: Wed Apr 1 22:14:43 PDT 2020
    diff --git a/cmucl-www/www/platforms.html b/cmucl-www/www/platforms.html index 5672c76..2c2bbf4 100644 --- a/cmucl-www/www/platforms.html +++ b/cmucl-www/www/platforms.html @@ -3,16 +3,20 @@ + content="HTML Tidy for HTML5 for Linux version 5.7.28"> CMUCL: Supported platforms - + + @@ -32,15 +36,13 @@ })(); - +
    - - + +
    Platforms: what does it run on? @@ -238,10 +240,8 @@

    - - -Last modified: Sat Feb 29 22:36:45 PST 2020 + +Last modified: Wed Apr 1 22:16:07 PDT 2020
    diff --git a/cmucl-www/www/ports.html b/cmucl-www/www/ports.html index 24598cf..d6e6c90 100644 --- a/cmucl-www/www/ports.html +++ b/cmucl-www/www/ports.html @@ -3,7 +3,7 @@ + content="HTML Tidy for HTML5 for Linux version 5.7.28"> CMUCL ports @@ -14,6 +14,9 @@ + @@ -289,7 +292,7 @@ Pictures is a CLOS-based interface to CLIO.
    -Last modified: Sat Mar 21 08:10:21 PDT 2020 +Last modified: Wed Apr 1 22:17:29 PDT 2020
    diff --git a/cmucl-www/www/projects.html b/cmucl-www/www/projects.html index 605cbe5..f6c4047 100644 --- a/cmucl-www/www/projects.html +++ b/cmucl-www/www/projects.html @@ -3,16 +3,19 @@ + content="HTML Tidy for HTML5 for Linux version 5.7.28"> CMUCL projects - + + @@ -32,15 +35,13 @@ })(); - +
    - - + +
    Projects: improvements to CMUCL @@ -118,10 +119,8 @@
    - - -Last modified: Sat Feb 29 22:36:53 PST 2020 + +Last modified: Wed Apr 1 22:18:09 PDT 2020
    diff --git a/cmucl-www/www/support.html b/cmucl-www/www/support.html index a475995..63bd825 100644 --- a/cmucl-www/www/support.html +++ b/cmucl-www/www/support.html @@ -3,16 +3,20 @@ + content="HTML Tidy for HTML5 for Linux version 5.7.28"> CMUCL support - + + @@ -32,15 +36,13 @@ })(); - + -- GitLab