There was some semi-recent interest in the design of a "visual" block (or screen?) editor, and I stumbled across an intact copy of my first block editor and some trace of its evolution since that initial version, and I thought I'd share. Maybe someone will find it helpful, or at least interesting.
Some background: This was for a standalone x86 PC Forth implementation along the Loeliger model, with some influence from cmForth and Pygmy Forth, and is probably very idiosyncratic, but the basic ideas should tend to apply anywhere. Blocks are 1k, so there's no concept of a "screen" of source text separate from a block.
The original block editor appears to have had the ability to copy a line at a time within a block, to erase a line, and to replace a line entirely. There was no editing within a line, it was a pure replacement operation.
Code:
( Block editor) HEX VARIABLE CURBLOCK
: LINE ( n - a) DUP 10 >= 6 AND - 40 * CURBLOCK @ BLOCK + ;
: showline ( a - a+40) 40 BEGIN DUP IF 1- SWAP
DUP C@ CHROUT 1+ SWAP WHILE DROP ;
: showlines ( a - a+400) 10 BEGIN DUP IF 1- 0F OVER -
DUP 0A < IF 20 ECHO THEN . 7C ECHO SWAP showline SWAP
7C ECHO CR WHILE 2DROP ;
: SHOW DECIMAL CLRSCR 0 LINE showlines ;
: COPY ( src dst) LINE SWAP LINE SWAP UPDATE
40 CMOVE SHOW ;
: (CLEAR) ( a) UPDATE 2020 SWAP 20 WBLOCKFLOOD ;
: CLEAR ( n) LINE (CLEAR) SHOW ;
: CLEARALL 10 BEGIN DUP IF 1- DUP CLEAR WHILE DROP ;
: REPLACE ( n) LINE CR INLINE DUP (CLEAR) TIB @ SWAP
TIB# @ 40 MIN CMOVE 0 TIB# ! SHOW ;
: EDIT ( n) CURBLOCK ! SHOW ;
From what I recall, editing code with this was a struggle. Especially since INLINE only had backspace for input editing. For that matter, navigating between blocks was more difficult than I particularly wanted. Moving blocks around was painful, moving lines of text between blocks even more so, and for some reason I seem to recall only having one disk in use for Forth at the time, hence part of why the code is so terse and dense within the block.
At some point I realized that I could make a simple loop to watch the keyboard for page-up, page-down, and escape, and use this to navigate through the available blocks without having to constantly re-type "<number> EDIT" all the time. And being able to switch between two arbitrary blocks can be very handy, either because you're using "shadow blocks" for documentation, or because you're trying to figure out some problem that requires looking at two widely-separate parts of the system, or because you have a key that copies the contents of one block to the other. I seem to no longer have the original version of this "block browser", but the remaining part is still interesting:
Code:
( block browser ) HEX VARIABLE saveblock
: showblock 0 0 GOTOXY 0 LINE showlines
." Block " CURBLOCK @ . ." " CR ;
: nextblock CURBLOCK DUP @ 1+ SWAP ! ;
: prevblock CURBLOCK DUP @ 1- 0 MAX SWAP ! ;
: swapblock saveblock @ CURBLOCK @ saveblock ! CURBLOCK ! ;
: storeblock CURBLOCK @ saveblock ! ;
: copyblock CURBLOCK @ BLOCK saveblock @ BLOCK UPDATE
400 CMOVE ;
: check ( c a) 2DUP W@ <> IF 6 + EXIT THEN
SWAP R> 2DROP 2 + @ EXECUTE 0 ;
: checkall ( c a) BEGIN DUP W@ IF check WHILE 2DROP 1 ;
The dispatch logic in check and checkall is more than a bit sketchy, especially the way that check can force a return from checkall. The toplevel logic that went with this would have been in the same block, and probably looked somewhat like this (written "off the cuff", and not tested):
Code:
VARIABLE browsetable DICTTOP DUP @ 4 - SWAP !
4900 W, ' prevblock , 5100 W, ' nextblock , 3F00 W, ' swapblock ,
0 W,
: browse DECIMAL CLRSCR BEGIN showblock SKEY browsetable checkall UNTIL ;
Note that the browsetable format is 16 bits of SKEY result followed by a 32-bit execution token, followed by a zero in place of the SKEY (EKEY by any other name?) value. From here it doesn't take much to come to the concept of a dedicated "exit" key, maintaining a position within the current block, using GOTOXY to set the cursor to that position, having "printable" keypress events insert text into the block, and so on. It's a bit less than three more blocks of code (spread out over four in my case). A later version of the editor used a linked-list, so that new keys could be defined later on. The same mechanism was then used for a rewrite of INLINE, though it did not progress to the point of any input editing beyond backspace.
If I were to do it all over today, I'd probably start with a toplevel control structure derived from the third version of the editor and basic display of and navigation within and between blocks, then immediately the editing bits. No point to writing a line-oriented editor when I can go straight to one that I find more comfortable to work with... Also, I'd probably take the time to add better input editing to INLINE, and possibly even an input history...