1implement Commandline; 2 3include "sys.m"; 4 sys: Sys; 5include "draw.m"; 6include "tk.m"; 7 tk: Tk; 8include "tkclient.m"; 9 tkclient: Tkclient; 10include "commandline.m"; 11 12Debug: con 0; 13 14nomodule(modpath: string) 15{ 16 sys->fprint(stderr(), "fibs: couldn't load %s: %r\n", modpath); 17 raise "fail:bad module"; 18} 19 20init() 21{ sys = load Sys Sys->PATH; 22 23 tk = load Tk Tk->PATH; 24 if (tk == nil) nomodule(Tk->PATH); 25 26 tkclient = load Tkclient Tkclient->PATH; 27 if (tkclient == nil) nomodule(Tkclient->PATH); 28 tkclient->init(); 29} 30 31Cmdline.new(top: ref Tk->Toplevel, w, textopts: string): (ref Cmdline, chan of string) 32{ 33 window_cfg := array[] of { 34 "frame " + w, 35 "scrollbar " + w + ".scroll -command {" + w + ".t yview}", 36 "text " + w + ".t -yscrollcommand {" + w + ".scroll set} " + textopts, 37 "pack " + w + ".scroll -side left -fill y", 38 "pack " + w + ".t -fill both -expand 1", 39 40 "bind " + w + ".t <Key> {send evch k {%A}}", 41 "bind " + w + ".t <Control-d> {send evch k {%A}}", 42 "bind " + w + ".t <Control-u> {send evch k {%A}}", 43 "bind " + w + ".t <Control-w> {send evch k {%A}}", 44 "bind " + w + ".t <Control-h> {send evch k {%A}}", 45 # treat button 2 and button 3 the same so we're alright with a 2-button mouse 46 "bind " + w + ".t <ButtonPress-2> {send evch b %x %y}", 47 "bind " + w + ".t <ButtonPress-3> {send evch b %x %y}", 48 w + ".t mark set outpoint end", 49 w + ".t mark gravity outpoint left", 50 w + ".t mark set inpoint end", 51 w + ".t mark gravity inpoint left", 52 }; 53 evch := chan of string; 54 tk->namechan(top, evch, "evch"); 55 56 for (i := 0; i < len window_cfg; i++) { 57 e := cmd(top, window_cfg[i]); 58 if (e != nil && e[0] == '!') 59 break; 60 } 61 62 err := tk->cmd(top, "variable lasterror"); 63 if (err != nil) { 64 sys->fprint(stderr(), "error in commandline config: %s\n", err); 65 raise "fail:commandline config error"; 66 } 67 cmd(top, w + ".t mark set insert end;" + w + ".t see insert"); 68 return (ref Cmdline(w, top), evch); 69} 70 71Cmdline.focus(cmdl: self ref Cmdline) 72{ 73 cmd(cmdl.top, "focus " + cmdl.w + ".t"); 74} 75 76Cmdline.event(cmdl: self ref Cmdline, e: string): list of string 77{ 78 case e[0] { 79 'k' => 80 return handle_key(cmdl, e[2:]); 81 'b' => 82 ; 83 } 84 return nil; 85} 86 87BS: con 8; # ^h backspace character 88BSW: con 23; # ^w bacspace word 89BSL: con 21; # ^u backspace line 90 91handle_key(cmdl: ref Cmdline, c: string): list of string 92{ 93 (w, top) := (cmdl.w, cmdl.top); 94 # don't allow editing of the text before the inpoint. 95 if (int cmd(top, w + ".t compare insert < inpoint")) 96 return nil; 97 lines: list of string; 98 char := c[1]; 99 if (char == '\\') 100 char = c[2]; 101 case char { 102 * => 103 cmd(top, w + ".t insert insert "+c+" {}"); 104 '\n' => 105 cmd(top, w + ".t insert insert "+c+" {}"); 106 lines = sendinput(cmdl); 107 BSL or BSW or BS => 108 delpoint: string; 109 case char { 110 BSL => delpoint = "{insert linestart}"; 111 BSW => delpoint = "{insert -1char wordstart}"; # wordstart isn't ideal 112 BS => delpoint = "{insert-1char}"; 113 } 114 if (int cmd(top, w + ".t compare inpoint < " + delpoint)) 115 cmd(top, w + ".t delete "+delpoint+" insert"); 116 else 117 cmd(top, w + ".t delete inpoint insert"); 118 } 119 cmd(top, w + ".t see insert;update"); 120 return lines; 121} 122 123sendinput(cmdl: ref Cmdline): list of string 124{ 125 (w, top) := (cmdl.w, cmdl.top); 126 # loop through all the lines that have been entered, 127 # processing each one in turn. 128 nl, lines: list of string; 129 for (;;) { 130 input: string; 131 input = cmd(top, w + ".t get inpoint end"); 132 if (len input == 0) 133 break; 134 for (i := 0; i < len input; i++) 135 if (input[i] == '\n') 136 break; 137 if (i >= len input) 138 break; 139 cmd(top, w + ".t mark set outpoint inpoint+"+string (i+1)+"chars"); 140 cmd(top, w + ".t mark set inpoint outpoint"); 141 lines = input[0:i+1] :: lines; 142 } 143 for (; lines != nil; lines = tl lines) 144 nl = hd lines :: nl; 145 return nl; 146} 147 148add(cmdl: ref Cmdline, t: string, n: int) 149{ 150 (w, top) := (cmdl.w, cmdl.top); 151 cmd(top, w + ".t insert outpoint " + t); 152 cmd(top, w + ".t mark set outpoint outpoint+"+string n+"chars"); 153 cmd(top, w + ".t mark set inpoint outpoint"); 154 cmd(top, w + ".t see insert"); 155} 156 157Cmdline.tagaddtext(cmdl: self ref Cmdline, t: list of (string, string)) 158{ 159 txt := ""; 160 n := 0; 161 for (; t != nil; t = tl t) { 162 (tags, s) := hd t; 163 txt += " " + tk->quote(s) + " {" + tags + "}"; 164 n += len s; 165 } 166 add(cmdl, txt, n); 167} 168 169Cmdline.addtext(cmdl: self ref Cmdline, txt: string) 170{ 171 if (Debug) sys->print("%s", txt); 172 add(cmdl, tk->quote(txt) + " {}" , len txt); 173} 174 175Cmdline.maketag(cmdl: self ref Cmdline, name, options: string) 176{ 177 cmd(cmdl.top, cmdl.w + ".t tag configure " + name + " " + options); 178} 179 180stderr(): ref Sys->FD 181{ 182 return sys->fildes(2); 183} 184 185cmd(top: ref Tk->Toplevel, s: string): string 186{ 187 e := tk->cmd(top, s); 188 if (e != nil && e[0] == '!') 189 sys->fprint(stderr(), "cmd error on '%s': %s\n", s, e); 190 return e; 191} 192