.flags overstrike .keep .nast .nap .no headers .page size 58,73 .nopaging .left margin 8 .right margin 73 .spacing 1 .nofill .nojustify .paging .c;Teaching a New Dog Old Tricks: .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;DRAFT of September 30, 1986 .number page 1 .page .c;Teaching a New Dog Old Tricks: .c;Enhancing the VAXTPU EDT Keypad Emulator .headers ^&Abstract\& We report the further 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, DCL Command Window, keystroke sequence learning, buffer write and compile, 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. ^&Introduction\& .title ########Introduction VMS Version 4.2 included 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. Now that VMS 4.4 provides the .EXE format for section files, they can be installed shared, significantly reducing memory consumption, which on some systems negated the performance advantage under VMS 4.2. For those who have customized EDT with extensive EDTINI.EDT files, and especially for managers who have routinely provided such 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 had a variety of commands, one making EDT even more pleasant to work with for programmers by including algebraic punctuation among the 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 by David Spencer in the September, 1982, issue of ^&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 .TPU$SECTION for the binary code; hence the default when used by TPU as a section file is .TPU$SECTION 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. If you have assigned MAIL$EDIT to be "Callable__TPU", then this default section file will be used also for such MAIL commands as "REPLY/EDIT", "SEND/EDIT", etc.. The preferred method to change section files is to assign the appropriate meaning to the logical name TPUSECINI, either on a system-wide basis or, by using LOGIN.COM, on a process basis. This will work for MAIL's /EDIT, as well as with the EDIT/TPU DCL command. TPU 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.) Listing 2 is a TPU command file. Although the comments may seem verbose, they add only 150 CPU milliseconds (on a VAX-11/750) to the startup of an editing session, and they certainly increase the odds that a user new to TPU will be willing to make the modifications he or she wants to have. A simple method to put a personal section file to work is to define a DCL symbol by a line such as the following at some point, interactively or in a login command file: $ ED*IT :== EDIT/TPU/SECTION=filename Since no /COMMAND option is specified, TPU will search the current default directory, as mentioned above, for TPUINI.TPU. Once the system manager has selected a standard section file for the installation, two methods are possible. The new section file can be copied to SYS$LIBRARY:TPUSECINI.TPU$SECTION, or it can be given a different name, such as our KAZSECINI, and a line such as the following included in SYS$MANAGER:SYSTARTUP.COM: $ DEFINE/SYSTEM/EXEC TPUSECINI SYS$SHARE:KAZSECINI.TPU$SECTION The new file must be correctly protected of course. If either of these approaches is used, then the /SECTION will not be needed in the symbol definition or explicit EDIT/TPU command if that section file is to be used. 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 may also be placed in the version of TPUINI.TPU that is made available for users to copy (at the expense of more startup overhead and some disk space for each user), or the whole custom section file itself may be made available for copying (at the expense of a lot of disk space used by those who keep copies). ^&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. .c;Command ^&vs\& Section Files 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. 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. DIGITAL's code in EDTSECINI.TPU consistently starts the names of all procedures with "edt$", and of all global variables with "edt$x__", 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 with names starting "KAZ__", and all of our global variables with names starting "KAZ__X__". 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 in the SAVE command should be different, either in directory or extension or both, from the current production section file. Taking a trick from EDTSECINI.TPU, just before the SAVE, the procedure that defines the keys is re-defined to a null procedure, thereby saving unneeded code space, since the key definitions are saved directly. .number page +1 DIGITAL's 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$x__ global variables (changing the default first tab size and word wrapping margin, for example), and a call to our procedure KAZ__DEFINE__KEYS. .c;Debugging Following the precept that computer science is an experimental science (!), we have found that the TPU compiler can be reasonably effective at finding bugs. We have not yet resorted to the special debugging aids that are provided, but as the section file grows more complex, we may have to. The main problem is the obscurity of the error messages that TPU provides, a traditional problem with the first release of any compiler that is compounded here because the limited default size of the message buffer encouraged the developers to keep those messages terse. 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! Although the following sequence may seem cumbersome in writing, it works well in practice. 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.PAS $! $ WED :== EDIT/TPU/SECTION=SYS$LOGIN:KAZSECINI.GOOD- /COMMAND=GOOD:TPUINI.PAS These definitions were used with a SAVE command that specified the new binary file as SYS$LOGIN:KAZSECINI.NEW. The command file TPUINI.PAS sets the margin, tab, and word delimiters to those for PASCAL, which are also appropriate for working with TPU code. Thus, each time we think the code is correct, we give the command NEWTPU and watch to see if any error messages are displayed. If all seems well, then we use the command NED to invoke TPU with the new section file and try out the modified or newly introduced routines. If all is well, we RENAME the .NEW file to .GOOD, thereby setting aside a working enhancement. At the same time, a copy of the source code is set aside with a suitably distinctive name in WORK:. From then on routine editing is done with the command WED, using those working enhancements. As each round of enhancement is completed, WORK:KAZSECINI.TPU is copied to GOOD:* and SYS$LOGIN: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 *.TPU$SECTION, thereby replacing the one in use. Now that we have stabilized the code, we also make copies, suitably protected, in SYS$SHARE: named TPUSECINI.TPU$SECTION and KAZSECINI.TPU$SECTION. Under VMS V4.4 we now use the INSTALL utility, with the /HEADER__RESIDENT, /OPEN, and /SHARE options, to make the section file "known". In this case, the above changes are not actually in use until the REPLACE command of INSTALL has been used, or the system rebooted. If there is a mistake anywhere 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 + : SET (INFORMATIONAL,ON) COMPILE(CURRENT__BUFFER) and then, if errors are reported, inspect the message buffer ( + B invokes our general procedure to switch buffers, simply respond "message" when asked which buffer). 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. Another useful TPU command in this context is SET (max__lines, message__buffer, 400) which will provide a quite adequate space for recording the compiler's output. 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 edit the section file, set up another buffer and include the new code. Then we cut the code out of that buffer and paste it into the growing section in the main buffer, placing it among the regular procedure declarations, cut and paste the key-binding statement into the procedure KAZ__DEFINE__KEYS, and finally edit the table of contents to indicate the location of the new procedure. In the case of a flawed procedure that we seek to correct, once the full file is in place, we either create a separate file with the suspect procedure as its only contents, or cut and paste it into a buffer of its own. Then we edit that file or buffer. After modifying it we give the TPU command "COMPILE (CURRENT__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 newly compiled procedure will be executed upon typing of the standard key sequence for that function, so we can test the revisions. .title # .test page 12 ^&The Code\& .title ########Code .c;Search Patterns 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 that indicates whether the pattern matching is to be case-sensitive, or not. The search command returns as a range the first pattern match that it finds. The returned range will be 0 if the pattern is not found. A search pattern may be as simple as the string being sought itself, or it may include TPU built-ins and operators to ^&describe\& that string. In search patterns the symbol '_&' is the CONCATENATION operator. Thus, for example, 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. (The SEARCH command itself can be told to ignore case.) An equivalent search pattern is ANY('Aa') In general, the ANY built-in will permit more efficient machine code to be generated by the TPU compiler than will be generated from the equivalent alternation of single character strings. Alternation and concatenation 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 list of pure concatenation possibilities for the whole string. A search is then performed, through to the end of the buffer, for each of those complete possibilities in turn. The overall search terminates successfully the first time an instance of any of these possibilities is encountered. Thus, an earlier instance in the buffer of a later possibility on the search list will be skipped over by a seek mode search if there is an instance ^&anywhere\& in the buffer (between the starting position and the beginning or end of the buffer, depending on the direction of the search) of one of the earlier possibilities on the list. Incremental mode searching, on the other hand, will terminate with a successful match at the first instance in the file of a string matching ^&any\& of the possiblities on the list. 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 ''_&. If the alternation is of single characters, the ANY('string') built-in will provide higher performance, especially since it permits removal of the initial ''_&. (See J. Boes' report on the TPU sessions at the December, 1985, DECUS Symposium, page L_&T-17 of the March, 1986, DECUS U.S. Chapter SIGs Newsletter.) In the case of our sentence delimiter pattern, discussed in the next paragraph, using ANY decreased CPU consumption for a sentence advance by a factor of three, and then removing the initial ''_& decreased CPU consumption by a further factor of two! That factor of six reduction in CPU time transformed an annoyingly slow step to a pleasantly sudden one. Our sentence delimiter pattern, KAZ__X__SENT__DELIM, is initialized, as shown in Fig. 3, by the procedure TPU$LOCAL__INIT. The search pattern consists of the concatenation of two alternations: first, any of the punctuation, '.', '?', or '!', and, second, the conclusion which terminates with either a space or line__end and may include any "concealing" character such as ']', '}', ')', ''', or '"'. The second alternation is rather extended, since there are four possible combinations of concealment and termination. This pattern, as used in procedures KAZ__END__SENT and KAZ__TOP__SENT, is significantly more versatile than true EDT's +SEN and -SEN commands, since those do not recognize any "concealed" sentences. kaz__x__sent__delim := ANY('.?!') _&( ' ' | line__end | (any('''")}]') _& ' ') | (any('''")}]') _& line__end) ); .c;Figure 3: Sentence Delimiter .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! This bug was present in the initial release of TPU and is still present with the version included in the VMS 4.4 distribution. 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) and then modifying it by adding three lines, just prior to the call to EDT$SKIP__LEADING__SPACES(b__mark) at the third line of the main sequence of the code, as shown in Fig. 4. Those three lines move b__mark from its original location to the beginning of the line that it had been in. ! skip leading spaces on first line only position (b_mark); move_horizontal (-current_offset); b_mark := mark(none); ! ! the above three lines are Kalamazoo's fix of a bug. ! edt$skip_leading_spaces(b_mark); position(original_position); .c;Figure 4: A portion of the code, including the fix. Another weakness in the EDTSECINI code provided by DIGITAL is that CTRL/T (tab adjust) is designed to force the indentation of each line to ^&be\& a multiple of the current tab size, instead of just ^&changing\& the indentation ^&by\& a multiple of the current tab size. The following two lines of code (40 lines down from the LOCAL statement at the beginning of procedure EDT$TAB__ADJUST) should be replaced by the single line indicated after them: tab__level := get__info(current__buffer,'offset__column') / edt$x__tab__size; edt$x__Tab__goal := (tab__level + adjust__level) * edt$x__tab__size; should be replaced by edt$x__Tab__goal := get__info(current__buffer,'offset__column') - 1 + (adjust__level * edt$x__tab__size) ; This creates a more accurate emulation of EDT, and also has the very attractive attribute of preserving the relative indentation of all lines, unless, of course, you move a block of text leftward and some of the lines reach the left margin. Although not perhaps a bug, EDTSECINI is weaker than needed when moving vertically in the presence of characters, for example. We have implemented a procedure KAZ__MOTION closely modeled on the code presented in the session "Programming with TPU" at the DECUS Symposium in Anahiem, December, 1985. This is used by the and keys, and by the window jump commands + and + . .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;Programmers' Delights 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__X__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 construction to determine the current editing context from the variable KAZ__X__WORD__DELIM and then toggle the two variables, EDT$X__WORD and KAZ__X__WORD__DELIM. For instance, if upon entering this procedure KAZ__X__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__X__WORD__DELIM, indicating the new editing context. In conjunction with the toggling of the word delimiters, the definition of the key is also toggled. In the text mode, it retains its conventional meaning. In the program mode it implements autoindentation similar to that found, for example, in Apple PASCAL. In particular, any initial combination of and characters at the beginning of the line on which the cursor ^&was\& located will be replicated at the start of the new line, and the cursor positioned after them. .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 + 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 prior to editing would provide yet another mechanism for generalizing the use of this command. PROCEDURE KAZ__GET__OUT This procedure eliminates the following six-keystroke sequence: "CTRL/Z + EXIT + ". This procedure does a normal exit out of TPU after it saves the current buffer under the filename that was used at the start of the editing session and deletes the journal file. We have chosen the two-keystroke sequence + CTRL/Z to cause the execution of this procedure. The on__error section protects the user against loss of work if there should be some problem writing out the file, such as exceeding disk quota. 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 successfully 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" might have been used here, because we took care of writing out the main buffer ourselves, but "exit" will also write out any other buffers that have been modified and are not set no__write. 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 The search command is used three times in KAZ__END__SENT. In this proceedure the direction is always FORWARD and the qualifier is always EXACT (case sensitive). The first search looks for the end-of-sentence delimiters that are defined by the search pattern KAZ__X__SENT__DELIM; one character string that will match that pattern is ". ", a period followed by a space character. The initialization of KAZ__X__SENT__DELIM was described above. In this procedure, if the pattern is not found, i.e., the search range returned to end__sent__range is 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. .c;New and Wonderful Options 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. Following procedure EVE_CHANGE_MODE, from page 23 of EVESECINI.TPU, the procedure KAZ__OVERSTRIKE uses the TPU built-in GET__INFO to determine the current text entry mode. This allows the defined key to take on a toggle function, with its action depending on which mode the current buffer is in. For example, if the current editing context is insert mode, then the procedure switches into overstrike mode. In the procedure, an if-then-else construct implements the above logic. The commands SET(OVERSTRIKE, CURRENT__BUFFER); and SET(INSERT, CURRENT__BUFFER); set the editing context of the current buffer to overstrike and insert respectively. After the editing context is set, then the status line is updated to indicate 'overstrike' or 'insert' mode, if the status line is displayed; if it is not displayed (e.g., for the MAIN buffer edited in the MAIN window), then a message announcing the current mode is written at the bottom of the screen. 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. Global variables are maintained identifying the windows and buffers. Dual window editing, for those who have not enjoyed the luxury, has three particular uses: first, the coordinated modification of two files, including cutting and pasting between them. Second, the two windows can be located in two different places within the same buffer, such as the declarations section of a PASCAL program and the place where you are modifying the executable statements. Thus, you can cut and paste between the two locations, or just hop back and forth to make insertions at each place in turn. Third, as will be discussed below, one window can present the DCL interaction of a subprocess. Because several procedures use the dual window display, we initialize those two windows at startup time, maintaining only three windows: the regular MAIN window and top and bottom half windows as well. The sizes of the top and bottom windows are set appropriately to allow for status lines at the bottom of each window, clarifying the boundary between them. The scrolling limits for all three windows 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 scrolling output to the terminal. The TPU built-in GET__INFO is used to determine if a GIGI terminal (VK-100) is being used, with "jumpy" scrolling implemented for these terminals to reduce the frequency of screen refresh activity. In normal usage we define + B to execute a procedure (closely modeled on the support routine for the EDT line mode "= buffer" command from page 22 of EDTSECINI) that prompts for the name of the buffer, moves the cursor to it, and maps it to the main window. We define + M to execute that procedure with argument "MAIN", to move the cursor to the main buffer and map that buffer to the main window. Thus, those keys are routinely used to return to the main buffer or to shift to a named buffer. Those procedures function only when in single-window mode. During dual-window editing, the key toggles between the two windows, and + CTRL/W uses procedure KAZ__WINDOWS to return to single-window operation or to change buffers. During single-window editing, toggles between the two buffers most recently requested in the + B or + CTRL/W sequences. We have defined + to jump back a screen, and + to jump forward a screen. This is implemented using a global variable, KAZ__X__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, in either single- or dual- window mode. PROCEDURE DCL ('COMMAND') This procedure captures the output of DCL commands, stores that output into a buffer, and displays it in the top or bottom window. Procedure DCL accepts a parameter that is the DCL command to be executed. If the parameter is the null string, then the user is prompted for a command. If no command is then given, the procedure terminates. We chose not to prefix the procedure name with "KAZ__" because of the natural use with the + (TPU Command), responding, for example "DCL('DIR')" to get a current directory listing. The first time it executes, this procedure creates a subprocess that will actually issue the DCL commands and return the system's responses. The DCL buffer is created by procedure TPU$LOCAL__INIT at edit session startup. Procedure DCL initiates double-window operation if necessary, and maps the DCL buffer to the second window on the screen. All output from the DCL command will be captured into the DCL buffer for display through this window. When the command completes, the cursor is returned to its original position by mapping the other half-screen window to the buffer in use when the DCL command was issued. This procedure was modified from the routine EVE__DCL on page 86 of EVESECINI.TPU. PROCEDURE KAZ__DCL__COMPILE ('LANGUAGE') We define keys to execute this procedure with string paramater 'PASCAL' and with string parameter 'FORTRAN', but any language could be used. (The "language" may be a programming lanugage or it could be a nonprogramming language such as RUNOFF.) This procedure saves the current buffer to disk, prompting the user for a filename; as usual, the return key can be used to accept a default filename. The procedure DCL is then called with string parameter that is the concatenation of the parameter for this procedure, a space, and the filename to which the buffer was just written. The output of the compile command, including any error messages, is then displayed in the DCL window as usual, and after the output is completed, the cursor is returned to its original location in the other half-screen window. PROCEDURES KAZ__LEARN and KAZ__REMEMBER These procedures work in tandem interactively to associate multiple keystrokes to one definable key. This is very helpful when a key sequence will need to be repeated many times. After KAZ__LEARN is executed, all following keystrokes are 'learned' by TPU. The 'learn' sequence is terminated by executing the procedure KAZ__REMEMBER, which prompts the user for a key to define as the previous sequence of keystrokes. If the user hits the return key, the command sequence is discarded. These procedures are adaptations of the procedures EVE__LEARN and EVE__REMEMBER from page 89 of EVESECINI.TPU, with the prompting messages particularly improved. ^&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 files EDTSECINI.TPU and EVESECINI.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. An earlier version of this article was published in the February, 1986, DECUS U.S. Chapter SIGs Newsletter, pages VAX-4 through VAX-17. That version of this article, the code, and a companion article describing the customized environment were submitted for inclusion in the VAX SIG Symposium tape for the December, 1985, DECUS Symposium. .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 | _____________________________________ _____________________________________ | | | null definition of | | KAZ_DEFINE_KEYS | | SAVE SYS$LOGIN:filename.NEW | | QUIT | _____________________________________ Figure 2: TPU Section File Organization .el .page .c;Listing 1: Kalamazoo's EDTINI.EDT .lt ! Kalamazoo College Standard EDTINI.EDT ! Developed from one published in "THE DEC PROFESSIONAL." ! ! The commands defined here are included in those described in ! the "Command Guide for EDT." ! ! While editing, the extra commands are described by the ! comments present in the buffer HELPINI, which is accessed ! by the command GOLD + H (or PF1 + H). After reading that ! material, you can return to editing by the command GOLD + M. ! ! ! DEF M DELIMITERS_PROGRAMMING F=DELIMITERS_PROGRAMMING I DEF K GOLD CONT D AS "EXT DELIMITERS_WORD_PROCESSING." ! the following string starts SE EN WO ' ()[]{},-+*/=' ^Z DEF M DELIMITERS_WORD_PROCESSING F=DELIMITERS_WORD_PROCESSING I DEF K GOLD CONT D AS "EXT DELIMITERS_PROGRAMMING." ! the following string IS SE EN WO ' ' ^Z DEF M RESTORE_POSITION F=RESTORE_POSITION I FIND BEGIN NEXT/^^&&^^// ^Z DEF K CONT B AS "-SEN." DEF K CONT F AS "+SEN." DEF K CONT G AS "PASTE=?'Replace from buffer: '." DEF K CONT N AS "-PAR." DEF K CONT P AS "+PAR." DEF K CONT R AS "EXT RESTORE_POSITION." DEF K CONT V AS "I^^&&^^^Z -6C." DEF K GOLD B AS "EXT F=?'Edit Buffer: '." DEF K GOLD D AS "95ASC 27ASC 58ASC." DEF K GOLD F AS "(BACK PAR SEL ADV PAR FILLSR)." DEF K GOLD H AS "EXT F=HELPINI." DEF K GOLD I AS "EXT INC ?'Input file: ' =?' To Buffer: '." DEF K GOLD M AS "EXT F=MAIN." DEF K GOLD O AS "EXT WR ?'Output file: ' =?' From Buffer: '." DEF K GOLD P AS "BACK PAGE ADV 59L." DEF K GOLD R AS "EXT INC STANDARD.RNO ." DEF K GOLD Q AS "EXT QUIT." DEF K GOLD S AS "EXT SH BU." DEF K GOLD U AS "95ASC 27ASC 59ASC." DEF K GOLD V AS "8ASC." DEF K GOLD X AS "EXT CO SELECT TO=?'Copy to Buffer: '." DEF K GOLD DEL AS "EXT CL ?'Clear what buffer?: '." DEF K GOLD 12 AS "(-22V)." DEF K GOLD 13 AS "(+22V)." DEF K GOLD CONT G AS "CUTSR=DELETE PASTE=?'REPLACE WITH BUFFER: '." DEF K GOLD CONT H AS "(D-C +C UNDC)." DEF K GOLD CONT I AS "(8C)." DEF K GOLD CONT Z AS "EXT EX." F=HELPINI INCLUDE SYS$SYSDEVICE:[SYSLOGIN]EDTINI.TXT F=MAIN SET TAB 4 SET NOTRUNCATE SET WRAP 65 SET MODE CHANGE .el .page .c;Listing 2: Kalamazoo's TPUINI.TPU .lt ! ! TPUINI.TPU ! ! This is an individual's initialization file for the VAX TPU ! editor. It is designed to work with the section file ! KAZSECINI which is a set of further customizations layered ! on the EDTSECINI file provided by DIGITAL. This file ! contains some variable initializations and key definitions, ! and includes comments indicating how it might be further ! customized according to the preferences of the individual ! user. ! ! This file and KAZSECINI were developed together by Michael ! L. Penix and Richard D. Piccard at Kalamazoo College in the ! Fall, 1985, and further enhanced in the Winter, 1986. ! ! ! To change the size of the first indentation with the ! key, modify the 4 in the first line below to your preferred ! value. We suggest 8 for FORTRAN and 2 or 3 for PASCAL ! programmers. ! edt$x_tab_size := 4; edt$x_tab_goal := edt$x_tab_size; edt$x_tab_set := 1; ! ! To change the width of the text, modify the 65 in the first ! line below to your preferred value. We suggest 60 for ! theses, 72 for FORTRAN, and 78 for PASCAL programmers. ! edt$x_wrap_position := 65; define_key('edt$wrap_word',key_name(' ')); ! ! The following command should be activated by deleting the ! initial exclamation point if you want to customize for ! FORTRAN or PASCAL. It changes the definition of word ! boundaries to include algebraic punctuation. ! !kaz_swap_delim; ! ! The string 'PASCAL' in the following command can be modified ! to match the compile command for whatever language you wish. ! define_key("kaz_DCL_COMPILE('PASCAL')",key_name("p",shift_key), "Write and compile the PASCAL file located in current buffer."); ! ! The 58 should be changed to 29 or less if you will print ! double spaced; this command is for manual pagination, it ! is not normally used if you let RUNOFF break the pages. ! define_key ('KAZ_page_length(+58)',key_name(CTRL_P_KEY,shift_key), "Go to bottom of current page."); .el