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 <guy.ste...@oracle.com> 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 <james.las...@oracle.com 
>> <mailto:james.las...@oracle.com>> 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 <guy.ste...@oracle.com 
>>> <mailto: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.  :-)
>>> 
>> 
> 

Reply via email to