We’re going to try align. Example:
String a = ` abc def ghi `.align(); Result: abc def ghi Example: String b = ` abc def ghi `.align(4); Result: abc def ghi Example: String c = ` abc def ghi `.align(-1); Result: abc def ghi Implementation: /** * When applied to a string, left justifies * lines without loss of relative indentation. This is * accomplished by removing an equal number of * {@link Character#isWhitespace(int) white space} characters * from each line so that at least one line has a non-white * space character in the left-most position. * The result is realigned by indenting {@code n} * {@link Character#isWhitespace(int) white space} * characters. * First and last blank lines introduced to allow * bracketing delimiters to appear on separate source lines * are also removed. * <p> * @apinote All * {@link Character#isWhitespace(int) white space characters}, * including tab, are treated as a single space. * <p> * @apiNote The all line terminators in the result will be * replaced with line feed {@code "\n"} ({@code U+000A}). * * @param n number of leading white space characters * to adjust * * @return string left justified * * @see Character#isWhitespace(int) * @see String#indent(int) * @see String#lines() * @see String#lines(LinesOptions... options) * * @since 11 */ public String align(int n) { if (isEmpty()) { return ""; } int min = lines().skip(1) .filter(not(String::isEmpty)) .mapToInt(String::indexOfNonWhitespace) .min() .orElse(0); return indent(n - min, true); } /** * When applied to a string, left justifies * lines without loss of relative indentation. This is * accomplished by removing an equal number of * {@link Character#isWhitespace(int) white space} characters * from each line so that at least one line has a non-white * space character in the left-most position. * First and last blank lines introduced to allow * bracketing delimiters to appear on separate source lines * are also removed. * <p> * @apinote All * {@link Character#isWhitespace(int) white space characters}, * including tab, are treated as a single space. * <p> * @apiNote The all line terminators in the result will be * replaced with line feed {@code "\n"} ({@code U+000A}). * * @return string left justified * * @see Character#isWhitespace(int) * @see String#indent(int) * @see String#lines() * @see String#lines(LinesOptions... options) * * @since 11 */ public String align() { return align(0); } With lines, we’re going to drop the LinesOptions silliness and just go with maxLeading and maxTrailing. Example: int d = ` abc def ghi `.lines().count(); Result: 5 Example: int e = ` abc def ghi `.lines(1, 1).count(); Result: 3 Example: int f = ` abc def ghi `.lines(Integer.Max_VALUE,Integer.Max_VALUE).count(); Result: 3 Implementation: /** * Returns a stream of substrings extracted from this string * partitioned by line terminators. * <p> * Line terminators recognized are line feed * {@code "\n"} ({@code U+000A}), * carriage return * {@code "\r"} ({@code U+000D}) * and a carriage return followed immediately by a line feed * {@code "\r\n"} ({@code U+000D U+000A}). * <p> * The stream returned by this method contains each line of * this string that is terminated by a line terminator except that * the last line can either be terminated by a line terminator or the * end of the string. * The lines in the stream are in the order in which * they occur in this string and do not include the line terminators * partitioning the lines. * <p> * The {@code maxLeading} and {@code maxTrailing} arguments can be * used to remove incidental blank lines from the beginning and * end of a multi-line sequence. A value of {@code 1} will remove * at most one blank line. A value of {@link Integer.MAX_VALUE} * will all leading or trailing blank lines. * * @implNote This method provides better performance than * split("\R") by supplying elements lazily and * by faster search of new line terminators. * * @param maxLeading the maximum number of leading blank lines * to remove * * @param maxTrailing the maximum number of trailing blank lines * to remove * * @return the stream of strings extracted from this string * partitioned by line terminators * * @throws IllegalArgumentException if {@code maxLeading} or * {@code maxTrailing} is negative. * * @since 11 */ public Stream<String> lines(int maxLeading, int maxTrailing) { if (maxLeading < 0) { throw new IllegalArgumentException("maxLeading is negative: " + maxLeading); } if (maxTrailing < 0) { throw new IllegalArgumentException("maxTrailing is negative: " + maxTrailing); } Stream<String> stream = isLatin1() ? StringLatin1.lines(value, maxLeading, maxTrailing) : StringUTF16.lines(value, maxLeading, maxTrailing); return stream; } /** * Returns a stream of substrings extracted from this string * partitioned by line terminators. * <p> * Line terminators recognized are line feed * {@code "\n"} ({@code U+000A}), * carriage return * {@code "\r"} ({@code U+000D}) * and a carriage return followed immediately by a line feed * {@code "\r\n"} ({@code U+000D U+000A}). * <p> * The stream returned by this method contains each line of * this string that is terminated by a line terminator except that * the last line can either be terminated by a line terminator or the * end of the string. * The lines in the stream are in the order in which * they occur in this string and do not include the line terminators * partitioning the lines. * * @implNote This method provides better performance than * split("\R") by supplying elements lazily and * by faster search of new line terminators. * * @return the stream of strings extracted from this string * partitioned by line terminators * * @since 11 */ public Stream<String> lines() { return lines(0, 0); } > On Jun 7, 2018, at 2:49 PM, Guy Steele <guy.ste...@oracle.com> wrote: > > >> On Jun 7, 2018, at 1:48 PM, Brian Goetz <brian.go...@oracle.com >> <mailto:brian.go...@oracle.com>> wrote: >> >>> >>> Part of the goal was to shorten the name to reduce the annoyance quotient. >>> “reindent” speaks to me more than “leftMargin” (starts searching >>> thesaurus.) The combo makes sense too. Then you start thinking in terms of >>> indent is just reindent without the magic. >> >> reindent() is OK by me; its a little longer but it says what it means, and >> still less fussy than trimMargin() or autoMagicReindent(). >> >> undent() is shorter, but sounds more like what happens at an auto-body >> repair shop :) > > Or redent(), which happens a week later. :-) >