!* -*-TECO-*- This library implements EMACS BLISS MODE. It is a version of ECC's PL1 mode, modified by T.Hess [HESS@DEC]! !~FILENAME~:! !Package for editing BLISS code (BLISS Mode).! BLISS !BLISS Mode:! !C Set up for editing BLISS code. $BLSIND ..D$ is set up for use by BLISS indent macros. (E.g. no special Lisp characters, and "words" are $, _, and alphanumerics. Calls user-providable macro, INIT BLISS MODE, which can put BLISS macros into desired qregs. If no macro exits, calls & Default Init BLISS Mode. See the description of that subr for more details on what it does and how to construct your own.! m(m.m_&_Init_Buffer_Locals) !* Discard locals of old mode, and set: *! !* .Q: Make Local Q-Reg. *! !* .0: old value of $Switch Mode Process *! !* Options$ *! !* Standard stuff to set up: *! 1,:i*!m.LComment_Startw !* Comment start... *! 1,:i*!_m.LComment_Beginw !* begin... *! 1,:I*_)%M.LImbedded_Comment_Endw 1,:I*%(_M.LImbedded_Comment_Beginw 1,m.m&_Indent_Without_Tabsm.LMM_&_Indentw !* Dont use tabs. *! 1,m.m&_XIndent_Without_Tabsm.LMM_&_XIndentw !* ... *! 1,40m.lComment_Columnw !* Set comment column. *! !* Now either personal or standard init: *! !* They can use .0, .Q. *! 1:"N !* User has no init, *! m(m.m&_Default_Init_BLISS_Mode)' !* ...so use default one. *! m.vBLSIND_..Dw !* Create a var for our dispatch table. *! ^:IBLSIND_..D\________________________________________________________________________________________________________________________________________A_____________________________A____A____A___AA___AA____A____'____(____)____A____A_________A____A____/___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA____A____;____A____A____A____A____A___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA____A____A____A____A___AA____A___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA___AA____A____|____A____A________\ !* And setup the dispatch table. *! q.0,1M(M.M&_Set_Mode_Line)BLISS !* Set, display mode. *!  !^R Indent BLISS Stmt:! !^R Indentation via previous statement type. If there are non-label tokens to left, then just calls ^R BLISS Indent Relative. If non-null arg, calls ^R Indent Nested. Calls macros to indent statements after prev stmt's type, e.g. looks for macro named & BLISS Indent After Begin Stmt. None found, it will call & BLISS Indent After Stmt. If the previous statement was unfinished (i.e. we are not indenting a new statement really), then macros such as & BLISS Indent Unfin If Stmt are called. The default unfinished-stmt macro is & BLISS Indent Unfin Stmt. ^R Print Last BLISS Indenter will print the name of the indentation macro called.! !* The name of the indentation macro called is left in $Last BLISS Indenter$. * All indent macros are passes two args: * ARG1 points to where to indent (previous indentation has been * deleted). * ARG2 points to the prev stmt's token, so that the macro can see * its indentation or whatever it wants. * Indent macros should return two values, bounding the buffer area changed. *! [.2[.3[.4[.5 :i*m.vLast_BLISS_Indenterw !* Start null. Generally, subrs *! !* will put their own names in here. *! .m(m.m&_BLISS_Tokens_to_Leftp)"N !* Toks left => ^R BLISS Indent Relative. *! f^m(m.m^R_BLISS_Indent_Relative) ' ff"G !* ARG => goto ^R Indent Nested. *! :iLast_BLISS_Indenter(^R_Indent_Nested) !* We must put *! !* name in since macro is EMACS *! !* and doesnt like us... *! f^m(m.m^R_Indent_Nested) ' .[.1 !* .1: where indent. *! 1m(m.m&_BLISS_End_Prev_Stmt)u.2 !* .2: end of prev statement. *! q.2j m(m.m&_BLISS_Next_Token)u.5u.3 !* .3: before token after prev stmt. *! !* .5: after token... *! q.5-."L q.2j m(m.m&_BLISS_Next_Token)u.5u.3 ' !* Don't pass . *! q.3-q.1"L !* Prev stmt unfinished. *! q.3,q.5x.4 !* .4: string of token. *! 1:< q.1,q.3m(m.m&_BLISS_Indent_Unfin_.4_Stmt)u.3u.1 >"N !* No indent macro found. *! q.1,q.3m(m.m&_BLISS_Indent_Unfin_Stmt)u.3u.1 !* Default. *! '' "# !* Prev stmt finished. *! .-b"N !* There is a prev stmt. *! 1m(m.m&_BLISS_End_Prev_Stmt)u.2 !* Go back another stmt. *! q.2j m(m.m&_BLISS_Next_Token)u.5u.3 !* Get token, which is *! !* the token for the statement just *! !* before where we want to indent. *! q.5-."L q.2j m(m.m&_BLISS_Next_Token)u.5u.3 ' !* Don't pass . *! q.3,q.5x.4' !* String of token. *! "# !* No prev stmt in buffer. *! bu.3bu.5 :i.4' 1:< q.1,q.3m(m.m&_BLISS_Indent_After_.4_Stmt)u.3u.1 !* Try ind. *! >"N !* No macro for indenting that. *! q.1,q.3m(m.m&_BLISS_Indent_After_Stmt)u.3u.1 !* Default. *! '' fsrgetty"n q.1,q.3 ' !* .1,.3 bound buffer area changed. *! fsechodis Afsechodis 0t  !^R Insert BLISS End:! !^R Insert end and show matching block. Indents if no whitespace to left of cursor. Displays buffer around matching block statement. ARG = number of seconds to display there. Inserts a comment after the "end;" to show what was ended, e.g. "%( begin-name )%" or "%( if-begin )%". The MARK is left before this comment, point after so you can easily delete it with ^R Kill Region.! 0,0a-_"N 0,0a- "N !* If no whitespace to left... *! ^m(m.m^R_Indent_BLISS_Stmt)f'' !* ...then indent... *! .,(iEND;).f !* ...and insert end. *! .: !* Leave MARK after end;. *! .[.1 fnq.1j !* .1: Auto-restoring point. *! 4r .[.6 !* .: Start before END. *! [.2 [.4[.5 0[.3 !* .3: BEGIN level counter. *! < b-."E FG 1' !* Go back statements until find *! !* a matching BEGIN *! 1m(m.m&_BLISS_End_Prev_Stmt)j !* Back 1 semicolon... *! m(m.m&_BLISS_Next_Token)u.5u.4 !* ... and fwd to prev stmt token. *! q.4u.2 !* .2: Save beginning of this stmt. *! q.4,q.5f~IF"E !* IF: check last sub-stmt. *! q.4m(m.m&_BLISS_Last_Sub-Stmt_Token)u.5u.4' q.4,q.5f~ELSE"E !* ELSE: check last sub-stmt. *! q.4m(m.m&_BLISS_Last_Sub-Stmt_Token)u.5u.4' q.4,q.5f~INCR"E !* INCR: check last sub-stmt. *! q.4m(m.m&_BLISS_Last_Sub-Stmt_Token)u.5u.4' q.4,q.5f~DECR"E !* DECR: check last sub-stmt. *! q.4m(m.m&_BLISS_Last_Sub-Stmt_Token)u.5u.4' q.4,q.5f~DO"E !* DO: check last sub-stmt. *! q.4m(m.m&_BLISS_Last_Sub-Stmt_Token)u.5u.4' q.4,q.5f~WHILE"E !* WHILE: check last sub-stmt. *! q.4m(m.m&_BLISS_Last_Sub-Stmt_Token)u.5u.4' q.4,q.5f~UNTIL"E !* UNTIL: check last sub-stmt. *! q.4m(m.m&_BLISS_Last_Sub-Stmt_Token)u.5u.4' q.4,q.5f~GLOBAL"E !* GLOBAL: check next token *! q.5+1m(m.m&_BLISS_Next_Token)u.5u.4 !* Might be ROUTINE *! q.4u.2' !* Pretend "GLOBAL" doesn't exist *! q.4,q.5f~ROUTINE"E !* ROUTINE: check first stmt after = *! q.5m(m.m&_BLISS_Skip_Decl)"E 1;' m(m.m&_BLISS_Next_Token)u.5u.4' q.4,q.5f~MODULE"E !* MODULE: check first stmt after = *! q.5m(m.m&_BLISS_Skip_Decl)"E 1;' m(m.m&_BLISS_Next_Token)u.5u.4' q.4,q.5f~MACRO"E !* MACRO: check first stmt after = *! q.5m(m.m&_BLISS_Skip_Decl)"E 1;' m(m.m&_BLISS_Next_Token)u.5u.4' q.4,q.5f~BEGIN"E q.3-1u.3' !* Dec level counter on BEGIN, *! q.4,q.5f~END"E q.4-q.6"N %.3w'' !* Inc on END (finds ARG2 first). *! q.3-1:; !* Found match when level = 0. *! > !* .4,.5: Bound BEGIN *! q.4,q.5x.4' !* .4 := BEGIN *! q.2j !* First token of statement containing *! !* matching BEGIN *! fwx.5 !* Now figure something for comment: *! 5f~MACRO"E !* MACRO *! .[.6 q.1j i_% .u.1 ].6j !* Change END; to END; % *! Onamit' 6f~MODULE"E Onamit' !* MODULE *! .m(m.m&_BLISS_Get_Statement_Label)u.3 !* .3: 0 or label. *! q.3"E 7f~ROUTINE"E Onamit' !* Unlabelled ROUTINE *! "# 5f~BEGIN"N !* Unlabelled, if not BEGIN, *! fwx.3 !* .3: token starting stmt (IF/...). *! :i.3.3-.4' !* .3: IF-BEGIN, ELSE-... *! "# fwx.3''' !* .3: BEGIN. *! Ocomend !namit! !* get next token. *! fw+.m(m.m&_BLISS_Next_Token)x.3 !* .3: token after ROUTINE/MACRO/MODULE *! :i.3.5_.3 !* .3: "declaration"+nexttoken. *! !comend! q.1j i_%(_.3_)% !* Comment the end statement. MARK is *! !* before the comment from earlier. *! q.1-fku.1 !* .1: Point after ; *! q.2j !* Back to match and... *! 0 ^v !* ...Display there. *! *30:w !* Wait ARG seconds or til input. *! 1  !* Exit, restore point. *! !& BLISS Get Statement Label:! !S Return label for statement at ARG. ARG: Point within statement. Returns string containing label, or Returns 0 if no label found.! .[.0 fnq.0j !* .0: Auto-restoring point. *! 1m(m.m&_BLISS_End_Prev_Stmt)j !* Move to end last stmt. *! 1,.m(m.m&_BLISS_Next_Token)[.2[.1 !* .1,.2: Bound next token. *! q.2j 0,1a-:"N 0 ' !* Return 0, no label found. *! q.1,q.2x*  !* Return label string. *! !Indent BLISS Region:! !C Indents each line from here to MARK. On each line it: moves past labels, comments to first token, then calls ^R Delete Horizontal Space, then calls ^R Indent BLISS Stmt. Leaves mark and point around changed region, and old text in kill stack.! :[.1 .u.2 [.3[.4[.5 q.1,q.2fu.2u.1 !* Order point and mark. *! q.1j !* To beginning of region. *! 1[9 q.1,q.2x.5 !* Save text, set kill direction. *! q.1,q.2m(m.m&_Kill_Text) !* Kill region to save it on kill stack. *! g.5 !* Get back region now to edit it. *! q.1j z-q.2u.2 !* Reset .2 so doesnt change as edit. *! < 0l m(m.m&_BLISS_Next_Token)u.4u.3 !* Find next token. May be on another line *! q.3j !* Go to its left edge: first tok on its *! !* line. *! z-.-q.2:; !* See if done. *! ^m(m.m^R_Delete_Horizontal_Space)w ^m(m.m^R_Indent_BLISS_Stmt)w !* Indent that line. *! l > q.1 !* Set MARK at beginning of region. *! z-q.2j !* Set point at end. *! q.1,.  !* Display and exit. *! !^R BLISS Indent Relative:! !^R BLISS 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, underscores, 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_BLISS_Indenter[.1 :iLast_BLISS_Indenter.1(^R_BLISS_Indent_Relative) [1 ^:I1| qLast_BLISS_Indenteru.1 !* 1: Macro for weird tabbing. *! :iLast_BLISS_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:FB_., _"E:L'"#R' !* Find end of word. *! ^F_.,_ R !* 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. *! !^R Global BLISS Comment:! !^R Call ^R for large comment. While in global comment ^R: Comment column is set to 10, ^R Indent for Comment is called initially, MM Auto Fill Mode$ is entered with auto-indent, $Comment Begin$ is set to "%(*" to mark global comments. "%(** )%" (comment with just a *) expand into %( * * *... )%, aligned with any surrounding global comments (for boxes), or if no surrounding comments expand into $BLISS Star Line Width$ wide (default is 51). When ^R returns, comment-ends within each global comment will be vertically aligned.! 0fo..qGlob_of_Comments_Flag"N !* Already in Global Comment. *! f:^m(m.m^R_Indent_for_Comment)' !* So just do M-;. *! 1m.vGlob_of_Comments_Flagw !* Signal now in glob mode. *! fn0m.vGlob_of_Comments_Flagw !* When leave, signal not in mode. *! [.1[.2 m.m^R_End_Comment[..\ !* Redefine M-\ temporarily *! :i*Global_Comment[..j 10[Comment_Column :i*%(*[Comment_Start !* So mark each global comment *! !* for later comment-end aligning. *! :i*_)%[Comment_End !* For ^R Comment end *! :i*%(*[Comment_Begin !* For comment continuation *! 1[Space_Indent_Flag fs qp ptr !* Q: Pop to here to restore fill mode. *! [Auto_Fill_Mode !* Save whether auto-filling now. *! m(m.m_Auto_Fill_Mode) !* Into auto fill mode. *! :l gComment_Start !* Put in comment starter... *! 1m(m.m^R_Indent_for_Comment) !* ... and then align it. *! .-z"L -l' :l !* Position at end of started comment. *!  w !* Enter ^R mode for globals. *! 0l !* Always be at line beginning. *! !* so that HPOS works in virtl bufs. *! :fb%(*"L !* If we are in a global comment, *! < l :fb%(*"E 1;' >' !* ...then go to line after comments. *! fs z-.f[ vzw !* Bounds around buffer before point. *! m(m.m&_Align_Global_BLISS_Comment_Ends) !* Align those before point. *! zj f] vzw !* Restore bounds. *! .f[ vbw !* Bounds around buffer after point. *! m(m.m&_Align_Global_BLISS_Comment_Ends) !* Align those after point. *! bj f] vbw !* Restore bounds. *! qQ fs qp unwindw !* Pop down and restore auto-fill. *! m(m.m&_Process_Options) !* Auto-fill may change. *! m(m.m&_Set_Mode_Line) !* ... *!  !& Align Global BLISS 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 %( * *...)%, $BLISS Star Line Width$ wide, default 51, and extending to left margin. After alignment, "%(*" is replaced by "%( ".! [.1[.2[.3 bj !* Go thru whole buffer. *! < :s%(*; 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. *! .( < :fb%(*; !* Next gc start in this cgc. *! %.3w !* .3: Inc gc count. *! .-1f_ !* Replace with regular comment start. *! 4f=*_)%"E 1u.2' !* .2: Found a %(** )% in this cgc. *! :fb)%; !* 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, *! 51fo..qBLISS_Star_Line_Widthu.1'' !* ...and set width of star line. *! q.1-2u.1 !* .1: HPOS to put %( at. *! q.2"N !* If have a %( * * *...)% in * cgc, *! (q.1-qComment_Column-3&1)+q.1u.1' !* .1: round up if need to, to *! !* make the * * )% come out evenly. *! < :s)%; !* Next gc end. *! -7f=%(_*_)%"E !* If comment was just a *, expand *! 2r !* ...into * * * * ... *! q.1-(fs hpos)/2 !* ... *! 2c ' !* ... (to match 2r next). *! 2r 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 End Global BLISS Comment:! !^R Exit large comment ^R. Calls ^R End Comment, then exits ^R, so that global comment ^R is ended, and comment column will be reset to its old (one-line comment form) value. If given an ARG, will inhibit alignment of global comment-ends, by replace all global comment-starts ("%(*") by regular comment-starts ("%( ").! ^m(m.m^R_End_Comment) f ff"G !* Given an ARG. *! .( bj !* ...so replace all %(*, *! < :s%(*; .-1f_> !* ...with %( . *! )j' !* ...and return to original point. *! fs ^R_Exit  !* Exit this global com mode ^R. *! !^R Slurp BLISS 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 BLISS 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_BLISS_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_BLISS_Stmt) f !* Now reindent slurped text line. *! z-q.1j w 1  !* Exit, with orig point restored. *! !^R Print Last BLISS Indenter:! !^R Print macro name in echo area.! qLast_BLISS_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  !* Exit. *! !& BLISS Indent After Stmt:! !S Default after-indent, same as prev stmt. If no prev stmt (i.e. 1st stmt in buffer), then indentation is given by the variable $1st Stmt Indentation$, default = 10. ARG1 -> where to indent. ARG2 -> token for previous statement. Returns two vals surrounding insertion. Leaves pointer after indentation.! qLast_BLISS_Indenter[.1 :iLast_BLISS_Indenter.1(&_BLISS_Indent_After_Stmt) u.1 !* Where to indent. *! [.2 !* Pointer to prev stmt token. *! [.3 !* Indentation amount. *! q.2-b"E !* This is 1st statement, no prev. *! 4u.3 !* Indentation amount, default. *! 1:' !* See if user provides an amount. *! "# !* There is a previous stmt. *! q.2j fs hposu.3' !* See how much prev stmt indented. *! q.1j !* Go to where indent. *! q.3-(fshpos)-1"L ^m(m.m^R_BLISS_Indent_Relative) ' !* If past where indent *! !* to then call indent relative. *! q.3m(m.m&_Indent)  !* Indent and return ptrs around. *! !& BLISS Indent After Compound:! !S Handle indentation for stmt after IF etc. Compound stmts are IF-, ELSE-, INCR-, WHILE-, ... ARG1 -> where to indent. ARG2 -> token for previous statement. Returns two vals surrounding insertion. Leaves pointer after indentation. If (e.g.) IF ... BEGIN, then indent aligns with BEGIN, + $COMP BLOCK Indentation$, i.e. $...$ over from BLOCK. (Default $COMP BLOCK$ is 0.) If non-BLOCK, aligns with IF.! qLast_BLISS_Indenter[.1 :iLast_BLISS_Indenter.1(&_BLISS_Indent_After_Compound) u.1 !* Where to indent. *! [.2 !* Pointer to prev stmt token. *! 1:"N !* Get $...$ or make. *! 0m.vCOMP_BLOCK_Indentationu.3' !* Default is 0. *! q.2j fs hpos[.6 !* .6 has (default) indentation. *! !* e.g. indentation of non-BEGIN IF. *! !* Check for IF-BEGIN, IF-UNTIL, etc. *! !* and align with BEGIN or UNTIL: *! q.2m(m.m&_BLISS_Last_Sub-Stmt_Token)[.5[.4 !* .4,.5 around final token. *! q.4,q.5f~BEGIN"E q.4j fs hpos+q.3u.6' !* IF ... BEGIN. *! q.4,q.5f~DO"E q.4j fs hpos+q.3u.6' !* IF ... DO. *! q.4,q.5f~UNTIL"E q.4j fs hpos+q.3u.6' !* IF ... UNTIL. *! q.4,q.5f~WHILE"E q.4j fs hpos+q.3u.6' !* IF ... WHILE. *! q.4,q.5f~INCR"E q.4j fs hpos+q.3u.6' !* IF ... INCR. *! q.4,q.5f~DECR"E q.4j fs hpos+q.3u.6' !* IF ... DECR. *! q.1j !* Go to where indent. *! q.6-(fshpos)-1"L ^m(m.m^R_BLISS_Indent_Relative) ' !* If past where indent *! !* to then call indent relative. *! q.6m(m.m&_Indent)  !* Indent and return ptrs around. *! !& BLISS Indent Unfin Begin Stmt:! !S Indent after BEGIN. ARG1: where to indent. ARG2: BEGIN token.! ,m(m.m&_BLISS_Indent_After_New_Block)  !& BLISS Indent After New Block:! !S Next statement indented more, for BEGIN etc. ARG1 -> where to indent. ARG2 -> BEGIN token. 2 Vals returned, around indentation. Pointer left after indentation. Indentation aligns with whatever follows the BEGIN, etc.! qLast_BLISS_Indenter[.1 :iLast_BLISS_Indenter.1(&_BLISS_Indent_After_New_Block) u.1 [.2 q.2j !* Start of Begin token. *! fs hpos+4[.3 !* Record this column. *! q.1j q.3-(fshpos)-1"L ^m(m.m^R_BLISS_Indent_Relative) ' !* If past where indent *! !* to then call indent relative. *! q.3m(m.m&_Indent)  !* Indent and return ptrs around. *! !& BLISS Indent After Routine Stmt:! !S Indent $1st Routine Stmt Indentation$ amount. If no $...$ then default 0. ARG1 -> where to indent. ARG2 -> ROUTINE token. Returns two vals around indentation. Leaves pointer after indentation.! [.1 [.2 [.3 qLast_BLISS_Indenter[.4 :iLast_BLISS_Indenter.4(&_BLISS_Indent_After_Routine_Stmt) 0fo..q1st_Routine_Stmt_Indentationu.3 !* Indentation amount. *! q.1j !* Where to indent. *! q.3-(fshpos)-1"L .,. ' !* If past where indent do nothing. *! q.3m(m.m&_Indent)  !* Indent and return ptrs around. *! !& BLISS Indent After End Stmt:! !S Insert indentation by matching BEGIN/ROUTINE ARG1 -> where to indent. ARG2 -> END token. Returns 2 vals around indentation. Returns with pointer after indentation.! qLast_BLISS_Indenter[.1 :iLast_BLISS_Indenter.1(&_BLISS_Indent_After_End_Stmt) u.1 [.2 [.4[.5 0[.3 !* BEGIN/ROUTINE level counter. *! q.2j !* Start from END token. *! !Put in sometime search for matching label subr... *! < b-.; !* Go back statements until find *! !* a matching BEGIN *! 1m(m.m&_BLISS_End_Prev_Stmt)j !* Back 1 semicolon... *! m(m.m&_BLISS_Next_Token)u.5u.4 !* ... and fwd to prev stmt token. *! q.4u.2 !* Save beginning of this stmt. *! q.4,q.5f~IF"E !* IF: check last sub-stmt. *! q.4m(m.m&_BLISS_Last_Sub-Stmt_Token)u.5u.4' q.4,q.5f~ELSE"E !* ELSE: check last sub-stmt. *! q.4m(m.m&_BLISS_Last_Sub-Stmt_Token)u.5u.4' q.4,q.5f~INCR"E !* INCR: check last sub-stmt. *! q.4m(m.m&_BLISS_Last_Sub-Stmt_Token)u.5u.4' q.4,q.5f~DECR"E !* DECR: check last sub-stmt. *! q.4m(m.m&_BLISS_Last_Sub-Stmt_Token)u.5u.4' q.4,q.5f~DO"E !* DO: check last sub-stmt. *! q.4m(m.m&_BLISS_Last_Sub-Stmt_Token)u.5u.4' q.4,q.5f~WHILE"E !* WHILE: check last sub-stmt. *! q.4m(m.m&_BLISS_Last_Sub-Stmt_Token)u.5u.4' q.4,q.5f~UNTIL"E !* UNTIL: check last sub-stmt. *! q.4m(m.m&_BLISS_Last_Sub-Stmt_Token)u.5u.4' q.4,q.5f~BEGIN"E q.3-1u.3' !* or BEGIN, *! q.4,q.5f~END"E %.3w' !* Inc on END (finds ARG2 first). *! q.3-1:; !* Found match when level = 0. *! > q.2j !* First token of statement containing *! !* matching BEGIN *! fs hpos[.3 !* Save match's indentation. *! q.1j q.3-(fshpos)-1"L ^m(m.m^R_BLISS_Indent_Relative) ' !* If past where indent *! !* to then call indent relative. *! q.3m(m.m&_Indent)  !* Indent and return ptrs around. *! !& BLISS Indent Unfin Stmt:! !S Indent new line of unfinished statement. Default for indenting unfinished statements. Does: Case1: If there is an open paren, aligns with next non-white. Case2: If $Unfin Stmt Indentation$ is defined it will indent $Unfin Stmt Indentation$ more than the start of the stmt. Case3: If prev line contains start of stmt, aligns with 1st word after beginning of stmt token (which may be part of token). Case4: Calls ^R BLISS Indent Relative. ARG1 -> where to indent. ARG2 -> token starting unfinished statement. Returns 2 vals around indentation. Point left after indentation.! qLast_BLISS_Indenter[.1 :iLast_BLISS_Indenter.1(&_BLISS_Indent_Unfin_Stmt) u.1 !* Where to indent. *! [.2 !* Pointer to prev stmt token. *! [.4 !* Extra indentation amount. *! [.3[.5 1:< fo..qUnfin_Stmt_Indentationu.4 !* Get $...$ if defined. *! q.2j fs hposu.3 !* How much prev stmt indented. *! q.3+q.4u.5 !* Set column to indent to by $...$. *! >"N !* Not defined. *! q.1j -l !* To line before current. *! q.2-."g !* Line has stmt token. *! 5*.,5*.+1:g..d[.6 !* Save . dispatch. *! 5*.:f..d  !* Change it to be break char. *! 2:fwl fshposu.5 !* Align with next word. *! 5*.:f..d.6 ' !* Change back. *! "# !* Line doesnt have stmt token. *! 0u.5'' !* Force call to indent rel. *! q.1j !* Go to where indent. *! 1:< -:ful !* See if any open parens. *! ^f_ jw !* Yes, go past white-space. *! fs hposu.5 !* Will indent to this column. *! > q.1j !* Where indent *! q.5-(fshpos)-1"L ^m(m.m^R_BLISS_Indent_Relative) ' !* If past where indent *! !* to then call indent relative. *! q.5m(m.m&_Indent)  !* Indent and return ptrs around. *! !& BLISS Indent Unfin If Stmt:! !S Indent new line of unfinished IF statement. Indents same amount as the IF if the THEN hasn't occured yet. If THEN has occured, just calls & BLISS Indent Unfin Stmt. ARG1 -> where to indent. ARG2 -> token starting unfinished statement. Returns 2 vals around indentation. Point left after indentation.! qLast_BLISS_Indenter[.1 :iLast_BLISS_Indenter.1(&_BLISS_Indent_Unfin_If_Stmt) u.1 !* Where to indent. *! [.2 !* Pointer to prev stmt token. *! fsqpptr[.0 !* .0: Unwind to here to restore vbuf. *! fsz-q.1f[vzw !* VBuf ends at point so dont consider *! !* any statements after this unfin one. *! q.2j .,zm(m.m&_BLISS_Token_Search)THEN"N !* Point after last THEN. *! .,1m(m.m&_BLISS_Token_Search)IF"E !* No following IF, so *! q.0 fs qp unwindw !* Restore vbuf. *! q.1,q.2m(m.m&_BLISS_Indent_Unfin_Stmt) ' !* treat as random. *! 2r' !* Before IF under which to indent. *! q.0 fs qp unwindw !* Restore vbuf. *! fs hpos[.3 !* See how much IF was indented. *! q.1j !* Go to where indent. *! q.3-(fshpos)-1"L !* Past where indent to, *! ^m(m.m^R_BLISS_Indent_Relative) ' !* so call indent relative. *! q.3m(m.m&_Indent)  !* Indent and return ptrs around. *! !& BLISS Indent Unfin Comp:! !S Indent new line of unfinished compound. Indents same amount as the last sub-statement so far. ARG1 -> where to indent. ARG2 -> token starting unfinished statement. Returns 2 vals around indentation. Point left after indentation.! qLast_BLISS_Indenter[.1 :iLast_BLISS_Indenter.1(&_BLISS_Indent_Unfin_Comp) u.1 !* Where to indent. *! [.2 !* Pointer to prev stmt token. *! [.4[.5 q.2,q.1f[hboundw !* Virtual buf around this unfin stmt. *! bm(m.m&_BLISS_Last_Sub-Stmt_Token)u.5u.4 !* Find last sub-stmt token. *! q.4j !* Go there. *! f]hboundw !* Restore original buf boundaries. *! q.4,q.5f~IF"E Ocfound' !* IF compound *! q.4,q.5f~INCR"E Ocfound' !* INCR compound *! q.4,q.5f~DECR"E Ocfound' !* DECR compound *! q.4,q.5f~DO"E Ocfound' !* DO compound *! q.4,q.5f~UNTIL"E Ocfound' !* UNTIL compound *! q.4,q.5f~WHILE"E Ocfound' !* WHILE compound *! q.1,q.4m(m.m&_BLISS_Indent_Unfin_Stmt)  !* No compound - use default *! !cfound! q.4j fs hpos[.3 !* See how much compound was indented. *! q.1j !* Go to where indent. *! q.3-(fshpos)-1"L ^m(m.m^R_BLISS_Indent_Relative) ' !* If past where indent to *! !* Then call indent relative. *! q.3m(m.m&_Indent)  !* Indent and return ptrs around. *! !& BLISS Next Non-Begin Token:! !C Returns next token which isn't BEGIN.! [.1[.2 m(m.m&_BLISS_Next_Token)u.2u.1 !BEGLP! !* pass over Begins *! q.1,q.2F~BEGIN"E m(m.m&_BLISS_Next_Token)u.2u.1 q.2j oBEGLP ' q.1,q.2  !& BLISS Next Token:! !S Return ptrs around next token in buffer. ARG gives place to start from in buffer. No ARG means here. ARG,: Labels are skipped unless pre-comma argument given. Comments are skipped.! .[.1 !* Save point while search. *! [.2[.3 !* .3,.2 will bound token. *! qBLSIND_..D[..D !* Use BLISS break-chars. *! ff"G j' !* Position at ARG1 if any. *! z-b"E q.1j z,z ' !* Empty buffer, no token. *! < !* Loop over comments, maybe labels. *! !* Search for end of token or beginning *! !* of comment: *! :s%(!"E !* Search for token end. *! zj 0a"B q.1j z,z ' !* No tokens found. *! .u.2 !* .2: End of buf is end of token. *! -fwl .u.3 1;' !* .3: Mark beginning of token. *! 0a-!"E 1@l !' !* Skip to next line for comment *! -1a-%"E :s)%"E zu.3zu.2 1;' ' !* Skip imbedded comment. *! "# .-1u.2 !* .2: Possible end of token. *! (ff&2)1a-:"N !* If pre-comma (i.e. accept labels) *! !* or if not a label, *! q.2j-fwl .u.3 1;' !* .3: then have token. *! ' !* Skip past a label. *! > q.1j !* Restore point. *! q.3,q.2  !* Return bounds of token. *! !* z,z if none found. *! !& BLISS Skip Decl:! !S Searches for = , but not within parens. Comments are skiped. ARG1 is point in buffer to start from. Returns 0 if search unsuccessful: end of statement (;) or end of buffer reached first, non-0 if successful.! 0[.1 .[.2 j 0[.3 !* .3 is return val. Init notfound. *! < :s=%(;!()"E 1;' !* Found nothing. *! 0a-;"E 1;' !* End of statement found first. *! 0a-!"E 1@l ' !* Skip to next line for comment *! -1a-%"E :s)%"E 1;'' !* Skip imbedded comments *! 0a-("E %.1' !* .1: Paren depth *! 0a-)"E q.1-1u.1' !* .1: Decr depth counter on ) *! 0a-="E q.1"E 1u.3 1;'' !* Found = *! > q.3"E q.2j ' !* If unsuccessful, dont move. *! q.3  !* Return 0 lost, 1 won. *! !& BLISS Token Search:! !S Searches for a token in a statement. Comments are skipped. ARG1 is point in buffer to start from. ARG2 is iteration: i.e. search for ARG2th token. STRINGARG is token to search for. Returns 0 if search unsuccessful: end of statement (;) or end of buffer reached first, non-0 if successful. Returns with pointer after token if successful.! :i*[.1 .[.2 j 0[.3 !* .3 is return val. Init notfound. *! qBLSIND_..D[..D !* Use dispatch table for BLISS. *! f' !* Skip to next line for comment *! -1a-%"E :s)%"E f;DONE' !* Skip imbedded comments, but allow *! r 1u.3' !* .3: paren to end token. *! >> q.3"E q.2j ' !* If unsuccessful, dont move. *! q.3  !* Return 0 lost, 1 won. *! !& BLISS Tokens to Leftp:! !S Returns 0 if no tokens to left on line. ARG is pointer into line where to look left from. Labels or comments are not counted.! [.1 .[.2 [.3[.4 q.1j 0l !* To beginning of line. *! m(m.m&_BLISS_Next_Token)u.4u.3 !* Find next token after that. *! q.2j !* Back to original point. *! q.3-q.1"L 1 ' !* Token is to left. *! "# 0 ' !* Token is not to left. *! !& BLISS Last Sub-Stmt Token:! !S Finds last stmt in compound stmt (if, on). If current statement is "IF a THEN IF b THEN c;" this subr will return ptrs surrounding c's statement token. ARG1 points to statement token from where to start, i.e. the compound statement's token (IF). If ARG1 points to a non-compound statement token, pointers around that token are returned.! [.1 .[.2 [.3[.4 q.1j !* Position at starting stmt token. *! f !* End next. *! > !* End found. *! q.3,q.4(q.2j)  !* Return ptrs around token. *! !& BLISS End Prev Stmt:! !S Return ptr to end prev ARG'th statement. Value returned points to the semicolon. ARG = 1 means find first end-; looking back from here.! .[.1 !* Save point while search. *! < !* Look back ARG statements. *! < !* Look back one statement. *! -:s;)%!"E bj 1; ' !* No previous statement. *! "# 1a-!"E !' !* Skip past comment *! 1a-;"E 1;' !* Find end of prev stmt. *! -:s%(' !* Skip past imbedded comments. *! > !* Point at end of prev stmt. *! > .(q.1j)  !* Return semicolon-ptr and restore *! !* point. *! !& Back Over BLISS Line Comment:! !S From point, move left on line past comment. ARG is ptr into some line to check left from. Returns ptr to before comment and white-space before it. If no comment on line, no movement: ARG is returned.! .[.1 j .,(0l).:fb%(!"E  ' !* No comment found on line. *! -^fq_ [.2j !* Move before any white space. *! .(q.1j)  !* Return that place, reset point. *! !& Setup BLISS Library:! !S Put macros into $MM ...$ qregs for speed.! m.m&_BLISS_End_Prev_Stmtm.vMM_&_BLISS_End_Prev_Stmtw m.m&_BLISS_Next_Tokenm.vMM_&_BLISS_Next_Tokenw m.m&_BLISS_Indent_After_Stmtm.vMM_&_BLISS_Indent_After_Stmtw m.m&_BLISS_Indent_After_New_Blockm.vMM_&_BLISS_Indent_After_Begin_Stmtw m.m&_BLISS_Indent_After_Endm.vMM_&_BLISS_Indent_After_End_Stmtw m.m&_BLISS_Indent_Unfin_Stmtm.vMM_&_BLISS_Indent_Unfin_Stmtw m.m&_BLISS_Indent_Unfin_If_Stmtm.vMM_&_BLISS_Indent_Unfin_If_Stmtw m.m&_BLISS_Indent_Unfin_Compm.vMM_&_BLISS_Indent_Unfin_Else_Stmtw m.m&_BLISS_Indent_Unfin_Compm.vMM_&_BLISS_Indent_Unfin_Do_Stmtw m.m&_BLISS_Indent_Unfin_Compm.vMM_&_BLISS_Indent_Unfin_Incr_Stmtw m.m&_BLISS_Indent_Unfin_Compm.vMM_&_BLISS_Indent_Unfin_Decr_Stmtw m.m&_BLISS_Indent_Unfin_Compm.vMM_&_BLISS_Indent_Unfin_While_Stmtw m.m&_BLISS_Indent_Unfin_Compm.vMM_&_BLISS_Indent_Unfin_Until_Stmtw m.m&_BLISS_Last_Sub-Stmt_Tokenm.vMM_&_BLISS_Last_Sub-Stmt_Tokenw m.m&_BLISS_Indent_After_Compoundm.vMM_&_BLISS_Indent_After_If_Stmtw m.m&_BLISS_Indent_After_Compoundm.vMM_&_BLISS_Indent_After_Else_Stmtw m.m&_BLISS_Indent_After_Compoundm.vMM_&_BLISS_Indent_After_Do_Stmtw m.m&_BLISS_Indent_After_Compoundm.vMM_&_BLISS_Indent_After_Incr_Stmtw m.m&_BLISS_Indent_After_Compoundm.vMM_&_BLISS_Indent_After_Decr_Stmtw m.m&_BLISS_Indent_After_Compoundm.vMM_&_BLISS_Indent_After_While_Stmtw m.m&_BLISS_Indent_After_Compoundm.vMM_&_BLISS_Indent_After_Until_Stmtw m.m&_BLISS_Token_Searchm.vMM_&_BLISS_Token_Searchw m.m&_BLISS_Indent_After_Routine_Stmtm.vMM_&_BLISS_Indent_After_Routine_Stmtw  !& Default Init BLISS Mode:! !S Default setup for BLISS mode. It sets up the following characters for this mode and buffer: RUBOUT Tab-hacking rubout, C-RUBOUT Normal rubout, TAB ^R Indent BLISS Stmt, C-M-? ^R Print Last BLISS Indenter, M-; ^R Indent for Comment, M-\ ^R Insert BLISS End, C-M-; ^R Global BLISS Comment, C-M-\ ^R End Global BLISS Comment, M-{ ^R Slurp BLISS To Char. Turns on Auto Fill if $BLISS Auto Fill Default$ is non-0. Users who want to provide their own MM Init BLISS Mode$ can make ^R keys (q-registers) and variables local to this mode and buffer by using the following subroutine which is global to Init BLISS Mode: .Q: Make Local Q-register! !* Set up local ^R keys: *! 1,q(1,q. m.Qw)m.Q. !* Exchange rubout flavors. *! 1,m.m^R_Indent_BLISS_Stmtm.QI !* Use Bliss-indenting tab. *! 1,m.m^R_Print_Last_BLISS_Indenterm.Q...? !* C-M-? For tracing. *! 1,m.m^R_Indent_for_Commentm.Q..; 1,m.m^R_Insert_BLISS_Endm.Q..\ 1,m.m^R_Global_BLISS_Commentm.Q...; 1,m.m^R_End_Global_BLISS_Commentm.Q...\ 1,m.m^R_Slurp_BLISS_To_Charm.Q..{ 0fo..qBLISS_Auto_Fill_Default( !* Default is to auto-fill, *! )m(m.m_Auto_Fill_Mode) !* ...will cause unfin indenting. *! 1,1m.LSpace_Indent_Flagw !* ... *!  !* Done. *! !* Following should be kept as long comments: * Local Modes: * Fill Column:76 * Comment End: *! !* * End: * *!