diff --git a/core/src/main/java/org/spongepowered/configurate/loader/ParsingException.java b/core/src/main/java/org/spongepowered/configurate/loader/ParsingException.java index 1c19b53b5..80a38ef83 100644 --- a/core/src/main/java/org/spongepowered/configurate/loader/ParsingException.java +++ b/core/src/main/java/org/spongepowered/configurate/loader/ParsingException.java @@ -175,7 +175,9 @@ public int column() { if (this.context != null) { message.append(System.lineSeparator()).append(this.context); - if (this.column >= 0 && this.column < this.context.length()) { + // column is 1-indexed: column == context.length() points at the last character, + // so the caret drawn at position (column - 1) stays within the context bounds. + if (this.column >= 0 && this.column <= this.context.length()) { message.append(System.lineSeparator()); if (this.column > 0) { final char[] spaces = new char[this.column - 1]; diff --git a/core/src/test/java/org/spongepowered/configurate/loader/ParsingExceptionTest.java b/core/src/test/java/org/spongepowered/configurate/loader/ParsingExceptionTest.java new file mode 100644 index 000000000..c89892238 --- /dev/null +++ b/core/src/test/java/org/spongepowered/configurate/loader/ParsingExceptionTest.java @@ -0,0 +1,36 @@ +package org.spongepowered.configurate.loader; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class ParsingExceptionTest { + + // column is 1-indexed, so column == context.length() points at the last char. + // The caret must be rendered there too. Previously the guard was + // `column < context.length()`, which dropped the caret for the last char (#625). + @Test + void caretRenderedForLastColumn() { + final String context = "hello"; // length 5 + final String message = new ParsingException(1, context.length(), context, "err", null).getMessage(); + + // The caret sits under the last character: (length - 1) leading spaces, then '^'. + final int caretIndex = message.indexOf('^'); + assertTrue(caretIndex >= 0, "a caret should be rendered for the last column"); + int leadingSpaces = 0; + for (int i = caretIndex - 1; i >= 0 && message.charAt(i) == ' '; i--) { + leadingSpaces++; + } + assertEquals(context.length() - 1, leadingSpaces, "caret should be aligned under the last character"); + } + + @Test + void caretNotRenderedBeyondContext() { + final String context = "hello"; + final String message = new ParsingException(1, context.length() + 1, context, "err", null).getMessage(); + assertFalse(message.contains("^"), "caret should not be rendered past the context"); + } + +}