Skip to content

Commit 083bedc

Browse files
Keep the spaces of code, name where a reference starts, mark names not found
- Code.render strips only the padding Code.of adds around code that starts or ends with a backtick, and reads code across line breaks. - SourceReference gives where its words start in the message, since the code of a call may hold the same words. - Not-found errors and their suggestion mark the name as code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 4dcdca5 commit 083bedc

7 files changed

Lines changed: 31 additions & 11 deletions

File tree

‎liquidjava-verifier/src/main/java/liquidjava/diagnostics/Code.java‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
public final class Code {
1111

1212
// a run of backticks, the code, and a run of the same length
13-
private static final Pattern SPAN = Pattern.compile("(`+)(.+?)(?<!`)\\1(?!`)");
13+
private static final Pattern SPAN = Pattern.compile("(`+)(.+?)(?<!`)\\1(?!`)", Pattern.DOTALL);
1414

1515
private Code() {
1616
}
@@ -38,8 +38,11 @@ static String render(String message, boolean colour) {
3838
StringBuilder rendered = new StringBuilder();
3939
while (span.find()) {
4040
String code = span.group(2);
41-
if (code.length() > 2 && code.startsWith(" ") && code.endsWith(" "))
42-
code = code.substring(1, code.length() - 1);
41+
// the padding of() adds around code that starts or ends with a backtick, not spaces of the code
42+
String inner = code.length() > 2 && code.startsWith(" ") && code.endsWith(" ")
43+
? code.substring(1, code.length() - 1) : code;
44+
if (inner.startsWith("`") || inner.endsWith("`"))
45+
code = inner;
4346
span.appendReplacement(rendered, Matcher.quoteReplacement(Colors.BLUE + code + Colors.RESET));
4447
}
4548
span.appendTail(rendered);

‎liquidjava-verifier/src/main/java/liquidjava/diagnostics/SourceReference.java‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,25 @@
99
* @param message
1010
* the whole line, a message note or a hint, as the diagnostic gives it
1111
* @param text
12-
* the words of {@code message} that name the place, which occur in it once
12+
* the words of {@code message} that name the place
13+
* @param start
14+
* where {@code text} starts in {@code message}, as an index of its characters
1315
* @param file
1416
* the path of the file
1517
* @param line
1618
* the line, from 1
1719
* @param column
1820
* the column, from 1
1921
*/
20-
public record SourceReference(String message, String text, String file, int line, int column) {
22+
public record SourceReference(String message, String text, int start, String file, int line, int column) {
2123

24+
/** {@code text} where it first occurs in {@code message}, naming {@code position} */
2225
public static SourceReference of(String message, String text, SourcePosition position) {
23-
return new SourceReference(message, text, position.getFile().getPath(), position.getLine(),
26+
return of(message, text, message.indexOf(text), position);
27+
}
28+
29+
public static SourceReference of(String message, String text, int start, SourcePosition position) {
30+
return new SourceReference(message, text, start, position.getFile().getPath(), position.getLine(),
2431
position.getColumn());
2532
}
2633
}

‎liquidjava-verifier/src/main/java/liquidjava/diagnostics/errors/NotFoundError.java‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.Collection;
44
import java.util.Locale;
55

6+
import liquidjava.diagnostics.Code;
67
import liquidjava.diagnostics.NameSuggester;
78
import liquidjava.diagnostics.TranslationTable;
89
import liquidjava.diagnostics.hints.Hint;
@@ -29,11 +30,12 @@ public NotFoundError(SourcePosition position, String name, Kind kind, Collection
2930

3031
public NotFoundError(SourcePosition position, String name, Kind kind, TranslationTable translationTable,
3132
Collection<String> availableElements) {
32-
super("Not Found Error", String.format("%s '%s' could not be found", kind, name), position, translationTable);
33+
super("Not Found Error", String.format("%s %s could not be found", kind, Code.of(name)), position,
34+
translationTable);
3335
this.name = Utils.getSimpleName(name);
3436
this.kind = kind;
3537
NameSuggester.findClosest(name, availableElements)
36-
.ifPresent(match -> addHint(Hint.general(String.format("Did you mean '%s'?", match))));
38+
.ifPresent(match -> addHint(Hint.general(String.format("Did you mean %s?", Code.of(match)))));
3739
}
3840

3941
public String getName() {

‎liquidjava-verifier/src/main/java/liquidjava/diagnostics/hints/StateHints.java‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,12 @@ public static Hint decidingArgument(DecidingArgument deciding, String receiver)
120120
String values = deciding.values().isEmpty() ? "other arguments" : deciding.values().entrySet().stream()
121121
.map(e -> Code.of(e.getKey() + " == " + e.getValue())).collect(Collectors.joining(" and "));
122122
String line = "line " + deciding.call().position().getLine();
123-
String message = String.format("%s on %s decides the state of %s: with %s, this call would be allowed",
124-
Code.of(call), line, Code.of(receiver), values);
125-
return new Hint(Kind.DECIDING_ARGUMENT, message, SourceReference.of(message, line, deciding.call().position()));
123+
String before = Code.of(call) + " on ";
124+
String message = String.format("%s%s decides the state of %s: with %s, this call would be allowed", before,
125+
line, Code.of(receiver), values);
126+
// the call's code may hold the same words
127+
return new Hint(Kind.DECIDING_ARGUMENT, message,
128+
SourceReference.of(message, line, before.length(), deciding.call().position()));
126129
}
127130

128131
/**

‎liquidjava-verifier/src/test/java/liquidjava/api/tests/TestOriginNotes.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ void noteNamesTheLineOfTheCallForClientsToLinkTo() {
3535
SourceReference reference = error.getReferences().get(0);
3636
assertEquals(error.getMessageNotes().get(0), reference.message());
3737
assertEquals("line 27", reference.text());
38+
assertEquals("Origin: ".length(), reference.start());
3839
assertEquals(27, reference.line());
3940
assertTrue(reference.file().endsWith("ErrorStateHintMultipleSets.java"));
4041
}

‎liquidjava-verifier/src/test/java/liquidjava/api/tests/TestStateHints.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ void argumentThatDecidedTheStateIsShownWithAValueThatWouldWork() {
218218
// the line it names, for clients to link to
219219
SourceReference reference = errors("ErrorArgumentPicksTheState.java", 1).get(0).getHints().get(0).reference();
220220
assertEquals("line 20", reference.text());
221+
assertEquals("line 20", reference.message().substring(reference.start(), reference.start() + 7));
221222
assertEquals(20, reference.line());
222223
}
223224

‎liquidjava-verifier/src/test/java/liquidjava/diagnostics/CodeTest.java‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,8 @@ void terminalShowsCodeInColourWithoutItsBackticks() {
2020
"found " + Colors.BLUE + "x > 0" + Colors.RESET + " but expected " + Colors.BLUE + "`a" + Colors.RESET,
2121
Code.render(message, true));
2222
assertEquals(message, Code.render(message, false));
23+
// spaces and line breaks of the code itself are kept
24+
assertEquals(Colors.BLUE + " a " + Colors.RESET, Code.render(Code.of(" a "), true));
25+
assertEquals(Colors.BLUE + "a\nb" + Colors.RESET, Code.render(Code.of("a\nb"), true));
2326
}
2427
}

0 commit comments

Comments
 (0)