summaryrefslogtreecommitdiff
path: root/appl/wm/brutus/excerpt.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/wm/brutus/excerpt.b')
-rw-r--r--appl/wm/brutus/excerpt.b264
1 files changed, 264 insertions, 0 deletions
diff --git a/appl/wm/brutus/excerpt.b b/appl/wm/brutus/excerpt.b
new file mode 100644
index 00000000..ccb0e647
--- /dev/null
+++ b/appl/wm/brutus/excerpt.b
@@ -0,0 +1,264 @@
+implement Brutusext;
+
+# <Extension excerpt file [start [end]]>
+
+Name: con "Brutus entry";
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+ draw: Draw;
+ Context: import draw;
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "regex.m";
+ regex: Regex;
+
+include "tk.m";
+ tk: Tk;
+
+include "tkclient.m";
+ tkclient: Tkclient;
+
+include "brutus.m";
+include "brutusext.m";
+
+init(s: Sys, d: Draw, b: Bufio, t: Tk, w: Tkclient)
+{
+ sys = s;
+ draw = d;
+ bufio = b;
+ tk = t;
+ tkclient = w;
+ regex = load Regex Regex->PATH;
+}
+
+create(parent: string, t: ref Tk->Toplevel, name, args: string): string
+{
+ (text, err) := gather(parent, args);
+ if(err != nil)
+ return err;
+ err = tk->cmd(t, "text "+name+" -tabs {1c} -wrap none -font /fonts/pelm/latin1.9.font");
+ if(len err > 0 && err[0] == '!')
+ return err;
+ (n, maxw) := nlines(text);
+ if(maxw < 40)
+ maxw = 40;
+ if(maxw > 70)
+ maxw = 70;
+ tk->cmd(t, name+" configure -height "+string n+".01h -width "+string maxw+"w");
+ return tk->cmd(t, name+" insert end '"+text);
+}
+
+gather(parent, args: string): (string, string)
+{
+ argl := tokenize(args);
+ nargs := len argl;
+ if(nargs == 0)
+ return (nil, "usage: excerpt [start] [end] file");
+ file := hd argl;
+ argl = tl argl;
+ b := bufio->open(fullname(parent, file), Bufio->OREAD);
+ if(b == nil)
+ return (nil, sys->sprint("can't open %s: %r", file));
+ start := "";
+ end := "";
+ if(argl != nil){
+ start = hd argl;
+ if(tl argl != nil)
+ end = hd tl argl;
+ }
+ (text, err) := readall(b, start, end);
+ return (text, err);
+}
+
+tokenize(s: string): list of string
+{
+ l: list of string;
+ i := 0;
+ a := "";
+ first := 1;
+ while(i < len s){
+ (a, i) = arg(first, s, i);
+ if(a != "")
+ l = a :: l;
+ first = 0;
+ }
+ rl: list of string;
+ while(l != nil){
+ rl = hd l :: rl;
+ l = tl l;
+ }
+ return rl;
+}
+
+arg(first: int, s: string, i: int): (string, int)
+{
+ while(i<len s && (s[i]==' ' || s[i]=='\t'))
+ i++;
+ if(i == len s)
+ return ("", i);
+ j := i+1;
+ if(first || s[i] != '/'){
+ while(j<len s && (s[j]!=' ' && s[j]!='\t'))
+ j++;
+ return (s[i:j], j);
+ }
+ while(j<len s && s[j]!='/')
+ if(s[j++] == '\\')
+ j++;
+ if(j == len s)
+ return (s[i:j], j);
+ return (s[i:j+1], j+1);
+}
+
+readall(b: ref Iobuf, start, end: string): (string, string)
+{
+ revlines : list of string = nil;
+ appending := 0;
+ lineno := 0;
+ for(;;){
+ line := b.gets('\n');
+ if(line == nil)
+ break;
+ lineno++;
+ if(!appending){
+ m := match(start, line, lineno);
+ if(m < 0)
+ return (nil, "error in pattern");
+ if(m)
+ appending = 1;
+ }
+ if(appending){
+ revlines = line :: revlines;
+ if(start != ""){
+ m := match(end, line, lineno);
+ if(m < 0)
+ return (nil, "error in pattern");
+ if(m)
+ break;
+ }
+ }
+ }
+ return (prep(revlines), "");
+}
+
+prep(revlines: list of string) : string
+{
+ tabstrip := -1;
+ for(l:=revlines; l != nil; l = tl l) {
+ s := hd l;
+ if(len s > 1) {
+ n := nleadtab(hd l);
+ if(tabstrip == -1 || n < tabstrip)
+ tabstrip = n;
+ }
+ }
+ # remove tabstrip tabs from each line
+ # and concatenate in reverse order
+ ans := "";
+ for(l=revlines; l != nil; l = tl l) {
+ s := hd l;
+ if(tabstrip > 0 && len s > 1)
+ s = s[tabstrip:];
+ ans = s + ans;
+ }
+ return ans;
+}
+
+nleadtab(s: string) : int
+{
+ slen := len s;
+ for(i:=0; i<slen; i++)
+ if(s[i] != '\t')
+ break;
+ return i;
+}
+
+nlines(s: string): (int, int)
+{
+ n := 0;
+ maxw := 0;
+ w := 0;
+ for(i:=0; i<len s; i++) {
+ if(s[i] == '\n') {
+ n++;
+ if(w > maxw)
+ maxw = w;
+ w = 0;
+ }
+ else if(s[i] == '\t')
+ w += 5;
+ else
+ w++;
+ }
+ if(len s>0 && s[len s-1]!='\n') {
+ n++;
+ if(w > maxw)
+ maxw = w;
+ }
+ return (n, maxw);
+}
+
+match(pat, line: string, lineno: int): int
+{
+ if(pat == "")
+ return 1;
+ case pat[0] {
+ '0' to '9' =>
+ return int pat <= lineno;
+ '/' =>
+ if(len pat < 3 || pat[len pat-1]!='/')
+ return -1;
+ re := compile(pat[1:len pat-1]);
+ if(re == nil)
+ return -1;
+ match := regex->execute(re, line);
+ return match != nil;
+ }
+ return -1;
+}
+
+pats: list of (string, Regex->Re);
+
+compile(pat: string): Regex->Re
+{
+ l := pats;
+ while(l != nil){
+ (p, r) := hd l;
+ if(p == pat)
+ return r;
+ l = tl l;
+ }
+ (re, nil) := regex->compile(pat, 0);
+ pats = (pat, re) :: pats;
+ return re;
+}
+
+cook(parent: string, nil: int, args: string): (ref Brutusext->Celem, string)
+{
+ (text, err) := gather(parent, args);
+ if(err != nil)
+ return (nil, err);
+ el1 := ref Brutusext->Celem(Brutusext->Text, text, nil, nil, nil, nil);
+ el2 := ref Brutusext->Celem(Brutus->Type*Brutus->NSIZE+Brutus->Size10, "", el1, nil, nil, nil);
+ el1.parent = el2;
+ ans := ref Brutusext->Celem(Brutus->Example, "", el2, nil, nil, nil);
+ el2.parent = ans;
+ return (ans, "");
+}
+
+fullname(parent, file: string): string
+{
+ if(len parent==0 || (len file>0 && (file[0]=='/' || file[0]=='#')))
+ return file;
+
+ for(i:=len parent-1; i>=0; i--)
+ if(parent[i] == '/')
+ return parent[0:i+1] + file;
+ return file;
+}