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 }