📄 elog.c
字号:
#include <u.h>#include <libc.h>#include <draw.h>#include <thread.h>#include <cursor.h>#include <mouse.h>#include <keyboard.h>#include <frame.h>#include <fcall.h>#include <plumb.h>#include "dat.h"#include "fns.h"#include "edit.h"static char Wsequence[] = "warning: changes out of sequence\n";static int warned = FALSE;/* * 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 */typedef struct Buflog Buflog;struct Buflog{ short type; /* Replace, Filename */ uint q0; /* location of change (unused in f) */ uint nd; /* # runes to delete */ uint nr; /* # runes in string or file name */};enum{ Buflogsize = sizeof(Buflog)/sizeof(Rune),};/* * Minstring shouldn't be very big or we will do lots of I/O for small changes. * Maxstring is RBUFSIZE so we can fbufalloc() once and not realloc elog.r. */enum{ Minstring = 16, /* distance beneath which we merge changes */ Maxstring = RBUFSIZE, /* maximum length of change we will merge into one */};voideloginit(File *f){ if(f->elog.type != Empty) return; f->elog.type = Null; if(f->elogbuf == nil) f->elogbuf = emalloc(sizeof(Buffer)); if(f->elog.r == nil) f->elog.r = fbufalloc(); bufreset(f->elogbuf);}voidelogclose(File *f){ if(f->elogbuf){ bufclose(f->elogbuf); free(f->elogbuf); f->elogbuf = nil; }}voidelogreset(File *f){ f->elog.type = Null; f->elog.nd = 0; f->elog.nr = 0;}voidelogterm(File *f){ elogreset(f); if(f->elogbuf) bufreset(f->elogbuf); f->elog.type = Empty; fbuffree(f->elog.r); f->elog.r = nil; warned = FALSE;}voidelogflush(File *f){ Buflog b; b.type = f->elog.type; b.q0 = f->elog.q0; b.nd = f->elog.nd; b.nr = f->elog.nr; switch(f->elog.type){ default: warning(nil, "unknown elog type 0x%ux\n", f->elog.type); break; case Null: break; case Insert: case Replace: if(f->elog.nr > 0) bufinsert(f->elogbuf, f->elogbuf->nc, f->elog.r, f->elog.nr); /* fall through */ case Delete: bufinsert(f->elogbuf, f->elogbuf->nc, (Rune*)&b, Buflogsize); break; } elogreset(f);}voidelogreplace(File *f, int q0, int q1, Rune *r, int nr){ uint gap; if(q0==q1 && nr==0) return; eloginit(f); if(f->elog.type!=Null && q0<f->elog.q0){ if(warned++ == 0) warning(nil, Wsequence); elogflush(f); } /* try to merge with previous */ gap = q0 - (f->elog.q0+f->elog.nd); /* gap between previous and this */ if(f->elog.type==Replace && f->elog.nr+gap+nr<Maxstring){ if(gap < Minstring){ if(gap > 0){ bufread(f, f->elog.q0+f->elog.nd, f->elog.r+f->elog.nr, gap); f->elog.nr += gap; } f->elog.nd += gap + q1-q0; runemove(f->elog.r+f->elog.nr, r, nr); f->elog.nr += nr; return; } } elogflush(f); f->elog.type = Replace; f->elog.q0 = q0; f->elog.nd = q1-q0; f->elog.nr = nr; if(nr > RBUFSIZE) editerror("internal error: replacement string too large(%d)", nr); runemove(f->elog.r, r, nr);}voideloginsert(File *f, int q0, Rune *r, int nr){ int n; if(nr == 0) return; eloginit(f); if(f->elog.type!=Null && q0<f->elog.q0){ if(warned++ == 0) warning(nil, Wsequence); elogflush(f); } /* try to merge with previous */ if(f->elog.type==Insert && q0==f->elog.q0 && f->elog.nr+nr<Maxstring){ runemove(f->elog.r+f->elog.nr, r, nr); f->elog.nr += nr; return; } while(nr > 0){ elogflush(f); f->elog.type = Insert; f->elog.q0 = q0; n = nr; if(n > RBUFSIZE) n = RBUFSIZE; f->elog.nr = n; runemove(f->elog.r, r, n); r += n; nr -= n; }}voidelogdelete(File *f, int q0, int q1){ if(q0 == q1) return; eloginit(f); if(f->elog.type!=Null && q0<f->elog.q0+f->elog.nd){ if(warned++ == 0) warning(nil, Wsequence); elogflush(f); } /* try to merge with previous */ if(f->elog.type==Delete && f->elog.q0+f->elog.nd==q0){ f->elog.nd += q1-q0; return; } elogflush(f); f->elog.type = Delete; f->elog.q0 = q0; f->elog.nd = q1-q0;}#define tracelog 0voidelogapply(File *f){ Buflog b; Rune *buf; uint i, n, up, mod; uint tq0, tq1; Buffer *log; Text *t; elogflush(f); log = f->elogbuf; t = f->curtext; buf = fbufalloc(); mod = FALSE; /* * The edit commands have already updated the selection in t->q0, t->q1, * but using coordinates relative to the unmodified buffer. As we apply the log, * we have to update the coordinates to be relative to the modified buffer. * Textinsert and textdelete will do this for us; our only work is to apply the * convention that an insertion at t->q0==t->q1 is intended to select the * inserted text. */ /* * We constrain the addresses in here (with textconstrain()) because * overlapping changes will generate bogus addresses. We will warn * about changes out of sequence but proceed anyway; here we must * keep things in range. */ while(log->nc > 0){ up = log->nc-Buflogsize; bufread(log, up, (Rune*)&b, Buflogsize); switch(b.type){ default: fprint(2, "elogapply: 0x%ux\n", b.type); abort(); break; case Replace: if(tracelog) warning(nil, "elog replace %d %d (%d %d)\n", b.q0, b.q0+b.nd, t->q0, t->q1); if(!mod){ mod = TRUE; filemark(f); } textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1); textdelete(t, tq0, tq1, TRUE); up -= b.nr; for(i=0; i<b.nr; i+=n){ n = b.nr - i; if(n > RBUFSIZE) n = RBUFSIZE; bufread(log, up+i, buf, n); textinsert(t, tq0+i, buf, n, TRUE); } if(t->q0 == b.q0 && t->q1 == b.q0) t->q1 += b.nr; break; case Delete: if(tracelog) warning(nil, "elog delete %d %d (%d %d)\n", b.q0, b.q0+b.nd, t->q0, t->q1); if(!mod){ mod = TRUE; filemark(f); } textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1); textdelete(t, tq0, tq1, TRUE); break; case Insert: if(tracelog) warning(nil, "elog insert %d %d (%d %d)\n", b.q0, b.q0+b.nr, t->q0, t->q1); if(!mod){ mod = TRUE; filemark(f); } textconstrain(t, b.q0, b.q0, &tq0, &tq1); up -= b.nr; for(i=0; i<b.nr; i+=n){ n = b.nr - i; if(n > RBUFSIZE) n = RBUFSIZE; bufread(log, up+i, buf, n); textinsert(t, tq0+i, buf, n, TRUE); } if(t->q0 == b.q0 && t->q1 == b.q0) t->q1 += b.nr; break;/* case Filename: f->seq = u.seq; fileunsetname(f, epsilon); f->mod = u.mod; up -= u.n; free(f->name); if(u.n == 0) f->name = nil; else f->name = runemalloc(u.n); bufread(delta, up, f->name, u.n); f->nname = u.n; break;*/ } bufdelete(log, up, log->nc); } fbuffree(buf); elogterm(f); /* * Bad addresses will cause bufload to crash, so double check. * If changes were out of order, we expect problems so don't complain further. */ if(t->q0 > f->nc || t->q1 > f->nc || t->q0 > t->q1){ if(!warned) warning(nil, "elogapply: can't happen %d %d %d\n", t->q0, t->q1, f->nc); t->q1 = min(t->q1, f->nc); t->q0 = min(t->q0, t->q1); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -