.flags overstrike
.keep
.nast
.nap
.no headers
.page size 58,73
.nopaging
.left margin 8
.right margin 73
.spacing 1
.nofill
.nojustify
.paging

















.c;Enhancing the VAXTPU

.c;EDT Keypad Emulator




.c;by

.c;Michael L. Penix

.c;and

.c;Richard D. Piccard

.b-8
.c;Kalamazoo College
.c;Kalamazoo, Michigan


.c;December 1, 1985
.number page 1
.page


.c;Enhancing the VAXTPU EDT Keypad Emulator
.headers



^&Abstract\&


    We report the development of customizations to the VAXTPU EDT
Keypad emulator that closely parallel those reported in the past
for EDT itself:  word processing enhancements, multi-buffer
operations, etc.  In addition, the reported customizations
include dual-window operation and overstrike mode text entry.  We
discuss the choices made in coding, some basic techniques for
debugging with TPU, and several portions of the code itself.
This article, the code, and a companion article describing the 
customized enviromonment are being submitted for inclusion in the
VAX SIG Symposium tape for the December, 1985 DECUS Symposium. 



^&Introduction\&
.title ########Introduction


    VMS Version 4.2 includes the new VAXTPU Text Processing
Utility, together with two user interfaces, EVE (the Extensible
VAX Editor) and the EDT Keypad Emulator.  For those users who
have been working with "plain-vanilla" EDT, the latter is likely
to prove quite adequate, requires very little re-learning, and
will therefore be chosen on the basis of its far superior
performance:  in one benchmark it finished in half the elapsed
time while consuming CPU time at one quarter the rate that "real
EDT" consumed it. 

    For those of us, however, who have customized EDT with
extensive EDTINI.EDT files, and especially for managers who have
provided standard EDTINI.EDT files to new users, the choice is
not immediately obvious.  Our experience at Kalamazoo College
should prove quite encouraging to others, since we made the
transition in about a week, for over 95_% of our usage, and we
had extensive EDTINI.EDT files in place for all of our users. 

    This paper discusses the choices made in coding the
enhancement files, the techniques that proved effective for us in
making the transition, and several portions of the TPU code
itself.  Listing 1 is the EDTINI.EDT file we started with.  As
can be seen, we have a variety of commands, one making EDT even
more pleasant to work with for programmers by selecting alternate
delimiters for "WORD" motion and deletion commands, and many
enhancing EDT for word processing (defining keys for sentence and
paragraph motions, for example).  This file had evolved from one
presented several years ago in ^&The DEC Professional\&. 



^&Choices\&
.title ########Choices


    TPU provides two layers of enhancement, "section" and
"command" files.  Section files are pre-compiled, and therefore
should be chosen for the stable bulk of the enhancements.  The
DIGITAL-supplied section file for the EDT Keypad Emulator is
SYS$LIBRARY:EDTSECINI.  (The filename extension is .TPU for the
source code and .GBL for the binary code; the default when used
by TPU as a section file is .GBL and when used as a command file
is .TPU.)  TPU looks for a section file in SYS$LIBRARY: named
TPUSECINI.GBL unless the command line includes a /NOSECTION or a
/SECTION=filename option.  Command files are read-in and compiled
at edit session startup, and should therefore be kept short and
used for the volatile portion of the enhancements.  TPU looks for
a command file named TPUINI.TPU in the current default directory,
unless the command line includes a /COMMAND=filename option, or a
/NOCOMMAND option.  (If you specify /NOSECTION/NOCOMMAND, then
the only action possible is to exit by CTRL/Y.) 

    A simple method to put a section file to work is to define a
DCL symbol by a line such as the following at some point in a
login command file: 

$ ED*IT  :==  EDIT/TPU/SECTION=filename

Since no /COMMAND option is specified, it will search the 
current default directory, as mentioned above, for TPUINI.TPU.  

    Although our ultimate goal was of course the creation of a
custom section file, we worked for quite a while with a (rapidly
growing!) command file.  The time to turn the nascent section
file into a real one is when the start-up delay becomes
irritating, or when you are far enough along that you want others
to start trying to use the product of your efforts.  We will
discuss below the modifications needed to change a working
command file into a working section file.  

    The most reasonable items to leave in the command file are
the two items most likely to be modified by individuals:  the
word wrap margin and the first tab size.  These should be
initialized in the section file, also, so that new converts need
not have a copy of TPUINI.TPU in their directories.  In order to
assist ambitious users by providing examples, a few stable
routines and key definitions should also be placed in the version
of TPUINI.TPU that is made available for users to copy. 



^&Techniques\&
.title ########Techniques


    Probably the key technique to rapid implementation is the 
prompt switching over to the partly customized editor as soon as 
possible.  As you work with it to implement further changes or 
to refine those already in place, the lack of a customization 
you are used to in real EDT will act as a goad to keep working.  
It will also ensure that you implement the most important (i.e., 
heavily used) features first. 

    We have followed the method discribed in the ^&VAXTPU Users 
Guide\& for creating new section files layered on top of existing 
ones.  In particular, our file "KAZSECINI" is layered on their 
"EDTSECINI".  During the early development phase, then, we dealt 
with our code as a command file and used theirs as the section 
file.  Later on, the "old" portion of our code, together with 
their code, served as section file and the "new" portion of our 
code served as command file or was the file being edited.  

    The advantages of layering deserve some discussion:  we can
use their code when it serves our purpose and replace it if we
want to.  The design of TPU as a programming language includes
named procedures as well as local and global variables, and
DIGITAL's code in EDTSECINI.TPU is careful to start the names of
all procedures and global variables with "edt$", so by observing
reasonable naming conventions we can keep track of which portions
of our code are vulnerable to changes in theirs.  We chose to
name all of our procedures and global variables with names
starting "KAZ$". 

    During the first development phase, when all customizations
are in the form of a command file, the structure is as shown in
Fig. 1:  procedure declarations and code, followed by key
definitions to implement those procedures, and finally assignment
statements initializing the global variables.  During this phase
the file is used as the command file, either by naming it
TPUINI.TPU or by defining a symbol for DCL that includes specific
designation of the command file. 
.number page +1

    In the later phase, when the initial customizations are in
the form of a section file, the structure is as shown in Fig. 2:
first, a procedure named TPU$LOCAL__INIT (see below), second, all
the other procedure declarations and code, third, a procedure (in
our case named KAZ$DEFINE__KEYS) that, as you might suspect from
its name, includes all of the key definitions, and finally, the
commands to SAVE the current context in a named file and to QUIT.
The filename given should be different, either in directory or
extension or both, from the current production section file. 
.number page +1

    The DIGITAL file EDTSECINI ends with a call to
TPU$LOCAL__INIT, as well as including an empty definition of that
procedure.  By including a procedure with that name in our file,
we replace their empty one.  This procedure includes
initialization of our global variables, re-initialization of some
of the EDT$ global variables (changing the default first tab size
and word wrapping margin, for example), and a call to our
procedure KAZ$DEFINE__KEYS. 

    Although the following sequence may seem cumbersome in
writing, it works well in practice.  The main problem is the
obscurity of the error messages that TPU provides, a traditional
problem with the first release of any compiler which is
compounded here by the limited size of the message buffer and the
consequent default terseness of those messages.  It is therefore
essential to maintain efficiency by ^&incremental\& enhancements,
checking each new procedure and key definition as you go along.
As few changes as possible should be made at one time! 

    For the sake of discussion, assume that these logical names
have been defined:  WORK: for the device and directory containing
the evolving section file, and GOOD: for the device and directory
containing the section file and standard command file most
recently released for public use.  Several DCL symbols are
useful, as well; the following lines were included in the 
author's LOGIN.COM:

$ NEWTPU  :==  EDIT/TPU/SECTION=SYS$LIBRARY:EDTSECINI-
    		/COMMAND=WORK:KAZSECINI
$!
$ NED     :==  EDIT/TPU/SECTION=SYS$LOGIN:KAZSECINI.NEW-
    		/COMMAND=GOOD:TPUINI
$!
$ WED     :==  EDIT/TPU/SECTION=SYS$LOGIN:KAZSECINI.GOOD-
    		/COMMAND=GOOD:TPUINI


    These definitions were used with a SAVE command that
specified the new binary file as SYS$LOGIN:KAZSECINI.NEW.  Thus,
each time we thought the code was correct, we gave the command
NEWTPU and watched to see if any error messages were displayed.
If all seemed well, then we used the command NED to use the new
editor and tried out the modified or newly introduced routines.
If all was well, we RENAMED the .NEW file to .GOOD, thereby
setting aside a working enhancement.  At the same time a copy of
the source code was set aside with a suitably distinctive name in
WORK:.  From then on routine editing was done with the command
WED, using those working enhancements.  

    As each round of enhancement is completed, WORK:KAZSECINI.TPU
is copied to GOOD:* and SYSLOGIN:KAZSECINI.GOOD is copied to 
GOOD:*.NEW.  Then the protection of GOOD:KAZSECINI.NEW is set
correctly and finally GOOD:KAZSECINI.NEW is RENAMED to *.GBL,
thereby replacing the one in use. 

    If there is a mistake in the code (even as simple as a
missing ";" termination for a statement), the only message
usually visible is "compilation aborted at line 1."  In order to
debug at all efficiently, we took the advice offered us by the
Customer Support Center and used the following command sequence
from the "TPU Command: " prompt elicited by <PF1> + <KP7>: 

    SET (INFORMATIONAL,ON)
    COMPILE(CURRENT__BUFFER)

and then, if errors are reported, type CTRL/Z and  at the "*"
prompt, "= message", to inspect the message buffer (our code
includes a less clumsy way to switch buffers, <PF1> + B).  If the
buffer being compiled includes routines with names the same as
any already in place (from DIGITAL's EDTSECINI, or the local
customizations, or previous compilations during the same edit
session), an informational message that the old procedure was
replaced will be generated.  Since the message buffer is limited
in size, the unit being compiled should ordinarily be limited to
as few procedures as possible. 

    For enhancements, our routine is to write the new procedure
and the key-binding command in a separate file, compiling and
re-compiling as we go.  When it seems logically complete and
generates no compiler errors, we cut it out of that buffer and
paste it into the growing section in the main buffer, placing it
at the end of the regular procedure declarations and finally
cutting and pasting the key-binding statement into the procedure
KAZ$DEFINE__KEYS. 

    In the case of a flawed procedure that we seek to correct,
once the full file is in place, we create a separate file with
the suspect procedure as its only contents.  Then we edit that
file.  After modifying it we give the TPU command
"COMPILE(MAIN__BUFFER)", and then (if no compiler errors were
generated) select an alternate buffer and include text from a
file known to elicit the misbehavior whose correction we seek.
Because the revised procedure has the standard name, the
compilation of the main buffer re-defined the procedure that will
be executed upon typing of the standard key sequence bound to
that procedure. 
.title #


.test page 12

^&The Code\&
.title ########Code


.c;Search Patterns


     A search pattern may be as simple as the string being 
sought itself, or it may include TPU builtins and operators to 
^&describe\& that string.  In search patterns the symbol '_&' is the
CONCATENATION operator.  Thus the pattern defined by 

             'a' _& 'b' _& 'c' 

is identical to that defined by 'abc'.

     The symbol '|' is the OR or ALTERNATION operator.  Thus the
pattern defined by 

             'a' | 'A'

will be matched by any a, regardless of the case.  

     These two symbols may be used to form more complex search
patterns.  The pattern 

             ('a'|'A') _& ('b'|'B')

will match any sequence 'ab' regardless of the case of either
letter. 

    The VAXTPU Reference Manual, section 2.8.4, "Modes of 
Searching", discusses ^&incremental\& and ^&seek\& searching.  
The latter is the default because it is more efficient if it 
will produce the desired result.  They differ on the handling of 
alternate parts of the pattern.  This will matter only if there 
may be more than one location in the buffer that matches the 
pattern.  

    Seek searching essentially "multiplies out" the pattern, like
the distributive law of algebra, producing a set of pure
concatenation possibilities.  A search is then performed, through
to the end of the buffer for each of those possibilities in turn.
The overall search terminates successfully upon encountering of
the first instance of any of these possibilities.  Thus, an
earlier instance of a later possibility will be skipped over by a
seek mode search if there is any instance of the earlier
possibility. 

    Incremental mode searching, on the other hand, will 
terminate with a successful match at the first instance of a 
string matching ^&any\& of the possiblities.  Any alternation that is 
enclosed in parentheses and preceded by a concatenation will be
searched using the ^&incremental\& mode.  This is why we have on 
occasion started our patterns with the string ''_&. 



.c;Bug Work-arounds


    With help from William White of the Colorado Customer Support
Center, we have implemented a work-around for a bug in the FILL
command.  The problem manifests itself whenever the beginning of
the SELECTED region is at the start of or within a word that ^&ends\& 
beyond the margin for wrapping.  The result of executing the FILL
command under these circumstances is that a line break is
inserted at the start of the select region, whether it is beyond
the margin for wrapping or not, and whether it is in the middle
of a word or not!  The concept of the work-around is simply to
ensure that the beginning of the SELECT region is moved to the
start of the line before executing the FILL command. 

    The work-around is implemented by copying DIGITAL's procedure
EDT$PRESERVE__BLANKS(flag), creating a procedure named
KAZ$FIND__BEG__OF__LINE(b__mark), and then modifying 
EDT$PRESERVE__BLANKS, by adding a call to KAZ$FIND__BEG__OF__LINE, at 
the third line of the main sequence of the code, just prior to
the call to EDT$SKIP__LEADING__SPACES(b__mark).  
KAZ$FIND__BEG__OF__LINE moves b__mark from its original
location to the beginning of the line that it had been in. 



.c;Replacement Customizations


    The following procedures are among those that implemented 
equivalent functions to those that we already had in place 
through EDTINI.EDT.  



.c;Word Delimiter Changes


PROCEDURE KAZ$SWAP__DELIM


    The procedure KAZ$SWAP__DELIM allows the user to toggle back
and forth between two sets of characters to be taken as the
delimiters between words.  The two sets are optimized for word
processing and for programming.  The word delimiters in word
processing are a space, a tab, a form-feed, a line-feed, a
carriage return, and a vertical tab.  The word delimiters for
programming include all the above as well as the characters
"/<>[]{},.:*_&!;+-=^()_\|'" which constitute most of the "algebraic
punctuation" that is used in FORTRAN, DCL, and PASCAL programs. 

    DIGITAL's code includes a global variable EDT$X__WORD that is
the set of characters used to identify word breaks.  We maintain
a variable called KAZ$WORD__DELIM with the value of either 'text'
or 'program' to indicate the current editing context:  word
processing or programming.  Procedure KAZ$SWAP__DELIM uses an
if-then-else statement to determine the current editing context
from the variable KAZ$WORD__DELIM and then toggles the two 
variables, EDT$X__WORD and KAZ$WORD__DELIM.  For instance, if upon 
entering this procedure KAZ$WORD__DELIM has the value 'text', then 
the old context is word processing.  The programming delimiters
are assigned to EDT$X__WORD and the value 'program' is assigned to 
KAZ$WORD__DELIM, indicating the new editing context. 



.c;Word Processing Conveniences


DEFINE__KEY ('READ__FILE (''STANDARD.RNO'')',
    		KEY__NAME('R',SHIFT__KEY),
    		"Insert a copy of the file STANDARD.RNO.");


    This defines <PF1> + R to include a file named 'STANDARD.RNO'
on the lines preceding the line containing the current position
of the cursor.  This filename results from the routine usage of
this command to include a user's standard RUNOFF initializations.
Obviously, any sort of contents could be maintained in that file.

    This definition can be easily modified to include any
frequently used file into the current editing buffer by
substituting the appropriate name for STANDARD.RNO.  Logical 
name assignments would provide yet another mechanism for 
generalizing the use of this command.  



PROCEDURE KAZ$GET__OUT


    This procedure eliminates the following command sequence: 
"CTRL/Z + EXIT + <RETURN>".  This command sequence does a normal
exit out of TPU, saves the current buffer under the filename that
was used at the starting of the editing session and deletes the
journal file.  We have chosen <PF1> + CTRL/Z to cause the
execution of this procedure. 

    The first line, "write__file(main__buffer)", writes out the 
main buffer.  The second line, "set(no__write,main__buffer,on)", 
indicates that upon the termination of the editing session the
main__buffer is not to be written out, since we just performed 
that function in the previous line.  The last line, "exit",
simply tells TPU that the editing session is to be terminated and
the journal file is to be deleted.  The TPU command "quit" would
have worked equally well here because we took care of writing out
the buffer ourselves. 


KAZ$FILL_PARAG


    This permits an entire paragraph to be re-filled at once.
We take real EDT's default definition of a paragraph:  a block of
text bounded by a line containing no characters, or by the
beginning or end of the buffer.  The right margin used is the
same as the current EDT "wrap" emulation.  The cursor is returned
to the original location in the buffer.  The entire operation is
performed without ongoing screen updating.  Our procedure to move
to the beginning of the current paragraph is used to establish
the start of the range to be filled.  Then a search is conducted
forwards for the end of the paragraph.  If it fails, the
paragraph ends at the end of the buffer.  If it succeeds, we move
back one character to avoid swallowing the blank line between
paragraphs.  The word separators used for the fill operation are
the current word delimiters, EDT$X__WORD. 



KAZ$END__SENT


     First a short discussion on the search command is warranted.
Search uses three parameters:  the first parameter is the search
pattern.  The second parameter is the direction of the search.
The third parameter is a qualifier on the search.  In this
proceedure the direction is always FORWARD and the qualifier is
always EXACT.  The search command, as used in this procedure,
returns the range of the first exact pattern match that is
forward of the cursor position.  The returned range will be 0 if
the pattern was not found. 

     In KAZ$END__SENT, the search command is used three times.
The first search looks for the end-of-sentence delimiters that
are defined by the search pattern KAZ$SENT__DELIM.  One character
string that will match that pattern is ". ", a period followed by
a space character.  The initialization of KAZ$END__SENT will be
described later.  In this procedure, if the pattern is not found,
i.e., the search range returned to end__sent__range was 0, then the
procedure is exited.  If the pattern is found, then the cursor is
positioned to the beginning of the delimiter.  The second search
looks for either a space character or the end of the line.  The
cursor is then positioned to the beginning of that pattern if it
was found (thereby skipping over the printing characters of the
sentence delimiter) or the procedure is exited.  The third search
looks for either line__begin or the first non-space character
(thereby finding the beginning of the next sentence).  If the 
next sentence is found, the cursor is positioned there, otherwise
the procedure is exited. 

    KAZ$SENT__DELIM is initialized in procedure TPU$LOCAL__INIT.
The search pattern consists of the concatenation of the empty
string (to force incremental searching) with two alternations:
first, the punctuation, '.', '?', or '!', and second the
conclusion which may include a "concealing" character such as
']', '}', ')', or '"', and terminates with a space or line__end.
The second alternation is rather extended, since the various
combinations of concealment and termination are all explicitly
given.  This implementation is significantly more versatile than
true EDT's +SEN and -SEN commands, since those do not recognize
any "concealed" sentences. 



.c;New and Wonderful Options


KAZ$WINDOWS


    This procedure permits selection of single- or double-window
editing.  After interactively specifying the number of windows,
the user is asked for the name of the second buffer.  If the
buffer is new, the user is asked for the name of the file to edit
and whether that file should be written to disk upon exit.  The
windows and buffers are established and mapped, and keys are
suitably re-defined for moving between buffers.  Global variables
are established containing the names of the first and second
windows and buffers. 

    In normal usage, before executing KAZ$WINDOWS, we define PF1
+ M to execute a procedure that moves the cursor to the main
buffer and map that buffer to the main window.  That procedure
was created by copying the support routine for the EDT line mode
"= buffer" command from page 22 of EDTSECINI, and then
specializing it to go for the main buffer.  In normal usage,
before executing KAZ$WINDOWS, we define PF1 + B to execute a
procedure (even more closely modeled on the "= buffer" support
from EDTSECINI) that prompts for the name of the buffer, moves
the cursor to it, and maps it to the main window.  Thus, those
keys are routinely used to return to the main buffer or to shift
to a named buffer.  KAZ$WINDOWS restores these definitions
whenever single-window editing is specified.  When dual-window
operation is to commence, PF 1 + M is defined to move to the
first (upper) window, and PF1 + B is defined to move to the
second (lower) window. 

    We have defined PF1 + <UP ARROW> to jump back a screen, and 
PF1 + <DOWN ARROR> to jump forward a screen.  This is 
implemented using a global variable, KAZ$WINDOW_SIZE, whose 
value is initialized to 21 for single-window editing and is set
to 10 for dual-window editing.  The action of the screen jump 
command is thereby adjusted to avoid either overlap or skipped 
lines as one moves through a buffer.  

    The sizes of the windows are adjusted appropriately to allow
for status lines at the bottom of each window, clarifying the
boundary between them.  The scrolling limits are set so that a 
reasonable amount of context is presented before and after the 
cursor, while still permitting some range of motion without 
stimulating excessive output to the terminal.  



KAZ$OVERSTRIKE 


     This procedure is used to swap between the overstrike and
insert modes using one key definition rather than using a key
definition for each mode.  The procedure KAZ$OVERSTRIKE uses a
global variable called KAZ$ENTRY__MODE to indicate the current
editing context.  This allows the defined key to take on a dual
function depending on which 'state' the variable KAZ$ENTRY__MODE
is in.  For example:  if KAZ$ENTRY__MODE is 'insert', the current
editing context is insert mode and the procedure should switch
into overstrike mode.  In the procedure an if statement
implements the above logic; if the mode is not 'insert' then the
else clause is executed to switch to 'insert' mode.  The commands

            SET(OVERSTRIKE, CURRENT__BUFFER); and
            SET(INSERT, CURRENT__BUFFER);

switch the current editing context of the current buffer to
overstrike and insert respectively.  After the editing context is
set, then the variable KAZ$ENTRY__MODE is updated to 'overstrike'
or 'insert', respectively. 



^&Conclusion\&


    The VAX Text Processing Utility has certainly demonstrated
its utility!  It provides virtually the full functionality of the
EDT Keypad environment, and can be readily enhanced both in ways
that EDT can and in ways that EDT cannot.  While providing this
superb editing capability, it consumes significantly less CPU
time than EDT itself to do the same job.  Thus users see a more
friendly system that responds faster, too.  



^&Acknowledgement\&


    We wish to express our thanks to the staff of the Customer
Support Center in Colorado Springs, several of whom have provided
quite useful suggestions.  The file EDTSECINI.TPU provided with
VMS 4.2 contained many useful examples.  In fact, some portions
of the code we used were simply copied from there and modified
slightly.  A variety of useful examples from the ^&VAXTPU Users 
Guide\& have also found their way into our code.  The users of
Kalamazoo College's Educational Computing VAX system tolerated
being forced from EDT onto an early version of the customizations
reported here, and have survived the various stages of refinement
or brutalization it has gone through since, so to them we owe
especial thanks. 
.noheaders
.page

.lt


Enhancing VAXTPU EDT                  M.L. Penix and R.D. Piccard



               _____________________________________
               |                                   |
               |             Procedure             |
               |                 .                 |
               |                 .                 |
               |                 .                 |
               |            Endprocedure           |
               _____________________________________

               _____________________________________
               |                                   |
               |          Key-definitions          |
               _____________________________________

               _____________________________________
               |                                   |
               |       Assignment statements       |
               _____________________________________




              Figure 1:  TPU Command File Organization
.el
.page

.lt

Enhancing VAXTPU EDT                  M.L. Penix and R.D. Piccard



               _____________________________________
               |                                   |
               |     Procedure TPU$LOCAL_INIT      |
               _____________________________________

               _____________________________________
               |                                   |
               |         other procedures          |
               _____________________________________

               _____________________________________
               |                                   |
               |     Procedure KAZ$DEFINE_KEYS     |
               _____________________________________

               _____________________________________
               |                                   |
               |     SAVE SYS$LOGIN:filename.NEW   |
               |     QUIT                          |
               _____________________________________



             Figure 2:  TPU Section File Organization
.el
