Some follow up. Now that we allow additional indentation to be specified when
using .align(n), we’re dropping the “closing delimiter trick” that was
initially proposed.
Example:
// With the trick of allowing the line that includes the closing quote to
influence the indentation.
String a = `
abc
def
ghi
`.align();
Result:
abc
def
ghi
This was accomplished by only filtering empty lines.
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);
}
By change the filter to .filter(not(String::isBlank)), we only measure lines
that have visible (non-white space) characters and hence ignore the last line.
Examples:
String a = `
abc
def
ghi
`.align(4);
String a = `
abc
def
ghi
`.align(4);
String a = `
abc
def
ghi
`.align(4);
String a = `
abc
def
ghi
`.align(4);
All produce the same result:
abc
def
ghi
Note that the first line is skipped so that
String a = `abc
def
ghi`.align(4);
String a = `abc
def
ghi
`.align(4);
also produces the same result (not enforcing a style convention.)
— Jim
> On Jun 8, 2018, at 12:43 PM, Guy Steele <[email protected]> wrote:
>
> I think “align” is an excellent choice of method name.
>
> In the doc, "The result is realigned” => "The result is then realigned” ?
>
> And the simplification to just `.lines(m, n)` is welcome. If you really want
> to eliminate blank lines in the middle, just say `.lines(m,
> n).filter(not(String::isEmpty))`.
>
> —Guy
>
>> On Jun 8, 2018, at 10:36 AM, Jim Laskey <[email protected]
>> <mailto:[email protected]>> wrote:
>>
>> 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 <[email protected]
>>> <mailto:[email protected]>> wrote:
>>>
>>>
>>>> On Jun 7, 2018, at 1:48 PM, Brian Goetz <[email protected]
>>>> <mailto:[email protected]>> 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. :-)
>>>
>>
>