Plan 9 from Bell Labs’s /usr/web/sources/contrib/nemo/octopus/port/x/samlog.b

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


# Sam language and implementation taken from acme.
# This file was /appl/acme/elog.b, changed for o/x.
# Changed (read: broken for long files) to use a list of Elog entries
# because we keep edit logs in memory.

implement Samlog;
include "mods.m";
	Edit, Tree, Elog, Empty, Null, Elogbuf, Insert, Replace, Delete,
	Etext, seled, trees, Esel: import oxedit;
	warnc: import sam;

init(d: Oxdat)
{
	initmods(d->mods);
}

Wsequence := "warning: changes out of sequence\n";
warned := 0;

#
# Log of changes made by editing commands.  Three reasons for this:
# 1) We want addresses in commands to apply to old file, not file-in-change.
# 2) It's difficult to track changes correctly as things move, e.g. ,x m$
# 3) This gives an opportunity to optimize by merging adjacent changes.
# It's a little bit like the Undo/Redo log in Files, but Point 3) argues for a
# separate implementation.  To do this well, we use Replace as well as
# Insert and Delete
#

#
# Minstring shouldn't be very big or we will do lots of I/O for small changes.
# Maxstring just places a limit.
#
Minstring: con 16;	# distance beneath which we merge changes
Maxstring: con 4*1024;	# maximum length of change we will merge into one

eloginit(f: ref Edit)
{
	if (f.elog == nil)
		f.elog = ref Elog(Empty, 0, 0, "");
	if(f.elog.typex != Empty)
		return;
	f.edited = 0;
	f.elog.typex = Null;
	nullelog: ref Elog;
	nullelog = nil;
	f.elogbuf = Elogbuf.new();
	f.elog.r = "";
}

elogreset(f: ref Edit)
{
	f.elog.typex = Null;
	f.elog.nd = 0;
	f.elog.r = "";
}

elogterm(f: ref Edit)
{
	f.elogbuf = nil;
	f.elog = nil;
	warned = 0;
}

elogflush(f: ref Edit)
{
	case(f.elog.typex){
	* =>
		error(sprint("unknown elog type 0x%ux\n", f.elog.typex));
	Null =>
		break;
	Insert or
	Replace or
	Delete =>
		f.elogbuf.push(f.elog);
		break;
	}
	elogreset(f);
}

elogreplace(f: ref Edit, q0: int, q1: int, r: string)
{
	gap: int;

	if(q0==q1 && len r==0)
		return;
	eloginit(f);
	if(f.elog.typex!=Null && q0<f.elog.q0){
		if(warned++ == 0)
			warnc <-= Wsequence;
		elogflush(f);
	}
	# try to merge with previous
	gap = q0 - (f.elog.q0+f.elog.nd);	# gap between previous and this
	if(f.elog.typex==Replace && len f.elog.r+gap+len r<Maxstring){
		if(gap < Minstring){
			if(gap > 0)
				f.elog.r += f.buf.gets(f.elog.q0+f.elog.nd, gap);
			f.elog.nd += gap + q1-q0;
			f.elog.r += r;
			return;
		}
	}
	elogflush(f);
	f.elog.typex = Replace;
	f.elog.q0 = q0;
	f.elog.nd = q1-q0;
	f.elog.r = r;
}

eloginsert(f: ref Edit, q0: int, r: string)
{
	if(len r == 0)
		return;
	eloginit(f);
	if(f.elog.typex!=Null && q0<f.elog.q0){
		if(warned++ == 0)
			warnc <-= Wsequence;
		elogflush(f);
	}
	# try to merge with previous
	if(f.elog.typex==Insert && q0==f.elog.q0 && len f.elog.r+len r<Maxstring){
		f.elog.r += r;
		return;
	}
	if(len r > 0){
		elogflush(f);
		f.elog.typex = Insert;
		f.elog.q0 = q0;
		f.elog.r = r;
	}
}

elogdelete(f: ref Edit, q0: int, q1: int)
{
	if(q0 == q1)
		return;
	eloginit(f);
	if(f.elog.typex!=Null && q0<f.elog.q0+f.elog.nd){
		if(warned++ == 0)
			warnc <-= Wsequence;
		elogflush(f);
	}
	#  try to merge with previous
	if(f.elog.typex==Delete && f.elog.q0+f.elog.nd==q0){
		f.elog.nd += q1-q0;
		return;
	}
	elogflush(f);
	f.elog.typex = Delete;
	f.elog.q0 = q0;
	f.elog.nd = q1-q0;
}

elogapply(f: ref Edit): int
{
	elogflush(f);
	log := f.elogbuf;
	#
	# The edit commands have already updated the selection in t.q0, t.q1.
	# The text.insert and text.delete calls below will update it again, so save the
	# current setting and restore it at the end.
	#
	f.edited |= Esel;
	q0 := f.q0;
	q1 := f.q1;
	mod := 0;
	while( (b := log.pop()) != nil){
		case(b.typex){
		* =>
			error(sprint("elogapply: 0x%ux\n", b.typex));
			break;

		Replace =>
			mod++;
			f.buf.del(b.nd, b.q0);
			f.buf.ins(b.r, b.q0);
			f.edited |= Etext;
			break;

		Delete =>
			mod++;
			f.buf.del(b.nd, b.q0);
			f.edited |= Etext;
			break;

		Insert =>
			mod++;
			f.buf.ins(b.r, b.q0);
			f.edited |= Etext;
			break;
		}
	}
	elogterm(f);
	f.q0 = q0;
	f.q1 = q1;
	return mod;
}


Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.