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 }