1 /*    e_fold.cpp
2  *
3  *    Copyright (c) 1994-1996, Marko Macek
4  *
5  *    You may distribute under the terms of either the GNU General Public
6  *    License or the Artistic License, as specified in the README file.
7  *
8  */
9 
10 #include "e_undo.h"
11 #include "o_buflist.h"
12 #include "sysdep.h"
13 
FindFold(int Line)14 int EBuffer::FindFold(int Line) { // optimize /*FOLD00*/
15     int f = FindNearFold(Line);
16     if (f != -1)
17         if (FF[f].line == Line)
18             return f;
19     return -1;
20 }
21 
FindNearFold(int Line)22 int EBuffer::FindNearFold(int Line) { /*FOLD00*/
23     int b = 0, B = FCount - 1, c;
24 
25     while (b <= B) {
26         c = (b + B) / 2;
27 //        printf("%d %d %d %d %d\n", b, B, c, Line, FF[c].line);
28         if (FF[c].line == Line)
29             return c;
30         if (c < FCount - 1) {
31             if (FF[c].line <= Line && FF[c + 1].line > Line)
32                 return c;
33         } else {
34             if (FF[c].line <= Line)
35                 return c;
36         }
37         if (FF[c].line < Line)
38             b = c + 1;
39         else
40             B = c - 1;
41         if (b > B)
42             break;
43     }
44     return -1;
45 }
46 
ShowRow(int Row)47 int EBuffer::ShowRow(int Row) { /*FOLD00*/
48     int V = RToVN(Row), GapSize;
49 
50 //    printf("Showing row %d\n", Row);
51 
52     assert(Row >= 0 && Row < RCount); // 0 cannot be hidden
53 
54     if (V + Vis(V) == Row) return 1; // already visible
55 
56     assert(VCount <= VAllocated);
57     if (VCount == VAllocated) {
58         if (AllocVis(VCount ? (VCount * 2) : 1) == 0) return 0;
59         memmove(VV + VAllocated - (VCount - VGap),
60                 VV + VGap,
61                 sizeof(int) * (VCount - VGap));
62     }
63     if (VGap != V + 1)
64         if (MoveVGap(V + 1) == 0) return 0;
65     VV[VGap] = Row - (VGap);
66     VGap++;
67     VCount++;
68 
69     GapSize = VAllocated - VCount;
70     if (VGap != V + 2)
71         if (MoveVGap(V + 2) == 0) return 0;
72     for (int i = V + 2; i < VCount; i++)
73         VV[i + GapSize]--;
74 //        Vis(i, Vis(i) - 1);
75     UpdateVisible(Row, 1);
76 //    if (CP.Row > Row)
77 //        if (SetPos(CP.Col, CP.Row + 1) == 0) return 0;
78     Draw(Row, -1);
79     return 1;
80 }
81 
HideRow(int Row)82 int EBuffer::HideRow(int Row) { /*FOLD00*/
83     int V = RToV(Row), GapSize;
84 
85     assert(Row > 0 && Row < RCount); // 0 cannot be hidden
86 
87     if (V == -1) return 1; // already hidden
88     UpdateVisible(Row, -1);
89 
90     if (VGap != V)
91         if (MoveVGap(V) == 0) return 0;
92     GapSize = VAllocated - VCount;
93     VV[VGap + GapSize] = 0;
94     VCount--;
95     GapSize++;
96     if (VAllocated - VAllocated / 2 > VCount) {
97         memmove(VV + VGap + GapSize - VAllocated / 3,
98                 VV + VGap + GapSize,
99                 sizeof(int) * (VCount - VGap));
100         if (AllocVis(VAllocated - VAllocated / 3) == 0) return 0;
101     }
102     GapSize = VAllocated - VCount;
103     if (VGap != V)
104         if (MoveVGap(V) == 0) return 0;
105     for (int i = V; i < VCount; i++)
106         VV[i + GapSize]++;
107 //        Vis(i, Vis(i) + 1);
108 //    if (CP.Row > Row)
109 //        if (SetPos(CP.Col, CP.Row - 1) == 0) return 0;
110     Draw(Row, -1);
111     return 1;
112 }
113 
ExposeRow(int Row)114 int EBuffer::ExposeRow(int Row) { /*FOLD00*/
115     int V;
116     int f, level, oldlevel = 100;
117 
118     //DumpFold();
119 
120     assert(Row >= 0 && Row < RCount); // range
121 
122     V = RToV(Row);
123     if (V != -1) return 1; // already exposed
124 
125     f = FindNearFold(Row);
126     assert(f != -1); // if not visible, must be folded
127 
128     while (f >= 0) {
129         level = FF[f].level;
130         if (level < oldlevel) {
131             if (FF[f].open == 0) {
132 //                printf("opening fold %d\n", f);
133                 if (FoldOpen(FF[f].line) == 0) return 0;
134             }
135             oldlevel = level;
136         }
137         f--;
138         if (level == 0) break;
139     }
140 
141     V = RToV(Row);
142 //    if (V == -1) {
143 //        printf("Expose Row = %d\n", Row);
144 //        DumpFold();
145 //    }
146     assert (V != -1);
147     return 1;
148 }
149 
UpdateVis(EPoint & M,int Row,int Delta)150 void EBuffer::UpdateVis(EPoint &M, int Row, int Delta) { /*FOLD00*/
151     if (Delta < 0) {
152         if (M.Row > Row) {
153             if (M.Row < Row - Delta)
154                 M.Row = Row;
155             else
156                 M.Row += Delta;
157         }
158     } else if (M.Row >= Row)
159         M.Row += Delta;
160 }
161 
UpdateVisible(int Row,int Delta)162 void EBuffer::UpdateVisible(int Row, int Delta) { /*FOLD00*/
163     EView *w;
164 
165     Row = RToV(Row);
166     UpdateVis(CP, Row, Delta);
167     w = View;
168     if (w) do {
169         UpdateVis(GetViewVPort(w)->TP, Row, Delta);
170         UpdateVis(GetViewVPort(w)->CP, Row, Delta);
171         w = w->Next;
172     } while (w != View);
173 }
174 
FoldCreate(int Line)175 int EBuffer::FoldCreate(int Line) { /*FOLD00*/
176     int n;
177 
178     if (Modify() == 0) return 0;
179 
180     if (FindFold(Line) != -1) return 1; // already exists
181 
182 #ifdef CONFIG_UNDOREDO
183     if (BFI(this, BFI_Undo)) {
184         if (PushULong(Line) == 0) return 0;
185         if (PushUChar(ucFoldCreate) == 0) return 0;
186     }
187 #endif
188 
189     n = FindNearFold(Line);
190     n++;
191     FF = (EFold *) realloc((void *)FF, sizeof(EFold) * ((1 + FCount) | 7));
192     assert(FF != 0);
193     memmove(FF + n + 1, FF + n, sizeof(EFold) * (FCount - n));
194     FCount++;
195     FF[n].line = Line;
196     FF[n].level = 0;
197     FF[n].open = 1;
198     FF[n].flags = 0;
199     Draw(Line, Line);
200     return 1;
201 }
202 
FoldCreateByRegexp(const char * Regexp)203 int EBuffer::FoldCreateByRegexp(const char *Regexp) { /*FOLD00*/
204     RxNode *R;
205     int err = 1;
206 
207     if (Modify() == 0) return 0;
208 
209     R = RxCompile(Regexp);
210     if (R != NULL) {
211         PELine X;
212         int first = -1;
213         int L;
214 
215         for (L = 0; L < RCount; L++) {
216             RxMatchRes RM;
217 
218             X = RLine(L);
219             if (RxExec(R, X->Chars, X->Count, X->Chars, &RM) == 1) {
220                 if (first >= 0) {
221                     int i;
222 
223                     for(i = L; i > 0; i--) {
224                         PELine Y;
225 
226                         Y = RLine(i);
227                         if ((Y->Count == 0) || strrchr(Y->Chars, '}')) {
228                             if ((L - i) > 2) {
229                                 while ((i > 0) && (RLine(i - 1)->Count == 0))
230                                     i--;
231                                 if ((first >= 0) && i
232                                     && (FoldCreate(i) == 0))
233                                     err = 0;
234                             }
235                             break;
236                         }
237                     }
238                 } else
239                     first = L;
240                 if (FoldCreate(L) == 0) {
241                     err = 0;
242                     break;
243                 }
244             }
245         }
246         RxFree(R);
247     }
248     return err;
249 }
250 
FoldCreateAtRoutines()251 int EBuffer::FoldCreateAtRoutines() { /*FOLD00*/
252     if (BFS(this, BFS_RoutineRegexp) == 0)
253         return 0;
254     return FoldCreateByRegexp(BFS(this, BFS_RoutineRegexp));
255 }
256 
FoldDestroy(int Line)257 int EBuffer::FoldDestroy(int Line) { /*FOLD00*/
258     int f = FindFold(Line);
259 
260     if (Modify() == 0) return 0;
261 
262     if (f == -1) return 0;
263     if (FF[f].open == 0)
264         if (FoldOpen(Line) == 0) return 0;
265 
266 #ifdef CONFIG_UNDOREDO
267     if (BFI(this, BFI_Undo)) {
268         if (PushULong(FF[f].level) == 0) return 0;
269         if (PushULong(Line) == 0) return 0;
270         if (PushUChar(ucFoldDestroy) == 0) return 0;
271     }
272 #endif
273 
274     memmove(FF + f, FF + f + 1, sizeof(EFold) * (FCount - f - 1));
275     FCount--;
276     FF = (EFold *) realloc((void *)FF, sizeof(EFold) * (FCount | 7));
277     Draw(Line, Line);
278     return 1;
279 }
280 
FoldDestroyAll()281 int EBuffer::FoldDestroyAll() { /*FOLD00*/
282     int l;
283 
284     if (Modify() == 0) return 0;
285 
286     for (l = 0; l < RCount; l++)
287         if (FindFold(l) != -1)
288             if (FoldDestroy(l) == 0) return 0;
289     return 1;
290 }
291 
FoldPromote(int Line)292 int EBuffer::FoldPromote(int Line) { /*FOLD00*/
293     int f = FindFold(Line);
294 
295     if (Modify() == 0) return 0;
296 
297     if (f == -1) return 0;
298     if (FF[f].open == 0) return 0;
299     if (FF[f].level == 0) return 0;
300 
301 #ifdef CONFIG_UNDOREDO
302     if (BFI(this, BFI_Undo)) {
303         if (PushULong(Line) == 0) return 0;
304         if (PushUChar(ucFoldPromote) == 0) return 0;
305     }
306 #endif
307 
308     if ((FF[f].line > 0) && (ExposeRow(FF[f].line - 1) == 0))
309         return 0;
310 
311     FF[f].level--;
312     Draw(Line, Line);
313     return 1;
314 }
315 
FoldDemote(int Line)316 int EBuffer::FoldDemote(int Line) { /*FOLD00*/
317     int f = FindFold(Line);
318 
319     if (Modify() == 0) return 0;
320 
321     if (f == -1) return 0;
322     if (FF[f].open == 0) return 0;
323     if (FF[f].level == 99) return 0;
324 
325 #ifdef CONFIG_UNDOREDO
326     if (BFI(this, BFI_Undo)) {
327         if (PushULong(Line) == 0) return 0;
328         if (PushUChar(ucFoldDemote) == 0) return 0;
329     }
330 #endif
331     if ((FF[f].line > 0) && (ExposeRow(FF[f].line - 1) == 0))
332         return 0;
333 
334     FF[f].level++;
335     Draw(Line, Line);
336     return 1;
337 }
338 
FoldOpen(int Line)339 int EBuffer::FoldOpen(int Line) { /*FOLD00*/
340     int f = FindFold(Line);
341     int l;
342     int level, toplevel;
343     int top;
344 
345     if (f == -1) return 0;
346     if (FF[f].open == 1) return 1; // already open
347 
348     if (Modify() == 0) return 0;
349 
350 #ifdef CONFIG_UNDOREDO
351     if (BFI(this, BFI_Undo)) {
352         if (PushULong(Line) == 0) return 0;
353         if (PushUChar(ucFoldOpen) == 0) return 0;
354     }
355 #endif
356 
357     FF[f].open = 1;
358     top = FF[f].line;
359     toplevel = FF[f].level;
360     //    printf("Fold starts with %d\n", FF[f].line);
361     if (ShowRow(FF[f].line) == 0) return 0;
362     while (f < FCount) {
363         level = FF[f].level;
364         if (FF[f].open == 1) {
365             // fold is open
366             if (f == FCount - 1) {
367                 for (l = FF[f].line; l < RCount; l++)
368                     if (l != top)
369                         if (ShowRow(l) == 0) return 0;
370             } else {
371                 for (l = FF[f].line; l < FF[f + 1].line; l++)
372                     if (l != top)
373                         if (ShowRow(l) == 0) return 0;
374             }
375             f++;
376         } else { // fold is closed
377             // show head line
378             if (ShowRow(FF[f].line) == 0) return 0;
379             // skip closed folds
380             while ((f < FCount) && (level < FF[f + 1].level))
381                 f++;
382             f++;
383         }
384         if (f < FCount && FF[f].level <= toplevel)
385             break;
386     }
387     return 1;
388 }
389 
FoldOpenAll()390 int EBuffer::FoldOpenAll() { /*FOLD00*/
391     for (int l = 0; l < RCount; ++l)
392         if ((FindFold(l) != -1)
393             && !FoldOpen(l))
394             return 0;
395 
396     return 1;
397 }
398 
FoldOpenNested()399 int EBuffer::FoldOpenNested() { /*FOLD00*/
400     int Line = VToR(CP.Row);
401     int f = FindFold(Line);
402     int l;
403     int level;
404 
405     if (f == -1) return 0;
406     level = FF[f].level;
407 
408     while (f + 1 < FCount && FF[f + 1].level > level) f++;
409 
410     if (f + 1 == FCount) {
411         if (FoldOpen(Line) == 0) return 0;
412     } else {
413         for (l = Line; l < RCount && l < FF[f + 1].line; l++) {
414             if (FindFold(l) != -1)
415                 if (FoldOpen(l) == 0) return 0;
416         }
417     }
418     return 0;
419 }
420 
FoldClose(int Line)421 int EBuffer::FoldClose(int Line) { /*FOLD00*/
422     int f = FindNearFold(Line);
423     int l, top;
424     int level;
425 
426     if (f == -1) return 0;
427     if (FF[f].open == 0) return 1; // already closed
428 
429     if (Modify() == 0) return 0;
430 
431     if (SetPosR(CP.Col, FF[f].line, tmLeft) == 0) return 0;
432 
433 #ifdef CONFIG_UNDOREDO
434     if (BFI(this, BFI_Undo)) {
435         if (PushULong(Line) == 0) return 0;
436         if (PushUChar(ucFoldClose) == 0) return 0;
437     }
438 #endif
439 
440     FF[f].open = 0;
441     top = FF[f].line;
442     level = FF[f].level;
443     while ((f < FCount - 1) && (FF[f + 1].level > level)) f++;
444 
445     /* performance tweak: do it in reverse (we'll see if it helps) */
446 
447     if (f == FCount - 1) {
448         for (l = RCount - 1; l > top; l--)
449             if (HideRow(l) == 0) return 0;
450     } else {
451         for (l = FF[f + 1].line - 1; l > top; l--)
452             if (HideRow(l) == 0) return 0;
453     }
454 
455     /* yup, it does. try below for a (MUCH!) slower version */
456 
457     /*if (f == FCount - 1) {
458      for (l = top + 1; l < RCount; l++)
459      if (HideRow(l) == 0) return 0;
460      } else {
461      for (l = top + 1; l < FF[f + 1].line; l++)
462      if (HideRow(l) == 0) return 0;
463      }*/
464     return 1;
465 }
466 
FoldCloseAll()467 int EBuffer::FoldCloseAll() { /*FOLD00*/
468     for (int l = RCount - 1; l >= 0; --l)
469         if ((FindFold(l) != -1)
470             && !FoldClose(l))
471             return 0;
472     return 1;
473 }
474 
FoldToggleOpenClose()475 int EBuffer::FoldToggleOpenClose() { /*FOLD00*/
476     int Line = VToR(CP.Row);
477     int f = FindNearFold(Line);
478 
479     if (f == -1)
480         return 0;
481 
482     if (FF[f].open) {
483         if (!FoldClose(Line))
484             return 0;
485     } else {
486         if (!FoldOpen(Line))
487             return 0;
488     }
489 
490     return 1;
491 }
492 
MoveFoldTop()493 int EBuffer::MoveFoldTop() { /*FOLD00*/
494     int f = FindNearFold(VToR(CP.Row));
495 
496     if (f <= 0)
497         return 0;
498 
499     if (FF[f].line == VToR(CP.Row))
500         return 1;
501 
502     return SetPosR(CP.Col, FF[f].line, tmLeft);
503 }
504 
MoveFoldPrev()505 int EBuffer::MoveFoldPrev() { /*FOLD00*/
506     int f = FindNearFold(VToR(CP.Row));
507 
508     if (f <= 0)
509         return 0;
510 
511     if (FF[f].line == VToR(CP.Row)) {
512         for (;;) {
513             if (--f < 0)
514                 return 0;
515             if (RToV(FF[f].line) != -1)
516                 break;
517         }
518     }
519 
520     return SetPosR(CP.Col, FF[f].line, tmLeft);
521 }
522 
MoveFoldNext()523 int EBuffer::MoveFoldNext() { /*FOLD00*/
524     int f = FindNearFold(VToR(CP.Row));
525 
526     if ((f == (FCount - 1)) || (f == -1))
527         return 0;
528 
529     while (++f < FCount)
530         if (RToV(FF[f].line) != -1)
531             return SetPosR(CP.Col, FF[f].line, tmLeft);
532 
533     return 0;
534 }
535