forth/lf.4th

1765 lines
46 KiB
Forth
Executable File

\ 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