\ lf v0.0.12f 06 August 2002 + \ Leo Wong \ hello@albany.net \ http://www.albany.net/~hello/ \ I thank Wil Baden, Anton Ertl, Marcel Hendrix, \ Benjamin Hoyt, Chris Jakeman, Bruce R. McFarling, \ Barrie Stott, and Jonah Thomas for their help. \ I am grateful to Chris Jakeman for pointing out \ and correcting several mistakes. \ LF is an NPBP (not pretty but portable) ANS Forth \ word processor. \ Portable means: designed to work in any ANS Forth \ ("Standard") system that implements, can define, or can \ provide the functionality of the ANS Forth words that LF \ uses (see below for a list of these words). LF has a few \ environmental dependencies that could be gotten rid of. \ See also below the CONSTANTs that may need to be changed \ for LF to work optimally on your system. \ I have tested the NP part of LF. LF could easily become \ quite comely though still austere. I await word on the BP \ part. Please tell me if LF works or doesn't work on your \ ANS Forth system. \ I would also appreciate being notified of any bugs you find. \ To start LF, load a Standard System, then enter: \ \ INCLUDE LF.4TH ( S" LF.F" INCLUDED) \ \ Enter a filename. You are now in text-entry mode. Enter \ some text or press: \ \ `` \ \ that is, two single opening quotations marks, or glottal \ stops, or left hands clapping to enter Command Mode. In \ Command Mode, press: \ \ q \ \ to query the help screen. \ \ The Enter, Backspace, and Tab keys work in both text-entry \ and command modes. \ I have not provided a printing function, not knowing how to \ do so portably. I've provided some words to try if you can \ teach your ANS Forth to print. \ ANS Forth Documentation \ \ LF uses ANS Forth words from the Core word set. \ \ LF also uses words from other word sets. Though "required" \ by LF, many of these words don't need to be in your Forth \ system: they can be easily defined or their functionality \ can be provided by other words. I believe that the only \ real requirements are the Core word set and the abilities to \ position the cursor and to read and write to mass storage. \ \ Having said this, I say that: \ \ LF is an ANS Forth Program \ \ With environmental dependencies: \ will respond to control characters 8, 9, and 13 though \ the ability to receive control characters is not required \ may be configured to send control character 7. \ uses flags as arithmetic operands (I think it does) \ uses two's complement arithmetic (maybe - I hope not) \ \ Requiring from the Core Extensions word set: \ 2>R 2R> <> ?DO CASE ENDCASE ENDOF ERASE FALSE MARKER \ NIP OF PAD TO TRUE TUCK U.R UNUSED VALUE WITHIN \ \ \ Requiring from the Facility word set: \ AT-XY PAGE \ \ Requiring from the File-Access word set: \ ( BIN CREATE-FILE FILE-SIZE INCLUDED OPEN-FILE \ R/O READ-FILE S" W/O WRITE-FILE \ \ Requiring from the String word set: \ -TRAILING BLANK CMOVE CMOVE> SEARCH \ \ Requiring the Memory-Allocation word set (if ALLOCATEing): \ ALLOCATE FREE \ \ \ LF requires keyboard input, the ability to position \ the cursor, and access to mass storage in the form of \ files. \ \ \ A Standard System exists after LF is loaded. \ =================================================== \ Notes (by Krishna Myneni, 2002-09-06): \ \ -- Line numbering may be turned off/on by setting the \ constant LINE#-SPACE. Here, line numbers are turned off \ by default. \ \ -- The page length may be changed by setting the constant \ MAX-Y. Here it is set to the original default of 23, \ but I prefer to use a full page length (54 for MAX-Y). \ Longer page lengths may be used in ANSI consoles with \ a sufficient number of rows, for example a BASH shell \ under X-Windows that has been resized to accomodate \ the full text display. The cursor position will be \ incorrect if the console does not support enough output \ lines. MAX-Y of 23 should work on any console. \ \ -- The constant 'CR has been changed from decimal 13 to 10, \ since the LF character represents an end of line under \ UNIX systems. \ \ -- The constant EDIT-BUF-SIZE is 1 MB, suitable for most \ day to day usage. Increase/decrease as desired. \ \ =================================================== \ Code modifications for the kForth version (KM 2002-08-13): \ \ 1. Changed >FILE to BUF>FILE and FILE> to FILE>BUF. \ 2. Modified BUF>FILE test for WRITE-FILE result. \ 3. Recoded -TRAILING<> to remove WHILE ... THEN structure. \ 4. Changed READ to READ-DOC and ?READ to ?READ-DOC. \ 5. Changed CALL to CALL-WAY. \ 6. VALUEs which are addresses have been changed to "ptr"s \ 7. Remove use of HERE and "," and replace with equivalent code. \ 8. Replaced ?DE-ALLOCATE and DO-ALLOCATE with dummy definitions. \ 9. TEXT buffer is CREATEd and ALLOTed initially. \ \ =============== kForth requires =================== \ include ans-words \ include strings \ include ansi \ include files \ include filesw under Windows \ : ptr CREATE 1 CELLS ?ALLOT ! DOES> a@ ; \ : BIN ; \ ANS compliant defn of >NUMBER is now part of ans-words.4th (km 2003-3-9) \ ============== end of kForth requires ============ \ Here begins the source code for LF: 1024 1024 * CONSTANT EDIT-BUF-SIZE CREATE EDIT-BUF EDIT-BUF-SIZE ALLOT ( MARKER TASK ) : K* ( n1 -- n2 ) 1024 * ; \ adjust constants as needed \ filename delimiters CHAR / CONSTANT PATH-DELIMITER CHAR : CONSTANT DRIVE-DELIMITER \ using ALLOCATE ? FALSE CONSTANT ALLOCATING 128 CONSTANT DEFAULT-ALLOCATE \ in K \ beeps? TRUE CONSTANT BEEPS \ tab, linewidth 5 CONSTANT TABWIDTH 12 CONSTANT TABS/LINE TABWIDTH TABS/LINE * CONSTANT LINEWIDTH \ multiple makes easy 2 CONSTANT LEDGE \ room for spaces beyond linewidth LINEWIDTH LEDGE + CONSTANT PLANK \ a cut or copy goes to memory if it fits, \ otherwise to a file 2 K* CONSTANT POCKET-SIZE \ left margin holds line number \ the start of a page is shown to the right of the line ( 6) 0 CONSTANT LINE#-SPACE \ 0 if not displaying 5 CONSTANT PAGE#-SPACE \ 0 if not displaying \ screen display LINEWIDTH LINE#-SPACE + PAGE#-SPACE LEDGE MAX + CONSTANT MAX-X \ # of columns ( 23) ( 54) 40 CONSTANT MAX-Y \ # of rows MAX-X 16 - CONSTANT MAX-INPUT \ reserves space for a prompt \ screen/page 0 CONSTANT BANNER-LINE 2 CONSTANT TOP \ line 1 has a ruler MAX-Y 1- CONSTANT STATUS-LINE \ display status below text STATUS-LINE TOP - 1- CONSTANT LMAX/SCREEN \ text lines to show TOP LMAX/SCREEN + 1- CONSTANT BOTTOM \ last line to show text \ displayable characters are implementation defined 126 CONSTANT LAST-DISPLAYABLE \ this from Marcel Hendrix \ TRUE PAD ! PAD C@ CONSTANT LAST-DISPLAYABLE \ characters for displaying "invisibles" CHAR _ CONSTANT .BL \ BL CHAR | CONSTANT .CR \ CR CHAR ^ CONSTANT .OTHER \ e.g. LF ! \ keyboard entry \ ASCII characters used in command mode \ command mode provides all the functions \ that LF implements \ two of these start, one ends command mode \ consider using ESCape once if it's available \ Bruce R. McFarling recommends having a character for \ starting and a different character for ending command mode CHAR ` CONSTANT ^COMMAND \ command keys CHAR F CONSTANT ^Find-string CHAR G CONSTANT ^find-aGain CHAR R CONSTANT ^Replace CHAR T CONSTANT ^replace-Too CHAR " CONSTANT ^(un)mark(1) CHAR ' CONSTANT ^(un)mark(2) CHAR C CONSTANT ^Copy CHAR D CONSTANT ^Delete CHAR E CONSTANT ^Embed CHAR W CONSTANT ^Wedge CHAR V CONSTANT ^inVest CHAR Q CONSTANT ^Query CHAR A CONSTANT ^Alter-input CHAR Z CONSTANT ^Show CHAR X CONSTANT ^change-name CHAR S CONSTANT ^Save CHAR B CONSTANT ^good-Bye \ next 6 aren't shown in the help screen \ I don't expect them to be used but they would \ eliminate the environmental dependency on the use of \ control codes (if you silence BEEP) CHAR | CONSTANT -Enter-key(1) CHAR \ CONSTANT -Enter-key(2) CHAR _ CONSTANT -Backspace-key(1) CHAR - CONSTANT -Backspace-key(2) CHAR @ CONSTANT -Tab-key(1) CHAR 2 CONSTANT -Tab-key(2) \ cursor keys CHAR L CONSTANT ^right CHAR J CONSTANT ^left CHAR I CONSTANT ^up CHAR K CONSTANT ^down(1) CHAR < CONSTANT ^down(2) CHAR , CONSTANT ^down(3) CHAR H CONSTANT ^1st-col CHAR : CONSTANT ^last-col(1) CHAR ; CONSTANT ^last-col(2) CHAR O CONSTANT ^page-up CHAR > CONSTANT ^page-down(1) CHAR . CONSTANT ^page-down(2) CHAR U CONSTANT ^BOF CHAR M CONSTANT ^EOF CHAR P CONSTANT ^TOP CHAR ? CONSTANT ^BOP(1) CHAR / CONSTANT ^BOP(2) \ ASCII control characters \ use of control characters is an environmental dependency 7 CONSTANT BEL \ bell \ 8 CONSTANT BS \ backspace 127 CONSTANT BS \ backspace on Linux/KDE system 9 CONSTANT HT \ horizontal tab \ 10 CONSTANT LF \ LF doesn't know LF \ 12 CONSTANT FF \ formfeed for printing \ 13 CONSTANT 'CR \ Enter (also marks end of a paragraph) 10 CONSTANT 'CR \ use LF for EOL on Unix systems \ in-key \ would be nice to have DEFER and IS \ 0 ptr (IN-KEY) \ changed from VALUE to ptr -- km 8-11-02 0 VALUE (IN-KEY) : IN-KEY ( -- u flag ) (IN-KEY) EXECUTE ; \ Jonah Thomas: \ Here is something that should work on standard systems: \ \ : NO-GOOD ." bad DEFERed word" ABORT ; \ : DEFER \ CREATE ['] NO-GOOD , \ DOES> @ EXECUTE ; \ \ : (IS) ( xt -- ) \ ' >BODY ! ; \ : [IS] ( -- ) \ ' >BODY POSTPONE LITERAL POSTPONE ! ; IMMEDIATE \ : IS ( S: xt -- ) ( C: -- ) \ STATE @ IF POSTPONE [IS] ELSE (IS) THEN ; IMMEDIATE \ KEY is a Core word : KEY-CHAR ( -- char true) KEY TRUE ; ' KEY-CHAR TO (IN-KEY) \ if your system supports it, and you want to, add \ more keys (such as actual cursor keys) and use \ EKEY instead of KEY : \ : EVENT ( -- u flag ) EKEY EKEY>CHAR ; \ \ ' EVENT TO (IN-KEY) \ PAD space 84 CONSTANT PAD-SPACE \ region guaranteed by PAD \ search pad \ LF uses PAD PAD-SPACE CONSTANT GULP \ #characters in search space : SEARCH-PAD ( -- ) PAD ; \ constants for printing + 50 CONSTANT LINES/PAGE \ printed page \ 11 CONSTANT PMARGIN \ left margin for printing \ tools \ some of these may already exist in your ANS Forth \ do nothing : NOP ; \ number of cells/characters in n1 address units 1 CELLS CONSTANT /CELL 1 CHARS CONSTANT /CHAR \ stack manipulation \ : -ROT ( x1 x2 x3 -- x3 x1 x2 ) ROT ROT ; \ unsigned max and min : UMAX ( u1 u2 -- u1|u2 ) 2DUP U< IF NIP ELSE DROP THEN ; : UMIN ( u1 u2 -- u1|u2 ) 2DUP U< IF DROP ELSE NIP THEN ; \ increment/decrement variable : INCR ( a -- ) 1 SWAP +! ; : DECR ( a -- ) -1 SWAP +! ; \ add stack items : UNDER+ ( n1 n2 n3 -- n1+n3 n2 ) ROT + SWAP ; \ unsigned division : U/MOD ( u1 u2 -- r q) >R 1 UM* R> UM/MOD ; : U/ ( u1 u2 -- q ) U/MOD NIP ; \ fences : BETWEEN ( n1 n2 n3 -- f) 1+ WITHIN ; : CLAMP ( n1 lo hi - n2) ROT MIN MAX ; \ warnings ' NOP VALUE (BEEP) : ?BEEP ( -- ) (BEEP) EXECUTE ; : BEEP ( -- ) BEL EMIT ; : DEEP ( n) DROP ?BEEP ; : ?BEEPS ( -- ) BEEPS IF ['] BEEP ELSE ['] NOP THEN TO (BEEP) ; : WAIT ( -- ) ." Press a key to continue." IN-KEY 2DROP ; \ string words \ Is character between A and Z? : UPPER? ( c -- ? ) [CHAR] A - 26 U< ; \ Is character between a and z? : lower? ( c -- ? ) [CHAR] a - 26 U< ; \ make a character lower/upper case : >lower ( C -- c) DUP UPPER? BL AND XOR ; : >UPPER ( c -- C) DUP lower? BL AND XOR ; \ make a string lower case : lcase ( a u -- ) 0 ?DO DUP C@ >lower OVER C! CHAR+ LOOP DROP ; \ string less the number of trailing characters <> c : -TRAILING<> \ a u1 c -- a u2 >R BEGIN DUP IF 1- 2DUP CHARS + C@ R@ = ELSE 1- TRUE THEN UNTIL 1+ R> DROP ; \ string after last character = c : TRAILING<> ( a1 u1 c -- a2 u2 ) OVER >R -TRAILING<> R> SWAP /STRING ; \ leading characters = c : LEADING= ( a u1 c -- a u2 ) >R 2DUP BEGIN OVER C@ R@ = OVER AND WHILE 1 /STRING REPEAT R> DROP NIP - ; \ string less leading characters <> c : -LEADING<> ( a1 u1 c -- a2 u2 ) >R BEGIN OVER C@ R@ <> OVER AND WHILE 1 /STRING REPEAT R> DROP ; \ string arithmetic : C+! ( n a -- ) DUP C@ UNDER+ C! ; : S+! ( a u s -- ) 2DUP 2>R COUNT CHARS + SWAP CMOVE 2R> C+! ; \ move a counted string : SMOVE ( s1 s2 -- ) OVER C@ 1+ CMOVE ; \ vectored execution + VARIABLE way# : CALL-WAY ( a n -- ? ) CELLS + a@ EXECUTE ; : WAYS CREATE ( n -- ) DUP CELLS ?allot SWAP 0 DO DUP ' SWAP ! /CELL + LOOP DROP DOES> way# @ CALL-WAY ; \ at most one file is open at a time \ some error recovery could be introduced here 0 VALUE FILE-ID \ create a file for writing : CREATE-WRITE ( a u -- ) W/O BIN CREATE-FILE ABORT" CREATE-FILE problem" TO FILE-ID ; \ open a file for reading only : OPEN-READ ( a u - fileid flag ) R/O BIN OPEN-FILE ; \ close an opened file : FCLOSE ( -- ) FILE-ID CLOSE-FILE ABORT" CLOSE-FILE problem" ; \ write u characters starting at a , then close the file : BUF>FILE ( a u -- ) FILE-ID WRITE-FILE 0< ABORT" WRITE-FILE problem" FCLOSE ; \ read u chars to a , then close the file : FILE>BUF ( a u -- ) FILE-ID READ-FILE ABORT" READ-FILE problem" DROP FCLOSE ; \ data structures + \ chars and lines \ actual values determined later 0 VALUE TEXT \ start of text area 0 VALUE CMAX \ max # of characters 0 VALUE LINES \ start of lines data 0 VALUE LMAX \ max # of lines CREATE POCKET POCKET-SIZE CHARS ALLOT \ cut/copy buffer CREATE FILENAME$ MAX-INPUT 6 + CHARS ALLOT \ filename string CREATE CURSOR> 2 CELLS ?ALLOT 0 0 ROT 2! ( 0 , 0 ,) \ cursor position \ document VARIABLE doc-size \ size of document VARIABLE last-line \ last line of document VARIABLE last-old \ previous last line VARIABLE char-now \ current character # VARIABLE topline \ current top screen line VARIABLE top-old \ previous top screen line VARIABLE line-now \ current line VARIABLE line-old \ previous current line VARIABLE col# \ current column \ before the last character? : -DOC-END ( -- f) char-now @ doc-size @ U< ; \ room to add u characters? : ROOM? ( u -- f) doc-size @ + CMAX 1+ U< ; \ lines \ address of nth element of line array : LINE ( l# - a) CELLS LINES + ; \ starting character # and length of a line : LINESPEC ( l# - c# u) LINE 2@ TUCK - ; \ number of characters in a line : LINELENGTH ( l# - u) LINESPEC NIP ; \ zero line data between line#1 and line#2 : 0LINES ( l#1 l#2) OVER - 1+ 0 MAX >R LINE R> CELLS ERASE ; \ zero all line information : 0>LMAX ( -- ) 0 LMAX 0LINES ; \ add u to lines between current line and last line : LINES+! ( u -- ) line-now @ 1+ DUP LINE SWAP last-line @ SWAP - 1+ 0 MAX 0 ?DO 2DUP +! CELL+ LOOP 2DROP ; \ move lines data starting with l# forward one cell : LINES> ( l# -- ) DUP LINE DUP CELL+ ROT last-line @ 1+ DUP last-line ! SWAP - CELLS 0 MAX MOVE ; \ move lines data starting with l#+1 back one cell : <LINES ( l# -- ) 1+ DUP LINE DUP CELL+ SWAP ROT last-line @ SWAP - CELLS 0 MAX MOVE last-line DECR ; \ starting from a line, find the line a character is in : C>L ( c# l#1 -- l#2 ) OVER doc-size @ U< 0= IF 2DROP last-line @ ELSE OVER IF 1- LINE BEGIN CELL+ 2DUP @ U< UNTIL NIP LINES - /CELL / 1- ELSE DROP THEN THEN ; \ find screen row of line : >Y ( l# -- row#) topline @ - TOP + ; \ find bottom line of screen : BOTTOMLINE ( -- u ) topline @ LMAX/SCREEN + 1- ; \ allocate / allot memory \ allocate memory 0 VALUE ALLOCATED \ GET-NUMBER from Woehr, Forth: the New Model : GET-NUMBER ( -- ud f ) 0 0 PAD 84 BLANK PAD 84 ACCEPT PAD SWAP -TRAILING >NUMBER NIP 0= ; \ get a number : GET-INTEGER ( -- u ) GET-NUMBER DROP D>S ; ( ============================================================= \ release previously allocated memory : ?DE-ALLOCATE \ -- ALLOCATED IF LINES FREE ABORT" FREE problem" 0 TO ALLOCATED THEN ; \ allocate memory from user input : DO-ALLOCATE \ -- PAGE 10 10 AT-XY ." Reserve space for how many characters [K]:" GET-INTEGER ?DUP 0= IF DEFAULT-ALLOCATE THEN K* DUP LINEWIDTH 2/ U/ 1+ 2DUP CELLS DUP ROT CHARS + DUP ALLOCATE ABORT" ALLOCATE problem. Not enough memory?" DUP TO LINES ROT + TO TEXT TO ALLOCATED 1- TO LMAX TO CMAX ; \ allot memory : DO-ALLOT CMAX 0= IF UNUSED 4 K* CELLS - \ breathing room - could be less? LINEWIDTH 2/ CHARS /CELL + U/ DUP 1- TO LMAX DUP HERE TO LINES CELLS ALLOT \ allot cells first LINEWIDTH 2/ * DUP TO CMAX HERE TO TEXT CHARS ALLOT THEN ; =========================================================== ) : ?DE-ALLOCATE ; : DO-ALLOCATE ; : DO-ALLOT CMAX 0= IF EDIT-BUF-SIZE 4 K* CELLS - \ breathing room - could be less? LINEWIDTH 2/ CHARS /CELL + U/ DUP 1- TO LMAX EDIT-BUF TO LINES \ allot cells first DUP EDIT-BUF + TO TEXT LINEWIDTH 2/ * TO CMAX THEN ; \ character<-->memory : SPOT ( -- a ) TEXT char-now @ CHARS + ; : T>MEM ( c# u -- a u ) >R CHARS TEXT + R> ; \ screen display \ blank a screen line : RUB ( row -- ) 0 SWAP 2DUP AT-XY MAX-X LEDGE + SPACES AT-XY ; \ display a tab section in a ruler line : .TAB ( -- ) TABWIDTH 1- 0 MAX 0 ?DO [CHAR] - EMIT LOOP [CHAR] | EMIT ; \ display a ruler line : .RULER ( row -- ) 0 SWAP AT-XY LINE#-SPACE IF ." Line " THEN [CHAR] | EMIT LINEWIDTH TABWIDTH / 0 ?DO .TAB LOOP PAGE#-SPACE IF ." Page" THEN ; \ display top and bottom rulers : .RULERS ( -- ) TOP 1- DUP RUB .RULER BOTTOM 1+ DUP RUB .RULER ; \ display current way of input : .INSERT ( -- ) ." INSERT " ; : .OVERWRITE ( -- ) ." OVERWRITE " ; : .MARKING ( -- ) ." MARKING " ; ' NOP VALUE (.WAY) : .WAY ( -- ) (.WAY) EXECUTE ; FALSE VALUE COMMANDING \ false = text entry mode \ delete path from filename : -PATH ( a1 u1 -- a2 u2 ) PATH-DELIMITER TRAILING<> DRIVE-DELIMITER TRAILING<> ; \ display filename : .FILENAME ( -- ) FILENAME$ COUNT -PATH TYPE SPACE ; \ display filename, way, mode : .HEADLINE ( a u -- ) BANNER-LINE RUB .FILENAME .WAY 2 SPACES TYPE 2 SPACES ; \ headline when entering text : .TEXT-ENTRY ( -- ) S" TEXT ENTRY" .HEADLINE ^COMMAND DUP EMIT EMIT SPACE ^Query EMIT SPACE ." for help" ; \ headline when commanding : .COMMANDING ( -- ) S" COMMANDING" .HEADLINE ^Query EMIT SPACE ." to query help" ; \ display the headline : BANNER ( -- ) COMMANDING IF .COMMANDING ELSE .TEXT-ENTRY THEN ; \ display screen before displaying the document : .SCREEN ( -- ) PAGE BANNER .RULERS LINE#-SPACE TOP AT-XY ; \ document display BL VALUE "bl" \ to EMIT BL BL VALUE "cr" \ to EMIT 'CR BL VALUE "other" \ to EMIT other "invisible" character \ 32 displays as "bl" , 13 displays as "cr" : "INVISIBLE" ( c1 -- c2 ) CASE BL OF "bl" ENDOF 'CR OF "cr" ENDOF "other" SWAP ENDCASE ; : ?DISPLAY ( c1 -- c2) DUP BL 1+ < IF "INVISIBLE" THEN ; \ toggle visible and invisible "bl" AND "cr" : ~DISPLAY ( -- ) "cr" BL <> IF BL TO "bl" BL TO "cr" BL TO "other" ELSE .BL TO "bl" .CR TO "cr" .OTHER TO "other" THEN -1 top-old ! ; \ "highlighting" ' NOP VALUE (?MARK) : ?MARK ( c1 -- c2 ) (?MARK) EXECUTE ; \ erasers \ keep current line in screen within n lines of top : AIM ( c# n -- ) >R 0 C>L DUP line-now ! R> - 0 MAX topline ! ; \ erase to end of line : EraseEOL ( col -- ) PLANK SWAP - SPACES ; \ erase to end of text area : EraseEOS ( -- ) BOTTOM last-line @ >Y - 0 MAX 0 ?DO MAX-X SPACES CR LOOP ; \ display text line : LTYPE ( c# u -- ) TUCK T>MEM 0 ?DO COUNT ?DISPLAY ?MARK EMIT LOOP DROP EraseEOL ; \ much faster ltype by Marcel Hendrix: \ LINEWIDTH LEDGE + CONSTANT C/L \ 0 VALUE cnt \ CREATE lbuff 128 CHARS ALLOT \ : LTYPE ( c# u -- ) \ 0 TO cnt \ TUCK T>MEM \ 0 ?DO \ COUNT ?DISPLAY ?MARK \ lbuff cnt + C! 1 +TO cnt ( or: cnt 1+ TO cnt ) \ LOOP DROP ( u) \ lbuff cnt C/L 1- MIN TYPE \ ( u) EraseEOL ; \ line and page numbers ' NOP VALUE (.LINE#) ' NOP VALUE (.PAGE#) : ?LINE# ( -- ) (.LINE#) EXECUTE ; : ?PAGE# ( -- ) (.PAGE#) EXECUTE ; \ display line number : <.LINE#> ( l# -- l#) DUP 1+ 5 U.R SPACE ; \ calculate page number : PAGE-LINE ( l# -- p# n ) LINES/PAGE /MOD 1+ SWAP ; \ if first line of a page, display the page number : <.PAGE#> ( l# -- l# ) DUP PAGE-LINE IF DROP 3 SPACES ELSE 3 U.R THEN ; \ display line and page numbers? : ?MARGIN ( -- ) LINE#-SPACE IF ['] <.LINE#> TO (.LINE#) THEN PAGE#-SPACE IF ['] <.PAGE#> TO (.PAGE#) THEN ; \ display line#, line, page# : .TLINE ( l# l# -- l# ) ?LINE# LINESPEC LTYPE ?PAGE# CR ; \ which lines to display VARIABLE .start \ first VARIABLE .end \ last VARIABLE .mend \ override .end \ display some lines of text : .TLINES ( -- ) .start @ topline @ MAX 0 OVER >Y AT-XY .end @ .mend @ MAX last-line @ MIN BOTTOMLINE MIN OVER - 1+ 0 ?DO DUP .TLINE 1+ LOOP DROP top-old @ topline @ U< last-line @ last-old @ U< OR last-line @ BOTTOMLINE U< AND IF 0 last-line @ topline @ - 1+ TOP + AT-XY EraseEOS THEN ; \ formatting FALSE VALUE FORMAT-ALL \ true = format the entire document FALSE VALUE SAME \ true if line data hasn't changed VARIABLE line# \ line being formatted \ 'CR a special case : CReturn ( a -- ) line# @ TUCK 1+ LINE @ 2DUP <> IF U< IF LINES> ELSE <LINES THEN last-line @ .mend ! ELSE 2DROP DROP THEN ; \ formatting old ground? : ?SAME ( c# 'line -- ) FORMAT-ALL IF 2DROP ELSE @ = IF line# @ line-now @ OVER U< OVER LINELENGTH LINEWIDTH U< AND AND ?DUP IF 1- .end ! TRUE TO SAME THEN THEN THEN ; \ store a character position in the next line : LINE! ( c# -- ) line# DUP INCR @ LINE 2DUP ?SAME ! ; \ word wrap \ lines are wrapped by priority: \ 1. first CR up to LINEWIDTH+1 \ 2. last BL up to LINEWIDTH+1, allowing \ for LEDGE BLs beyond LINEWIDTH \ 3. at LINEWIDTH LINEWIDTH 1+ CONSTANT LINEWIDTH+ : WRAP ( c#1 a u -- c#2 ) 2DUP LINEWIDTH+ MIN \ allow 1+ column for CR 'CR -LEADING<> \ look for first CR IF NIP SWAP - 1+ + DUP CReturn DUP LINE! \ end of paragraph ELSE DROP DUP LINEWIDTH > \ else need to wrap? IF OVER LINEWIDTH+ \ allow 1+ column for BL BL -TRAILING<> ?DUP \ break on last BL IF DUP LINEWIDTH+ = \ at extra column? IF 2SWAP \ ( c# a u2 a u1 ) LINEWIDTH+ /STRING \ rest of LEDGE BL LEADING= NIP + \ add its leading BLs ELSE 2SWAP 2DROP \ else dump plank THEN NIP \ ( c# u ) ELSE DROP 2DROP LINEWIDTH \ no BLs THEN + DUP LINE! \ ( c# ) ELSE NIP + \ no need to wrap THEN THEN ; \ clean-up after formatting : DEJA? ( -- ) SAME IF last-line DUP @ line# @ 1- MAX SWAP ! doc-size @ last-line @ 1+ LINE ! ELSE last-line @ 1+ line# @ DUP last-line ! DUP .end ! 1+ doc-size @ OVER LINE ! 1+ SWAP 0LINES THEN ; \ the f word : FORMAT ( -- ) FALSE TO SAME line-now @ 1- 0 MAX DUP line# ! LINE @ BEGIN DUP DUP PLANK + doc-size @ UMIN OVER - T>MEM WRAP DUP doc-size @ = SAME OR UNTIL DROP DEJA? ; \ moving around in the document \ cursor right : RIGHT ( -- ) -DOC-END IF char-now INCR ELSE ?BEEP THEN ; \ cursor left : LEFT ( -- ) char-now @ IF char-now DECR ELSE ?BEEP THEN ; \ calculate the column of the current character : CPLACE ( -- col# ) char-now @ line-now @ LINE @ - ; \ calculate where to place the cursor in a line : >char-now ( cplace l# -- ) LINESPEC ROT 2DUP U< IF DROP 1- 0 MAX ELSE NIP THEN + char-now ! ; \ cursor up : UP ( -- ) line-now @ IF CPLACE line-now DUP DECR @ >char-now ELSE ?BEEP THEN ; \ cursor down : DOWN ( -- ) line-now @ last-line @ U< IF CPLACE line-now DUP INCR @ >char-now ELSE ?BEEP THEN ; \ text pushes and pulls \ number of characters to the end of the document : #>END ( a -- u ) TEXT - /CHAR U/ doc-size @ SWAP - ; \ suture text separated by u chars : JOIN ( u -- ) CHARS SPOT DUP UNDER+ OVER #>END CMOVE ; \ prepare to delete u characters : <#SLIDE ( u -- ) doc-size @ IF DUP JOIN NEGATE doc-size +! ELSE DEEP THEN ; \ prepare to delete character : <SLIDE ( -- ) 1 <#SLIDE -1 LINES+! ; \ make room for u characters : SPLIT ( u -- ) CHARS SPOT TUCK + OVER #>END CMOVE> ; \ prepare to insert u characters : #SLIDE> ( u -- ) DUP ROOM? IF DUP SPLIT doc-size +! ELSE DEEP THEN ; \ prepare to insert character : SLIDE> ( -- ) 1 #SLIDE> 1 LINES+! ; \ text input 0 VALUE PREVIOUS-KEY \ two keys need to enter command mode 0 VALUE VANQUISHED \ text character overwritten by ^command character \ put character into the document : OVERWRITE ( c -- ) char-now @ CMAX U< line-now @ LMAX U< AND IF SPOT C@ -DOC-END AND PREVIOUS-KEY ^COMMAND <> AND TO VANQUISHED SPOT C! doc-size DUP @ char-now DUP INCR @ UMAX SWAP ! FORMAT ELSE DEEP THEN ; \ insert character into the document : INSERT ( c -- ) 1 ROOM? last-line @ LMAX U< AND line-now @ LMAX 1- U< AND IF -DOC-END IF SLIDE> THEN OVERWRITE ELSE DEEP THEN ; \ delete character : DELETE ( -- ) -DOC-END IF <SLIDE FORMAT ELSE ?BEEP THEN ; \ delete the previous character : <DELETE ( -- ) char-now @ IF LEFT DELETE ELSE ?BEEP THEN ; \ Enter key \ inserting: put in a paragraph end : PARAGRAPH ( -- ) last-line @ LMAX 1- U< IF 'CR INSERT ELSE ?BEEP THEN ; \ overwriting: if not at document's end go to the next \ line, else insert a paragraph end : RETURN ( -- ) -DOC-END IF line-now @ LINE @ char-now ! DOWN ELSE way# @ 2 <> IF PARAGRAPH ELSE ?BEEP THEN THEN ; \ Tab CREATE TAB$ TABWIDTH DUP CHARS ?ALLOT SWAP BLANK \ #cols to next tab mark : NEXT-TAB ( -- n ) TABWIDTH col# @ TABWIDTH MOD - ; \ tab while inserting \ will sometimes fall short of the first tab mark but \ but will go to it with the next tab : NUDGE ( -- ) NEXT-TAB DUP ROOM? IF DUP >R #SLIDE> TAB$ SPOT R@ CMOVE R> DUP LINES+! char-now +! FORMAT ELSE DEEP THEN ; \ tab while overwriting : HOP ( -- ) -DOC-END IF NEXT-TAB char-now @ + line-now @ 1+ LINE @ MIN doc-size @ 1+ UMIN char-now ! ELSE NUDGE THEN ; \ jumps \ keep jumped to line within document : CONFINE ( l1 -- l2 ) 0 last-line @ CLAMP ; \ jump n lines : JUMP ( n -- ) DUP topline @ + CONFINE topline ! CPLACE SWAP line-now @ + CONFINE DUP line-now ! >char-now ; \ jump down : +JUMP ( u -- ) line-now @ last-line @ = IF DEEP ELSE JUMP THEN ; \ jump up : -JUMP ( u -- ) line-now @ 0= IF DEEP ELSE NEGATE JUMP THEN ; \ jump to the beginning of the line : <LEFT ( -- ) line-now @ LINE @ char-now ! ; \ jump to the end of the line : RIGHT> ( -- ) line-now @ DUP 1+ LINE @ 1- SWAP last-line @ = 1 AND + char-now ! ; \ jump up one screen : PAGE-UP ( -- ) LMAX/SCREEN -JUMP ; \ jump down one screen : PAGE-DOWN ( -- ) LMAX/SCREEN +JUMP ; \ jump to the start of the document : >BOF ( -- ) 0 char-now ! 0 line-now ! 0 topline ! ; \ jump to the end of the document : >EOF ( -- ) doc-size @ char-now ! last-line @ DUP line-now ! DUP .end ! DUP topline @ LMAX/SCREEN + 1- > IF 6 - DUP .start ! topline ! ELSE DROP THEN ; \ jump to current top screen line : >TOP ( -- ) topline @ line-now @ U< IF CPLACE topline @ DUP line-now ! >char-now ELSE ?BEEP THEN ; \ jump to current bottom screen line : >BOTTOM ( -- ) line-now @ DUP last-line @ U< SWAP BOTTOMLINE U< AND IF CPLACE last-line @ BOTTOMLINE MIN DUP line-now ! >char-now ELSE ?BEEP THEN ; \ ~insert \ toggle insert/overwrite : ~INSERT ( -- ) way# DUP @ 1 XOR SWAP ! BANNER ; \ find/replace CREATE S$ MAX-INPUT CHARS ALLOT \ search string CREATE R$ MAX-INPUT CHARS ALLOT \ replace string FALSE VALUE FOUND \ has search string been found? VARIABLE found-char \ where? VARIABLE slen \ length of search string VARIABLE spad> \ offset in PAD of found string VARIABLE rlen \ length of replace string \ does the string have an uppercase character? : UC? ( a u -- f ) 0 ?DO COUNT UPPER? IF DROP TRUE UNLOOP EXIT THEN LOOP DROP FALSE ; \ does the string have a lowercase character? : lc? ( a u -- f ) 0 ?DO COUNT lower? IF DROP TRUE UNLOOP EXIT THEN LOOP DROP FALSE ; \ does the string have both upper- and lower-case characters? : MIXED? ( a u -- f ) 2DUP UC? >R lc? R> AND ; ' 2DROP VALUE ?lcase \ "deferred" ?lcase \ make string lower case if NOT mixed : ?MIXED ( a u -- ) 2DUP MIXED? IF ['] 2DROP ELSE ['] lcase THEN TO ?lcase ?lcase EXECUTE ; \ look for searched string in search pad : LOOKING ( a u -- ) 2DUP ?lcase EXECUTE S$ slen @ SEARCH NIP ?DUP IF TO FOUND SEARCH-PAD - spad> ! ELSE DROP THEN ; \ you can't go home again (i.e. you can go home once) TRUE VALUE OK-TO-GO-HOME \ ok to loop back to BOF? VARIABLE snow \ char# now at SEARCH-PAD \ move some text to the search pad : T>SPAD ( a u -- spad u ) T>MEM >R SEARCH-PAD R@ CMOVE SEARCH-PAD R> ; \ search text for a string, if it isn't found, \ continue to look from the beginning of the document : SWEEP ( -- ) TRUE TO OK-TO-GO-HOME doc-size @ >R char-now @ 1+ DUP R@ 1+ slen @ - U< AND BEGIN DUP snow ! DUP DUP GULP + R@ UMIN DUP >R OVER - T>SPAD LOOKING R> R@ = OK-TO-GO-HOME AND IF DROP 0 FALSE TO OK-TO-GO-HOME ELSE GULP slen @ 1- - + THEN DUP char-now @ 1+ U< OK-TO-GO-HOME OR 0= FOUND OR UNTIL R> 2DROP ; \ if the string found identify the starting character \ if necessary ensure that it can be displayed : ?FOUND ( -- ) FOUND IF snow @ spad> @ + DUP char-now ! DUP found-char ! 6 AIM ELSE ?BEEP THEN ; \ the seek word : SEEK ( -- ) FALSE TO FOUND slen @ ?DUP IF doc-size @ 1+ U< IF S$ slen @ ?MIXED SWEEP THEN THEN ?FOUND ; \ seek with prompt \ empty string seeks the previous string : SEEK? ( -- ) BANNER-LINE RUB ." Find:" S$ MAX-INPUT ACCEPT ?DUP IF slen ! THEN SEEK BANNER ; \ was something found here? : POINT? ( -- f ) FOUND char-now @ found-char @ = AND ; \ adjust for difference between sought and replace lengths : SLIDE ( n -- ) DUP 0< IF NEGATE <#SLIDE ELSE #SLIDE> THEN ; \ replace : PUT ( -- ) POINT? rlen @ DUP >R AND R@ slen @ - TUCK 0 MAX ROOM? AND IF ?DUP IF DUP SLIDE LINES+! THEN R$ SPOT R@ CMOVE FORMAT ELSE DEEP THEN R> DROP FALSE TO FOUND ; \ replace with prompt \ empty string subsitutes the previous string : PUT? ( -- ) POINT? IF BANNER-LINE RUB ." Replace with:" R$ MAX-INPUT ACCEPT ?DUP IF rlen ! THEN PUT BANNER ELSE ?BEEP THEN ; \ insert text from the command line : STUFF ( -- ) BANNER-LINE RUB ." Wedge in:" PAD MAX-INPUT ACCEPT DUP ?DUP ROOM? AND IF DUP SLIDE DUP LINES+! DUP PAD SPOT ROT CMOVE FORMAT char-now +! THEN BANNER ; \ marking a block VARIABLE was \ way# before marking VARIABLE bstart \ where marking originated VARIABLE .bstart \ beginning of marked text VARIABLE .bend \ end of marked text VARIABLE blength \ number of characters in the block VARIABLE btop \ top block line to display \ keeping the block within the document, give the block's size : BLOCK-IN ( -- n ) char-now DUP @ doc-size @ 1- UMIN TUCK SWAP ! ; \ if marking, define marked area : <BLOCK> ( -- ) BLOCK-IN bstart @ 2DUP UMIN .bstart ! UMAX .bend ! line-old @ line-now @ 2DUP MIN .start ! MAX .end ! ; \ starting character and length of the block : MARKED ( -- c# u ) .bstart @ .bend @ OVER - 1+ ; \ start and end lines of the block : <LL> ( -- l1 l2 ) .bstart @ 0 C>L .bend @ OVER C>L ; \ would like a Standard way to highlight: GLOW ? : MARK ( a c -- a c ) OVER 1- .bstart @ CHARS TEXT + .bend @ CHARS TEXT + BETWEEN IF >UPPER THEN ; ' NOP VALUE (?BLOCK) : ?BLOCK (?BLOCK) EXECUTE ; \ start marking : +MARK ( -- ) way# DUP @ was ! 2 SWAP ! ['] MARK TO (?MARK) ['] <BLOCK> TO (?BLOCK) BLOCK-IN bstart ! topline @ btop ! -1 top-old ! BANNER ; \ leave marking : -MARK ( -- ) <LL> .end ! .start ! was @ way# ! ['] NOP DUP TO (?MARK) TO (?BLOCK) BANNER ; \ copy, cut, embed \ fits into allotted space? : SMALL? ( u -- flag ) POCKET-SIZE 1+ U< ; \ write larger block to a temporary file : >PURSE ( a u -- ) S" temp.wnk" CREATE-WRITE BUF>FILE ; \ copy marked : APE ( -- ) MARKED DUP blength ! T>MEM DUP SMALL? IF POCKET SWAP CMOVE ELSE >PURSE THEN -MARK ; \ copy and delete : CUT ( -- ) APE .bend @ .bstart @ DUP 0 C>L DUP .start ! line-now ! DUP char-now ! - 1+ DUP <#SLIDE NEGATE LINES+! FORMAT btop @ topline @ U< IF btop @ topline ! THEN last-line @ .end ! ; \ read large cut block : PURSE> ( u -- ) S" temp.wnk" OPEN-READ ABORT" OPEN-FILE problem" TO FILE-ID SPOT SWAP FILE>BUF ; \ paste copied or cut block : PASTE ( -- ) blength @ DUP DUP ROOM? AND <LL> SWAP - last-line @ + LMAX U< AND IF DUP >R #SLIDE> R@ SMALL? IF POCKET SPOT R@ CMOVE ELSE R@ PURSE> THEN R@ LINES+! FORMAT R> char-now +! char-now @ LMAX/SCREEN 2/ AIM ELSE DEEP THEN ; \ print \ some code to try if you can invoke printing \ not tested with LF \ VARIABLE spacing \ VARIABLE pline \ define >PRN and PRN> according to your system \ : >PRN ... ; \ enable printing \ : PRN> ... ; \ return from printing \ : SPACED ( u) spacing ! ; \ : CRs ( n) 0 ?DO CR LOOP ; \ : FF 12 EMIT ; \ : .PAGE ( n -- ) PMARGIN LINEWIDTH + SPACES 1+ . ; \ : NEWPAGE ( n -- ) FF 3 CRs .PAGE 3 CRs ; \ : ?NEWPAGE ( -- ) \ pline @ ?DUP \ IF LINES/PAGE spacing @ / /MOD SWAP 0= \ IF NEWPAGE ELSE DROP THEN \ ELSE 6 CRs THEN ; \ : TPRINT ( a u -- ) \ T>MEM \ 0 ?DO COUNT DUP 'CR > AND EMIT LOOP \ DROP ; \ : <print> ( start end -- ) \ >PRN \ 0 pline ! OVER - 1+ 0 \ ?DO ?NEWPAGE PMARGIN SPACES \ DUP LINESPEC TPRINT spacing @ CRs pline INCR 1+ \ LOOP DROP FF \ PRN> ; \ : printing ( n -- ) 0 last-line @ <print> ; \ : bprinting ( n -- ) <LL> <print> ; \ : (PRINT) ( -- ) 1 SPACED printing ; \ : BPRINT ( -- ) 1 SPACED bprinting ; \ : (2PRINT) ( -- ) 2 SPACED printing ; \ : 2BPRINT ( -- ) 2 SPACED bprinting ; \ file i/o \ request filename \ a u1 is the prompt, u2 is the number of characters entered : FILENAME ( a u1 -- u2 ) BANNER-LINE RUB TYPE PAD 1+ MAX-INPUT ACCEPT DUP PAD C! ; \ number of chars to dot : >DOT ( s -- n ) COUNT [CHAR] . -TRAILING<> NIP ; \ add extension? : ?+WNK ( s -- ) DUP >DOT 0= IF S" .wnk" ROT S+! ELSE DROP THEN ; \ move name to filename : PAD$>FILENAME$ ( -- ) PAD FILENAME$ SMOVE ; \ file? : GET-FILENAME ( -- a u) S" Filename: " FILENAME IF PAD$>FILENAME$ ELSE ?DE-ALLOCATE QUIT THEN FILENAME$ ?+WNK FILENAME$ COUNT ; \ save file : FSAVE ( s -- ) COUNT CREATE-WRITE TEXT doc-size @ BUF>FILE ; \ save the document : SAVE-DOC ( -- ) FILENAME$ FSAVE ; \ save a marked block : SAVE-MARKED ( -- ) S" Save marked to:" FILENAME IF PAD ?+WNK PAD COUNT CREATE-WRITE MARKED T>MEM BUF>FILE THEN BANNER ; \ read in the document from a file : READ-DOC ( -- ) FILE-ID FILE-SIZE ABORT" FILE-SIZE problem" OVER CMAX U< 0= OR ABORT" FILE TOO BIG" doc-size ! TEXT doc-size @ FILE>BUF ; \ if there's a file read it, else create a file : ?READ-DOC ( a u -- ) 2DUP R/O BIN OPEN-FILE IF DROP CREATE-WRITE FCLOSE ELSE TO FILE-ID 2DROP READ-DOC THEN ; \ prompt for a filename, try to read the file : GET-DOCUMENT ( -- ) GET-FILENAME ?READ-DOC ; \ inVest file : FROM> ( -- ) S" Read from:" FILENAME IF PAD ?+WNK PAD COUNT OPEN-READ IF DROP BANNER-LINE RUB PAD COUNT TYPE 2 SPACES ." ?? " WAIT ELSE TO FILE-ID BANNER-LINE RUB FILE-ID FILE-SIZE ABORT" FILE-SIZE problem" OVER ROOM? 0= OR IF ." NOT ENOUGH ROOM " DROP WAIT ELSE DUP #SLIDE> SPOT OVER FILE>BUF TRUE TO FORMAT-ALL FORMAT FALSE TO FORMAT-ALL char-now +! char-now @ LMAX/SCREEN 2/ AIM THEN THEN THEN BANNER ; \ do a backup : BACKUP ( -- ) PAD PAD-SPACE BLANK FILENAME$ PAD SMOVE PAD >DOT ?DUP IF 1- PAD C! THEN S" .bak" PAD S+! PAD FSAVE ; \ if the file has some data, back it up : ?BACKUP ( -- ) doc-size @ IF BACKUP THEN ; \ change filename : ~NAME ( -- ) S" Change filename to:" FILENAME IF PAD$>FILENAME$ FILENAME$ ?+WNK FILENAME$ FSAVE THEN BANNER ; \ scrolling \ scroll up one line : SCRUP ( -- ) topline DUP INCR @ .start ! last-line @ .end ! ; \ scroll down one line : SCROWN ( -- ) topline DUP DECR @ .start ! last-line @ .end ! ; \ do I need to scroll? : SCROLL? ( row#1 -- row#2 ) DUP BOTTOM > IF SCRUP DROP BOTTOM topline @ top-old ! ELSE DUP TOP < IF SCROWN 1+ topline @ top-old ! THEN THEN ; \ where to put the cursor : CURSOR! ( -- ) char-now @ DUP line-now @ 1- 0 MAX C>L DUP line-now ! DUP >Y SCROLL? -ROT LINE @ - DUP col# ! LINE#-SPACE + SWAP CURSOR> 2! ; \ should I redisplay the entire text area? : ?FRAME ( -- ) topline @ top-old @ <> IF topline @ .start ! last-line @ .end ! THEN ; \ .status \ display of and the statistic : .OF ( n -- ) [CHAR] / EMIT U. ; \ display max : .MAX ( n -- ) [CHAR] m EMIT U. SPACE ; \ display status line : .STATUS ( -- ) STATUS-LINE RUB [CHAR] C EMIT SPACE char-now @ 1+ U. doc-size @ 1+ .OF CMAX .MAX last-line @ line-now @ PAGE#-SPACE IF 2DUP THEN [CHAR] L EMIT SPACE 1+ U. 1+ .OF LMAX .MAX PAGE#-SPACE IF [CHAR] P EMIT SPACE PAGE-LINE DROP U. PAGE-LINE DROP .OF THEN ." Col " col# @ 1+ U. ; \ .result : .RESULT ( -- ) CURSOR! ?FRAME .TLINES .STATUS CURSOR> 2@ AT-XY ; \ begin and end \ virgin mother \ reserve memory for text and lines data : MOTHER ( -- ) ALLOCATING IF DO-ALLOCATE ELSE DO-ALLOT THEN 0>LMAX ; : VIRGIN ( -- ) MOTHER 0 doc-size ! 0 last-line ! 0 char-now ! 0 line-now ! 0 topline ! 0 way# ! BL TO "bl" BL TO "cr" BL TO "other" FALSE TO FOUND ['] NOP DUP TO (?MARK) TO (?BLOCK) ; \ yes, I wrote most of this : (c) ( -- ) PAGE 13 12 AT-XY ." LF v1.0 " ." Copyright 1997 Leo Wong. All rights reserved." ; \ our story begins : START ( -- ) VIRGIN ?BEEPS (c) ?MARGIN GET-DOCUMENT .SCREEN FORMAT .RESULT ?BACKUP ; \ finish FALSE VALUE DONE \ true if leaving LF \ offer to save before leaving : FINISH ( -- ) BANNER-LINE RUB ." Save " .FILENAME ." (Y/n)?" IN-KEY AND BL OR [CHAR] n <> IF SAVE-DOC THEN TRUE TO DONE ; \ help - designed for 25 lines \ leave help : BACK-TO-TEXT ( -- ) .SCREEN topline @ .start ! last-line @ .end ! .RESULT BANNER ; \ show help : HELP ( -- ) PAGE 4 0 AT-XY ." when in TEXT ENTRY:" 0 1 AT-XY ^COMMAND DUP EMIT EMIT ." enter COMMANDs" 4 3 AT-XY ." when COMMANDing:" 0 4 AT-XY ^COMMAND EMIT ." return to TEXT ENTRY" 0 6 AT-XY ^Find-string EMIT ." Find" 0 7 AT-XY ^find-aGain EMIT ." find aGain" 0 8 AT-XY ^Replace EMIT ." Replace" 0 9 AT-XY ^replace-Too EMIT ." replace Too" 0 11 AT-XY ^(un)mark(1) EMIT ." mark<->unmark" 0 12 AT-XY ^Copy EMIT ." Copy marked" 0 13 AT-XY ^Delete EMIT ." Delete char / cut marked" 0 14 AT-XY ^Embed EMIT ." Embed (paste) copied/cut" 0 16 AT-XY ^inVest EMIT ." inVest (insert) a file" 0 17 AT-XY ^Wedge EMIT ." Wedge in text" 0 19 AT-XY ^Alter-input EMIT ." insert<->overwrite" 0 20 AT-XY ^Show EMIT ." show<->hide spaces/CRs" 0 22 AT-XY ^change-name EMIT ." change filename" 38 0 AT-XY ." when COMMANDing:" 34 2 AT-XY ." cursor moves:" 34 4 AT-XY ^right EMIT ." right" 34 5 AT-XY ^left EMIT ." left" 34 6 AT-XY ^up EMIT ." up" 34 7 AT-XY ^down(1) EMIT ." or " ^down(2) EMIT ." down" 34 9 AT-XY ^1st-col EMIT ." first column" 34 10 AT-XY ^last-col(1) EMIT ." last column" 34 12 AT-XY ^page-up EMIT ." page up" 34 13 AT-XY ^page-down(1) EMIT ." page down" 34 15 AT-XY ^TOP EMIT ." top of page" 34 16 AT-XY ^BOP(1) EMIT ." bottom of page" 34 18 AT-XY ^BOF EMIT ." beginning of document" 34 19 AT-XY ^EOF EMIT ." end of document" 34 21 AT-XY ^Save EMIT ." Save document" 34 22 AT-XY ^good-Bye EMIT ." Bye to LF" 14 24 AT-XY ." Press a key to leave this screen" IN-KEY 2DROP BACK-TO-TEXT ; \ most commands depend on whether you're inserting, \ overwriting, or marking text \ Insert Overwrite Marking 3 WAYS <.WAY> .INSERT .OVERWRITE .MARKING 3 WAYS CHARACTER INSERT OVERWRITE DEEP 3 WAYS ENTER PARAGRAPH RETURN RETURN 3 WAYS BACKSPACE <DELETE <DELETE ?BEEP 3 WAYS TABITHA NUDGE HOP ?BEEP 3 WAYS FIND-1ST SEEK? SEEK? ?BEEP 3 WAYS FIND-AGAIN SEEK SEEK ?BEEP 3 WAYS REPLACE PUT? PUT? ?BEEP 3 WAYS REPLACE-TOO PUT PUT ?BEEP 3 WAYS ~MARK +MARK +MARK -MARK 3 WAYS COPY ?BEEP ?BEEP APE 3 WAYS EMBED PASTE PASTE ?BEEP 3 WAYS DELE DELETE DELETE CUT 3 WAYS WEDGE STUFF STUFF ?BEEP 3 WAYS QUERY-HELP HELP HELP HELP 3 WAYS ~INPUT ~INSERT ~INSERT ?BEEP 3 WAYS ~SHOW ~DISPLAY ~DISPLAY ~DISPLAY 3 WAYS SAVING SAVE-DOC SAVE-DOC SAVE-MARKED 3 WAYS INVEST FROM> FROM> ?BEEP \ 3 WAYS PRINT (PRINT) (PRINT) BPRINT \ 3 WAYS 2PRINT (2PRINT) (2PRINT) 2BPRINT \ there had to be a (.WAY) ' <.WAY> TO (.WAY) \ control keys \ control-key handler : CONTROL-KEY? ( u -- ) CASE 'CR OF ENTER ENDOF BS OF BACKSPACE ENDOF HT OF TABITHA ENDOF ?BEEP ENDCASE ; \ command mode \ toggle text-entry and command modes : ~COMMANDING ( -- ) COMMANDING 0= DUP TO COMMANDING IF BACKSPACE way# @ 1 = VANQUISHED AND ?DUP IF INSERT LEFT THEN ELSE way# @ 2 = IF -MARK THEN THEN BANNER .RESULT ; \ am I commanding? : COMMAND-MODE? ( c -- f ) ^COMMAND = ^COMMAND PREVIOUS-KEY = AND COMMANDING OR ; \ so that the space bar can be used in command mode : SPACE-BAR ( -- ) BL CHARACTER ; \ command-mode key handler : COMMAND-MODE ( c1 -- c2 ) >UPPER CASE \ cursor keys ^left OF LEFT ENDOF ^right OF RIGHT ENDOF ^up OF UP ENDOF ^down(1) OF DOWN ENDOF ^down(2) OF DOWN ENDOF ^down(3) OF DOWN ENDOF ^1st-col OF <LEFT ENDOF ^last-col(1) OF RIGHT> ENDOF ^last-col(2) OF RIGHT> ENDOF ^page-up OF PAGE-UP ENDOF ^page-down(1) OF PAGE-DOWN ENDOF ^page-down(2) OF PAGE-DOWN ENDOF ^BOF OF >BOF ENDOF ^EOF OF >EOF ENDOF ^TOP OF >TOP ENDOF ^BOP(1) OF >BOTTOM ENDOF ^BOP(2) OF >BOTTOM ENDOF \ function keys ^Find-string OF FIND-1ST ENDOF ^find-aGain OF FIND-AGAIN ENDOF ^Replace OF REPLACE ENDOF ^replace-Too OF REPLACE-TOO ENDOF ^(un)mark(1) OF ~MARK ENDOF ^(un)mark(2) OF ~MARK ENDOF ^Delete OF DELE ENDOF ^Wedge OF WEDGE ENDOF ^inVest OF INVEST ENDOF ^Copy OF COPY ENDOF ^Embed OF EMBED ENDOF ^COMMAND OF ~COMMANDING ENDOF ^Query OF QUERY-HELP ENDOF ^Alter-input OF ~INPUT ENDOF ^Show OF ~SHOW ENDOF ^change-name OF ~NAME ENDOF ^Save OF SAVING ENDOF ^good-Bye OF FINISH ENDOF \ -control-keys -Enter-key(1) OF ENTER ENDOF -Enter-key(2) OF ENTER ENDOF -Backspace-key(1) OF BACKSPACE ENDOF -Backspace-key(2) OF BACKSPACE ENDOF -Tab-key(1) OF TABITHA ENDOF -Tab-key(2) OF TABITHA ENDOF \ space in command mode BL OF SPACE-BAR ENDOF DUP CONTROL-KEY? ENDCASE 0 ; \ process \ get ready to process a keyboard event : PROCESS> ( -- ) line-now @ DUP line-old ! 1- topline @ DUP top-old ! last-line @ DUP last-old ! CLAMP DUP .start ! 1- DUP .end ! .mend ! ; \ process a character : KEYBOARD-CHARACTER ( c -- ) DUP COMMAND-MODE? IF COMMAND-MODE ELSE DUP BL LAST-DISPLAYABLE BETWEEN IF DUP CHARACTER ELSE DUP CONTROL-KEY? THEN THEN DROP ; \ process other keyboard event (such as a cursor key) \ if using ekey : OTHER-KEYBOARD-EVENT ( u -- ) DEEP ; \ handle a keyboard event : PROCESS-KEY ( c flag -- ) PROCESS> 2DUP AND COMMANDING OR >R \ if commanding, hide key IF KEYBOARD-CHARACTER ELSE OTHER-KEYBOARD-EVENT THEN R> TO PREVIOUS-KEY ; \ LF, an NPBP ANS Forth word processor : LF ( -- ) FALSE TO COMMANDING FALSE TO DONE START BEGIN IN-KEY PROCESS-KEY ?BLOCK .RESULT DONE UNTIL ?DE-ALLOCATE PAGE ; LF \ start LF