shithub: purgatorio

ref: a411870ee4640241e3c494367d922847da84f972
dir: /appl/acme/file.b/

View raw version
implement Filem;

include "common.m";

sys : Sys;
dat : Dat;
utils : Utils;
buffm : Bufferm;
textm : Textm;
editlog: Editlog;

FALSE, TRUE, XXX, Delete, Insert, Filename, BUFSIZE, Astring : import Dat;
Buffer, newbuffer : import buffm;
Text : import textm;
error : import utils;

init(mods : ref Dat->Mods)
{
	sys = mods.sys;
	dat = mods.dat;
	utils = mods.utils;
	buffm = mods.bufferm;
	textm = mods.textm;
	editlog = mods.editlog;
}

#
# Structure of Undo list:
# 	The Undo structure follows any associated data, so the list
#	can be read backwards: read the structure, then read whatever
#	data is associated (insert string, file name) and precedes it.
#	The structure includes the previous value of the modify bit
#	and a sequence number; successive Undo structures with the
#	same sequence number represent simultaneous changes.
#
 
Undo : adt
{
	typex : int;	# Delete, Insert, Filename 
	mod : int;		# modify bit 
	seq : int;		# sequence number 
	p0 : int;		# location of change (unused in f) 
	n : int;		# # runes in string or file name 
};

Undosize : con 8;
SHM : con 16rffff;

undotostr(t, m, s, p, n : int) : string
{
	a := "01234567";
	a[0] = t;
	a[1] = m;
	a[2] = s&SHM;
	a[3] = (s>>16)&SHM;
	a[4] = p&SHM;
	a[5] = (p>>16)&SHM;
	a[6] = n&SHM;
	a[7] = (n>>16)&SHM;
	return a;
}

strtoundo(s: string): Undo
{
	u: Undo;

	u.typex = s[0];
	u.mod = s[1];
	u.seq = s[2]|(s[3]<<16);
	u.p0 = s[4]|(s[5]<<16);
	u.n = s[6]|(s[7]<<16);
	return u;
}

nullfile : File;

File.addtext(f : self ref File, t : ref Text) : ref File
{
	if(f == nil) {
		f = ref nullfile;
		f.buf = newbuffer();
		f.delta = newbuffer();
		f.epsilon = newbuffer();
		f.ntext = 0;
		f.unread = TRUE;
	}
	oft := f.text;
	f.text = array[f.ntext+1] of ref Text;
	f.text[0:] = oft[0:f.ntext];
	oft = nil;
	f.text[f.ntext++] = t;
	f.curtext = t;
	return f;
}

File.deltext(f : self ref File, t : ref Text)
{
	i : int;

	for(i=0; i<f.ntext; i++)
		if(f.text[i] == t)
			break;
	if (i == f.ntext)
		error("can't find text in File.deltext");

	f.ntext--;
	if(f.ntext == 0){
		f.close();
		return;
	}
	f.text[i:] = f.text[i+1:f.ntext+1];
	if(f.curtext == t)
		f.curtext = f.text[0];
}

File.insert(f : self ref File, p0 : int, s : string, ns : int)
{
	if (p0 > f.buf.nc)
		error("bad assert in File.insert");
	if(f.seq > 0)
		f.uninsert(f.delta, p0, ns);
	f.buf.insert(p0, s, ns);
	if(ns)
		f.mod = TRUE;
}

File.uninsert(f : self ref File, delta : ref Buffer, p0 : int, ns : int)
{
	# undo an insertion by deleting 
	a := undotostr(Delete, f.mod, f.seq, p0, ns);
	delta.insert(delta.nc, a, Undosize);
}

File.delete(f : self ref File, p0 : int, p1 : int)
{
	if (p0>p1 || p0>f.buf.nc || p1>f.buf.nc)
		error("bad assert in File.delete");
	if(f.seq > 0)
		f.undelete(f.delta, p0, p1);
	f.buf.delete(p0, p1);
	if(p1 > p0)
		f.mod = TRUE;
}

File.undelete(f : self ref File, delta : ref Buffer, p0 : int, p1 : int)
{
	buf : ref Astring;
	i, n : int;

	# undo a deletion by inserting 
	a := undotostr(Insert, f.mod, f.seq, p0, p1-p0);
	m := p1-p0;
	if(m > BUFSIZE)
		m = BUFSIZE;
	buf = utils->stralloc(m);
	for(i=p0; i<p1; i+=n){
		n = p1 - i;
		if(n > BUFSIZE)
			n = BUFSIZE;
		f.buf.read(i, buf, 0, n);
		delta.insert(delta.nc, buf.s, n);
	}
	utils->strfree(buf);
	buf = nil;
	delta.insert(delta.nc, a, Undosize);
}

File.setname(f : self ref File, name : string, n : int)
{
	if(f.seq > 0)
		f.unsetname(f.delta);
	f.name = name[0:n];
	f.unread = TRUE;
}

File.unsetname(f : self ref File, delta : ref Buffer)
{
	# undo a file name change by restoring old name 
	a := undotostr(Filename, f.mod, f.seq, 0, len f.name);
	if(f.name != nil)
		delta.insert(delta.nc, f.name, len f.name);
	delta.insert(delta.nc, a, Undosize);
}

File.loadx(f : self ref File, p0 : int, fd : ref Sys->FD) : int
{
	if(f.seq > 0)
		error("undo in file.load unimplemented");
	return f.buf.loadx(p0, fd);
}

File.undo(f : self ref File, isundo : int, q0 : int, q1 : int) : (int, int)
{
	buf : ref Astring;
	i, j, n, up : int;
	stop : int;
	delta, epsilon : ref Buffer;
	u : Undo;

	a := utils->stralloc(Undosize);
	if(isundo){
		# undo; reverse delta onto epsilon, seq decreases 
		delta = f.delta;
		epsilon = f.epsilon;
		stop = f.seq;
	}else{
		# redo; reverse epsilon onto delta, seq increases 
		delta = f.epsilon;
		epsilon = f.delta;
		stop = 0;	# don't know yet 
	}

	buf = utils->stralloc(BUFSIZE);
	while(delta.nc > 0){
		up = delta.nc-Undosize;
		delta.read(up, a, 0, Undosize);
		u = strtoundo(a.s);
		if(isundo){
			if(u.seq < stop){
				f.seq = u.seq;
				utils->strfree(buf);
				utils->strfree(a);
				return (q0, q1);
			}
		}else{
			if(stop == 0)
				stop = u.seq;
			if(u.seq > stop){
				utils->strfree(buf);
				utils->strfree(a);
				return (q0, q1);
			}
		}
		case(u.typex){
		Delete =>
			f.seq = u.seq;
			f.undelete(epsilon, u.p0, u.p0+u.n);
			f.mod = u.mod;
			f.buf.delete(u.p0, u.p0+u.n);
			for(j=0; j<f.ntext; j++)
				f.text[j].delete(u.p0, u.p0+u.n, FALSE);
			q0 = u.p0;
			q1 = u.p0;
		Insert =>
			f.seq = u.seq;
			f.uninsert(epsilon, u.p0, u.n);
			f.mod = u.mod;
			up -= u.n;
			# buf = utils->stralloc(BUFSIZE);
			for(i=0; i<u.n; i+=n){
				n = u.n - i;
				if(n > BUFSIZE)
					n = BUFSIZE;
				delta.read(up+i, buf, 0, n);
				f.buf.insert(u.p0+i, buf.s, n);
				for(j=0; j<f.ntext; j++)
					f.text[j].insert(u.p0+i, buf.s, n, FALSE, 0);
			}
			# utils->strfree(buf);
			# buf = nil;
			q0 = u.p0;
			q1 = u.p0+u.n;
		Filename =>
			f.seq = u.seq;
			f.unsetname(epsilon);
			f.mod = u.mod;
			up -= u.n;
			f.name = nil;
			if(u.n == 0)
				f.name = nil;
			else {
				fn0 := utils->stralloc(u.n);
				delta.read(up, fn0, 0, u.n);
				f.name = fn0.s;
				utils->strfree(fn0);
				fn0 = nil;
			}
		* =>
			error(sys->sprint("undo: 0x%ux", u.typex));
			error("");
		}
		delta.delete(up, delta.nc);
	}
	utils->strfree(buf);
	utils->strfree(a);
	buf = nil;
	if(isundo)
		f.seq = 0;
	return (q0, q1);
}

File.reset(f : self ref File)
{
	f.delta.reset();
	f.epsilon.reset();
	f.seq = 0;
}

File.close(f : self ref File)
{
	f.name = nil;
	f.ntext = 0;
	f.text = nil;
	f.buf.close();
	f.delta.close();
	f.epsilon.close();
	editlog->elogclose(f);
	f = nil;
}

File.mark(f : self ref File)
{
	if(f.epsilon.nc)
		f.epsilon.delete(0, f.epsilon.nc);
	f.seq = dat->seq;
}

File.redoseq(f : self ref File): int
{
	u: Undo;
	delta: ref Buffer;

	delta = f.epsilon;
	if(delta.nc == 0)
		return ~0;
	buf := utils->stralloc(Undosize);
	delta.read(delta.nc-Undosize, buf, 0, Undosize);
	u = strtoundo(buf.s);
	utils->strfree(buf);
	return u.seq;
}