OK, so let me summarize and see if I understand it:

 - there are fixed indenting methods, .indent(n), which never touch NLs;
 - the magic align() method _ignores_ blank lines now, uses the non-blank lines to identify a maximal common prefix to remove, and removes that, and then removes a leading blank line if any, and if the trailing line is blank, strips out all whitespace (so it ends in NL);
 - the methods align(n) are just align().indent(n).



On 6/11/2018 12:59 PM, Jim Laskey wrote:
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 <mailto: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