From 34e4e2cda5f060fe039dd5601f4857e853b58ca3 Mon Sep 17 00:00:00 2001 From: Abscissa Date: Thu, 1 Sep 2011 22:32:44 -0400 Subject: [PATCH 01/10] Added std.string.outdent --- std/string.d | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 183 insertions(+), 1 deletion(-) diff --git a/std/string.d b/std/string.d index 2c5fd0f8542..8a16b47a375 100644 --- a/std/string.d +++ b/std/string.d @@ -60,7 +60,7 @@ module std.string; import core.exception : onRangeError; import core.vararg, core.stdc.stdio, core.stdc.stdlib, core.stdc.string, - std.ascii, std.conv, std.exception, std.format, std.functional, + std.algorithm, std.ascii, std.conv, std.exception, std.format, std.functional, std.metastrings, std.range, std.regex, std.stdio, std.traits, std.typetuple, std.uni, std.utf; @@ -3802,6 +3802,188 @@ unittest assert(wrap("u u") == "u u\n"); } +/****************************************** + * Removes indentation from a multi-line string or an array of single-line strings. + * + * This uniformly outdents the text as much as possible. + * Whitespace-only lines are always converted to blank lines. + * + * The indentation style must be consistent (except for whitespace-only lines) + * or else this will throw an Exception. + * + * Works at compile-time. + * + * Example: + * --- + * writeln(q{ + * import std.stdio; + * void main() { + * writeln("Hello"); + * } + * }.outdent()); + * --- + * + * Output: + * --- + * + * import std.stdio; + * void main() { + * writeln("Hello"); + * } + * + * --- + * + */ + +C[] outdent(C)(C[] str) if(isSomeChar!C) +{ + if (str.empty) + { + return ""; + } + + C[] nl = "\n"; + C[][] lines = str.split(nl); + lines = outdent(lines); + return lines.join(nl); +} + +/// ditto +C[][] outdent(C)(C[][] lines) if(isSomeChar!C) +{ + if (lines.empty) + { + return null; + } + + static C[] leadingWhiteOf(C[] str) + { + return str[ 0 .. $-find!(not!(std.uni.isWhite))(str).length ]; + } + + // Apply leadingWhiteOf, but emit null instead for whitespace-only lines + C[][] indents; + indents.length = lines.length; + foreach (i, line; lines) + { + auto stripped = __ctfe? line.ctfe_strip() : line.strip(); + indents[i] = stripped.empty? null : leadingWhiteOf(line); + } + + static C[] shorterAndNonNull(C[] a, C[] b) + { + if (a is null) + { + return b; + } + + if (b is null) + { + return a; + } + + return (a.length < b.length)? a : b; + }; + auto shortestIndent = std.algorithm.reduce!shorterAndNonNull(indents); + + foreach (i; 0..lines.length) + { + if (indents[i] is null) + { + lines[i] = ""; + } + else if (indents[i].startsWith(shortestIndent)) + { + lines[i] = lines[i][shortestIndent.length..$]; + } + else + { + if (__ctfe) + { + assert(false, "Inconsistent indentation"); + } + else + { + throw new Exception("Inconsistent indentation"); + } + } + } + + return lines; +} + +// TODO: Remove this and use std.string.strip when retro() becomes ctfe-able. +private C[] ctfe_strip(C)(C[] str) if(isSomeChar!C) +{ + return str.stripLeft().ctfe_stripRight(); +} + +// TODO: Remove this and use std.string.strip when retro() becomes ctfe-able. +private C[] ctfe_stripRight(C)(C[] str) if(isSomeChar!C) +{ + size_t endIndex = 0; + + foreach_reverse (i, C ch; str) + { + if (!std.uni.isWhite(ch)) + { + endIndex = i+1; + break; + } + } + + return str[0..endIndex]; +} + +unittest +{ + debug(string) printf("string.outdent.unittest\n"); + + static assert(ctfe_strip(" \tHi \r\n") == "Hi"); + static assert(ctfe_strip("Hi") == "Hi"); + static assert(ctfe_strip(" \t \r\n") == ""); + static assert(ctfe_strip("") == ""); + + enum testStr = +" + \t\tX + \t\U00010143X + \t\t + + \t\t\tX +\t "c; + + enum expected = +" +\tX +\U00010143X + + +\t\tX +"c; + + immutable iblank = ""; + + assert(testStr.outdent() == expected); + assert(to!wstring(testStr).outdent() == to!wstring(expected)); + assert(to!dstring(testStr).outdent() == to!dstring(expected)); + assert(""c.outdent() == ""c); + assert(""w.outdent() == ""w); + assert(""d.outdent() == ""d); + assert(" \n \t\n "c.outdent() == "\n\n"c); + assert(" \n \t\n "w.outdent() == "\n\n"w); + assert(" \n \t\n "d.outdent() == "\n\n"d); + assert(iblank.outdent() == iblank); + + static assert(testStr.outdent() == expected); + // TODO: Uncomment these when to!w/dstring(string) works at compile-time + //static assert(to!wstring(testStr).outdent() == to!wstring(expected)); + //static assert(to!dstring(testStr).outdent() == to!dstring(expected)); + + static assert(" \n \t\n "c.outdent() == "\n\n"c); + static assert(" \n \t\n "w.outdent() == "\n\n"w); + static assert(" \n \t\n "d.outdent() == "\n\n"d); +} private template softDeprec(string vers, string date, string oldFunc, string newFunc) { From 1aa115f6a2947816bec04f960efc040bbab7737d Mon Sep 17 00:00:00 2001 From: Abscissa Date: Fri, 2 Sep 2011 00:33:52 -0400 Subject: [PATCH 02/10] outdent: Improved constness compatibility and fixed a compile-time UTF bug. --- std/string.d | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/std/string.d b/std/string.d index 8a16b47a375..53566cea9c3 100644 --- a/std/string.d +++ b/std/string.d @@ -3835,34 +3835,37 @@ unittest * */ -C[] outdent(C)(C[] str) if(isSomeChar!C) +S outdent(S)(S str) if(isSomeString!(Unqual!S)) { + alias immutable(ElementEncodingType!S)[] SplitArgType; + if (str.empty) { - return ""; + return to!S(""); } - C[] nl = "\n"; - C[][] lines = str.split(nl); + S nl = to!S("\n"); + // split seems limited in what it can accept + S[] lines = to!(S[])( str.split( to!SplitArgType(nl) ) ); lines = outdent(lines); return lines.join(nl); } /// ditto -C[][] outdent(C)(C[][] lines) if(isSomeChar!C) +S[] outdent(S)(S[] lines) if(isSomeString!(Unqual!S)) { if (lines.empty) { return null; } - static C[] leadingWhiteOf(C[] str) + static S leadingWhiteOf(S str) { return str[ 0 .. $-find!(not!(std.uni.isWhite))(str).length ]; } // Apply leadingWhiteOf, but emit null instead for whitespace-only lines - C[][] indents; + S[] indents; indents.length = lines.length; foreach (i, line; lines) { @@ -3870,7 +3873,7 @@ C[][] outdent(C)(C[][] lines) if(isSomeChar!C) indents[i] = stripped.empty? null : leadingWhiteOf(line); } - static C[] shorterAndNonNull(C[] a, C[] b) + static S shorterAndNonNull(S a, S b) { if (a is null) { @@ -3890,7 +3893,7 @@ C[][] outdent(C)(C[][] lines) if(isSomeChar!C) { if (indents[i] is null) { - lines[i] = ""; + lines[i] = to!S(""); } else if (indents[i].startsWith(shortestIndent)) { @@ -3913,23 +3916,25 @@ C[][] outdent(C)(C[][] lines) if(isSomeChar!C) } // TODO: Remove this and use std.string.strip when retro() becomes ctfe-able. -private C[] ctfe_strip(C)(C[] str) if(isSomeChar!C) +/+private+/ S ctfe_strip(S)(S str) if(isSomeString!(Unqual!S)) { return str.stripLeft().ctfe_stripRight(); } // TODO: Remove this and use std.string.strip when retro() becomes ctfe-able. -private C[] ctfe_stripRight(C)(C[] str) if(isSomeChar!C) +private S ctfe_stripRight(S)(S str) if(isSomeString!(Unqual!S)) { size_t endIndex = 0; - - foreach_reverse (i, C ch; str) + size_t prevIndex = str.length; + + foreach_reverse (i, dchar ch; str) { if (!std.uni.isWhite(ch)) { - endIndex = i+1; + endIndex = prevIndex; break; } + prevIndex = i; } return str[0..endIndex]; @@ -3940,6 +3945,7 @@ unittest debug(string) printf("string.outdent.unittest\n"); static assert(ctfe_strip(" \tHi \r\n") == "Hi"); + static assert(ctfe_strip(" \tHi©\u2028 \r\n") == "Hi©"); static assert(ctfe_strip("Hi") == "Hi"); static assert(ctfe_strip(" \t \r\n") == ""); static assert(ctfe_strip("") == ""); @@ -3973,7 +3979,10 @@ unittest assert(" \n \t\n "c.outdent() == "\n\n"c); assert(" \n \t\n "w.outdent() == "\n\n"w); assert(" \n \t\n "d.outdent() == "\n\n"d); - assert(iblank.outdent() == iblank); + assert(['a','b'].outdent() == ['a','b']); + + // TODO: Uncomment this when find works on immutable(string) + //assert(iblank.outdent() == iblank); static assert(testStr.outdent() == expected); // TODO: Uncomment these when to!w/dstring(string) works at compile-time @@ -3983,6 +3992,7 @@ unittest static assert(" \n \t\n "c.outdent() == "\n\n"c); static assert(" \n \t\n "w.outdent() == "\n\n"w); static assert(" \n \t\n "d.outdent() == "\n\n"d); + static assert(['a','b'].outdent() == ['a','b']); } private template softDeprec(string vers, string date, string oldFunc, string newFunc) From c77d12cef0e505ae919720b9e8a01db42cfd6512 Mon Sep 17 00:00:00 2001 From: Abscissa Date: Fri, 2 Sep 2011 00:35:49 -0400 Subject: [PATCH 03/10] outdent should throw StringException, not Exception. --- std/string.d | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/std/string.d b/std/string.d index 53566cea9c3..18182cad957 100644 --- a/std/string.d +++ b/std/string.d @@ -3809,7 +3809,7 @@ unittest * Whitespace-only lines are always converted to blank lines. * * The indentation style must be consistent (except for whitespace-only lines) - * or else this will throw an Exception. + * or else this will throw a StringException. * * Works at compile-time. * @@ -3907,7 +3907,7 @@ S[] outdent(S)(S[] lines) if(isSomeString!(Unqual!S)) } else { - throw new Exception("Inconsistent indentation"); + throw new StringException("Inconsistent indentation"); } } } @@ -3916,7 +3916,7 @@ S[] outdent(S)(S[] lines) if(isSomeString!(Unqual!S)) } // TODO: Remove this and use std.string.strip when retro() becomes ctfe-able. -/+private+/ S ctfe_strip(S)(S str) if(isSomeString!(Unqual!S)) +private S ctfe_strip(S)(S str) if(isSomeString!(Unqual!S)) { return str.stripLeft().ctfe_stripRight(); } From 7346e01017daf4016a256777dae5bcb156a523ad Mon Sep 17 00:00:00 2001 From: Abscissa Date: Fri, 2 Sep 2011 00:39:02 -0400 Subject: [PATCH 04/10] tabs->spaces --- std/string.d | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/std/string.d b/std/string.d index 18182cad957..2736ec3fd9f 100644 --- a/std/string.d +++ b/std/string.d @@ -3837,15 +3837,15 @@ unittest S outdent(S)(S str) if(isSomeString!(Unqual!S)) { - alias immutable(ElementEncodingType!S)[] SplitArgType; - + alias immutable(ElementEncodingType!S)[] SplitArgType; + if (str.empty) { return to!S(""); } S nl = to!S("\n"); - // split seems limited in what it can accept + // split seems limited in what it can accept S[] lines = to!(S[])( str.split( to!SplitArgType(nl) ) ); lines = outdent(lines); return lines.join(nl); @@ -3926,7 +3926,7 @@ private S ctfe_stripRight(S)(S str) if(isSomeString!(Unqual!S)) { size_t endIndex = 0; size_t prevIndex = str.length; - + foreach_reverse (i, dchar ch; str) { if (!std.uni.isWhite(ch)) @@ -3934,7 +3934,7 @@ private S ctfe_stripRight(S)(S str) if(isSomeString!(Unqual!S)) endIndex = prevIndex; break; } - prevIndex = i; + prevIndex = i; } return str[0..endIndex]; @@ -3980,8 +3980,8 @@ unittest assert(" \n \t\n "w.outdent() == "\n\n"w); assert(" \n \t\n "d.outdent() == "\n\n"d); assert(['a','b'].outdent() == ['a','b']); - - // TODO: Uncomment this when find works on immutable(string) + + // TODO: Uncomment this when find works on immutable(string) //assert(iblank.outdent() == iblank); static assert(testStr.outdent() == expected); From 5bedad6ac60d8652274b1563bf1bb0d48396d536 Mon Sep 17 00:00:00 2001 From: Abscissa Date: Tue, 27 Sep 2011 08:39:05 -0400 Subject: [PATCH 05/10] outdent: Major cleanup. --- std/string.d | 159 +++++++++++++++++++++++++++------------------------ 1 file changed, 83 insertions(+), 76 deletions(-) diff --git a/std/string.d b/std/string.d index 6fe183835ea..7eff84ae70a 100644 --- a/std/string.d +++ b/std/string.d @@ -3817,6 +3817,8 @@ unittest * * Works at compile-time. * + * This currently has a side-effect of replacing all line endings with "\n". + * * Example: * --- * writeln(q{ @@ -3839,24 +3841,24 @@ unittest * */ -S outdent(S)(S str) if(isSomeString!(Unqual!S)) +S outdent(S)(S str) if(isSomeString!S) { - alias immutable(ElementEncodingType!S)[] SplitArgType; - - if (str.empty) - { - return to!S(""); - } - - S nl = to!S("\n"); - // split seems limited in what it can accept - S[] lines = to!(S[])( str.split( to!SplitArgType(nl) ) ); - lines = outdent(lines); - return lines.join(nl); + bool hasTrailingEOL = + str.endsWith('\n') || str.endsWith('\r') || + str.endsWith(lineSep) || str.endsWith(paraSep); + + auto lines = str.splitLines().outdent(); + S nl = "\n"; + str = lines.join(nl); + + // TODO: Remove this when BUG6735 is fixed + if (hasTrailingEOL) str ~= '\n'; + + return str; } /// ditto -S[] outdent(S)(S[] lines) if(isSomeString!(Unqual!S)) +S[] outdent(S)(S[] lines) if(isSomeString!S) { if (lines.empty) { @@ -3868,51 +3870,41 @@ S[] outdent(S)(S[] lines) if(isSomeString!(Unqual!S)) return str[ 0 .. $-find!(not!(std.uni.isWhite))(str).length ]; } - // Apply leadingWhiteOf, but emit null instead for whitespace-only lines - S[] indents; - indents.length = lines.length; + S shortestIndent; + bool foundPossibleShortest=false; foreach (i, line; lines) { auto stripped = __ctfe? line.ctfe_strip() : line.strip(); - indents[i] = stripped.empty? null : leadingWhiteOf(line); + auto indent = stripped.empty? null : leadingWhiteOf(line); + + if (stripped.empty) + lines[i] = null; + else + { + // Comparing number of code units instead of code points is OK here + // because this function throws upon inconsistent indentation. + if (!foundPossibleShortest || indent.length < shortestIndent.length) + { + foundPossibleShortest = true; + shortestIndent = indent; + } + } } - - static S shorterAndNonNull(S a, S b) - { - if (a is null) - { - return b; - } - - if (b is null) - { - return a; - } - - return (a.length < b.length)? a : b; - }; - auto shortestIndent = std.algorithm.reduce!shorterAndNonNull(indents); foreach (i; 0..lines.length) { - if (indents[i] is null) + if (lines[i].empty) { - lines[i] = to!S(""); + // Do nothing } - else if (indents[i].startsWith(shortestIndent)) + else if (lines[i].startsWith(shortestIndent)) { lines[i] = lines[i][shortestIndent.length..$]; } else { - if (__ctfe) - { - assert(false, "Inconsistent indentation"); - } - else - { - throw new StringException("Inconsistent indentation"); - } + if (__ctfe) assert(false, "Inconsistent indentation"); + else throw new StringException("Inconsistent indentation"); } } @@ -3944,59 +3936,74 @@ private S ctfe_stripRight(S)(S str) if(isSomeString!(Unqual!S)) return str[0..endIndex]; } +version(unittest) +{ + template outdent_testStr(S) + { + enum S outdent_testStr = +" + \t\tX + \t\U00010143X + \t\t + + \t\t\tX +\t "; + } + + template outdent_expected(S) + { + enum S outdent_expected = +" +\tX +\U00010143X + + +\t\tX +"; + } +} + unittest { - debug(string) printf("string.outdent.unittest\n"); + debug(string__) printf("string.outdent.unittest\n"); static assert(ctfe_strip(" \tHi \r\n") == "Hi"); static assert(ctfe_strip(" \tHi©\u2028 \r\n") == "Hi©"); static assert(ctfe_strip("Hi") == "Hi"); static assert(ctfe_strip(" \t \r\n") == ""); static assert(ctfe_strip("") == ""); + + foreach (S; TypeTuple!(string, wstring, dstring)) + { + enum S blank = ""; + assert(blank.outdent() == blank); + static assert(blank.outdent() == blank); + + enum S testStr1 = " \n \t\n "; + enum S expected1 = "\n\n"; + assert(testStr1.outdent() == expected1); + static assert(testStr1.outdent() == expected1); - enum testStr = + enum S testStr2 = " \t\tX \t\U00010143X \t\t \t\t\tX -\t "c; +\t "; - enum expected = + enum S expected2 = " \tX \U00010143X \t\tX -"c; - - immutable iblank = ""; - - assert(testStr.outdent() == expected); - assert(to!wstring(testStr).outdent() == to!wstring(expected)); - assert(to!dstring(testStr).outdent() == to!dstring(expected)); - assert(""c.outdent() == ""c); - assert(""w.outdent() == ""w); - assert(""d.outdent() == ""d); - assert(" \n \t\n "c.outdent() == "\n\n"c); - assert(" \n \t\n "w.outdent() == "\n\n"w); - assert(" \n \t\n "d.outdent() == "\n\n"d); - assert(['a','b'].outdent() == ['a','b']); - - // TODO: Uncomment this when find works on immutable(string) - //assert(iblank.outdent() == iblank); - - static assert(testStr.outdent() == expected); - // TODO: Uncomment these when to!w/dstring(string) works at compile-time - //static assert(to!wstring(testStr).outdent() == to!wstring(expected)); - //static assert(to!dstring(testStr).outdent() == to!dstring(expected)); - - static assert(" \n \t\n "c.outdent() == "\n\n"c); - static assert(" \n \t\n "w.outdent() == "\n\n"w); - static assert(" \n \t\n "d.outdent() == "\n\n"d); - static assert(['a','b'].outdent() == ['a','b']); +"; + assert(testStr2.outdent() == expected2); + static assert(testStr2.outdent() == expected2); + } } private template softDeprec(string vers, string date, string oldFunc, string newFunc) From 820b0b99faf345aec6bf02ef7c5a1f23e3753de4 Mon Sep 17 00:00:00 2001 From: Abscissa Date: Tue, 27 Sep 2011 08:41:17 -0400 Subject: [PATCH 06/10] tab2space yet again... --- std/string.d | 88 ++++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/std/string.d b/std/string.d index 7eff84ae70a..e024286405f 100644 --- a/std/string.d +++ b/std/string.d @@ -3843,17 +3843,17 @@ unittest S outdent(S)(S str) if(isSomeString!S) { - bool hasTrailingEOL = - str.endsWith('\n') || str.endsWith('\r') || - str.endsWith(lineSep) || str.endsWith(paraSep); - - auto lines = str.splitLines().outdent(); - S nl = "\n"; + bool hasTrailingEOL = + str.endsWith('\n') || str.endsWith('\r') || + str.endsWith(lineSep) || str.endsWith(paraSep); + + auto lines = str.splitLines().outdent(); + S nl = "\n"; str = lines.join(nl); - - // TODO: Remove this when BUG6735 is fixed + + // TODO: Remove this when BUG6735 is fixed if (hasTrailingEOL) str ~= '\n'; - + return str; } @@ -3870,25 +3870,25 @@ S[] outdent(S)(S[] lines) if(isSomeString!S) return str[ 0 .. $-find!(not!(std.uni.isWhite))(str).length ]; } - S shortestIndent; - bool foundPossibleShortest=false; + S shortestIndent; + bool foundPossibleShortest=false; foreach (i, line; lines) { auto stripped = __ctfe? line.ctfe_strip() : line.strip(); auto indent = stripped.empty? null : leadingWhiteOf(line); - - if (stripped.empty) - lines[i] = null; - else - { - // Comparing number of code units instead of code points is OK here - // because this function throws upon inconsistent indentation. - if (!foundPossibleShortest || indent.length < shortestIndent.length) - { - foundPossibleShortest = true; - shortestIndent = indent; - } - } + + if (stripped.empty) + lines[i] = null; + else + { + // Comparing number of code units instead of code points is OK here + // because this function throws upon inconsistent indentation. + if (!foundPossibleShortest || indent.length < shortestIndent.length) + { + foundPossibleShortest = true; + shortestIndent = indent; + } + } } foreach (i; 0..lines.length) @@ -3938,9 +3938,9 @@ private S ctfe_stripRight(S)(S str) if(isSomeString!(Unqual!S)) version(unittest) { - template outdent_testStr(S) - { - enum S outdent_testStr = + template outdent_testStr(S) + { + enum S outdent_testStr = " \t\tX \t\U00010143X @@ -3948,11 +3948,11 @@ version(unittest) \t\t\tX \t "; - } + } - template outdent_expected(S) - { - enum S outdent_expected = + template outdent_expected(S) + { + enum S outdent_expected = " \tX \U00010143X @@ -3960,7 +3960,7 @@ version(unittest) \t\tX "; - } + } } unittest @@ -3975,16 +3975,16 @@ unittest foreach (S; TypeTuple!(string, wstring, dstring)) { - enum S blank = ""; - assert(blank.outdent() == blank); - static assert(blank.outdent() == blank); + enum S blank = ""; + assert(blank.outdent() == blank); + static assert(blank.outdent() == blank); - enum S testStr1 = " \n \t\n "; - enum S expected1 = "\n\n"; - assert(testStr1.outdent() == expected1); - static assert(testStr1.outdent() == expected1); + enum S testStr1 = " \n \t\n "; + enum S expected1 = "\n\n"; + assert(testStr1.outdent() == expected1); + static assert(testStr1.outdent() == expected1); - enum S testStr2 = + enum S testStr2 = " \t\tX \t\U00010143X @@ -3993,7 +3993,7 @@ unittest \t\t\tX \t "; - enum S expected2 = + enum S expected2 = " \tX \U00010143X @@ -4001,9 +4001,9 @@ unittest \t\tX "; - assert(testStr2.outdent() == expected2); - static assert(testStr2.outdent() == expected2); - } + assert(testStr2.outdent() == expected2); + static assert(testStr2.outdent() == expected2); + } } private template softDeprec(string vers, string date, string oldFunc, string newFunc) From 09873a1f7f528a499c7fe9ad55514200f08d5981 Mon Sep 17 00:00:00 2001 From: Abscissa Date: Tue, 27 Sep 2011 09:02:49 -0400 Subject: [PATCH 07/10] Wrong debug symbol in outdent unittest. --- std/string.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/string.d b/std/string.d index e024286405f..1dbe152bd92 100644 --- a/std/string.d +++ b/std/string.d @@ -3965,7 +3965,7 @@ version(unittest) unittest { - debug(string__) printf("string.outdent.unittest\n"); + debug(string) printf("string.outdent.unittest\n"); static assert(ctfe_strip(" \tHi \r\n") == "Hi"); static assert(ctfe_strip(" \tHi©\u2028 \r\n") == "Hi©"); From 0de51992be1515015f461b332ea96109e245d58f Mon Sep 17 00:00:00 2001 From: Abscissa Date: Tue, 27 Sep 2011 18:16:15 -0400 Subject: [PATCH 08/10] Incorporated outdent suggestions. --- std/string.d | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/std/string.d b/std/string.d index 1dbe152bd92..6398c7dec09 100644 --- a/std/string.d +++ b/std/string.d @@ -3812,8 +3812,8 @@ unittest * This uniformly outdents the text as much as possible. * Whitespace-only lines are always converted to blank lines. * - * The indentation style must be consistent (except for whitespace-only lines) - * or else this will throw a StringException. + * A StringException will be thrown if inconsistent indentation prevents + * the input from being outdented. * * Works at compile-time. * @@ -3871,21 +3871,21 @@ S[] outdent(S)(S[] lines) if(isSomeString!S) } S shortestIndent; - bool foundPossibleShortest=false; foreach (i, line; lines) { auto stripped = __ctfe? line.ctfe_strip() : line.strip(); - auto indent = stripped.empty? null : leadingWhiteOf(line); if (stripped.empty) lines[i] = null; else { + auto indent = leadingWhiteOf(line); + // Comparing number of code units instead of code points is OK here // because this function throws upon inconsistent indentation. - if (!foundPossibleShortest || indent.length < shortestIndent.length) + if (shortestIndent is null || indent.length < shortestIndent.length) { - foundPossibleShortest = true; + if (indent.empty) return lines; shortestIndent = indent; } } @@ -3903,8 +3903,8 @@ S[] outdent(S)(S[] lines) if(isSomeString!S) } else { - if (__ctfe) assert(false, "Inconsistent indentation"); - else throw new StringException("Inconsistent indentation"); + if (__ctfe) assert(false, "outdent: Inconsistent indentation"); + else throw new StringException("outdent: Inconsistent indentation"); } } @@ -3984,7 +3984,11 @@ unittest assert(testStr1.outdent() == expected1); static assert(testStr1.outdent() == expected1); - enum S testStr2 = + enum S testStr2 = "a\n \t\nb"; + assert(testStr2.outdent() == testStr2); + static assert(testStr2.outdent() == testStr2); + + enum S testStr3 = " \t\tX \t\U00010143X @@ -3993,7 +3997,7 @@ unittest \t\t\tX \t "; - enum S expected2 = + enum S expected3 = " \tX \U00010143X @@ -4001,8 +4005,8 @@ unittest \t\tX "; - assert(testStr2.outdent() == expected2); - static assert(testStr2.outdent() == expected2); + assert(testStr3.outdent() == expected3); + static assert(testStr3.outdent() == expected3); } } From d118b87cc48927b0b838e11089ce5f43cd4d4c9a Mon Sep 17 00:00:00 2001 From: Abscissa Date: Fri, 30 Sep 2011 13:20:52 -0400 Subject: [PATCH 09/10] outdent: Preserve original line endings. --- std/string.d | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/std/string.d b/std/string.d index 4afd1f85e99..8eea08d8aed 100644 --- a/std/string.d +++ b/std/string.d @@ -3844,8 +3844,6 @@ unittest * * Works at compile-time. * - * This currently has a side-effect of replacing all line endings with "\n". - * * Example: * --- * writeln(q{ @@ -3870,18 +3868,7 @@ unittest S outdent(S)(S str) if(isSomeString!S) { - bool hasTrailingEOL = - str.endsWith('\n') || str.endsWith('\r') || - str.endsWith(lineSep) || str.endsWith(paraSep); - - auto lines = str.splitLines().outdent(); - S nl = "\n"; - str = lines.join(nl); - - // TODO: Remove this when BUG6735 is fixed - if (hasTrailingEOL) str ~= '\n'; - - return str; + return str.splitLines(KeepTerminator.yes).outdent().join(); } /// ditto @@ -3903,7 +3890,9 @@ S[] outdent(S)(S[] lines) if(isSomeString!S) auto stripped = __ctfe? line.ctfe_strip() : line.strip(); if (stripped.empty) - lines[i] = null; + { + lines[i] = line[line.chomp().length..$]; + } else { auto indent = leadingWhiteOf(line); @@ -3920,7 +3909,8 @@ S[] outdent(S)(S[] lines) if(isSomeString!S) foreach (i; 0..lines.length) { - if (lines[i].empty) + auto stripped = __ctfe? lines[i].ctfe_strip() : lines[i].strip(); + if (stripped.empty) { // Do nothing } @@ -4011,6 +4001,9 @@ unittest assert(testStr1.outdent() == expected1); static assert(testStr1.outdent() == expected1); + assert(testStr1[0..$-1].outdent() == expected1); + static assert(testStr1[0..$-1].outdent() == expected1); + enum S testStr2 = "a\n \t\nb"; assert(testStr2.outdent() == testStr2); static assert(testStr2.outdent() == testStr2); @@ -4034,6 +4027,21 @@ unittest "; assert(testStr3.outdent() == expected3); static assert(testStr3.outdent() == expected3); + + enum testStr4 = " X\r X\n X\r\n X\u2028 X\u2029 X"; + enum expected4 = "X\rX\nX\r\nX\u2028X\u2029X"; + assert(testStr4.outdent() == expected4); + static assert(testStr4.outdent() == expected4); + + enum testStr5 = testStr4[0..$-1]; + enum expected5 = expected4[0..$-1]; + assert(testStr5.outdent() == expected5); + static assert(testStr5.outdent() == expected5); + + enum testStr6 = " \r \n \r\n \u2028 \u2029"; + enum expected6 = "\r\n\r\n\u2028\u2029"; + assert(testStr6.outdent() == expected6); + static assert(testStr6.outdent() == expected6); } } From 5ef488f0c77d32edbea98f83a73923ebf1e38311 Mon Sep 17 00:00:00 2001 From: Abscissa Date: Fri, 30 Sep 2011 13:30:02 -0400 Subject: [PATCH 10/10] Kill trailing whitespace. --- std/string.d | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/std/string.d b/std/string.d index 8eea08d8aed..7c9b306f92c 100644 --- a/std/string.d +++ b/std/string.d @@ -3878,17 +3878,17 @@ S[] outdent(S)(S[] lines) if(isSomeString!S) { return null; } - + static S leadingWhiteOf(S str) { return str[ 0 .. $-find!(not!(std.uni.isWhite))(str).length ]; } - + S shortestIndent; foreach (i, line; lines) { auto stripped = __ctfe? line.ctfe_strip() : line.strip(); - + if (stripped.empty) { lines[i] = line[line.chomp().length..$]; @@ -3896,7 +3896,7 @@ S[] outdent(S)(S[] lines) if(isSomeString!S) else { auto indent = leadingWhiteOf(line); - + // Comparing number of code units instead of code points is OK here // because this function throws upon inconsistent indentation. if (shortestIndent is null || indent.length < shortestIndent.length) @@ -3906,7 +3906,7 @@ S[] outdent(S)(S[] lines) if(isSomeString!S) } } } - + foreach (i; 0..lines.length) { auto stripped = __ctfe? lines[i].ctfe_strip() : lines[i].strip(); @@ -3924,7 +3924,7 @@ S[] outdent(S)(S[] lines) if(isSomeString!S) else throw new StringException("outdent: Inconsistent indentation"); } } - + return lines; } @@ -3939,7 +3939,7 @@ private S ctfe_stripRight(S)(S str) if(isSomeString!(Unqual!S)) { size_t endIndex = 0; size_t prevIndex = str.length; - + foreach_reverse (i, dchar ch; str) { if (!std.uni.isWhite(ch)) @@ -3949,7 +3949,7 @@ private S ctfe_stripRight(S)(S str) if(isSomeString!(Unqual!S)) } prevIndex = i; } - + return str[0..endIndex]; } @@ -3983,13 +3983,13 @@ version(unittest) unittest { debug(string) printf("string.outdent.unittest\n"); - + static assert(ctfe_strip(" \tHi \r\n") == "Hi"); static assert(ctfe_strip(" \tHi©\u2028 \r\n") == "Hi©"); static assert(ctfe_strip("Hi") == "Hi"); static assert(ctfe_strip(" \t \r\n") == ""); static assert(ctfe_strip("") == ""); - + foreach (S; TypeTuple!(string, wstring, dstring)) { enum S blank = ""; @@ -4027,17 +4027,17 @@ unittest "; assert(testStr3.outdent() == expected3); static assert(testStr3.outdent() == expected3); - + enum testStr4 = " X\r X\n X\r\n X\u2028 X\u2029 X"; enum expected4 = "X\rX\nX\r\nX\u2028X\u2029X"; assert(testStr4.outdent() == expected4); static assert(testStr4.outdent() == expected4); - + enum testStr5 = testStr4[0..$-1]; enum expected5 = expected4[0..$-1]; assert(testStr5.outdent() == expected5); static assert(testStr5.outdent() == expected5); - + enum testStr6 = " \r \n \r\n \u2028 \u2029"; enum expected6 = "\r\n\r\n\u2028\u2029"; assert(testStr6.outdent() == expected6);