From ae32f2a6c8d1f7d02971632111c54629a2f1d520 Mon Sep 17 00:00:00 2001 From: Eric Swenson Date: Fri, 16 Nov 2018 22:22:27 -0800 Subject: [PATCH] Added emacs pascal mode pl mode and documentation. Resolves #1303. --- build/emacs.tcl | 1 + doc/info/epasc.32 | 748 +++++++++++++++++++ doc/info/epasc.info | 736 ++++++++++++++++++ src/emacs/pascal.defn | Bin 0 -> 2632 bytes src/emacs/pascal.syn | 23 + src/emacs1/pascal.51 | 1650 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 3158 insertions(+) create mode 100755 doc/info/epasc.32 create mode 100644 doc/info/epasc.info create mode 100755 src/emacs/pascal.defn create mode 100755 src/emacs/pascal.syn create mode 100755 src/emacs1/pascal.51 diff --git a/build/emacs.tcl b/build/emacs.tcl index 26d9dcc4..904d3d74 100644 --- a/build/emacs.tcl +++ b/build/emacs.tcl @@ -81,6 +81,7 @@ respond ":EJ" "\033xgenerate\033emacs;vt100\033emacs1;vt100\r" respond ":EJ" "\033xgenerate\033emacs;vt52\033emacs1;vt52\r" respond ":EJ" "\033xgenerate\033emacs;xlisp\033emacs1;xlisp\r" respond ":EJ" "\033xgenerate\033emacs;renum\033emacs1;renum\r" +respond ":EJ" "\033xgenerate\033emacs;pascal\033emacs1;pascal\r" respond ":EJ" "\033xrun\033einit\033? Document\r" respond "\n" "\030\003" diff --git a/doc/info/epasc.32 b/doc/info/epasc.32 new file mode 100755 index 00000000..11e99f50 --- /dev/null +++ b/doc/info/epasc.32 @@ -0,0 +1,748 @@ +-*-Text-*- + +File: EPASC Node: Top Up: (EMACS)Top Next: Available + +PASCAL mode in EMACS provides support for inputing and editing Pascal +code. You should be in Pascal Mode automatically when you edit a +Pascal file. You can call it directly by loading the Pascal library +(M-X Load Library Pascal ) and giving the command M-X Pascal Mode. + +* Menu: + +* Available:: List of all special commands. +* Compile:: Macros for hopping right to your compiler errors. +* Variables:: Synopsis of the customization variables. +* Indent:: Indenting old and new lines of code. +* Statements:: Inserting complete structured statements. +* Comments:: Things you can do with comments in PASCAL mode. +* Paren Groups:: Commands which manipulate parenthesized expressions. +* Hints:: Other commands useful for editing Pascal code. +* Hacker:: Information for hackers or the very interested. + +Node: Available, Previous: Top, Next: Compile, Up: Top + +Commands which start on keys: + +C-M-I The main indentation driver, described under Indent. +LINEFEED Does a CRLF, then a C-M-I. Useful at the ends of lines. +C-M-? Explains the last indentation, if you're curious. +C-M-\,C-M-G,M-G Indent Pascal Region (re-indent all lines in region) + +Rubout TAB-hacking Rubout (converts TABs to spaces). +C-Rubout Normal Rubout. + +M-; Start comment at end of line. *note comments: comments. +C-M-* Insert a comment right here (don't re-indent) +C-M-$ Global Pascal Comment + +C-M-N Forward over a BEGIN/END (or REPEAT/UNTIL etc.) pair. +C-M-P Backward over a BEGIN/END pair. +C-M-H Mark Procedure + +C-M-. Prefix for Structured Insert Commands. *note: St: Statements + +C-M-E If in ErrList Mode, jumps to position of next compiler error + and prints the error in the echo area. *note Error: Compile + +C-M-C Same Capitalization, make all occurences of word same cap. +C-M-S No Comment Search, search for something ignoring + comment contents. + +C-M-+ Turns previous variable name into command to increment + that variable. That is, FOO C-M-+ turns into + FOO:= FOO + 1; +C-M-' Inserts a string of specified length and lets you + recursively edit the contents of the string in + overwrite mode. +C-M-{ Pulls non-comment text from previous line beginning at + character (asked for) and puts it on this line. Also + performs indentation on the result. Useful when you + want to change the way a line is broken. + +Pascal Mode + Restart Pascal Mode +Pascal Expand Abbrevs in Region + Expands all abbrevs in region, but not in comments. + +Node: Compile, Previous: Available, Next: Variables, Up: Top + + With the command M-X Compile (C-M-,) you can automatically compile +and execute your program in an inferior exec. To get special compiler +switches or to load other files with yours, put the switches and other +files in the variable Compiler Switches. + + If you are in ErrList Mode, the compiler errors you see when you do M-X +Compile (C-M-,) are saved away in the buffer named *LST*. You can jump to +the position of the "next" error in your source file with the command +C-M-E. Do this till the message "(No more errors)" appears. + + To go into ErrList Mode, just give the command M-X ErrList Mode. To get +out, give the same command again. You can also get in by setting +the variable, ErrList Mode, to something other than zero. This will work +from an init or evars file (*Note: Init: (EMACS)INIT.) or a local modes list +(*Note: Locals: (EMACS)Locals). You do not want to be in ErrList mode when +you are not finding compiler errors because it is inefficient and it will be +creating a .LST file every time you compile. + + Appologies: (1) In the current implementation this command creates a +compiler listing of your program, which could waste a lot of space. If this +is a problem, connect to a scratch directory while doing the compilation. It +is also a good idea to have your source program on the same directory you are +connected to. (2) The routine for jumping to the position of an error does +not adjust for changes you have made to your file after the compilation. This +means that it may show errors you have already fixed, and that the position it +jumps to will be wrong if (a) you change the number of pages before the page +with the error, or (b) you change the number of lines before the line with the +error on the page with the error, or (c) you change the number of characters +before position of the error on the line with the error. + + +Node: Variables, Previous: Available, Up: Top, Next: Indent + + The PASCAL library has several switches you can set to customize its +behavior. For info on how to set variables *note Vars: (EMACS)Variables. + +Variable name D Explaination +---------------------------------------------------------------------------- +ErrList Mode 0 If non-zero, compiler errors found with + M-X Compile (C-M-,) are saved so that + you can step through them with C-M-E. + +Indent Align Only 0 If non-zero, LINEFEED only aligns + with previous line (does ^R Indent Nested). + +Indent After --- 4 For any keyword, Indent After + is the amount to indent extra after + seeing . +Begin-Block Body Indentation 4 Default indent after BEGIN, + REPEAT, CASE, LOOP, RECORD. +IF-Block Body Indentation 4 Default indent after IF, THEN, ELSE, + WHILE, FOR, DO, WITH. +Decl Body Indentation 4 Default indent after CONST, TYPE, + VAR, LABEL. +--- Body Indentation 4 Amount to indent Body, but not variables, of + subprogram keyword specified. Indentation + for declaration section is Indent After --- +PROCEDURE Body Indentation 4 Default for both "--- Body Indentation" and + "Indent After ---" for PROCEDURE, FUNCTION, + and PROGRAM. +Indent After Everything 4 Default for any value not specified. + +Reindent ENDs 1 0 = leave ENDs & UNTILs indented same as + statements in block; 1 = align END's with + corresponding BEGIN line. +Match Block Word 0 If non-zero, indentation after line with + the keyword BEGIN, REPEAT, CASE, and RECORD + will start from the position of the keyword + within the line, not the indentation of the + line. + +BEGIN on same line 0 Non-zero means inserted BEGINs are + left on same line as stmt. +THEN on same line 1 Non-zero means leave THEN on line + with IF. +ELSE on same line 0 Non-zero means try to stick ELSE on + end of previous line. + +Automatic Capitalization: 0 If non-zero, all keywords typed or + inserted are capitalized. +Insert Comments 1 Non-zero means insert functions + insert some labeling comments. + +-------------------Comment Controlling Variables-------------------- +Comment Column 0 Where EMACS tries to start your + comments (here or after your code). +Global Comment Column 10 Comment Column when doing a Global + Pascal Comment. +Pascal Star Line Width 51 Do Describe on ^R Global Pascal Comment. +Comment Rounding "+1" Algorithm which EMACS uses + to figure where to start the comment + if your code extends beyond the + comment column. +* Menu: + +* Examples:: Variable settings for various kinds of indentation. + +File: EPASC, Node: Indent, Up: Top, Previous: Variables, Next: Statements + + PASCAL mode allows easy formatting of Pascal code as you type it in. Its +main concern is with indentation, not with breaking lines, so that it fits in +well with most styles of formatting. There is also a great deal of +customization avaialable to suit individual tastes. Note: If the load is too +high to effectively use the indentation package, or you just don't get along +with it, you can turn it off by setting Indent Align Only to 1. This will +force the indentation to only align with the previous line, without trying to +figure out how much less or more to indent. + +* Menu: + +* Basics:: The basics of using PASCAL mode indentation +* Details:: Some details about how indentation mode operates +* Examples:: Variable settings for various kinds of indentation. + +Node: Basics, Previous: Indent, Up: Indent, Next: Details + +The two indentation commands are LINEFEED and C-M-I. C-M-I calls the +main routine to do indenting, and LINEFEED does a return, then calls +C-M-I. + +While you are typing in a program, the normal mode of operation will +be to type in your lines of code, and at the end of each line type +LINEFEED instead of Return. This will cause EMACS to decide how far +in the next statement should be indented. You can get the same effect +by typing Return then C-M-I. C-M-I can also be used to indent or +re-indent existing lines. If you type C-M-I at the beginning of an +existing line, the line will be indented the way that EMACS thinks it +ought to be. Typing C-M-I in the middle of a line will cause the rest +of the line to be lined up with a word in the line above (i.e. M-X ^R +Pascal Indent Relative is called). + +A special case is when you type LINEFEED after an END or UNTIL, or +type C-M-I at the beginning of the line after an END or UNTIL. Doing +either of these causes EMACS to go back and decide which BEGIN or +REPEAT or other keyword the END (UNTIL) matches. It then will go and +move the END (UNTIL) so that it lines up with the BEGIN or whatever. +This allows you to not have to worry about trying to convince it that +the line you are about to type is an END, not just another statement. +Just type the END, which will be in the wrong place, then type +LINEFEED, like you would with any line. It will go back and move the +END so that it's in the right place. You can turn off this "feature" +by setting the variable Reindent ENDs to zero. + +Note that, even though TABs are used for indentation, RUBOUT is +redefined so that TABs act like 8 spaces. So, if you try to rub out a +TAB, what you will get as a result is that you've moved one character +position to the left on the screen. If you want to get the ordinary +Rubout, you can use C-Rubout, which you can get by typing Control-^ +followed by Rubout. + +Using LINEFEED and C-M-I, you should be able to do all of the +formatting that you want. In cases where it fails, complain! + +Node: Details, Previous: Basics, Up: Indent, Next: Statements + +If you know that EMACS is about to give you indentation that you don't +want (see *note Failure: Future Work, for some known cases) you can be +more specific about the indentation you want. Giving an argument to +LINEFEED (or C-M-I) makes it run ^R Indent Nested. Positive arguments +make it line up with a line levels out (to the left) of the +previous line and negative arguments make in indent -arg spaces +further in (to the right). This also works if you've set Indent Align +Only. + +If there is no argument then if the cursor is not at the left margin, +the indenter calls M-X ^R Pascal Indent Relative, which lines up with +successive keywords on the previous line. Otherwise, if there is an +un-matched open parenthesis or open comment in the previous 10 lines, +it matches that. Otherwise it calls the indenter. + +If there is a keyword on the previous line and the line doesn't end +with ";", the new line is indented to the amount of the previous line +plus the amount specified by "Indent After " for the last +keyword on the previous line. + +If no keyword is found or the line ends with a sem, we just line up +with the previous statement, which may not be the previous line if +it is a nested statement or the END of a compound statement. + +If the PREVIOUS line contains an END, it will be re-indented to match +the matching BEGIN, unless Reindent END is zero. + +If the indentation you just got seems strange to you, you can get a +short description of what was done (including a pointer to the point +which was matched to get the base indentation) with M-X Print Last +Pascal Indenter (C-M-?). + +To make the indenter ignore a keyword which it now recognizes, set the +variable "Indent After " to -1. + +A description of the algorithm used is in *note Internal: Indentation. + +Node: Statements, Previous: Indent, Up: Top, Next: Comments + +These are the available insertion commands: + +C-M-. E ^R Pascal END +C-M-. W ^R Pascal WHILE +C-M-. F ^R Pascal FOR +C-M-.  ^R Pascal WITH +C-M-. I ^R Pascal IF +C-M-.  ^R Pascal ELSE +C-M-. P ^R Pascal PROCEDURE +C-M-.  ^R Pascal FUNCTION +C-M-.  ^R Pascal PROGRAM +C-M-. R ^R Pascal REPEAT +C-M-. B ^R Pascal BEGIN +C-M-. C ^R Pascal CASE +C-M-.  ^R Pascal RECORD +C-M-. B ^R Pascal BEGIN + +M-X ^R Pascal End goes back to an open block statement and inserts the +corresponding close block (END or UNTIL). Use Describe (see *Note +Help: (EMACS)Help.) on ^R Pascal End for more details. + +The general purpose of the other commands is to insert the redundant +text in their kinds of statements. For some individualities of the +commands, you can see their internal documentation via the Describe +command. Here are some of their general properties: + +The column indented to is the current horizontal position or the +indentation of the current line, if there is text on the line. For +instance, if the cursor is at column three and C-M-F is pressed, this is +inserted: + FOR DO + BEGIN + + END; (* FOR *) +(assuming Indent After BEGIN = 3, Indent After DO = 3, and Capitalize +Pascal Keywords = 1) +but if the cursor is at the end of this line: + while true do +and C-M-B is pressed, this results: + while true do begin + + end; (* while *) +(if Capitalize Pascal Keywords = 0, Indent After BEGIN = 4, Match Block +Word = 0). + + If given a positive argument, the macros will not insert the +BEGIN/END pair (where this makes sense), except the PROCEDURE/FUNCTION/ +PROGRAM group will not insert the line with VAR. If given a +negative argument, they will try to enclose - lines within the +BEGIN/END (or whatever) pair. If you want to enclose the region +instead, do ^R Count Line Region (M-=) to see what argument to give. + + ^R Pascal Procedure and ^R Pascal Function will ask you for the +subprogram name and parameters before it inserts the rest of the +block. Type them normally into the buffer where the cursor is left +and exit with C-M-C (C-M-Z on Twenex). + + Undoing and fixing: Any of the calls to these functions can be be +taken back with M-X Undo$. Remember, they insert a lot of comments; +if you don't want them, they can be killed individually with C-M-;. +Setting Insert Comments to zero will stop them all from being +inserted. + +* Menu: + +* Examples:: Variable settings for various kinds of indentation. + +Node: Examples, Previous: Statements, Up: Top, Next: Statements + + +EXAMPLE1 + + Variable settings. + +Procedure Body Indentation: 2 +Indent After Procedure: 5 +Indent after IF: 2 +Indent after THEN: 2 +Indent after ELSE: 1 +Indent after BEGIN: 3 +Indent after REPEAT: 4 +Indent after DO: 3 +Automatic Capitalization: 0 +BEGIN on same line: 0 +THEN on same line: 0 +ELSE on same line: 0 + + Indentation (assuming start column is left margin). + + +procedure xx; + + var b: integer; + begin (* xx *) + March; + end; (* xx *) + +while Foo do + begin + Bar; + end; (* while *) + +repeat + Fosit; +until false; + +if x + then + Bar + else + Rag; + + +EXAMPLE2: + + The modern indentation style proposed by Arthur Sale might have these + settings: + +Indent After Everything: 4 +Automatic Capitalization: 1 +BEGIN on same line: 1 +THEN on same line: 1 +ELSE on same line: 1 +Match Block Word: 0 + + With resulting code indented this way: +IF cond THEN BEGIN + stmts; +END; (* IF *) + +PROCEDURE Foo(bar,baz: real); + + VAR i: INTEGER; + + BEGIN (* Foo *) + body; + END; (* Foo *) + +Node: Comments, Previous: Statements, Up: Top, Next: Paren Groups + +There are various things that you can do with comments in Pascal mode +(most also work in other language mode). You can ask EMACS to start +comments for you, go to the next comment, and go to the previous +comment. There is also a global comment mode (only in Pascal) which +allows you to enter a large comment, for instance at the beginning of +a program. + +To ask EMACS to start a comment on the current line, use the command +Meta-;. This will go to the end of the line and start a comment by +inserting open and close comment brackets and leaving the point +between them. If there is already a comment on the line, EMACS will +realign it and then move into it. C-U C-X ; will create a comment +lined up with comment on the previous line, and make all subsequent +comments also try to start in that column. M-* will create a comment +at the current cursor position always (Pascal only). + +If you want EMACS to always start comments no further left than a +specific column, go to the column you want, and type C-X ; (Control-X +Semicolon). EMACS should confirm by typing Comment Column = n at the +bottom of the screen, where n is the column you changed it to. If you +want to have the same one set up for you each time you start Pascal +mode, set the variable Comment Column in you Pascal Mode Hook. +*Note Vars: (EMACS)Variables. + +If you want to go on and enter a comment at the end of the next line, +you can use the command M-N to move down to (and maybe create) a +comment on the next line. Similarly, you can use M-P to go up a line +and start a comment. + +To kill a comment at the end of the line use C-M-;. You can also use +this to move a comment by killing it and then un-killing it where you +want it to be. *Note Un-Killing: (EMACS)Un-killing. + +There is another way to enter comments, and that is the global +comment mode. To use this, use the command M-X Global Pascal Comment. +This will enter a special mode where you will be entering a block of +comments. Each comment will start in the 2nd column, and will start +with "(**" (or "{*"). You can use the commands M-;, M-N, and M-P as +described above. When you're done entering the block of comments, +exit the inner mode with C-M-C (C-M-Z on Twenex). As it leaves, it +will convert the beginnings of the comments to "(* " (or "{ ", and +move the ends so that they all line up. + +As it leaves the global comment mode, it will also convert any line +which looks like "(*** *)" into a line of stars and spaces: + "(* * * ...*)". + +Node: Paren Groups, Previous: Comments, Up: Top, Next: Hints + +The parenthesis manipulation functions are really LISP commands and +their names reflect it. A "Sexp" is (for our purposes) either a +parenthesized expression or a single word (depending what we are next +to). Commands for manipulating them are: + +C-M-F ^R Forward Sexp +C-M-B ^R Backward Sexp +C-M-K ^R Kill Sexp +C-M-Rubout ^R Backward Kill Sexp +C-M-K ^R Kill Sexp +C-M-@ ^R Mark Sexp + +In addition to the Sexp's there are "Lists". These are also +parenthesized expressions, but we have additional commands for moving +"down" inside the parens and "up" out of the paren group. + +C-M-( ^R Backward Up List +C-M-) ^R Forward Up List +C-M-D ^R Down List +C-M-U ^R Backward Up List + + +Node: Hints, Previous: Paren Groups, Up: Top, Next: Hackers + +. To figure out your BEGIN/END pairing, use the command M-X Display + Matching Lines to show only the lines with a BEGIN or END. You can + also use C-M-P and C-M-N to move over matching BEGIN/END (or + REPEAT/UNTIL, etc.) pairs. + +. Use M-X ^R Indent Rigidly (C-X TAB) to conveniently change the + indentation of a block of text. + +. Use ^R Back to Indentation (M-M) to move to the logical start of + this line. ^R Up Indented Line and ^R Down Indented Line will move + over their lines and end up at their logical start. + +Node: Hackers, Previous: Hints, Up: Top, Next: Philosophy + +This section is designed for people intending to do work on Pascal +Mode, to install it on another system, or to modify it into another +language. It might also be interesting and/or helpful for a heavy +user of Pascal Mode. + +* Menu: + +* Philosophy:: Reasons why some things are how they are. +* Syntax Table:: Table which keeps the syntax of interesting keywords. +* Install:: Information for somebody installing or re-targetting. +* Algorithms:: Description of the less obvious algorithms used. +* Future Work:: If you know or want to learn TECO and have time... + +Node: Philosophy, Previous: Hackers, Up: Hackers, Next: Syntax Table + +The idea behind Pascal (or any language mode) in EMACS is to give the +user extra support by allowing the editor to know something about the +higher level structure of his file. At the same time, the reason +there is not a seperate "structured editor" for Pascal is so that the +basic commands will be common between all editing tasks. + +Because of the last point, it has been my attempt in Pascal Mode to +minimize the disturbance of the user's basic editing. That's why I +broke with standard EMACS philosophy and did not put the line indenter +on TAB. + +The reasons all of the keys defined are control-meta- commands are 1) +to provide a better mnemonic for the user to remember where all the +Pascal commands are, 2) because they replace unused keys or dangerous +LISP commands, and 3) so that there will be a consistent "language +prefix" which users know will be squashed by the major modes. It is +my opinion that more major modes should follow this practice. + +A lot of work has gone in to making the indentation commands correct +and fast. This allows the user to ignore the question of indentation +when typing in text and frees his concentration for more important +aspects of the program. At the same time, I have attempted to keep +the base algorithm as simple as possible; the user has to understand +what is going on in the algorithm to get the most good out of it, and +simple is better for that. + +Node: Syntax Table, Previous: Philosophy, Up: Hackers, Next: Install + +The syntax table is read from EMACS:PASCAL.SYN by the function M-X & +Read Keywords when Pascal is loaded. There is one line per keyword. +The first word on the line is the keyword itself, the second word is +the default indentation, and the third word is a binary word of the +"descriptor" bits. On loading the Pascal library, the file is read +into the q-vector variable Pascal Syntax Table. This variable exactly +mirrors the structure of the file. + +The bits in the status word are decoded as follows: 1 (the least +significant) is set for keywords which are "block beginners", things +like BEGIN, REPEAT, RECORD & CASE; 2 is set for the "block enders"; 3 +& 4 are set for ELSE and THEN, resp., because these are matched up +with each other when going back over a nested statement; 5 is for +non-compounding and non-if keywords like WHILE, DO, WITH; 6 is for the +subprogram statements, PROGRAM, PROCEDURE & FUNCTION; 7 is for the +declaration keywords, LABEL, TYPE, CONST, & VAR; 8 is for the IF-Block +Body keywords (IF, THEN, ELSE); 9 is for THEN and DO because when +they end a sub-statement, the following statement is nested from that +statement; and 10 is for BEGIN alone becuase it has significance as a +simple blocker the other "block beginners" don't have. + +Node: Install, Previous: Syntax Table, Up: Hackers, Next: Algorithms + +Most of the language dependancies for indentation are keyed through +the syntax table. If you can't get the indenter to work for your site +by making changes to the syntax table, try adding a new entry to the +syntax table and updating the indent macro for both languages. It +will be nice to have as much in common as possible between structured +languages. + +Any characters which can appear in keywords should be listed in the +routines & Back To Word and & Forward To Word. + +Macros which would probably have to be replaced are Pascal Mode and & +Default Init Pascal Mode. Macros which will have to be modified or +replaced because of language dependancies which are not in the syntax +table are the insert macros, Mark Procedure, and & Save Errors (which +goes through the listing file getting the info on all the compiler +errors when ErrList mode is in effect). There is also a shameful but +necessary direct dependance on the syntax of the compiler. See & In +ErrList Mode and the file PASCAL.MIC for places to make changes. + +Also, in general when I look to ignore comments or strings I assume +that both "{}" and "(* *)" are comment delimiters in addition to the +variables Comment Start and Comment End. This is becuase our version +of Pascal allows both those comment delimiters and more. The routines +where this occurs are M-X & Forward To Chars, M-X & Back To Chars, and +M-X ^R Pascal Begin. In the former two routines I pass over quoted +strings, which I assume are bounded by a single quote, "'", on each +side. If your language uses different or alternate string delimiters +this will have to be changed in line, too. + +Node: Algorithms, Previous: Install, Up: Hackers, Next: Future Work + +Most of the algorithms used in the file are straight forward and are +commented in line. + +* Menu: + +* Indentation:: How & Indent Line works +* Error Saving:: Kluges for ErrList mode + +Node: Indentation, Previous: Algorithms, Up: Algorithms, Next: Error Saving + +The indent macro has four phases it goes through in trying to indent a +line: scanning for the key token, moving back over begin/end pairs, +moving back to controlling statements if the one found is nested, and +rejoining split lines. + +The first involves scaning back in the immediately previous line (or +to any line which has an open paren or open comment for a close on +this line) till it finds a keyword with a non-negative indent value or +a block ender. If a semicolon is encountered it's presence is noted +by setting the indent value to -2 (it starts as -1); after we see a +sem we usually don't care if there is a keyword with a non-negative +indent value (we want to line up with "IF x < 0 THEN Write(x);"). +However, we do pick up an indent value if we see one of the subprogram +keywords or declaration keywords (you want to indent extra after "VAR +i: Integer;"). + +At the end of that stage, if we have found a positive indent keyword, +we are at our goal; we will indent from this statement offset by the +value of the indent for that keyword and skip the next two stages. If +we found a Block Beginner and Match Block Word is set, allign with the +word (and goto DONE0 to avoid calculating a new position). Otherwise +align with the indentation of this statement (DONE1). + +Otherwise, we just want to line up with the proper line, but finding +it will be tricky. In the next stage we continue back over the last +line (and those connected by parens) to see if there is a block ender. +If so, we use Back Level to move backwards to the matching beginer; +the statement controling the one with the beginner keyword is the one +we want to line up the next statement with. + +This section also takes care of re-indenting ENDs. There are two +philosophies on aligning ENDs which are supported. One says they +should be lined up with the statements of the block. To get this +behavior the user sets Reindent End to zero -- END's will be left +alone where the normal statement indenter put them. The philosophy +holds that the END (or UNTIL) should match the indentation of either +the line with the beginner or (if Match Block Word is set) the +beginner word itself. In this case the END will not be indented +correctly in the text because we indented it for a statement; this +stage takes care of properly re-aligning it. + +The next stage goes back over nested statements (all substatements +which end in THEN or DO nest the next statement) and THEN/ELSE pairs +to the actual line we want to indent from. If we come to a THEN and +the there was no SEM on the first line saw in the back parsing, assume +the user is about to type an ELSE and indent the line the same as the +THEN. + +When we finally have the exact statement we want to match, we still +can't count on it's being entirely on the line we went to. When +conditions for an IF or WHILE or such get too large people often split +them between several lines. The simple fix is to scan back to the +matching IF or WHILE or such whenever we wound up on a DO or THEN. +The two complications are 1) we want to do the same thing for THEN +BEGIN and 2) many people like to have the THEN on a seperate line from +the IF. To fix (1) we just move back over a BEGIN before checking for +the THEN/ELSE and to fix (2), we check to be sure that the THEN or DO +is not the first word on the line. + +Now we finally have a line and a position in a line. The indentation +is done and properly marked to be shown if the user is curious about +what happened. + + +Node: Error Saving, Previous: Indentation, Up: Algorithms, Next: Algorithms + +None of the algorithms for this part are particularly brilliant; +instead the problem is that they are somewhat klugy. There has been a +constant race with the compiler developers who like to change the +syntax for calling the compiler with a listing file and to change the +format of the listing file itself. + +When ErrList mode is selected, the hooks used by M-X Compile are +arranged so that a listing file will be created on the compilation and +so that M-X & Compile Compilation will be called after the compile. + +M-X & Compile Compilation grabs the listing file in the buffer *LST* +and deletes the file from disk. Then it calls M-X & Save Errors to +extract the relevant information the errors from the listing file and +build a q-vector to contain them. To find each error we search for +a consistent string which the compiler puts on error lines. From the +error line we get the error message (to display) and the position in +the line of the error. From the previous line we extract the line +number. While we are searching for the error messages we also keep +the current page active. + +M-X ^R Next Error just keeps an index into the q-vector of error +information. Each time it is called it updates the pointer, hops to +page, line, and line pos of the error and prints out the error +message. + +This will all work much better when I 1) hack the compiler to put out +an error listing which contains just the errors and has the +information I need in a more direct format (I might even convince it +to give me the character position of the errors) and 2) hack TECO to +have updating marks in the file. As things stand now, if the user +inserts a page before the page of the error, or a line before the line +of the error in the page of the error, or a character before the +position of the error in the line of the error, everything is messed +up. All those operations are reasonably likely in the course of +fixing the previous errors. The fix will be to mark the positions of +all the errors with updating marks so that we can still find them +after all the changes have taken place. + + +Node: Future Work, Previous: Algorithms, Up: Hackers, Next: Hackers +1. Cases the indenter gets wrong + A: After the declarations and before the BEGIN of a block we indent + for another declaration. + Fix: Re-indent previous line when BEGIN typed if previous keyword + is a declaration keyword or subprogram keyword? Bad. + + B: After the last END of a procedure. Tends to line up with a + declaration statement of the ENDed procedure instead of with the + PROCEDURE line itself. + Fix: ? + + C: Labels are totally ignored. I could find no clean way to handle + them. + Fix: Correct behavior = ? Statement labels (those which start on + the left margin?) should be totally ignored, both in syntactic + processing (make "back to token" ignore them?) and in + determining the indentation of the line they're on. Case + labels should probably have an indent after them if there is + no text after them, but they should be ignored if there is + text on the same line as them. + D: After a THEN when it is on the same line as the IF. + +2. Mark Procedure should be able to handle procedures with nested + procedures. + +3. Automatic capitalization should not expand the words in comments. + This is easy but expensive to do. You just put a macro on each + expansion definition which does a search backwards for an open or + close comment. If an open comment is found, we are in an abbrev + and the abbrev should be unexpanded. + +4. Match Block Word should be replaced by a bit per keyword saying + whether to match that keyword. This is for two reasons: 1) many + people like to match RECORD but few like to match BEGIN and 2) this + will allow people to indent like this if they want: + IF c1 THEN WHILE c2 DO + s; + +5. The insert commands should be hacked to be one command parsing a + general schema string. Maybe add a bit to the symbol table + corresponding to "Indent After " to help out. + +6. The language dependancies of Mark Procedure and the insert routines + should be eliminated. The insert routines should have one driver + to which a schema is passed; they are too ad hoc and messy as it is. + \ No newline at end of file diff --git a/doc/info/epasc.info b/doc/info/epasc.info new file mode 100644 index 00000000..e59bd5f1 --- /dev/null +++ b/doc/info/epasc.info @@ -0,0 +1,736 @@ +-*-Text-*- + +File: EPASC Node: Top Up: (EMACS)Top Next: Available + +PASCAL mode in EMACS provides support for inputing and editing Pascal +code. You should be in Pascal Mode automatically when you edit a +Pascal file. You can call it directly by loading the Pascal library +(M-X Load Library Pascal ) and giving the command M-X Pascal Mode. + +* Menu: + +* Available:: List of all special commands. +* Compile:: Macros for hopping right to your compiler errors. +* Variables:: Synopsis of the customization variables. +* Indent:: Indenting old and new lines of code. +* Statements:: Inserting complete structured statements. +* Comments:: Things you can do with comments in PASCAL mode. +* Paren Groups:: Commands which manipulate parenthesized expressions. +* Hints:: Other commands useful for editing Pascal code. +* Hackers:: Information for hackers or the very interested. + +Node: Available, Previous: Top, Next: Compile, Up: Top + +Commands which start on keys: + +C-M-I The main indentation driver, described under Indent. +LINEFEED Does a CRLF, then a C-M-I. Useful at the ends of lines. +C-M-? Explains the last indentation, if you're curious. +C-M-\,C-M-G,M-G Indent Pascal Region (re-indent all lines in region) + +Rubout TAB-hacking Rubout (converts TABs to spaces). +C-Rubout Normal Rubout. + +M-; Start comment at end of line. *note comments: comments. +C-M-* Insert a comment right here (don't re-indent) +C-M-$ Global Pascal Comment + +C-M-N Forward over a BEGIN/END (or REPEAT/UNTIL etc.) pair. +C-M-P Backward over a BEGIN/END pair. +C-M-H Mark Procedure + +C-M-. Prefix for Structured Insert Commands. *note: St: Statements + +C-M-E If in ErrList Mode, jumps to position of next compiler error + and prints the error in the echo area. *note Error: Compile + +C-M-, Causes file currently being edited to be compiled and run. + *note Compile: Compile + +C-M-C Same Capitalization, make all occurences of word same cap. +C-M-S No Comment Search, search for something ignoring + comment contents. + +C-M-+ Turns previous variable name into command to increment + that variable. That is, FOO C-M-+ turns into + FOO:= FOO + 1; +C-M-' Inserts a string of specified length and lets you + recursively edit the contents of the string in + overwrite mode. String length is in variable + String Length Default. *note Variables: Variables +C-M-{ Pulls non-comment text from previous line beginning at + character (asked for) and puts it on this line. Also + performs indentation on the result. Useful when you + want to change the way a line is broken. + +M-X Pascal Mode + Restart Pascal Mode +M-X Pascal Expand Abbrevs in Region + Expands all abbrevs in region, but not in comments. +M-X Capitalize Pascal Keywords + Causes all keywords typed or inserted to be capitalized. + See variable Automatic Capitalization for how to do this + automatically. + +Node: Compile, Previous: Available, Next: Variables, Up: Top + + With the command M-X Compile (C-M-,) you can automatically compile +and execute your program in an inferior exec. You will probably need +to type "POP" (on TWENEX) or $P (on ITS) after the compilation to +return from the inferior exec to EMACS. Hint: to get special +compiler switches or to load other files with yours, put the switches +and other files in the variable Compiler Switches; this variable will +be appended to the compile command line (*Note Variables: +(EMACS)Variables.). + + If you are in ErrList Mode, the compiler errors you see when you do +M-X Compile (C-M-,) are saved away in the buffer named *ErrList*. The +command M-X ^R Next Error (C-M-E) will move to the position of an +error in your Pascal program and type out what the error was in the +echo area (at the bottom of the screen). + + To go into ErrList Mode, just give the command M-X ErrList Mode. To +get out, give the same command again. You can also get in by setting +the variable, ErrList Mode, to something other than zero. This will +work from an init or evars file (*Note Init: (EMACS)INIT.) or a local +modes list (*Note Locals: (EMACS)Locals.). + + +Node: Variables, Previous: Available, Up: Top, Next: Indent + + The PASCAL library has several switches you can set to customize its +behavior. For info on how to set variables *note Vars: (EMACS)Variables. + +Variable name D Explaination +---------------------------------------------------------------------------- +ErrList Mode 0 If non-zero, compiler errors found with + M-X Compile (C-M-,) are saved so that + you can step through them with C-M-E. + +Indent Align Only 0 If non-zero, LINEFEED only aligns + with previous line (does ^R Indent Nested). + +Indent After --- 4 For any keyword, Indent After + is the amount to indent extra after + seeing . +Begin-Block Body Indentation 4 Default indent after BEGIN, + REPEAT, CASE, LOOP, RECORD. +IF-Block Body Indentation 4 Default indent after IF, THEN, ELSE, + WHILE, FOR, DO, WITH. +Decl Body Indentation 4 Default indent after CONST, TYPE, + VAR, LABEL. +--- Body Indentation 4 Amount to indent Body, but not variables, of + subprogram keyword specified. Indentation + for declaration section is Indent After --- +PROCEDURE Body Indentation 4 Default for both "--- Body Indentation" and + "Indent After ---" for PROCEDURE, FUNCTION, + and PROGRAM. +Indent After Everything 4 Default for any value not specified. + +Reindent ENDs 1 0 = leave ENDs & UNTILs indented same as + statements in block; 1 = align END's with + corresponding BEGIN line. +Match Block Word 0 If non-zero, indentation after line with + the keyword BEGIN, REPEAT, CASE, and RECORD + will start from the position of the keyword + within the line, not the indentation of the + line. +Match ---- 0 For any keyword, if this is set indentation + will be from that keyword, not the line's + indentation. +Match ( 1 Like Match ----, only it defaults to 1. + +BEGIN on same line 0 Non-zero means inserted BEGINs are + left on same line as stmt. +THEN on same line 1 Non-zero means leave THEN on line + with IF. +ELSE on same line 0 Non-zero means try to stick ELSE on + end of previous line. +---- on same line 0 Like the above for many other keywords. + +Automatic Capitalization 0 If non-zero before Pascal Mode Entered + (calling MM & Default Init Pascal Mode), + all keywords typed or inserted are + capitalized. If set non-zero later, + only keywords inserted are capitalized. + See M-X Capitalize Pascal Keywords. + +String Length Default 10 Default length for ^R Insert String +Insert Comments 1 Non-zero means insert functions + insert some labeling comments. + +-------------------Comment Controlling Variables-------------------- +Comment Column 0 Where EMACS tries to start your + comments (here or after your code). +Global Comment Column 10 Comment Column when doing a Global + Pascal Comment. +Pascal Star Line Width 51 Do Describe on ^R Global Pascal Comment. +Comment Rounding "+1" Algorithm which EMACS uses + to figure where to start the comment + if your code extends beyond the + comment column. +* Menu: + +* Examples:: Variable settings for various kinds of indentation. + +File: EPASC, Node: Indent, Up: Top, Previous: Variables, Next: Statements + + PASCAL mode allows easy formatting of Pascal code as you type it in. Its +main concern is with indentation, not with breaking lines, so that it fits in +well with most styles of formatting. There is also a great deal of +customization avaialable to suit individual tastes. Note: If the load is too +high to effectively use the indentation package, or you just don't get along +with it, you can turn it off by setting Indent Align Only to 1. This will +force the indentation to only align with the previous line, without trying to +figure out how much less or more to indent. + +* Menu: + +* Basics:: The basics of using PASCAL mode indentation +* Details:: Some details about how indentation mode operates +* Examples:: Variable settings for various kinds of indentation. + +Node: Basics, Previous: Indent, Up: Indent, Next: Details + +The two indentation commands are LINEFEED and C-M-I. C-M-I calls the +main routine to do indenting, and LINEFEED does a return, then calls +C-M-I. + +While you are typing in a program, the normal mode of operation will +be to type in your lines of code, and at the end of each line type +LINEFEED instead of Return. This will cause EMACS to decide how far +in the next statement should be indented. You can get the same effect +by typing Return then C-M-I. C-M-I can also be used to indent or +re-indent existing lines. If you type C-M-I at the beginning of an +existing line, the line will be indented the way that EMACS thinks it +ought to be. Typing C-M-I in the middle of a line will cause the rest +of the line to be lined up with a word in the line above (i.e. M-X ^R +Pascal Indent Relative is called). + +A special case is when you type LINEFEED after an END or UNTIL, or +type C-M-I at the beginning of the line after an END or UNTIL. Doing +either of these causes EMACS to go back and decide which BEGIN or +REPEAT or other keyword the END (UNTIL) matches. It then will go and +move the END (UNTIL) so that it lines up with the BEGIN or whatever. +This allows you to not have to worry about trying to convince it that +the line you are about to type is an END, not just another statement. +Just type the END, which will be in the wrong place, then type +LINEFEED, like you would with any line. It will go back and move the +END so that it's in the right place. You can turn off this "feature" +by setting the variable Reindent ENDs to zero. + +Note that, even though TABs are used for indentation, RUBOUT is +redefined so that TABs act like 8 spaces. So, if you try to rub out a +TAB, what you will get as a result is that you've moved one character +position to the left on the screen. If you want to get the ordinary +Rubout, you can use C-Rubout, which you can get by typing Control-^ +followed by Rubout. + +Using LINEFEED and C-M-I, you should be able to do all of the +formatting that you want. In cases where it fails, complain! + +Node: Details, Previous: Basics, Up: Indent, Next: Statements + +If you know that EMACS is about to give you indentation that you don't +want (see *note Failure: Future Work, for some known cases) you can be +more specific about the indentation you want. Giving an argument to +LINEFEED (or C-M-I) makes it run ^R Indent Nested. Positive arguments +make it line up with a line levels out (to the left) of the +previous line and negative arguments make in indent -arg spaces +further in (to the right). This also works if you've set Indent Align +Only. + +If there is no argument then if the cursor is not at the left margin, +the indenter calls M-X ^R Pascal Indent Relative, which lines up with +successive keywords on the previous line. Otherwise, if there is an +un-matched open parenthesis or open comment in the previous 10 lines, +it matches that. Otherwise it calls the indenter. + +If there is a keyword on the previous line and the line doesn't end +with ";", the new line is indented to the amount of the previous line +plus the amount specified by "Indent After " for the last +keyword on the previous line. + +If no keyword is found or the line ends with a sem, we just line up +with the previous statement, which may not be the previous line if +it is a nested statement or the END of a compound statement. + +If the PREVIOUS line contains an END, it will be re-indented to match +the matching BEGIN, unless Reindent END is zero. + +If the indentation you just got seems strange to you, you can get a +short description of what was done (including a pointer to the point +which was matched to get the base indentation) with M-X Print Last +Pascal Indenter (C-M-?). + +To make the indenter ignore a keyword which it now recognizes, set the +variable "Indent After " to -1. + +A description of the algorithm used is in *note Internal: Indentation. + +Node: Statements, Previous: Indent, Up: Top, Next: Comments + +These are the available insertion commands: + +C-M-. E ^R Pascal END +C-M-. W ^R Pascal WHILE +C-M-. F ^R Pascal FOR +C-M-.  ^R Pascal WITH +C-M-. I ^R Pascal IF +C-M-.  ^R Pascal ELSE +C-M-. P ^R Pascal PROCEDURE +C-M-.  ^R Pascal FUNCTION +C-M-.  ^R Pascal PROGRAM +C-M-. R ^R Pascal REPEAT +C-M-. B ^R Pascal BEGIN +C-M-. C ^R Pascal CASE +C-M-.  ^R Pascal RECORD +C-M-. B ^R Pascal BEGIN + +M-X ^R Pascal End goes back to an open block statement and inserts the +corresponding close block (END or UNTIL). Use Describe (see *Note +Help: (EMACS)Help.) on ^R Pascal End for more details. + +The general purpose of the other commands is to insert the redundant +text in their kinds of statements. For some individualities of the +commands, you can see their internal documentation via the Describe +command. Here are some of their general properties: + +The column indented to is the current horizontal position or the +indentation of the current line, if there is text on the line. For +instance, if the cursor is at column three and C-M-. F is pressed, +this is inserted: + FOR DO + BEGIN + + END; (* FOR *) +(assuming Capitalize Pascal Keywords = 1, Indent After BEGIN = 3, and +Indent After DO = 3) but if the cursor is at the end of this line: + WHILE true DO +and C-M-B is pressed, this results: + WHILE true DO BEGIN + + END; (* WHILE *) +(assuming Capitalize Pascal Keywords = 1, Indent After BEGIN = 3, and +Match Block Word = 0). + + If given a positive argument, the macros will not insert the +BEGIN/END pair (where this makes sense), except the PROCEDURE/FUNCTION/ +PROGRAM group will not insert the line with VAR. If given a +negative argument, they will try to enclose - lines within the +BEGIN/END (or whatever) pair. If you want to enclose the region +instead, do ^R Count Line Region (M-=) to see how many lines that is. + + ^R Pascal Procedure and ^R Pascal Function and ^R Pascal Program +will ask you for a name and parameters before they inserts the rest of +the block. Type them normally into the buffer where the cursor is +left and exit with C-M-C (C-M-Z on Twenex). + + Undoing and fixing: Any of the calls to these functions can be be +taken back with M-X Undo$. Remember, they insert a lot of comments; +if you don't want them, they can be killed individually with C-M-;. +Setting Insert Comments to zero will stop them all from being +inserted. + +* Menu: + +* Examples:: Variable settings for various kinds of indentation. + +Node: Examples, Previous: Statements, Up: Top, Next: Statements + + +EXAMPLE1 + + Variable settings. + +Procedure Body Indentation: 2 +Indent After Procedure: 5 +Indent after IF: 2 +Indent after THEN: 2 +Indent after ELSE: 1 +Indent after BEGIN: 3 +Indent after REPEAT: 4 +Indent after DO: 3 +Automatic Capitalization: 0 +BEGIN on same line: 0 +THEN on same line: 0 +ELSE on same line: 0 + + Indentation (assuming start column is left margin). + + +procedure xx; + + var b: integer; + begin (* xx *) + March; + end; (* xx *) + +while Foo do + begin + Bar; + end; (* while *) + +repeat + Fosit; +until false; + +if x + then + Bar + else + Rag; + + +EXAMPLE2: + + The modern indentation style proposed by Arthur Sale might have these + settings: + +Indent After Everything: 4 +Automatic Capitalization: 1 +BEGIN on same line: 1 +THEN on same line: 1 +ELSE on same line: 1 +Match Block Word: 0 + + With resulting code indented this way: +IF cond THEN BEGIN + stmts; +END; (* IF *) + +PROCEDURE Foo(bar,baz: real); + + VAR i: INTEGER; + + BEGIN (* Foo *) + body; + END; (* Foo *) + +Node: Comments, Previous: Statements, Up: Top, Next: Paren Groups + +There are various things that you can do with comments in Pascal mode +(most also work in other language mode). You can ask EMACS to start +comments for you, go to the next comment, and go to the previous +comment. There is also a global comment mode (only in Pascal) which +allows you to enter a large comment, for instance at the beginning of +a program. + +To ask EMACS to start a comment on the current line, use the command +Meta-;. This will go to the end of the line and start a comment by +inserting open and close comment brackets and leaving the point +between them. If there is already a comment on the line, EMACS will +realign it and then move into it. C-U C-X ; will create a comment +lined up with comment on the previous line, and make all subsequent +comments also try to start in that column. M-* will create a comment +at the current cursor position always (Pascal only). + +If you want EMACS to always start comments no further left than a +specific column, go to the column you want, and type C-X ; (Control-X +Semicolon). EMACS should confirm by typing Comment Column = n at the +bottom of the screen, where n is the column you changed it to. If you +want to have the same one set up for you each time you start Pascal +mode, set the variable Comment Column in you Pascal Mode Hook. +*Note Vars: (EMACS)Variables. + +If you want to go on and enter a comment at the end of the next line, +use the command M-N to move down to (and maybe create) a comment on +the next line. Similarly, you can use M-P to go up a line and start a +comment. + +To kill a comment at the end of the line use C-M-;. You can also use +this to move a comment by killing it and then un-killing it where you +want it to be. *Note Un-Killing: (EMACS)Un-killing. + +There is another way to enter comments, and that is the global +comment mode. To use this, use the command M-X Global Pascal Comment. +This will enter a special mode where you will be entering a block of +comments. Each comment will start in the 2nd column, and will start +with "(**" (or "{*"). You can use the commands M-;, M-N, and M-P as +described above. When you're done entering the block of comments, +exit the inner mode with C-M-C (C-M-Z on Twenex). As it leaves, it +will convert the beginnings of the comments to "(* " (or "{ ", and +move the ends so that they all line up. + +As it leaves the global comment mode, it will also convert any line +which looks like "(*** *)" into a line of stars and spaces: + "(* * * ...*)". + +Node: Paren Groups, Previous: Comments, Up: Top, Next: Hints + +The parenthesis manipulation functions are really LISP commands and +their names reflect it. A "Sexp" is (for our purposes) either a +parenthesized expression or a single word (depending what we are next +to). Commands for manipulating them are: + +C-M-F ^R Forward Sexp +C-M-B ^R Backward Sexp +C-M-K ^R Kill Sexp +C-M-Rubout ^R Backward Kill Sexp +C-M-@ ^R Mark Sexp + +In addition to the Sexp's there are "Lists". These are also +parenthesized expressions, but we have additional commands for moving +"down" inside the parens and "up" out of the paren group. + +C-M-( ^R Backward Up List +C-M-) ^R Forward Up List +C-M-D ^R Down List +C-M-U ^R Backward Up List + + +Node: Hints, Previous: Paren Groups, Up: Top, Next: Hackers + +. To figure out your BEGIN/END pairing, use the command M-X Display + Matching Lines to show only the lines with a BEGIN or END. You can + also use C-M-P and C-M-N to move over matching BEGIN/END (or + REPEAT/UNTIL, etc.) pairs. + +. Use M-X ^R Indent Rigidly (C-X TAB) to conveniently change the + indentation of a block of text. + +. Use ^R Back to Indentation (M-M) to move to the logical start of + this line. ^R Up Indented Line and ^R Down Indented Line will move + to the th line and end up at its logical start. + +Node: Hackers, Previous: Hints, Up: Top, Next: Philosophy + +This section is designed for people intending to do work on Pascal +Mode, to install it on another system, or to modify it into another +language. It might also be interesting and/or helpful for a heavy +user of Pascal Mode. + +* Menu: + +* Philosophy:: Reasons why some things are how they are. +* Syntax Table:: Table which keeps the syntax of interesting keywords. +* Install:: Information for somebody installing or re-targetting. +* Algorithms:: Description of the less obvious algorithms used. +* Future Work:: If you know or want to learn TECO and have time... + +Node: Philosophy, Previous: Hackers, Up: Hackers, Next: Syntax Table + +The idea behind Pascal (or any language mode) in EMACS is to give the +user extra support by allowing the editor to know something about the +higher level structure of his file. At the same time, the reason +there is not a seperate "structured editor" for Pascal is so that the +basic commands will be common between all editing tasks. + +Because of the last point, it has been my attempt in Pascal Mode to +minimize the disturbance of the user's basic editing. That's why I +broke with standard EMACS philosophy and did not put the line indenter +on TAB. + +The reasons all of the keys defined are control-meta- commands are 1) +to provide a better mnemonic for the user to remember where all the +Pascal commands are, 2) because they replace unused keys or dangerous +LISP commands, and 3) so that there will be a consistent "language +prefix" which users know will be squashed by the major modes. It is +my opinion that more major modes should follow this practice. + +A lot of work has gone in to making the indentation commands correct +and fast. This allows the user to ignore the question of indentation +when typing in text and frees his concentration for more important +aspects of the program. At the same time, I have attempted to keep +the base algorithm as simple as possible; the user has to understand +what is going on in the algorithm to get the most good out of it, and +simple is better for that. + +Node: Syntax Table, Previous: Philosophy, Up: Hackers, Next: Install + +The syntax table is read from EMACS:PASCAL.SYN by the function M-X & +Read Keywords when Pascal is loaded. There is one line per keyword. +The first word on the line is the keyword itself, the second word is +the default indentation, and the third word is a binary word of the +"descriptor" bits. On loading the Pascal library, the file is read +into the q-vector variable Pascal Syntax Table. This variable exactly +mirrors the structure of the file. + +The bits in the status word are decoded as follows: 1 (the least +significant) is set for keywords which are "block beginners", things +like BEGIN, REPEAT, RECORD & CASE and also "("; 2 is set for the +"block enders", END & UNTIL and ")"; 3 & 4 are set for ELSE and THEN, +resp., because these are matched up with each other when going back +over a nested statement; 5 is for non-compounding and non-if keywords +like WHILE, DO, WITH; 6 is for the subprogram statements, PROGRAM, +PROCEDURE & FUNCTION; 7 is for the declaration keywords, LABEL, TYPE, +CONST, & VAR; 8 is for the IF-Block Body keywords (IF, THEN, ELSE); 9 +is for THEN, DO & OF and 10 is for CASE, WHILE, WITH & FOR because +they bracket the control part of a statement; 11 is for BEGIN alone +because it has significance as a simple blocker the other "block +beginners" don't have; 12 is for REPEAT. + + +Node: Install, Previous: Syntax Table, Up: Hackers, Next: Algorithms + +Most of the language dependancies for indentation are keyed through +the syntax table. If you can't get the indenter to work for your site +by making changes to the syntax table, try adding a new entry to the +syntax table and updating the indent macro for both languages. It +will be nice to have as much in common as possible between structured +languages. + +Any characters which can appear in keywords should be listed in the +routines & Back To Word and & Forward To Word. + +Macros which would probably have to be replaced are Pascal Mode and & +Default Init Pascal Mode. Macros which will have to be modified or +replaced because of language dependancies which are not in the syntax +table are the insert macros, Mark Procedure, and & Save Errors (which +goes through the listing file getting the info on all the compiler +errors when ErrList mode is in effect). There is also a shameful but +necessary direct dependance on the syntax of the compiler. See M-X & +Compile Compilation for places to make changes. + +In general, when I look to ignore comments or strings I assume that +both "{}" and "(* *)" are comment delimiters in addition to the +variables Comment Start and Comment End. This is because our version +of Pascal allows both those comment delimiters and more. The routines +where this occurs are M-X & Forward To Chars, M-X & Back To Chars, and +M-X ^R Pascal Begin. In the former two routines I pass over quoted +strings, which I assume are bounded by a the ..D quote char on each +side. + +Node: Algorithms, Previous: Install, Up: Hackers, Next: Future Work + +Most of the algorithms used in the file are straight forward and are +commented in line. + +* Menu: + +* Indentation:: How & Indent Line works +* Error Saving:: Kluges for ErrList mode + +Node: Indentation, Previous: Algorithms, Up: Algorithms, Next: Error Saving + +The indent macro has four phases it goes through in trying to indent a +line: scanning for the key token, moving back over begin/end pairs, +moving back to controlling statements if the one found is nested, and +rejoining split lines. + +The first involves scaning back in the immediately previous line (or +to any line which has an open paren or open comment for a close on +this line) till it finds a keyword with a non-negative indent value or +a block ender. If a semicolon is encountered it's presence is noted +by setting the indent value to -2 (it starts as -1); after we see a +sem we usually don't care if there is a keyword with a non-negative +indent value (we want to line up with "IF x < 0 THEN Write(x);"). +However, we do pick up an indent value if we see one of the subprogram +keywords or declaration keywords (you want to indent extra after "VAR +i: Integer;"). + +At the end of that stage, if we have found a positive indent keyword, +we are at our goal; we will indent from this statement offset by the +value of the indent for that keyword and skip the next two stages. If +we found a Block Beginner and Match Block Word is set, allign with the +word (and goto DONE0 to avoid calculating a new position). Otherwise +align with the indentation of this statement (DONE1). + +Otherwise, we just want to line up with the proper line, but finding +it will be tricky. In the next stage we continue back over the last +line (and those connected by parens) to see if there is a block ender. +If so, we use Back Level to move backwards to the matching beginer; +the statement controling the one with the beginner keyword is the one +we want to line up the next statement with. + +This section also takes care of re-indenting ENDs. There are two +philosophies on aligning ENDs which are supported. One says they +should be lined up with the statements of the block. To get this +behavior the user sets Reindent End to zero -- END's will be left +alone where the normal statement indenter put them. The philosophy +holds that the END (or UNTIL) should match the indentation of either +the line with the beginner or (if Match Block Word is set) the +beginner word itself. In this case the END will not be indented +correctly in the text because we indented it for a statement; this +stage takes care of properly re-aligning it. + +The next stage goes back over nested statements (all substatements +which end in THEN or DO nest the next statement) and THEN/ELSE pairs +to the actual line we want to indent from. If we come to a THEN and +the there was no SEM on the first line saw in the back parsing, assume +the user is about to type an ELSE and indent the line the same as the +THEN. + +When we finally have the exact statement we want to match, we still +can't count on it's being entirely on the line we went to. When +conditions for an IF or WHILE or such get too large people often split +them between several lines. The simple fix is to scan back to the +matching IF or WHILE or such whenever we wound up on a DO or THEN. +The two complications are 1) we want to do the same thing for THEN +BEGIN and 2) many people like to have the THEN on a seperate line from +the IF. To fix (1) we just move back over a BEGIN before checking for +the THEN/ELSE and to fix (2), we check to be sure that the THEN or DO +is not the first word on the line. + +Now we finally have a line and a position in a line. The indentation +is done and properly marked to be shown if the user is curious about +what happened. + + +Node: Error Saving, Previous: Indentation, Up: Algorithms, Next: Algorithms + +None of the algorithms for this part is particularly brilliant; I +describe them because they are somewhat klugy. There has been a +constant race with the compiler developers who like to change the +syntax for calling the compiler with an error listing and to change +the format of the listing file itself. + +When ErrList mode is selected, the hooks used by M-X Compile are +arranged so that a listing file will be created on the compilation and +so that M-X & Compile Compilation will be called after the compile. & +Compile Compilation visits the error listing file in the buffer +*ErrList*. Then it calls M-X & Save Errors to extract the relevant +information from the listing file and build a q-vector to contain +them. + +M-X ^R Next Error keeps an index into the q-vector of error +information. Each time it is called it updates the pointer, hops to +the position of the error and prints out the error message. + + +Node: Future Work, Previous: Algorithms, Up: Hackers, Next: Hackers +1. Cases the indenter gets wrong + A: After the declarations and before the BEGIN of a block we indent + for another declaration. + Fix: Re-indent previous line when BEGIN typed if previous keyword + is a declaration keyword or subprogram keyword? Bad. + + B: After the last END of a procedure. Tends to line up with a + declaration statement of the ENDed procedure instead of with the + PROCEDURE line itself. + Fix: ? + + C: Labels are not dealt with. I could find no clean way to handle + them. + Fix: Correct behavior = ? Statement labels (those which start on + the left margin?) should be invisible, both in syntactic + processing (make "back to token" ignore them?) and in + determining the indentation of the line they're on. Case + labels should probably have an indent after them if there is + no text after them, but they should be ignored if there is + text on the same line as them. + +2. Mark Procedure should be able to handle nested procedures. + +3. Match Block Word should be replaced by a bit per keyword saying + whether to match that keyword. This is for two reasons: 1) many + people like to match RECORD but few like to match BEGIN and 2) this + will allow people to indent like this if they want: + IF c1 THEN WHILE c2 DO + s; + +4. The insert commands should be hacked to be one command parsing a + general schema string. Maybe add a bit to the symbol table + corresponding to " on Same Line" to help out. + +5. The language dependancies of Mark Procedure should be eliminated. + +6. Make backward searches for keywords again use "S" instead of + checking each word found against the syntax table. This way is too + inefficient. + +7. Takes too long to check for user customization variables. + +8. There should be some indentation after := . + +9. C-M-A goes to beginning of function. diff --git a/src/emacs/pascal.defn b/src/emacs/pascal.defn new file mode 100755 index 0000000000000000000000000000000000000000..b0ec9e995b007623b221ec21058044682d67d2ad GIT binary patch literal 2632 zcmZuz!FJ*>4D7kTz=hKudHV|{7|3ggvyKDw^<;v9yh5BhPN9Flrn`|-nX^W+G@6WZ znOuI2SMptri&h@hnCx~&eU8`CRC0G#qqp_*$7J&HHks(>)p|RY^?sa@JR-?|=!m4V z(eKf}pee1aahykjq&;M(j#Yq27AygP)@KzInKG78U!^=rgPw_&g!eVdQyBF@-QrZ1 z_nG^uZFH*;U>4^Y%V`f%`hWnG&3*8U=c6ov`PHq#x!bJIJ;-zL_M9aCi&u5i;c%R0#QyB~Io$hQn8!NC|p0|6dnkx4@w&)aW1C>a_`}Q zwL8Ew=OB1=2Y}g)YJ0tHjAUv;Z+ z+jJX4*G^^11lGJ2l!M3*8jF0+K;$lHO>meTKyZJU9OxmX@b(ijrC>G}jm>Puz{_fl8BQ4xtoJyR%^)*4 znf_u1qvioxvWFaTd{8!NDjRqmM>jkUAY~!O5zZs8MBjX*i7r z$Hu5eNlXK$8706(Xx9U?wkL%*FP0Rx9_-w2+ + +!& Set Major Variable:! !S Set up one of the controlling vars to set +dependant vars. Var name is first string arg. Match val is second. +Comment is third.! + + [v :iv [n :in + + -99@fo..qv[1 !* Get current value! + + !* Make var to automatically set dependant values on change! + m.cv! *_(Pascal)_! + ,n m(m.m&_Set_Group_Value) + + Q1+99"N Q1Uv' !* Make sure old value works.! +  + + +!& Set minor variable:! !S Set up a normal var of name "Indent After x" +to automatically set pascal keyword var x. Number Arg is index in table.! + + [v Q:Pascal_Syntax_Table() Uv + [n +1:\Un + + -99@fo..qIndent_After_v[1 !* Get current value! + + !* Make var to automatically set dependant values on change! + m.cIndent_After_v! *_(Pascal)_Indent_after_keyword_v! + U:Pascal_Syntax_Table(n) + + Q1+99"N Q1UIndent_After_v' !* Make sure old value works.! +  + + +!& Setup variables:! !S Set up indentation vars to interface to syntax table! + + QPascal_Syntax_Table + + m.m&_Set_Major_Variable + + !* Set up "class" vars to change all relevant keyword locations! + MjIndent_After_Everything361.Default_indentation_after_all_keywords + MjBegin-Block_Body_Indentation21.Indentation_after_loop_constructs + MjIf-Block_Body_Indentation200.Indentation_after_IF-group_keywords + MjDecl_Body_Indentation100.Indentation_after_declaration_keywords + MjProcedure_Body_Indentation40.Indentation_for_Proc._&_Func._Bodies + + !* Set up for all keywords to be settable through "Indent After x"! + 0[1 m.m&_Set_Minor_Variable + QPascal_Keyword_Count < + Q:t(Q1*3+3)&361."N + Q1*3+1 mn' + %1> + + +!^R PASCAL New Line:! !^R Inserts CRLF, then indents the second line. +Any spaces before the inserted CRLF are deleted. +If there is a Fill Prefix it is used to indent, else calls +^R Indent Pascal Stmt.! + -@F_ K !* Leave no spacing at end of line.! + .[w !* save point ! + @MM !* Run this user's CR definition.! + 0@fo..q Fill_Prefix[3 !* If a prefix is defined, insert it.! + fq3"g g3' + "# F@M(M.M^R_Indent_Pascal_Stmt)[x[y' !* Else indent.! + fs rgetty"e :0t' + Qy,Qwf*0:,. !* min(Qw,Qy) is start of changed area ! + +!^R Find Unmatched Paren:! !^R Show unmatched paren if there is one. +Argument is number of seconds to stay at paren, if no argument stays there +permanently.! + + .[.0 fnQ.0 J !* On exit, go back.! + + 1:<-M(M.M&_Back_Paren)>"N + :i*NUP No_Unmatched_ParenthesesFSErr ' + + ff&1"N 0@V : ' !* Sleep if argument ! +  + +!^R Insert Comment:! !^R Insert comment here (don't move to do so). +Can be undone with UNDO.! + + !* Get comment Begin in Qb! + qComment_Start + qb"e :ib;' !* Use ; as default comment starter.! + 0fo..qComment_Beginf"N ub' + + !* Get comment End in Qe ! + :i*fo..qComment_End + + .,.m(m.m&_Save_For_Undo)Insert_Comment + + .(Gb Ge),.(FKC) + +!^R Indent PASCAL Stmt:! !^R Indentation for Pascal code. +If numeric arg, calls ^R Indent Nested, + Else if not at left margin, calls ^R PASCAL Indent Relative. + Else parses back to calculate proper indentaion, except setting the + variable Indent Align Only to non-zero will make it always just + align (call ^R Indent Nested). + +Print Last Pascal Indenter will print the name of the indentation + function called.! + + ff"'E*(0@fo..QIndent_Align_Only"'E)"E !* ARG => do ^R Indent Nested. ! + :iLast_Pascal_Indenter^R_Indent_Nested + f@:m(m.m^R_Indent_Nested)' + + FSHPos"N @:m(m.m^R_Pascal_Indent_Relative)' + + @f_ K !* Delete current indentation! + m(m.m&_Indent_Line)[x[y !* Indent line! + fsrgetty"e + fsechodis Afsechodis !* force new line on non-displays ! + 0t' + + Qy,Qx + + +!& Forward To Chars:! !S forward skipping comments & quoted strings +up to character in string arg.! + + QComment_Start QComment_End FQs[l [1 :I1 + 0:Ge-32"E 1,FQe:GeUe' !* Get rid of starting space in com. end! + + ' !* Match "{"! + + !"! 1A-' "E C !"! :S' "E!"!:i*Unmatched_'FSERR' !' !* Match "'"! + + 1A-( "E .+2-Z:"G 2A-* "E !* Match "*)"! + 2C :S*) "E :i*Unmatched_(*FSERR' !''' + + QlF=s"E !* Match comment end! + QlC :Se"E :i*Unmatched_sFSERR'!' + + 1Af1; !* Otherwise, done if char found.! + !* Check this after so we can find ()! + !* while skipping (* *)! + + 1C !* If we got here, we found a bare ")"! + > +  + +!& Back to Chars:! !S back skipping comments & quoted strings, +TO any character are passed in the string argument (or beg. of file)! + + QComment_Start QComment_End FQe[l [1 :I1 + 0:Ge-32"E 1,FQe:GeUe' !* Get rid of starting space in com. end! + < !"!-@:f )}'e 1L .-B@; + + 0A-} "E R -:S{ "E :i*Unmatched_}FSERR' !' !* Match "}"! + !"! 0A-' "E R !"!-:S' "E!"!:i*Unmatched_'FSERR' !' !* Match "'"! + 0A-) "E .-2-B:"L -1A-* "E !* Match "*)"! + 2R -:S(* "E :i*Unmatched_*)FSERR' !''' + QlF=e"E !* Match comment end! + QlR -:Ss"E :i*Unmatched_eFSERR'!' + + 0Af1; !* Otherwise, done if char found.! + !* Check this after so we can find ()! + !* while skipping (* *)! + + 1R !* If we got here, we found a bare ")"! + > +  + +!& Back to Word:! !S Backward over comments to a letter.! + Mb ABCDEFGHIJKLMNIOPRSTUVWXYZabcdefghijklmnioprstuvwxyz +  + +!& Forward to Word:! !S Forwards over comments to a letter or string arg.! + Mf ABCDEFGHIJKLMNIOPRSTUVWXYZabcdefghijklmnioprstuvwxyz +  + +!& Back to Word Over Paren:! !S back to word + string arg, skipping parens +Assumes M-X Back To Word is in Q-Reg w and M-X & Back Paren is in Q-Reg p! + +[1 :I1 +< Mw)1 + .-B@; 0A-):@; + 0Mp !* Pass over paren group if there.! + > +  + +!& Back Pair:! !C Moves back over a class of matched objects (like THEN & ELSE) +in which the matching is subservient to the block matching. +Assumes it is already deep. (arg = -1 means starting behind an END). +If Qt is less than zero, we assume we are being called from & Indent Line! + + -1[l !* Start level at -1! + Qt:"L !* Don't do this if from & Indent Line! + !* (for efficiency)! + [.p + [1 [9 + QPascal_Syntax_Table + + + M.M&_Back_To_Chars + M.M&_Back_To_Word + M.MBack_Level + + .U.p fnQ.p J !* If ^G'ed, stay here.! + ' + + < mw .-B"E Ql"N :i*Unmatched_syntax_pairFSERR '' + + -FW F(X1)L + @:FOt1U9 !* Get pointer to syntax table in Q9! + Q9"G + Q:t(Q9+2)&2."N !* If a block closer, Skip block! + -1Mk' + "# + Q:t(Q9+2)&"N !* If a first, we're climbing out! + Ql+1 Ul' + "# Q:t(Q9+2)&"N !* if a last, then we're going deeper! + Ql-1 Ul '' + '' + QL@;> + + Qt:"L 0U..n' +  + +!Back Level:! !C Moves back to beyond a BEGIN/END block. +Assumes it is already deep. (arg = -1 means starting behind an END). +! +!* Seperate implementation of Back Level from Forward Level for efficiency + since it is called by the indenting routines! + + [.p ff&1"E0'"#'[l !* Arg defaults to 0! + [1 [9 + QPascal_Syntax_Table + + + M.m &_Back_To_Chars [b + M.M&_Back_To_Word + + .U.p fnQ.p J !* If ^G'ed, stay here.! + + < mw .-B"E Ql"N :i*Unmatched_block_statementFSERR '' + + -FW F(X1)L + @:FOt1U9 !* Get pointer to syntax table in Q9! + Q9"G + Q:t(Q9+2)&1."N !* If an opener, we're climbing out! + Ql+1 Ul' + "# Q:t(Q9+2)&2."N !* if a closer, then we're going deeper! + Ql-1 Ul '' + ' + QL@;> + + 0U..n + :F "L 0'"#Q9' !* IF INTERNAL, RETURN KEYWORD FOUND.! + + +!& Keyword Search:! !S Search for a keyword matching second arg. +First arg is direction of search. +Returns -1 for failing search, index of keyword found for success! + + [.p [d [e + [1 [9 + QPascal_Syntax_Table + + !* Parameterize direction of motion! + "L + M.m &_Back_To_Chars [b + M.M&_Back_To_Word + -1Ud + BUe' + "# + M.m &_Forward_To_Chars [f + M.M&_Forward_To_Word + 1Ud + ZUe' + + .U.p fnQ.p J !* If ^G'ed, stay here.! + + < mw .-Qe"E -1' + + QdFW F(X1)L + @:FOt1U9 !* Get pointer to syntax table in Q9! + Q9"G + Q:t(Q9+2)&@:; !* Exit when keyword match found! + ' + > + + 0U..n + Q9 + +!& Forward Paren:! !S Moves Forward over matching parens. Assumes it is +already deep. (arg = -1 means starting after a "(").! + + [.p [t ff"E0'"#'[l + M.M &_Forward_To_Chars + + .U.p fnQ.p J !* If ^G'ed, stay here.! + + < Mf() + .-B "E Ql; !* No parens found ok if at level zero! + :I*Bad_Paren_Matching@FSERR + ' + + 1A-("E !* if an opener,! + Ql-1 Ul ' !* then we're going deeper! + "#Ql+1 Ul ' !* else were climbing out. ! + C + Ql@;> + 0U..n  + +!& Back Paren:! !S Moves back over matching parens, not confused by comments. +Assumes it is already deep. (arg = -1 means starting behind a ")").! + + [.p [t ff"E0'"#'[l + M.m &_Back_To_Chars [b + + .U.p fnQ.p J !* If ^G'ed, stay here.! + + < Mb() + .-B"E Ql; !* No paren found => exit if at level 0! + :I*Bad_Paren_Matching@FSERR' !* Else, error.! + + 0A-)"E !* if an ender,! + Ql-1 Ul ' !* then we're going deeper! + "#Ql+1 Ul ' !* else were climbing out. ! + R + Ql@;> + 0U..n  + + +!Forward Level:! !C Moves Forward to beyond a BEGIN/END block. +Assumes it is already deep. (arg = -1 means starting after a BEGIN). +! + + + [.p ff&1"E0'"#'[l !* Arg defaults to 0! + [1 [9 + QPascal_Syntax_Table + + M.m &_Forward_To_Chars [f + M.M&_Forward_To_Word + + .U.p fnQ.p J !* If ^G'ed, stay here.! + + < mw .-Z"E Ql"N :i*Unmatched_block_statementFSERR '' + + FW F(X1)L + @:FOt1U9 !* Get pointer to syntax table in Q9! + Q9"G + Q:t(Q9+2)&1."N !* If an opener, we're going deeper! + Ql-1 Ul' + "# Q:t(Q9+2)&2."N !* if an ender, then we're climbing out! + Ql+1 Ul'' + ' + Ql@;> + 0U..n  + +!& Join Line:! !S Macro to move back to statement start when parse is at +a THEN or DO. Assumes all the state of & Indent Line and modifies it.! + Q9"G + Q:t(Q9+2)&1."N !* If it is begin, ignore it.! + Mw  !* Get the previous keyword to work with.! + 0A- "N + -FW F(X1) L + @:FOt1U9 + ' ' ' + Q9"G !* If we got a keyword, and! + Q:t(Q9+2)&400."N !* it is an ender for a control stmt, and! + -@f _L 0A- "N !* this is not first on the line! + -1,220.M(M.M&_Keyword_Search) !* Go back to starter of controler! + ' ' ' +  + +!& Indent Line:! !S Assumes it's starting at the start of a line with no +existing indentation.! + + [1 [9 [2 [i -1[x .[a [c [s :i* !* Save local registers! + z-.[3 fnZ-Q3J !* Set up to stay here if ^G'ed! + QPascal_Syntax_Table + M.M &_Back_to_Word_over_Paren !* Put some oft-called functions! + M.M &_Back_to_Word !* In Q-Regs for efficiency.! + M.M Back_Level + M.M &_Back_To_Chars + M.M &_Forward_To_Chars + M.M &_Back_Paren + + !************************************************************************! + !*** Check for unmatched paren ! + -10fU1 F[ V B !* Narrow search to 10 lines and ! + 1:<-1M(M.M&_Back_Paren)>"E !* see if any open parens. ! + :ilIndent_Unmatched_Open_Paren + C @f_ L !* Skip spaces after paren! + oDONE1 + ' + f] V Bw + + + !************************************************************************! + !*** Go back till we find the keyword with indent amount (or claim none) ! + 0:L Mo; + .-B"E' !* If at beg. of file, just don't indent! + + < + 0A-;"E -2Ux R ' !* Sem found => mark it with indent of -2! + + "# !* Here we are at a word! + -fw f(x1) L + @:FOt1U9 !* Get index of potential keywrd in reg 9! + + Q9"G !* If a keyword,! + + Qx+1"'E ( !* If no sem so far or ! + Q:t(Q9+2)&141."'N) "N !* this is subprog, decl, or beg keyword ! + Q:t(Q9+1)Ux !* then this is our keyword (unless it! + ' !* has a negative indent value)! + + Q:t(Q9+2)&1. "N !* If we found a block beginner,! + 0@fo..qMatch_Block_Word"N !* and Match Block Word ! + :illMatching_Block_Word + oDONE1'' !* then indent is to this hpos.! + + Q:t(Q9+2)&2."N !* If a block ender then! + FWL !* reparse it! + 0; !* And exit to "begin-end" matching! + ' + + ' !* Keyword found.! + '' !* at word! + + Qx; !* Exit if we got a keyword.! + + Mo ; !* Back to next interesting stuff! + + .-B@; (0A -  )@; !* Keep trying till at start of line! + > + +Qx"G :ilIndent_After_1 !* Remember indenter to tell our user.! + oDone0 !* Go and indent right away.! + ' + + !************************************************************************! + !*** Moving over blocks and re-indenting Ends ! + 1@fo..qReindent_ENDs"'NUs !* Lie and say END found, if no re-indent! + + <.-B@; + (0A -  )@; !* Exit if we are at start of line! + + -fw f(x1) L + @:FOt1U9 !* Get index of potential ender in reg 9! + Q9"G + Q:t(Q9+2)&2."N !* If a block ender then! + Qs"L !* if this is first end, we may re-indent! + 0f:Us w !* save start of line in reg s ! + ' + + -1Mk U9 !* But mostly, line w/ ind. is way back.! + + Qs"N !* if first end that took us over a line.! + (Qs-.)"G !* if we went back past end of line,! + :ill_(Back_Over_Block) !* say so to user! + + M(M.M &_Join_Line) !* Make sure we get to start of line from! + + .-Z(0@fo..qMatch_Block_Word"E !* if not indenting from word! + 0L @f _L' !* use this lines indentation ! + fsHPOS( !* otherwise just the current pos.! + QsJ + )M(M.M&_Indent) !* to indent END line,! + 0L .Ua !* & mark our changes as starting here! + :Ill_(Reindented_an_1) !* Mention we did this! + )+ZJ 0Us' + "# -1Us' !* If BEGIN END on same line, don't count! + + ' !* This was the first! + + ' !* Beginner found! + + "# Q:t(Q9+2)"G FWL 0;'' !* Else, if a keyword, reparse and quit! + + ' !* Was found! + + Mo  !* Back to next interesting stuff! + > + + + !************************************************************************! + !*** Check statement nesting.! + !*** E.g. if "while x do / if c then / y;" seen, indent frm line w/ while! + < .Us !* If stoped, indent will be frm this line! + Mo; + .-B@; + + 0A-;@; !* Sem. breaks any nesting.! + + -FW F(X1) L + @:FOt1U9 !* Get index of potential ender in reg 9! + Q9"'G; !* If not in keyword table, we're done! + + Q:t(Q9+2)&4."N + 10.,4.M(M.M&_Back_Pair) !* If ELSE, look back for THEN! + :ill_(Back_Over_THEN/ELSE) + ' + "# + Q:t(Q9+2)&10."N Qx+2"N !* If controled by a THEN with no SEM, ! + :ilIndent_For_ELSEl + oDONE0'' !* indent for ELSE (same as THEN line).! + + !* Otherwise, chain is only continued by DO or THEN on this line! + Q:t(Q9+2)&400.@; + ' + + -1,220.M(M.M&_Keyword_Search):; !* if DO or THEN found, back! + !* to (IF, WHILE, WITH, etc.)! + + > + QsJ !* Indent to prev. line! + + :ilIndent_After_Statementl !* No keyword found! + + + !DONE0! + M(M.M&_Join_Line) + + !************************************************************************! + !*** Actually do the indenting.! + + 0L @f_ L !* Get indent from this line.! + + !DONE1! !* We are at position to indent from.! + + Qx"L 0Ux' + + QlULast_Pascal_Indenter !* Last indenter to var where usr can see! + .ULast_Pascal_Indent_Pos + + FSHPosUi + m..n 0u..n !* Pop back to start pos! + 0,Qx+Qim(m.m&_XIndent) + + Qa,. + +!^R Toggle Indentation:! !^R Turn off or on PASCAL Mode indentation. +With no arg arg, toggles value of Indent Align Only; otherwise puts arg +in Indent Align Only.! +ff"E + 0@fo..qIndent_Align_Only"'E m.lIndent_Align_Only' + "#  M.LIndent_Align_OnlyW' 0 + + +!Mark Procedure:! !C Set point and mark around current procedure. + Doesn't work for nested procedure definitions. + Comment lines immediately before the procedure and blank lines after + it are considered part of the procedure.! + + + . !* Mark here for recovery.! + FN  !* If ^G'ed, stay here.! + 1,-M(M.mNo_Comment_Search)procedurefunction"E + :I*Not_In_A_ProcedureFS Err' + + M(M.m &_Back_To_Chars); !* Go back to previous proc's sem! + M(M.M &_Forward_To_Chars)î !* Give back any comment and! + @F_ î L !* blank lines! + -@F_ L + + .( + 1,M(M.m No_Comment_Search)begin + -1M(M.MForward_Level) !* Forward to END! + + !* Skip any comment on END (before and after the sem)! + M(M.M &_Forward_To_Chars);. C M(M.M &_Forward_To_Chars)î + @F_ î L !* And claim blank lines at end.! + -@F_ L . !* Make mark at start of line.! + )J +0U..n  + +!^R Insert Pascal End:! !^R Pascal End:! !^R Insert end and show matching block. +Indents under matching BEGIN, LOOP, CASE, RECORD, or REPEAT. +Displays buffer around matching block statement. + ARG = number of seconds to display there. +Inserts a comment after the "END;" to show what was ended. + If the "BEGIN" is commented, it copies down that comment, + otherwise it tries to construct one from the code. e.g. + "(* while not eoln *)" or "(* for i:= 1 to 7 *)". + If you don't like the comment you can kill it with ^R Kill Comment. + +If matching block is REPEAT, inserts UNTIL and puts point between UNTIL and ;.! + +!* Needs work. Should use symbol table. Comments are usually too verbose. + Should return to @V the bounds of the buffer which have been changed! + + .[.1 fnq.1j !* .1: Auto-restoring point. ! + [.2 + [.4 + 0[.3 + M.M &_Back_To_Chars + + -1M(M.MBack_Level) fwx.6 !* Find matching token and remember it ! + .U.2 !* Where word found, case display there ! + + .(0@fo..qMatch_Block_Word"E !* if not indenting from word ! + 0L @f _L' !* use this line's indentation ! + fshpos[.7)J + f~.6REPEAT + f~.6RECORD + f~.6LOOP + + !* Get the proper comment for the END. ! + s(*{î 0A-î"N !* If this line is commented ! + @f_ L + .(s}*) fkc-@f_ L),.X.3 !* use it's comment ! + '"# !* otherwise, we will get it from code. ! + Qr*Qd*Ql"N !* if didn't match REPEAT or RECORD ! + 1 + '' + + + q.1J + 0L.U.4 @f_ L !* Remember start of line, move past ind ! + Q.1-."G !* If there is something on the line, ! + Q.1J !* just go to old pos. ! + .,.m(m.m&_Save_For_Undo)Insert_Pascal_End !* But undo insertion ! + ' + "# !* otherwise ! + Q.4,.m(m.m&_Save_For_Undo)Insert_Pascal_End + 0L q.7m(m.m&_Indent) !* Go back and indent ! + ' + + Qr"E !* If repeat, ! + iUNTIL_;.-1u.1' !* need UNTIL and pt. before sem. ! + "#iEND; .u.1' !* otherwise point after sem. ! + + 0@fo..qAutomatic_Capitalization"E + -fwfc' + 1@fo..qInsert_Comments"'N & q.3"N + i_gComment_Beging.3gComment_End !* Insert comment ! + Qr"N .u.1'' !* Unless repeat, point after comment ! + + q.2j !* Back to match and... ! + 0 @v !* ...Display there. ! + FF"E1'"#'*30:w !* Wait ARG seconds or til input. ! +  !* Exit, restore point. ! + + +!Indent Pascal Region:! !C Indents each line from here to MARK. +On each line it: + moves past comments to first token, + kills indentation, + then calls ^R Indent Pascal Stmt. +Leaves mark and point around changed region. Can be fixed with M-X Undo. +At end, expands all abbreviations in the region if in capitalize mode. + +This subroutine is NOT recommended for any large blocks (over 20 lines) +of code, because there are a few special cases which are indented wrong, +(mainly between declarations and BEGIN) and it is very inefficient.! + + QComment_Start QComment_End FQs[l [1 :I1 + 0:Ge-32"E 1,FQe:GeUe' !* Get rid of starting space in com. end! + + :,.f[.2[.1 !* Order point and mark in .2 & .1 ! + Q.1,Q.2m(m.m&_Save_For_Undo)Pascal_Region_Indent + q.1j 0L (z-q.2)u.2 + << @F î _L !* Forward to real character. ! + (.-z-2); + 2f~s"E :se"Ez-Q.2J''"#-;'> !* Skiping over comments ! + .-(z-q.2); !* See if done. ! + 0l @f _K !* Kill indentation ! + m(m.m&_Indent_Line)w !* Indent that line. ! + l> + + (z-q.2)j + i + + !* insert blank line after end ! + 2r m(m.m&_Indent_Line)w !* do indent in case END on last line ! + 0l-2d k !* clean up ! + q.1 !* Set MARK at beginning of region. ! + z-q.2j !* Set point at end. ! + 0@FO..QPascal_Abbreviations_In_Use"N + m(m.mExpand_Abbrevs_In_Region)' !* Expand all abbreviations if in use ! + q.1,.  !* Display and exit. ! + +!Same Capitalization:! !C Standardizes capitalization of next word. +No argument => same as it is now; +Neg argument => all are lower case; +Zero argument => all are capitalized; +Pos argument => all are upper case.! + [1,[2 + Fwl !* forward past word.! + ff"N [a !* if there is an arg then: ! + Qa"L-fwfc' !* neg arg => replace with lower case.! + Qa"E-m(m.m^R_Uppercase_Initial)'!* zero arg => replace with capitalized! + Qa"G-fw@fc' !* pos => replace with upper case.! + ' + -fwx1 + 0[Case_Replace + 1MMReplace_String11 + + +!^R Pascal Indent Relative:! !^R Pascal Indent Relative to last line's words. +Successive calls get successive indentation points; each call + aligns under ARGth next word in previous line. +Words are separated by spaces, tabs, semicolons, commas, periods. +To facilitate moving into a line, and changing an indentation, if + there is whitespace to the right and left (i.e. this is a new + indent call here), then the cursor is moved one column left.! + qLast_Pascal_Indenter[.1 + :iLast_Pascal_Indenter.1(^R_Pascal_Indent_Relative) + [1 @:I1| qLast_Pascal_Indenteru.1 !* 1: Macro for weird tabbing. ! + :iLast_Pascal_Indenter.1(^R_Tab_to_Tab_Stop) !* ... ! + :M(M.M^R_Tab_to_Tab_Stop)| !* ... ! + 0,1af_ +1"G !* If char to right is whitespace, ! + 0,0af_ +1"G !* ...as is char to left, ! + fs hpos-1( !* ...then note column left, ! + -d)m(m.m&_Indent)w !* ...and move there. ! + '' !* ... ! + .[P FSHPOS[0 !* P: orig point. 0: hpos. ! + @-f_ L !* Move left past whitespace. ! + .[Q FSHPOS[2 !* Q: point left of white. 2: hpos. ! + 0L< B-."E QPJ :m1' !* No line for relative indenting ! + !* so just tab stop. ! + -L1A-15."N0;'> !* Back to non-blank line. ! + Q0"G1:<0,Q0+1:FM>"ER' !* Cursor to orig hpos. ! + "#0L1:<0,Q2+1:FM>"ER' !* ... ! + "#QPJ:M1''' !* ... ! + + < :@F.,; _îL !* Find end of word. ! + @F.,; _L !* Right to start next word. ! + > !* Go to start of ARGth next word. ! + + FSHPOS(QQ,QPK !* Back to orig point, remove white left. ! + ):M(M.M&_Indent) !* And indent to column found. ! + + +!Global Pascal Comment:! !C Recursive edit for large block of comments. +Comment column is set to Global Comment Column, auto fill mode is turned on, +^R Indent for Comment is called initially, Comment Start is set to "(**" to +mark global comments (read "(*" as Q$Comment Begin$). "(*** *)" (comment +with just a *) expand into (* * * *... *), aligned with any surrounding +global comments (for boxes), or if no surrounding comments expand into +Pascal Star Line Width wide. When the recursive edit returns, comment-ends +within each global comment will be vertically aligned.! + + QComment_Start QComment_End + 0@fo..qGlob_of_Comments_Flag"N !* Already in Global Comment. ! + f:@m(m.m^R_Indent_for_Comment)' !* So just do M-;. ! + 0m.vGlob_of_Comments_Flag + 1[Glob_of_Comments_Flagw !* Signal now in glob mode. ! + :i*Global_Comment[..j + 10fo..qGlobal_Comment_Column[Comment_Column + 0[Comment_Begin + :i*s*[Comment_Start !* So mark each global comment ! + !* for later comment-end aligning. ! + 1[Space_Indent_Flag + 1[Auto_Fill_Mode !* Temp. turn on auto-filling now. ! + @m(m.m^R_Indent_for_Comment) !* ... and then align it. ! +  w !* Enter ^R mode for globals. ! + 0f"N L' !* If inside a line, go to next line. ! + fs z-.f[ vzw !* Bounds around buffer before point. ! + m(m.m&_Align_Global_Pascal_Comment_Ends) !* Align those before point. +! + zj f]vz + .f[vb + m(m.m&_Align_Global_Pascal_Comment_Ends) !* Align after point. ! +  + +!& Align Global Pascal Comment Ends:! !S Ends within contiguous global comments. +Contiguous global comments are comments starting with "(**", on + successive lines. +Contiguous global comments have their comment-ends aligned vertically. +If a global comment consists soley of "*", i.e. it is "(*** *)", + then it will expand out into a "(* * * * * ...* *)" comment, aligned + with its contiguous global comments. If no contiguous comments, + expands out into (* * *...*), Pascal Star Line Width wide + and extending to left margin. +After alignment, "(**" is replaced by "(* ".! +!* We expect the old Comment Start in Qs and Comment End in Qe ! + [.1[.2[.3 + bj !* Go thru whole buffer. ! + < :ss*; fkc !* Find next contig global comment. ! + 0u.1 !* .1: Max width of gc within cgc. ! + 0u.2 !* .2: (** *) found flag. ! + 0u.3 !* .3: Count of gcs in this cgc. ! + .( < :fbs*; !* Next gc start in this cgc. ! + %.3w !* .3: Inc gc count. ! + .-1f_ !* Replace with regular comment start. ! + (1+FQe)f=*e"E 1u.2' !* .2: Found a (*** *) in this cgc. ! + :fbe; !* Find next gc end in this cgc. ! + fs hpos,q.1 f u.1 w !* .1: Max width of gc. ! + l > !* Next line, maybe end of cgc. ! + fs z-.f[ vzw !* Set virtual buffer to end with cgc. ! + )j !* Back to start of cgc. ! + q.3-1"E !* If just one gc in cgc, ! + q.2"N !* ...and if was (*** *), ! + -@f_ b k !* ...then kill indentation, ! + 51@fo..qPascal_Star_Line_Widthu.1'' !* ...and set width of star line. ! + Q.1-fQs U.1 !* .1: HPOS to put (* at. ! + Q.2"N !* If have a (* * * *...*) in * cgc, ! + (q.1-qComment_Column-1-FQs&1)+q.1u.1' !* .1: round up if need to, to ! + !* make the * * *) come out evenly. ! + < :se; !* Next gc end. ! + -(FQs+FQe+2)f=s_*e"E !* If comment was just a *, expand ! + FQe-1:F"G1'R !* ...into * * * * ... ! + q.1-(fs hpos)/2 !* ... ! + ' !* ... (to match 2r next). ! + "#FQe-1:F"G1'Rr q.1m(m.m&_Indent) f' !* Indent the comment end, tell ^R. ! + l > !* Next line, continue. ! + zj f] vzw !* Restore boundaries. ! + > !* Continue with next cgc. ! +  !* Done aligning. ! + +!^R Slurp Pascal to Char:! !^R Prev line back to CHAR moved to point, indented. +Non-comment text from previous line (back to CHAR typed) is moved + onto the current line, after indentation, and then ^R Indent + Pascal Stmt is called to re-align. Now that the prev line is + changed, things might look better. +Any comment on prev line is left there, and ^R Indent for Comment is + called on it to align it after the text is removed from before it.! + [.1[.2[.3[.4[.5[.6 + m.i fiu.5 !* .5: Get char to slurp to. ! + z-.u.1 !* .1: Original point. ! + 0l @f_  r !* Line begin, past indentation. ! + z-.u.6 !* .6: Where to put slurped stuff. ! + 0:l z-.u.3 !* .3: Prev lines end. ! + .m(m.m&_Back_Over_Pascal_Line_Comment)j !* Before any comment & white. ! + .u.2 !* .2: Point before any comment. ! + < .,(0l).:fb.5; > !* Search back ARG chars on this line. ! + .,q.2f( fx.4 ) f !* .4: Slurped chars. ! + z-.-q.3"N !* If was a comment, reindent it. ! + @m(m.m^R_Indent_for_Comment) f' !* ... ! + z-q.6j !* Back to where to put text. ! + .,(g.4 i ). f !* Get the slurped text. ! + 0l !* Back before text. ! + @m(m.m^R_Indent_Pascal_Stmt) f !* Now reindent slurped text line. ! + z-q.1j w 1  !* Exit, with orig point restored. ! + +!Print Last Pascal Indenter:! !S Explain the last indentation a bit. +Prints what variable or idea was significant in the indentation and shows +the point where the indentation was measured from for ARG seconds! + + qLast_Pascal_Indenter[.0 !* Put name in more usable qreg. ! + fs echo displayw !* Clear the... ! + Cfs echo displayw !* ... echo area. ! + @ft.0 !* Print name. ! + 0fs echo activew + + .[1 fn Q1J !* Set up to jump home on exit! + QLast_Pascal_Indent_PosJ 0@V !* Go to last indent pos and display! + FF&1"E 1 '"# '*30 : !* Wait a while or until user types.! + +  + +!Capitalize Pascal Keywords:! !C Turn on capitalization of keywords. +With 0 argument, turns off capitalization. +Uses the WORDAB library and a file of pre-defined abbreviations + which are the keywords. +This command is currently not recommended because: 1) it expands common +words like "in" or "if" in comments, which is a hassle; and 2) it intertwines +all the capitalization abbreviations with your own abbreviations! + + [.1 + ff"E 1u.1'"# u.1' + q.1"E 0@FO..QPascal_Abbreviations_In_Use"N + 0m(m.mWord_Abbrev_Mode) !* Turn off wordab library ! + 0m.vPascal_Abbreviations_In_Use + 0m.lAutomatic_Capitalization'' + + 0@FO..QPascal_Abbreviations_In_Use"N ' !* already loaded ! + 1m.vPascal_Abbreviations_In_Use !* say in use ! + 1m.lAutomatic_Capitalization + 0@fo..qWORDAB_Loaded"E !* prevent multiple loading ! + m(m.mLoad_Library)WORDAB !* load it ! + 1m.vWORDAB_Loaded' + m(m.mWord_Abbrev_Mode) !* start it ! + m(m.mRead_Word_Abbrev_File)EMACS;PASCAL_DEFN !* get the abbrevs ! +  !* Done ! + +!Pascal Expand Abbrevs in Region:! !S Expand WORDAB abbreviations in region, +but comments and strings are skipped. Assumes WORDAB already loaded.! + + .,: f : [a J .-Z[z !* Put pt, Qz at end of reg, Qa at strt! + + M.m &_Back_To_Chars [b + M.m &_Back_To_Word [w !* Commands in regs for efficiency.! + M.m^R_Abbrev_Expand_Only [e + + 0A"C -FWL FWL' !* Move out of first word.! + < Mw !* Get the next word! + Qa-.; !* done if past start! + -fw(Me):J !* else expand and move back over wd.! + > + + QaJ Qz+Z !* Restore region.! + .,:  !* Return changes! + + +!^R Start String:! !^R Set up a string of length , and overwrite. +Supply length of desired string as numeric argument (default is 10). +The string is created, full of spaces. +You enter a recursive editing level in overwrite mode +with point at the beginning of the string. +Overwrite the contents, then use ^R Exit to turn +off overwrite mode and move past the string.! + + FF"E 10[0' "# !* Def arg of ten if no! + :"G10[0' "#[0'' !* or if zero or negative arg! + i'' R Q0,_i !* Insert the string filled with spaces! + Z-(.+1)[1 Q0R !* Mark the end and go back to the start! + 1[Overwrite_Mode !* Overwrite! + [..J :I..JFill_in_string_(Ovwrt)_ + .-1,.+Q0+1 !* Let user edit the string ! + Z-Q1J 0 + +!^R Plus One:! !^R Make Pascal assgt stmt to increment word before point. +Saves for undoing.! + + [a .( + <-FWL -@F_ L .-B@; !* Go back over words until out of full ! + (0A-. "'E) (0A-, "'E) + (0A - [ "'E) + (0A-] "'E) ;> !* variable ! + + @F_ L !* Back forwards over white space.! + + .[1)J + + Q1,.M(M.M &_Save_for_Undo)Plus_One + + M.VWord_Abbrev_Mode"N + m(m.m^R_Abbrev_Expand_Only)' + + .( -@f_ L + Q1,.Xa !* Put variable name in reg A ! + )J + I:=_ Ga I_+_1; Q1,. !* Fill out the rest of the stmt! + + +!& Pascal DO-OF:! !S Sets up a structured pascal WITH, WHILE, FOR, or CASE +statement. WITH, WHILE, FOR, or CASE is the first string argument; OF or +BEGIN is the second.! + + QPascal_Syntax_Table + [c:ic !* string arg (WHILEFORWITHCASE)! + [e :ie !* second string arg (OFBEGIN)! + [d :id !* only use DO for DO stmts ! + FF*[b !* arg >0 => no BEGIN; <0 => surround ! + Qb"L-Qb+1'"#.,.'M(M.M &_Save_for_Undo)Insert_c_Statement + .[p .[s !* save start of changes, cap area. ! + + .(0L @F_ L Qp-."L QpJ' fsHPos[1 )J + 0@fo..qe_on_same_line*(Qb:"'G)"E !* Don't count DO if BEGIN right after.! + 4@FOt DOf"LW0' +'Q1[2 + Qb:"G4@FOt ef"LW0'+'( !* Extra indent after BEGIN only if BEGIN! + )Q2 U3 + + fsHPos,Q1m(m.m&_XIndent) Ic_#d !* Insert start.! + Qb:"G !* If there is to be a BEGIN! + 0@fo..qe_on_same_line"Ei +!* if not begins on same line, ins ! + 0,(Q2)m(m.m&_XIndent)'"#i_' !* and indent! + Ie' !* and put in BEGIN! + Qb"L . + 0@fo..qAutomatic_Capitalization"E + Qs,.fc ' + -Qb+1:L + Q3-Q1m(m.m^R_Indent_Rigidly) + .(W)J !* Get rid of the mark we set! + .Us' + "# i + + 0,Q3m(m.m&_XIndent)' !* Indent block body line.! + Qb:"G + i + + 0,(Q2)m(m.m&_XIndent) + IEND; 1@fo..qInsert_Comments"Ni_gComment_BegingcgComment_End'' + 0@fo..qAutomatic_Capitalization"E + Qs,.fc' + .[r QpJ s# -D Qp,Qr-1 !* back to mark, find and kill marker.! + +!^R Pascal WHILE:! !^R Sets up a structured WHILE statement. +See PASCAL info node for customization information. +Positive argument means do not insert BEGIN and END; +negative arg means try to surround - lines within BEGIN/END.! + + Fm(m.m&_Pascal_DO-OF)WHILEBEGIN_DO  + +!^R Pascal FOR:! !^R Sets up a structured FOR statement. +See PASCAL info node for customization information. +Positive argument means do not insert BEGIN and END; +negative arg means try to surround - lines within BEGIN/END.! + + Fm(m.m&_Pascal_DO-OF)FORBEGIN_DO  + +!^R Pascal WITH:! !^R Sets up a structured WITH statement. +See PASCAL info node for customization information. +Positive argument means do not insert BEGIN and END; +negative arg means try to surround - lines within BEGIN/END.! + + Fm(m.m&_Pascal_DO-OF)WITHBEGIN_DO  + +!^R Pascal CASE:! !^R Sets up a structured CASE statement. +See PASCAL info node for customization information. +Negative arg means try to surround - lines within BEGIN/END; +you don't want a positive arg.! + + 1M.LOF_On_Same_Line + Fm(m.m&_Pascal_DO-OF)CASEOF  + +!^R Pascal IF:! !^R Sets up a structured IF statement. +See PASCAL info node for customization information. +Positive argument means do not insert BEGIN and END; +negative arg means try to surround - lines within BEGIN/END.! + + QPascal_Syntax_Table + FF*[b + .[p .[s + .(0L @F_ L Qp-."L QpJ' fsHPos[1 )J + Qb"L-Qb+1'"#.,.'M(M.M &_Save_for_Undo)Insert_IF_Statement + 1@fo..qTHEN_on_same_line"E4@FOt IFf"LW0'+'( + )Q1[2 + 0@fo..qBEGIN_on_same_line*(Qb:"'G)"E !* Don't count THEN if BEGIN right after.! + 4@FOt THENf"LW0'+'Q2[3 + Qb:"G4@FOt BEGINf"LW0'+'( !* Extra indent after BEGIN only if BEGIN! + )Q3 U4 + + IIF_# !* Insert IF start.! + 1@fo..qTHEN_on_same_line"Ei + !* if not THEN on same line, ins ! + 0,Q2m(m.m&_XIndent)'"#i_' !* and indent.! + ITHEN' !* put in then (and )! + Qb:"G !* If there is to be a BEGIN! + 0@fo..qBEGIN_on_same_line"Ei + !* if not BEGINs on same line, ins ! + 0,Q3m(m.m&_XIndent)'"#i_' + IBEGIN' !* put in BEGIN! + Qb"L . + 0@fo..qAutomatic_Capitalization"E + Qs,.fc ' + -Qb+1:L + Q4-Q1m(m.m^R_Indent_Rigidly) + .(W)J !* Get rid of the mark we set! + .Us' + "# i + + 0,Q4m(m.m&_XIndent)' !* Indent block body line.! + Qb:"G + i + + 0,Q3m(m.m&_XIndent) + IEND; 1@fo..qInsert_Comments"Ni_gComment_BeginiIFgComment_End'' + 0@fo..qAutomatic_Capitalization"E + Qs,.fc' + .[r QpJ s# -D Qp,Qr-1 !* back to mark, find and kill marker.! + +!^R Pascal ELSE:! !^R Set up matching ELSE statement. +Positive argument means do not insert BEGIN/END; negative arg +means enclose - lines withing the BEGIN/END. +It will delete a ; if it finds one on the previous line.! + + + QPascal_Syntax_Table + FF*[b !* numeric argument <=> no BEGIN/END! + + .[s + Qb"L .(-Qb+1L .[y)J'"#.[y' + + +!** Check for sem on previous line ! + + M.m &_Back_To_Chars [b + M(M.m&_Back_to_Word); ]b + + (0A-;)"E !* ";" found on previous line ! + .-1,QyM(M.M &_Save_for_Undo)Insert_ELSE_Statement + -D (Qs-1)Us' + "#.,QyM(M.M &_Save_for_Undo)Insert_ELSE_Statement' + +!* Move point back where it started, but leave QS pointing at end of prev. line.! + .(QsJ)Us +Qs[x + + 0@FO..Q ELSE_On_Same_Line"N QSJ I_' + + .[p + .(0L @F_ L Qp-."L QpJ' fsHPOS[1 )J + 0@fo..qBEGIN_on_same_line*(Qb:"'G)"E !* Don't count ELSE if BEGIN right after.! + 4@FOt ELSEf"LW0' +'Q1[2 + Qb:"G4@FOt BEGINf"LW0'+'( !* Extra indent after BEGIN only if BEGIN! + )Q2 U3 + + IELSE !* Insert ELSE.! + Qb:"G !* If there is to be a BEGIN then ! + 0@fo..qBEGIN_on_same_line"Ei + !* if not BEGINs on same line, ins ! + 0,Q2m(m.m&_XIndent)'"#i_' !* and indent! + IBEGIN' !* and put in BEGIN.! + Qb"L . + 0@fo..qAutomatic_Capitalization"E + Qs,.fc ' + -Qb+1:L + Q3-Q1m(m.m^R_Indent_Rigidly) + .(W)J !* Get rid of the mark we set! + .Us' + "# i + + 0,Q3m(m.m&_XIndent)' !* Indent block body line.! + i# + Qb:"G + i + + 0,Q2m(m.m&_XIndent) + IEND; 1@fo..qInsert_Comments"Ni_gComment_BeginiELSEgComment_End'' + 0@fo..qAutomatic_Capitalization"E + Qs,.fc' + + .[r QxJ + s# -D + Qx,Qr-1 !* back to mark, find and kill marker.! + +!& Pascal Subroutine:! !S Sets up a PROCEDURE or FUNCTION block.! + + QPascal_Syntax_Table + 0@fo..qAutomatic_Capitalization + [n [c [d [s [g + FF[b !* any num means no VAR! + + :ic !* PROC. or FUN. in reg c ! + :is !* ";" or "." in reg s ! + + .Up + .,.M(M.M &_Save_for_Undo)insert_c_block + + .(0L @F_ L Qp-."L QpJ' FSHPos[1 )J !* get HPos at start of line ! + Q1 + (4@fo..qc_Body_Indentationf"LW0')[2 + Q2 + (4@FOt BEGINf"LW0')[3 + Q1 + (4@FOt cf"LW0')[4 + + gc i_ Qa"E -fwfc' !* Insert and maybe capit. PROC. or FUN. ! + + !* Allow user to type in subroutine name and arguments ! + :i*Type_c_Name_and_Arguments_(End_with_C-M-Z)[..J + .Ug ZUd  ]..J + + Qgj @f_ î L FWXn !* Skip blanks to name and grab it. ! + Qg+Z-QdJ + 0A-;"N I;' !* Insert a sem if the user didn't! + + iî iî  !* Insert two CRLF's ! + + Qb"E + 0,Q4m(m.m&_XIndent) + IVAR_# + î  ' Qa"E -fwfc' + 0,Q2m(m.m&_XIndent) + IBEGIN Qa"E -fwfc' + 1@fo..qInsert_Comments"Ni_gComment_BegingngComment_End' I + + 0,Q3m(m.m&_XIndent) + Qb:"E I#' I + + 0,Q2m(m.m&_XIndent) + IEND gs Qa"E -fwfc' + 1@fo..qInsert_Comments"Ni_gComment_BegingngComment_End' + .[r QpJ s# -D Qp,Qr-1 + +!^R Pascal FUNCTION:! !^R Inserts a FUNCTION block. +See Pascal info node for customization information. +Sets up a VAR statement, unless it is given an argument. +Indents to this lines indentation.! + F@m(m.m&_Pascal_Subroutine)FUNCTION; + +!^R Pascal PROCEDURE:! !^R Inserts a PROCEDURE block. +See Pascal info node for customization information. +Sets up a VAR statement, unless it is given an argument. +Indents to this lines indentation.! + F@m(m.m&_Pascal_Subroutine)PROCEDURE; + + +!^R Pascal PROGRAM:! !^R Inserts a PROGRAM block. +See Pascal info node for customization information. +Sets up a VAR statement, unless it is given an argument. +Indents to this lines indentation.! + F@m(m.m&_Pascal_Subroutine)PROGRAM. + + +!^R Pascal REPEAT:! !^R Inserts a REPEAT statement. +See PASCAL info node for customization information. +If given a negative argument, surrounds - lines within the +REPEAT/UNTIL! +fm(m.m&_Pascal_REPEAT-RECORD)REPEATUNTIL  + + +!^R Pascal RECORD:! !^R Inserts a RECORD with the END. +See PASCAL info node for customization information. +If given a negative argument, surrounds - lines within the +RECORD/END! +fm(m.m&_Pascal_REPEAT-RECORD)RECORDEND  + + +!& Pascal REPEAT-RECORD:! !S Powerhorse for REPEAT & RECORD! + QPascal_Syntax_Table + FF*[b + [c :ic !* REPEAT or RECORD ! + [d :id !* UNTIL or END ! + Qb"L-Qb+1'"#.,.'M(M.M &_Save_for_Undo)Insert_c_Statement + .[p .[s + + .(0@fo..qMatch_Block_Word"E + 0L @F_ L Qp-."L QpJ'' + fsHPos[1)J + Q1+(4@FOt cf"LW0')[2 + + Ic !* Insert start.! + Qb"L . + 0@fo..qAutomatic_Capitalization"E + Qs,.fc ' + -Qb+1:L + Q2-Q1m(m.m^R_Indent_Rigidly) + .(W)J !* Get rid of the mark we set! + .Us' + "# i + + 0,Q2m(m.m&_XIndent)' !* Indent block body line.! + i + + 0,Q1m(m.m&_XIndent) Id#; + 0@fo..qAutomatic_Capitalization"E + Qs,.fc' + .[r QpJ s# -D Qp,Qr-1 + + +!^R Pascal BEGIN:! !^R Writes a BEGIN/END pair +If given a negative argument, encloses - lines between the +BEGIN/END.! + + QPascal_Syntax_Table + FF*[b + Qb"L-Qb+1'"#.,.'M(M.M &_Save_for_Undo)Insert_BEGIN/END + .[p .[s + + .(0@fo..qMatch_Block_Word"E + 0L @F_ L Qp-."L QpJ'' + fsHPos[1)J + + Q1+(4@FOt BEGINf"LW0')[2 + + IBEGIN !* Insert start.! + Qb"L . + 0@fo..qAutomatic_Capitalization"E + Qs,.fc ' + -Qb+1:L + Q2-Q1m(m.m^R_Indent_Rigidly) + .(W)J !* Get rid of the mark we set! + .Us' + "# i + + 0,Q2m(m.m&_XIndent)' !* Indent block body line.! + i# + + 0,Q1m(m.m&_XIndent) IEND; + 0@fo..qAutomatic_Capitalization"E + Qs,.fc' + .[r QpJ s# -D Qp,Qr-1 + + +!No Comment Search:! !C Searches for it's string argument, ignoring comments. +With a pre-comma argument, only finds delimited words. +Reverse searching works, except in languages which end comments with CRLF! + + + FF-1[2 1,F Search_String:_[1 + "'L*2+1[3 !* Q3 is -1 for arg < 0; 1 for arg >= 0! + 0FO..qComment_Start !* Get comment start or ";"! + FQs:"G :I*;Us' + 0FO..qComment_End !* If comment end not defined, find CRLF! + FQe:"G :I*î Ue' + Q3"L Qe(QsUe)Us' !* If backward, interchange roles of s & e! + + .[.1 fnq.1j !* .1: Auto-restoring point. ! + + + 0U..n + -1 + +!ErrList Mode:! !S Turns on and off ErrList mode. +With no argument, toggles ErrList Mode. Otherwise sets ErrList Mode to arg.! + + ff"E QErrList_Mode"'E '"# ' UErrList_Mode +  + +!& In ErrList Mode:! !S Turns on ErrList mode.! + + + [1 [5 + 0FO..qCompiler_FilenameUOld_Compiler_Filename + + 0FO..q After_Compilation_HookU1 !* Save old compilation hook! + Q1UOld_After_Compilation_Hook + Q1"E :i1' + @:iAfter_Compilation_Hook|1M(M.M&_Compile_Compilation) + 0U..H -1FSPJATY | + + 0FO..QCompiler_SwitchesU1 + Q1UOld_Compiler_Switches + Q1"E :i1' + +!* :i*1/LISTUCompiler_Switches ! + + 0FO..QCompile_CommandU1 + Q1UOld_Compile_Command + Q1"E :i1 !* If no compile command and! + 0FO..qCompiler_Filename"G !* using EXECUTE command, no "/LIST",! + QPascal_Compile_CommandUCompile_Command !* so use special hack.! + !* :iCompiler_FilenameSYS:PAS.EXE! + ' ' +  + +!& Out ErrList Mode:! !S Turns off ErrList Mode! + + QOld_After_Compilation_HookUAfter_Compilation_Hook + QOld_Compiler_SwitchesUCompiler_Switches + QOld_Compile_CommandUCompile_Command + QOld_Compiler_FilenameUCompiler_Filename +  + +!& Compile Compilation:! !S To be run as the After Compilation Hook! + [0 + QBuffer_NameU0 + [Previous_Buffer + m(m.mSelect_Buffer) *LST* + + Q1FS DFILE + F6LST FS D FN2 + 0 FS DVERS + 1:< -1m(m.mVisit_File) + @ED + >"N :i*No_Listing_Fileî FG' + m(m.m&_Save_Errors) + + Q0m(m.mSelect_Buffer) + + +!& Save Errors:! !S Gather all errors in an array.! +!* Currently this has a kludgey dependance on the format of the listing! +!* file. It will have to be changed for every compiler.! + + [a [b [e 1[p [l [c [m 0[i +100Ua +!* ZJ -Sdetected 0L @f_ L \Ua |* # of errors in Qa| +! !* # of errors no longer revealed! + + (Qa*4)*5 FS Q VectorUb !* Now Qb is buffer long enough! + + J + < + 0Ue + <:s + __*****î ; !* Find error or page marker, or give up ! + + 0A- "E sPage_ \Up' !* if a page marker, get the #, go on! + "# -1Ue 0;'> + + Qe; !* No more errors - exit! + + -1L @f_ L \Ul !* Get line #! + + 1L 8C + + .Um 0Uc + < !* Loop over all errors for this line! + + @f*_ f(+QcUc)L !* Get position in line! + 1A-^:@; !* Quit if "^" pointer not found! + + Qp U:b(Qi*4) !* Save page in first word! + Ql U:b(Qi*4 +1) !* Line # in 2nd! + Qc U:b(Qi*4 +2) !* Character pos in 3rd! + + .(QmJ + 1L 14C 1X:b(Qi*4 +3) !* Get error message.! + .Um)J + + %iW !* Increment error counter ! + + 1C %cW !* Move past "^"! + > + > + Qb UError_Vector + Qi UNum_Errors + 0 UError_Index + + + +!^R Next Error:! !^R Hop to the next compilation error.! + [i [m [0 QError_Vector + + QError_Index - QNum_Error"G 0UError_Index' !* Cycle on the errors! + + %Error_IndexUi + Qi - QNum_Error - 1"E @FT(No_More_Errors)î ' + "# + + J + Q:b(Qi-1*4)-1 S  !* Go to page,! + Q:b(Qi-1*4+1)-1 L !* then line,! + Q:b(Qi-1*4+2) C !* then column.! + Q:b(Qi-1*4+3) Um + @FTm !* Type error message! + ' + 1fsechoactive + + +!& Read Keywords:! !S Read the keywords file into the syntax table. +Looks for file on EMACS: called PASCAL.SYN! + + + QBuffer_NameU0 + [Previous_Buffer !* Make sure we don't mess up Prev. Buffer! + m(m.mSelect_Buffer) *SYN* + + 1:< m(m.mVisit_File)emacs:pascal.syn !* Get the syntax file! + >"N :i*No_Pascal_Syntax_Fileî FG' + + !* Create Symbol Table! + H m(m.m &_Count_Lines) m.v Pascal_Keyword_Count !* # of keywords.! + QPascal_Keyword_Count*3+1*5 FS Q Vector !* Qs is buffer just big enuf! + 3 U:s(0) !* Initialize entry length.! + Qs M.V Pascal_Syntax_Table !* Copy buffer ptr. to global var.! + + + !* Get the entries! + 0 [x + <@f_ î L !* Skip all blanks and null lines.! + .-Z; !* Exit if at end of file! + + FW X:s(%x) FWL !* Get the name as 1st table entry! + + @f_ L !* Skip blanks! + :\ U:s(%x) !* Second entry! + + @f_ L !* Skip blanks! + 2f[IBase:\ U:s(%x)F]IBase !* Third entry in binary! + 1L + > + + Q0m(m.mSelect_Buffer) !* Get back home and erase our tracks! + m(m.mKill_Buffer) *SYN* + + + \ No newline at end of file