Raw String Literals indentation management

Jim Laskey james.laskey at oracle.com
Fri Jun 8 14:36:25 UTC 2018


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.steele at oracle.com> wrote:
> 
> 
>> On Jun 7, 2018, at 1:48 PM, Brian Goetz <brian.goetz at oracle.com <mailto:brian.goetz at 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.  :-)
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20180608/15c69a04/attachment-0001.html>


More information about the amber-spec-experts mailing list