]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/acme/elog.c
Libflac: Tell it that we have stdint.h so it finds SIZE_MAX
[plan9front.git] / sys / src / cmd / acme / elog.c
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 && f->elog.nr+nr<Maxstring){
174                 runemove(f->elog.r+f->elog.nr, r, nr);
175                 f->elog.nr += nr;
176                 return;
177         }
178         while(nr > 0){
179                 elogflush(f);
180                 f->elog.type = Insert;
181                 f->elog.q0 = q0;
182                 n = nr;
183                 if(n > RBUFSIZE)
184                         n = RBUFSIZE;
185                 f->elog.nr = n;
186                 runemove(f->elog.r, r, n);
187                 r += n;
188                 nr -= n;
189         }
190 }
191
192 void
193 elogdelete(File *f, int q0, int q1)
194 {
195         if(q0 == q1)
196                 return;
197         eloginit(f);
198         if(f->elog.type!=Null && q0<f->elog.q0+f->elog.nd){
199                 if(warned++ == 0)
200                         warning(nil, Wsequence);
201                 elogflush(f);
202         }
203         /* try to merge with previous */
204         if(f->elog.type==Delete && f->elog.q0+f->elog.nd==q0){
205                 f->elog.nd += q1-q0;
206                 return;
207         }
208         elogflush(f);
209         f->elog.type = Delete;
210         f->elog.q0 = q0;
211         f->elog.nd = q1-q0;
212 }
213
214 #define tracelog 0
215 void
216 elogapply(File *f)
217 {
218         Buflog b;
219         Rune *buf;
220         uint i, n, up, mod;
221         uint tq0, tq1;
222         Buffer *log;
223         Text *t;
224         int owner;
225
226         elogflush(f);
227         log = f->elogbuf;
228         t = f->curtext;
229
230         buf = fbufalloc();
231         mod = FALSE;
232
233         owner = 0;
234         if(t->w){
235                 owner = t->w->owner;
236                 if(owner == 0)
237                         t->w->owner = 'E';
238         }
239
240         /*
241          * The edit commands have already updated the selection in t->q0, t->q1,
242          * but using coordinates relative to the unmodified buffer.  As we apply the log,
243          * we have to update the coordinates to be relative to the modified buffer.
244          * Textinsert and textdelete will do this for us; our only work is to apply the
245          * convention that an insertion at t->q0==t->q1 is intended to select the 
246          * inserted text.
247          */
248
249         /*
250          * We constrain the addresses in here (with textconstrain()) because
251          * overlapping changes will generate bogus addresses.   We will warn
252          * about changes out of sequence but proceed anyway; here we must
253          * keep things in range.
254          */
255
256         while(log->nc > 0){
257                 up = log->nc-Buflogsize;
258                 bufread(log, up, (Rune*)&b, Buflogsize);
259                 switch(b.type){
260                 default:
261                         fprint(2, "elogapply: 0x%ux\n", b.type);
262                         abort();
263                         break;
264
265                 case Replace:
266                         if(tracelog)
267                                 warning(nil, "elog replace %d %d (%d %d)\n",
268                                         b.q0, b.q0+b.nd, t->q0, t->q1);
269                         if(!mod){
270                                 mod = TRUE;
271                                 filemark(f);
272                         }
273                         textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
274                         textdelete(t, tq0, tq1, TRUE);
275                         up -= b.nr;
276                         for(i=0; i<b.nr; i+=n){
277                                 n = b.nr - i;
278                                 if(n > RBUFSIZE)
279                                         n = RBUFSIZE;
280                                 bufread(log, up+i, buf, n);
281                                 textinsert(t, tq0+i, buf, n, TRUE);
282                         }
283                         if(t->q0 == b.q0 && t->q1 == b.q0)
284                                 t->q1 += b.nr;
285                         break;
286
287                 case Delete:
288                         if(tracelog)
289                                 warning(nil, "elog delete %d %d (%d %d)\n",
290                                         b.q0, b.q0+b.nd, t->q0, t->q1);
291                         if(!mod){
292                                 mod = TRUE;
293                                 filemark(f);
294                         }
295                         textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
296                         textdelete(t, tq0, tq1, TRUE);
297                         break;
298
299                 case Insert:
300                         if(tracelog)
301                                 warning(nil, "elog insert %d %d (%d %d)\n",
302                                         b.q0, b.q0+b.nr, t->q0, t->q1);
303                         if(!mod){
304                                 mod = TRUE;
305                                 filemark(f);
306                         }
307                         textconstrain(t, b.q0, b.q0, &tq0, &tq1);
308                         up -= b.nr;
309                         for(i=0; i<b.nr; i+=n){
310                                 n = b.nr - i;
311                                 if(n > RBUFSIZE)
312                                         n = RBUFSIZE;
313                                 bufread(log, up+i, buf, n);
314                                 textinsert(t, tq0+i, buf, n, TRUE);
315                         }
316                         if(t->q0 == b.q0 && t->q1 == b.q0)
317                                 t->q1 += b.nr;
318                         break;
319
320 /*              case Filename:
321                         f->seq = u.seq;
322                         fileunsetname(f, epsilon);
323                         f->mod = u.mod;
324                         up -= u.n;
325                         free(f->name);
326                         if(u.n == 0)
327                                 f->name = nil;
328                         else
329                                 f->name = runemalloc(u.n);
330                         bufread(delta, up, f->name, u.n);
331                         f->nname = u.n;
332                         break;
333 */
334                 }
335                 bufdelete(log, up, log->nc);
336         }
337         fbuffree(buf);
338         elogterm(f);
339
340         /*
341          * Bad addresses will cause bufload to crash, so double check.
342          * If changes were out of order, we expect problems so don't complain further.
343          */
344         if(t->q0 > f->nc || t->q1 > f->nc || t->q0 > t->q1){
345                 if(!warned)
346                         warning(nil, "elogapply: can't happen %d %d %d\n", t->q0, t->q1, f->nc);
347                 t->q1 = min(t->q1, f->nc);
348                 t->q0 = min(t->q0, t->q1);
349         }
350
351         if(t->w)
352                 t->w->owner = owner;
353 }