1 #include <u.h> 2 #include <libc.h> 3 #include <draw.h> 4 #include <thread.h> 5 #include <cursor.h> 6 #include <mouse.h> 7 #include <keyboard.h> 8 #include <frame.h> 9 #include <fcall.h> 10 #include <plumb.h> 11 #include "dat.h" 12 #include "fns.h" 13 #include "edit.h" 14 15 static char Wsequence[] = "warning: changes out of sequence\n"; 16 static int warned = FALSE; 17 18 /* 19 * Log of changes made by editing commands. Three reasons for this: 20 * 1) We want addresses in commands to apply to old file, not file-in-change. 21 * 2) It's difficult to track changes correctly as things move, e.g. ,x m$ 22 * 3) This gives an opportunity to optimize by merging adjacent changes. 23 * It's a little bit like the Undo/Redo log in Files, but Point 3) argues for a 24 * separate implementation. To do this well, we use Replace as well as 25 * Insert and Delete 26 */ 27 28 typedef struct Buflog Buflog; 29 struct Buflog 30 { 31 short type; /* Replace, Filename */ 32 uint q0; /* location of change (unused in f) */ 33 uint nd; /* # runes to delete */ 34 uint nr; /* # runes in string or file name */ 35 }; 36 37 enum 38 { 39 Buflogsize = sizeof(Buflog)/sizeof(Rune), 40 }; 41 42 /* 43 * Minstring shouldn't be very big or we will do lots of I/O for small changes. 44 * Maxstring is RBUFSIZE so we can fbufalloc() once and not realloc elog.r. 45 */ 46 enum 47 { 48 Minstring = 16, /* distance beneath which we merge changes */ 49 Maxstring = RBUFSIZE, /* maximum length of change we will merge into one */ 50 }; 51 52 void 53 eloginit(File *f) 54 { 55 if(f->elog.type != Empty) 56 return; 57 f->elog.type = Null; 58 if(f->elogbuf == nil) 59 f->elogbuf = emalloc(sizeof(Buffer)); 60 if(f->elog.r == nil) 61 f->elog.r = fbufalloc(); 62 bufreset(f->elogbuf); 63 } 64 65 void 66 elogclose(File *f) 67 { 68 if(f->elogbuf){ 69 bufclose(f->elogbuf); 70 free(f->elogbuf); 71 f->elogbuf = nil; 72 } 73 } 74 75 void 76 elogreset(File *f) 77 { 78 f->elog.type = Null; 79 f->elog.nd = 0; 80 f->elog.nr = 0; 81 } 82 83 void 84 elogterm(File *f) 85 { 86 elogreset(f); 87 if(f->elogbuf) 88 bufreset(f->elogbuf); 89 f->elog.type = Empty; 90 fbuffree(f->elog.r); 91 f->elog.r = nil; 92 warned = FALSE; 93 } 94 95 void 96 elogflush(File *f) 97 { 98 Buflog b; 99 100 b.type = f->elog.type; 101 b.q0 = f->elog.q0; 102 b.nd = f->elog.nd; 103 b.nr = f->elog.nr; 104 switch(f->elog.type){ 105 default: 106 warning(nil, "unknown elog type 0x%ux\n", f->elog.type); 107 break; 108 case Null: 109 break; 110 case Insert: 111 case Replace: 112 if(f->elog.nr > 0) 113 bufinsert(f->elogbuf, f->elogbuf->nc, f->elog.r, f->elog.nr); 114 /* fall through */ 115 case Delete: 116 bufinsert(f->elogbuf, f->elogbuf->nc, (Rune*)&b, Buflogsize); 117 break; 118 } 119 elogreset(f); 120 } 121 122 void 123 elogreplace(File *f, int q0, int q1, Rune *r, int nr) 124 { 125 uint gap; 126 127 if(q0==q1 && nr==0) 128 return; 129 eloginit(f); 130 if(f->elog.type!=Null && q0<f->elog.q0){ 131 if(warned++ == 0) 132 warning(nil, Wsequence); 133 elogflush(f); 134 } 135 /* try to merge with previous */ 136 gap = q0 - (f->elog.q0+f->elog.nd); /* gap between previous and this */ 137 if(f->elog.type==Replace && f->elog.nr+gap+nr<Maxstring){ 138 if(gap < Minstring){ 139 if(gap > 0){ 140 bufread(f, f->elog.q0+f->elog.nd, f->elog.r+f->elog.nr, gap); 141 f->elog.nr += gap; 142 } 143 f->elog.nd += gap + q1-q0; 144 runemove(f->elog.r+f->elog.nr, r, nr); 145 f->elog.nr += nr; 146 return; 147 } 148 } 149 elogflush(f); 150 f->elog.type = Replace; 151 f->elog.q0 = q0; 152 f->elog.nd = q1-q0; 153 f->elog.nr = nr; 154 if(nr > RBUFSIZE) 155 editerror("internal error: replacement string too large(%d)", nr); 156 runemove(f->elog.r, r, nr); 157 } 158 159 void 160 eloginsert(File *f, int q0, Rune *r, int nr) 161 { 162 int n; 163 164 if(nr == 0) 165 return; 166 eloginit(f); 167 if(f->elog.type!=Null && q0<f->elog.q0){ 168 if(warned++ == 0) 169 warning(nil, Wsequence); 170 elogflush(f); 171 } 172 /* try to merge with previous */ 173 if(f->elog.type==Insert && q0==f->elog.q0 && (q0+nr)-f->elog.q0<Maxstring){ 174 f->elog.r = runerealloc(f->elog.r, f->elog.nr+nr); 175 runemove(f->elog.r+f->elog.nr, r, nr); 176 f->elog.nr += nr; 177 return; 178 } 179 while(nr > 0){ 180 elogflush(f); 181 f->elog.type = Insert; 182 f->elog.q0 = q0; 183 n = nr; 184 if(n > RBUFSIZE) 185 n = RBUFSIZE; 186 f->elog.nr = n; 187 runemove(f->elog.r, r, n); 188 r += n; 189 nr -= n; 190 } 191 } 192 193 void 194 elogdelete(File *f, int q0, int q1) 195 { 196 if(q0 == q1) 197 return; 198 eloginit(f); 199 if(f->elog.type!=Null && q0<f->elog.q0+f->elog.nd){ 200 if(warned++ == 0) 201 warning(nil, Wsequence); 202 elogflush(f); 203 } 204 /* try to merge with previous */ 205 if(f->elog.type==Delete && f->elog.q0+f->elog.nd==q0){ 206 f->elog.nd += q1-q0; 207 return; 208 } 209 elogflush(f); 210 f->elog.type = Delete; 211 f->elog.q0 = q0; 212 f->elog.nd = q1-q0; 213 } 214 215 void 216 elogapply(File *f) 217 { 218 Buflog b; 219 Rune *buf; 220 uint i, n, up, mod; 221 Buffer *log; 222 223 elogflush(f); 224 log = f->elogbuf; 225 226 buf = fbufalloc(); 227 mod = FALSE; 228 while(log->nc > 0){ 229 up = log->nc-Buflogsize; 230 bufread(log, up, (Rune*)&b, Buflogsize); 231 switch(b.type){ 232 default: 233 fprint(2, "elogapply: 0x%ux\n", b.type); 234 abort(); 235 break; 236 237 case Replace: 238 if(!mod){ 239 mod = TRUE; 240 filemark(f); 241 } 242 textdelete(f->curtext, b.q0, b.q0+b.nd, TRUE); 243 up -= b.nr; 244 for(i=0; i<b.nr; i+=n){ 245 n = b.nr - i; 246 if(n > RBUFSIZE) 247 n = RBUFSIZE; 248 bufread(log, up+i, buf, n); 249 textinsert(f->curtext, b.q0+i, buf, n, TRUE); 250 } 251 f->curtext->q0 = b.q0; 252 f->curtext->q1 = b.q0+b.nr; 253 break; 254 255 case Delete: 256 if(!mod){ 257 mod = TRUE; 258 filemark(f); 259 } 260 textdelete(f->curtext, b.q0, b.q0+b.nd, TRUE); 261 f->curtext->q0 = b.q0; 262 f->curtext->q1 = b.q0; 263 break; 264 265 case Insert: 266 if(!mod){ 267 mod = TRUE; 268 filemark(f); 269 } 270 up -= b.nr; 271 for(i=0; i<b.nr; i+=n){ 272 n = b.nr - i; 273 if(n > RBUFSIZE) 274 n = RBUFSIZE; 275 bufread(log, up+i, buf, n); 276 textinsert(f->curtext, b.q0+i, buf, n, TRUE); 277 } 278 f->curtext->q0 = b.q0; 279 f->curtext->q1 = b.q0+b.nr; 280 break; 281 282 /* case Filename: 283 f->seq = u.seq; 284 fileunsetname(f, epsilon); 285 f->mod = u.mod; 286 up -= u.n; 287 free(f->name); 288 if(u.n == 0) 289 f->name = nil; 290 else 291 f->name = runemalloc(u.n); 292 bufread(delta, up, f->name, u.n); 293 f->nname = u.n; 294 break; 295 */ 296 } 297 bufdelete(log, up, log->nc); 298 } 299 fbuffree(buf); 300 elogterm(f); 301 } 302