code: purgatorio

ref: acc58dfee42368825f7595d63240fd6e88559776
dir: /appl/spree/lib/commandline.b/

View raw version
implement Commandline;

include "sys.m";
	sys: Sys;
include "draw.m";
include "tk.m";
	tk: Tk;
include "tkclient.m";
	tkclient: Tkclient;
include "commandline.m";

Debug: con 0;

nomodule(modpath: string)
{
	sys->fprint(stderr(), "fibs: couldn't load %s: %r\n", modpath);
	raise "fail:bad module";
}

init()
{	sys = load Sys Sys->PATH;

	tk = load Tk Tk->PATH;
	if (tk == nil) nomodule(Tk->PATH);

	tkclient = load Tkclient Tkclient->PATH;
	if (tkclient == nil) nomodule(Tkclient->PATH);
	tkclient->init();
}

Cmdline.new(top: ref Tk->Toplevel, w, textopts: string): (ref Cmdline, chan of string)
{
	window_cfg := array[] of {
		"frame " + w,
		"scrollbar " + w + ".scroll -command {" + w + ".t yview}",
		"text " + w + ".t -yscrollcommand {" + w + ".scroll set} " + textopts,
		"pack " + w + ".scroll -side left -fill y",
		"pack " + w + ".t -fill both -expand 1",
	
		"bind " + w + ".t <Key> {send evch k {%A}}",
		"bind " + w + ".t <Control-d> {send evch k {%A}}",
		"bind " + w + ".t <Control-u> {send evch k {%A}}",
		"bind " + w + ".t <Control-w> {send evch k {%A}}",
		"bind " + w + ".t <Control-h> {send evch k {%A}}",
		# treat button 2 and button 3 the same so we're alright with a 2-button mouse
		"bind " + w + ".t <ButtonPress-2> {send evch b %x %y}",
		"bind " + w + ".t <ButtonPress-3> {send evch b %x %y}",
		w + ".t mark set outpoint end",
		w + ".t mark gravity outpoint left",
		w + ".t mark set inpoint end",
		w + ".t mark gravity inpoint left",
	};
	evch := chan of string;
	tk->namechan(top, evch, "evch");

	for (i := 0; i < len window_cfg; i++) {
		e := cmd(top, window_cfg[i]);
		if (e != nil && e[0] == '!')
			break;
	}

	err := tk->cmd(top, "variable lasterror");
	if (err != nil) {
		sys->fprint(stderr(), "error in commandline config: %s\n", err);
		raise "fail:commandline config error";
	}
	cmd(top, w + ".t mark set insert end;" + w + ".t see insert");
	return (ref Cmdline(w, top), evch);
}

Cmdline.focus(cmdl: self ref Cmdline)
{
	cmd(cmdl.top, "focus " + cmdl.w + ".t");
}

Cmdline.event(cmdl: self ref Cmdline, e: string): list of string
{
	case e[0] {
	'k' =>
		return handle_key(cmdl, e[2:]);
	'b' =>
		;
	}
	return nil;
}

BS:		con 8;		# ^h backspace character
BSW:		con 23;		# ^w bacspace word
BSL:		con 21;		# ^u backspace line

handle_key(cmdl: ref Cmdline, c: string): list of string
{
	(w, top) := (cmdl.w, cmdl.top);
	# don't allow editing of the text before the inpoint.
	if (int cmd(top, w + ".t compare insert < inpoint"))
		return nil;
	lines: list of string;
	char := c[1];
	if (char == '\\')
		char = c[2];
	case char {
	* =>
		cmd(top, w + ".t insert insert "+c+" {}");
	'\n' =>
		cmd(top, w + ".t insert insert "+c+" {}");
		lines = sendinput(cmdl);
	BSL or BSW or BS =>
		delpoint: string;
		case char {
		BSL =>	delpoint = "{insert linestart}";
		BSW =>	delpoint = "{insert -1char wordstart}";	# wordstart isn't ideal
		BS  =>	delpoint = "{insert-1char}";
		}
		if (int cmd(top, w + ".t compare inpoint < " + delpoint))
			cmd(top, w + ".t delete "+delpoint+" insert");
		else
			cmd(top, w + ".t delete inpoint insert");
	}
	cmd(top, w + ".t see insert;update");
	return lines;
}

sendinput(cmdl: ref Cmdline): list of string
{
	(w, top) := (cmdl.w, cmdl.top);
	# loop through all the lines that have been entered,
	# processing each one in turn.
	nl, lines: list of string;
	for (;;) {
		input: string;
		input = cmd(top, w + ".t get inpoint end");
		if (len input == 0)
			break;
		for (i := 0; i < len input; i++)
			if (input[i] == '\n')
				break;
		if (i >= len input)
			break;
		cmd(top, w + ".t mark set outpoint inpoint+"+string (i+1)+"chars");
		cmd(top, w + ".t mark set inpoint outpoint");
		lines = input[0:i+1] :: lines;
	}
	for (; lines != nil; lines = tl lines)
		nl = hd lines :: nl;
	return nl;
}

add(cmdl: ref Cmdline, t: string, n: int)
{
	(w, top) := (cmdl.w, cmdl.top);
	cmd(top, w + ".t insert outpoint " + t);
	cmd(top, w + ".t mark set outpoint outpoint+"+string n+"chars");
	cmd(top, w + ".t mark set inpoint outpoint");
	cmd(top, w + ".t see insert");
}

Cmdline.tagaddtext(cmdl: self ref Cmdline, t: list of (string, string))
{
	txt := "";
	n := 0;
	for (; t != nil; t = tl t) {
		(tags, s) := hd t;
		txt += " " + tk->quote(s) + " {" + tags + "}";
		n += len s;
	}
	add(cmdl, txt, n);
}

Cmdline.addtext(cmdl: self ref Cmdline, txt: string)
{
	if (Debug) sys->print("%s", txt);
	add(cmdl, tk->quote(txt) + " {}" , len txt);
}

Cmdline.maketag(cmdl: self ref Cmdline, name, options: string)
{
	cmd(cmdl.top, cmdl.w + ".t tag configure " + name + " " + options);
}

stderr(): ref Sys->FD
{
	return sys->fildes(2);
}

cmd(top: ref Tk->Toplevel, s: string): string
{
	e := tk->cmd(top, s);
	if (e != nil && e[0] == '!')
		sys->fprint(stderr(), "cmd error on '%s': %s\n", s, e);
	return e;
}