# 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 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 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 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; }