Files
PLASMA/src/toolsrc/ed.pla
T
2025-04-06 22:45:56 -07:00

1605 lines
42 KiB
Plaintext
Executable File

//=====================================
//
// Text Editor
//
//=====================================
include "inc/cmdsys.plh"
include "inc/args.plh"
include "inc/fileio.plh"
include "inc/conio.plh"
include "inc/vblank.plh"
sysflags restxt1|resxtxt1|nojitc // Keep JITC from compiling and pausing while editing
const CSW = $36
//
// Data and text buffer constants
//
const MAXLINES = 999
const MAXLINESSIZE = MAXLINES+24
const MAXCLIPLINES = 256
const MAXLNLEN = 127
const pgjmp = 16
const changed = 1
const insmode = 2
const gutter = 4 // This has to be 4 to match on-screen width
const selection = 8
const exit = 16
const numlock = 128
//
// Text screen dimensions
//
byte[] UltraTermWidth = 80, 96, 160, 80, 80, 80, 132, 128
byte[] UltraTermHeight = 24, 24, 24, 24, 32, 48, 24, 32
word scrnheight = 24
byte scrnwidth = 80
byte viewwidth
byte viewleft
word viewtop
//
// Editor variables
//
byte nullstr = ""
byte[80] findstr = ""
byte[64] filename = "UNTITLED"
byte flags = 0
word numlines = 0
word numcliplines = 0
word arg
word strpool, strplmapsize, strpoolmap
word txtlinbuf, cliplinbuf
word cursx, cursy, cursrow, curscol, selrow
word cursflash = 500 // default cursor flash delay
//
// Predeclared functions
//
predef cmdmode#0
//
// Utility functions
//
// Defines for ASM routines
//
asm equates
!SOURCE "vmsrc/plvmzp.inc"
end
asm lnupcpy(dststr, srcstr)#0
LDA ESTKL,X
STA SRCL
LDA ESTKH,X
STA SRCH
INX
LDA ESTKL,X
STA DSTL
LDA ESTKH,X
STA DSTH
INX
LDY #$00
LDA (SRC),Y
CMP #80
BCC +
LDA #79
+ STA (DST),Y
TAY
BEQ +++
CPUPLP LDA (SRC),Y
CMP #$20
BCS +
ADC #$60
+ AND #$7F
CMP #$7B
BCS ++
CMP #$61
BCC ++
SEC
SBC #$20
++ STA (DST),Y
DEY
BNE CPUPLP
+++ RTS
end
//def sizemask(size)
// if size <= 80
// return sizetomask[(size - 1) >> 4]
// fin
// if size <= 16
// return $01
// elsif size <= 32
// return $03
// elsif size <= 48
// return $07
// elsif size <= 64
// return $0F
// elsif size <= 80
// return $1F
// fin
// return 0
//end
asm sizemask(size)#1
LDA ESTKL,X
CMP #129 ; 81
BCS ++
ADC #$0F
LSR
LSR
LSR
LSR
TAY
LDA #$FF
- ASL
DEY
BNE -
EOR #$FF
STA ESTKL,X
LDA #$00
STA ESTKH,X
RTS
-- DEY ; THIS IS NEXT MAP BYTE FROM BELOW
CPY #$FF
BNE +
DEC DSTH
DEC ESTKH+1,X
LDA ESTKH+1,X
CMP #$FF
BNE +
INX
INX
INX
++ LDA #$00
STA ESTKL,X
STA ESTKH,X
RTS
end
//def strpoolalloc(size)
// szmask = sizemask(size)
// for i = strplmapsize - 1 downto 0
// if ^(strpoolmap + i) <> $FF
// mapmask = szmask
// ofst = 0
// repeat // Look for memory hole to fit
// if ^(strpoolmap + i) & mapmask // Nope, shift over
// mapmask = mapmask << 1
// ofst++
// else // Yep, take it
// ^(strpoolmap + i) = ^(strpoolmap + i) | mapmask
// return (ofst << 4) + (i << 7) + strpool
// fin
// until mapmask & $100 // No fit
// fin
// next
// return 0
//end
asm poolalloc(poolbase, mapbase, idx, mask)#1
LDA ESTKL+2,X ; MAPBASEL
STA DSTL
LDA ESTKH+2,X ; MAPBASEH
CLC
ADC ESTKH+1,X ; IDXH
STA DSTH
LDY ESTKL+1,X ; IDXL
+ LDA (DST),Y
CMP #$FF
BEQ -- ; MAP BYTE FULL, TRY NEXT
LDA #$00
STA TMP ; OFST
LDA ESTKL,X
STA ESTKH,X
- AND (DST),Y
BEQ ++
INC TMP ; OFST++
ASL ESTKH,X ; MASK <<= 1
BCS -- ; NO FIT, TRY NEXT MAP BYTE
LDA ESTKH,X
BCC -
++ LDA ESTKH,X ; UPDATE MAPMASK
ORA (DST),Y
STA (DST),Y
LDA TMP ; OFST
ASL
ASL
ASL
ASL
;CLC
ADC ESTKL+3,X ; + OFST << 4
STA ESTKL+3,X
BCC +
INC ESTKH+3,X
+ TYA
LSR ESTKH+1,X
ROR
TAY
LDA #$00
ROR
;CLC
ADC ESTKL+3,X ; + IDX << 7
STA ESTKL+3,X
TYA
ADC ESTKH+3,X
STA ESTKH+3,X
INX
INX
INX
RTS
end
def bell#0
putc($07)
end
//
// Memory management routines
//
def striplead(strptr, chr)#0
byte striplen
for striplen = 1 to ^strptr
if ^(strptr + striplen) <> chr
break
fin
next
if striplen > 1
^strptr = ^strptr - (striplen - 1)
memcpy(strptr + 1, strptr + striplen, ^strptr)
fin
end
def strstripcpy(dststr, srcstr)#0
byte strlen
memcpy(dststr, srcstr, ^srcstr + 1)
for strlen = ^dststr downto 1
if ^(dststr + strlen) > keyspace
break
fin
next
^dststr = strlen
end
def delstr(strptr)#0
word ofst, mask
if strptr <> @nullstr
ofst = (strptr - strpool) >> 4
mask = sizemask(^strptr + 1) << (ofst & $07)
ofst = ofst >> 3
^(strpoolmap + ofst) = ^(strpoolmap + ofst) & ~mask
fin
end
def newstr(strptr)
byte strlen
word newptr
for strlen = ^strptr downto 1
if ^(strptr + strlen) > keyspace
break
fin
next
if strlen == 0
return @nullstr
fin
newptr = poolalloc(strpool, \
strpoolmap, \
strplmapsize - 1, \
sizemask(strlen + 1))
if newptr
memcpy(newptr + 1, strptr + 1, strlen)
^newptr = strlen
else
puts("Out of memory!\n")
newptr = @nullstr
fin
return newptr
end
def inittxtbuf#0
word i
if not strpool
txtlinbuf = heapalloc(MAXLINESSIZE*2)
cliplinbuf = heapalloc(MAXCLIPLINES*2)
strplmapsize = (heapavail - 1024) / 128
strpoolmap = heapalloc(strplmapsize)
strpool = heapalloc(strplmapsize * 128)
memset(txtlinbuf, @nullstr, MAXLINESSIZE*2)
memset(cliplinbuf, @nullstr, MAXCLIPLINES*2)
memset(strpoolmap, 0, strplmapsize)
else
for i = 0 to MAXLINESSIZE-1
if txtlinbuf=>[i] <> @nullstr
delstr(txtlinbuf=>[i])
txtlinbuf=>[i] = @nullstr
fin
next
fin
numlines = 1
cursrow = 0
curscol = 0
cursx = flags & gutter
cursy = 0
viewleft = 0
viewtop = 0
viewwidth = scrnwidth - (flags & gutter) - 2
flags = flags & ~selection
end
//
// String routines
//
def fillnum(huns, tens, ones, chrptr)#3
if huns
chrptr->[0] = '0' + huns
chrptr->[1] = '0' + tens
chrptr->[2] = '0' + ones
ones++
if ones > 9
ones = 0
tens++
if tens > 9
tens = 0
huns++
fin
fin
elsif tens
chrptr->[0] = ' '
chrptr->[1] = '0' + tens
chrptr->[2] = '0' + ones
ones++
if ones > 9
ones = 0
tens++
if tens > 9
tens = 0
huns = 1
fin
fin
elsif ones
chrptr->[0] = ' '
chrptr->[1] = ' '
chrptr->[2] = '0' + ones
ones++
if ones > 9
ones = 0
tens = 1
fin
fin
return huns, tens, ones
end
def strtonum(strptr)
word num, i
byte c
num = 0
for i = 1 to ^strptr
c = ^(strptr + i) & $7F
if c < '0' and c > '9'
break
fin
num = num * 10 + c - '0'
next
return num
end
def nametostr(namestr, len, strptr)#0
^strptr = len
memcpy(strptr + 1, namestr, len)
end
//
// File routines
//
def readtxt(filename, startline)#0
byte refnum, i, j, txtbuf[MAXLNLEN+2]
refnum = fileio:open(filename)
if refnum
fileio:newline(refnum, $7F, $0D)
numlines = startline
repeat
txtbuf = fileio:read(refnum, @txtbuf + 1, MAXLNLEN)
if txtbuf
txtlinbuf=>[numlines] = newstr(@txtbuf)
numlines++
fin
if !(numlines & $0F); putc('.'); fin
until txtbuf == 0 or numlines == MAXLINES
fileio:close(refnum)
//
// Make sure there is a blank line at the end of the buffer
//
if numlines < MAXLINES and txtlinbuf=>[numlines - 1] <> @nullstr
txtlinbuf=>[numlines] = @nullstr
numlines++
fin
fin
putln
end
def writetxt(filename)#0
word i
byte refnum, txtbuf[MAXLNLEN+2], txtlen
//
// Remove blank lines at end of text.
//
while numlines > 1 and txtlinbuf=>[numlines - 1] == @nullstr; numlines--; loop
//
// Delete old file and re-create
//
fileio:destroy(filename)
fileio:create(filename, $04, $00) // full access, TXT file
refnum = fileio:open(filename)
if refnum == 0
puts("\nError $"); puth(perr); puts(" opening: "); puts(filename); putln
return
fin
//
// Write all the text lines to the file.
//
for i = 0 to numlines - 1
txtlen = ^(txtlinbuf=>[i])
memcpy(@txtbuf, txtlinbuf=>[i] + 1, txtlen)
txtbuf[txtlen] = keyenter
txtlen++
if fileio:write(refnum, @txtbuf, txtlen) <> txtlen
puts("\nError $"); puth(perr); puts(" writing: "); puts(filename); putln
fileio:close(refnum)
return
fin
if !(i & $0F); putc('.'); fin
next
if fileio:close(refnum) <> FILE_ERR_OK
puts("\nError $"); puth(perr); puts(" closing: "); puts(filename)
fin
putln
end
//
// Screen routines
//
def drawstr(row, strptr)#0
word numchars
conio:gotoxy(flags & gutter, row)
numchars = ^strptr - viewleft
if numchars >= viewwidth
conio:putchars(viewwidth, strptr + viewleft + 1)
else
conio:putchars(numchars, strptr + viewleft + 1)
conio:clear(cleol)
fin
end
def drawgutterrow(row)#0
byte seperator, huns, tens, ones
word scrnrow
char gutterchars[3]
//
// Draw line numbers and gutter hilites
//
if flags & gutter
scrnrow = row + viewtop
huns, tens = divmod(scrnrow + 1, 100)
tens, ones = divmod(tens, 10)
if scrnrow < numlines
seperator = viewleft ?? '<' :: ' '
if flags & selection
if (scrnrow >= selrow and scrnrow <= cursrow) or (scrnrow >= cursrow and scrnrow <= selrow)
seperator = '['
conio:textctrl(ctrlattr, INVERSE)
fin
fin
if scrnrow == cursrow
conio:textctrl(ctrlattr, INVERSE)
fin
huns, tens, ones = fillnum(huns, tens, ones, @gutterchars)
conio:gotoxy(0, row)
conio:putchars(3, @gutterchars)
conio:textctrl(ctrlattr, INVERSE)
putc(seperator)
conio:textctrl(ctrlattr, NORMAL)
fin
fin
end
def drawgutter#0
byte row, seperator, huns, tens, ones
word scrnrow
char gutterchars[3]
//
// Draw line numbers and gutter hilites
//
if flags & gutter
seperator = viewleft ?? '<' :: ' '
huns, tens = divmod(viewtop + 1, 100)
tens, ones = divmod(tens, 10)
for row = 0 to scrnheight-1
scrnrow = row + viewtop
if scrnrow < numlines
seperator = viewleft ?? '<' :: ' '
if flags & selection
if (scrnrow >= selrow and scrnrow <= cursrow) or (scrnrow >= cursrow and scrnrow <= selrow)
seperator = '['
conio:textctrl(ctrlattr, INVERSE)
fin
fin
if scrnrow == cursrow
conio:textctrl(ctrlattr, INVERSE)
fin
huns, tens, ones = fillnum(huns, tens, ones, @gutterchars)
conio:gotoxy(0, row)
conio:putchars(3, @gutterchars)
conio:textctrl(ctrlattr, INVERSE)
putc(seperator)
conio:textctrl(ctrlattr, NORMAL)
fin
next
fin
end
def drawrows(toprow, bottomrow)#0
byte row, seperator, huns, tens, ones
word scrnrow, strptr, numchars
char gutterchars[3]
//
// Draw gutter and text
//
huns, tens = divmod(viewtop + toprow + 1, 100)
tens, ones = divmod(tens, 10)
for row = toprow to bottomrow
conio:gotoxy(0, row)
scrnrow = row + viewtop
if scrnrow < numlines
if flags & gutter
seperator = viewleft ?? '<' :: ' '
if flags & selection
if (scrnrow >= selrow and scrnrow <= cursrow) or (scrnrow >= cursrow and scrnrow <= selrow)
seperator = '['
conio:textctrl(ctrlattr, INVERSE)
fin
fin
if scrnrow == cursrow
conio:textctrl(ctrlattr, INVERSE)
fin
huns, tens, ones = fillnum(huns, tens, ones, @gutterchars)
conio:putchars(3, @gutterchars)
conio:textctrl(ctrlattr, INVERSE)
putc(seperator)
conio:textctrl(ctrlattr, NORMAL)
fin
strptr = txtlinbuf=>[scrnrow]
numchars = ^strptr - viewleft
if numchars >= viewwidth
numchars = viewwidth
fin
conio:putchars(numchars, strptr + viewleft + 1)
fin
conio:clear(cleol)
next
end
def drawscrn#0
drawrows(0, scrnheight - 1)
end
def curshpos(hpos)#1
byte needredraw, scrnleft, scrnright
needredraw = TRUE
if hpos < 0; hpos = 0; fin
if hpos > MAXLNLEN; hpos = MAXLNLEN; fin
scrnleft = flags & gutter
scrnright = scrnwidth - 2
curscol = hpos
cursx = curscol - viewleft + scrnleft
if cursx > scrnright
cursx = scrnright
viewleft = curscol - viewwidth
elsif cursx < scrnleft
cursx = scrnleft
viewleft = curscol
else
needredraw = FALSE
fin
return needredraw
end
def cursvpos(vpos)#1
byte needredraw
needredraw = TRUE
if vpos < 0; vpos = 0; fin
if vpos > numlines - 1; vpos = numlines - 1; fin
cursrow = vpos
cursy = cursrow - viewtop
if cursy > scrnheight - 1
cursy = scrnheight - 1
viewtop = cursrow - (scrnheight - 1)
elsif cursy < 0
cursy = 0
viewtop = cursrow
else
needredraw = FALSE
fin
return needredraw
end
def curshome#0
cursrow = 0
curscol = 0
cursx = flags & gutter
cursy = 0
viewleft = 0
viewtop = 0
drawscrn
end
def cursend#0
if numlines > scrnheight - 1
cursrow = numlines - 1
cursy = scrnheight - 1
viewtop = cursrow - (scrnheight - 1)
else
cursrow = numlines - 1
cursy = numlines - 1
viewtop = 0
fin
curscol = 0
cursx = flags & gutter
viewleft = 0
drawscrn
end
def cursup#0
if cursrow > 0
cursrow--
drawgutterrow(cursy)
if cursy > 0
cursy--
drawgutterrow(cursy)
else
viewtop = cursrow
if conio:textctrl(ctrlscroll, DOWN)
drawrows(0, 0)
else
drawscrn
fin
fin
fin
end
def pgup#0
word scroll
scroll = viewtop
if cursvpos(cursrow - pgjmp)
scroll = viewtop - scroll
if scroll
if conio:textctrl(ctrlscroll, scroll)
drawrows(0, -scroll - 1)
drawgutter
else
drawscrn
fin
fin
else
drawgutter
fin
end
def cursdown#0
if cursrow < numlines - 1
cursrow++
drawgutterrow(cursy)
if cursy < scrnheight - 1
cursy++
drawgutterrow(cursy)
else
viewtop = cursrow - (scrnheight - 1)
if conio:textctrl(ctrlscroll, UP)
drawrows(scrnheight - 1, scrnheight - 1)
else
drawscrn
fin
fin
fin
end
def pgdown#0
word scroll
scroll = viewtop
if cursvpos(cursrow + pgjmp)
scroll = viewtop - scroll
if scroll
if conio:textctrl(ctrlscroll, scroll)
drawrows(scrnheight - scroll, scrnheight - 1)
drawgutter
else
drawscrn
fin
fin
else
drawgutter
fin
end
def cursleft#0
if curshpos(curscol - 1); drawscrn; fin
end
def pgleft#0
word i, strptr
strptr = txtlinbuf=>[cursrow]
if curscol > ^strptr
i = ^strptr
else
for i = 1 to ^strptr
if ^(strptr + i) <> keyspace
break
fin
next
i--
if i >= curscol
i = 0
fin
fin
if curshpos(i)
drawscrn
else
drawgutter
fin
end
def cursright#0
if curshpos(curscol + 1); drawscrn; fin
end
def pgright#0
word i, strptr
strptr = txtlinbuf=>[cursrow]
if curscol >= ^strptr
i = curscol + 8
else
for i = 1 to ^strptr
if ^(strptr + i) <> keyspace
break
fin
next
i--
if curscol >= i
i = ^strptr
fin
fin
if curshpos(i)
drawscrn
else
drawgutter
fin
end
//
// Find string in text
//
def findline(strptr, start)#1
byte scan, i, upstr[MAXLNLEN+1]
if ^strptr >= findstr
lnupcpy(@upstr, strptr)
for scan = start to upstr - findstr + 1
if upstr[scan] == findstr[1]
for i = 2 to findstr
if upstr[scan + i - 1] <> findstr[i]
break
fin
next
if i > findstr
curshpos(scan - 1)
return TRUE
fin
fin
next
fin
return FALSE
end
def findtxt#0
word f
//
// Search from current pos to end
//
if findline(txtlinbuf=>[cursrow], curscol + 2)
return
fin
for f = cursrow + 1 to numlines - 1
if findline(txtlinbuf=>[f], 1)
cursvpos(f)
return
fin
next
//
// Search from beginning to current pos
//
for f = 0 to cursrow
if findline(txtlinbuf=>[f], 1)
cursvpos(f)
return
fin
next
bell
end
//
// Printer routines
//
def printtxt(slot)#0
word i, scrncsw
scrncsw = *CSW
*CSW = $C000 | (slot << 8)
for i = 0 to numlines - 1
puts(txtlinbuf=>[i]); putln
next
*CSW = scrncsw
end
def freesel#0
word i
if numcliplines
for i = 0 to numcliplines - 1
if cliplinbuf=>[i] <> @nullstr
delstr(cliplinbuf=>[i])
cliplinbuf=>[i] = @nullstr
fin
next
numcliplines = 0
fin
end
def selrange#2
word first, last
if flags & selection
if cursrow > selrow
first, last = selrow, cursrow
else
first, last = cursrow, selrow
fin
else
first = cursrow
last = first
fin
return first, last
end
def beginsel#0
flags = flags ^ selection
selrow = cursrow
drawgutter
end
def copysel#0
word firstsel, lastsel
freesel
firstsel, lastsel = selrange
if lastsel - firstsel < MAXCLIPLINES
for numcliplines = 0 to lastsel - firstsel
cliplinbuf=>[numcliplines] = newstr(txtlinbuf=>[firstsel + numcliplines])
next
flags = flags & ~selection
drawgutter
else
bell
fin
end
def cutsel#0
word firstsel, lastsel
freesel
firstsel, lastsel = selrange
if lastsel - firstsel < MAXCLIPLINES
for numcliplines = 0 to lastsel - firstsel
cliplinbuf=>[numcliplines] = txtlinbuf=>[firstsel + numcliplines]
next
memcpy(@txtlinbuf=>[firstsel], \
@txtlinbuf=>[lastsel + 1], \
(numlines - lastsel + 1) * 2)
numlines = numlines - numcliplines
for lastsel = numlines to numlines + numcliplines
txtlinbuf=>[lastsel] = @nullstr
next
cursrow = firstsel
if cursrow >= numlines
cursrow = numlines - 1
fin
if cursrow < viewtop
viewtop = cursrow
fin
cursy = cursrow - viewtop
flags = flags | changed
flags = flags & ~selection
drawscrn
else
bell
fin
end
def pastesel#0
word p
if numcliplines and numcliplines + numlines < MAXLINES
memcpy(@txtlinbuf=>[cursrow + numcliplines], \
@txtlinbuf=>[cursrow], \
(numlines - cursrow) * 2)
for p = 0 to numcliplines - 1
txtlinbuf=>[cursrow + p] = newstr(cliplinbuf=>[p])
next
numlines = numlines + numcliplines
flags = flags | changed
drawscrn
else
bell
fin
end
def indentsel#0
byte indentstr[MAXLNLEN+1], j, l
word firstsel, lastsel, i
freesel
firstsel, lastsel = selrange
for i = firstsel to lastsel
l = ^(txtlinbuf=>[i])
if l and l < MAXLNLEN - 2
memcpy(@indentstr + 3, txtlinbuf=>[i] + 1, l)
indentstr[0] = l + 2
indentstr[1] = keyspace
indentstr[2] = keyspace
//
// Align indent
//
for j = 3 to indentstr[0]
if indentstr[j] <> keyspace
if j & 1 == 0
indentstr[0]--
memcpy(@indentstr + 1, @indentstr + 2, indentstr[0])
fin
break
fin
next
delstr(txtlinbuf=>[i])
txtlinbuf=>[i] = newstr(@indentstr)
flags = flags | changed
fin
next
drawscrn
end
def undentsel#0
byte undentstr[MAXLNLEN+1], l
word firstsel, lastsel, i
freesel
firstsel, lastsel = selrange
for i = firstsel to lastsel
l = ^(txtlinbuf=>[i])
if l
memcpy(@undentstr + 1, txtlinbuf=>[i] + 1, l)
if undentstr[1] == keyspace
memcpy(@undentstr + 1, @undentstr + 2, l - 1)
l--
if l and undentstr[1] == keyspace
memcpy(@undentstr + 1, @undentstr + 2, l - 1)
l--
fin
undentstr[0] = l
delstr(txtlinbuf=>[i])
txtlinbuf=>[i] = newstr(@undentstr)
flags = flags | changed
fin
fin
next
drawscrn
end
def autoindent(strptr)#0
byte i
for i = 1 to ^strptr
if ^(strptr + i) <> keyspace
break
fin
next
curshpos(i - 1)
end
def openline(row)
if numlines < MAXLINES
memcpy(@txtlinbuf=>[row + 1], @txtlinbuf=>[row], (numlines - row) * 2)
txtlinbuf=>[row] = @nullstr
numlines++
flags = flags | changed
return TRUE
fin
bell
return FALSE
end
def joinline#0
word joinlen, joinstr, stripjoin
if cursrow < numlines - 1
joinstr = heapalloc(MAXLNLEN+1)
stripjoin = heapalloc(MAXLNLEN+1)
strstripcpy(joinstr, txtlinbuf=>[cursrow])
memcpy(stripjoin, txtlinbuf=>[cursrow + 1], ^(txtlinbuf=>[cursrow + 1]) + 1)
striplead(stripjoin, keyspace);
joinlen = ^joinstr + ^stripjoin
if joinlen <= MAXLNLEN
curshpos(^joinstr)
memcpy(joinstr + ^joinstr + 1, stripjoin + 1, ^stripjoin)
^joinstr = joinlen
delstr(txtlinbuf=>[cursrow])
txtlinbuf=>[cursrow] = newstr(joinstr)
delstr(txtlinbuf=>[cursrow + 1])
numlines--
memcpy(@txtlinbuf=>[cursrow + 1], @txtlinbuf=>[cursrow + 2], (numlines - cursrow) * 2)
flags = flags | changed
heaprelease(joinstr)
return
fin
heaprelease(joinstr)
fin
bell
end
def splitline#0
byte splitstr[MAXLNLEN+1], splitlen, i
if openline(cursrow + 1)
if curscol
splitlen = ^(txtlinbuf=>[cursrow])
if curscol < splitlen - 1
splitstr = splitlen - curscol
memcpy(@splitstr + 1, txtlinbuf=>[cursrow] + curscol + 1, splitstr)
striplead(@splitstr, keyspace)
for i = 1 to curscol
if ^(txtlinbuf=>[cursrow] + i) <> keyspace
break
fin
memcpy(@splitstr + 2, @splitstr + 1, splitstr)
splitstr[1] = keyspace
splitstr++
next
txtlinbuf=>[cursrow + 1] = newstr(@splitstr)
splitstr = curscol
memcpy(@splitstr + 1, txtlinbuf=>[cursrow] + 1, splitstr)
delstr(txtlinbuf=>[cursrow])
txtlinbuf=>[cursrow] = newstr(@splitstr)
curshpos(i - 1)
else
if splitlen > 0
for curscol = 1 to splitlen - 1
if ^(txtlinbuf=>[cursrow] + curscol) <> keyspace
break
fin
next
curshpos(curscol - 1)
fin
fin
else
txtlinbuf=>[cursrow + 1] = txtlinbuf=>[cursrow]
txtlinbuf=>[cursrow] = @nullstr
fin
fin
end
//
// Keyboard routines
//
def waitkey#1
word delay
for delay = 0 to cursflash
if conio:keypressed()
return conio:getkey()
fin
next
return 0
end
def keyin
byte key, underchr, curschr
underchr = curscol >= ^(txtlinbuf=>[cursrow]) ?? ' ' :: txtlinbuf=>[cursrow]->[curscol + 1]
curschr = flags & insmode ?? '+' :: '_'
if conio:keypressed()
key = conio:getkey()
conio:gotoxy(cursx, cursy)
else
conio:gotoxy(cursx, cursy)
repeat
conio:textctrl(ctrlattr, INVERSE)
conio:putchars(1, curschr)
conio:gotoxy(cursx, cursy)
key = waitkey
conio:textctrl(ctrlattr, NORMAL)
if not key
conio:putchars(1, underchr)
conio:gotoxy(cursx, cursy)
key = waitkey
fin
until key
fin
conio:putchars(1, underchr)
if (key & ~keyoptmod) >= '0' and (key & ~keyoptmod) <= '9'
key = key ^ (flags & numlock) // numlock == keyoptmod
fin
if key & keyoptmod // Closed-Apple/Option pressed
when key
is keyarrowleft | keyoptmod
key = keyctrla; break
is keyarrowright | keyoptmod
key = keyctrls; break
is keyarrowup | keyoptmod
key = keyctrlw; break
is keyarrowdown | keyoptmod
key = keyctrlz; break
is keyenter | keyoptmod
key = keyctrlf; break
is keytab | keyoptmod
key = keydetab; break
is '\\' | keyoptmod
key = keydelete; break // Delete
//
// Map option+keypad on Platinum //e
//
is '4' | keyoptmod
key = keyarrowleft; break
is '6' | keyoptmod
key = keyarrowright; break
is '8' | keyoptmod
key = keyarrowup; break
is '2' | keyoptmod
key = keyarrowdown; break
is '7' | keyoptmod
key = keyctrlq; break // Top
is '1' | keyoptmod
key = keyctrle; break // Bottom
is '9' | keyoptmod
key = keyctrlw; break // Pg Up
is '3' | keyoptmod
key = keyctrlz; break // Pg Dn
is '5' | keyoptmod
key = keyctrlb; break // Selection start/end
is '0' | keyoptmod
key = keyctrld; break // Del
is '.' | keyoptmod
key = keyctrlv; break // Paste
is '-' | keyoptmod
key = keyctrlx; break // Cut
is '+' | keyoptmod
key = keyctrlc; break // Copy
is '*' | keyoptmod
key = keyctrlo; break // Open line
is '/' | keyoptmod
key = keyctrlt; break // Insert/Overwrite
is '=' | keyoptmod
flags = flags ^ numlock
if flags & numlock
bell
fin
bell
key = 0
break
otherwise
key = key & ~keyoptmod
wend
fin
return key
end
def editkey(key)
if key >= keyspace or key == keyctrld or key == keyctrlr
return TRUE
fin
return FALSE
end
def editline(key)
word undoline
byte undopos, localchange, editstr[], editlen, editchars[MAXLNLEN+1]
if (editkey(key))
localchange = FALSE
undopos = curscol
undoline = txtlinbuf=>[cursrow]
editlen = MAXLNLEN
memcpy(@editchars, undoline + 1, ^undoline)
memset(@editchars + ^undoline, keyspace | (keyspace << 8), MAXLNLEN - ^undoline)
txtlinbuf=>[cursrow] = @editstr
repeat
when key
is keyctrld
memcpy(@editchars[curscol], @editchars[curscol + 1], MAXLNLEN - 1 - curscol)
editchars[MAXLNLEN - 1] = keyspace
drawstr(cursy, @editstr)
localchange = TRUE
break
is keyctrlr
memset(@editchars, $A0A0, MAXLNLEN)
memcpy(@editchars, undoline + 1, ^undoline)
if curshpos(undopos)
drawscrn
else
drawstr(cursy, @editstr)
fin
localchange = FALSE
break
is keydelete
if curscol > 0
memcpy(@editchars[curscol - 1], @editchars[curscol], MAXLNLEN - curscol)
editchars[MAXLNLEN - 1] = keyspace
if curshpos(curscol - 1)
drawscrn
else
drawstr(cursy, @editstr)
fin
localchange = TRUE
fin
break
otherwise
if flags & insmode
if editchars[MAXLNLEN - 1] == keyspace
memcpy(@editchars[curscol+1], @editchars[curscol], MAXLNLEN - 1 - curscol)
else
bell
break
fin
fin
editchars[curscol] = key
if curshpos(curscol + 1)
drawscrn
else
drawstr(cursy, @editstr)
fin
localchange = TRUE
break
wend
key = keyin
until not editkey(key)
if localchange
flags = flags | changed
delstr(undoline)
txtlinbuf=>[cursrow] = newstr(@editstr)
else
txtlinbuf=>[cursrow] = undoline
fin
fin
return key
end
def editmode#0
conio:textctrl(ctrlecho, OFF)
conio:textctrl(ctrlcursor, OFF)
repeat
when editline(keyin)
is keyarrowup
cursup; break
is keyarrowdown
cursdown; break
is keyarrowleft
cursleft; break
is keyarrowright
cursright; break
is keyctrlw
pgup; break
is keyctrlz
pgdown; break
is keyctrla
pgleft; break
is keyctrls
pgright; break
is keyctrlq
curshome; break
is keyctrle
cursend; break
is keyctrlb
beginsel; break
is keyctrlc
copysel; break
is keyctrlx
cutsel; break
is keyctrlv
pastesel; break
is keyctrlf
if cursrow == (numlines - 1)
if numlines < MAXLINES
numlines++
cursdown
flags = flags | changed
drawgutter
else
bell
fin
break
fin
cursdown
is keyctrlo
openline(cursrow)
if cursrow
autoindent(txtlinbuf=>[cursrow - 1])
else
curshpos(0)
fin
drawscrn
break
is keyenter
if flags & insmode
splitline
else
openline(cursrow + 1)
fin
autoindent(txtlinbuf=>[cursrow])
cursvpos(cursrow + 1)
drawscrn
break
is keyctrly
joinline
drawscrn
break
is keytab
if flags & insmode
indentsel
if not ^(txtlinbuf=>[cursrow])
curshpos((curscol + 2) & $FE)
else
autoindent(txtlinbuf=>[cursrow])
fin
break
fin
is keydetab
undentsel
if not ^(txtlinbuf=>[cursrow])
curshpos((curscol - 2) & $FFFE)
else
autoindent(txtlinbuf=>[cursrow])
fin
break
is keyctrlt
flags = flags ^ insmode
break
is keyescape
cmdmode
if not (flags & exit)
conio:clear(cls)
conio:textctrl(ctrlecho, OFF)
conio:textctrl(ctrlcursor, OFF)
drawscrn
fin
wend
until flags & exit
end
//
// Command mode
//
def prfiles(optpath)
byte path[64]
byte refnum
byte firstblk
byte entrylen, entriesblk
byte i, type, len
word databuff, entry
if ^optpath
strstripcpy(@path, optpath)
else
fileio:getpfx(@path)
puts(@path)
putln
fin
databuff = heapalloc(512)
refnum = fileio:open(@path)
if perr
return perr
fin
firstblk = 1
while fileio:read(refnum, databuff, 512) == 512
entry = databuff + 4
if firstblk
entrylen = databuff->$23
entriesblk = databuff->$24
entry = entry + entrylen
fin
for i = firstblk to entriesblk
type = ^entry
if type <> 0
len = type & $0F
^entry = len
puts(entry)
if type & $F0 == $D0 // Is it a directory?
putc('/')
len++
fin
for len = 20 - len downto 1
putc(' ')
next
fin
entry = entry + entrylen
next
firstblk = 0
loop
fileio:close(refnum)
heaprelease(databuff)
putln
return 0
end
def parsecmd(strptr)
byte cmd
cmd = 0
striplead(strptr, ' ')
if ^strptr
cmd = ^(strptr + 1)
memcpy(strptr + 1, strptr + 2, ^strptr)
^strptr--
fin
if ^strptr
striplead(strptr, ' ')
fin
return cmd
end
def chkchng
if flags & changed
puts("LOSE CHANGES TO FILE (Y/N)?")
if toupper(getc) == 'N'
putln
return FALSE
fin
putln
fin
return TRUE
end
def cmdmode#0
byte slot, b
word cmdptr, line
var i, freepool
conio:gotoxy(0, scrnheight - 1)
conio:clear(cleol)
conio:textctrl(ctrlecho, ON)
conio:textctrl(ctrlcursor, ON)
perr = 0 // Reset perr
line = 0
while not (flags & exit)
puts(@filename)
cmdptr = gets($BA)
when toupper(parsecmd(cmdptr))
is 'F' // Find string
if ^cmdptr
lnupcpy(@findstr, cmdptr)
fin
findtxt
return
is 'T' // Toggle upper/lower case display
if ^cmdptr
when toupper(^(cmdptr + 1))
is 'G' // Gutter view
flags = flags ^ gutter
viewwidth = scrnwidth - (flags & gutter) - 1
curshpos(curscol)
break
is 'C' // Lower case chip (Apple ][/][+ only)
conio:textctrl(ctrlcase, 0)
break
wend
fin
return
is '9' // Goto line #
line++
is '8'
line++
is '7'
line++
is '6'
line++
is '5'
line++
is '4'
line++
is '3'
line++
is '2'
line++
is '1'
line++
is '0'
memcpy(cmdptr + 2, cmdptr + 1, ^cmdptr)
^(cmdptr + 1) = line + '0' // Push number back on cmdptr
^cmdptr++
line = strtonum(cmdptr)
if line
curshpos(0)
cursvpos(line - 1)
fin
is 'E' // Edit mode
is 0
return
is 'A' // Append file
readtxt(cmdptr, numlines)
flags = flags | changed
break
is 'R' // Read file
if chkchng
inittxtbuf
strstripcpy(@filename, cmdptr)
readtxt(@filename, 0)
flags = flags & ~changed
fin
break
is 'W' // Write file
if ^cmdptr
strstripcpy(@filename, cmdptr)
fin
writetxt(@filename)
//if flags & changed; fin
flags = flags & ~changed
break
is 'C' // Catalog
prfiles(cmdptr); break
is 'P' // Prefix
fileio:setpfx(cmdptr); break
is 'H' // Hardcopy
if ^cmdptr
slot = cmdptr.1 - '0'
else
slot = 1
fin
printtxt(slot)
break
is 'Q' // Quit
flags = flags | (chkchng ?? exit :: 0)
if not (flags & exit)
return
fin
break
is 'N'
if chkchng
inittxtbuf
strstripcpy(@filename, "UNTITLED")
fin
break
is 'D' // Dump stats
freepool = 0
for i = strplmapsize - 1 downto 0
slot = ^(strpoolmap + i)
if slot <> $FF
for b = 0 to 7
if not ((1 << b) & slot)
freepool++
fin
next
fin
next
puts("Pool size: "); puti(strplmapsize*128); putln
puts("Pool free: "); puti(freepool*16); putln
puts("Num lines: "); puti(numlines); putln
puts("Clip lines: "); puti(numcliplines); putln
break
otherwise
bell
putc('?')
putln
wend
if perr
puts("ERROR: $")
putb(perr)
putln
fin
loop
end
//
// Init editor
//
arg = argNext(argFirst)
if ^arg
while ^(arg+1) == '-'
when ^(arg+2)
is '4' // Force 40 columns
scrnwidth = 40
break
is 'U' // Use extended VIDEX UltraTerm mode
scrnwidth = 80 + ^(arg+3) - '0'
break
wend
arg = argNext(arg)
loop
strcpy(@filename, arg)
arg = argNext(arg)
if ^arg
cursrow = strtonum(arg)
fin
fin
if waitVBL
cursflash = waitVBL
fin
flags = flags | insmode | gutter
scrnwidth = conio:textmode(scrnwidth)
if scrnwidth > 80 // Lookup UltraTerm screen dimensions
scrnheight = UltraTermHeight[scrnwidth - 81]
scrnwidth = UltraTermWidth[scrnwidth - 81]
fin
puts("PLEIADES 2.20 Editor\n")
inittxtbuf
puts(@filename)
readtxt(@filename, 0)
if cursrow
curshpos(0)
cursvpos(cursrow - 1)
fin
conio:clear(cls)
drawscrn
editmode
conio:textmode(0) // Set default mode
done