1

I started using Java 15 text blocks which are nice for readability. Now I want to format text blocks and inject strings in them, and from what I understand the idiomatic way to do this right now is to use String::formatted. But it seems that source-code indentation in text blocks is a bit limited when injecting multi-line strings.

For example, the following program:

class Scratch {

    public static void main(String[] args) {

        String text = """
            Hello!
            This is a text block!
            """;

        System.out.println(text);

    }

}

prints the two lines without any indentation, as expected:

Hello!
This is a text block!

However, this one:

class Scratch {

    public static void main(String[] args) {

        String text = """
            Hello!
            This is a text block!
            """;

        String wrapperText = """
            Hello again, now I'd like the entire text below to be indented with a single tab:
                %s
            """.formatted(text);

        System.out.println(wrapperText);

    }

}

prints the following:

Hello again, now I'd like the entire text below to be indented with a single tab:
    Hello!
This is a text block!

whereas I was kind of hoping to get the following:

Hello again, now I'd like the entire text below to be indented with a single tab:
    Hello!
    This is a text block!

I do realize that the problem has nothing to do with the fact that text is a text block in my second program, it might as well have been a traditional string literal with a new line character \n, and the outcome would have been the same. The wrapper text block clearly only applies the indentation to the first line of the injected multi-line string, which is understandable as it is probably just appending the injected string as it is after the indentation.

So I guess my question is: is there a clean way to apply the indentation specified in the wrapper text block to all lines of the injected string?

I know I could do something like .formatted(text.replaceAll("\n", "\n\t")) to format the text block, but that is clumsy, makes assumptions about the line termination sequence, and defeats the purpose of having the indentation specified in one place, cause then if I want to update the indentation, I would need to do it both in the text block and in the replacement string.

Namefie
  • 117
  • 1
  • 2
  • 14
  • The _cleanest way_ of doing that would be to wrap this behaviour in a function. This way, you could centralize where your indentation is defined, and still use the _clumsy way_ you've described. – Nicolas Jan 02 '22 at 14:10
  • @Nicolas I'm not sure what that would look like concretely. I would appreciate it if you could write an answer with a short example :) – Namefie Jan 02 '22 at 14:19
  • 1
    Since text blocks always use `\n` as line terminator, there is no problem if your workaround makes assumptions about the line terminator (as long as the assumption is that the line terminator is `\n`). – Holger May 02 '23 at 16:07

1 Answers1

0

I use Java 8. Here's one method to indent a multi-line String.

Here are the test results from one of my tests.

Hello!
This is a text block!

Hello again, now I'd like the entire text below to be indented with 4 spaces:
    Hello!
    This is a text block!

You may have to adjust the indent method to work with Java 15.

Here's the complete runnable code.

public class TextIndentationTesting {

    public static void main(String[] args) {
        TextIndentationTesting tit = new TextIndentationTesting();

        String text = "Hello!\nThis is a text block!";
        System.out.println(text);
        System.out.println();

        String wrapperText = "Hello again, now I'd like the entire text below "
                + "to be indented with 4 spaces:";
        System.out.println(wrapperText + "\n" + tit.indent(text, 4));
    }

    public String indent(String text, int indentation) {
        // Create the indent space
        StringBuilder indentBuilder = new StringBuilder();
        for (int index = 0; index < indentation; index++) {
            indentBuilder.append(' ');
        }
        String indentSpace = indentBuilder.toString();

        // Indent the text
        String lineSeparator = Character.toString((char) 10);
        String replacement = lineSeparator + indentSpace;
        text = text.replaceAll(lineSeparator, replacement);

        return indentSpace + text;
    }

}
Gilbert Le Blanc
  • 50,182
  • 6
  • 67
  • 111
  • Well, to me this would not work for a couple of reasons: 1) it does not use Java 15 text blocks, or any other feature of the language to write text with the same advantages of text blocks such as improved source-code readability; 2) it does `System.out.println(wrapperText + "\n" + tit.indent(text, 4));` so it's kind of specific to my example where I inject only one string at the very end of the wrapper text, what if I have a long text block with multiple format specifiers? It becomes unwieldy very quickly; 3) it does not really improve on the workaround I mentioned at the end of my question... – Namefie Jan 02 '22 at 22:17