Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 451 lines (410 sloc) 13.21 kB
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
1 /* fold.c */
2 /* Copyright 2000 by Steve Kirkendall */
3
4 #include "elvis.h"
5 #ifdef FEATURE_RCSID
6 char id_fold[] = "$Id: fold.c,v 2.10 2003/10/17 17:41:23 steve Exp $";
7 #endif
8 #undef DEBUG_FOLD
9
10 #ifdef FEATURE_FOLD
11
12 static void foldfree P_((FOLD fold));
13 static long foldcmp P_((FOLD fold1, FOLD fold2));
14
15 /* Create a new FOLD. After creation, it still needs to be added to either
16 * the "fold" or "unfold" list by calling foldadd().
17 */
18 FOLD foldalloc(from, to, name)
19 MARK from; /* first char of region */
20 MARK to; /* last char of region -- INCLUSIVE! */
21 CHAR *name; /* name of the region */
22 {
23 FOLD fold; /* the new fold */
24
25 #ifdef DEBUG_FOLD
26 fprintf(stderr, "foldalloc(%ld-%ld, \"%s\")\n",
27 markoffset(from), markoffset(to), tochar8(name));
28 #endif
29 /* allocate the fold, and fill it with copies of the args */
30 fold = (FOLD)safealloc(1, sizeof(struct fold_s));
31 fold->from = markdup(from);
32 fold->to = markdup(to);
33 fold->name = CHARdup(name);
34
35 return fold;
36 }
37
38 /* Free a FOLD and its resources. This should only be done after it has
39 * been deleted from the "fold" or "unfold" list.
40 */
41 static void foldfree(fold)
42 FOLD fold; /* the fold to be freed */
43 {
44 #ifdef DEBUG_FOLD
45 fprintf(stderr, "foldfree({%s})\n", tochar8(fold->name));
46 #endif
47 /* free the FOLD's resources, and then free the FOLD itself */
48 markfree(fold->from);
49 markfree(fold->to);
50 safefree(fold->name);
51 safefree(fold);
52 }
53
54 /* This function compares two FOLDs. The "fold" and "unfold" lists are always
55 * maintained in this order. It returns 0 if identical, <0 if fold1 should
56 * come before fold2 in the list, or >0 if fold1 should come after fold2.
57 */
58 static long foldcmp(fold1, fold2)
59 FOLD fold1, fold2; /* the FOLDs to compare. */
60 {
61 long diff;
62
63 diff = markoffset(fold1->from) - markoffset(fold2->from);
64 if (diff == 0)
65 diff = markoffset(fold2->to) - markoffset(fold1->to);
66 return diff;
67 }
68
69 /* Insert a FOLD into a buffer's "fold" or "unfold" list. The buffer is
70 * implied by the MARKs within the FOLD. It is assumed that the any
71 * identical or overlapping folds have been deleted from both the "fold"
72 * and "unfold" lists already.
73 */
74 void foldadd(fold, infold)
75 FOLD fold; /* the FOLD to be added to "fold" list */
76 ELVBOOL infold; /* add to "fold" list? (else "unfold") */
77 {
78 BUFFER buf = markbuffer(fold->from);
79 FOLD scan, lag;
80
81 #ifdef DEBUG_FOLD
82 fprintf(stderr, "foldadd({%s}, %sinfold)\n",
83 tochar8(fold->name), infold ? "" : "!");
84 #endif
85 /* insert it at the proper point within the list, to keep the list
86 * sorted by markoffset(from) in ascending order, or markoffset(to)
87 * in decending order for equal from offsets.
88 */
89 for (scan = infold ? buf->fold : buf->unfold, lag = NULL;
90 scan && foldcmp(fold, scan) > 0;
91 lag = scan, scan = scan->next)
92 {
93 }
94 fold->next = scan;
95 if (lag)
96 lag->next = fold;
97 else if (infold)
98 buf->fold = fold;
99 else
100 buf->unfold = fold;
101 #ifdef DEBUG_FOLD
102 fprintf(stderr, " added between %s and %s\n",
103 lag ? tochar8(lag->name) : "NULL",
104 scan ? tochar8(scan->name) : "NULL");
105 #endif
106 }
107
108 /* Fold or unfold (depending on "infold" parameter) existing FOLDs which have
109 * a given name. Returns RESULT_COMPLETE if any were moved, else RESULT_ERROR.
110 */
111 RESULT foldbyname(buf, name, infold)
112 BUFFER buf; /* buffer whose FOLDs are to be affected */
113 CHAR *name; /* name to search for */
114 ELVBOOL infold; /* unfold them? (else refold them) */
115 {
116 RESULT result = RESULT_ERROR;
117 FOLD next, scan, lag;
118
119 /* scan for the name */
120 for (scan = infold ? buf->fold : buf->unfold, lag = NULL; scan; )
121 {
122 if (!CHARcmp(scan->name, name))
123 {
124 /* delete from this list */
125 next = scan->next;
126 if (lag)
127 lag->next = next;
128 else if (infold)
129 buf->fold = next;
130 else
131 buf->unfold = next;
132
133 /* add to other list */
134 if (infold)
135 foldadd(scan, ElvFalse);
136 else
137 foldadd(scan, ElvTrue);
138
139 /* remember that we moved an item */
140 result = RESULT_COMPLETE;
141
142 /* move on to next item */
143 scan = next;
144 }
145 else
146 {
147 /* move on to next item */
148 lag = scan;
149 scan = scan->next;
150 }
151 }
152
153 /* return the result */
154 return result;
155 }
156
157 /* Locate FOLDs by region, and either delete them or unfold/refold them.
158 * Return RESULT_COMPLETE if at least one region was deleted/moved, or
159 * RESULT_COMPLETE if it had no effect.
160 *
161 * This search always acts on regions which overlap the from/to range,
162 * or which exactly match the from/to range. They can also optionally
163 * affected wholely enclosed FOLDs, and/or FOLDs which are nested inside
164 * other FOLDs which are affected by this command.
165 */
166 RESULT foldbyrange(from, to, infold, flags)
167 MARK from; /* start of region to be tested against */
168 MARK to; /* end of region to be tested against, inclusive */
169 ELVBOOL infold; /* search the "fold" list? (else the "unfold" list) */
170 int flags; /* mixture of FOLD_{INSIDE,OUTSIDE,NESTED,TOGGLE,DESTROY} */
171 {
172 FOLD scan, lag, next, lagnext;
173 BUFFER buf = markbuffer(from);
174 long fromoff = markoffset(from);
175 RESULT result = RESULT_ERROR;
176 enum {FOLD_IS_IDENTICAL, FOLD_IS_INSIDE, FOLD_IS_OUTSIDE, FOLD_IS_OVERLAP} relation;
177
178 #ifdef DEBUG_FOLD
179 fprintf(stderr, "foldbyrange(%ld-%ld, %sinfold,%s%s%s%s%s%s)\n",
180 markoffset(from), markoffset(to),
181 infold ? "" : "!",
182 (flags & FOLD_INSIDE) ? " Inside" : "",
183 (flags & FOLD_OUTSIDE) ? " Outside" : "",
184 (flags & FOLD_NESTED) ? " Nested" : "",
185 (flags & FOLD_TOGGLE) ? " Toggle" : "",
186 (flags & FOLD_DESTROY) ? " Destroy" : "",
187 (flags & FOLD_TEST) ? " Test" : "");
188 #endif
189
190 for (scan = infold ? buf->fold : buf->unfold, lag = NULL;
191 scan && fromoff <= markoffset(to);
192 lag = scan, scan = next)
193 {
194 /* remember scan->next, so we can go there even if scan is
195 * deleted or moved to the other list.
196 */
197 next = scan->next;
198
199 /* if totally outside the affected range, then skip it */
200 if (markoffset(scan->to) < fromoff
201 || markoffset(to) < markoffset(scan->from))
202 {
203 #ifdef DEBUG_FOLD
204 fprintf(stderr, " {%s,%ld-%ld} is disjoint from the range %ld-%ld -- skipping\n",
205 scan->name,
206 markoffset(scan->from), markoffset(scan->to),
207 fromoff, markoffset(to));
208 #endif
209 continue;
210 }
211
212 /* classify the relationship between the region and this FOLD */
213 if (markoffset(scan->from) == fromoff
214 && markoffset(scan->to) == markoffset(to))
215 relation = FOLD_IS_IDENTICAL;
216 else if (markoffset(scan->from) >= fromoff
217 && markoffset(scan->to) <= markoffset(to))
218 relation = FOLD_IS_INSIDE;
219 else if (markoffset(scan->from) <= fromoff
220 && markoffset(scan->to) >= markoffset(to))
221 relation = FOLD_IS_OUTSIDE;
222 else
223 relation = FOLD_IS_OVERLAP;
224
225 #ifdef DEBUG_FOLD
226 fprintf(stderr, " {%s,%ld-%ld} is %s the range %ld-%ld\n",
227 scan->name,
228 markoffset(scan->from), markoffset(scan->to),
229 relation == FOLD_IS_IDENTICAL ? "identical to" :
230 relation == FOLD_IS_INSIDE ? "inside" :
231 relation == FOLD_IS_OUTSIDE ? "outside" :
232 "overlapping",
233 fromoff, markoffset(to));
234 #endif
235
236 /* Is it a relationship that we care about? We always want
237 * FOLD_IS_IDENTICAL and FOLD_IS_OVERLAP, but we only want
238 * FOLD_IS_INSIDE if the 'inside' flag is set, and we only want
239 * FOLD_IS_OUTSIDE if the 'outside' flag is set.
240 */
241 if (((flags & FOLD_INSIDE) == 0 && relation == FOLD_IS_INSIDE)
242 || ((flags & FOLD_OUTSIDE)== 0 && relation == FOLD_IS_OUTSIDE))
243 {
244 #ifdef DEBUG_FOLD
245 fprintf(stderr, " skipping %s because !inside or !outside\n",
246 scan->name);
247 #endif
248 continue;
249 }
250
251 /* if we don't want to process whole nested trees, and we're
252 * scanning the "unfold" list, then we want to skip this item
253 * if the following item is inside it, and is part of the
254 * region we're scanning.
255 */
256 if (!infold && (flags & FOLD_NESTED) == 0)
257 {
258 /* oops, we may need to skip some intervening FOLDs
259 * that are disjoint from the range.
260 */
261 lagnext = scan;
262 while (next && markoffset(next->to) < fromoff)
263 {
264 #ifdef DEBUG_FOLD
265 fprintf(stderr, " bypassing %s because disjoint\n",
266 next->name);
267 #endif
268 lagnext = next;
269 next = next->next;
270 }
271
272 /* okay, now finish the test */
273 if (next
274 && markoffset(scan->to) > markoffset(next->from)
275 && markoffset(next->from) <= markoffset(to)
276 && markoffset(next->to) >= fromoff)
277 {
278 #ifdef DEBUG_FOLD
279 fprintf(stderr, " skipping %s because !nested and %s is better\n",
280 scan->name, next->name);
281 #endif
282 /* need to tweak scan, in case we bypassed any
283 * FOLDs in the little while-loop, above.
284 */
285 scan = lagnext;
286 continue;
287 }
288 }
289
290 if (flags & (FOLD_DESTROY|FOLD_TOGGLE))
291 {
292 /* delete the item from this list */
293 if (lag)
294 lag->next = scan->next;
295 else if (infold)
296 buf->fold = scan->next;
297 else
298 buf->unfold = scan->next;
299
300 /* if we don't want to process whole nested trees, and
301 * we're scanning the "fold" list, then we only want
302 * the outermost folds. Tweak the "fromoff" value to
303 * prevent any nested FOLDs from being processed.
304 */
305 if ((flags & FOLD_NESTED) == 0 && infold)
306 fromoff = markoffset(scan->to) + 1;
307
308 /* either free the item, or move it to the other list */
309 if (flags & FOLD_DESTROY)
310 foldfree(scan);
311 else if (infold)
312 foldadd(scan, ElvFalse);
313 else
314 foldadd(scan, ElvTrue);
315
316 /* remember that we found at least one FOLD */
317 result = RESULT_COMPLETE;
318
319 /* normally we set 'lag' to 'scan', but since we
320 * deleted 'scan' we want to leave 'lag' unchanged.
321 * The easiest way to do this is to set 'scan' to
322 * 'lag' now.
323 */
324 scan = lag;
325 }
326 else /* FOLD_TEST */
327 {
328 /* Don't change anything, but still remember that a
329 * a fold was found. Also, there's no reason to stay
330 * in the loop, so might as well break out of it.
331 */
332 result = RESULT_COMPLETE;
333 break;
334 }
335 }
336
337 return result;
338 }
339
340 /* Check to see whether a given mark is part of a currently-folded region.
341 * If so, then return the mark; else return NULL.
342 */
343 FOLD foldmark(mark, infold)
344 MARK mark; /* the mark to check (implies which buffer to check) */
345 ELVBOOL infold; /* search in "fold" list? (else search "unfold") */
346 {
347 FOLD scan, smallest = NULL;
348 BUFFER buf = markbuffer(mark);
349
350 /* scan the "fold" or "unfold" list of the mark's buffer */
351 for (scan = infold ? buf->fold : buf->unfold; scan; scan = scan->next)
352 {
353 /* does this FOLD contain the MARK? */
354 if (markoffset(scan->from) <= markoffset(mark)
355 && markoffset(mark) <= markoffset(scan->to))
356 {
357 if (infold)
358 /* return the first (largest) fold */
359 return scan;
360 else
361 smallest = scan;
362 }
363 }
364
365 /* return the smallest "unfold", or NULL if there were none or we were
366 * trying to search the "fold" list.
367 */
368 return smallest;
369 }
370
371 /* Adjust the "fold" and "unfold" lists in response to editing.
372 *
373 * There are two cases that we care about: Deleting text which includes the
374 * endpoints of a FOLD, and copying a region which includes a FOLD. We
375 * distinguish between these cases via the "dest" parameter -- it is NULL
376 * if deleting, or the destination if copying. (*Moving* text is accomplished
377 * by first copying, and then deleting the original text.)
378 *
379 * Note that this function should be called immediately BEFORE deleting, but
380 * immediately AFTER copying.
381 */
382 void foldedit(from, to, dest)
383 MARK from; /* first character to be deleted */
384 MARK to; /* first char AFTER the affected region */
385 MARK dest; /* where the region will be copied; NULL if no copy */
386 {
387 FOLD scan, fold;
388 MARKBUF foldfrom, foldto;
389 BUFFER buf = markbuffer(from);
390 ELVBOOL infold;
391
392 #ifdef DEBUG_FOLD
393 if (dest)
394 fprintf(stderr, "foldedit(%ld-%ld, {%s,%ld})\n",
395 markoffset(from), markoffset(to),
396 o_bufname(markbuffer(dest)), markoffset(dest));
397 else
398 fprintf(stderr, "foldedit(%ld-%ld, NULL)\n",
399 markoffset(from), markoffset(to));
400 #endif
401
402 /* if no folds in this buffer, then we certainly have nothing to do */
403 if (!buf->fold && !buf->unfold)
404 return;
405
406 /* Are we copying or deleting? */
407 if (dest)
408 {
409 /* After copy -- Duplicate any folds which are entirely within
410 * the source range. The duplicates are in the destination.
411 */
412 infold = (ELVBOOL)buf->fold;
413 for (scan = infold ? buf->fold : buf->unfold; scan; scan = fold)
414 {
415 /* if entirely within source region, then duplicate */
416 if (markoffset(scan->from) >= markoffset(from)
417 && markoffset(scan->to) < markoffset(to))
418 {
419 (void)marktmp(foldfrom, markbuffer(dest),
420 markoffset(dest) + markoffset(scan->from) - markoffset(from));
421 (void)marktmp(foldto, markbuffer(dest),
422 markoffset(dest) + markoffset(scan->to) - markoffset(from));
423 fold = foldalloc(&foldfrom, &foldto, scan->name);
424 if (infold)
425 foldadd(fold, ElvTrue);
426 else
427 foldadd(fold, ElvFalse);
428 }
429
430 /* choose next FOLD, wrapping from "fold" to "unfold" */
431 fold = scan->next;
432 if (!fold && infold)
433 {
434 fold = buf->unfold;
435 infold = ElvFalse;
436 }
437 }
438 }
439 else
440 {
441 /* Before delete -- Destroy any folds whose endpoints are in
442 * the deleted range.
443 */
444 foldto = *to;
445 markaddoffset(&foldto, -1);
446 foldbyrange(from, &foldto, ElvTrue, FOLD_INSIDE|FOLD_NESTED|FOLD_DESTROY);
447 foldbyrange(from, &foldto, ElvFalse, FOLD_INSIDE|FOLD_NESTED|FOLD_DESTROY);
448 }
449 }
450 #endif /* defined(FEATURE_FOLD) */
Something went wrong with that request. Please try again.