1 /// String manipulation
2 module sily..string;
3 
4 import std.array: split, popFront;
5 import sily.uni: isAlphaNumeric;
6 import std.traits: isSomeString;
7 
8 import std.stdio: writeln;
9 import std.conv: to;
10 
11 /** 
12 Splits string at `max` width while trying not to split words in half 
13 Params:
14   str = String to split
15   max = Maximum width string allowed to be
16 Examples:
17 ---
18 string[] arr = "My work here is not done yet".splitStringWidth(7);
19 writeln(arr);
20 // ["My work", " here is ", "not ", "done ", "yet"]
21 ---
22 */
23 T[] splitStringWidth(T)(T str, ulong max) if (isSomeString!T) {
24     T[] output;
25     int i = 0;
26     T[] lines = str.split('\n');
27 
28     foreach (line; lines) {
29         output ~= "";
30         T word = "";
31         foreach (c; line) {
32             if (c.isAlphaNumeric) {
33                 word ~= c;
34             } else {
35                 if (output[i].length + word.length > max) {
36                     ++i;
37                     output ~= "";
38                 }
39                 // if it's still problem at newline
40                 if (output[i].length + word.length > max) {
41                     while (word.length > 0) {
42                         output[i] ~= word[0];
43                         word.popFront();
44                         if (output[i].length + 1 >= max) {
45                             ++i;
46                             output ~= "";
47                         }
48                     }
49                 } else {
50                     output[i] ~= word;
51                     word = "";
52                 }
53                 word = [c];
54             }
55         }
56         // fix for words at end of string
57         if (output[i].length + word.length > max) {
58             ++i;
59             output ~= "";
60         }
61         if (output[i].length + word.length > max) {
62             while (word.length > 0) {
63                 output[i] ~= word[0];
64                 word.popFront();
65                 if (output[i].length + 1 >= max) {
66                     ++i;
67                     output ~= "";
68                 }
69             }
70         } else {
71             output[i] ~= word;
72             word = "";
73         }
74         ++i;
75     }
76     i = 0;
77     foreach (line; output) {
78         if (line.length == 0) { ++i; continue; }
79         if (line[0] == ' ' && line.length != 1) output[i] = line[1..$];
80         ++i;
81     }
82     return output;
83 }