# TP3 editor — structure in pseudo-code The editor is a tight key-dispatch loop over one big CR-terminated buffer, with the current line edited in a small "line cache" that is flushed back on every move. This mirrors the real TPSRC5/6 structure (entry `editor2`, `edmain`, `edproc`, `eflush`). --- ## 1. Entry — `Run(drive, fileName, changed)` ``` Run(drv, fname, changed): copy drv, fname into editor globals text[txend] = CR; text[txend+1] = LF; txend += 1 statObsolete = 0 // force status-line repaint dislin = 1 // force full redraw sepptr = SEPARATOR_TABLE // "<>,[].*+-/$:=(){}^#'" + space ClrScr() setPosFromArg(inPos) // inPos=FFFF 'keep' | error-line offset mainLoop() return (changed updated from internal modified flag) ``` --- ## 2. Main loop — `mainLoop()` ``` loop: redisplay() // redraw window (skip if key pending!) if statera != 0: // erase stale status junk eraseChars(statera, LowVideo); statera = 0 statusLine() // paint "Line n Col n Insert X:F" key = getKey() cmd = dispatch(key) // returns action or NOT_A_COMMAND if cmd == NOT_A_COMMAND: insertChar(key) // enter printable char into line buf elif cmd == UNKNOWN: nothing // TP3 ignores; loop again else: if cmd.markChanged: modified = true; codeInvalid = true save currentPos into posFIFO // ^Q-P last-position stack (8 bytes) cmd.handle() // each handler returns to the loop if cmd == CMD_QUIT: return ``` --- ## 3. Dispatch — table-driven `dispatch(key)` ``` dispatch(key): if key >= 0x20 and key != DEL: return NOT_A_COMMAND cmdBuf = [ length=1, key ] loop: found = searchTable(cmdBuf, TABLE_1, exactMatch) // arrows/ESC seqs if not found: found = searchTable(cmdBuf, TABLE_2, maskSecond&0x1F) // ^Q + 'd' == ^Q-D if found == NEED_MORE_KEYS: echo(cmdBuf) // show "^K" while typing key2 = getKey(); cmdBuf += key2; continue if found == COMMAND: cmdNo = ...; return cmdTable[cmdNo] // 45 entries, ejmptab order return UNKNOWN ``` Command numbers in manual order (the `MSB` of the address = "changed" flag): ``` CR, Left, Left, Right, ^A wordLeft, ^F wordRight, ^E up, ^X down, ^W scrollUp, ^Z scrollDown, ^R pageUp, ^C pageDown, ^Q-S lineStart, ^Q-D lineEnd, ^Q-E pageTop, ^Q-X pageBottom, ^Q-R textStart, ^Q-C textEnd, ^Q-B blockStart, ^Q-K blockEnd, ^Q-P lastPos, ^V insertToggle, ^N insertLine, ^Y deleteLine, ^Q-Y deleteToEOL, ^T deleteWord, ^G deleteRight, DEL deleteLeft, [2nd DEL], ^K-B markBlockStart, ^K-K markBlockEnd, ^K-T markWord, ^K-H hideBlock, ^K-C copyBlock, ^K-V moveBlock, ^K-Y deleteBlock, ^K-R readBlock, ^K-W writeBlock, ^K-D quit, TAB autoIndent, ^Q-I indentToggle, ^Q-L restoreLine, ^Q-F find, ^Q-A replace, ^L repeatSearch, ^P literalPrefix ``` --- ## 4. Text model ``` const SEPARATORS = "<>,[].*+-/$:=(){}^#'" text : array[0..62903] of CR-terminated lines, no trailing CR at buffer end lineBuf : small cache holding the current line under edit lnpos : cursor offset inside lineBuf edpos : absolute cursor offset inside text txbeg,txend : used span flush(): // eflush - write lineBuf back into text end = lineEnd(text, edpos) + 1 clamp block markers bkbeg/bkend into [edpos..end] (recompute from line buf) delta = oldLineLen - newLineLen if delta != 0: resizeText(end, delta) // echgsize moves the tail copy lineBuf -> text[edpos..end-1] remap bkbegl/bkendl (lineBuf positions) -> bkbeg/bkend (text positions) // Every handling that moves the cursor calls flush() first. forMove(apply): // edn, eup, pageUp...: move + auto-scroll if rowJustChanged: flush() edpos = apply(edpos) load lineBuf from text at edpos; adjust disbeg (scrolling) as needed if target line out of window: set dislin for full redraw + scroll ``` --- ## 5. Editing commands ``` insertChar(ch): // edput if linePos == lineEnd0: return // "Line too long - CR inserted" if insertMode: makeRoomInLine() // einsch (shift tail of lineBuf right) else: (overwrite) lineBuf[lnpos] = ch; lnpos++ redrawLine(); reposition() insertLine(): // CR / ^N (elinbrk) flush(); split text at edpos: move [edpos..endOfLine] down to next line; cursor to start of new line deleteRight(): // ^G if at line end: delete the CR (join with next line), efl2 else: delete char, collapse, redraw deleteLeft() (DEL): if pos==0: join previous line else: delete char at pos-1 deleteLine(): // ^Y flush(); remove chars [lineStart..lineEndInclCarriageReturn] cursor to start of next line; redraw deleteToEOL(): // ^Q-Y from lnpos: [lnpos..lineEnd] = spaces / removed (fill with blanks is how TP3 draws it, then tail removed on next flush) deleteWord(): // ^T move right past word (separator scan), delete scanned chars wordLeft()/wordRight(): // ^A / ^F scan SEPARATORS vs word chars (etstsep) ``` --- ## 6. Block commands ``` markBlockStart(): bkbeg = edpos ('Marked' your position) markBlockEnd() : bkend = edpos markWord() : mark word under cursor (^K-T) hideBlock() : bkhide ^= 1 // invert display or not testBlock(): // etstblk if bkhide: restore hidden block first; beep; fail if bkbeg==bkend or unset: beep; fail copyBlock(): // ^K-C testBlock(); flush() makeGap(bkend, bklen); copy [bkbeg..bkend) into the gap (echgsize) move block markers to the copy; redraw moveBlock(): // ^K-V testBlock(); flush() save block tail [bkend..], then delete [bkbeg..bkend), then reinsert tail at new cursor -> effectively moves the block text; restore line deleteBlock(): // ^K-Y testBlock(); flush() delete [bkbeg..bkend); collapse; block markers cleared; modified=true readBlock(file): // ^K-R open file; read bytes; insert chunk at cursor; close; redraw on 'file too big' (etsterr) -> error path writeBlock(file): // ^K-W if bkhide: un-hide first open/create file; write [bkbeg..bkend); append ^Z? (TP3 writes raw) close; error path on failure quit(): // ^K-D (ekd) restore screen attributes; return to shell (changed already tracked) ``` --- ## 7. Search — `find()` ``` find(mode): // ^Q-F find / ^Q-A replace status prompt: "Find word:" (or "...and replace with:") pattern -> srword buffer, length -> srword1 if replace: prompt again, summary length -> srrepl1, text -> srrepl2 ask options: prompt, keys set sropt bitfield: B=0x10 backward, G global, n=Nth occurrence, U ignore case, W whole word, N no-confirm (replace) scan from cursor: match candidates by walking CR lines; compare case per U flag; test word boundaries per W flag (SEPARATORS) remember candidate position + length if G: continue after each found position (repeat through whole text) if Nth: skip n-1 matches if found: if replace and not N: confirm "Replace (Y/N):" -> ^Y/^X or 'Y' doReplace(candidatePos, oldLen, newText, newLen) echgsize by delta; write replacement; modified=true cursor to end of match (TP3 leaves cursor after the word) if G: keep prompting/looping until text exhausted else: beep, "Not found", restore status repeatSearch(): // ^L rerun last find/replace with stored pattern+options, no prompts ``` --- ## 8. Status line & display ``` statusLine(): // estat if keyPending: return // never paint while a key waits col = horscr + phcol if statObsolete: clear line; if width >= 56: print fileName right-aligned write "Line ", write "Col ", " ", (Insert|Overwrite) " ", (Indent|" ") always refresh Col number (3-digit, low video); Line only when moved monitorRedraw(): // edmalin over rows disbeg..disbeg+22 for each visible row: for chars from current line: attr = blockActive(pos in [bkbeg,bkend]) ? HIGH_VIDEO : NORMAL if bkhide: no block highlighting write char; stop at CR or row width pad/blank; handle horizontal scroll (horscr) for long lines never repaint if key is pending between rows scrollUp()/scrollDown(): // ^W/^Z - move disbeg, full redraw pageUp()/pageDown() : // ^R/^C move disbeg by 23 lines; clamp; full redraw (eredall) moveTo(row, col): // setcpos via BIOS row/col repositions cursor physically; keeps phrow/phcol in sync ``` --- ## 9. Literal prefix & abort ``` literalPrefix(): // ^P echo "^"; ch = getKey(); insert ch literally (even control chars) abort(): // ^U or ESC cancel current find/replace prompt / block operation; restore old state ``` --- ## 10. Error/edge handling ``` "Line too long - CR inserted": when insert would exceed lineend0 -> beep, treat as CR (line split) "File too big": readBlock when text would overflow txend limit -> etsterr keyPending(): poll keyboard stat (kbdstat) without blocking coldStart/editor init and modes: insert default ON, indent depends ^Q-I ```