emode.mss 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060
  1. @use[bibliography = "<galway.scribe>biblio.bib"]
  2. @make[article]
  3. @style[references = STDalphabetic]
  4. @style[spacing 1]
  5. @style[indentation 5]
  6. @modify[enumerate, numbered=<@a. @,@i. >, spread 0, above 1, below 1]
  7. @modify[itemize,spread 0, above 1, below 1]
  8. @modify[example, above 1, below 1]
  9. @modify[description, spread 1, above 1, below 1]
  10. @modify[appendix, numbered=<APPENDIX @A: >]
  11. @pageheading[Left "Utah Symbolic Computation Group",
  12. Right "June 1982",
  13. Line "Operating Note No. 69"
  14. ]
  15. @set[page=1]
  16. @newpage[]
  17. @begin[titlepage]
  18. @begin[titlebox]
  19. @begin[center]
  20. @b[A Guide to EMODE]
  21. by
  22. William F. Galway and Martin L. Griss
  23. Department of Computer Science
  24. University of Utah
  25. Salt Lake City, Utah 84112
  26. Last Revision: @value[date]
  27. @end[center]
  28. @end[titlebox]
  29. @begin[abstract]
  30. EMODE is a LISP-based EMACS-like editor that runs on the PSL system. This
  31. document is meant to serve as a guide to using EMODE--but will only be
  32. roughly up to date, since the system is in a state of transition.
  33. @end[abstract]
  34. @begin[Researchcredit]
  35. Work supported in part by the National Science Foundation under Grant No.
  36. MCS80-07034.
  37. @end[Researchcredit]
  38. @end[titlepage]
  39. @pageheading[Left "Guide to EMODE",
  40. Right "@value(Page)"]
  41. @set[page=1]
  42. @newpage[]
  43. @section[Introduction and Acknowledgments]
  44. @Comment{Needs more?}
  45. This paper describes the EMODE editor being developed for PSL
  46. @cite[PSL-manual]. EMODE is an interactive, EMACS like
  47. @cite[STALLMAN-ARTICLE-81], screen editor. EMODE provides multiple
  48. windows, can simultaneously support different "modes" of editing in
  49. different buffers, and supports a variety of CRT terminals such as the
  50. Teleray 1061 and the DEC VT-100.
  51. Several people have made contributions to EMODE. EMODE itself is based on
  52. an earlier editor EMID @cite[Armantrout81], written by Robert Armantrout
  53. and Martin Griss for LISP 1.6. Tony Carter has used EMODE to develop
  54. several large packages for VLSI circuitry design @cite[Carter81,
  55. Carter-THESIS]. Optimizations for the Vax version, and many useful
  56. comments, have been provided by Russ Fish. Several features have been
  57. added by Alan Snyder and Cris Perdue at Hewlett Packard Research Labs.
  58. Cris implemented the current version of "mode lists", while Alan has
  59. implemented a huge number of commands and improved the efficiency of
  60. several operations.
  61. @section[Running EMODE]
  62. EMODE is available as a "loadable" file. It can be invoked as follows:
  63. @begin[example]
  64. @@PSL:RLISP
  65. [1] load emode;
  66. [2] emode();
  67. @end[example]
  68. Of course, you may choose to invoke RLISP (or PSL) differently, and to
  69. perform other operations before loading and running EMODE. From this point
  70. on the term "PSL" will be used to refer to this family of systems,
  71. independently of whether they use Lisp or RLISP syntax.
  72. The terminal that EMODE uses by default is determined by its
  73. LOADing the file DEFAULT-TERMINAL. At the University of Utah this
  74. is the TELERAY driver. At other sites, some other driver may be
  75. chosen as the default. To use a different terminal you must LOAD
  76. in a different "driver file" after loading EMODE. For example, to
  77. run EMODE on the Hewlett Packard 2648A terminal, you could type:
  78. @begin[example]
  79. @@PSL:RLISP
  80. [1] load emode, hp2648a;
  81. [2] emode();
  82. @end[example]
  83. The following drivers are currently available:
  84. @begin[description,spread 0]
  85. AAA@\For the Ann Arbor Ambassador.
  86. DM1520@\For the Datamedia 1520.
  87. HP2648A@\For the Hewlett Packard 2648A and similar Hewlett Packard
  88. terminals.
  89. @Comment{Should we be this specific?}
  90. TELERAY@\For the Teleray 1061.
  91. VT52@\For the DEC VT52.
  92. VT100@\For the DEC VT100.
  93. @end[description]
  94. See section @ref[terminal-drivers] for information on creating new terminal
  95. drivers.
  96. EMODE is quite similar to EMACS @cite[EMACS-manual, STALLMAN-ARTICLE-81],
  97. although it doesn't have nearly as many commands. A detailed list of
  98. commands is given in appendix @ref[key-bindings]. This information can
  99. also be obtained by typing @w["HELP EMODE;"] to RLISP, or (equivalently) by
  100. reading the file PH:EMODE.HLP.
  101. The notation used here to describe character codes is basically the same as
  102. that used for EMACS. For example: C-Z means "control-Z", the character
  103. code produced by typing Z while holding down the control key. The ascii
  104. code for a control character is the same as the 5 low order bits of the
  105. original character--the code for Z is 132 octal, while the code for C-Z is
  106. 32 octal. M-Z means "meta-Z", the character produced by typing Z while
  107. holding down the meta key. To support those terminals without a meta key,
  108. the same result can normally be achieved by typing two characters--first
  109. the ESCAPE character, then the Z character. The ascii code for a meta
  110. character is the same as the original character with the parity bit
  111. set--the code for M-Z is 332 octal. (Some terminals use the ESCAPE
  112. character for other purposes, in which case the "META prefix" will be some
  113. other character.) Rather than using the EMACS convention, we write
  114. "control-meta" characters (such as C-M-Z) as "meta-control" characters
  115. (M-C-Z), since the latter notation better reflects the internal code (232
  116. octal for M-C-Z). The C-Z character is used as a "meta-control" prefix, so
  117. one way to type M-C-Z is to type @w[C-Z C-Z]. (Another way to type it is
  118. to hold down the meta and control keys and type "Z".)
  119. When EMODE is started up as described above, it will immediately enter "two
  120. window mode". To enter "one window mode", you can type "C-X 1" (as in
  121. EMACS). Commands can be typed into a buffer shown in the top window. The
  122. result of evaluating a command is printed into the OUT_WINDOW buffer (shown
  123. in the bottom window). To evaluate the expression starting on the current
  124. line, type M-E. M-E will (normally) automatically enter two window mode if
  125. anything is "printed" to the OUT_WINDOW buffer. If you don't want to see
  126. things being printed to the output window, you can set the variable
  127. !*OUTWINDOW to NIL. (Or use the RLISP command "OFF OUTWINDOW;".) This
  128. prevents EMODE from automatically going into two window mode when something
  129. is printed to OUT_WINDOW. You must still use the "C-X 1" command to enter
  130. one window mode initially.
  131. Figure @ref[two-window-figure] shows EMODE in two window mode. In this
  132. mode the top window includes everything above (and including) the first
  133. line of dashes. This is followed by a single line window, showing the
  134. current prompt from PSL. Beneath this is the "output window", the window
  135. which usually shows the OUT_WINDOW buffer. This is followed by another
  136. single line window, which EMODE uses to prompt the user for values (not the
  137. same as PSL's prompt).
  138. @begin[figure]
  139. @begin[example]
  140. % Commands can be typed in the top window.
  141. % When they're executed the value is printed into
  142. % the OUT_WINDOW buffer.
  143. x := '(now is the time);
  144. y := cddr x;
  145. ----MAIN-----------------------------------------85%---
  146. [7]
  147. -------------------------------------------------------
  148. NIL
  149. (NOW IS THE TIME)
  150. (THE TIME)
  151. ----OUT_WINDOW-----------------------------------75%---
  152. File for photo: s:twowindow.photo
  153. @end[example]
  154. @caption[Two window mode]
  155. @tag[two-window-figure]
  156. @end[figure]
  157. Figure @ref[one-window-figure] shows EMODE in one window mode. The "top
  158. window" takes up most of the screen, followed by EMODE's prompt line, and
  159. then by PSL's prompt line.
  160. @begin[figure]
  161. @begin[example]
  162. % Commands can be typed in the top window.
  163. % When they're executed the value is printed into
  164. % the OUT_WINDOW buffer.
  165. x := '(now is the time);
  166. y := cddr x;
  167. ----MAIN-----------------------------------------85%---
  168. File for photo: s:onewindow.photo
  169. [7]
  170. @end[example]
  171. @caption[One window mode]
  172. @tag[one-window-figure]
  173. @end[figure]
  174. The BREAK handler has been modified by EMODE to "pop up" a "break window
  175. menu". This is illustrated in figure @ref[break-window-figure]. The
  176. commands in the menu can be executed with the M-E command, and you can also
  177. edit the BREAK buffer just like any other buffer. If you wish to move to
  178. another window, use the @w[C-X N] command. This may cause the break window
  179. to disappear as it is covered by some other window, but @w[C-X P] will find
  180. it and pop it to the "top" of the screen again.
  181. @begin[figure]
  182. @begin[example]
  183. cdr 2; +------------------------------+
  184. |A ;% To abort |
  185. |Q ;% To quit |
  186. |T ;% To traceback |
  187. |I ;% Trace interpreted stuff |
  188. |R ;% Retry |
  189. |C ;% Continue, |
  190. | % using last value |
  191. ----MAIN-----------|? ;% For more help |-
  192. 4 lisp break> +----BREAK---------------11%---+
  193. ----------------------------------------------------
  194. NIL
  195. ***** An attempt was made to do CDR on `2', which is
  196. not a pair {99}
  197. Break loop
  198. ----OUT_WINDOW-----------------------------------75%---
  199. File for photo: s:breakwindow.photo
  200. @end[example]
  201. @caption[A break window (doctored from the original)]
  202. @tag[break-window-figure]
  203. @end[figure]
  204. EMODE is not very robust in its handling of errors. Here's a summary of
  205. known problems and suggestions on how to deal with them:
  206. @begin[description]
  207. Garbage collection messages "blow up":@\Printing messages into EMODE
  208. buffers involves CONSing, so the system blows up if it tries to print a
  209. message from inside the garbage collector. EMODE sets GC OFF at load time.
  210. Always run EMODE with GC OFF.
  211. @begin[multiple]
  212. Terminal doesn't echo:@\This can be caused by abnormal exits from EMODE.
  213. If PSL is still running, you can call the routine "EchoOn" to turn
  214. echoing back on. (It's the routine "EchoOff" that turns echoing off, and
  215. starts "raw output" mode.)
  216. Otherwise, as may happen on the Vax running Unix, you will have to give
  217. shell commands to turn echoing back on. This is best done by defining the
  218. following alias in your ".login" file.
  219. @begin[example]
  220. alias rst 'reset; stty -litout intr ^C'
  221. @end[example]
  222. (That's a "control-C", not "uparrow C".) The "rst" command must be typed
  223. as "<LF>rst<LF>" because carriage-return processing is turned off.
  224. @end[multiple]
  225. "Garbled" printout:@\This is probably caused by EMODE's not running in "raw
  226. output" mode--a problem which can be caused by some other errors. A cure
  227. is to type @w[C-Z C-Z] to leave EMODE, and then to call EMODE again. This
  228. should reset the terminal mode to "raw mode" (by calling EchoOff). (The
  229. @w[C-Z C-Z] must be followed by a linefeed on the Vax, to force the
  230. @w[C-Z C-Z] to be read.)
  231. @begin[multiple]
  232. Stuck in an error:@\This is often caused by trying to evaluate an expression
  233. that lacks a closing parenthesis (or some other terminator)--producing a
  234. message something like:
  235. @begin[example]
  236. ***** Unexpected EOF while reading ...
  237. @end[example]
  238. If it's obvious that an additional parenthesis will cure the problem, you
  239. can use @w[C-X N] to select the input window and insert it. Then position
  240. the cursor to the left of the parenthesis and use @w[C-X N] to select the
  241. break window and "Quit".
  242. Otherwise you should use the "Abort" option of the break handler.
  243. Currently this resets the terminal mode (at least on the DEC-20), so you'll
  244. have to restart EMODE as described above. The BREAK window will still be
  245. present on the screen after restarting, even though you are no longer in
  246. the break loop. You can use the @w[C-X 2] or @w[C-X 1] command to get rid
  247. of the break window, and then use the @w[C-X B] command to select some
  248. buffer other than the break buffer.
  249. @end[multiple]
  250. @end[description]
  251. @section[A Guide to the Sources and Rebuilding]
  252. The "primary" sources for EMODE reside on UTAH-20:
  253. @begin[description]
  254. PES:@\Is defined locally as <GALWAY.EMODE.V2>. This directory is for the
  255. "version 2" of EMODE--being maintained now. The corresponding "logical
  256. name" on the VAX is "$pes".
  257. PE:@\Is defined as <PSL.EMODE>. Holds sources and documentation which may
  258. be generally useful to the public. It includes sources for the various
  259. terminal drivers available for EMODE. (Further described in section
  260. @ref[terminal-drivers].) The corresponding logical name on the VAX is
  261. "$pe".
  262. @end[description]
  263. The file PES:BUILD-EMODE.CTL is the command file for building EMODE on the
  264. DEC-20. Use SUBMIT or DO to run the command file, which builds EMODE in
  265. two parts on the local directory: EMODE-B-1.B and EMODE-B-2.B.
  266. PES:BUILD-EMODE.CSH (or $pes/build-emode.csh) is the build file for the
  267. VAX. It also builds the binary files on the "local directory". On both
  268. machines the ".B" files for the terminal drivers and for RAWIO.B are built
  269. separately.
  270. The PES:EMODE.TAGS file can be used with the TAGS facility provided by
  271. EMACS on the DEC-20. (Highly recommended!)
  272. @section[Terminology: Buffers, Views/Windows, and Virtual Screens]
  273. @Comment{Need to say more about NSTRUCT, refer to some manual.}
  274. "Buffers", "views", and "virtual screens" are the three major data
  275. structures in EMODE. Virtual screens correspond fairly closely to what are
  276. often called @i[windows] in other systems. They are rectangular regions on
  277. the screen, possibly overlapping, that characters can be written to.
  278. A virtual screen provides a sort of pseudo-hardware. The operations that
  279. can be performed on a virtual screen are modeled after what can be done
  280. with a real terminal. The use of a virtual screen provides these
  281. advantages:
  282. @begin[itemize]
  283. Operations on a virtual screen are machine independent. (To some extent,
  284. this will be less true if we try to support "fancier" graphics.)
  285. The "bandwidth problem" of maintaining the screen image is isolated to the
  286. virtual screen package--other programs don't have to worry about the
  287. problem.
  288. Several virtual screens can be shown on one physical screen.
  289. @end[itemize]
  290. Virtual screens are implemented as "Structs" using the "DefStruct" facility
  291. provided by the loadable file "NSTRUCT".
  292. Buffers hold the data to be edited, possibly something other than text,
  293. depending on the buffer's "data mode". Views are data structures used to
  294. display buffers on the screen, they may be made of several virtual screens.
  295. The term @i["window"] is often used instead of "view", when you see the one
  296. term it should be possible to substitute the other.
  297. Buffers and views are implemented as "environments". An environment is an
  298. association list of @w[(NAME . VALUE)] pairs. (These association lists are
  299. sometimes referred to as "descriptors".) The usual method for working with
  300. an environment is "restoring" (or "selecting") the environment by calling
  301. the procedure "RestoreEnv". This sets each variable name in the list to
  302. its associated value. The procedure "SaveEnv" does the inverse operation
  303. of updating the values of each variable name in the association list.
  304. (This is done "destructively", using RPLACD.) The names in an environment
  305. are sometimes called "per-environment" variables. Names in "buffer
  306. environments" are called "per-buffer variables", and similarly for
  307. "per-view variables".
  308. Buffers and views are just environments that follow certain conventions.
  309. These conventions are that they always include certain @w[(name . value)]
  310. pairs--i.e. that they always include certain "per-buffer" or "per-view"
  311. variables. For example, the required per-buffer variables include:
  312. @begin[description]
  313. buffers_file@\The name (a string) of a file associated with the buffer, or
  314. NIL if no file is associated with the buffer.
  315. buffers_view_creator@\A routine that creates a "view" (or "window") looking
  316. into the buffer.
  317. @end[description]
  318. In addition to the required per-buffer variables, text buffers include
  319. variables containing things like the text being edited in the buffer and
  320. the location of "point" in the buffer.
  321. The required per-view variables include:
  322. @begin[description]
  323. windows_refresher@\(Which should actually be called the "views_refresher")
  324. defines a routine to be the refresh algorithm for whatever data structure
  325. this view looks into.
  326. WindowsBufferName@\Is the name (an ID) of the buffer that the view looks
  327. into.
  328. @end[description]
  329. Views into text buffers include additional information such as a virtual
  330. screen to display the text in, and "cache" information to make refreshing
  331. faster.
  332. The choice of whether variables should be per-buffer or per-view is
  333. sometimes unclear. For example, it would seem to make better sense to have
  334. "point" be part of a view, rather than a buffer. This would allow the user
  335. to have two windows looking into different parts of the same buffer.
  336. However, it would also require the selection of a window for the many
  337. functions that insert strings into the buffer, delete strings from the
  338. buffer, etc., since these routines all work around the current "point".
  339. Somehow it seems unnatural to require the selection of a @i[view] for these
  340. @i[buffer] operations. The current decision is to make point a per-buffer
  341. variable.
  342. Further details on buffers and views for different modes are given in
  343. section @ref[creating-modes].
  344. A list of all the buffers in EMODE is stored in the variable "BufferNames"
  345. as a list of @w[(name . environment)] pairs . These pairs are created with
  346. the routine "CreateBuffer".
  347. A list of "active" views in EMODE is stored in the variable "WindowList".
  348. This is simply a list of "environments" (association lists as described
  349. above). Unlike buffers, views are not referred to by name. Instead,
  350. specific views can be referred to by storing their environment in a
  351. variable (such as "BreakWindow").
  352. @section[Modes and Key bindings in EMODE]
  353. @label[key-modes]
  354. There are two aspects to "modes" in EMODE. One is the choice of the data
  355. structure to be edited within a buffer. Until recently there has only been
  356. one kind of structure: "text". As discussed in section
  357. @ref[creating-modes] EMODE now provides tools for editing other, user
  358. defined, structures.
  359. @begin[Comment]
  360. Is this DISTINCTION between key bindings and the binding of other variables
  361. really VALID?
  362. @end[Comment]
  363. The other aspect of "modes", discussed in this section, is the binding of
  364. "handler" routines to terminal keys (or sequences of keys for multi-key
  365. commands). A simple version of this would associate a table of handlers
  366. (indexed by character code) with each buffer (or view). The method
  367. actually used is more complicated due to a desire to divide keyboard
  368. bindings into groups that can be combined in different ways. For example,
  369. we might have a text mode and an Rlisp mode, and an optional Word
  370. Abbreviation Mode that could be combined with either of them to cause
  371. automatic expansion of abbreviations as they are typed.
  372. Implementing optional keyboard bindings that can @i[removed] as well as
  373. @i[added] is difficult. Consider the situation with an optional
  374. "Abbreviation Mode" and an optional "Auto Fill Mode". Turning on either
  375. mode redefines the space character to act differently. In each case, the
  376. new definition for space would be something like "do some fancy stuff for
  377. this submode, and then do whatever space used to do". Imagine the
  378. difficulties involved in turning on "Abbreviation Mode" and then "Auto Fill
  379. Mode" and then turning off "Abbreviation Mode".
  380. EMODE's solution to the problem is based on the method suggested in
  381. @cite[FINSETH]. A @i[single], @i[global] "dispatch vector" is used, but is
  382. rebuilt when switching between buffers. The mode for each buffer is stored
  383. as a list of expressions to be evaluated. Evaluating each expression
  384. enters the bindings for an associated group of keys into the vector.
  385. Incremental modes can be added or deleted by adding or deleting expressions
  386. from the list. Although changing modes is fairly time consuming (more than
  387. a few microseconds), we assume that this is rare enough that the overhead
  388. is acceptable. NOTE that simply changing an entry in the dispatch vector
  389. will not work--since any switching between buffers will cause the entry to
  390. be permanently lost.
  391. The dispatch "vector" is actually implemented as a combination of a true
  392. PSL vector "MainDispatch", indexed by character code, and an association
  393. list "PrefixAssociationLists" used to implement two character commands.
  394. Currently the only two character commands start with the "prefix character"
  395. C-X, although the mechanism is more general. Prefix characters are
  396. "declared" by calling the routine "define_prefix_character" (refer to code
  397. for details). Bindings for prefix-character commands are stored in
  398. PrefixAssociationLists as an association list of association lists. The
  399. top level of the list is "indexed" by the prefix character, the next level
  400. contains @w[(character . handler)] pairs indexed by the character following
  401. the prefix character.
  402. The list of expressions for building the dispatch vector is called the
  403. "mode list", and is stored in the per-buffer variable
  404. "ModeEstablishExpressions". See the following section for more on how
  405. ModeEstablishExpressions is used in the declaration of a mode. The
  406. procedure "EstablishCurrentMode" evaluates these expressions in reverse
  407. order (the last expression in the list is evaluated first) to establish the
  408. keyboard dispatch vector used for editing the current buffer. Reverse
  409. order is used so that the @i[last] expression added to the @i[front] of the
  410. list will be evaluated last. EstablishCurrentMode must be called after
  411. changing the mode list for the current buffer and when switching to a
  412. different buffer @i[for editing from the keyboard]. The routine
  413. SelectBuffer switches to a buffer without "establishing" the buffer's mode.
  414. This saves the cost of setting up the dispatch vector when it isn't needed
  415. (which is the case for most "internal operations" on buffers).
  416. The expressions in ModeEstablishExpressions can execute @i[any] code
  417. desired. This generality is rarely needed, the usual action is to call the
  418. routine SetKeys with a list of @w[(character . handler)] pairs. For
  419. example, the mode list for text mode is defined by this Lisp code:
  420. @begin[example]
  421. (setf FundamentalTextMode
  422. '((SetKeys TextDispatchList)
  423. (SetKeys BasicDispatchList)
  424. (NormalSelfInserts)))
  425. @end[example]
  426. The RLISP mode is built "on top of" FundamentalTextMode as follows:
  427. @begin[example]
  428. (setf RlispMode
  429. (cons
  430. '(SetKeys RlispDispatchList)
  431. FundamentalTextMode))
  432. @end[example]
  433. This section taken from the code that builds BasicDispatchList shows what a
  434. "key list" for the SetKeys routine should look like:
  435. @begin[example]
  436. (setf BasicDispatchList
  437. (list
  438. (cons (char ESC) 'EscapeAsMeta)
  439. (cons (char (cntrl U)) '$Iterate)
  440. (cons (char (cntrl Z)) 'DoControlMeta)
  441. % "C-X O" switches to "next window" (or "other
  442. % window" if in "two window mode").
  443. (cons (CharSequence (cntrl X) O) 'next_window)
  444. (cons (CharSequence (cntrl X) (cntrl F)) 'find_file)
  445. .
  446. .
  447. .
  448. @end[example]
  449. Note that the pairs in a key list can specify character sequences like
  450. "@w[(cntrl X) O]" as well as single characters.
  451. At runtime, after they're created, key lists can be most easily modified by
  452. calling the routine AddToKeyList. For example
  453. @begin[example]
  454. (AddToKeyList
  455. 'RlispDispatchList
  456. (char (meta (cntrl Z)))
  457. 'DeleteComment)
  458. @end[example]
  459. could be executed to add a new, "delete comment" handler to RLISP mode.
  460. The routine SetTextKey is equivalent to adding to the key list
  461. TextDispatchList (see code). For example
  462. @begin[example]
  463. (SetTextKey (char (meta !$)) 'CheckSpelling)
  464. @end[example]
  465. could be executed to add a new "spelling checker" command to text mode (and
  466. other modes such as RLISP mode that incorporate text mode). SetTextKey
  467. seems to correspond most closely to EMACS's "Set Key" command.
  468. The routine "SetLispKey" is also defined for adding bindings to "Lisp
  469. mode". (There is no "SetRlispKey" routine in EMODE, although it would be
  470. easy to define for yourself if desired.)
  471. @section[Creating New Modes]
  472. @label[creating-modes]
  473. To define a new mode you must provide a "buffer creator" routine that
  474. returns a "buffer environment" with the required per-buffer variables along
  475. with any other state information needed for the type of data being edited.
  476. You need to "declare" the mode by calling the routine "declare_data_mode".
  477. It's also possible to associate the mode with a file extension by calling
  478. the routine "declare_file_mode".
  479. For example, the current EMODE declares the modes, "text" and
  480. "rlisp", as follows:
  481. @begin[example]
  482. (declare_data_mode "text" 'create_text_buffer)
  483. (declare_data_mode "rlisp" 'create_rlisp_buffer)
  484. (declare_file_mode "txt" 'create_text_buffer)
  485. (declare_file_mode "red" 'create_rlisp_buffer)
  486. @end[example]
  487. The second argument to both routines is the "buffer creator" routine for
  488. that mode. The first argument to declare_data_mode is a "name" for the
  489. mode. The first argument to declare_file_mode is a file extension
  490. associated with that mode.
  491. The conventions for "buffer environments" are that they always include certain
  492. @w[(name . value)] pairs--i.e. that they always include certain
  493. "per-buffer" variables. These variables are:
  494. @begin[description]
  495. ModeEstablishExpressions@\A list of expressions to evaluate for
  496. establishing the keyboard bindings for the buffer's mode.
  497. buffers_file@\The name (a string) of a file associated with the buffer, or
  498. NIL if no file is associated with the buffer.
  499. buffers_file_reader@\A routine to APPLY to one argument--a PSL io-channel.
  500. The routine should read the channel into the current buffer.
  501. buffers_file_writer@\A routine to APPLY to an io-channel. The routine
  502. writes the current buffer out to that channel.
  503. buffers_view_creator@\A routine to create a "view" (or "window") looking
  504. into the buffer. This is described in more detail below.
  505. @end[description]
  506. For example, the buffer creator for "text mode" is:
  507. @begin[example]
  508. (de create_text_buffer ()
  509. (cons
  510. (cons 'ModeEstablishExpressions FundamentalTextMode)
  511. (create_raw_text_buffer)))
  512. @end[example]
  513. Most of the work is done by create_raw_text_buffer, which does everything
  514. but determine the keyboard bindings for the buffer. Here's the code with
  515. comments removed:
  516. @begin[example]
  517. (de create_raw_text_buffer ()
  518. (list
  519. (cons 'buffers_view_creator 'create_text_view)
  520. (cons
  521. 'buffers_file_reader
  522. 'read_channel_into_text_buffer)
  523. (cons
  524. 'buffers_file_writer
  525. 'write_text_buffer_to_channel)
  526. (cons 'buffers_file NIL)
  527. (cons 'CurrentBufferText (MkVect 0))
  528. (cons 'CurrentBufferSize 1)
  529. (cons 'CurrentLine NIL)
  530. (cons 'CurrentLineIndex 0)
  531. (cons 'point 0)
  532. (cons 'MarkLineIndex 0)
  533. (cons 'MarkPoint 0)
  534. ))
  535. @end[example]
  536. Other modes based on text can be similarly defined by consing an
  537. appropriate binding for ModeEstablishExpressions to the environment
  538. returned by create_raw_text_buffer.
  539. Of course we need some way of "viewing" buffers once they've been created.
  540. The per-buffer variable "buffers_view_creator" is responsible for creating
  541. a view into a buffer. The "view creator" is typically invoked by the
  542. routine "select_or_create_buffer".
  543. The required per-view variables are:
  544. @begin[description]
  545. @begin[group]
  546. windows_refresher@\Which should actually be called the "views_refresher",
  547. is a routine to APPLY to no arguments. This routine is the refresh
  548. algorithm for whatever data structure this view looks into.
  549. @end[group]
  550. @begin[group]
  551. WindowsBufferName@\Is the name (an ID) of the buffer that the view looks
  552. into.
  553. @end[group]
  554. @begin[group]
  555. views_cleanup_routine@\A routine that's called when a view is being deleted
  556. from the screen. Different views may require different kinds of cleaning
  557. up at this point. For example, they should "deselect" any "virtual
  558. screens" that make up the view.
  559. @end[group]
  560. @end[description]
  561. The view creator for text structures is "create_text_view". This routine
  562. typically modifies and returns the current view (which is almost certainly
  563. also looking into text in the current system) so that the current view
  564. looks into the new text buffer. Most of the real work of creating text
  565. views is done by the routine "FramedWindowDescriptor", which is typically
  566. invoked by the routines "OneWindow" and "TwoRFACEWindows". (So, although
  567. select_or_create_buffer is one way of creating views into a buffer, there's
  568. quite a bit of freedom in using other methods for creating views.)
  569. @section[Manipulating Text Buffers]
  570. The text in "text buffers" is stored as a vector of strings in the
  571. per-buffer variable "CurrentBufferText"--with the exception of a "current
  572. line" (stored in the per-buffer variable "CurrentLine"), which is a linked
  573. list of character codes. The CurrentLine is the line indexed by
  574. "CurrentLineIndex". Refer to the routine create_text_buffer for details of
  575. the contents of a text buffer.
  576. It's an easy mistake to modify CurrentLine but to forget to update the
  577. CurrentBufferText when moving to a new line. For this reason, and because
  578. the representation used for text may change in the future, you should use
  579. the utilities provided (mostly) in PES:EMODE1.RED to manipulate text. The
  580. procedure "GetLine(x)" can be used to get line x as the current line. The
  581. procedure "PutLine()" is used to store the current line back into
  582. CurrentBufferText. The procedure "SelectLine(x)" first "puts away" the
  583. current line, and then "gets" line x.
  584. It would seem natural to move forward a line in the text by doing something
  585. like
  586. @begin[example]
  587. SelectLine(CurrentLineIndex + 1);
  588. @end[example]
  589. but you should resist the temptation. For one thing, SelectLine makes
  590. little attempt to check that you stay within the limits of the buffer.
  591. Furthermore, future representations of text may not use integers to index
  592. lines. For example, some future version may use a doubly linked list of
  593. "line structures" instead of a vector of strings.
  594. So, you should use the routines "NextIndex" and "PreviousIndex" to
  595. calculate new "indices" into text, and you should also check to make sure
  596. that CurrentLineIndex is within the bounds of the buffer. You can probably
  597. just use the routines "!$ForwardLine" and "!$BackwardLine", (or
  598. "!$ForwardCharacter" and "!$BackwardCharacter"). You should also read some
  599. of the code in EMODE1.RED before attempting your own modifications. (Much
  600. of the code is rather ugly, but it does seem to work!)
  601. @section[Evaluating Expressions in EMODE Buffers]
  602. The "M-E" command for evaluating an expression in a buffer (of the
  603. appropriate mode) depends on I/O channels that read from and write to EMODE
  604. buffers. This is implemented in a fairly straightforward manner, using the
  605. general I/O hooks provided by PSL. (See the Input/Output chapter of the
  606. PSL Manual for further details.) The code for EMODE buffer I/O resides in
  607. the file RFACE.RED.
  608. The tricky part of implementing M-E is making it fit with the
  609. READ/EVAL/PRINT loop that Lisp and other front ends use. The most obvious
  610. scheme would be to have EMODE invoke one "READ/EVAL/PRINT" for each M-E
  611. typed. However, this doesn't work well when a break loop, or a user's
  612. program, unexpectedly prompts for input.
  613. Instead, the top level read functions in PSL call the "hook" function,
  614. MakeInputAvailable(), which allows the user to edit a buffer before the
  615. reader actually takes characters from the current standard input channel.
  616. Examples of top level read functions are READ (for Lisp), and XREAD (for
  617. RLISP). If you define your own read function, for example--to use with the
  618. general TopLoop mechanism, it should also call MakeInputAvailable before
  619. trying to actually read anything.
  620. When EMODE dispatches on M-E, it RETURNS to the routine that called it
  621. (e.g. READ), which then reads from the selected channel (which gets
  622. characters from an EMODE buffer). After evaluating the expression, the
  623. program then PRINTs to an output channel which inserts into another EMODE
  624. buffer. EMODE is then called again by the read routine (indirectly, via
  625. MakeInputAvailable).
  626. The fact that EMODE @i[returns to the reader] means that different buffers
  627. cannot use different readers. This can be a bit confusing when editing
  628. several buffers with different kinds of code. Simply switching to a buffer
  629. with Lisp code does not cause the system to return to READ instead of
  630. XREAD. Implementing this would require some sort of coroutine or process
  631. mechanism--neither of which are currently provided in PSL. (However, it
  632. may be possible to provide an acceptable approximation by having M-E
  633. normally invoke a READ/EVAL/PRINT operation, while preserving the
  634. MakeInputAvailable hook for exceptional situations.)
  635. @section[Customizing EMODE for New Terminals]
  636. @label[terminal-drivers]
  637. The files PE:AAA.SL, PE:DM1520.SL, PE:HP2648A.SL, PE:TELERAY.SL, PE:VT52.SL,
  638. and PE:VT100.SL define the different terminal drivers currently available.
  639. Terminal drivers define some values and functions used to emit the
  640. appropriate character strings to position the cursor, erase the screen and
  641. clear to end of line. To define a new terminal, use one of the files as a
  642. guide. A listing of TELERAY.SL follows:
  643. @begin[verbatim]
  644. %
  645. % TELERAY.SL - EMODE support for Teleray terminals
  646. %
  647. % Author: William F. Galway
  648. % Symbolic Computation Group
  649. % Computer Science Dept.
  650. % University of Utah
  651. % Date: 27 June 1982
  652. % Copyright (c) 1982 University of Utah
  653. %
  654. % Screen starts at (0,0), and other corner is offset by (79,23)
  655. % (total dimensions are 80 wide by 24 down).
  656. (setf ScreenBase (Coords 0 0))
  657. (setf ScreenDelta (Coords 79 23))
  658. % Parity mask is used to clear "parity bit" for those terminals
  659. % that don't have a meta key. It should be 8#177 in that case.
  660. % Should be 8#377 for terminals with a meta key.
  661. (setf parity_mask 8#377)
  662. (DE EraseScreen ()
  663. (progn
  664. (PBOUT (Char ESC))
  665. (PBOUT (Char (lower J)))))
  666. (DE Ding ()
  667. (PBOUT (Char Bell)))
  668. % Clear to end of line from current position (inclusive).
  669. (DE TerminalClearEol ()
  670. (progn
  671. (PBOUT (Char ESC))
  672. (PBOUT (Char K))))
  673. % Move physical cursor to Column,Row
  674. (DE SetTerminalCursor (ColLoc RowLoc)
  675. (progn
  676. (PBOUT (char ESC))
  677. (PBOUT (char Y))
  678. (PBOUT (plus (char BLANK) RowLoc))
  679. (PBOUT (plus (char BLANK) ColLoc))))
  680. @end[verbatim]
  681. @Comment{Newpage???}
  682. @newpage[]
  683. @Comment{Section???}
  684. @section[Bibliography]
  685. @Bibliography[]
  686. @newpage[]
  687. @appendix[Default Keyboard Bindings for EMODE]
  688. @label[key-bindings]
  689. @include[keybindings.mss]
  690. @newpage[]
  691. @appendix[Some Important Fluid Variables]
  692. Here is an incomplete list of the fluid ("global") variables in EMODE.
  693. @begin[description]
  694. @begin[group]
  695. *outwindow@\A flag for PSL's ON/OFF mechanism. When T, means that the
  696. "output" (or OUT_WINDOW) window should be "popped up" when output occurs.
  697. @end[group]
  698. @begin[group]
  699. *EMODE@\T when EMODE is running. (Not quite the same as "runflag"
  700. described below. For example, runflag will be set NIL to cause EMODE to
  701. leave a "recursive edit", but *EMODE stays T.)
  702. @end[group]
  703. @begin[group]
  704. *RAWIO@\T when "raw I/O" is in effect.
  705. @end[group]
  706. @begin[group]
  707. BasicDispatchList@\The "key list" for "basic" operations.
  708. @end[group]
  709. @begin[group]
  710. BreakWindow@\The view for the "popup" break window.
  711. @end[group]
  712. @begin[group]
  713. BufferNames@\An association list of the @w[(name . buffer-environment)]
  714. pairs for all the buffers.
  715. @end[group]
  716. @begin[group]
  717. CurrentBufferName@\The name of the currently selected buffer.
  718. @end[group]
  719. @begin[group]
  720. CurrentBufferSize@\A per-buffer variable for text buffers, gives number of
  721. lines actually within buffer.
  722. @end[group]
  723. @begin[group]
  724. CurrentBufferText@\A per-buffer variable for text buffers. A vector of
  725. lines making up the buffer.
  726. @end[group]
  727. @begin[group]
  728. CurrentLine@\A per-buffer variable for text buffers. The contents (text)
  729. of current line--as a linked list of character codes. (Takes precedence
  730. over whatever is contained in the text vector.)
  731. @end[group]
  732. @begin[group]
  733. CurrentLineIndex@\A per-buffer variable for text buffers. Index of the
  734. "current line" within buffer.
  735. @end[group]
  736. @begin[group]
  737. CurrentVirtualScreen@\Per-view variable for text windows (views), holds the
  738. virtual screen used by the view.
  739. @end[group]
  740. @begin[group]
  741. CurrentWindowDelta@\Per-view variable for text windows, gives window
  742. dimensions as @w[(delta x . delta y)].
  743. @end[group]
  744. @begin[group]
  745. CurrentWindowDescriptor@\The currently selected window environment.
  746. @end[group]
  747. @begin[group]
  748. declared_data_modes@\List of @w[(mode-name . buffer-creator)] pairs for all
  749. the declared modes.
  750. @end[group]
  751. @begin[group]
  752. declared_file_extensions@\List of @w[(file-extension . buffer-creator)]
  753. pairs for all modes with declared file extensions.
  754. @end[group]
  755. @begin[group]
  756. EmodeBufferChannel@\Channel used for EMODE I/O. Perhaps this should be
  757. expanded to allow different channels for different purposes (break loops,
  758. error messages, etc.) (Or, perhaps the whole model needs more thought! )
  759. @end[group]
  760. @begin[group]
  761. FirstCall@\NIL means re-entering EMODE, T means first time.
  762. @end[group]
  763. @begin[group]
  764. FundamentalTextMode@\Mode list (list of expressions) for establishing
  765. "fundamental" text mode.
  766. @end[group]
  767. @begin[group]
  768. kill_buffer_ring@\Vector of vectors of strings--holds recently
  769. deleted text.
  770. @end[group]
  771. @begin[group]
  772. kill_opers@\list of (names of) handler routines that kill text. NEEDS
  773. MORE DOCUMENTATION!
  774. @end[group]
  775. @begin[group]
  776. kill_ring_index@\Pointer to the most recent "kill buffer".
  777. @end[group]
  778. @begin[group]
  779. last_buffername@\Name (a string) of the last buffer visited.
  780. @end[group]
  781. @begin[group]
  782. last_operation@\The "last" routine dispatched to (before the "current
  783. operation").
  784. @end[group]
  785. @begin[group]
  786. last_search_string@\The last string searched for by a search command--used
  787. as default for next search command.
  788. @end[group]
  789. @begin[group]
  790. last_yank_point@\Vector of [buffer lineindex point], giving location
  791. where last "yank" occured.
  792. @end[group]
  793. @begin[group]
  794. LispDispatchList@\The "key list" for Lisp mode.
  795. @end[group]
  796. @begin[group]
  797. LispMode@\The mode list for Lisp mode.
  798. @end[group]
  799. @begin[group]
  800. MainDispatch@\Dispatch table (vector), an entry for each key.
  801. @end[group]
  802. @begin[group]
  803. minor_window_list@\List of windows to be ignored by the "next_window"
  804. routine.
  805. @end[group]
  806. @begin[group]
  807. ModeEstablishExpressions@\List of expressions to be evaluated. Each
  808. expression is expected to modify (add to?) the dispatch table.
  809. @end[group]
  810. @begin[group]
  811. OldErrOut@\The error output channel in effect before EMODE was started.
  812. @end[group]
  813. @begin[group]
  814. OldStdIn@\The standard input channel in effect before EMODE was started.
  815. @end[group]
  816. @begin[group]
  817. OldStdOut@\The standard output channel in effect before EMODE was started.
  818. @end[group]
  819. @begin[group]
  820. point@\A per-buffer variable for text buffers. Number of chars to the left
  821. of point within CurrentLine.
  822. @end[group]
  823. @begin[group]
  824. PrefixAssociationLists@\Additional dispatch information for prefixed
  825. characters.
  826. @end[group]
  827. @begin[group]
  828. PrefixCharacterList@\A list of the declared prefix characters.
  829. @end[group]
  830. @begin[group]
  831. pushed_back_characters@\A list of characters pushed back for EMODE's
  832. command reader. This may be used when a command isn't recognized by one
  833. dispatcher, so it can push the characters back and pass control to another
  834. dispatcher.
  835. @end[group]
  836. @begin[group]
  837. reading_from_output@\Kludge flag, T when input buffer is OUT_WINDOW buffer
  838. (for M-E).
  839. @end[group]
  840. @begin[group]
  841. RlispDispatchList@\The "key list" for RLISP mode.
  842. @end[group]
  843. @begin[group]
  844. RlispMode@\The mode list for RLISP mode.
  845. @end[group]
  846. @begin[group]
  847. runflag@\EMODE continues its READ/DISPATCH/REDISPLAY until this flag is NIL.
  848. @end[group]
  849. @begin[group]
  850. SelfInsertCharacter@\Character being dispatched upon. (Usually the last
  851. character typed.)
  852. @end[group]
  853. @begin[group]
  854. ShiftDisplayColumn@\Amount to shift things to the left by before
  855. (re)displaying lines in a text view.
  856. @end[group]
  857. @begin[group]
  858. TextDispatchList@\The "key list" for fundamental text mode.
  859. @end[group]
  860. @begin[group]
  861. Two_window_midpoint@\Gives location (roughly) of dividing line for two
  862. window mode.
  863. @end[group]
  864. @begin[group]
  865. WindowList@\List of active windows (views).
  866. @end[group]
  867. @begin[group]
  868. WindowsBufferName@\Required per-view variable giving the name of the buffer
  869. being viewed.
  870. @end[group]
  871. @begin[group]
  872. Windows_Refresher@\Required per-view variable giving the refresh algorithm
  873. to be APPLYed for this view.
  874. @end[group]
  875. @begin[group]
  876. Window_Image@\Per-view variable for text views, holding information for
  877. speeding up refresh.
  878. @end[group]
  879. @end[description]