diff options
Diffstat (limited to 'appl/acme/look.b')
| -rw-r--r-- | appl/acme/look.b | 743 |
1 files changed, 743 insertions, 0 deletions
diff --git a/appl/acme/look.b b/appl/acme/look.b new file mode 100644 index 00000000..1004b803 --- /dev/null +++ b/appl/acme/look.b @@ -0,0 +1,743 @@ +implement Look; + +include "common.m"; + +sys : Sys; +draw : Draw; +utils : Utils; +dat : Dat; +graph : Graph; +acme : Acme; +framem : Framem; +regx : Regx; +bufferm : Bufferm; +textm : Textm; +windowm : Windowm; +columnm : Columnm; +exec : Exec; +scrl : Scroll; +plumbmsg : Plumbmsg; + +sprint : import sys; +Point : import draw; +warning, isalnum, stralloc, strfree, strchr, tgetc : import utils; +Range, TRUE, FALSE, XXX, BUFSIZE, Astring : import Dat; +Expand, seltext, row : import dat; +cursorset : import graph; +frptofchar : import framem; +isaddrc, isregexc, address : import regx; +Buffer : import bufferm; +Text : import textm; +Window : import windowm; +Column : import columnm; +Msg : import plumbmsg; + +init(mods : ref Dat->Mods) +{ + sys = mods.sys; + draw = mods.draw; + utils = mods.utils; + graph = mods.graph; + acme = mods.acme; + framem = mods.framem; + regx = mods.regx; + dat = mods.dat; + bufferm = mods.bufferm; + textm = mods.textm; + windowm = mods.windowm; + columnm = mods.columnm; + exec = mods.exec; + scrl = mods.scroll; + plumbmsg = mods.plumbmsg; +} + +nuntitled : int; + +look3(t : ref Text, q0 : int, q1 : int, external : int) +{ + n, c, f : int; + ct : ref Text; + e : Expand; + r : ref Astring; + expanded : int; + + ct = seltext; + if(ct == nil) + seltext = t; + (expanded, e) = expand(t, q0, q1); + if(!external && t.w!=nil && t.w.nopen[Dat->QWevent]>byte 0){ + if(!expanded) + return; + f = 0; + if((e.at!=nil && t.w!=nil) || (e.name!=nil && lookfile(e.name, len e.name)!=nil)) + f = 1; # acme can do it without loading a file + if(q0!=e.q0 || q1!=e.q1) + f |= 2; # second (post-expand) message follows + if(e.name != nil) + f |= 4; # it's a file name + c = 'l'; + if(t.what == Textm->Body) + c = 'L'; + n = q1-q0; + if(n <= Dat->EVENTSIZE){ + r = stralloc(n); + t.file.buf.read(q0, r, 0, n); + t.w.event(sprint("%c%d %d %d %d %s\n", c, q0, q1, f, n, r.s[0:n])); + strfree(r); + r = nil; + }else + t.w.event(sprint("%c%d %d %d 0 \n", c, q0, q1, f)); + if(q0==e.q0 && q1==e.q1) + return; + if(e.name != nil){ + n = len e.name; + if(e.a1 > e.a0) + n += 1+(e.a1-e.a0); + r = stralloc(n); + for (i := 0; i < len e.name; i++) + r.s[i] = e.name[i]; + if(e.a1 > e.a0){ + r.s[len e.name] = ':'; + e.at.file.buf.read(e.a0, r, len e.name+1, e.a1-e.a0); + } + }else{ + n = e.q1 - e.q0; + r = stralloc(n); + t.file.buf.read(e.q0, r, 0, n); + } + f &= ~2; + if(n <= Dat->EVENTSIZE) + t.w.event(sprint("%c%d %d %d %d %s\n", c, e.q0, e.q1, f, n, r.s[0:n])); + else + t.w.event(sprint("%c%d %d %d 0 \n", c, e.q0, e.q1, f)); + strfree(r); + r = nil; + return; + } + if(0 && dat->plumbed){ # don't do yet : 2 acmes running => only 1 receives msg + m := ref Msg; + m.src = "acme"; + m.dst = nil; + (dir, nil) := dirname(t, nil, 0); + if(dir == ".") # sigh + dir = nil; + if(dir == nil) + dir = acme->wdir; + m.dir = dir; + m.kind = "text"; + m.attr = nil; + if(q1 == q0){ + if(t.q1>t.q0 && t.q0<=q0 && q0<=t.q1){ + q0 = t.q0; + q1 = t.q1; + }else{ + p := q0; + while(q0 > 0 && (c = tgetc(t, q0-1)) != ' ' && c != '\t' && c != '\n') + q0--; + while(q1 < t.file.buf.nc && (c = tgetc(t, q1)) != ' ' && c != '\t' && c != '\n') + q1++; + if(q1 == q0) + return; + m.attr = "click=" + string (p-q0); + } + } + r = stralloc(q1-q0); + t.file.buf.read(q0, r, 0, q1-q0); + m.data = array of byte r.s; + strfree(r); + if(m.send() >= 0) + return; + # plumber failed to match : fall through + } + if(!expanded) + return; + if(e.name != nil || e.at != nil) + (nil, e) = openfile(t, e); + else{ + if(t.w == nil) + return; + ct = t.w.body; + if(t.w != ct.w) + ct.w.lock('M'); + if(t == ct) + ct.setselect(e.q1, e.q1); + n = e.q1 - e.q0; + r = stralloc(n); + t.file.buf.read(e.q0, r, 0, n); + if(search(ct, r.s, n) && e.jump) + cursorset(frptofchar(ct.frame, ct.frame.p0).add((4, ct.frame.font.height-4))); + if(t.w != ct.w) + ct.w.unlock(); + strfree(r); + r = nil; + } + e.name = nil; + e.bname = nil; +} + +plumblook(m : ref Msg) +{ + e : Expand; + + if (len m.data > Dat->PLUMBSIZE) { + warning(nil, sys->sprint("plumb message too long : %s\n", string m.data)); + return; + } + e.q0 = e.q1 = 0; + if (len m.data == 0) + return; + e.ar = nil; + e.name = string m.data; + if(e.name[0] != '/' && m.dir != nil) + e.name = m.dir + "/" + e.name; + (e.name, nil) = cleanname(e.name, len e.name); + e.bname = e.name; + e.jump = TRUE; + e.a0 = e.a1 = 0; + (found, addr) := plumbmsg->lookup(plumbmsg->string2attrs(m.attr), "addr"); + if (found && addr != nil) { + e.ar = addr; + e.a1 = len addr; + } + openfile(nil, e); + e.at = nil; +} + +plumbshow(m : ref Msg) +{ + w := utils->newwindow(nil); + (found, name) := plumbmsg->lookup(plumbmsg->string2attrs(m.attr), "filename"); + if (!found || name == nil) { + nuntitled++; + name = "Untitled-" + string nuntitled; + } + if (name[0] != '/' && m.dir != nil) + name = m.dir + "/" + name; + (name, nil) = cleanname(name, len name); + w.setname(name, len name); + d := string m.data; + w.body.insert(0, d, len d, TRUE, FALSE); + w.body.file.mod = FALSE; + w.dirty = FALSE; + w.settag(); + scrl->scrdraw(w.body); + w.tag.setselect(w.tag.file.buf.nc, w.tag.file.buf.nc); +} + +search(ct : ref Text, r : string, n : int) : int +{ + q, nb, maxn : int; + around : int; + s : ref Astring; + b, c : int; + + if(n==0 || n>ct.file.buf.nc) + return FALSE; + if(2*n > BUFSIZE){ + warning(nil, "string too long\n"); + return FALSE; + } + maxn = utils->max(2*n, BUFSIZE); + s = utils->stralloc(BUFSIZE); + b = nb = 0; + around = 0; + q = ct.q1; + for(;;){ + if(q >= ct.file.buf.nc){ + q = 0; + around = 1; + nb = 0; + } + if(nb > 0){ + for (c = 0; c < nb; c++) + if (s.s[b+c] == r[0]) + break; + if(c >= nb){ + q += nb; + nb = 0; + if(around && q>=ct.q1) + break; + continue; + } + q += c; + nb -= c; + b += c; + } + # reload if buffer covers neither string nor rest of file + if(nb<n && nb!=ct.file.buf.nc-q){ + nb = ct.file.buf.nc-q; + if(nb >= maxn) + nb = maxn-1; + ct.file.buf.read(q, s, 0, nb); + b = 0; + } + if(n <= nb && s.s[b:b+n] == r[0:n]){ + if(ct.w != nil){ + ct.show(q, q+n); + ct.w.settag(); + }else{ + ct.q0 = q; + ct.q1 = q+n; + } + seltext = ct; + utils->strfree(s); + s = nil; + return TRUE; + } + if(around && q>=ct.q1) + break; + --nb; + b++; + q++; + } + utils->strfree(s); + s = nil; + return FALSE; +} + +isfilec(r : int) : int +{ + if(isalnum(r)) + return TRUE; + if(strchr(".-+/:", r) >= 0) + return TRUE; + return FALSE; +} + +cleanname(b : string, n : int) : (string, int) +{ + i, j, found : int; + + b = b[0:n]; + # compress multiple slashes + for(i=0; i<n-1; i++) + if(b[i]=='/' && b[i+1]=='/'){ + b = b[0:i] + b[i+1:]; + --n; + --i; + } + # eliminate ./ + for(i=0; i<n-1; i++) + if(b[i]=='.' && b[i+1]=='/' && (i==0 || b[i-1]=='/')){ + b = b[0:i] + b[i+2:]; + n -= 2; + --i; + } + # eliminate trailing . + if(n>=2 && b[n-2]=='/' && b[n-1]=='.') { + --n; + b = b[0:n]; + } + do{ + # compress xx/.. + found = FALSE; + for(i=1; i<=n-3; i++) + if(b[i:i+3] == "/.."){ + if(i==n-3 || b[i+3]=='/'){ + found = TRUE; + break; + } + } + if(found) + for(j=i-1; j>=0; --j) + if(j==0 || b[j-1]=='/'){ + i += 3; # character beyond .. + if(i<n && b[i]=='/') + ++i; + b = b[0:j] + b[i:]; + n -= (i-j); + break; + } + }while(found); + if(n == 0){ + b = "."; + n = 1; + } + return (b, n); +} + +includefile(dir : string, file : string, nfile : int) : (string, int) +{ + m, n : int; + a : string; + + if (dir == ".") { + m = 0; + a = file; + } + else { + m = 1 + len dir; + a = dir + "/" + file; + } + n = utils->access(a); + if(n < 0) { + a = nil; + return (nil, 0); + } + file = nil; + return cleanname(a, m+nfile); +} + +objdir : string; + +includename(t : ref Text , r : string, n : int) : (string, int) +{ + file : string; + i, nfile : int; + w : ref Window; + + { + w = t.w; + if(n==0 || r[0]=='/' || w==nil) + raise "e"; + if(n>2 && r[0]=='.' && r[1]=='/') + raise "e"; + file = nil; + nfile = 0; + (file, nfile) = includefile(".", r, n); + if (file == nil) { + (dr, dn) := dirname(t, r, n); + (file, nfile) = includefile(".", dr, dn); + } + if (file == nil) { + for(i=0; i<w.nincl && file==nil; i++) + (file, nfile) = includefile(w.incl[i], r, n); + } + if(file == nil) + (file, nfile) = includefile("/module", r, n); + if(file == nil) + (file, nfile) = includefile("/include", r, n); + if(file==nil && objdir!=nil) + (file, nfile) = includefile(objdir, r, n); + if(file == nil) + raise "e"; + return (file, nfile); + } + exception{ + * => + return (r, n); + } + return (nil, 0); +} + +dirname(t : ref Text, r : string, n : int) : (string, int) +{ + b : ref Astring; + c : int; + m, nt : int; + slash : int; + + { + b = nil; + if(t == nil || t.w == nil) + raise "e"; + nt = t.w.tag.file.buf.nc; + if(nt == 0) + raise "e"; + if(n>=1 && r[0]=='/') + raise "e"; + b = stralloc(nt+n+1); + t.w.tag.file.buf.read(0, b, 0, nt); + slash = -1; + for(m=0; m<nt; m++){ + c = b.s[m]; + if(c == '/') + slash = m; + if(c==' ' || c=='\t') + break; + } + if(slash < 0) + raise "e"; + for (i := 0; i < n; i++) + b.s[slash+1+i] = r[i]; + r = nil; + return cleanname(b.s, slash+1+n); + } + exception{ + * => + b = nil; + if(r != nil) + return cleanname(r, n); + return (r, n); + } + return (nil, 0); +} + +expandfile(t : ref Text, q0 : int, q1 : int, e : Expand) : (int, Expand) +{ + i, n, nname, colon : int; + amin, amax : int; + r : ref Astring; + c : int; + w : ref Window; + + amax = q1; + if(q1 == q0){ + colon = -1; + while(q1<t.file.buf.nc && isfilec(c=t.readc(q1))){ + if(c == ':'){ + colon = q1; + break; + } + q1++; + } + while(q0>0 && (isfilec(c=t.readc(q0-1)) || isaddrc(c) || isregexc(c))){ + q0--; + if(colon==-1 && c==':') + colon = q0; + } + # + # if it looks like it might begin file: , consume address chars after : + # otherwise terminate expansion at : + # + + if(colon>=0 && colon<t.file.buf.nc-1 && isaddrc(t.readc(colon+1))){ + q1 = colon+1; + while(q1<t.file.buf.nc-1 && isaddrc(t.readc(q1))) + q1++; + }else if(colon >= 0) + q1 = colon; + if(q1 > q0) + if(colon >= 0){ # stop at white space + for(amax=colon+1; amax<t.file.buf.nc; amax++) + if((c=t.readc(amax))==' ' || c=='\t' || c=='\n') + break; + }else + amax = t.file.buf.nc; + } + amin = amax; + e.q0 = q0; + e.q1 = q1; + n = q1-q0; + if(n == 0) + return (FALSE, e); + # see if it's a file name + r = stralloc(n); + t.file.buf.read(q0, r, 0, n); + # first, does it have bad chars? + nname = -1; + for(i=0; i<n; i++){ + c = r.s[i]; + if(c==':' && nname<0){ + if(q0+i+1<t.file.buf.nc && (i==n-1 || isaddrc(t.readc(q0+i+1)))) + amin = q0+i; + else { + strfree(r); + r = nil; + return (FALSE, e); + } + nname = i; + } + } + if(nname == -1) + nname = n; + for(i=0; i<nname; i++) + if(!isfilec(r.s[i])) { + strfree(r); + r = nil; + return (FALSE, e); + } + # + # See if it's a file name in <>, and turn that into an include + # file name if so. Should probably do it for "" too, but that's not + # restrictive enough syntax and checking for a #include earlier on the + # line would be silly. + # + + isfile := 0; + if(q0>0 && t.readc(q0-1)=='<' && q1<t.file.buf.nc && t.readc(q1)=='>') + (r.s, nname) = includename(t, r.s, nname); + else if(q0>0 && t.readc(q0-1)=='"' && q1<t.file.buf.nc && t.readc(q1)=='"') + (r.s, nname) = includename(t, r.s, nname); + else if(amin == q0) + isfile = 1; + else + (r.s, nname) = dirname(t, r.s, nname); + if (!isfile) { + e.bname = r.s; + # if it's already a window name, it's a file + w = lookfile(r.s, nname); + # if it's the name of a file, it's a file + if(w == nil && utils->access(e.bname) < 0){ + e.bname = nil; + strfree(r); + r = nil; + return (FALSE, e); + } + } + + e.name = r.s[0:nname]; + e.at = t; + e.a0 = amin+1; + (nil, e.a1, nil) = address(nil, nil, (Range)(-1,-1), (Range)(0, 0), t, nil, e.a0, amax, FALSE); + strfree(r); + r = nil; + return (TRUE, e); +} + +expand(t : ref Text, q0 : int, q1 : int) : (int, Expand) +{ + e : Expand; + ok : int; + + e.q0 = e.q1 = e.a0 = e.a1 = 0; + e.name = e.bname = nil; + e.at = nil; + # if in selection, choose selection + e.jump = TRUE; + if(q1==q0 && t.q1>t.q0 && t.q0<=q0 && q0<=t.q1){ + q0 = t.q0; + q1 = t.q1; + if(t.what == Textm->Tag) + e.jump = FALSE; + } + + (ok, e) = expandfile(t, q0, q1, e); + if (ok) + return (TRUE, e); + + if(q0 == q1){ + while(q1<t.file.buf.nc && isalnum(t.readc(q1))) + q1++; + while(q0>0 && isalnum(t.readc(q0-1))) + q0--; + } + e.q0 = q0; + e.q1 = q1; + return (q1 > q0, e); +} + +lookfile(s : string, n : int) : ref Window +{ + i, j, k : int; + w : ref Window; + c : ref Column; + t : ref Text; + + # avoid terminal slash on directories + if(n > 1 && s[n-1] == '/') + --n; + for(j=0; j<row.ncol; j++){ + c = row.col[j]; + for(i=0; i<c.nw; i++){ + w = c.w[i]; + t = w.body; + k = len t.file.name; + if(k>0 && t.file.name[k-1] == '/') + k--; + if(t.file.name[0:k] == s[0:n]){ + w = w.body.file.curtext.w; + if(w.col != nil) # protect against race deleting w + return w; + } + } + } + return nil; +} + +lookid(id : int, dump : int) : ref Window +{ + i, j : int; + w : ref Window; + c : ref Column; + + for(j=0; j<row.ncol; j++){ + c = row.col[j]; + for(i=0; i<c.nw; i++){ + w = c.w[i]; + if(dump && w.dumpid == id) + return w; + if(!dump && w.id == id) + return w; + } + } + return nil; +} + +openfile(t : ref Text, e : Expand) : (ref Window, Expand) +{ + r : Range; + w, ow : ref Window; + eval, i, n : int; + + if(e.name == nil){ + w = t.w; + if(w == nil) + return (nil, e); + }else + w = lookfile(e.name, len e.name); + if(w != nil){ + t = w.body; + if(!t.col.safe && t.frame.maxlines==0) # window is obscured by full-column window + t.col.grow(t.col.w[0], 1, 1); + } + else{ + ow = nil; + if(t != nil) + ow = t.w; + w = utils->newwindow(t); + t = w.body; + w.setname(e.name, len e.name); + t.loadx(0, e.bname, 1); + t.file.mod = FALSE; + t.w.dirty = FALSE; + t.w.settag(); + t.w.tag.setselect(t.w.tag.file.buf.nc, t.w.tag.file.buf.nc); + if(ow != nil) + for(i=ow.nincl; --i>=0; ){ + n = len ow.incl[i]; + w.addincl(ow.incl[i], n); # really do want to copy here + } + } + if(e.a1 == e.a0) + eval = FALSE; + else + (eval, nil, r) = address(nil, t, (Range)(-1, -1), (Range)(t.q0, t.q1), e.at, e.ar, e.a0, e.a1, TRUE); + # was (eval, nil, r) = address(nil, t, (Range)(-1, -1), (Range)(t.q0, t.q1), e.at, nil, e.a0, e.a1, TRUE); + if(eval == FALSE){ + r.q0 = t.q0; + r.q1 = t.q1; + } + t.show(r.q0, r.q1); + t.w.settag(); + seltext = t; + if(e.jump) + cursorset(frptofchar(t.frame, t.frame.p0).add((4, t.frame.font.height-4))); + return (w, e); +} + +new(et : ref Text, t : ref Text, argt : ref Text, flag1 : int, flag2 : int, arg : string, narg : int) +{ + ndone : int; + a, f : string; + na, nf : int; + e : Expand; + + (nil, a, na) = exec->getarg(argt, FALSE, TRUE); + if(a != nil){ + new(et, t, nil, flag1, flag2, a, na); + if(narg == 0) + return; + } + # loop condition: *arg is not a blank + for(ndone=0; ; ndone++){ + (a, na) = utils->findbl(arg, narg); + if(a == arg){ + if(ndone==0 && et.col!=nil) + et.col.add(nil, nil, -1).settag(); + break; + } + nf = narg-na; + f = arg[0:nf]; # want a copy + (f, nf) = dirname(et, f, nf); + e.q0 = e.q1 = e.a0 = e.a1 = 0; + e.at = nil; + e.name = f; + e.bname = f; + e.jump = TRUE; + (nil, e) = openfile(et, e); + f = nil; + e.bname = nil; + (arg, narg) = utils->skipbl(a, na); + } +} |
