1 /* Part of XPCE --- The SWI-Prolog GUI toolkit
2
3 Author: Jan Wielemaker and Anjo Anjewierden
4 E-mail: jan@swi.psy.uva.nl
5 WWW: http://www.swi.psy.uva.nl/projects/xpce/
6 Copyright (c) 1985-2005, University of Amsterdam
7 All rights reserved.
8
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions
11 are met:
12
13 1. Redistributions of source code must retain the above copyright
14 notice, this list of conditions and the following disclaimer.
15
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in
18 the documentation and/or other materials provided with the
19 distribution.
20
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include <h/kernel.h>
36 #include <h/graphics.h>
37 #include <h/text.h>
38 #include <h/unix.h>
39
40 forwards Int getMatchingQuoteTextBuffer(TextBuffer, Int, Name);
41 forwards int room(TextBuffer, intptr_t, intptr_t);
42 forwards status capitalise_textbuffer(TextBuffer, intptr_t, intptr_t);
43 forwards status clear_textbuffer(TextBuffer);
44 forwards status downcase_textbuffer(TextBuffer, intptr_t, intptr_t);
45 forwards void end_change(TextBuffer, intptr_t);
46 forwards Int getSizeTextBuffer(TextBuffer);
47 forwards status store_textbuffer(TextBuffer, intptr_t, wint_t);
48 forwards status transpose_textbuffer(TextBuffer, intptr_t, intptr_t, intptr_t, intptr_t);
49 forwards status upcase_textbuffer(TextBuffer, intptr_t, intptr_t);
50 forwards status save_textbuffer(TextBuffer, intptr_t, intptr_t, SourceSink);
51 forwards status insert_file_textbuffer(TextBuffer, intptr_t, intptr_t, SourceSink);
52 forwards status shift_fragments(TextBuffer, intptr_t, intptr_t);
53 forwards void start_change(TextBuffer, intptr_t);
54 forwards status insert_textbuffer_shift(TextBuffer, intptr_t, intptr_t, PceString, int);
55 forwards status promoteTextBuffer(TextBuffer tb);
56
57 #define ALLOC (256) /* increment allocation by this amount */
58 #define ROUND(n, r) ( (((n) + (r)-1) / (r)) * (r) )
59 #define NormaliseIndex(tb, i) ( i < 0 ? 0 : i > tb->size ? tb->size : i)
60 #define Swap(a, b) { intptr_t _tmp = (a); (a) = (b); (b) = _tmp; }
61 #define Before(i1, i2) { if ( i1 > i2 ) Swap(i1, i2); }
62 #define Before_i(x, y) if ( x > y ) { intptr_t _z = x; x=y; y=_z; }
63 #define fetch(i) fetch_textbuffer(tb, i)
64 #define istbA(tb) isstrA(&(tb)->buffer)
65 #define Address(tb, i) (istbA(tb) ? &(tb)->tb_bufferA[(i)] \
66 : (charA *)&(tb)->tb_bufferW[(i)])
67 #define Index(tb, p) ((tb)->gap_start <= (p) ? \
68 (tb)->gap_end + ((p) - (tb)->gap_start) : (p) )
69
70
71 static status
initialiseTextBuffer(TextBuffer tb,CharArray ca)72 initialiseTextBuffer(TextBuffer tb, CharArray ca)
73 { initialiseSourceSink((SourceSink)tb);
74
75 assign(tb, first_fragment, NIL);
76 assign(tb, last_fragment, NIL);
77 assign(tb, editors, newObject(ClassChain, EAV));
78 assign(tb, generation, ZERO);
79 obtainClassVariablesObject(tb); /* dubious: subclassing? */
80
81 tb->undo_buffer = NULL;
82 tb->tb_bufferA = NULL;
83 if ( notDefault(ca) )
84 str_cphdr(&tb->buffer, &ca->data);
85 else
86 str_cphdr(&tb->buffer, str_nl(NULL)); /* ASCII */
87
88 clear_textbuffer(tb); /* (re)initialise buffer */
89
90 if ( notDefault(ca) )
91 insertTextBuffer(tb, ZERO, ca, ONE);
92 assign(tb, modified, OFF);
93
94 succeed;
95 }
96
97
98 static status
unlinkTextBuffer(TextBuffer tb)99 unlinkTextBuffer(TextBuffer tb)
100 { Any editor;
101
102 for_chain(tb->editors, editor,
103 send(ReceiverOfEditor(editor), NAME_lostTextBuffer, EAV));
104 clearChain(tb->editors);
105
106 while( notNil(tb->first_fragment) ) /* destroy fragments */
107 freeObject(tb->first_fragment);
108
109 if ( tb->tb_bufferA != NULL ) /* deallocate the buffer */
110 { pceFree(tb->tb_bufferA);
111 tb->tb_bufferA = NULL;
112 }
113
114 if ( tb->undo_buffer != NULL )
115 { destroyUndoBuffer(tb->undo_buffer);
116 tb->undo_buffer = NULL;
117 }
118
119 succeed;
120 }
121
122
123 static TextBuffer
getConvertTextBuffer(Any ctx,Editor e)124 getConvertTextBuffer(Any ctx, Editor e)
125 { answer(e->text_buffer);
126 }
127
128
129 static status
storeTextBuffer(TextBuffer tb,FileObj file)130 storeTextBuffer(TextBuffer tb, FileObj file)
131 { IOENC oenc = file->fd->encoding;
132 int i;
133
134 TRY(storeSlotsObject(tb, file));
135 storeIntFile(file, toInt(tb->size));
136
137 file->fd->encoding = ENC_UTF8;
138 for(i=0; i<tb->size; i++)
139 Sputcode(fetch_textbuffer(tb, i), file->fd);
140 file->fd->encoding = oenc;
141
142 return checkErrorFile(file);
143 }
144
145
146 static status
loadTextBuffer(TextBuffer tb,IOSTREAM * fd,ClassDef def)147 loadTextBuffer(TextBuffer tb, IOSTREAM *fd, ClassDef def)
148 { IOENC oenc = fd->encoding;
149 size_t i;
150 int chr;
151
152 TRY( loadSlotsObject(tb, fd, def) );
153
154 if ( isNil(tb->syntax) )
155 assign(tb, syntax, getClassVariableValueObject(tb, NAME_syntax));
156 if ( !isName(tb->encoding) )
157 assign(tb, encoding, NAME_text);
158
159 assign(tb, editors, newObject(ClassChain, EAV));
160 tb->size = loadWord(fd);
161 tb->allocated = ROUND(tb->size, ALLOC);
162
163 str_cphdr(&tb->buffer, str_nl(NULL));
164 tb->tb_bufferA = pceMalloc(tb->allocated);
165
166 if ( restoreVersion <= 17 ) /* PRE Unicode */
167 { Sfread(Address(tb, 0), sizeof(char), tb->size, fd);
168 } else
169 { size_t end = (size_t)tb->size;
170
171 fd->encoding = ENC_UTF8;
172
173 for(i=0; i<end; i++)
174 { chr = Sgetcode(fd);
175
176 if ( chr <= 0xff )
177 tb->tb_bufferA[i] = chr;
178 else
179 break;
180 }
181
182 if ( i < end ) /* non-ISO characters: promote */
183 { charW *w = pceMalloc(tb->allocated * sizeof(charW));
184 const charA *f = Address(tb, 0);
185 const charA *e = &f[i];
186 charW *t = w;
187
188 while(f<e)
189 *t++ = *f++;
190
191 pceFree(tb->tb_bufferA);
192 tb->tb_bufferW = w;
193 tb->buffer.s_iswide = TRUE;
194 tb->tb_bufferW[i++] = chr;
195
196 for(; i<end; i++)
197 { chr = Sgetcode(fd);
198
199 tb->tb_bufferW[i] = chr;
200 }
201 }
202 fd->encoding = oenc;
203 }
204
205 tb->gap_start = tb->size;
206 tb->gap_end = tb->allocated;
207
208 if ( tb->lines == 0 )
209 { tb->lines = -1; /* indicate invalid */
210 tb->lines = count_lines_textbuffer(tb, 0, tb->size);
211 }
212
213 tb->changed_start = tb->size;
214 tb->changed_end = 0;
215 CmodifiedTextBuffer(tb, OFF);
216 assign(tb, generation, ZERO);
217
218 succeed;
219 }
220
221
222 static status
cloneTextBuffer(TextBuffer tb,TextBuffer clone)223 cloneTextBuffer(TextBuffer tb, TextBuffer clone)
224 { size_t bytes;
225
226 clonePceSlots(tb, clone);
227
228 bytes = istbA(tb) ? (size_t)clone->allocated
229 : (size_t)clone->allocated*sizeof(charW);
230
231 clone->undo_buffer = NULL;
232 clone->tb_bufferA = pceMalloc(bytes);
233 memcpy(clone->tb_bufferA, tb->tb_bufferA, bytes);
234 clone->changed_start = clone->size;
235 clone->changed_end = 0;
236
237 succeed;
238 }
239
240
241 /*******************************
242 * EDITOR *
243 *******************************/
244
245 static status
attachTextBuffer(TextBuffer tb,Editor e)246 attachTextBuffer(TextBuffer tb, Editor e)
247 { return appendChain(tb->editors, e);
248 }
249
250
251 static status
detachTextBuffer(TextBuffer tb,Editor e)252 detachTextBuffer(TextBuffer tb, Editor e)
253 { deleteChain(tb->editors, e);
254
255 succeed;
256 }
257
258
259 /********************************
260 * REPORT ERRORS *
261 ********************************/
262
263 static status
reportTextBuffer(TextBuffer tb,Name kind,CharArray fmt,int argc,Any * argv)264 reportTextBuffer(TextBuffer tb, Name kind, CharArray fmt, int argc, Any *argv)
265 { Any to;
266
267 if ( (to = get(tb, NAME_reportTo, EAV)) && notNil(to) )
268 { Any editor;
269
270 ArgVector(av, argc + 2);
271
272 av[0] = kind;
273 av[1] = fmt;
274 copyArgs(argc, argv, &av[2]);
275
276 if ( (editor = get(to, NAME_container, ClassEditor, EAV)) )
277 { sendv(editor, NAME_report, argc+2, av);
278 } else
279 { sendv(to, NAME_report, argc+2, av);
280 }
281
282 succeed;
283 }
284
285 return printReportObject(tb, kind, fmt, argc, argv);
286 }
287
288
289 /********************************
290 * FORWARDING CHANGES *
291 *********************************/
292
293 status
changedTextBuffer(TextBuffer tb)294 changedTextBuffer(TextBuffer tb)
295 { Cell cell;
296
297 if ( tb->changed_start <= tb->changed_end )
298 { Any av[2];
299
300 av[0] = toInt(tb->changed_start);
301 av[1] = toInt(tb->changed_end);
302
303 for_cell(cell, tb->editors)
304 qadSendv(cell->value, NAME_ChangedRegion, 2, av);
305 }
306
307 tb->changed_start = tb->size;
308 tb->changed_end = 0;
309
310 succeed;
311 }
312
313
314 status
ChangedRegionTextBuffer(TextBuffer tb,Int start,Int end)315 ChangedRegionTextBuffer(TextBuffer tb, Int start, Int end)
316 { intptr_t s = valInt(start);
317 intptr_t e = valInt(end);
318
319 if ( s > e )
320 { intptr_t tmp = s;
321 s = e; e = tmp;
322 }
323 start_change(tb, s);
324 end_change(tb, e);
325
326 return changedTextBuffer(tb);
327 }
328
329
330 status
ChangedFragmentListTextBuffer(TextBuffer tb)331 ChangedFragmentListTextBuffer(TextBuffer tb)
332 { Cell cell;
333
334 for_cell(cell, tb->editors)
335 qadSendv(cell->value, NAME_ChangedFragmentList, 0, NULL);
336
337 succeed;
338 }
339
340
341 /********************************
342 * OBJECT LEVEL OPERATIONS *
343 *********************************/
344
345 status
clearTextBuffer(TextBuffer tb)346 clearTextBuffer(TextBuffer tb)
347 { clear_textbuffer(tb);
348
349 return changedTextBuffer(tb);
350 }
351
352
353 status
insertFileTextBuffer(TextBuffer tb,Int where,SourceSink file,Int times)354 insertFileTextBuffer(TextBuffer tb, Int where, SourceSink file, Int times)
355 { if ( isDefault(times) )
356 times = ONE;
357
358 if ( valInt(times) <= 0 )
359 succeed;
360
361 if ( insert_file_textbuffer(tb, valInt(where), valInt(times), file) )
362 return changedTextBuffer(tb);
363
364 fail;
365 }
366
367
368 status
insertTextBuffer(TextBuffer tb,Int where,CharArray ca,Int times)369 insertTextBuffer(TextBuffer tb, Int where, CharArray ca, Int times)
370 { if ( isDefault(times) )
371 times = ONE;
372
373 insert_textbuffer(tb,
374 valInt(where),
375 valInt(times),
376 &ca->data);
377
378 return changedTextBuffer(tb);
379 }
380
381
382 status
CAppendTextBuffer(TextBuffer tb,const char * text)383 CAppendTextBuffer(TextBuffer tb, const char *text)
384 { string s;
385
386 str_set_ascii(&s, (char*)text);
387 insert_textbuffer_shift(tb,
388 tb->size,
389 1,
390 &s,
391 FALSE);
392
393 return changedTextBuffer(tb);
394
395 }
396
397
398 status
appendTextBuffer(TextBuffer tb,CharArray ca,Int times)399 appendTextBuffer(TextBuffer tb, CharArray ca, Int times)
400 { if ( isDefault(times) )
401 times = ONE;
402
403 insert_textbuffer_shift(tb,
404 tb->size,
405 valInt(times),
406 &ca->data,
407 FALSE); /* don't shift fragments! */
408
409 return changedTextBuffer(tb);
410 }
411
412
413 static status
formatTextBuffer(TextBuffer tb,CharArray fmt,int argc,Any * argv)414 formatTextBuffer(TextBuffer tb, CharArray fmt, int argc, Any *argv)
415 { string s;
416
417 TRY(str_writefv(&s, fmt, argc, argv));
418 insert_textbuffer_shift(tb, tb->size, 1, &s, FALSE);
419 /* don't shift fragments! */
420 str_unalloc(&s);
421
422 return changedTextBuffer(tb);
423 }
424
425
426 status
deleteTextBuffer(TextBuffer tb,Int where,Int times)427 deleteTextBuffer(TextBuffer tb, Int where, Int times)
428 { if ( isDefault(times) )
429 times = ONE;
430
431 delete_textbuffer(tb, valInt(where), valInt(times));
432
433 return changedTextBuffer(tb);
434 }
435
436
437 status
saveTextBuffer(TextBuffer tb,SourceSink file,Int from,Int len)438 saveTextBuffer(TextBuffer tb, SourceSink file, Int from, Int len)
439 { int clear_modified = (isDefault(from) && isDefault(len));
440
441 if ( isDefault(from) )
442 from = ZERO;
443 if ( isDefault(len) )
444 len = toInt(tb->size);
445
446 if ( save_textbuffer(tb, valInt(from), valInt(len), file) )
447 { if ( clear_modified )
448 CmodifiedTextBuffer(tb, OFF);
449 succeed;
450 }
451
452 fail;
453 }
454
455
456 static Int
getSizeTextBuffer(TextBuffer tb)457 getSizeTextBuffer(TextBuffer tb)
458 { return toInt(tb->size);
459 }
460
461
462 status
CmodifiedTextBuffer(TextBuffer tb,BoolObj val)463 CmodifiedTextBuffer(TextBuffer tb, BoolObj val)
464 { if ( tb->modified != val )
465 sendv(tb, NAME_modified, 1, (Any *) &val);
466
467 if ( val == ON )
468 tb->generation = toInt(valInt(tb->generation)+1);
469
470 succeed;
471 }
472
473
474 static status
modifiedTextBuffer(TextBuffer tb,BoolObj val)475 modifiedTextBuffer(TextBuffer tb, BoolObj val)
476 { if ( tb->modified != val )
477 { Cell cell;
478 assign(tb, modified, val);
479
480 if ( val == OFF )
481 checkpointUndoTextBuffer(tb);
482
483 for_cell(cell, tb->editors)
484 forwardModifiedEditor(cell->value, val);
485 }
486
487 succeed;
488 }
489
490
491 static Int
getCharacterTextBuffer(TextBuffer tb,Int where)492 getCharacterTextBuffer(TextBuffer tb, Int where)
493 { int c = fetch_textbuffer(tb, valInt(where));
494
495 if ( c >= 0 )
496 answer(toInt(c));
497
498 fail;
499 }
500
501
502 status
characterTextBuffer(TextBuffer tb,Int where,Int c)503 characterTextBuffer(TextBuffer tb, Int where, Int c)
504 { TRY(store_textbuffer(tb, valInt(where), (wint_t)valInt(c)));
505
506 return changedTextBuffer(tb);
507 }
508
509
510 status
transposeTextBuffer(TextBuffer tb,Int f1,Int t1,Int f2,Int t2)511 transposeTextBuffer(TextBuffer tb, Int f1, Int t1, Int f2, Int t2)
512 { transpose_textbuffer(tb, valInt(f1), valInt(t1), valInt(f2), valInt(t2));
513
514 return changedTextBuffer(tb);
515 }
516
517
518 status
downcaseTextBuffer(TextBuffer tb,Int from,Int len)519 downcaseTextBuffer(TextBuffer tb, Int from, Int len)
520 { downcase_textbuffer(tb, valInt(from), valInt(len));
521
522 return changedTextBuffer(tb);
523 }
524
525
526 status
upcaseTextBuffer(TextBuffer tb,Int from,Int len)527 upcaseTextBuffer(TextBuffer tb, Int from, Int len)
528 { upcase_textbuffer(tb, valInt(from), valInt(len));
529
530 return changedTextBuffer(tb);
531 }
532
533
534 status
capitaliseTextBuffer(TextBuffer tb,Int from,Int len)535 capitaliseTextBuffer(TextBuffer tb, Int from, Int len)
536 { capitalise_textbuffer(tb, valInt(from), valInt(len));
537
538 return changedTextBuffer(tb);
539 }
540
541
542 Int
getScanTextBuffer(TextBuffer tb,Int from,Name unit,Int amount,Name az)543 getScanTextBuffer(TextBuffer tb, Int from, Name unit, Int amount, Name az)
544 { if ( isDefault(amount) )
545 amount = ONE;
546 if ( isDefault(az) )
547 az = (valInt(amount) >= 0 ? NAME_end : NAME_start);
548
549 return toInt(scan_textbuffer(tb,
550 valInt(from),
551 unit,
552 valInt(amount),
553 az == NAME_start ? 'a' : 'z'));
554 }
555
556
557 static status
contentsTextBuffer(TextBuffer tb,CharArray ca)558 contentsTextBuffer(TextBuffer tb, CharArray ca)
559 { clearTextBuffer(tb);
560
561 return appendTextBuffer(tb, ca, ONE);
562 }
563
564
565 static StringObj
getSubTextBuffer(TextBuffer tb,Int from,Int to)566 getSubTextBuffer(TextBuffer tb, Int from, Int to)
567 { string s;
568 intptr_t f = (isDefault(from) ? 0 : valInt(from));
569 intptr_t t = (isDefault(to) ? tb->size : valInt(to));
570
571 str_sub_text_buffer(tb, &s, f, t-f);
572 answer(StringToString(&s));
573 }
574
575
576 StringObj
getContentsTextBuffer(TextBuffer tb,Int from,Int length)577 getContentsTextBuffer(TextBuffer tb, Int from, Int length)
578 { if ( isDefault(from) )
579 from = ZERO;
580
581 return getSubTextBuffer(tb, from,
582 isDefault(length) ? DEFAULT : add(from, length));
583 }
584
585
586 static status
forAllFragmentsTextBuffer(TextBuffer tb,Code msg)587 forAllFragmentsTextBuffer(TextBuffer tb, Code msg)
588 { int size = 0;
589 Fragment f;
590 Fragment *argv;
591 int alloced = FALSE;
592 int i;
593 int rc = SUCCEED;
594
595 for(f = tb->first_fragment; notNil(f); f = f->next)
596 size++;
597
598 if ( size > 1024 || !(argv = alloca(size*sizeof(Fragment))) )
599 { argv = pceMalloc(size*sizeof(Fragment));
600 alloced = TRUE;
601 }
602
603
604 for(i=0, f = tb->first_fragment; notNil(f); f = f->next)
605 argv[i++] = f;
606
607 for(i=0; i<size; i++)
608 { if ( !isFreedObj(argv[i]) )
609 { if ( !forwardCodev(msg, 1, (Any *)&argv[i]) )
610 { rc = FAIL;
611 break;
612 }
613 }
614 }
615
616 if ( alloced )
617 pceFree(argv);
618
619 return rc;
620 }
621
622
623 static Chain
getFindAllFragmentsTextBuffer(TextBuffer tb,Code msg)624 getFindAllFragmentsTextBuffer(TextBuffer tb, Code msg)
625 { Chain result = answerObject(ClassChain, EAV);
626 Fragment f;
627
628 for(f = tb->first_fragment; notNil(f); f = f->next)
629 { if ( isDefault(msg) || forwardCodev(msg, 1, (Any *)&f) )
630 appendChain(result, f);
631 }
632
633 answer(result);
634 }
635
636
637 static Fragment
getFindFragmentTextBuffer(TextBuffer tb,Code msg)638 getFindFragmentTextBuffer(TextBuffer tb, Code msg)
639 { Fragment f;
640
641 for(f = tb->first_fragment; notNil(f); f = f->next)
642 { if ( forwardCodev(msg, 1, (Any *)&f) )
643 answer(f);
644 }
645
646 fail;
647 }
648
649
650 static Int
getFindTextBuffer(TextBuffer tb,Int from,StringObj str,Int times,Name start,BoolObj exactcase,BoolObj wordmode)651 getFindTextBuffer(TextBuffer tb, Int from, StringObj str,
652 Int times, Name start, BoolObj exactcase, BoolObj wordmode)
653 { char az;
654 int result;
655 int ec, wm;
656
657 if ( isDefault(times) )
658 times = ONE;
659 az = (isDefault(start) ? (valInt(times) >= 0 ? 'z' : 'a')
660 : start == NAME_start ? 'a' : 'z');
661 ec = (exactcase == ON || isDefault(exactcase) ? TRUE : FALSE);
662 wm = (wordmode == OFF || isDefault(wordmode) ? FALSE : TRUE);
663
664 result = find_textbuffer(tb,
665 valInt(from),
666 &str->data,
667 valInt(times),
668 az, ec, wm);
669 if ( result < 0 )
670 fail;
671
672 answer(toInt(result));
673 }
674
675 /********************************
676 * SCANNING *
677 *********************************/
678
679 /* Find a position by scanning the text. The scan starts at `from' and
680 returns the index resulting from skipping `amount' `units'. A unit
681 consists of the unit itself and a separator (with the exception of
682 characters that have a zero length separator between them). One can
683 scan for the start of a unit or for the end of the unit. The start
684 of the unit returns the first index of the unit. The end returns the
685 first index of the separator between this unit and the next. Below
686 are the definitions of the units recognised currently.
687
688 NAME_character
689 A character unit has no start, nor an end.
690 NAME_word
691 A word consists of a contiguous string of `alNum' characters
692 (digits + letters + _). A separator is a contiguous string of
693 'non-alNum' characters.
694 NAME_line
695 A line consists of a (possible empty) string of non-'\n'
696 characters. A separator is exactly one '\n'.
697 NAME_sentence
698 A sentence separator is a seqence [.!?], followed by a non-empty
699 sequence of blank characters. The sentence separator is a sequence
700 of any character except for the sentence separator sequence.
701 NAME_paragraph
702 A paragraph separator is sequence of blank lines.
703 NAME_term
704 A term if either:
705 a) a series of alnum characters
706 b) a quoted string
707 c) a string with matching brackets at its start end end
708 d) case a), immediately followed by case c).
709
710 Negative amount scans backwards. The returned index is in the range
711 [0, size).
712 */
713
714
715 static status
ends_sentence(TextBuffer tb,intptr_t here)716 ends_sentence(TextBuffer tb, intptr_t here)
717 { return matchRegex(tb->syntax->sentence_end, tb, toInt(here), DEFAULT);
718 }
719
720
721 status
parsep_line_textbuffer(TextBuffer tb,intptr_t here)722 parsep_line_textbuffer(TextBuffer tb, intptr_t here)
723 { int rval = matchRegex(tb->syntax->paragraph_end, tb, toInt(here), DEFAULT);
724
725 DEBUG(NAME_paragraph,
726 Cprintf("parsep_line_textbuffer(%s, %d) --> %s\n",
727 pp(tb), here, rval ? "yes" : "no"));
728
729 return rval;
730 }
731
732
733 static int
all_layout(TextBuffer tb,intptr_t from,intptr_t to)734 all_layout(TextBuffer tb, intptr_t from, intptr_t to)
735 { SyntaxTable syntax = tb->syntax;
736
737 while( from < to && tislayout(syntax, fetch(from)) )
738 from++;
739
740 return from == to;
741 }
742
743
744 static intptr_t
forward_skip_par_textbuffer(TextBuffer tb,intptr_t here)745 forward_skip_par_textbuffer(TextBuffer tb, intptr_t here)
746 { intptr_t size = tb->size;
747
748 while( here < size && parsep_line_textbuffer(tb, here) )
749 { intptr_t h = scan_textbuffer(tb, here, NAME_line, 1, 'a');
750 if ( !all_layout(tb, here, h) )
751 return h;
752 here = h;
753 }
754 while( here < size && !parsep_line_textbuffer(tb, here) )
755 here = scan_textbuffer(tb, here, NAME_line, 1, 'a');
756
757 return here;
758 }
759
760
761 static intptr_t
backward_skip_par_textbuffer(TextBuffer tb,intptr_t here)762 backward_skip_par_textbuffer(TextBuffer tb, intptr_t here)
763 { here = scan_textbuffer(tb, here, NAME_line, -1, 'a');
764
765 while( here > 0 && parsep_line_textbuffer(tb, here) )
766 { intptr_t h = scan_textbuffer(tb, here, NAME_line, -1, 'a');
767 if ( !all_layout(tb, h, here) )
768 return h;
769 here = h;
770 }
771 while( here > 0 && !parsep_line_textbuffer(tb, here) )
772 here = scan_textbuffer(tb, here, NAME_line, -1, 'a');
773
774 return here;
775 }
776
777
778 intptr_t
scan_textbuffer(TextBuffer tb,intptr_t from,Name unit,intptr_t amount,int az)779 scan_textbuffer(TextBuffer tb, intptr_t from, Name unit, intptr_t amount, int az)
780 { intptr_t here;
781 intptr_t size = tb->size;
782 SyntaxTable syntax = tb->syntax;
783
784 DEBUG(NAME_scan, Cprintf("scan_textbuffer(%s, %d, %s, %d, %c)\n",
785 pp(tb), from, pp(unit), amount, az));
786
787 here = from;
788
789 if ( unit == NAME_character )
790 { here = from + amount; /* 'az' does not count (no separator) */
791 return NormaliseIndex(tb, here);
792 } else if ( unit == NAME_word )
793 { if ( az == 'a' )
794 { if ( amount <= 0 )
795 { for( ; here > 0 && amount <= 0; amount++ )
796 { while( !tisalnum(syntax, fetch(here)) && here > 0 ) here--;
797 while( tisalnum(syntax, fetch(here)) && here > 0 ) here--;
798 }
799 return (here == 0 ? here : here+1);
800 } else
801 { for( ; here < size && amount > 0; amount-- )
802 { while( tisalnum(syntax, fetch(here)) && here < size ) here++;
803 while( !tisalnum(syntax, fetch(here)) && here < size ) here++;
804 }
805 return here;
806 }
807 } else /* 'z' mode */
808 { if ( amount >= 0 )
809 { for( ; here < size && amount >= 0; amount-- )
810 { while( !tisalnum(syntax, fetch(here)) && here < size ) here++;
811 while( tisalnum(syntax, fetch(here)) && here < size ) here++;
812 }
813 return here;
814 } else
815 { for( ; here > 0 && amount < 0; amount-- )
816 { while( tisalnum(syntax, fetch(here)) && here > 0 ) here--;
817 while( !tisalnum(syntax, fetch(here)) && here > 0 ) here--;
818 }
819 return here == 0 ? here : here+1;
820 }
821 }
822 } else if ( unit == NAME_line )
823 { if ( az == 'a' ) /* return first char of line */
824 { if ( amount <= 0 )
825 { for( ; here >= 0 && amount <= 0; amount++ )
826 { if ( tisendsline(syntax, fetch(here)) )
827 here--;
828 while( here >= 0 && !tisendsline(syntax, fetch(here)) )
829 here--;
830 }
831 return (here < 0 ? 0 : here + 1);
832 } else
833 { for( ; here <= size && amount > 0; amount--)
834 { while( here <= size && !tisendsline(syntax, fetch(here)) )
835 here++;
836 here++;
837 }
838 return (here >= size ? size : here);
839 }
840 } else /* 'z' case */
841 { if ( amount >= 0 )
842 { for( ; ; amount-- )
843 { while( here <= size && !tisendsline(syntax, fetch(here)) )
844 here++;
845 if ( here >= size || amount == 0 )
846 return here > size ? size : here;
847 here++;
848 }
849 } else
850 { for( ; ; amount++ )
851 { while( here > 0 && !tisendsline(syntax, fetch(here)) )
852 here--;
853 if ( here <= 0 || amount == 0 )
854 return here < 0 ? 0 : here;
855 here--;
856 }
857 }
858 }
859 } else if ( unit == NAME_sentence )
860 { if ( az == 'z' )
861 { if ( amount >= 0 )
862 { for( ; here < size && amount >= 0; amount-- )
863 { here++;
864 while( here < size && !ends_sentence(tb, here) ) here++;
865 }
866 return here;
867 } else
868 { while(here > 0 && !ends_sentence(tb, here) ) here--;
869 for( ; here > 0 && amount < 0; amount++ )
870 { here--;
871 while(here > 0 && !ends_sentence(tb, here) ) here--;
872 }
873 return here;
874 }
875 } else /* 'a' case */
876 { if ( amount <= 0 )
877 { for( ; here > 0 && amount <= 0; amount++ )
878 { here--;
879 while(here > 0 && tislayout(syntax, fetch(here)) ) here--;
880 while(here > 0 && !ends_sentence(tb, here) ) here--;
881 }
882 while( here < size && tislayout(syntax, fetch(here)) ) here++;
883 return here;
884 } else
885 { for( ; here < size && amount > 0; amount-- )
886 { here++;
887 while( here < size && !ends_sentence(tb, here) ) here++;
888 }
889 while( here < size && tislayout(syntax, fetch(here)) ) here++;
890 return here;
891 }
892 }
893 } else if ( unit == NAME_paragraph )
894 { if ( az == 'z' )
895 { if ( amount >= 0 )
896 { here = scan_textbuffer(tb, here, NAME_line, 0, 'a');
897 for( ; here < size && amount >= 0; amount-- )
898 here = forward_skip_par_textbuffer(tb, here);
899 return here;
900 } else
901 { for( ; here > 0 && amount < 0; amount++ )
902 { here--;
903 here = backward_skip_par_textbuffer(tb, here);
904 }
905 return here;
906 }
907 } else /* the 'a' case */
908 { if ( amount > 0 )
909 { for( ; here < size && amount > 0; amount-- )
910 here = forward_skip_par_textbuffer(tb, here);
911 return here;
912 } else
913 { for( ; here > 0 && amount <= 0; amount++ )
914 { here = backward_skip_par_textbuffer(tb, here);
915 }
916 if ( parsep_line_textbuffer(tb, here) )
917 here = scan_textbuffer(tb, here, NAME_line, 1, 'a');
918 return here;
919 }
920 }
921 } else if ( unit == NAME_term )
922 { if ( amount > 0 ) /* forwards */
923 { for( ; here < size && amount > 0; amount-- )
924 { while( here < size && !tischtype(syntax, fetch(here), AN|OB|QT) )
925 here++;
926 if ( amount == 1 && az == 'a' )
927 return here;
928 while( here < size && tisalnum(syntax, fetch(here)) )
929 here++;
930 if ( tisquote(syntax, fetch(here)) )
931 { Int h = getMatchingQuoteTextBuffer(tb, toInt(here), NAME_forward);
932 if ( h == FAIL ) return here;
933 here = valInt(h) + 1;
934 } else if ( tisopenbrace(syntax, fetch(here)) )
935 { Int h = getMatchingBracketTextBuffer(tb, toInt(here), DEFAULT);
936 if ( h == FAIL ) return here;
937 here = valInt(h) + 1;
938 }
939 }
940 return here;
941 } else if ( amount < 0 ) /* backwards */
942 { for( ; here > 0 && amount < 0; amount++ )
943 { here--;
944 while( here > 0 && !tischtype(syntax, fetch(here), AN|CB|QT) )
945 here--;
946 if ( amount == -1 && az == 'z' )
947 return here+1;
948 if ( tisquote(syntax, fetch(here)) )
949 { Int h = getMatchingQuoteTextBuffer(tb, toInt(here), NAME_backward);
950 if ( h == FAIL ) return here;
951 here = valInt(h);
952 } else if ( tisclosebrace(syntax, fetch(here)) )
953 { Int h = getMatchingBracketTextBuffer(tb, toInt(here), DEFAULT);
954 if ( h == FAIL ) return here;
955 here = valInt(h);
956 }
957 if ( tisalnum(syntax, fetch(here-1)) )
958 while( here > 0 && tisalnum(syntax, fetch(here-1)) )
959 here--;
960 }
961 return here;
962 }
963 return here;
964 } else
965 fail;
966 }
967
968
969 static Int
getMatchingQuoteTextBuffer(TextBuffer tb,Int idx,Name direction)970 getMatchingQuoteTextBuffer(TextBuffer tb, Int idx, Name direction)
971 { intptr_t i = valInt(idx);
972 int c = fetch(i);
973 SyntaxTable syntax = tb->syntax;
974
975 if ( !tisquote(tb->syntax, c) ) /* must start on quote */
976 fail;
977
978 if ( direction == NAME_forward )
979 { intptr_t i0 = i;
980 int quoteisescape = tisstringescape(syntax, c, c);
981
982 for(i++; i<tb->size; i++)
983 { int c2 = fetch(i);
984
985 if ( c2 == c )
986 { if ( quoteisescape && i+1 < tb->size && fetch(i+1) == c )
987 { i++; /* Prolog 'Can''t' syntax! */
988 continue;
989 }
990 if ( i-1 > i0 &&
991 (c2=fetch(i-1)) != c &&
992 tisstringescape(syntax, c, c2) )
993 continue;
994
995 answer(toInt(i));
996 }
997 }
998 } else /* if ( direction == NAME_backward ) */
999 { for(i--; i>=0; i--)
1000 { int c2 = fetch(i);
1001
1002 if ( c2 == c )
1003 { if ( i == 0 )
1004 answer(toInt(i));
1005 if ( tisstringescape(syntax, c, fetch(i-1)) )
1006 { if ( tisstringescape(syntax, c, c) )
1007 i--; /* Prolog 'Can''t' syntax! */
1008 } else
1009 answer(toInt(i));
1010 }
1011 }
1012 }
1013
1014 fail;
1015 }
1016
1017
1018 /*******************************
1019 * SYNTAX HANDLING *
1020 *******************************/
1021
1022 #define SST_PLAIN 0x0100 /* syntax-state */
1023 #define SST_COMMENT1 0x0200 /* 1-character comment-string */
1024 #define SST_COMMENT2 0x0400 /* 2-character comment-string */
1025 #define SST_STRING 0x0800 /* string (low-order is start) */
1026 #define SST_QQ 0x1000 /* Quasi quotation */
1027
1028 static int
match_qq(TextBuffer tb,intptr_t here,const char * s)1029 match_qq(TextBuffer tb, intptr_t here, const char *s)
1030 { for(; *s; s++)
1031 { if ( fetch(here) != *s )
1032 fail;
1033 }
1034
1035 succeed;
1036 }
1037
1038
1039 static int
scan_syntax_textbuffer(TextBuffer tb,intptr_t from,intptr_t to,int flags,intptr_t * start)1040 scan_syntax_textbuffer(TextBuffer tb,
1041 intptr_t from, intptr_t to,
1042 int flags,
1043 intptr_t *start)
1044 { intptr_t here = from; /* current position */
1045 SyntaxTable syntax = tb->syntax; /* syntax-table */
1046 int state = SST_PLAIN; /* initial/current state */
1047 intptr_t tokenstart = from;
1048 char *qq_start = NULL;
1049
1050 if ( notNil(syntax->qq_start) )
1051 qq_start = strName(syntax->qq_start);
1052
1053 for(; here < to; here++)
1054 { int c = fetch(here);
1055
1056 /* strings */
1057 if ( tisquote(syntax, c) )
1058 { int quoteisescape = tisstringescape(syntax, c, c);
1059
1060 /* Prolog 0'char syntax */
1061 if ( c == '\'' && syntax->prolog == ON && here > 0 )
1062 { wint_t c0 = fetch(here-1);
1063
1064 if ( iswdigit(c0) ) /* or <digit><number> */
1065 { if ( c0 == '0' )
1066 { here++; /* ignore this char */
1067 if ( here >= to )
1068 { tokenstart = here-2;
1069 state = SST_STRING;
1070 break;
1071 }
1072 }
1073 continue;
1074 }
1075 }
1076
1077 state = SST_STRING|c;
1078 tokenstart = here;
1079
1080 for(here++; here<to; here++)
1081 { int c2 = fetch(here);
1082
1083 if ( c2 == c )
1084 { if ( quoteisescape && here+1 < tb->size && fetch(here+1) == c )
1085 { here++;
1086 continue;
1087 }
1088 if ( here-1 > tokenstart &&
1089 (c2=fetch(here-1)) != c &&
1090 tisstringescape(syntax, c, c2) )
1091 continue;
1092
1093 state = SST_PLAIN;
1094 goto cont;
1095 } else if ( c2 == '\\' )
1096 here++; /* skip next */
1097 }
1098 } else if ( tiscommentstart(syntax, c) ) /* COMMENT (1) */
1099 { tokenstart = here;
1100
1101 for(here++ ; here < to; here++ )
1102 { int c = fetch(here);
1103
1104 if ( tiscommentend(syntax, c) )
1105 goto cont;
1106 }
1107
1108 state = SST_COMMENT1;
1109 break;
1110 } else if ( tiscommentstart1(syntax, c) &&
1111 tiscommentstart2(syntax, fetch(here+1)) )
1112 { tokenstart = here;
1113
1114 for( here += 4; here < to; here++ )
1115 { int c = fetch(here - 1);
1116
1117 if ( tiscommentend2(syntax, c) )
1118 { c = fetch(here - 2);
1119 if ( tiscommentend1(syntax, c) )
1120 goto cont;
1121 }
1122 }
1123
1124 state = SST_COMMENT2;
1125 break;
1126 } else if ( qq_start && c == qq_start[0] &&
1127 match_qq(tb, here+1, qq_start+1) )
1128 { char *qq_end;
1129
1130 if ( notNil(syntax->qq_end) )
1131 qq_end = strName(syntax->qq_end);
1132 else
1133 goto cont; /* no end. Warning? */
1134
1135 tokenstart = here;
1136
1137 for(here += strlen(qq_start); here < to; here++)
1138 { int c = fetch(here);
1139
1140 if ( c == qq_end[0] && match_qq(tb, here+1, qq_end+1) )
1141 { here += strlen(qq_end);
1142 goto cont;
1143 }
1144 }
1145
1146 state = SST_QQ;
1147 break;
1148 }
1149
1150 cont:
1151 ;
1152 }
1153
1154 if ( start )
1155 *start = tokenstart;
1156
1157 return state;
1158 }
1159
1160
1161 static Tuple
getScanSyntaxTextBuffer(TextBuffer tb,Int f,Int t)1162 getScanSyntaxTextBuffer(TextBuffer tb, Int f, Int t)
1163 { intptr_t from = NormaliseIndex(tb, valInt(f));
1164 intptr_t to = NormaliseIndex(tb, valInt(t));
1165 intptr_t start;
1166 Name class;
1167 int s;
1168
1169 if ( to == tb->size )
1170 to--;
1171
1172 s = scan_syntax_textbuffer(tb, from, to, 0, &start);
1173 switch(s&0xff00)
1174 { case SST_PLAIN:
1175 class = NAME_code;
1176 break;
1177 case SST_COMMENT1:
1178 case SST_COMMENT2:
1179 class = NAME_comment;
1180 break;
1181 case SST_STRING:
1182 class = NAME_string;
1183 break;
1184 case SST_QQ:
1185 class = NAME_quasiQuotation;
1186 break;
1187 default:
1188 assert(0);
1189 fail;
1190 }
1191
1192 answer(answerObject(ClassTuple, class, toInt(start), EAV));
1193 }
1194
1195
1196 static status
inStringTextBuffer(TextBuffer tb,Int pos,Int from)1197 inStringTextBuffer(TextBuffer tb, Int pos, Int from)
1198 { intptr_t idx = valInt(pos);
1199 intptr_t here = (isDefault(from) ? 0L : valInt(from));
1200 SyntaxTable syntax = tb->syntax;
1201
1202 for( ; here <= idx; here++)
1203 { int c = fetch(here);
1204
1205 if ( tisquote(syntax, c) )
1206 { Int match;
1207
1208 DEBUG(NAME_inString, Cprintf("here = %ld (idx = %ld)\n", here, idx));
1209
1210 /* Prolog 0'char syntax */
1211 if ( c == '\'' && syntax->name == NAME_prolog && here > 0 )
1212 { wint_t c0 = fetch(here-1);
1213
1214 if ( iswdigit(c0) )
1215 { if ( c0 == '0' && idx == here+1 )
1216 succeed;
1217 continue;
1218 }
1219 }
1220
1221 if ( (match = getMatchingQuoteTextBuffer(tb, toInt(here), NAME_forward)))
1222 { DEBUG(NAME_inString, Cprintf("Matching: %ld\n", valInt(match)));
1223
1224 if ( (here = valInt(match)) >= idx )
1225 succeed;
1226 } else
1227 succeed;
1228 }
1229 }
1230
1231 fail;
1232 }
1233
1234 #define MAXBRACKETS 1000 /* ??? */
1235
1236 Int
getMatchingBracketTextBuffer(TextBuffer tb,Int idx,Int bracket)1237 getMatchingBracketTextBuffer(TextBuffer tb, Int idx, Int bracket)
1238 { intptr_t i = valInt(idx);
1239 wint_t stack[MAXBRACKETS];
1240 int depth = 1;
1241 int ic;
1242 SyntaxTable syntax = tb->syntax;
1243 int c;
1244
1245 c = (notDefault(bracket) ? (int)valInt(bracket) : fetch(i));
1246 stack[0] = c;
1247
1248 if ( tisopenbrace(syntax, c) )
1249 ic = 1;
1250 else if ( tisclosebrace(syntax, c) )
1251 ic = -1;
1252 else
1253 fail;
1254
1255 for( i += ic; i >= 0 && i < tb->size; i += ic )
1256 { c = fetch(i);
1257
1258 if ( tisopenbrace(syntax, c) )
1259 { if ( ic > 0 )
1260 stack[depth] = c;
1261 depth += ic;
1262 if ( ic < 0 && !tismatching(syntax, c, stack[depth]) )
1263 { errorPce(tb, NAME_mismatchedBracket);
1264 fail;
1265 }
1266 } else if ( tisclosebrace(syntax, c) )
1267 { if ( ic < 0 )
1268 stack[depth] = c;
1269 depth -= ic;
1270 if ( ic > 0 && !tismatching(syntax, c, stack[depth]) )
1271 { errorPce(tb, NAME_mismatchedBracket);
1272 fail;
1273 }
1274 } else if ( tisquote(syntax, c) )
1275 { Int mb = getMatchingQuoteTextBuffer(tb, toInt(i),
1276 ic > 0 ? NAME_forward
1277 : NAME_backward);
1278 if ( mb )
1279 i = valInt(mb);
1280 else
1281 fail;
1282 }
1283
1284 if ( depth == 0 )
1285 answer(toInt(i));
1286 }
1287
1288 fail;
1289 }
1290
1291
1292 Int
getSkipBlanksTextBuffer(TextBuffer tb,Int where,Name direction,BoolObj skipnl)1293 getSkipBlanksTextBuffer(TextBuffer tb, Int where, Name direction, BoolObj skipnl)
1294 { intptr_t pos = valInt(where);
1295 intptr_t size = tb->size;
1296
1297 pos = NormaliseIndex(tb, pos);
1298
1299 if ( isDefault(skipnl) )
1300 skipnl = ON;
1301 if ( isDefault(direction) )
1302 direction = NAME_forward;
1303
1304 if ( direction == NAME_forward )
1305 { if ( skipnl == OFF )
1306 { while( pos < size && tisblank(tb->syntax, fetch(pos)) )
1307 pos++;
1308 } else
1309 { while( pos < size && tislayout(tb->syntax, fetch(pos)) )
1310 pos++;
1311 }
1312 } else
1313 { if ( skipnl == OFF )
1314 { while( pos > 0 && tisblank(tb->syntax, fetch(pos-1)) )
1315 pos--;
1316 } else
1317 { while( pos > 0 && tislayout(tb->syntax, fetch(pos-1)) )
1318 pos--;
1319 }
1320 }
1321
1322 return toInt(pos);
1323 }
1324
1325
1326 static Int
getSkipCommentTextBuffer(TextBuffer tb,Int where,Int to,BoolObj layouttoo)1327 getSkipCommentTextBuffer(TextBuffer tb, Int where, Int to, BoolObj layouttoo)
1328 { intptr_t pos = valInt(where);
1329 intptr_t end = (isDefault(to) ? tb->size : valInt(to));
1330 int fwd = (end >= pos);
1331
1332 pos = NormaliseIndex(tb, pos);
1333 end = NormaliseIndex(tb, end);
1334
1335 if ( fwd ) /* forward */
1336 { for(;;)
1337 { if ( pos < 0 )
1338 answer(toInt(tb->size));
1339
1340 if ( layouttoo != OFF )
1341 { for( ; pos < end && tislayout(tb->syntax, fetch(pos)); pos++ )
1342 ;
1343 }
1344
1345 if ( tiscommentstart(tb->syntax, fetch(pos)) )
1346 { for( ; pos < end; pos++ )
1347 { int c = fetch(pos);
1348
1349 if ( tiscommentend(tb->syntax, c) )
1350 break;
1351 }
1352 continue;
1353 }
1354
1355 if ( tiscommentstart1(tb->syntax, fetch(pos)) &&
1356 tiscommentstart2(tb->syntax, fetch(pos+1)) )
1357 { for( pos += 4; pos < end; pos++ )
1358 { int c = fetch(pos - 1);
1359
1360 if ( tiscommentend2(tb->syntax, c) )
1361 { c = fetch(pos - 2);
1362 if ( tiscommentend1(tb->syntax, c) )
1363 break;
1364 }
1365 }
1366
1367 continue;
1368 }
1369 break;
1370 }
1371 } else /* backwards */
1372 { for(;;)
1373 { wint_t c;
1374
1375 again:
1376
1377 if ( pos < end )
1378 break;
1379
1380 if ( layouttoo != OFF )
1381 { for(; pos >= end && tislayout(tb->syntax, c=fetch(pos))
1382 && !tiscommentend(tb->syntax, c)
1383 ; pos-- )
1384 ;
1385 }
1386
1387 if ( tiscommentend(tb->syntax, c=fetch(pos)) )
1388 { intptr_t possave = pos;
1389
1390 for( pos--;
1391 pos >= end && !tiscommentstart(tb->syntax, c=fetch(pos));
1392 pos-- )
1393 { if ( tiscommentend(tb->syntax, c) )
1394 { pos = possave;
1395 if ( tislayout(tb->syntax, fetch(pos)) )
1396 { pos--;
1397 goto again;
1398 } else
1399 goto next;
1400 }
1401 }
1402 if ( pos < end )
1403 { pos = possave;
1404 if ( tislayout(tb->syntax, fetch(pos)) )
1405 { pos--;
1406 goto again;
1407 } else
1408 goto next;
1409 }
1410 pos--;
1411 continue;
1412 }
1413
1414 next:
1415 if ( tiscommentend2(tb->syntax, fetch(pos)) &&
1416 tiscommentend1(tb->syntax, fetch(pos-1)) )
1417 { for( pos -= 4;
1418 pos >= end && !(tiscommentstart1(tb->syntax, fetch(pos+1)) &&
1419 tiscommentstart2(tb->syntax, fetch(pos+2)));
1420 pos-- )
1421 ;
1422 continue;
1423 }
1424 break;
1425 }
1426 }
1427
1428 if ( (fwd && pos > end) || (!fwd && pos < end) )
1429 pos = end;
1430
1431 answer(toInt(NormaliseIndex(tb, pos)));
1432 }
1433
1434
1435 static status
inCommentTextBuffer(TextBuffer tb,Int pos,Int from)1436 inCommentTextBuffer(TextBuffer tb, Int pos, Int from)
1437 { int idx = valInt(pos);
1438 int here = (isDefault(from) ? 0 : valInt(from));
1439 SyntaxTable syntax = tb->syntax;
1440
1441 while(here <= idx)
1442 { int c = fetch(here);
1443
1444 if ( tisquote(syntax, c) )
1445 { Int h = getMatchingQuoteTextBuffer(tb, toInt(here), NAME_forward);
1446
1447 if ( !h )
1448 succeed;
1449 here = valInt(h) + 1;
1450 continue;
1451 }
1452
1453 if ( tiscommentstart(syntax, c) ||
1454 (tiscommentstart1(syntax, c) &&
1455 tiscommentstart2(syntax, fetch(here+1))) )
1456 { here = valInt(getSkipCommentTextBuffer(tb, toInt(here), DEFAULT, OFF));
1457 if ( here >= idx )
1458 succeed;
1459 }
1460
1461 here++;
1462 }
1463
1464 fail;
1465 }
1466
1467
1468 static status
forAllCommentsTextBuffer(TextBuffer tb,Code msg,Int from,Int to)1469 forAllCommentsTextBuffer(TextBuffer tb, Code msg, Int from, Int to)
1470 { int here = (isDefault(from) ? 0 : valInt(from));
1471 int end = (isDefault(to) ? tb->size : valInt(to));
1472 SyntaxTable syntax = tb->syntax;
1473
1474 if ( here < 0 ) /* normalise the indices */
1475 here = 0;
1476 if ( end > tb->size )
1477 end = tb->size;
1478
1479 while(here < end)
1480 { int c = fetch(here);
1481
1482 if ( tisquote(syntax, c) )
1483 { Int h = getMatchingQuoteTextBuffer(tb, toInt(here), NAME_forward);
1484
1485 if ( !h )
1486 succeed;
1487 here = valInt(h) + 1;
1488 continue;
1489 }
1490
1491 if ( tiscommentstart(syntax, c) ||
1492 (tiscommentstart1(syntax, c) &&
1493 tiscommentstart2(syntax, fetch(here+1))) )
1494 { int endc = valInt(getSkipCommentTextBuffer(tb, toInt(here),
1495 DEFAULT, OFF));
1496
1497 forwardReceiverCode(msg, tb, toInt(here), toInt(endc), EAV);
1498
1499 here = endc;
1500 }
1501
1502 here++;
1503 }
1504
1505 succeed;
1506 }
1507
1508
1509 Int
getLineNumberTextBuffer(TextBuffer tb,Int i)1510 getLineNumberTextBuffer(TextBuffer tb, Int i)
1511 { int e = (isDefault(i) ? tb->size : valInt(i));
1512
1513 answer(toInt(count_lines_textbuffer(tb, 0, e) + 1));
1514 }
1515
1516
1517 Int
getCountLinesTextBuffer(TextBuffer tb,Int from,Int to)1518 getCountLinesTextBuffer(TextBuffer tb, Int from, Int to)
1519 { intptr_t f = (isDefault(from) ? 0 : valInt(from));
1520 intptr_t t = (isDefault(to) ? tb->size : valInt(to));
1521
1522 answer(toInt(count_lines_textbuffer(tb, f, t)));
1523 }
1524
1525
1526 intptr_t
find_textbuffer(TextBuffer tb,intptr_t here,PceString str,intptr_t times,char az,int ec,int wm)1527 find_textbuffer(TextBuffer tb, intptr_t here, PceString str,
1528 intptr_t times, char az, int ec, int wm)
1529 { int hit = FALSE;
1530 int where = here;
1531
1532 if ( times < 0 )
1533 { for( ; here >= 0 && times < 0; times++ )
1534 { for( ; here >= 0; here-- )
1535 { if ( match_textbuffer(tb, here, str, ec, wm) )
1536 { hit = TRUE;
1537 where = here;
1538 break;
1539 }
1540 }
1541 }
1542 } else if ( times > 0 )
1543 { int size = tb->size;
1544
1545 for( ; here < size && times > 0; times-- )
1546 { for( ; here < size; here++ )
1547 { if ( match_textbuffer(tb, here, str, ec, wm) )
1548 { hit = TRUE;
1549 where = here;
1550 break;
1551 }
1552 }
1553 }
1554 } else
1555 return here;
1556
1557 return hit ? (az == 'a' ? where : where + str->s_size) : -1;
1558 }
1559
1560
1561 int
match_textbuffer(TextBuffer tb,intptr_t here,PceString s,int ec,int wm)1562 match_textbuffer(TextBuffer tb, intptr_t here, PceString s, int ec, int wm)
1563 { intptr_t l = s->s_size;
1564 intptr_t i;
1565
1566 if ( wm && (tisalnum(tb->syntax, fetch(here-1)) ||
1567 tisalnum(tb->syntax, fetch(here+l))) )
1568 return FALSE;
1569
1570 if ( ec )
1571 { for( i=0; i < l; i++ )
1572 { if ( fetch(here++) != (int)str_fetch(s, i) )
1573 return FALSE;
1574 }
1575 } else
1576 { for( i=0; i < l; i++ )
1577 { wint_t c1 = fetch(here++);
1578 wint_t c2 = str_fetch(s, i);
1579
1580 if ( tolower(c1) != tolower(c2) )
1581 return FALSE;
1582 }
1583 }
1584
1585 return TRUE;
1586 }
1587
1588
1589 /********************************
1590 * FILLING *
1591 ********************************/
1592
1593 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1594 Fill a line. The text starts at `from' (possibly holding blanks) the
1595 first non-blank character is at column sc and rm is the right margin.
1596 The return value is the start position of the next line in the
1597 textbuffer. Characters after to are always left untouched.
1598 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1599
1600 #define MAX_WORDS 1000
1601
1602 static void
distribute_spaces(TextBuffer tb,int spaces,int nbreaks,intptr_t * breaks)1603 distribute_spaces(TextBuffer tb, int spaces, int nbreaks, intptr_t *breaks)
1604 { int s = (nbreaks > 1 ? (spaces / (nbreaks-1)) : 1);
1605 int n, m;
1606 int *extra = (int *)alloca(nbreaks * sizeof(int));
1607 PceString space = str_spc(&tb->buffer);
1608
1609 DEBUG(NAME_justify, Cprintf("%d spaces (each %d)\n", spaces, s));
1610
1611 for(n=0; n < nbreaks-1; n++) /* give them equal size */
1612 extra[n] = s;
1613 extra[n] = 0; /* not in last (is the newline) */
1614 /* distribute from the center */
1615 spaces -= s*(nbreaks-1);
1616 for(m = nbreaks / 2, n = 0; spaces > 0; n++, spaces--)
1617 { int i = m + (n%2 == 0 ? n/2 : -(n/2));
1618
1619 if ( i >= nbreaks-1 ) i = nbreaks - 2;
1620 if ( i < 0 ) i = 0;
1621 extra[i]++;
1622 DEBUG(NAME_justify, Cprintf("\tadding one at break %d\n", i));
1623 }
1624
1625 for(n=0, m=0; n<nbreaks; n++)
1626 { breaks[n] += m;
1627 if ( extra[n] )
1628 { insert_textbuffer(tb, breaks[n], extra[n], space);
1629 m += extra[n];
1630 }
1631 }
1632 }
1633
1634
1635 intptr_t
fill_line_textbuffer(TextBuffer tb,intptr_t here,intptr_t to,int sc,int rm,int justify)1636 fill_line_textbuffer(TextBuffer tb, intptr_t here, intptr_t to,
1637 int sc, int rm, int justify)
1638 { int col = sc;
1639 intptr_t breaks[MAX_WORDS];
1640 int nbreaks = 0;
1641 int last_break_col = 0;
1642 intptr_t i;
1643 PceString nl = str_nl(&tb->buffer);
1644 PceString space = str_spc(&tb->buffer);
1645
1646 DEBUG(NAME_fill, Cprintf("fill_line(tb, %ld, %ld, %d, %d)\n",
1647 here, to, sc, rm));
1648
1649 /* delete leading white space */
1650 for( i = here; i < to && tislayout(tb->syntax, fetch(i)); i++ )
1651 ;
1652 if ( i-here > 0 )
1653 { delete_textbuffer(tb, here, i-here);
1654 to -= i-here;
1655 DEBUG(NAME_fill, Cprintf("deleted %ld leading blanks\n", i-here));
1656 }
1657
1658
1659 for(;;)
1660 { /* copy string of non-blanks */
1661 for( ; here < to && !tislayout(tb->syntax, fetch(here)); here++ )
1662 col++;
1663 DEBUG(NAME_fill,
1664 Cprintf("Word to %ld; col = %d; here-1 = %c, here = %d, to=%ld\n",
1665 here, col, fetch(here-1), fetch(here), to));
1666
1667 if ( col > rm ) /* trapped margin */
1668 { if ( nbreaks > 0 )
1669 { store_textbuffer(tb, breaks[nbreaks-1], '\n');
1670 if ( justify && last_break_col < rm )
1671 distribute_spaces(tb, rm - last_break_col, nbreaks, breaks);
1672 return breaks[nbreaks-1] + 1;
1673 } else
1674 { if ( here == to ) /* end of buffer: add a newline */
1675 insert_textbuffer(tb, here, 1, nl);
1676 else
1677 store_textbuffer(tb, here, '\n');
1678 return here+1;
1679 }
1680 }
1681
1682 if ( here >= to )
1683 return here;
1684
1685 breaks[nbreaks] = here;
1686 if ( nbreaks < MAX_WORDS-1 )
1687 nbreaks++; /* avoid crash */
1688 last_break_col = col;
1689 if ( fetch(here) != ' ' )
1690 store_textbuffer(tb, here, ' ');
1691 here++, col++;
1692 if ( ends_sentence(tb, here-2) ) /* sentence: one more space */
1693 { DEBUG(NAME_fill, Cprintf("End-sentence at %ld\n", here-2));
1694 if ( fetch(here) != ' ' )
1695 { insert_textbuffer(tb, here, 1, space);
1696 to++;
1697 }
1698 here++; col++;
1699 }
1700
1701 for( i = here; i < to && tislayout(tb->syntax, fetch(i)); i++ )
1702 ;
1703 if ( i-here > 0 )
1704 { delete_textbuffer(tb, here, i-here);
1705 to -= i-here;
1706 DEBUG(NAME_fill, Cprintf("deleted %ld blanks\n", i-here));
1707 }
1708
1709 if ( here >= to )
1710 return here;
1711 }
1712 }
1713
1714 /********************************
1715 * SORTING *
1716 ********************************/
1717
1718 static int
compare_lines(const void * s,const void * t)1719 compare_lines(const void *s, const void *t)
1720 { return strcmp(*((char **)s), *((char **)t));
1721 }
1722
1723
1724 status
sortTextBuffer(TextBuffer tb,Int from,Int to)1725 sortTextBuffer(TextBuffer tb, Int from, Int to)
1726 { int f, t, ln, i, n;
1727 char *buf, **lines;
1728 char *bp, **lp;
1729 int bufsize;
1730
1731 if ( isDefault(from) )
1732 from = ZERO;
1733 if ( isDefault(to) )
1734 to = toInt(tb->size);
1735
1736 from = getScanTextBuffer(tb, from, NAME_line, ZERO, NAME_start);
1737 to = getScanTextBuffer(tb, to, NAME_line, ZERO, NAME_start);
1738 f = valInt(from);
1739 t = valInt(to);
1740 ln = count_lines_textbuffer(tb, f, t+1); /* <=t below */
1741
1742 if ( ln > 1 ) /* TBD (16B) */
1743 { bufsize = t - f + 1;
1744 lines = alloc((ln+1) * sizeof(char *));
1745 buf = alloc(bufsize);
1746
1747 for(bp=buf, lp=lines, i=f, *lp++=bp; i <= t; i++, bp++)
1748 { *bp = fetch_textbuffer(tb, i);
1749
1750 if ( tisendsline(tb->syntax, *bp) )
1751 { *bp = EOS;
1752 *lp++ = bp+1;
1753 }
1754 }
1755
1756 qsort(lines, ln, sizeof(char *), compare_lines);
1757
1758 delete_textbuffer(tb, f, t-f);
1759 for(n=0; n<ln; n++)
1760 { PceString nl = str_nl(&tb->buffer);
1761 string s;
1762
1763 str_set_ascii(&s, lines[n]);
1764 insert_textbuffer(tb, f, 1, &s);
1765 f += s.s_size;
1766 insert_textbuffer(tb, f, 1, nl);
1767 f++;
1768 }
1769
1770 unalloc((ln+1) * sizeof(char *), lines);
1771 unalloc(bufsize, buf);
1772 }
1773
1774 return changedTextBuffer(tb);
1775 }
1776
1777 /*******************************
1778 * LINE COUNTS *
1779 *******************************/
1780
1781 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1782 Counting lines and finding lines. This is in some applications done
1783 quite often on long buffers and therefore we have written this at a
1784 rather low level.
1785
1786 count_lines_textbuffer() finds the number of newlines in a region.
1787 start_of_line_n_textbuffer() finds the character index of the nth-1 line.
1788 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1789
1790 intptr_t
count_lines_textbuffer(TextBuffer tb,intptr_t f,intptr_t t)1791 count_lines_textbuffer(TextBuffer tb, intptr_t f, intptr_t t)
1792 { intptr_t lines = 0;
1793 SyntaxTable syntax = tb->syntax;
1794
1795 f = NormaliseIndex(tb, f);
1796 t = NormaliseIndex(tb, t);
1797
1798 if ( f == 0 && t == tb->size && tb->lines >= 0 )
1799 return tb->lines; /* use the total count */
1800
1801 if ( istbA(tb) )
1802 { charA *b = tb->tb_bufferA;
1803 int end1 = min(tb->gap_start, t);
1804
1805 for( ; f<end1; f++)
1806 { if ( tisendsline(syntax, b[f]) )
1807 { lines++;
1808 }
1809 }
1810 b += tb->gap_end - tb->gap_start;
1811 for( ; f<t; f++)
1812 { if ( tisendsline(syntax, b[f]) )
1813 { lines++;
1814 }
1815 }
1816 } else
1817 { charW *b = tb->tb_bufferW;
1818 int end1 = min(tb->gap_start, t);
1819
1820 for( ; f<end1; f++)
1821 { if ( tisendsline(syntax, b[f]) )
1822 lines++;
1823 }
1824 b += tb->gap_end - tb->gap_start;
1825 for( ; f<t; f++)
1826 { if ( tisendsline(syntax, b[f]) )
1827 lines++;
1828 }
1829 }
1830
1831 return lines;
1832 }
1833
1834
1835 int
start_of_line_n_textbuffer(TextBuffer tb,int lineno)1836 start_of_line_n_textbuffer(TextBuffer tb, int lineno)
1837 { int i;
1838 SyntaxTable syntax = tb->syntax;
1839
1840 if ( --lineno <= 0 )
1841 return 0;
1842
1843 if ( istbA(tb) )
1844 { charA *b = tb->tb_bufferA;
1845
1846 for(i=0 ; i<tb->gap_start; i++)
1847 { if ( tisendsline(syntax, b[i]) )
1848 { if ( --lineno <= 0 )
1849 return i+1;
1850 }
1851 }
1852 b += tb->gap_end - tb->gap_start;
1853 for( ; i<tb->size; i++)
1854 { if ( tisendsline(syntax, b[i]) )
1855 { if ( --lineno <= 0 )
1856 return i+1;
1857 }
1858 }
1859 } else
1860 { charW *b = tb->tb_bufferW;
1861
1862 for(i=0 ; i<tb->gap_start; i++)
1863 { if ( tisendsline(syntax, b[i]) )
1864 { if ( --lineno <= 0 )
1865 return i+1;
1866 }
1867 }
1868 b += tb->gap_end - tb->gap_start;
1869 for( ; i<tb->size; i++)
1870 { if ( tisendsline(syntax, b[i]) )
1871 { if ( --lineno <= 0 )
1872 return i+1;
1873 }
1874 }
1875 }
1876
1877 return tb->size;
1878 }
1879
1880 /********************************
1881 * PRIMITIVE OPERATIONS *
1882 *********************************/
1883
1884 int
fetch_textbuffer(TextBuffer tb,intptr_t where)1885 fetch_textbuffer(TextBuffer tb, intptr_t where)
1886 { int idx;
1887
1888 if ( where < 0 || where >= tb->size )
1889 return EOB;
1890 idx = Index(tb, where);
1891
1892 return istbA(tb) ? (wint_t)tb->tb_bufferA[idx] : (wint_t)tb->tb_bufferW[idx];
1893 }
1894
1895
1896 static status
store_textbuffer(TextBuffer tb,intptr_t where,wint_t c)1897 store_textbuffer(TextBuffer tb, intptr_t where, wint_t c)
1898 { intptr_t idx;
1899 wint_t old;
1900
1901 if ( where < 0 || where >= tb->size )
1902 fail;
1903 idx = Index(tb, where);
1904
1905 if ( istbA(tb) && c > 0xff )
1906 promoteTextBuffer(tb);
1907
1908 if ( istbA(tb) )
1909 old = tb->tb_bufferA[idx];
1910 else
1911 old = tb->tb_bufferW[idx];
1912
1913 if ( old == c )
1914 succeed;
1915 if ( tisendsline(tb->syntax, old) )
1916 tb->lines--;
1917 if ( tisendsline(tb->syntax, c) )
1918 tb->lines++;
1919
1920 start_change(tb, where);
1921 register_change_textbuffer(tb, where, 1);
1922
1923 if ( istbA(tb) )
1924 tb->tb_bufferA[idx] = c;
1925 else
1926 tb->tb_bufferW[idx] = c;
1927
1928 end_change(tb, where+1);
1929 CmodifiedTextBuffer(tb, ON);
1930
1931 succeed;
1932 }
1933
1934
1935 status
change_textbuffer(TextBuffer tb,intptr_t where,PceString s)1936 change_textbuffer(TextBuffer tb, intptr_t where, PceString s)
1937 { intptr_t w, n;
1938
1939 if ( s->s_size < 0 || where < 0 || where+s->s_size > tb->size )
1940 fail;
1941
1942 if ( istbA(tb) && str_iswide(s) )
1943 promoteTextBuffer(tb);
1944
1945 register_change_textbuffer(tb, where, s->s_size);
1946
1947 if ( istbA(tb) )
1948 { for( w=where, n=0; n < s->s_size; n++, w++ )
1949 { intptr_t i = Index(tb, w);
1950 wint_t new = str_fetch(s, n);
1951
1952 if ( tb->tb_bufferA[i] != new )
1953 { if ( tisendsline(tb->syntax, tb->tb_bufferA[i]) )
1954 tb->lines--;
1955 if ( tisendsline(tb->syntax, new) )
1956 tb->lines++;
1957 tb->tb_bufferA[i] = new;
1958 }
1959 }
1960 } else
1961 { for( w=where, n=0; n < s->s_size; n++, w++ )
1962 { intptr_t i = Index(tb, w);
1963 charW new = str_fetch(s, n);
1964
1965 if ( tb->tb_bufferW[i] != new )
1966 { if ( tisendsline(tb->syntax, tb->tb_bufferW[i]) )
1967 tb->lines--;
1968 if ( tisendsline(tb->syntax, new) )
1969 tb->lines++;
1970 tb->tb_bufferW[i] = new;
1971 }
1972 }
1973 }
1974
1975 start_change(tb, where);
1976 end_change(tb, where+s->s_size);
1977 CmodifiedTextBuffer(tb, ON);
1978
1979 succeed;
1980 }
1981
1982
1983 static void
mirror_textbuffer(TextBuffer tb,int f,int t)1984 mirror_textbuffer(TextBuffer tb, int f, int t)
1985 { if ( istbA(tb) )
1986 { for( ; f < t; f++, t-- )
1987 Swap(tb->tb_bufferA[f], tb->tb_bufferA[t])
1988 } else
1989 { for( ; f < t; f++, t-- )
1990 Swap(tb->tb_bufferW[f], tb->tb_bufferW[t])
1991 }
1992 }
1993
1994
1995 static status
transpose_textbuffer(TextBuffer tb,intptr_t f1,intptr_t t1,intptr_t f2,intptr_t t2)1996 transpose_textbuffer(TextBuffer tb, intptr_t f1, intptr_t t1, intptr_t f2, intptr_t t2)
1997 { Before_i(f1, t1);
1998 Before_i(f2, t2);
1999
2000 f1 = NormaliseIndex(tb, f1);
2001 t1 = NormaliseIndex(tb, t1);
2002 f2 = NormaliseIndex(tb, f2);
2003 t2 = NormaliseIndex(tb, t2);
2004
2005 if ( f1 > f2 )
2006 { Swap(f1, f2);
2007 Swap(t1, t2);
2008 }
2009 if ( t1 > f2 )
2010 fail;
2011
2012 register_change_textbuffer(tb, f1, t2-f1);
2013
2014 room(tb, t2, 0); /* move gap out of the way */
2015 t1--; t2--;
2016 mirror_textbuffer(tb, f1, t2);
2017 mirror_textbuffer(tb, f1, f1+t2-f2);
2018 mirror_textbuffer(tb, t2+f1-t1, t2);
2019 mirror_textbuffer(tb, f1+t2-f2+1, t2+f1-t1-1);
2020
2021 start_change(tb, f1);
2022 end_change(tb, t2+1);
2023 CmodifiedTextBuffer(tb, ON);
2024
2025 succeed;
2026 }
2027
2028
2029 static status
downcase_textbuffer(TextBuffer tb,intptr_t from,intptr_t len)2030 downcase_textbuffer(TextBuffer tb, intptr_t from, intptr_t len)
2031 { for( ; from < tb->size && len > 0; len--, from++ )
2032 { wint_t c;
2033
2034 if ( iswupper((c=fetch(from))) )
2035 store_textbuffer(tb, from, towlower(c));
2036 }
2037
2038 succeed;
2039 }
2040
2041
2042 static status
upcase_textbuffer(TextBuffer tb,intptr_t from,intptr_t len)2043 upcase_textbuffer(TextBuffer tb, intptr_t from, intptr_t len)
2044 { for( ; from < tb->size && len > 0; len--, from++ )
2045 { wint_t c;
2046
2047 if ( iswlower((c=fetch(from))) )
2048 store_textbuffer(tb, from, towupper(c));
2049 }
2050
2051 succeed;
2052 }
2053
2054
2055 static status
capitalise_textbuffer(TextBuffer tb,intptr_t from,intptr_t len)2056 capitalise_textbuffer(TextBuffer tb, intptr_t from, intptr_t len)
2057 { wint_t b = ' ';
2058
2059 for( ; from < tb->size && len > 0; len--, from++ )
2060 { wint_t c = fetch(from);
2061 wint_t c2;
2062
2063 if ( !iswalnum(b) )
2064 c2 = towupper(c);
2065 else
2066 c2 = towlower(c);
2067
2068 if ( c2 != c )
2069 store_textbuffer(tb, from, c2);
2070
2071 b = c;
2072 }
2073
2074 succeed;
2075 }
2076
2077
2078 static Any
streamError(IOSTREAM * fd)2079 streamError(IOSTREAM *fd)
2080 { if ( fd->message )
2081 return cToPceStringA(NIL, fd->message, strlen(fd->message), FALSE);
2082
2083 return getOsErrorPce(PCE);
2084 }
2085
2086
2087 static status
save_textbuffer(TextBuffer tb,intptr_t from,intptr_t len,SourceSink file)2088 save_textbuffer(TextBuffer tb, intptr_t from, intptr_t len, SourceSink file)
2089 { IOSTREAM *fd;
2090
2091 room(tb, tb->size, 0); /* move the gap to the end */
2092
2093 if ( !(fd = Sopen_object(file, "wr")) )
2094 return errorPce(file, NAME_openFile, NAME_write, getOsErrorPce(PCE));
2095
2096 from = NormaliseIndex(tb, from);
2097 if ( (from + len) > tb->size )
2098 len = tb->size - from;
2099
2100 if ( istbA(tb) )
2101 { const charA *f = &tb->tb_bufferA[from];
2102 const charA *e = &f[len];
2103
2104 for( ; f<e; f++)
2105 { if ( Sputcode(*f, fd) < 0 )
2106 { Any msg;
2107 error:
2108 msg = streamError(fd);
2109 Sclose(fd);
2110 return errorPce(file, NAME_ioError, msg);
2111 }
2112 }
2113 } else
2114 { const charW *f = &tb->tb_bufferW[from];
2115 const charW *e = &f[len];
2116
2117 for( ; f<e; f++)
2118 { if ( Sputcode(*f, fd) < 0 )
2119 goto error;
2120 }
2121 }
2122
2123 if ( Sclose(fd) < 0 )
2124 return errorPce(file, NAME_ioError, getOsErrorPce(PCE));
2125
2126 succeed;
2127 }
2128
2129
2130 status
str_sub_text_buffer(TextBuffer tb,PceString s,intptr_t start,intptr_t len)2131 str_sub_text_buffer(TextBuffer tb, PceString s, intptr_t start, intptr_t len)
2132 { int idx;
2133
2134 if ( start < 0 )
2135 start = 0;
2136 else if ( start > tb->size )
2137 start = tb->size-1;
2138
2139 if ( len < 0 )
2140 len = 0;
2141 else if ( start + len > tb->size )
2142 len = tb->size - start;
2143
2144 if ( start < tb->gap_start && start+len > tb->gap_start )
2145 room(tb, start + len, 1);
2146
2147 str_cphdr(s, &tb->buffer);
2148 s->s_size = len;
2149
2150 if ( start < tb->gap_start )
2151 idx = start;
2152 else
2153 idx = tb->gap_end + (start - tb->gap_start);
2154
2155 if ( isstrA(s) )
2156 s->s_textA = &tb->tb_bufferA[idx];
2157 else
2158 s->s_textW = &tb->tb_bufferW[idx];
2159
2160 succeed;
2161 }
2162
2163
2164 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2165 The promoteTextBuffer() function transforms an 8-bit textbuffer into a
2166 wide character one. Otherwise nothing is changed.
2167 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2168
2169 static status
promoteTextBuffer(TextBuffer tb)2170 promoteTextBuffer(TextBuffer tb)
2171 { if ( istbA(tb) )
2172 { charW *w = pceMalloc(tb->allocated * sizeof(charW));
2173 const charA *f = Address(tb, 0);
2174 const charA *e = &f[tb->allocated];
2175 charW *t = w;
2176
2177 while(f<e)
2178 *t++ = *f++;
2179
2180 pceFree(tb->tb_bufferA);
2181 tb->tb_bufferW = w;
2182 tb->buffer.s_iswide = TRUE;
2183 }
2184
2185 succeed;
2186 }
2187
2188
2189 static status
fits_iso_latin_1(const charW * s,size_t len)2190 fits_iso_latin_1(const charW *s, size_t len)
2191 { const charW *e = &s[len];
2192
2193 for( ;s<e; s++)
2194 { if ( *s > 0xff )
2195 succeed;
2196 }
2197
2198 fail;
2199 }
2200
2201
2202 static status
demoteTextBuffer(TextBuffer tb)2203 demoteTextBuffer(TextBuffer tb)
2204 { if ( !istbA(tb) )
2205 { if ( fits_iso_latin_1(tb->tb_bufferW, tb->gap_start) &&
2206 fits_iso_latin_1(tb->tb_bufferW+tb->gap_end,
2207 tb->allocated - tb->gap_end) )
2208 { charA *s = pceMalloc(tb->allocated * sizeof(charA));
2209 const charW *f = tb->tb_bufferW;
2210 const charW *e = &f[tb->allocated];
2211 charA *t = s;
2212
2213 while(f<e)
2214 *t++ = *f++;
2215
2216 pceFree(tb->tb_bufferW);
2217 tb->tb_bufferA = s;
2218 tb->buffer.s_iswide = FALSE;
2219 } else
2220 { fail;
2221 }
2222 }
2223
2224 succeed;
2225 }
2226
2227
2228 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2229 insert_file_textbuffer()
2230
2231 Insert the contents of file `file' into the text buffer at position
2232 `where' `times' times. Returns SUCCEED if everything was ok, FAIL
2233 otherwise.
2234
2235 The most common case for this function is to read an entire file simply
2236 once into the buffer. The simplest approach is to read the file into a
2237 string and use the string insertion function below, but the price is a
2238 potential duplication of memory usage.
2239 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2240
2241
2242 static int
insert_file_textbuffer(TextBuffer tb,intptr_t where,intptr_t times,SourceSink file)2243 insert_file_textbuffer(TextBuffer tb, intptr_t where, intptr_t times, SourceSink file)
2244 { intptr_t grow, here;
2245 int64_t size;
2246 IOSTREAM *fd;
2247
2248 if ( times <= 0 )
2249 succeed;
2250
2251 if ( !(fd = Sopen_object(file, "rr")) )
2252 fail;
2253 size = Ssize(fd); /* size in bytes */
2254
2255 room(tb, where, size); /* always enough */
2256 where = tb->gap_start; /* normalised */
2257 start_change(tb, tb->gap_start);
2258
2259 if ( istbA(tb) )
2260 { for(;;)
2261 { int c = Sgetcode(fd);
2262
2263 if ( c == EOF )
2264 goto done;
2265 if ( c > 0xff )
2266 { promoteTextBuffer(tb);
2267 tb->tb_bufferW[tb->gap_start++] = c;
2268 tb->size++;
2269 break;
2270 }
2271 tb->tb_bufferA[tb->gap_start++] = c;
2272 tb->size++;
2273 }
2274 }
2275 /* promoted or already the case */
2276 if ( !istbA(tb) && !Sfeof(fd) )
2277 { for(;;)
2278 { int c = Sgetcode(fd);
2279
2280 if ( c == EOF )
2281 goto done;
2282 tb->tb_bufferW[tb->gap_start++] = c;
2283 tb->size++;
2284 }
2285 }
2286
2287 done:
2288 if ( Sferror(fd) )
2289 { tb->gap_start = where; /* forget about it */
2290 Sclose(fd);
2291
2292 return errorPce(file, NAME_ioError, getOsErrorPce(PCE));
2293 }
2294
2295 if ( instanceOfObject(file, ClassFile) )
2296 { FileObj f = (FileObj)file;
2297
2298 switch(fd->newline)
2299 { case SIO_NL_POSIX:
2300 case SIO_NL_DETECT:
2301 assign(f, newline_mode, NAME_posix);
2302 break;
2303 case SIO_NL_DOS:
2304 assign(f, newline_mode, NAME_dos);
2305 break;
2306 }
2307 }
2308
2309 Sclose(fd);
2310 size = tb->gap_start - where;
2311 grow = times*size;
2312 register_insert_textbuffer(tb, where, grow);
2313
2314 times--;
2315 room(tb, tb->gap_start, times*size); /* enough for the copies */
2316 while(times-- > 0)
2317 { memmove(Address(tb, tb->gap_start),
2318 Address(tb, where),
2319 istbA(tb) ? size : size*sizeof(charW));
2320 tb->gap_start += size;
2321 tb->size += size;
2322 }
2323 end_change(tb, tb->gap_start);
2324
2325 /* update <-lines */
2326 for(here=where; here<where+grow; here++)
2327 { if ( tisendsline(tb->syntax, fetch(here)) )
2328 tb->lines++;
2329 }
2330
2331 shift_fragments(tb, where, grow);
2332 CmodifiedTextBuffer(tb, ON);
2333
2334 succeed;
2335 }
2336
2337
2338 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2339 Insert a string into a textbuffer. If the textbuffer is ISO Latin-1 and
2340 the string is wide, the textbuffer is promoted.
2341 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2342
2343 static status
insert_textbuffer_shift(TextBuffer tb,intptr_t where,intptr_t times,PceString s,int shift)2344 insert_textbuffer_shift(TextBuffer tb, intptr_t where, intptr_t times,
2345 PceString s, int shift)
2346 { intptr_t grow;
2347 intptr_t here;
2348
2349 if ( s->s_size == 0 )
2350 succeed;
2351
2352 if ( istbA(tb) && str_iswide(s) )
2353 promoteTextBuffer(tb);
2354
2355 grow = times * s->s_size;
2356 where = NormaliseIndex(tb, where);
2357 room(tb, where, grow);
2358
2359 register_insert_textbuffer(tb, where, grow);
2360 start_change(tb, tb->gap_start);
2361 while(times-- > 0)
2362 { if ( tb->buffer.s_iswide == s->s_iswide )
2363 { memmove(Address(tb, tb->gap_start), s->s_text, str_datasize(s));
2364 } else if ( isstrA(s) ) /* insert A in W */
2365 { charW *d = &tb->buffer.s_textW[tb->gap_start];
2366 const charA *f = s->s_textA;
2367 const charA *e = &f[s->s_size];
2368
2369 while(f<e)
2370 *d++ = *f++;
2371 } else /* insert W in A */
2372 { charA *d = &tb->buffer.s_textA[tb->gap_start];
2373 const charW *f = s->s_textW;
2374 const charW *e = &f[s->s_size];
2375
2376 while(f<e)
2377 *d++ = *f++;
2378 }
2379 tb->gap_start += s->s_size;
2380 tb->size += s->s_size;
2381 }
2382 end_change(tb, tb->gap_start);
2383
2384 for(here=where; here<where+grow; here++)
2385 { if ( tisendsline(tb->syntax, fetch(here)) )
2386 tb->lines++;
2387 }
2388
2389 if ( shift )
2390 shift_fragments(tb, where, grow);
2391
2392 CmodifiedTextBuffer(tb, ON);
2393
2394 succeed;
2395 }
2396
2397
2398 status
insert_textbuffer(TextBuffer tb,intptr_t where,intptr_t times,PceString s)2399 insert_textbuffer(TextBuffer tb, intptr_t where, intptr_t times, PceString s)
2400 { return insert_textbuffer_shift(tb, where, times, s, TRUE);
2401 }
2402
2403
2404 static status
clear_textbuffer(TextBuffer tb)2405 clear_textbuffer(TextBuffer tb)
2406 { register_delete_textbuffer(tb, 0, tb->size);
2407
2408 if ( tb->tb_bufferA != NULL )
2409 pceFree(tb->tb_bufferA);
2410
2411 start_change(tb, 0);
2412 end_change(tb, tb->size);
2413
2414 tb->size = 0;
2415 tb->lines = 0;
2416 tb->allocated = ALLOC;
2417 tb->tb_bufferA = pceMalloc(istbA(tb) ? ALLOC : ALLOC*sizeof(charW));
2418
2419 tb->gap_start = 0;
2420 tb->gap_end = tb->allocated;
2421
2422 while( notNil(tb->first_fragment) ) /* destroy fragments */
2423 freeObject(tb->first_fragment);
2424 CmodifiedTextBuffer(tb, ON);
2425
2426 succeed;
2427 }
2428
2429
2430 status
delete_textbuffer(TextBuffer tb,intptr_t where,intptr_t length)2431 delete_textbuffer(TextBuffer tb, intptr_t where, intptr_t length)
2432 { if ( length < 0 ) /* delete backwards */
2433 { if ( where + length < 0 ) /* passed start: normalise */
2434 length = -where;
2435
2436 where += length;
2437 length = -length;
2438 }
2439
2440 if ( where > tb->size )
2441 { intptr_t s = where-tb->size;
2442 where -= s;
2443 length -= s;
2444 if ( length <= 0 )
2445 succeed;
2446 }
2447
2448 if ( where + length > tb->size ) /* normalise on end */
2449 length = tb->size - where;
2450
2451 if ( length <= 0 ) /* out of bounds: ignore */
2452 succeed;
2453
2454 room(tb, where, 0); /* move the gap here */
2455 register_delete_textbuffer(tb, where, length);
2456
2457 start_change(tb, where);
2458 tb->gap_end += length;
2459 tb->size -= length;
2460 end_change(tb, tb->size);
2461
2462 shift_fragments(tb, where, -length);
2463 CmodifiedTextBuffer(tb, ON);
2464
2465 succeed;
2466 }
2467
2468 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2469 Shift the fragments after an insert/delete. Insert is easy as this occurs
2470 either before, inside or after the fragment. Delete is more complicated.
2471 The cases are:
2472
2473 Text: +++++++++++++++++++++++++++++++++++++++++++++++++++++
2474 Frag: -------
2475 1) 11111
2476 2) 2222
2477 3) 333
2478 4) 44444444444
2479 5) 5555555
2480 6) 6666666
2481 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2482
2483 static status
shift_fragments(TextBuffer tb,intptr_t from,intptr_t shift)2484 shift_fragments(TextBuffer tb, intptr_t from, intptr_t shift)
2485 { Fragment f;
2486 Cell cell;
2487
2488 DEBUG(NAME_shift, Cprintf("Start shift: from = %ld, shift = %ld\n",
2489 from, shift));
2490
2491 if ( shift > 0 ) /* insert */
2492 { for(f=tb->first_fragment; notNil(f); f = f->next)
2493 { if ( from < f->start ||
2494 (from == f->start && !(f->attributes & FRAG_INCLUDES_START)) )
2495 f->start += shift;
2496 else if ( from < f->start + f->length ||
2497 (from == f->start + f->length &&
2498 (f->attributes & FRAG_INCLUDES_END)) )
2499 f->length += shift;
2500 }
2501 } else /* delete */
2502 { intptr_t to = from - shift;
2503 Fragment next;
2504
2505 for(f=tb->first_fragment; notNil(f); f = next)
2506 { intptr_t oldlen = f->length;
2507
2508 next = f->next;
2509 DEBUG(NAME_shift, Cprintf("%s: start = %ld, length = %ld --> ",
2510 pp(f), f->start, f->length));
2511 if ( to < f->start ) /* 1 */
2512 f->start += shift;
2513 else
2514 { if ( from > f->start ) /* 3,5,6 */
2515 { if ( from < f->start + f->length ) /* 3,5 */
2516 { if ( to < f->start + f->length ) /* 3 */
2517 f->length += shift;
2518 else /* 5 */
2519 f->length += (to - (f->start + f->length)) + shift;
2520 }
2521 /* 6 */
2522 } else /* 2,4 */
2523 { if ( to < f->start + f->length ) /* 2 */
2524 { intptr_t reduce = to - f->start;
2525
2526 f->length -= reduce;
2527 f->start -= -shift - reduce;
2528 } else /* 4 */
2529 { f->length = 0;
2530 f->start = from;
2531 }
2532 }
2533 }
2534
2535 DEBUG(NAME_shift, Cprintf("start = %ld, length = %ld\n",
2536 f->start, f->length));
2537
2538 if ( f->length == 0 && oldlen != 0 )
2539 { DEBUG(NAME_shift, Cprintf("Invoking %s->emptied\n", pp(f)));
2540 send(f, NAME_emptied, EAV);
2541 }
2542 }
2543 }
2544
2545 for_cell(cell, tb->editors)
2546 send(cell->value, NAME_InsertEditor, toInt(from), toInt(shift), EAV);
2547
2548 succeed;
2549 }
2550
2551
2552 static void
start_change(TextBuffer tb,intptr_t where)2553 start_change(TextBuffer tb, intptr_t where)
2554 { if ( tb->changed_start > where )
2555 tb->changed_start = where;
2556 }
2557
2558
2559 static void
end_change(TextBuffer tb,intptr_t where)2560 end_change(TextBuffer tb, intptr_t where)
2561 { if ( tb->changed_end < where )
2562 tb->changed_end = where;
2563 }
2564
2565
2566 /* Ensures the gap starts at `where' and is at least `grow' bytes long.
2567
2568 ** Tue Apr 4 17:23:28 1989 jan@swivax.UUCP (Jan Wielemaker) */
2569
2570 static int
room(TextBuffer tb,intptr_t where,intptr_t grow)2571 room(TextBuffer tb, intptr_t where, intptr_t grow)
2572 { ssize_t shift;
2573
2574 if ( grow + tb->size > tb->allocated )
2575 { size_t s = ROUND(tb->size + grow, ALLOC);
2576 size_t ag = tb->allocated - tb->gap_end;
2577
2578 shift = s - tb->allocated;
2579 tb->tb_bufferA = pceRealloc(tb->tb_bufferA,
2580 istbA(tb) ? s : s*sizeof(charW));
2581 tb->allocated = s;
2582
2583 memmove(Address(tb, tb->gap_end + shift),
2584 Address(tb, tb->gap_end),
2585 istbA(tb) ? ag : ag*sizeof(charW));
2586 tb->gap_end += shift;
2587 }
2588
2589 shift = where - tb->gap_start;
2590 if ( shift < 0 ) /* move gap towards start */
2591 { size_t move = (size_t)-shift;
2592
2593 memmove(Address(tb, tb->gap_end + shift),
2594 Address(tb, where),
2595 istbA(tb) ? move : sizeof(charW) * move);
2596 } else if ( shift > 0 ) /* move gap towards end */
2597 { size_t move = (size_t)shift;
2598
2599 memmove(Address(tb, tb->gap_start),
2600 Address(tb, tb->gap_end),
2601 istbA(tb) ? move : sizeof(charW) * move);
2602 }
2603 tb->gap_start += shift; /* move the gap pointers */
2604 tb->gap_end += shift;
2605
2606 succeed;
2607 }
2608
2609
2610 /*******************************
2611 * ASFILE INTERFACE *
2612 *******************************/
2613
2614 static status
writeAsFileTextBuffer(TextBuffer tb,Int where,CharArray txt)2615 writeAsFileTextBuffer(TextBuffer tb, Int where, CharArray txt)
2616 { if ( isDefault(where) )
2617 where = toInt(tb->size);
2618
2619 return insertTextBuffer(tb, where, txt, ONE);
2620 }
2621
2622
2623 static status
truncateAsFileTextBuffer(TextBuffer tb)2624 truncateAsFileTextBuffer(TextBuffer tb)
2625 { return clearTextBuffer(tb);
2626 }
2627
2628
2629 static Int
getSizeAsFileTextBuffer(TextBuffer tb)2630 getSizeAsFileTextBuffer(TextBuffer tb)
2631 { answer(toInt(tb->size));
2632 }
2633
2634
2635 static StringObj
getReadAsFileTextBuffer(TextBuffer tb,Int from,Int size)2636 getReadAsFileTextBuffer(TextBuffer tb, Int from, Int size)
2637 { return getContentsTextBuffer(tb, from, size);
2638 }
2639
2640
2641 /*******************************
2642 * CLASS DECLARATION *
2643 *******************************/
2644
2645 /* Type declarations */
2646
2647 static char *T_writeAsFile[] =
2648 { "at=[int]", "text=char_array" };
2649 static char *T_character[] =
2650 { "at=int", "character=char" };
2651 static char *T_delete[] =
2652 { "at=int", "characters=[int]" };
2653 static char *T_insertFile[] =
2654 { "at=int", "data=source_sink", "times=[int]" };
2655 static char *T_insert[] =
2656 { "at=int", "text=char_array", "times=[int]" };
2657 static char *T_format[] =
2658 { "format=char_array", "argument=any ..." };
2659 static char *T_transpose[] =
2660 { "from1=int", "to1=int", "from2=int", "to2=int" };
2661 static char *T_contents[] =
2662 { "from=[int]", "size=[int]" };
2663 static char *T_fromADintD_toADintD[] =
2664 { "from=[int]", "to=[int]" };
2665 static char *T_matchingBracket[] =
2666 { "from=int", "bracket=[char]" };
2667 static char *T_matchingQuote[] =
2668 { "from=int", "direction={forward,backward}" };
2669 static char *T_find[] =
2670 { "from=int", "for=string", "times=[int]",
2671 "return=[{start,end}]", "exact_case=[bool]", "word=[bool]" };
2672 static char *T_fromAint_sizeAint[] =
2673 { "from=int", "size=int" };
2674 static char *T_skipComment[] =
2675 { "from=int", "to=[int]", "skip_layout=[bool]" };
2676 static char *T_skipLayout[] =
2677 { "from=int",
2678 "direction=[{forward,backward}]", "skip_newline=[bool]" };
2679 static char *T_scan[] =
2680 { "from=int",
2681 "unit={character,word,line,sentence,paragraph,term}",
2682 "times=[int]", "return=[{start,end}]" };
2683 static char *T_save[] =
2684 { "in=file", "from=[int]", "size=[int]" };
2685 static char *T_forAllComments[] =
2686 { "message=code", "from=[int]", "to=[int]" };
2687 static char *T_indexAint_startADintD[] =
2688 { "index=int", "start=[int]" };
2689 static char *T_append[] =
2690 { "text=char_array", "times=[int]" };
2691
2692 /* Instance Variables */
2693
2694 static vardecl var_textBuffer[] =
2695 { IV(NAME_firstFragment, "fragment*", IV_GET,
2696 NAME_fragment, "First fragment (lowest start index)"),
2697 IV(NAME_lastFragment, "fragment*", IV_GET,
2698 NAME_fragment, "Last fragment (highest start index)"),
2699 IV(NAME_editors, "chain", IV_GET,
2700 NAME_organisation, "Editors displaying this buffer"),
2701 SV(NAME_modified, "bool", IV_GET|IV_STORE, modifiedTextBuffer,
2702 NAME_modified, "Has buffer been modified"),
2703 SV(NAME_undoBufferSize, "bytes=int", IV_GET|IV_STORE, undoBufferSizeTextBuffer,
2704 NAME_modified, "Size of the undo-buffer in characters"),
2705 IV(NAME_syntax, "syntax_table", IV_BOTH,
2706 NAME_language, "Description of the used syntax"),
2707 IV(NAME_indentTabs, "bool", IV_BOTH,
2708 NAME_indentation, "Re-indent using tabs (true) or spaces (false)"),
2709 IV(NAME_generation, "0..", IV_GET,
2710 NAME_modified, "Indicate change-generation"),
2711 IV(NAME_changedStart, "alien:int", IV_NONE,
2712 NAME_repaint, "Start of changes since last repaint"),
2713 IV(NAME_changedEnd, "alien:int", IV_NONE,
2714 NAME_repaint, "End of changes since last repaint"),
2715 IV(NAME_gapStart, "alien:int", IV_NONE,
2716 NAME_storage, "Start of gap in buffer"),
2717 IV(NAME_gapEnd, "alien:int", IV_NONE,
2718 NAME_storage, "End of gap in buffer"),
2719 IV(NAME_size, "alien:int", IV_NONE,
2720 NAME_cardinality, "Number of valid characters in buffer"),
2721 IV(NAME_lines, "alien:int", IV_NONE,
2722 NAME_cardinality, "Number of newlines in the buffer"),
2723 IV(NAME_allocated, "alien:int", IV_NONE,
2724 NAME_storage, "Total size of buffer"),
2725 IV(NAME_undoBuffer, "alien:UndoBuffer", IV_NONE,
2726 NAME_storage, "Record undo information here"),
2727 IV(NAME_stringHeader, "alien:str_h", IV_NONE,
2728 NAME_storage, "Encoding description"),
2729 IV(NAME_buffer, "alien:char *", IV_NONE,
2730 NAME_storage, "Actual storage bin")
2731 };
2732
2733 /* Send Methods */
2734
2735 static senddecl send_textBuffer[] =
2736 { SM(NAME_initialise, 1, "contents=[char_array]", initialiseTextBuffer,
2737 DEFAULT, "Create from initial contents"),
2738 SM(NAME_unlink, 0, NULL, unlinkTextBuffer,
2739 DEFAULT, "Destroy the text"),
2740 SM(NAME_capitalise, 2, T_fromAint_sizeAint, capitaliseTextBuffer,
2741 NAME_case, "Capitalise (start, length)"),
2742 SM(NAME_downcase, 2, T_fromAint_sizeAint, downcaseTextBuffer,
2743 NAME_case, "Bring (start, length) to lowercase"),
2744 SM(NAME_upcase, 2, T_fromAint_sizeAint, upcaseTextBuffer,
2745 NAME_case, "Bring (start, length) to uppercase"),
2746 SM(NAME_append, 2, T_append, appendTextBuffer,
2747 NAME_edit, "Append string (n-times)"),
2748 SM(NAME_character, 2, T_character, characterTextBuffer,
2749 NAME_edit, "Change character at index to ASCII value"),
2750 SM(NAME_clear, 0, NULL, clearTextBuffer,
2751 NAME_edit, "Delete all contents (and fragments)"),
2752 SM(NAME_contents, 1, "char_array", contentsTextBuffer,
2753 NAME_edit, "Set the contents (deletes fragments)"),
2754 SM(NAME_delete, 2, T_delete, deleteTextBuffer,
2755 NAME_edit, "Delete characters from index"),
2756 SM(NAME_insert, 3, T_insert, insertTextBuffer,
2757 NAME_edit, "Insert string at index (n-times)"),
2758 SM(NAME_transpose, 4, T_transpose, transposeTextBuffer,
2759 NAME_edit, "Transpose [from1, to1) with [from2, to2)"),
2760 SM(NAME_insertFile, 3, T_insertFile, insertFileTextBuffer,
2761 NAME_file, "Insert file at index (n-times)"),
2762 SM(NAME_save, 3, T_save, saveTextBuffer,
2763 NAME_file, "Save (from, length) to file"),
2764 SM(NAME_format, 2, T_format, formatTextBuffer,
2765 NAME_format, "Append formatted text"),
2766 SM(NAME_forAllFragments, 1, "code", forAllFragmentsTextBuffer,
2767 NAME_iterate, "Iterate code over all fragments"),
2768 SM(NAME_inComment, 2, T_indexAint_startADintD, inCommentTextBuffer,
2769 NAME_language, "Test if first index is in comment"),
2770 SM(NAME_forAllComments, 3, T_forAllComments, forAllCommentsTextBuffer,
2771 NAME_iterate, "Iterate code over all comments"),
2772 SM(NAME_inString, 2, T_indexAint_startADintD, inStringTextBuffer,
2773 NAME_language, "Test if first index is in string constant"),
2774 SM(NAME_checkPointUndo, 0, NULL, checkpointUndoTextBuffer,
2775 NAME_modified, "Set `no-change' checkpoint in undo buffer"),
2776 SM(NAME_markUndo, 0, NULL, markUndoTextBuffer,
2777 NAME_modified, "Mark undo point"),
2778 SM(NAME_resetUndo, 0, NULL, resetUndoTextBuffer,
2779 NAME_modified, "Clear the undo-buffer"),
2780 SM(NAME_undo, 0, NULL, undoTextBuffer,
2781 NAME_modified, "Undo operations backto last mark"),
2782 SM(NAME_attach, 1, "editor", attachTextBuffer,
2783 NAME_organisation, "Attach the given editor"),
2784 SM(NAME_detach, 1, "editor", detachTextBuffer,
2785 NAME_organisation, "Detach the given editor"),
2786 SM(NAME_report, 3, T_report, reportTextBuffer,
2787 NAME_report, "Report message (send to <-editors)"),
2788 SM(NAME_sort, 2, T_fromADintD_toADintD, sortTextBuffer,
2789 NAME_sort, "Sort [from, to) alphabetically by line"),
2790 SM(NAME_truncateAsFile, 0, NULL, truncateAsFileTextBuffer,
2791 NAME_stream, "Implements handling a buffer as a file"),
2792 SM(NAME_writeAsFile, 2, T_writeAsFile, writeAsFileTextBuffer,
2793 NAME_stream, "Implements handling a buffer as a file"),
2794 SM(NAME_iso_latin_1, 0, NULL, demoteTextBuffer,
2795 NAME_encoding, "Try to represent text as ISO Latin-1")
2796 };
2797
2798 /* Get Methods */
2799
2800 static getdecl get_textBuffer[] =
2801 { GM(NAME_convert, 1, "text_buffer", "editor", getConvertTextBuffer,
2802 DEFAULT, "Return `editor <-text_buffer'"),
2803 GM(NAME_length, 0, "int", NULL, getSizeTextBuffer,
2804 NAME_cardinality, "Equivalent to <-size (# characters)"),
2805 GM(NAME_size, 0, "int", NULL, getSizeTextBuffer,
2806 NAME_cardinality, "Number of characters in buffer"),
2807 GM(NAME_findAllFragments, 1, "matching=chain", "test=[code]", getFindAllFragmentsTextBuffer,
2808 NAME_fragment, "New chain holding fragments accepted by code"),
2809 GM(NAME_findFragment, 1, "fragment", "test=code", getFindFragmentTextBuffer,
2810 NAME_fragment, "First fragment that accepts code"),
2811 GM(NAME_matchingBracket, 2, "index=int", T_matchingBracket, getMatchingBracketTextBuffer,
2812 NAME_language, "Find bracket matching bracket at index"),
2813 GM(NAME_matchingQuote, 2, "index=int", T_matchingQuote, getMatchingQuoteTextBuffer,
2814 NAME_language, "Find matching string-quote"),
2815 GM(NAME_skipComment, 3, "index=int", T_skipComment, getSkipCommentTextBuffer,
2816 NAME_language, "Skip comments and optionally white space"),
2817 GM(NAME_skipLayout, 3, "index=int", T_skipLayout, getSkipBlanksTextBuffer,
2818 NAME_language, "Skip white-space in either direction"),
2819 GM(NAME_lineNumber, 1, "line=int", "index=[int]", getLineNumberTextBuffer,
2820 NAME_line, "Get line number (1-based) for character index"),
2821 GM(NAME_scan, 4, "index=int", T_scan, getScanTextBuffer,
2822 NAME_parse, "Scan textual units"),
2823 GM(NAME_character, 1, "char", "at=int", getCharacterTextBuffer,
2824 NAME_read, "ASCII value of character at index"),
2825 GM(NAME_contents, 2, "string", T_contents, getContentsTextBuffer,
2826 NAME_read, "New string holding text (from, length)"),
2827 GM(NAME_sub, 2, "string", T_fromADintD_toADintD, getSubTextBuffer,
2828 NAME_read, "New string holding text [from, to)"),
2829 GM(NAME_find, 6, "index=int", T_find, getFindTextBuffer,
2830 NAME_search, "Search for a string"),
2831 GM(NAME_readAsFile, 2, "string", T_fromAint_sizeAint,getReadAsFileTextBuffer,
2832 NAME_stream, "Implement reading as a file"),
2833 GM(NAME_sizeAsFile, 0, "characters=int", NULL, getSizeAsFileTextBuffer,
2834 NAME_stream, "Implement seek when using as a file"),
2835 GM(NAME_scanSyntax, 2, "tuple", T_fromADintD_toADintD,
2836 getScanSyntaxTextBuffer,
2837 NAME_language, "Find syntactical state of position"),
2838 GM(NAME_countLines, 2, "int", T_fromADintD_toADintD,
2839 getCountLinesTextBuffer,
2840 NAME_line, "Count lines in character range")
2841 };
2842
2843 /* Resources */
2844
2845 static classvardecl rc_textBuffer[] =
2846 { RC(NAME_syntax, "[syntax_table]", "default",
2847 "Syntax definition"),
2848 RC(NAME_undoBufferSize, "int", "10000",
2849 "Memory allocated to store undo"),
2850 RC(NAME_indentTabs, "bool", "@on", NULL)
2851 };
2852
2853 /* Class Declaration */
2854
2855 static Name textBuffer_termnames[] = { NAME_string };
2856
2857 ClassDecl(textBuffer_decls,
2858 var_textBuffer, send_textBuffer, get_textBuffer, rc_textBuffer,
2859 0, textBuffer_termnames,
2860 "$Rev$");
2861
2862 status
makeClassTextBuffer(Class class)2863 makeClassTextBuffer(Class class)
2864 { declareClass(class, &textBuffer_decls);
2865
2866 setLoadStoreFunctionClass(class, loadTextBuffer, storeTextBuffer);
2867 saveStyleVariableClass(class, NAME_editors, NAME_nil);
2868 setCloneFunctionClass(class, cloneTextBuffer);
2869 cloneStyleVariableClass(class, NAME_editors, NAME_referenceChain);
2870
2871 succeed;
2872 }
2873