Skip to content

Commit 10162bb

Browse files
committed
Handle curly quotes in error checker
Also now prioritizes error messages on a single line for display to the user, since ECJ doesn't always get that right, reported mismatched argument lists when there's a syntax error and so on.
1 parent 4fc7599 commit 10162bb

File tree

7 files changed

+153
-23
lines changed

7 files changed

+153
-23
lines changed

app/src/processing/app/ui/Editor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3106,9 +3106,10 @@ public void updateEditorStatus() {
31063106

31073107

31083108
/**
3109-
* @return the Problem for the first error or warning on 'line'
3109+
* @return the Problem for the most relevant error or warning on 'line',
3110+
* defaulting to the first.
31103111
*/
3111-
Problem findProblem(int line) {
3112+
protected Problem findProblem(int line) {
31123113
int currentTab = getSketch().getCurrentCodeIndex();
31133114
return problems.stream()
31143115
.filter(p -> p.getTabIndex() == currentTab)

build/shared/lib/languages/PDE.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ editor.status.undef_global_var = The global variable "%s" does not exist
370370
editor.status.undef_class = The class "%s" does not exist
371371
editor.status.undef_var = The variable "%s" does not exist
372372
editor.status.undef_name = The name "%s" cannot be recognized
373+
editor.status.unterm_string_curly = String literal is not closed by a straight double quote. Curly quotes like %s won't help.
373374
editor.status.type_mismatch = Type mismatch, "%s" does not match with "%s"
374375
editor.status.unused_variable = The value of the local variable "%s" is not used
375376
editor.status.uninitialized_variable = The local variable "%s" may not have been initialized

java/src/processing/mode/java/JavaEditor.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2285,6 +2285,26 @@ public void statusHalted() {
22852285
}
22862286

22872287

2288+
/**
2289+
* @return the Problem for the most relevant error or warning on 'line',
2290+
* defaulting to the first.
2291+
*/
2292+
@Override
2293+
protected Problem findProblem(int line) {
2294+
List<Problem> l = findProblems(line);
2295+
if (l.size() == 0) return null;
2296+
Problem worst = l.get(0);
2297+
2298+
for (Problem p : l) {
2299+
if (p instanceof JavaProblem && ((!(worst instanceof JavaProblem)) ||
2300+
((JavaProblem)p).getPriority() > ((JavaProblem)worst).getPriority())) {
2301+
worst = p;
2302+
}
2303+
}
2304+
return worst;
2305+
}
2306+
2307+
22882308
/**
22892309
* Updates the error table in the Error Window.
22902310
* Overridden to handle the fugly import suggestions text.

java/src/processing/mode/java/pdex/ErrorMessageSimplifier.java

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public static String getIDName(int id) {
8585
/**
8686
* Tones down the jargon in the ecj reported errors.
8787
*/
88-
public static String getSimplifiedErrorMessage(IProblem iprob) {
88+
public static String getSimplifiedErrorMessage(IProblem iprob, String badCode) {
8989
if (iprob == null) return null;
9090

9191
String args[] = iprob.getArguments();
@@ -97,6 +97,7 @@ public static String getSimplifiedErrorMessage(IProblem iprob) {
9797
for (String arg : args) {
9898
Messages.log("Arg " + arg);
9999
}
100+
Messages.log("Bad code: " + badCode);
100101
}
101102

102103
String result = null;
@@ -111,6 +112,15 @@ public static String getSimplifiedErrorMessage(IProblem iprob) {
111112

112113
case IProblem.ParsingErrorDeleteToken:
113114
if (args.length > 0) {
115+
if (args[0].equalsIgnoreCase("Invalid Character")) {
116+
result = getErrorMessageForCurlyQuote(badCode);
117+
}
118+
}
119+
break;
120+
121+
case IProblem.ParsingErrorDeleteTokens:
122+
result = getErrorMessageForCurlyQuote(badCode);
123+
if (result == null) {
114124
result = Language.interpolate("editor.status.error_on", args[0]);
115125
}
116126
break;
@@ -136,13 +146,16 @@ public static String getSimplifiedErrorMessage(IProblem iprob) {
136146

137147
case IProblem.ParsingErrorInvalidToken:
138148
if (args.length > 0) {
139-
if (args[1].equals("VariableDeclaratorId")) {
140-
if (args[0].equals("int")) {
149+
if (args[0].equals("int")) {
150+
if (args[1].equals("VariableDeclaratorId")) {
141151
result = Language.text("editor.status.reserved_words");
142152
} else {
143153
result = Language.interpolate("editor.status.error_on", args[0]);
144154
}
145-
} else {
155+
} else if (args[0].equalsIgnoreCase("Invalid Character")) {
156+
result = getErrorMessageForCurlyQuote(badCode);
157+
}
158+
if (result == null) {
146159
result = Language.interpolate("editor.status.error_on", args[0]);
147160
}
148161
}
@@ -165,6 +178,9 @@ public static String getSimplifiedErrorMessage(IProblem iprob) {
165178
}
166179
break;
167180

181+
case IProblem.ParsingErrorReplaceTokens:
182+
result = getErrorMessageForCurlyQuote(badCode);
183+
168184
case IProblem.UndefinedConstructor:
169185
if (args.length == 2) {
170186
String constructorName = args[0];
@@ -230,6 +246,13 @@ public static String getSimplifiedErrorMessage(IProblem iprob) {
230246
}
231247
break;
232248

249+
case IProblem.UnterminatedString:
250+
if (badCode.contains("“") || badCode.contains("”")) {
251+
result = Language.interpolate("editor.status.unterm_string_curly",
252+
badCode.replaceAll("[^“”]", ""));
253+
}
254+
break;
255+
233256
case IProblem.TypeMismatch:
234257
if (args.length > 1) {
235258
result = Language.interpolate("editor.status.type_mismatch", args[0], args[1]);
@@ -261,16 +284,17 @@ public static String getSimplifiedErrorMessage(IProblem iprob) {
261284
result = Language.interpolate("editor.status.hiding_enclosing_type", args[0]);
262285
}
263286
break;
287+
}
264288

265-
default:
266-
String message = iprob.getMessage();
267-
if (message != null) {
268-
// Remove all instances of token
269-
// "Syntax error on token 'blah', delete this token"
270-
Matcher matcher = tokenRegExp.matcher(message);
271-
message = matcher.replaceAll("");
272-
result = message;
273-
}
289+
if (result == null) {
290+
String message = iprob.getMessage();
291+
if (message != null) {
292+
// Remove all instances of token
293+
// "Syntax error on token 'blah', delete this token"
294+
Matcher matcher = tokenRegExp.matcher(message);
295+
message = matcher.replaceAll("");
296+
result = message;
297+
}
274298
}
275299

276300
if (DEBUG) {
@@ -323,6 +347,20 @@ static private String getErrorMessageForBracket(char c) {
323347
}
324348

325349

350+
/**
351+
* @param badCode The code which may contain curly quotes
352+
* @return Friendly error message if there is a curly quote in badCode,
353+
* null otherwise.
354+
*/
355+
static private String getErrorMessageForCurlyQuote(String badCode) {
356+
if (badCode.contains("‘") || badCode.contains("’") ||
357+
badCode.contains("“") || badCode.contains("”")) {
358+
return Language.interpolate("editor.status.bad_curly_quote",
359+
badCode.replaceAll("[^‘’“”]", ""));
360+
} else return null;
361+
}
362+
363+
326364
// static private final String q(Object quotable) {
327365
// return "\"" + quotable + "\"";
328366
// }

java/src/processing/mode/java/pdex/JavaProblem.java

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020

2121
package processing.mode.java.pdex;
2222

23+
import java.util.Arrays;
24+
2325
import org.eclipse.jdt.core.compiler.IProblem;
26+
import static org.eclipse.jdt.core.compiler.IProblem.*;
2427

2528
import processing.app.Problem;
2629

@@ -53,35 +56,95 @@ public class JavaProblem implements Problem {
5356
*/
5457
private int type;
5558

59+
/**
60+
* Priority: bigger = higher. Currently 7 to 10 for errors,
61+
* 4 for warning.
62+
* <p>
63+
* The logic of the numbers in the priorityN arrays is that if ECJ wants a
64+
* token got rid of entirely it's most likely the root of the problem. If it
65+
* wants more tokens, that might have been caused by an unterminated string
66+
* or something but it's also likely to be the “real” error. Only if the
67+
* syntax is good are mismatched argument lists and so on of any real
68+
* significance. These rankings are entirely made up so can be changed to
69+
* support any other plausible scenario.
70+
*/
71+
private int priority;
72+
73+
static private final int[] priority10 = {
74+
ParsingError,
75+
ParsingErrorDeleteToken,
76+
ParsingErrorDeleteTokens,
77+
ParsingErrorInvalidToken,
78+
ParsingErrorMergeTokens,
79+
ParsingErrorMisplacedConstruct,
80+
ParsingErrorNoSuggestion,
81+
ParsingErrorNoSuggestionForTokens,
82+
ParsingErrorOnKeyword,
83+
ParsingErrorOnKeywordNoSuggestion,
84+
ParsingErrorReplaceTokens,
85+
ParsingErrorUnexpectedEOF
86+
};
87+
static private final int[] priority9 = {
88+
InvalidCharacterConstant,
89+
UnterminatedString
90+
};
91+
static private final int[] priority8 = {
92+
ParsingErrorInsertToComplete,
93+
ParsingErrorInsertToCompletePhrase,
94+
ParsingErrorInsertToCompleteScope,
95+
ParsingErrorInsertTokenAfter,
96+
ParsingErrorInsertTokenBefore,
97+
};
98+
// Sorted so I can do a one-line binary search later.
99+
static {
100+
Arrays.sort(priority10);
101+
Arrays.sort(priority9);
102+
Arrays.sort(priority8);
103+
}
104+
56105
/**
57106
* If the error is a 'cannot find type' contains the list of suggested imports
58107
*/
59108
private String[] importSuggestions;
60109

61110
public static final int ERROR = 1, WARNING = 2;
62111

63-
public JavaProblem(String message, int type, int tabIndex, int lineNumber) {
112+
public JavaProblem(String message, int type, int tabIndex, int lineNumber, int priority) {
64113
this.message = message;
65114
this.type = type;
66115
this.tabIndex = tabIndex;
67116
this.lineNumber = lineNumber;
117+
this.priority = priority;
68118
}
69119

70120
/**
71121
*
72122
* @param iProblem - The IProblem which is being wrapped
73123
* @param tabIndex - The tab number to which the error belongs to
74124
* @param lineNumber - Line number(pde code) of the error
125+
* @param badCode - The code iProblem refers to.
75126
*/
76-
public static JavaProblem fromIProblem(IProblem iProblem, int tabIndex, int lineNumber) {
127+
public static JavaProblem fromIProblem(IProblem iProblem,
128+
int tabIndex, int lineNumber, String badCode) {
77129
int type = 0;
78-
if(iProblem.isError()) {
130+
int priority = 0;
131+
if (iProblem.isError()) {
79132
type = ERROR;
133+
if (Arrays.binarySearch(priority10, iProblem.getID()) >= 0) {
134+
priority = 10;
135+
} else if (Arrays.binarySearch(priority9, iProblem.getID()) >= 0) {
136+
priority = 9;
137+
} else if (Arrays.binarySearch(priority8, iProblem.getID()) >= 0) {
138+
priority = 8;
139+
} else {
140+
priority = 7;
141+
}
80142
} else if (iProblem.isWarning()) {
81143
type = WARNING;
144+
priority = 4;
82145
}
83-
String message = ErrorMessageSimplifier.getSimplifiedErrorMessage(iProblem);
84-
return new JavaProblem(message, type, tabIndex, lineNumber);
146+
String message = ErrorMessageSimplifier.getSimplifiedErrorMessage(iProblem, badCode);
147+
return new JavaProblem(message, type, tabIndex, lineNumber, priority);
85148
}
86149

87150
public void setPDEOffsets(int startOffset, int stopOffset){
@@ -132,6 +195,10 @@ public void setImportSuggestions(String[] a) {
132195
importSuggestions = a;
133196
}
134197

198+
public int getPriority() {
199+
return priority;
200+
}
201+
135202
@Override
136203
public String toString() {
137204
return "TAB " + tabIndex + ",LN " + lineNumber + "LN START OFF: "

java/src/processing/mode/java/pdex/PDEX.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1126,7 +1126,10 @@ private void handleSketchProblems(PreprocessedSketch ps) {
11261126
SketchInterval in = ps.mapJavaToSketch(start, stop);
11271127
if (in == SketchInterval.BEFORE_START) return null;
11281128
int line = ps.tabOffsetToTabLine(in.tabIndex, in.startTabOffset);
1129-
JavaProblem p = JavaProblem.fromIProblem(iproblem, in.tabIndex, line);
1129+
ps.sketch.updateSketchCodes(); // seems to be needed
1130+
String badCode = ps.sketch.getCode(in.tabIndex).getProgram()
1131+
.substring(in.startTabOffset, in.stopTabOffset);
1132+
JavaProblem p = JavaProblem.fromIProblem(iproblem, in.tabIndex, line, badCode);
11301133
p.setPDEOffsets(in.startTabOffset, in.stopTabOffset);
11311134

11321135
// Handle import suggestions

java/src/processing/mode/java/pdex/SourceUtils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ static public List<JavaProblem> checkForMissingBraces(StringBuilder p, int[] tab
355355
if (depth < 0) {
356356
JavaProblem problem =
357357
new JavaProblem("Found one too many } characters without { to match it.",
358-
JavaProblem.ERROR, tabIndex, lineNumber);
358+
JavaProblem.ERROR, tabIndex, lineNumber, 8);
359359
problem.setPDEOffsets(i - tabStartOffset, i - tabStartOffset + 1);
360360
problems.add(problem);
361361
continue tabLoop;
@@ -364,7 +364,7 @@ static public List<JavaProblem> checkForMissingBraces(StringBuilder p, int[] tab
364364
if (depth > 0) {
365365
JavaProblem problem =
366366
new JavaProblem("Found one too many { characters without } to match it.",
367-
JavaProblem.ERROR, tabIndex, lineNumber - 1);
367+
JavaProblem.ERROR, tabIndex, lineNumber - 1, 8);
368368
problem.setPDEOffsets(tabEndOffset - tabStartOffset - 2, tabEndOffset - tabStartOffset - 1);
369369
problems.add(problem);
370370
}

0 commit comments

Comments
 (0)