Mercurial > projects > ldc
comparison tango/tango/text/Text.d @ 132:1700239cab2e trunk
[svn r136] MAJOR UNSTABLE UPDATE!!!
Initial commit after moving to Tango instead of Phobos.
Lots of bugfixes...
This build is not suitable for most things.
author | lindquist |
---|---|
date | Fri, 11 Jan 2008 17:57:40 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
131:5825d48b27d1 | 132:1700239cab2e |
---|---|
1 /******************************************************************************* | |
2 | |
3 copyright: Copyright (c) 2005 Kris Bell. All rights reserved | |
4 | |
5 license: BSD style: $(LICENSE) | |
6 | |
7 version: Initial release: December 2005 | |
8 | |
9 author: Kris | |
10 | |
11 | |
12 _Text is a class for storing and manipulating Unicode characters. | |
13 | |
14 _Text maintains a current "selection", controlled via the mark(), | |
15 select() and selectPrior() methods. Each of append(), prepend(), | |
16 replace() and remove() operate with respect to the selection. The | |
17 select() methods operate with respect to the current selection | |
18 also, providing a means of iterating across matched patterns. To | |
19 set a selection across the entire content, use the mark() method | |
20 with no arguments. | |
21 | |
22 Indexes and lengths of content always count code units, not code | |
23 points. This is similar to traditional ascii string handling, yet | |
24 indexing is rarely used in practice due to the selection idiom: | |
25 substring indexing is generally implied as opposed to manipulated | |
26 directly. This allows for a more streamlined model with regard to | |
27 surrogates. | |
28 | |
29 Strings support a range of functionality, from insert and removal | |
30 to utf encoding and decoding. There is also an immutable subset | |
31 called TextView, intended to simplify life in a multi-threaded | |
32 environment. However, TextView must expose the raw content as | |
33 needed and thus immutability depends to an extent upon so-called | |
34 "honour" of a callee. D does not enable immutability enforcement | |
35 at this time, but this class will be modified to support such a | |
36 feature when it arrives - via the slice() method. | |
37 | |
38 The class is templated for use with char[], wchar[], and dchar[], | |
39 and should migrate across encodings seamlessly. In particular, all | |
40 functions in tango.text.Util are compatible with _Text content in | |
41 any of the supported encodings. In future, this class will become | |
42 the principal gateway to the extensive ICU unicode library. | |
43 | |
44 Note that several common text operations can be constructed through | |
45 combining tango.text._Text with tango.text.Util e.g. lines of text | |
46 can be processed thusly: | |
47 --- | |
48 auto source = new Text!(char)("one\ntwo\nthree"); | |
49 | |
50 foreach (line; Util.lines(source.slice)) | |
51 // do something with line | |
52 --- | |
53 | |
54 Substituting patterns within text can be implemented simply and | |
55 rather efficiently: | |
56 --- | |
57 auto dst = new Text!(char); | |
58 | |
59 foreach (element; Util.patterns ("all cows eat grass", "eat", "chew")) | |
60 dst.append (element); | |
61 --- | |
62 | |
63 Speaking a bit like Yoda might be accomplished as follows: | |
64 --- | |
65 auto dst = new Text!(char); | |
66 | |
67 foreach (element; Util.delims ("all cows eat grass", " ")) | |
68 dst.prepend (element); | |
69 --- | |
70 | |
71 Below is an overview of the API and class hierarchy: | |
72 --- | |
73 class Text(T) : TextView!(T) | |
74 { | |
75 // set or reset the content | |
76 Text set (T[] chars, bool mutable=true); | |
77 Text set (TextView other, bool mutable=true); | |
78 | |
79 // retreive currently selected text | |
80 T[] selection (); | |
81 | |
82 // get the index and length of the current selection | |
83 Span selectionSpan (); | |
84 | |
85 // mark a selection | |
86 Text select (int start=0, int length=int.max); | |
87 | |
88 // move the selection around | |
89 bool select (T c); | |
90 bool select (T[] pattern); | |
91 bool select (TextView other); | |
92 bool selectPrior (T c); | |
93 bool selectPrior (T[] pattern); | |
94 bool selectPrior (TextView other); | |
95 | |
96 // append behind current selection | |
97 Text append (TextView other); | |
98 Text append (T[] text); | |
99 Text append (T chr, int count=1); | |
100 Text append (int value, options); | |
101 Text append (long value, options); | |
102 Text append (double value, options); | |
103 | |
104 // transcode behind current selection | |
105 Text encode (char[]); | |
106 Text encode (wchar[]); | |
107 Text encode (dchar[]); | |
108 | |
109 // insert before current selection | |
110 Text prepend (T[] text); | |
111 Text prepend (TextView other); | |
112 Text prepend (T chr, int count=1); | |
113 | |
114 // replace current selection | |
115 Text replace (T chr); | |
116 Text replace (T[] text); | |
117 Text replace (TextView other); | |
118 | |
119 // remove current selection | |
120 Text remove (); | |
121 | |
122 // clear content | |
123 Text clear (); | |
124 | |
125 // trim leading and trailing whitespace | |
126 Text trim (); | |
127 | |
128 // trim leading and trailing chr instances | |
129 Text strip (T chr); | |
130 | |
131 // truncate at point, or current selection | |
132 Text truncate (int point = int.max); | |
133 | |
134 // reserve some space for inserts/additions | |
135 Text reserve (int extra); | |
136 } | |
137 | |
138 class TextView(T) : UniText | |
139 { | |
140 // hash content | |
141 hash_t toHash (); | |
142 | |
143 // return length of content | |
144 uint length (); | |
145 | |
146 // compare content | |
147 bool equals (T[] text); | |
148 bool equals (TextView other); | |
149 bool ends (T[] text); | |
150 bool ends (TextView other); | |
151 bool starts (T[] text); | |
152 bool starts (TextView other); | |
153 int compare (T[] text); | |
154 int compare (TextView other); | |
155 int opEquals (Object other); | |
156 int opCmp (Object other); | |
157 | |
158 // copy content | |
159 T[] copy (T[] dst); | |
160 | |
161 // return content | |
162 T[] slice (); | |
163 | |
164 // return data type | |
165 typeinfo encoding (); | |
166 | |
167 // replace the comparison algorithm | |
168 Comparator comparator (Comparator other); | |
169 } | |
170 | |
171 class UniText | |
172 { | |
173 // convert content | |
174 abstract char[] toString (char[] dst = null); | |
175 abstract wchar[] toString16 (wchar[] dst = null); | |
176 abstract dchar[] toString32 (dchar[] dst = null); | |
177 } | |
178 --- | |
179 | |
180 *******************************************************************************/ | |
181 | |
182 module tango.text.Text; | |
183 | |
184 private import Util = tango.text.Util; | |
185 | |
186 private import Utf = tango.text.convert.Utf; | |
187 | |
188 private import Float = tango.text.convert.Float; | |
189 | |
190 private import Integer = tango.text.convert.Integer; | |
191 | |
192 /******************************************************************************* | |
193 | |
194 *******************************************************************************/ | |
195 | |
196 private extern (C) void memmove (void* dst, void* src, uint bytes); | |
197 | |
198 | |
199 /******************************************************************************* | |
200 | |
201 The mutable Text class actually implements the full API, whereas | |
202 the superclasses are purely abstract (could be interfaces instead). | |
203 | |
204 *******************************************************************************/ | |
205 | |
206 class Text(T) : TextView!(T) | |
207 { | |
208 public alias set opAssign; | |
209 public alias append opCatAssign; | |
210 private alias TextView!(T) TextViewT; | |
211 | |
212 private T[] content; | |
213 private bool mutable; | |
214 private Comparator comparator_; | |
215 private uint selectPoint, | |
216 selectLength, | |
217 contentLength; | |
218 | |
219 /*********************************************************************** | |
220 | |
221 Selection span | |
222 | |
223 ***********************************************************************/ | |
224 | |
225 public struct Span | |
226 { | |
227 uint begin, /// index of selection point | |
228 length; /// length of selection | |
229 } | |
230 | |
231 /*********************************************************************** | |
232 | |
233 Create an empty Text with the specified available | |
234 space | |
235 | |
236 ***********************************************************************/ | |
237 | |
238 this (uint space = 0) | |
239 { | |
240 content.length = space; | |
241 this.comparator_ = &simpleComparator; | |
242 } | |
243 | |
244 /*********************************************************************** | |
245 | |
246 Create a Text upon the provided content. If said | |
247 content is immutable (read-only) then you might consider | |
248 setting the 'copy' parameter to false. Doing so will | |
249 avoid allocating heap-space for the content until it is | |
250 modified via Text methods. This can be useful when | |
251 wrapping an array "temporarily" with a stack-based Text | |
252 | |
253 ***********************************************************************/ | |
254 | |
255 this (T[] content, bool copy = true) | |
256 { | |
257 set (content, copy); | |
258 this.comparator_ = &simpleComparator; | |
259 } | |
260 | |
261 /*********************************************************************** | |
262 | |
263 Create a Text via the content of another. If said | |
264 content is immutable (read-only) then you might consider | |
265 setting the 'copy' parameter to false. Doing so will avoid | |
266 allocating heap-space for the content until it is modified | |
267 via Text methods. This can be useful when wrapping an array | |
268 temporarily with a stack-based Text | |
269 | |
270 ***********************************************************************/ | |
271 | |
272 this (TextViewT other, bool copy = true) | |
273 { | |
274 this (other.slice, copy); | |
275 } | |
276 | |
277 /*********************************************************************** | |
278 | |
279 Set the content to the provided array. Parameter 'copy' | |
280 specifies whether the given array is likely to change. If | |
281 not, the array is aliased until such time it is altered via | |
282 this class. This can be useful when wrapping an array | |
283 "temporarily" with a stack-based Text | |
284 | |
285 ***********************************************************************/ | |
286 | |
287 final Text set (T[] chars, bool copy = true) | |
288 { | |
289 if ((this.mutable = copy) is true) | |
290 content = chars.dup; | |
291 else | |
292 content = chars; | |
293 | |
294 return select (0, contentLength = chars.length); | |
295 } | |
296 | |
297 /*********************************************************************** | |
298 | |
299 Replace the content of this Text. If the new content | |
300 is immutable (read-only) then you might consider setting the | |
301 'copy' parameter to false. Doing so will avoid allocating | |
302 heap-space for the content until it is modified via one of | |
303 these methods. This can be useful when wrapping an array | |
304 "temporarily" with a stack-based Text | |
305 | |
306 ***********************************************************************/ | |
307 | |
308 final Text set (TextViewT other, bool copy = true) | |
309 { | |
310 return set (other.slice, copy); | |
311 } | |
312 | |
313 /*********************************************************************** | |
314 | |
315 Explicitly set the current selection | |
316 | |
317 ***********************************************************************/ | |
318 | |
319 final Text select (int start=0, int length=int.max) | |
320 { | |
321 pinIndices (start, length); | |
322 selectPoint = start; | |
323 selectLength = length; | |
324 return this; | |
325 } | |
326 | |
327 /*********************************************************************** | |
328 | |
329 Return the currently selected content | |
330 | |
331 ***********************************************************************/ | |
332 | |
333 final T[] selection () | |
334 { | |
335 return slice [selectPoint .. selectPoint+selectLength]; | |
336 } | |
337 | |
338 /*********************************************************************** | |
339 | |
340 Return the index and length of the current selection | |
341 | |
342 ***********************************************************************/ | |
343 | |
344 final Span selectionSpan () | |
345 { | |
346 Span span; | |
347 span.begin = selectPoint; | |
348 span.length = selectLength; | |
349 return span; | |
350 } | |
351 | |
352 /*********************************************************************** | |
353 | |
354 Find and select the next occurrence of a BMP code point | |
355 in a string. Returns true if found, false otherwise | |
356 | |
357 ***********************************************************************/ | |
358 | |
359 final bool select (T c) | |
360 { | |
361 auto s = slice(); | |
362 auto x = Util.locate (s, c, selectPoint); | |
363 if (x < s.length) | |
364 { | |
365 select (x, 1); | |
366 return true; | |
367 } | |
368 return false; | |
369 } | |
370 | |
371 /*********************************************************************** | |
372 | |
373 Find and select the next substring occurrence. Returns | |
374 true if found, false otherwise | |
375 | |
376 ***********************************************************************/ | |
377 | |
378 final bool select (TextViewT other) | |
379 { | |
380 return select (other.slice); | |
381 } | |
382 | |
383 /*********************************************************************** | |
384 | |
385 Find and select the next substring occurrence. Returns | |
386 true if found, false otherwise | |
387 | |
388 ***********************************************************************/ | |
389 | |
390 final bool select (T[] chars) | |
391 { | |
392 auto s = slice(); | |
393 auto x = Util.locatePattern (s, chars, selectPoint); | |
394 if (x < s.length) | |
395 { | |
396 select (x, chars.length); | |
397 return true; | |
398 } | |
399 return false; | |
400 } | |
401 | |
402 /*********************************************************************** | |
403 | |
404 Find and select a prior occurrence of a BMP code point | |
405 in a string. Returns true if found, false otherwise | |
406 | |
407 ***********************************************************************/ | |
408 | |
409 final bool selectPrior (T c) | |
410 { | |
411 auto s = slice(); | |
412 auto x = Util.locatePrior (s, c, selectPoint); | |
413 if (x < s.length) | |
414 { | |
415 select (x, 1); | |
416 return true; | |
417 } | |
418 return false; | |
419 } | |
420 | |
421 /*********************************************************************** | |
422 | |
423 Find and select a prior substring occurrence. Returns | |
424 true if found, false otherwise | |
425 | |
426 ***********************************************************************/ | |
427 | |
428 final bool selectPrior (TextViewT other) | |
429 { | |
430 return selectPrior (other.slice); | |
431 } | |
432 | |
433 /*********************************************************************** | |
434 | |
435 Find and select a prior substring occurrence. Returns | |
436 true if found, false otherwise | |
437 | |
438 ***********************************************************************/ | |
439 | |
440 final bool selectPrior (T[] chars) | |
441 { | |
442 auto s = slice(); | |
443 auto x = Util.locatePatternPrior (s, chars, selectPoint); | |
444 if (x < s.length) | |
445 { | |
446 select (x, chars.length); | |
447 return true; | |
448 } | |
449 return false; | |
450 } | |
451 | |
452 /*********************************************************************** | |
453 | |
454 Append text to this Text | |
455 | |
456 ***********************************************************************/ | |
457 | |
458 final Text append (TextViewT other) | |
459 { | |
460 return append (other.slice); | |
461 } | |
462 | |
463 /*********************************************************************** | |
464 | |
465 Append text to this Text | |
466 | |
467 ***********************************************************************/ | |
468 | |
469 final Text append (T[] chars) | |
470 { | |
471 return append (chars.ptr, chars.length); | |
472 } | |
473 | |
474 /*********************************************************************** | |
475 | |
476 Append a count of characters to this Text | |
477 | |
478 ***********************************************************************/ | |
479 | |
480 final Text append (T chr, int count=1) | |
481 { | |
482 uint point = selectPoint + selectLength; | |
483 expand (point, count); | |
484 return set (chr, point, count); | |
485 } | |
486 | |
487 /*********************************************************************** | |
488 | |
489 Append an integer to this Text | |
490 | |
491 ***********************************************************************/ | |
492 | |
493 final Text append (int v, Integer.Style fmt=Integer.Style.Signed) | |
494 { | |
495 return append (cast(long) v, fmt); | |
496 } | |
497 | |
498 /*********************************************************************** | |
499 | |
500 Append a long to this Text | |
501 | |
502 ***********************************************************************/ | |
503 | |
504 final Text append (long v, Integer.Style fmt=Integer.Style.Signed) | |
505 { | |
506 T[64] tmp = void; | |
507 return append (Integer.format(tmp, v, fmt)); | |
508 } | |
509 | |
510 /*********************************************************************** | |
511 | |
512 Append a double to this Text | |
513 | |
514 ***********************************************************************/ | |
515 | |
516 final Text append (double v, uint decimals=2, int e=10) | |
517 { | |
518 T[64] tmp = void; | |
519 return append (Float.format(tmp, v, decimals, e)); | |
520 } | |
521 | |
522 /*********************************************************************** | |
523 | |
524 Insert characters into this Text | |
525 | |
526 ***********************************************************************/ | |
527 | |
528 final Text prepend (T chr, int count=1) | |
529 { | |
530 expand (selectPoint, count); | |
531 return set (chr, selectPoint, count); | |
532 } | |
533 | |
534 /*********************************************************************** | |
535 | |
536 Insert text into this Text | |
537 | |
538 ***********************************************************************/ | |
539 | |
540 final Text prepend (T[] other) | |
541 { | |
542 expand (selectPoint, other.length); | |
543 content[selectPoint..selectPoint+other.length] = other; | |
544 return this; | |
545 } | |
546 | |
547 /*********************************************************************** | |
548 | |
549 Insert another Text into this Text | |
550 | |
551 ***********************************************************************/ | |
552 | |
553 final Text prepend (TextViewT other) | |
554 { | |
555 return prepend (other.slice); | |
556 } | |
557 | |
558 /*********************************************************************** | |
559 | |
560 Append encoded text at the current selection point. The text | |
561 is converted as necessary to the appropritate utf encoding. | |
562 | |
563 ***********************************************************************/ | |
564 | |
565 final Text encode (char[] s) | |
566 { | |
567 T[1024] tmp = void; | |
568 | |
569 static if (is (T == char)) | |
570 return append(s); | |
571 | |
572 static if (is (T == wchar)) | |
573 return append (Utf.toString16(s, tmp)); | |
574 | |
575 static if (is (T == dchar)) | |
576 return append (Utf.toString32(s, tmp)); | |
577 } | |
578 | |
579 /// ditto | |
580 final Text encode (wchar[] s) | |
581 { | |
582 T[1024] tmp = void; | |
583 | |
584 static if (is (T == char)) | |
585 return append (Utf.toString(s, tmp)); | |
586 | |
587 static if (is (T == wchar)) | |
588 return append (s); | |
589 | |
590 static if (is (T == dchar)) | |
591 return append (Utf.toString32(s, tmp)); | |
592 } | |
593 | |
594 /// ditto | |
595 final Text encode (dchar[] s) | |
596 { | |
597 T[1024] tmp = void; | |
598 | |
599 static if (is (T == char)) | |
600 return append (Utf.toString(s, tmp)); | |
601 | |
602 static if (is (T == wchar)) | |
603 return append (Utf.toString16(s, tmp)); | |
604 | |
605 static if (is (T == dchar)) | |
606 return append (s); | |
607 } | |
608 | |
609 /// ditto | |
610 final Text encode (Object o) | |
611 { | |
612 return encode (o.toString); | |
613 } | |
614 | |
615 /*********************************************************************** | |
616 | |
617 Replace a section of this Text with the specified | |
618 character | |
619 | |
620 ***********************************************************************/ | |
621 | |
622 final Text replace (T chr) | |
623 { | |
624 return set (chr, selectPoint, selectLength); | |
625 } | |
626 | |
627 /*********************************************************************** | |
628 | |
629 Replace a section of this Text with the specified | |
630 array | |
631 | |
632 ***********************************************************************/ | |
633 | |
634 final Text replace (T[] chars) | |
635 { | |
636 int chunk = chars.length - selectLength; | |
637 if (chunk >= 0) | |
638 expand (selectPoint, chunk); | |
639 else | |
640 remove (selectPoint, -chunk); | |
641 | |
642 content [selectPoint .. selectPoint+chars.length] = chars; | |
643 return select (selectPoint, chars.length); | |
644 } | |
645 | |
646 /*********************************************************************** | |
647 | |
648 Replace a section of this Text with another | |
649 | |
650 ***********************************************************************/ | |
651 | |
652 final Text replace (TextViewT other) | |
653 { | |
654 return replace (other.slice); | |
655 } | |
656 | |
657 /*********************************************************************** | |
658 | |
659 Remove the selection from this Text and reset the | |
660 selection to zero length | |
661 | |
662 ***********************************************************************/ | |
663 | |
664 final Text remove () | |
665 { | |
666 remove (selectPoint, selectLength); | |
667 return select (selectPoint, 0); | |
668 } | |
669 | |
670 /*********************************************************************** | |
671 | |
672 Remove the selection from this Text | |
673 | |
674 ***********************************************************************/ | |
675 | |
676 private Text remove (int start, int count) | |
677 { | |
678 pinIndices (start, count); | |
679 if (count > 0) | |
680 { | |
681 if (! mutable) | |
682 realloc (); | |
683 | |
684 uint i = start + count; | |
685 memmove (content.ptr+start, content.ptr+i, (contentLength-i) * T.sizeof); | |
686 contentLength -= count; | |
687 } | |
688 return this; | |
689 } | |
690 | |
691 /*********************************************************************** | |
692 | |
693 Truncate this string at an optional index. Default behaviour | |
694 is to truncate at the current append point. Current selection | |
695 is moved to the truncation point, with length 0 | |
696 | |
697 ***********************************************************************/ | |
698 | |
699 final Text truncate (int index = int.max) | |
700 { | |
701 if (index is int.max) | |
702 index = selectPoint + selectLength; | |
703 | |
704 pinIndex (index); | |
705 return select (contentLength = index, 0); | |
706 } | |
707 | |
708 /*********************************************************************** | |
709 | |
710 Clear the string content | |
711 | |
712 ***********************************************************************/ | |
713 | |
714 final Text clear () | |
715 { | |
716 return select (contentLength = 0, 0); | |
717 } | |
718 | |
719 /*********************************************************************** | |
720 | |
721 Remove leading and trailing whitespace from this Text, | |
722 and reset the selection to the trimmed content | |
723 | |
724 ***********************************************************************/ | |
725 | |
726 final Text trim () | |
727 { | |
728 content = Util.trim (slice); | |
729 select (0, contentLength = content.length); | |
730 return this; | |
731 } | |
732 | |
733 /*********************************************************************** | |
734 | |
735 Remove leading and trailing matches from this Text, | |
736 and reset the selection to the stripped content | |
737 | |
738 ***********************************************************************/ | |
739 | |
740 final Text strip (T matches) | |
741 { | |
742 content = Util.strip (slice, matches); | |
743 select (0, contentLength = content.length); | |
744 return this; | |
745 } | |
746 | |
747 /*********************************************************************** | |
748 | |
749 Reserve some extra room | |
750 | |
751 ***********************************************************************/ | |
752 | |
753 final Text reserve (uint extra) | |
754 { | |
755 realloc (extra); | |
756 return this; | |
757 } | |
758 | |
759 | |
760 | |
761 /* ======================== TextView methods ======================== */ | |
762 | |
763 | |
764 | |
765 /*********************************************************************** | |
766 | |
767 Get the encoding type | |
768 | |
769 ***********************************************************************/ | |
770 | |
771 final TypeInfo encoding() | |
772 { | |
773 return typeid(T); | |
774 } | |
775 | |
776 /*********************************************************************** | |
777 | |
778 Set the comparator delegate. Where other is null, we behave | |
779 as a getter only | |
780 | |
781 ***********************************************************************/ | |
782 | |
783 final Comparator comparator (Comparator other) | |
784 { | |
785 auto tmp = comparator_; | |
786 if (other) | |
787 comparator_ = other; | |
788 return tmp; | |
789 } | |
790 | |
791 /*********************************************************************** | |
792 | |
793 Hash this Text | |
794 | |
795 ***********************************************************************/ | |
796 | |
797 override hash_t toHash () | |
798 { | |
799 return Util.jhash (cast(ubyte*) content.ptr, contentLength * T.sizeof); | |
800 } | |
801 | |
802 /*********************************************************************** | |
803 | |
804 Return the length of the valid content | |
805 | |
806 ***********************************************************************/ | |
807 | |
808 final uint length () | |
809 { | |
810 return contentLength; | |
811 } | |
812 | |
813 /*********************************************************************** | |
814 | |
815 Is this Text equal to another? | |
816 | |
817 ***********************************************************************/ | |
818 | |
819 final bool equals (TextViewT other) | |
820 { | |
821 if (other is this) | |
822 return true; | |
823 return equals (other.slice); | |
824 } | |
825 | |
826 /*********************************************************************** | |
827 | |
828 Is this Text equal to the provided text? | |
829 | |
830 ***********************************************************************/ | |
831 | |
832 final bool equals (T[] other) | |
833 { | |
834 if (other.length == contentLength) | |
835 return Util.matching (other.ptr, content.ptr, contentLength); | |
836 return false; | |
837 } | |
838 | |
839 /*********************************************************************** | |
840 | |
841 Does this Text end with another? | |
842 | |
843 ***********************************************************************/ | |
844 | |
845 final bool ends (TextViewT other) | |
846 { | |
847 return ends (other.slice); | |
848 } | |
849 | |
850 /*********************************************************************** | |
851 | |
852 Does this Text end with the specified string? | |
853 | |
854 ***********************************************************************/ | |
855 | |
856 final bool ends (T[] chars) | |
857 { | |
858 if (chars.length <= contentLength) | |
859 return Util.matching (content.ptr+(contentLength-chars.length), chars.ptr, chars.length); | |
860 return false; | |
861 } | |
862 | |
863 /*********************************************************************** | |
864 | |
865 Does this Text start with another? | |
866 | |
867 ***********************************************************************/ | |
868 | |
869 final bool starts (TextViewT other) | |
870 { | |
871 return starts (other.slice); | |
872 } | |
873 | |
874 /*********************************************************************** | |
875 | |
876 Does this Text start with the specified string? | |
877 | |
878 ***********************************************************************/ | |
879 | |
880 final bool starts (T[] chars) | |
881 { | |
882 if (chars.length <= contentLength) | |
883 return Util.matching (content.ptr, chars.ptr, chars.length); | |
884 return false; | |
885 } | |
886 | |
887 /*********************************************************************** | |
888 | |
889 Compare this Text start with another. Returns 0 if the | |
890 content matches, less than zero if this Text is "less" | |
891 than the other, or greater than zero where this Text | |
892 is "bigger". | |
893 | |
894 ***********************************************************************/ | |
895 | |
896 final int compare (TextViewT other) | |
897 { | |
898 if (other is this) | |
899 return 0; | |
900 | |
901 return compare (other.slice); | |
902 } | |
903 | |
904 /*********************************************************************** | |
905 | |
906 Compare this Text start with an array. Returns 0 if the | |
907 content matches, less than zero if this Text is "less" | |
908 than the other, or greater than zero where this Text | |
909 is "bigger". | |
910 | |
911 ***********************************************************************/ | |
912 | |
913 final int compare (T[] chars) | |
914 { | |
915 return comparator_ (slice, chars); | |
916 } | |
917 | |
918 /*********************************************************************** | |
919 | |
920 Return content from this Text | |
921 | |
922 A slice of dst is returned, representing a copy of the | |
923 content. The slice is clipped to the minimum of either | |
924 the length of the provided array, or the length of the | |
925 content minus the stipulated start point | |
926 | |
927 ***********************************************************************/ | |
928 | |
929 final T[] copy (T[] dst) | |
930 { | |
931 uint i = contentLength; | |
932 if (i > dst.length) | |
933 i = dst.length; | |
934 | |
935 return dst [0 .. i] = content [0 .. i]; | |
936 } | |
937 | |
938 /*********************************************************************** | |
939 | |
940 Return an alias to the content of this TextView. Note | |
941 that you are bound by honour to leave this content wholly | |
942 unmolested. D surely needs some way to enforce immutability | |
943 upon array references | |
944 | |
945 ***********************************************************************/ | |
946 | |
947 final T[] slice () | |
948 { | |
949 return content [0 .. contentLength]; | |
950 } | |
951 | |
952 /*********************************************************************** | |
953 | |
954 Convert to the UniText types. The optional argument | |
955 dst will be resized as required to house the conversion. | |
956 To minimize heap allocation during subsequent conversions, | |
957 apply the following pattern: | |
958 --- | |
959 _Text string; | |
960 | |
961 wchar[] buffer; | |
962 wchar[] result = string.utf16 (buffer); | |
963 | |
964 if (result.length > buffer.length) | |
965 buffer = result; | |
966 --- | |
967 You can also provide a buffer from the stack, but the output | |
968 will be moved to the heap if said buffer is not large enough | |
969 | |
970 ***********************************************************************/ | |
971 | |
972 final char[] toString (char[] dst = null) | |
973 { | |
974 static if (is (T == char)) | |
975 return slice(); | |
976 | |
977 static if (is (T == wchar)) | |
978 return Utf.toString (slice, dst); | |
979 | |
980 static if (is (T == dchar)) | |
981 return Utf.toString (slice, dst); | |
982 } | |
983 | |
984 /// ditto | |
985 final wchar[] toString16 (wchar[] dst = null) | |
986 { | |
987 static if (is (T == char)) | |
988 return Utf.toString16 (slice, dst); | |
989 | |
990 static if (is (T == wchar)) | |
991 return slice; | |
992 | |
993 static if (is (T == dchar)) | |
994 return Utf.toString16 (slice, dst); | |
995 } | |
996 | |
997 /// ditto | |
998 final dchar[] toString32 (dchar[] dst = null) | |
999 { | |
1000 static if (is (T == char)) | |
1001 return Utf.toString32 (slice, dst); | |
1002 | |
1003 static if (is (T == wchar)) | |
1004 return Utf.toString32 (slice, dst); | |
1005 | |
1006 static if (is (T == dchar)) | |
1007 return slice; | |
1008 } | |
1009 | |
1010 /*********************************************************************** | |
1011 | |
1012 Compare this Text to another. We compare against other | |
1013 Strings only. Literals and other objects are not supported | |
1014 | |
1015 ***********************************************************************/ | |
1016 | |
1017 override int opCmp (Object o) | |
1018 { | |
1019 auto other = cast (TextViewT) o; | |
1020 | |
1021 if (other is null) | |
1022 return -1; | |
1023 | |
1024 return compare (other); | |
1025 } | |
1026 | |
1027 /*********************************************************************** | |
1028 | |
1029 Is this Text equal to the text of something else? | |
1030 | |
1031 ***********************************************************************/ | |
1032 | |
1033 override int opEquals (Object o) | |
1034 { | |
1035 auto other = cast (TextViewT) o; | |
1036 | |
1037 if (other) | |
1038 return equals (other); | |
1039 | |
1040 // this can become expensive ... | |
1041 char[1024] tmp = void; | |
1042 return this.toString(tmp) == o.toString; | |
1043 } | |
1044 | |
1045 /// ditto | |
1046 final int opEquals (T[] s) | |
1047 { | |
1048 return slice == s; | |
1049 } | |
1050 | |
1051 /*********************************************************************** | |
1052 | |
1053 Pin the given index to a valid position. | |
1054 | |
1055 ***********************************************************************/ | |
1056 | |
1057 private void pinIndex (inout int x) | |
1058 { | |
1059 if (x > contentLength) | |
1060 x = contentLength; | |
1061 } | |
1062 | |
1063 /*********************************************************************** | |
1064 | |
1065 Pin the given index and length to a valid position. | |
1066 | |
1067 ***********************************************************************/ | |
1068 | |
1069 private void pinIndices (inout int start, inout int length) | |
1070 { | |
1071 if (start > contentLength) | |
1072 start = contentLength; | |
1073 | |
1074 if (length > (contentLength - start)) | |
1075 length = contentLength - start; | |
1076 } | |
1077 | |
1078 /*********************************************************************** | |
1079 | |
1080 Compare two arrays. Returns 0 if the content matches, less | |
1081 than zero if A is "less" than B, or greater than zero where | |
1082 A is "bigger". Where the substrings match, the shorter is | |
1083 considered "less". | |
1084 | |
1085 ***********************************************************************/ | |
1086 | |
1087 private int simpleComparator (T[] a, T[] b) | |
1088 { | |
1089 uint i = a.length; | |
1090 if (b.length < i) | |
1091 i = b.length; | |
1092 | |
1093 for (int j, k; j < i; ++j) | |
1094 if ((k = a[j] - b[j]) != 0) | |
1095 return k; | |
1096 | |
1097 return a.length - b.length; | |
1098 } | |
1099 | |
1100 /*********************************************************************** | |
1101 | |
1102 Make room available to insert or append something | |
1103 | |
1104 ***********************************************************************/ | |
1105 | |
1106 private void expand (uint index, uint count) | |
1107 { | |
1108 if (!mutable || (contentLength + count) > content.length) | |
1109 realloc (count); | |
1110 | |
1111 memmove (content.ptr+index+count, content.ptr+index, (contentLength - index) * T.sizeof); | |
1112 selectLength += count; | |
1113 contentLength += count; | |
1114 } | |
1115 | |
1116 /*********************************************************************** | |
1117 | |
1118 Replace a section of this Text with the specified | |
1119 character | |
1120 | |
1121 ***********************************************************************/ | |
1122 | |
1123 private Text set (T chr, uint start, uint count) | |
1124 { | |
1125 content [start..start+count] = chr; | |
1126 return this; | |
1127 } | |
1128 | |
1129 /*********************************************************************** | |
1130 | |
1131 Allocate memory due to a change in the content. We handle | |
1132 the distinction between mutable and immutable here. | |
1133 | |
1134 ***********************************************************************/ | |
1135 | |
1136 private void realloc (uint count = 0) | |
1137 { | |
1138 uint size = (content.length + count + 127) & ~127; | |
1139 | |
1140 if (mutable) | |
1141 content.length = size; | |
1142 else | |
1143 { | |
1144 mutable = true; | |
1145 T[] x = content; | |
1146 content = new T[size]; | |
1147 if (contentLength) | |
1148 content[0..contentLength] = x; | |
1149 } | |
1150 } | |
1151 | |
1152 /*********************************************************************** | |
1153 | |
1154 Internal method to support Text appending | |
1155 | |
1156 ***********************************************************************/ | |
1157 | |
1158 private Text append (T* chars, uint count) | |
1159 { | |
1160 uint point = selectPoint + selectLength; | |
1161 expand (point, count); | |
1162 content[point .. point+count] = chars[0 .. count]; | |
1163 return this; | |
1164 } | |
1165 } | |
1166 | |
1167 | |
1168 | |
1169 /******************************************************************************* | |
1170 | |
1171 Immutable string | |
1172 | |
1173 *******************************************************************************/ | |
1174 | |
1175 class TextView(T) : UniText | |
1176 { | |
1177 public typedef int delegate (T[] a, T[] b) Comparator; | |
1178 | |
1179 /*********************************************************************** | |
1180 | |
1181 Return the length of the valid content | |
1182 | |
1183 ***********************************************************************/ | |
1184 | |
1185 abstract uint length (); | |
1186 | |
1187 /*********************************************************************** | |
1188 | |
1189 Is this Text equal to another? | |
1190 | |
1191 ***********************************************************************/ | |
1192 | |
1193 abstract bool equals (TextView other); | |
1194 | |
1195 /*********************************************************************** | |
1196 | |
1197 Is this Text equal to the the provided text? | |
1198 | |
1199 ***********************************************************************/ | |
1200 | |
1201 abstract bool equals (T[] other); | |
1202 | |
1203 /*********************************************************************** | |
1204 | |
1205 Does this Text end with another? | |
1206 | |
1207 ***********************************************************************/ | |
1208 | |
1209 abstract bool ends (TextView other); | |
1210 | |
1211 /*********************************************************************** | |
1212 | |
1213 Does this Text end with the specified string? | |
1214 | |
1215 ***********************************************************************/ | |
1216 | |
1217 abstract bool ends (T[] chars); | |
1218 | |
1219 /*********************************************************************** | |
1220 | |
1221 Does this Text start with another? | |
1222 | |
1223 ***********************************************************************/ | |
1224 | |
1225 abstract bool starts (TextView other); | |
1226 | |
1227 /*********************************************************************** | |
1228 | |
1229 Does this Text start with the specified string? | |
1230 | |
1231 ***********************************************************************/ | |
1232 | |
1233 abstract bool starts (T[] chars); | |
1234 | |
1235 /*********************************************************************** | |
1236 | |
1237 Compare this Text start with another. Returns 0 if the | |
1238 content matches, less than zero if this Text is "less" | |
1239 than the other, or greater than zero where this Text | |
1240 is "bigger". | |
1241 | |
1242 ***********************************************************************/ | |
1243 | |
1244 abstract int compare (TextView other); | |
1245 | |
1246 /*********************************************************************** | |
1247 | |
1248 Compare this Text start with an array. Returns 0 if the | |
1249 content matches, less than zero if this Text is "less" | |
1250 than the other, or greater than zero where this Text | |
1251 is "bigger". | |
1252 | |
1253 ***********************************************************************/ | |
1254 | |
1255 abstract int compare (T[] chars); | |
1256 | |
1257 /*********************************************************************** | |
1258 | |
1259 Return content from this Text. A slice of dst is | |
1260 returned, representing a copy of the content. The | |
1261 slice is clipped to the minimum of either the length | |
1262 of the provided array, or the length of the content | |
1263 minus the stipulated start point | |
1264 | |
1265 ***********************************************************************/ | |
1266 | |
1267 abstract T[] copy (T[] dst); | |
1268 | |
1269 /*********************************************************************** | |
1270 | |
1271 Compare this Text to another | |
1272 | |
1273 ***********************************************************************/ | |
1274 | |
1275 abstract int opCmp (Object o); | |
1276 | |
1277 /*********************************************************************** | |
1278 | |
1279 Is this Text equal to another? | |
1280 | |
1281 ***********************************************************************/ | |
1282 | |
1283 abstract int opEquals (Object other); | |
1284 | |
1285 /*********************************************************************** | |
1286 | |
1287 Is this Text equal to another? | |
1288 | |
1289 ***********************************************************************/ | |
1290 | |
1291 abstract int opEquals (T[] other); | |
1292 | |
1293 /*********************************************************************** | |
1294 | |
1295 Get the encoding type | |
1296 | |
1297 ***********************************************************************/ | |
1298 | |
1299 abstract TypeInfo encoding(); | |
1300 | |
1301 /*********************************************************************** | |
1302 | |
1303 Set the comparator delegate | |
1304 | |
1305 ***********************************************************************/ | |
1306 | |
1307 abstract Comparator comparator (Comparator other); | |
1308 | |
1309 /*********************************************************************** | |
1310 | |
1311 Hash this Text | |
1312 | |
1313 ***********************************************************************/ | |
1314 | |
1315 abstract hash_t toHash (); | |
1316 | |
1317 /*********************************************************************** | |
1318 | |
1319 Return an alias to the content of this TextView. Note | |
1320 that you are bound by honour to leave this content wholly | |
1321 unmolested. D surely needs some way to enforce immutability | |
1322 upon array references | |
1323 | |
1324 ***********************************************************************/ | |
1325 | |
1326 abstract T[] slice (); | |
1327 } | |
1328 | |
1329 | |
1330 /******************************************************************************* | |
1331 | |
1332 A string abstraction that converts to anything | |
1333 | |
1334 *******************************************************************************/ | |
1335 | |
1336 class UniText | |
1337 { | |
1338 abstract char[] toString (char[] dst = null); | |
1339 | |
1340 abstract wchar[] toString16 (wchar[] dst = null); | |
1341 | |
1342 abstract dchar[] toString32 (dchar[] dst = null); | |
1343 | |
1344 abstract TypeInfo encoding(); | |
1345 } | |
1346 | |
1347 | |
1348 | |
1349 /******************************************************************************* | |
1350 | |
1351 *******************************************************************************/ | |
1352 | |
1353 debug (UnitTest) | |
1354 { | |
1355 //void main() {} | |
1356 unittest | |
1357 { | |
1358 auto s = new Text!(char); | |
1359 s = "hello"; | |
1360 | |
1361 s.select ("hello"); | |
1362 assert (s.selection == "hello"); | |
1363 s.replace ("1"); | |
1364 assert (s.selection == "1"); | |
1365 assert (s == "1"); | |
1366 | |
1367 assert (s.clear == ""); | |
1368 | |
1369 assert (s.append(12345) == "12345"); | |
1370 assert (s.selection == "12345"); | |
1371 | |
1372 //s.append ("fubar"); | |
1373 s ~= "fubar"; | |
1374 assert (s.selection == "12345fubar"); | |
1375 assert (s.select('5')); | |
1376 assert (s.selection == "5"); | |
1377 assert (s.remove == "1234fubar"); | |
1378 assert (s.select("fubar")); | |
1379 assert (s.selection == "fubar"); | |
1380 assert (s.select("wumpus") is false); | |
1381 assert (s.selection == "fubar"); | |
1382 | |
1383 assert (s.clear.append(1.2345, 4) == "1.2345"); | |
1384 | |
1385 assert (s.clear.append(0xf0, Integer.Style.Binary) == "11110000"); | |
1386 | |
1387 assert (s.clear.encode("one"d).toString == "one"); | |
1388 | |
1389 assert (Util.splitLines(s.clear.append("a\nb").slice).length is 2); | |
1390 | |
1391 assert (s.select.replace("almost ") == "almost "); | |
1392 foreach (element; Util.patterns ("all cows eat grass", "eat", "chew")) | |
1393 s.append (element); | |
1394 assert (s.selection == "almost all cows chew grass"); | |
1395 } | |
1396 } |