3. Files, Buffers, and 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.
- A file provides permanent storage of text. Hemlock has commands to read files into buffers and write buffers out into files.
- 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.
- 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.
In addition to some text, a buffer has several other user-visible attributes:
- 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 8.
- 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.
Select Buffer (bound to C-x b) [Command]
This command prompts for the name of a existing buffer and makes that buffer the 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.
Select Previous Buffer (bound to C-M-l) [Command]
Circulate Buffers (bound to C-M-L) [Command]
With no prefix argument, Select Previous Buffer selects the buffer that has been selected most recently, similar to C-x b Return. If given a prefix argument, then it does the same thing as Circulate Buffers.
Circulate Buffers moves back into successively earlier buffers in the buffer history. If the previous command was not Circulate Buffers or Select Previous Buffer, then it does the same thing as 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 Select Previous Buffer will always take you back to where you started.
These commands are generally used together. Often Select Previous Buffer will take you where you want to go. If you don't end up there, then using Circulate Buffers will do the trick.
Create Buffer (bound to C-x M-b) [Command]
This command is very similar to 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.
Kill Buffer (bound to C-x k) [Command]
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.
List Buffers (bound to C-x C-b) [Command]
This command displays a list of all existing buffers in a pop-up window. A "*" 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.
Buffer Not Modified (bound to M-~) [Command]
This command resets the current buffer's modification flag---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.
Check Buffer Modified (bound to C-x ~) [Command]
This command displays a message indicating whether the current buffer is modified.
Set Buffer Read-Only [Command]
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.
Set Buffer Writable [Command]
This command ensures the current buffer is modifiable.
Insert Buffer [Command]
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.
Rename Buffer [Command]
This command prompts for a new name for the current buffer, which defaults to a name derived from the associated filename.
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.
Find File (bound to C-x C-f) [Command]
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 "(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 "name type directory". This means that the filename "/sys/emacs/teco.mid" has "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 Create Buffer.
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, Find File just asks for confirmation before reading in the new version. If the buffer is modified, then Find File beeps and prompts for a single key-event to indicate what action to take. It recognizes the following key-events:
- 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.
- Delete, Backspace, n
- Forego reading the file.
- 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.
Save File (bound to C-x C-s) [Command]
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, Save File prompts for confirmation before overwriting the file.
Save All Files (bound to C-x C-m) [Command]
Save All Files and Exit (bound to C-x M-z) [Command]
Save All Files Confirm (initial value t) [Variable]
Save All Files does a Save File on all buffers which have an associated file. Save All Files and Exit does the same thing and then exits Hemlock.
When Save All Files Confirm is true, these commands will ask for confirmation before saving a file.
Visit File (bound to C-x C-v) [Command]
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 Find File, the file need not actually exist. This command warns if some other buffer also contains the file.
Write File (bound to C-x C-w) [Command]
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 Save File.
Backup File [Command]
This command is similar to 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 Backup File doesn't update the write date for the buffer, Find File and Save File will get all upset if you back up a buffer on any file that has been read into Hemlock.
Revert File [Command]
Revert File Confirm (initial value t) [Variable]
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 Revert File Confirm is true, then the user is asked for confirmation.
Insert File (bound to C-x C-r) [Command]
This command prompts for a file and inserts it at the point, pushing a buffer mark before inserting.
Write Region [Command]
This command prompts for a file and writes the text in the region out to it.
Add Newline at EOF on Writing File (initial value :ask-user) [Variable]
This variable controls whether some file writing commands add a newline at the end of the file if the last line is non-empty.
- Ask the user whether to add a newline.
- Automatically add a newline and inform the user.
- Never add a newline and do not ask.
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.
Keep Backup Files (initial value nil) [Variable]
Whenever a file is written by Save File and similar commands, the old file is renamed by appending ".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.
3.3.1 Auto Save Mode
Save mode protects against loss of work in system crashes by periodically saving modified buffers in checkpoint files.
Auto Save Mode [Command]
This command turns on Save mode if it is not on, and turns off when it is on. Save mode is on by default.
Auto Save Checkpoint Frequency (initial value 120) [Variable]
Auto Save Key Count Threshold (initial value 256) [Variable]
These variables determine how often modified buffers in Save mode will be checkpointed. Checkpointing is done after Auto Save Checkpoint Frequency seconds, or after 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.
Auto Save Cleanup Checkpoints (initial value t) [Variable]
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.
Auto Save Filename Pattern (initial value "~A~A.CKP") [Variable]
Auto Save Pathname Hook (initial value make-unique-save-pathname) [Variable]
These variables determine the naming of checkpoint files. 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.
Auto Save Pathname Hook is a function called by 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 Save mode is turned off in the buffer. The default value for this variable returns a pathname in the default directory of the form "save-number", where number is a number used to make the file unique.
3.3.2 Filename Defaulting and Merging
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.
Pathname Defaults (initial value (pathname "gazonk.del")) [Variable]
Last Resort Pathname Defaults Function (initial value ) [Variable]
Last Resort Pathname Defaults (initial value (pathname "gazonk")) [Variable]
These variables control the computation of default filename defaults when the current buffer has no associated file.
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 Pathname Defaults. Otherwise, the default is computed by calling Last Resort Pathname Defaults Function with the buffer as an argument.
The default value of Last Resort Pathname Defaults Function merges Last Resort Pathname Defaults with Pathname Defaults. Unlike Pathname Defaults, 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.
When a default is present in the prompt for a file, Hemlock 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:
- If Hemlock can find the user's input as a file on the "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 file-namestring. If the user only responds with a directory specification, without any name or type information, then Hemlock merges the default's 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.
3.3.3 Type Hooks and File Options
When a file is read either by Find File or Visit File, Hemlock attempts to guess the correct mode in which to put the buffer, based on the file's type (the part of the filename after the last dot). Any default action may be overridden by specifying the mode in the file's 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 "-*-", then Hemlock interprets the text between the first such occurrence and the second, which must be contained in one line , as a list of "option: value" pairs separated by semicolons. The following is a typical example:
;;; -*- Mode: Lisp, Editor; Package: Hemlock -*-
These options are currently defined:
- 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 Set Buffer Spelling Dictionary.
- The argument is the name of the change log file associated with this file (see page 5.3). The handler for this option merges the argument with the name of this file.
- The argument is a comma-separated list of the names of modes to turn on in the buffer that the file is read into.
- 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 9.3.)
- The handler for this option ignores its argument and turns on Editor mode (see Editor Mode).
If the option list contains no ":" then the entire string is used as the name of the major mode for the buffer.
Process File Options [Command]
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.
Hemlock windows display a portion of a buffer's text. See the section on window groups, 1.7.1, for a discussion of managing windows on bitmap device.
New Window (bound to C-x C-n) [Command]
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.
Split Window (bound to C-x 2) [Command]
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.
Next Window (bound to C-x n) [Command]
Previous Window (bound to C-x p) [Command]
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.
Delete Window (bound to C-x C-d, C-x d) [Command]
Delete Next Window (bound to C-x 1) [Command]
Delete Window makes the current window go away, making the next window current. 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.
Go to One Window [Command]
This command deletes all window groups leaving one with the Default Initial Window X, Default Initial Window Y, Default Initial Window Width, and Default Initial Window Height. This remaining window retains the contents of the current window.
Line to Top of Window (bound to M-!) [Command]
Line to Center of Window (bound to M-#) [Command]
Line to Top of Window scrolls the current window up until the current line is at the top of the screen.
Line to Center of Window attempts to scroll the current window so that the current line is vertically centered.
Scroll Next Window Down (bound to C-M-v) [Command]
Scroll Next Window Up (bound to C-M-V) [Command]
These commands are the same as Scroll Window Up and Scroll Window Down except that they operate on the next window.
Refresh Screen (bound to C-l) [Command]
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.