Terminal package - help file
Name: | Terminal |
Version: | 1.5.2 |
Last update: | 2019-11-27 14:12:46 |
Abstract
The terminal package is a collection of Tcl routines and data structures
for providing a terminal and log file (similar to those of TeX) that can be
used both with the embedded interpreter of the Alpha text editor and with a
standard tclsh shell. The terminal package does not provide any
mechanisms by which the user can provide direct input, so it can only
emulate TeX's \nonstopmode
and \batchmode
interaction
modes.
Introduction
Most of the code in AlphaTcl is of the type which makes little or no sense
outside the context of a text editor with Tcl as built-in command language,
but some code is more generally useful and would make sense also if run by
e.g. tclsh (the Tcl shell); such code very often performs the
function of converting some data in one format to another format. Most
programs performing such functions do however need to occasionally convey
some information to the user -- usually some error or warning message
although other kinds of information can also be useful, even if not quite
as necessary -- but this is not easy when the code has to run on both Alpha
and tclsh, due to the lack of a suitable communication channel. In
tclsh, the obvious choice is the stdout (or possibly stderr) output
stream, but for Alpha the logical channel would instead be text that is
inserted into a "shell" document window, but that makes no sense in tclsh. In short, these are two quite different worlds.
The solution to the dilemma is of course to introduce an API which lets
the calling program handle both cases uniformly and this is precisely what
terminal is about. terminal provides procedures for printing text, and
whether that means writing to an output stream or inserting the text into
an editor window is something that the calling program should not worry
about. In addition to this abstraction of the communication with the user,
terminal also provides for recording what is printed on a “log” file, and
some higher level procedures which provide more convenient operations than
the mere printing of a string, even though that is of course available as
well. Right now the communication is limited to textual messages from the
program to the user as that is the most immediate need, but additions could
be made if the need arises (and a suitable implementation is found). In
particular, some way of asking the user to choose a file could be a
valuable addition.
As a Tcl package, terminal consists of the file terminal.tcl. This is
installed as any normal Tcl package and should work on both Tcl 7 and 8
interpreters. From Tcl 8, the package is preferably loaded using the
command
package require terminal
whereas on Tcl 7 it is up to the user to make sure that the file is
sourced. There is also a file terminalPrefsAlpha.tcl which is only
useful with Alpha. It allows the user to set preferences for the position
and size of the terminal window when terminal is run under Alpha, but it
does not define any of the terminal package commands.
Usage
The terminal package deals with two targets for the output it
conveys: the terminal and the log file. The terminal is either an Alpha
window or an output stream (by default stdout). The log file is
simply something opened for writing using open; whether that there is a file
on disk or something else is up to the file system to sort out. By default,
output goes identically to both targets, but that can be overridden both in
specific cases and in general. This is mainly used to let some output (such
as verbose traces) go only to the log file, but it can be done the other
way round too. Furthermore a target must first be opened before any output
is actually printed on it; this is primarily to accommodate for programs
where keeping a log file would seem overly elaborated -- if you do not want
it then you should not be forced to open it either.
Commands for printing
- Proc [terminal::print_word]
- The main command one uses to print some text is the print_word procedure,
which has the syntax
terminal::print_word {before} {string} {after} {target}? {flush}?
The {string}
argument is the string that will be printed. There is no
restriction as to which characters the {string}
may contain; the `word'
part of the command name refers instead to the fact that there is
generally some amount of whitespace (that is managed by the command)
around the {string}
. The {before}
and {after}
arguments are used to
request that the {string}
is separated from surrounding text by at least
some grade of whitespace: the possible values are none, space, newline,
and emptyline. If the entire {string}
does not fit on what remains of
the line, then print_word will see to that it is printed on the beginning
of a line. The {flush}
argument is a boolean for whether the target
should be flushed after the {string}
has been printed. It defaults to 1
(flush).
The {target}
argument can be used to specify to what the string should be
printed. This argument can be `term' (the terminal), `log' (the log file),
`both', or `none' (which essentially turns the print_word command into a
no-op). The most common value is probably log, for information considered
too verbose to print on the terminal. If the {target}
argument is omitted
then the value of the terminal::selector variable is used. This is
initialized to both by default, but changing it to log would make a decent
emulation of TeX's \batchmode.
- Proc [terminal::print]
- Besides print_word, there is also a lower-level printing procedure
which is part of the interface to terminal, namely terminal::print. It is similar to print_word, but it lacks the
{before}
and {after}
arguments, and it never inserts any whitespace before or after
the string it prints. I suspect that it is usually more efficient to do one
print_word on a concatenated string than to do several prints on the parts,
so it is not unlikely that you will never have to use print directly.
Since some programs do not like very long lines, the terminal package
generally puts a bound on how long lines can be and inserts a line break
if they become too long. The bound is kept in the terminal::max_print_line
variable and can be changed at any time. The default value is 79, which
limits the line width to 79 characters. The print and print_word procedures
do however react slightly differently to this limit. If the entire {string}
to print does not fit on what remains of the line, then print_word will
see to that it is printed on the beginning of the following line, but
|print| makes no such adjustment. If either procedure prints the
$max_print_line'th character on a line then they break the line after that
character and put the next character in the {string}
first on the following
line.
- Proc [terminal::print_block]
- Another command which is occasionally useful is terminal::print_block.
It has the syntax
terminal::print_block {before} {indent} {line-list} {after} {target}? {flush}?
The {line-list}
argument is a list of lines that will be printed and the
{indent}
is a string (which of course can be empty) that will be inserted
first in each line. The main difference to print_word is that these lines
are not broken no matter how long they are -- this is mainly useful if one
is given a block of preformatted text and has to print it as given no
matter how long the lines are. If anything but emptyline is given as
{before}
or {after}
then it is treated as newline.
Opening and closing
- Proc [terminal::term_open]
- As was mentioned above, a target must be opened before anything can be
printed on it, as output to closed targets is silently ignored. The
terminal is opened using the procedure terminal::term_open, which has the
syntax
terminal::term_open {clear} {title}? {file}?
The {clear}
argument is a flag for whether the terminal should be cleared
after opening; 1 means clear, 0 means leave as it was. Whether clearing
actually does anything depends on whether there is some way of defining and
doing that with whatever serves as the terminal; it currently has no effect
when the terminal is an output channel such as stdout. The {title}
argument
can be used to specify a title for the terminal (again provided that there
is some way of defining and setting that for whatever serves as the
terminal). The {title}
defaults to `*terminal*'. The {file}
argument is
an identifier for an open channel to use for terminal output instead of
the default stdout. (An obvious alternative is stderr.) In Alpha, where
the terminal isn't a channel anyway, this argument is ignored.
- Proc [terminal::term_autoopen]
- A convenient variation on this procedure is terminal::term_autoopen, which
has the syntax
terminal::term_autoopen {banner} {clear} {title}? {file}?
When run in tclsh, this is equivalent to
terminal::term_open $clear $title $file
terminal::print_block newline {} $banner newline term
but when run in Alpha it tries to be more sophisticated. If the terminal
window isn't already open, then nothing will be shown until something else
is written to the terminal, but at that time the above code will indeed be
executed. This is intended mainly for programs which only write something
when an error occurs, since opening a terminal window to which nothing
interesting will be written can look rather odd.
- Proc [terminal::log_open]
- The log file is similarly opened using the procedure terminal::log_open,
which has the syntax
terminal::log_open {name}
where {name}
simply is the file system's name of the file to which the log
shall be written. The file is opened in access mode w, so any previous
contents in the file are deleted.
- Proc [terminal::term_close], [terminal::log_close], [terminal::cleanup]
- These opening procedures naturally have closing counterparts: the
terminal::term_close and terminal::log_close procedures. Neither procedure
takes any arguments. The log_close procedure actually effectuates a file
close, but what term_close does is more of a disconnection (you usually
don't want the text written to a terminal to disappear just because the
program that wrote it doesn't have to write anything more). In either case
though both terminal and log file should be closed if they are opened. To
simplify this in case there has been any errors there is a procedure
terminal::cleanup, which effectuates whatever closing operations are
necessary.
Printing error messages
- Proc [terminal::print_err]
- Since an important class of things written to the terminal will be error
messages, there is some point in combining all the related printing actions
in a single procedure call. This is what the terminal::print_err procedure
is for. The syntax is
terminal::print_err {message} {location} {help}?
where {message}
, {location}
, and {help}
are all lists of strings, each of
which correspond to one line of output. The {message}
is the ``official''
message that identifies the error and the {location}
is something which
specifies where the error was detected. The purpose of this argument is to
give some clue to where something should be changed (usually a position in
some input file) to avoid the error. The {help}
, finally, can be used to
give some clue to what one should probably do to fix the error. This
argument is optional, but can be quite helpful if there is a good value for
it. The {help}
lines are only written to the log file. The print_err
procedure adds a "! " to the beginning of each {message}
line (BTW, one
such line is usually sufficient) and it also adds a period to the end of
the last line of the {message}
. The print_err procedure ends by flushing
the output.
Each time print_err is called, it increments the terminal::error_count
variable by one. When the value becomes greater than that of the
terminal::max_error_count variable, print_err will generate the Tcl error
`That makes <error_count> errors, please try again.'. The purpose of this
is to stop programs that run amok generating an endless stream of error
messages. If your program normally generates quite a lot of error messages,
you might want to increase max_error_count; it is 100 by default, but it
will not be overwritten if the value was already set when the terminal
package is loaded. You can also clear the error_count whenever you want,
even though it is probably a good idea to do so only in places where you
know you're making progress. error_count is automatically cleared when you
close the terminal or log file.
Printing progress messages
Another class of things written to the terminal are messages whose primary
purpose is to show the user that some progress actually is being made on
some lengthy job. In a communication channel where the contents can be
updated this is rather straightforward, but in e.g. stdout where text is
just being added one has to find a balance between printing something
frequently enough to show that progress is being made and keeping the
total amount to text written at a reasonably low level. The progress
procedures have a number of features which facilitate maintaining this
balance.
The idea is to only print one character most of the time, but occasionally
(by default every tenth time) print something more which gives some numeric
information about how much work has been done. Thus a progress message
might look something like
Processing. ---------- [40%] ---------- [80%] ----- Done.
where `Processing.' and `Done.' are printed to mark the beginning and end
of the message, whereas the hyphens are printed one at a time as the
processing progresses. The bracketed expressions interrupting the sequence
of hyphens are called markers.
Proc [terminal::make_progress]
The most commonly used command here is terminal::make_progress, which has
the syntax
terminal::make_progress {amount}? {goal}?
It is called to inform the user that progress has been made; the {amount}
and {goal}
numbers are integers telling how much has been done and how much
should be done in total. The percentages in the example above are the
quotients of {amount}
to {goal}
at that particular point.
- Procs [terminal::begin_progress], [terminal::end_progress]
- Besides make_progress, there are also the terminal::begin_progress and
terminal::end_progress procedures; these print the `Processing.' and
`Done.' (both these strings may be changed by giving proper arguments to
the procedures) in the above example.
A typical example of how these procedures are used is
proc do_file {fname} {
terminal::begin_progress -title "Reading `$fname'."\
-percent [file size $fname] -mintime 300
set fid [open $fname r]
while {[gets $fid line]>=0} {
##
# Do some processing of $line
##
terminal::make_progress [tell $fid]
}
close $fid
terminal::end_progress
}
Here there is no {goal}
argument to make_progress since the goal never
changes; instead that number is given after the -percent option to
begin_progress. This options is one of the three begin_progress options
that select the marker format:
- -absolute
- Selects the absolute marker format `[<amount>]', where the numerical
values printed are simply the
{amount}
argument of make_progress. In
this format, the {goal}
amount is never used.
- -percent {goal}
- Selects the percentage marker format (as shown above), with
{goal}
as
the goal amount.
- -fraction
- Selects the fraction marker format `[<amount>/<goal>]'.
If none of these options are used then make_progress prints hyphens, but
never any markers. This is mainly useful if there is no quantity which can
serve as {amount}
. The fraction marker format is mainly intended
for cases where the goal amount can change -- this occurs for example when
the amount is a some number of files to process and files can include other
files with the effect that these too will have to be processed -- and the
goal amount is then updated using the {goal}
argument of
make_progress (if there is no such argument, then the most recently
specified goal amount is used). It is always possible to initialise the
goal amount in the call begin_progress, by using the -goal option:
- -goal {goal}
- Sets the goal amount to
{goal}
.
Another lone option is:
- -title {beginning}
- Sets the string printed at the beginning of a progress message.
Defaults to `Processing.' if not given.
A companion of this option is however that end_progress takes as
an optional argument a string to print at the end of the processing
message. The default for this argument is `Done.'.
The following three options are used to control how often make_progress
will print a marker.
- -callsep {call separation}
- This requests that make_progress must print at least {call separation}
hyphens between each marker printed. The default is 10. Setting this to 0
removes this restriction.
- -relsep {relative separation}
- This requests that the quotient of
{amount}
to {goal}
must have increased
by at least {relative separation} between two successive markers. The
default value is 0, which means nothing is requested. A {relative
separation} of 0.01 means that it has to increase by at least one
percentage point. This condition is ignored if no {goal}
value has been
given.
- -abssep {absolute separation}
- This requests that
{amount}
must have increased by at least {absolute
separation} between two successive markers. The default value is 0, which
means nothing is requested.
Finally, there are three options which can reduce the amount of time spent
reporting progress (and thus in particular the number of hyphens printed by
make_progress). The mechanism behind these is that make_progress begins
with a test related to these options, and if that test fails then
make_progress returns without printing a hyphen or even interpreting its
arguments; the make_progress call is (so to say) effective only if the
test succeeds. Exactly which tests are performed depends on the Tcl
version used, and therefore a calling program might be best off specifying
two options to cover all cases.
- -mintime {milliseconds}
- This option controls a test used in Tcl 8. The
{milliseconds}
is a
bound on the wall clock time that should elapse between two effective
make_progress calls. (Internally the test is implemented using the after
and update commands.)
- -mincmds {command count}
- This option controls a test based on [info cmdcount] (the number of
Tcl commands that have been evaluated in the interpreter); this test
is used in all Tcl versions. The {command count} value is the minimal
number of commands that should be evaluated between two effective
make_progress calls.
- -period {period}
- This option used to control a test based on the number of times
make_progress has been called since the last effective call to it,
but now it controls the same test as the -mincmds option. To make
the effect somewhat comparable however, the
{period}
number is
multiplied by 100 before it is used as a {command count}.
All of the above has described what is written to the log file and stdout
(when that serves as terminal), but when the terminal is an Alpha window,
only the beginning and end strings are written to it. The user is instead
given numerical information about the progress through a message shown on
the status bar. The exact format of this message depends on which marker
format is selected, but it is generally more verbose than the markers (as
the information doesn't accumulate, there is no need to keep it short).
Printing other things between the corresponding calls to begin_progress and
end_progress is not a problem, but it is probably a good idea to ensure
that this material is separated from what is before and after it by
newlines. This is automatically the case for material printed using
print_err.
The AlphaTcl package and its preferences
There are in fact two AlphaTcl packages associated with the terminal
Tcl package--the terminal library and the terminalPuts feature. This
division stems from their respective inpacts on the Alpha user
environment; terminal provides some code and preferences which do nothing
unless explicitly called upon, whereas terminalPuts redefines the puts
command to use the *terminal* window for the stdout and stderr channels.
Since terminalPuts is secondary, the deinstallation information is tied
to the terminal library.
There are three AlphaTcl preferences associated with the terminal
package. These become available for editing in the
Alpha ↣ Preferences ↣ Package Preferences ↣ Terminal Prefs dialog
when the terminalPuts package is activated. They are also available as
the mode preferences when the terminal window is the current.
- Window Geometry
- This preference can be used for setting a default size and
position for the terminal window.
- Max Print Line
- This is a default value for the maxPrintLine variable, that places
a limit on how long the lines written using print_word and friends
may be. This is useful if you set the terminal window to be narrow.
- Take Over Puts
- This flag is only declared if terminalPuts is active. It controls
a hack that redefines [puts stdout] and
[puts stderr] so that text written to these files
will instead show up in the terminal window. When the flag is on,
terminal redefines the puts command. Turning the flag off (or
deactivating the terminalPuts feature) restores the default definition.
All windows opened by terminal::term_open and terminal::term_autoopen
have "Term" as mode (although there is no declaration of that mode as
an Alpha package; it turns out that isn't needed). This can be useful
if you want to add for example syntax colouring to terminal windows.
Notes
In their current state, the mechanisms provided by terminal (with the
exception of the progress message stuff) are modelled after how TeX behaves
when it is in the \nonstopmode interaction mode. If ways for the user to
interact with the program are added then it might be possible to simulate
the \scrollmode and \errorstopmode as well.
The progress stuff has no immediate counterpart in TeX, though -- if it
looks terrible then I am to blame.
The terminal commands do not give special treatment to linefeed or
carriage return characters in the string printed, meaning these are passed
straight through as any other character, but there usually are underlying
mechanisms which use either of these characters to signify a new line. Thus
if the terminal commands are asked to print strings containing linefeeds or
carriage returns then new lines may start at times where you did not expect
it. The general cure to this is to [split] any string
that may contain newlines into a list of lines and then print this list
using [terminal::print_block].
An example
An example of what the terminal package can do can be found in the
file Terminal-Example.tcl. This file contains a
script which finds the undocumented Tcl commands that are built into Alpha,
using the terminal package to report its finds.
Known problems
Please report any problem or bug you encounter to
Alpha's Bug Tracker.
License and Disclaimer
Original Author: Lars Hellström.
Copyright (c) 2001-2019, Lars Hellström.
All rights reserved.
The Terminal package is free software and distributed under
the terms of the new BSD license:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of Lars Hellström nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LARS HELLSTRÖM BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
History:
modified | by | rev | reason |
2001-07-21 | LH | 1.0 | original |
2002-05-30 | LH | 1.1 | updated for terminal v1.3 |
2002-12-29 | LH | 1.2 | updated for terminal v1.4 |
2003-12-18 | LH | | updated for terminal v1.5 |
2004-05-07 | LH | | updated for terminal v1.5.1 |