Skip to content

Commit ebd5557

Browse files
authored
Merge pull request processing#5152 from GKFX/featurefixbadquotes
Handle curly quotes well
2 parents fe3beea + c7b6ba6 commit ebd5557

11 files changed

Lines changed: 287 additions & 113 deletions

File tree

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

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3119,20 +3119,15 @@ public void updateEditorStatus() {
31193119

31203120

31213121
/**
3122-
* @return the Problem for the first error or warning on 'line'
3122+
* @return the Problem for the most relevant error or warning on 'line',
3123+
* defaults to the first error, if there are no errors first warning.
31233124
*/
3124-
Problem findProblem(int line) {
3125-
int currentTab = getSketch().getCurrentCodeIndex();
3126-
return problems.stream()
3127-
.filter(p -> p.getTabIndex() == currentTab)
3128-
.filter(p -> {
3129-
int pStartLine = p.getLineNumber();
3130-
int pEndOffset = p.getStopOffset();
3131-
int pEndLine = textarea.getLineOfOffset(pEndOffset);
3132-
return line >= pStartLine && line <= pEndLine;
3133-
})
3134-
.findFirst()
3135-
.orElse(null);
3125+
protected Problem findProblem(int line) {
3126+
List<Problem> problems = findProblems(line);
3127+
for (Problem p : problems) {
3128+
if (p.isError()) return p;
3129+
}
3130+
return problems.isEmpty() ? null : problems.get(0);
31363131
}
31373132

31383133

build/shared/lib/languages/PDE.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@ editor.status.missing.right_paren = Missing right parenthesis ")"
360360
editor.status.missing.left_curly_bracket = Missing left curly bracket "{"
361361
editor.status.missing.right_curly_bracket = Missing right curly bracket "}"
362362
editor.status.missing.add = Consider adding "%s"
363+
editor.status.bad_curly_quote = Curly quotes like %s don't work. Use straight quotes. Ctrl-T to autocorrect.
363364
editor.status.reserved_words = "color" and "int" are reserved words & cannot be used as variable names
364365
editor.status.undefined_method = The function "%s(%s)" does not exist
365366
editor.status.undefined_constructor = The constructor "%s(%s)" does not exist
@@ -369,6 +370,7 @@ editor.status.undef_global_var = The global variable "%s" does not exist
369370
editor.status.undef_class = The class "%s" does not exist
370371
editor.status.undef_var = The variable "%s" does not exist
371372
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.
372374
editor.status.type_mismatch = Type mismatch, "%s" does not match with "%s"
373375
editor.status.unused_variable = The value of the local variable "%s" is not used
374376
editor.status.uninitialized_variable = The local variable "%s" may not have been initialized

java/src/processing/mode/java/AutoFormat.java

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -639,25 +639,48 @@ public String format(final String source) {
639639
break;
640640

641641
case '"':
642+
case '“':
643+
case '”':
642644
case '\'':
645+
case '‘':
646+
case '’':
643647
inStatementFlag = true;
644-
buf.append(c);
648+
char realQuote = c;
649+
if (c == '“' || c == '”') realQuote = '"';
650+
if (c == '‘' || c == '’') realQuote = '\'';
651+
buf.append(realQuote);
652+
653+
char otherQuote = c;
654+
if (c == '“') otherQuote = '”';
655+
if (c == '”') otherQuote = '“';
656+
if (c == '‘') otherQuote = '’';
657+
if (c == '’') otherQuote = '‘';
658+
645659
char cc = nextChar();
646-
while (!EOF && cc != c) {
660+
// In a proper string, all the quotes tested are c. In a curly-quoted
661+
// string, there are three possible end quotes: c, its reverse, and
662+
// the correct straight quote.
663+
while (!EOF && cc != otherQuote && cc != realQuote && cc != c) {
647664
buf.append(cc);
648665
if (cc == '\\') {
649666
buf.append(cc = nextChar());
650667
}
651-
if (cc == '\n') {
652-
writeIndentedLine();
653-
startFlag = true;
654-
}
668+
669+
// Syntax error: unterminated string. Leave \n in nextChar, so it
670+
// feeds back into the loop.
671+
if (peek() == '\n') break;
655672
cc = nextChar();
656673
}
657-
buf.append(cc);
658-
if (readForNewLine()) {
659-
// push a newline into the stream
660-
chars[pos--] = '\n';
674+
if (cc == otherQuote || cc == realQuote || cc == c) {
675+
buf.append(realQuote);
676+
if (readForNewLine()) {
677+
// push a newline into the stream
678+
chars[pos--] = '\n';
679+
}
680+
} else {
681+
// We've had a syntax error if the string wasn't terminated by EOL/
682+
// EOF, just abandon this statement.
683+
inStatementFlag = false;
661684
}
662685
break;
663686

java/src/processing/mode/java/JavaBuild.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.apache.tools.ant.ProjectHelper;
3939

4040
import processing.app.Base;
41+
import processing.app.Language;
4142
import processing.app.Library;
4243
import processing.app.Messages;
4344
import processing.app.Mode;
@@ -345,9 +346,30 @@ public String preprocess(File srcFolder,
345346

346347
// System.err.println("and then she tells me " + tsre.toString());
347348
// TODO not tested since removing ORO matcher.. ^ could be a problem
348-
String mess = "^line (\\d+):(\\d+):\\s";
349+
String locationRegex = "^line (\\d+):(\\d+):\\s";
350+
String message = tsre.getMessage();
351+
String[] m;
352+
353+
if (null != (m = PApplet.match(tsre.toString(),
354+
"unexpected char: (.*)"))) {
355+
char c = 0;
356+
if (m[1].startsWith("0x")) { // Hex
357+
c = (char) PApplet.unhex(m[1].substring(2));
358+
} else if (m[1].length() == 3) { // Quoted
359+
c = m[1].charAt(1);
360+
} else if (m[1].length() == 1) { // Alone
361+
c = m[1].charAt(0);
362+
}
363+
if (c == '\u201C' || c == '\u201D' || // “”
364+
c == '\u2018' || c == '\u2019') { // ‘’
365+
message = Language.interpolate("editor.status.bad_curly_quote", c);
366+
} else if (c != 0) {
367+
message = "Not expecting symbol " + m[1] +
368+
", which is " + Character.getName(c) + ".";
369+
}
370+
}
349371

350-
String[] matches = PApplet.match(tsre.toString(), mess);
372+
String[] matches = PApplet.match(tsre.toString(), locationRegex);
351373
if (matches != null) {
352374
int errorLine = Integer.parseInt(matches[1]) - 1;
353375
int errorColumn = Integer.parseInt(matches[2]);
@@ -362,7 +384,7 @@ public String preprocess(File srcFolder,
362384
}
363385
errorLine -= sketch.getCode(errorFile).getPreprocOffset();
364386

365-
throw new SketchException(tsre.getMessage(),
387+
throw new SketchException(message,
366388
errorFile, errorLine, errorColumn);
367389

368390
} else {

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: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,17 @@ public JavaProblem(String message, int type, int tabIndex, int lineNumber) {
7272
* @param iProblem - The IProblem which is being wrapped
7373
* @param tabIndex - The tab number to which the error belongs to
7474
* @param lineNumber - Line number(pde code) of the error
75+
* @param badCode - The code iProblem refers to.
7576
*/
76-
public static JavaProblem fromIProblem(IProblem iProblem, int tabIndex, int lineNumber) {
77+
public static JavaProblem fromIProblem(IProblem iProblem,
78+
int tabIndex, int lineNumber, String badCode) {
7779
int type = 0;
7880
if(iProblem.isError()) {
7981
type = ERROR;
8082
} else if (iProblem.isWarning()) {
8183
type = WARNING;
8284
}
83-
String message = ErrorMessageSimplifier.getSimplifiedErrorMessage(iProblem);
85+
String message = ErrorMessageSimplifier.getSimplifiedErrorMessage(iProblem, badCode);
8486
return new JavaProblem(message, type, tabIndex, lineNumber);
8587
}
8688

0 commit comments

Comments
 (0)