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.
This commit is contained in:
George Bateman
2017-06-24 14:35:18 +01:00
parent 96a042f873
commit cd7430fc10
7 changed files with 153 additions and 23 deletions

View File

@@ -3106,9 +3106,10 @@ public abstract class Editor extends JFrame implements RunnerListener {
/**
* @return the Problem for the first error or warning on 'line'
* @return the Problem for the most relevant error or warning on 'line',
* defaulting to the first.
*/
Problem findProblem(int line) {
protected Problem findProblem(int line) {
int currentTab = getSketch().getCurrentCodeIndex();
return problems.stream()
.filter(p -> p.getTabIndex() == currentTab)

View File

@@ -370,6 +370,7 @@ editor.status.undef_global_var = The global variable "%s" does not exist
editor.status.undef_class = The class "%s" does not exist
editor.status.undef_var = The variable "%s" does not exist
editor.status.undef_name = The name "%s" cannot be recognized
editor.status.unterm_string_curly = String literal is not closed by a straight double quote. Curly quotes like %s won't help.
editor.status.type_mismatch = Type mismatch, "%s" does not match with "%s"
editor.status.unused_variable = The value of the local variable "%s" is not used
editor.status.uninitialized_variable = The local variable "%s" may not have been initialized

View File

@@ -2285,6 +2285,26 @@ public class JavaEditor extends Editor {
}
/**
* @return the Problem for the most relevant error or warning on 'line',
* defaulting to the first.
*/
@Override
protected Problem findProblem(int line) {
List<Problem> l = findProblems(line);
if (l.size() == 0) return null;
Problem worst = l.get(0);
for (Problem p : l) {
if (p instanceof JavaProblem && ((!(worst instanceof JavaProblem)) ||
((JavaProblem)p).getPriority() > ((JavaProblem)worst).getPriority())) {
worst = p;
}
}
return worst;
}
/**
* Updates the error table in the Error Window.
* Overridden to handle the fugly import suggestions text.

View File

@@ -85,7 +85,7 @@ public class ErrorMessageSimplifier {
/**
* Tones down the jargon in the ecj reported errors.
*/
public static String getSimplifiedErrorMessage(IProblem iprob) {
public static String getSimplifiedErrorMessage(IProblem iprob, String badCode) {
if (iprob == null) return null;
String args[] = iprob.getArguments();
@@ -97,6 +97,7 @@ public class ErrorMessageSimplifier {
for (String arg : args) {
Messages.log("Arg " + arg);
}
Messages.log("Bad code: " + badCode);
}
String result = null;
@@ -111,6 +112,15 @@ public class ErrorMessageSimplifier {
case IProblem.ParsingErrorDeleteToken:
if (args.length > 0) {
if (args[0].equalsIgnoreCase("Invalid Character")) {
result = getErrorMessageForCurlyQuote(badCode);
}
}
break;
case IProblem.ParsingErrorDeleteTokens:
result = getErrorMessageForCurlyQuote(badCode);
if (result == null) {
result = Language.interpolate("editor.status.error_on", args[0]);
}
break;
@@ -136,13 +146,16 @@ public class ErrorMessageSimplifier {
case IProblem.ParsingErrorInvalidToken:
if (args.length > 0) {
if (args[1].equals("VariableDeclaratorId")) {
if (args[0].equals("int")) {
if (args[0].equals("int")) {
if (args[1].equals("VariableDeclaratorId")) {
result = Language.text("editor.status.reserved_words");
} else {
result = Language.interpolate("editor.status.error_on", args[0]);
}
} else {
} else if (args[0].equalsIgnoreCase("Invalid Character")) {
result = getErrorMessageForCurlyQuote(badCode);
}
if (result == null) {
result = Language.interpolate("editor.status.error_on", args[0]);
}
}
@@ -165,6 +178,9 @@ public class ErrorMessageSimplifier {
}
break;
case IProblem.ParsingErrorReplaceTokens:
result = getErrorMessageForCurlyQuote(badCode);
case IProblem.UndefinedConstructor:
if (args.length == 2) {
String constructorName = args[0];
@@ -230,6 +246,13 @@ public class ErrorMessageSimplifier {
}
break;
case IProblem.UnterminatedString:
if (badCode.contains("") || badCode.contains("")) {
result = Language.interpolate("editor.status.unterm_string_curly",
badCode.replaceAll("[^“”]", ""));
}
break;
case IProblem.TypeMismatch:
if (args.length > 1) {
result = Language.interpolate("editor.status.type_mismatch", args[0], args[1]);
@@ -261,16 +284,17 @@ public class ErrorMessageSimplifier {
result = Language.interpolate("editor.status.hiding_enclosing_type", args[0]);
}
break;
}
default:
String message = iprob.getMessage();
if (message != null) {
// Remove all instances of token
// "Syntax error on token 'blah', delete this token"
Matcher matcher = tokenRegExp.matcher(message);
message = matcher.replaceAll("");
result = message;
}
if (result == null) {
String message = iprob.getMessage();
if (message != null) {
// Remove all instances of token
// "Syntax error on token 'blah', delete this token"
Matcher matcher = tokenRegExp.matcher(message);
message = matcher.replaceAll("");
result = message;
}
}
if (DEBUG) {
@@ -323,6 +347,20 @@ public class ErrorMessageSimplifier {
}
/**
* @param badCode The code which may contain curly quotes
* @return Friendly error message if there is a curly quote in badCode,
* null otherwise.
*/
static private String getErrorMessageForCurlyQuote(String badCode) {
if (badCode.contains("") || badCode.contains("") ||
badCode.contains("") || badCode.contains("")) {
return Language.interpolate("editor.status.bad_curly_quote",
badCode.replaceAll("[^‘’“”]", ""));
} else return null;
}
// static private final String q(Object quotable) {
// return "\"" + quotable + "\"";
// }

View File

@@ -20,7 +20,10 @@ along with this program; if not, write to the Free Software Foundation, Inc.
package processing.mode.java.pdex;
import java.util.Arrays;
import org.eclipse.jdt.core.compiler.IProblem;
import static org.eclipse.jdt.core.compiler.IProblem.*;
import processing.app.Problem;
@@ -53,6 +56,52 @@ public class JavaProblem implements Problem {
*/
private int type;
/**
* Priority: bigger = higher. Currently 7 to 10 for errors,
* 4 for warning.
* <p>
* The logic of the numbers in the priorityN arrays is that if ECJ wants a
* token got rid of entirely it's most likely the root of the problem. If it
* wants more tokens, that might have been caused by an unterminated string
* or something but it's also likely to be the “real” error. Only if the
* syntax is good are mismatched argument lists and so on of any real
* significance. These rankings are entirely made up so can be changed to
* support any other plausible scenario.
*/
private int priority;
static private final int[] priority10 = {
ParsingError,
ParsingErrorDeleteToken,
ParsingErrorDeleteTokens,
ParsingErrorInvalidToken,
ParsingErrorMergeTokens,
ParsingErrorMisplacedConstruct,
ParsingErrorNoSuggestion,
ParsingErrorNoSuggestionForTokens,
ParsingErrorOnKeyword,
ParsingErrorOnKeywordNoSuggestion,
ParsingErrorReplaceTokens,
ParsingErrorUnexpectedEOF
};
static private final int[] priority9 = {
InvalidCharacterConstant,
UnterminatedString
};
static private final int[] priority8 = {
ParsingErrorInsertToComplete,
ParsingErrorInsertToCompletePhrase,
ParsingErrorInsertToCompleteScope,
ParsingErrorInsertTokenAfter,
ParsingErrorInsertTokenBefore,
};
// Sorted so I can do a one-line binary search later.
static {
Arrays.sort(priority10);
Arrays.sort(priority9);
Arrays.sort(priority8);
}
/**
* If the error is a 'cannot find type' contains the list of suggested imports
*/
@@ -60,11 +109,12 @@ public class JavaProblem implements Problem {
public static final int ERROR = 1, WARNING = 2;
public JavaProblem(String message, int type, int tabIndex, int lineNumber) {
public JavaProblem(String message, int type, int tabIndex, int lineNumber, int priority) {
this.message = message;
this.type = type;
this.tabIndex = tabIndex;
this.lineNumber = lineNumber;
this.priority = priority;
}
/**
@@ -72,16 +122,29 @@ public class JavaProblem implements Problem {
* @param iProblem - The IProblem which is being wrapped
* @param tabIndex - The tab number to which the error belongs to
* @param lineNumber - Line number(pde code) of the error
* @param badCode - The code iProblem refers to.
*/
public static JavaProblem fromIProblem(IProblem iProblem, int tabIndex, int lineNumber) {
public static JavaProblem fromIProblem(IProblem iProblem,
int tabIndex, int lineNumber, String badCode) {
int type = 0;
if(iProblem.isError()) {
int priority = 0;
if (iProblem.isError()) {
type = ERROR;
if (Arrays.binarySearch(priority10, iProblem.getID()) >= 0) {
priority = 10;
} else if (Arrays.binarySearch(priority9, iProblem.getID()) >= 0) {
priority = 9;
} else if (Arrays.binarySearch(priority8, iProblem.getID()) >= 0) {
priority = 8;
} else {
priority = 7;
}
} else if (iProblem.isWarning()) {
type = WARNING;
priority = 4;
}
String message = ErrorMessageSimplifier.getSimplifiedErrorMessage(iProblem);
return new JavaProblem(message, type, tabIndex, lineNumber);
String message = ErrorMessageSimplifier.getSimplifiedErrorMessage(iProblem, badCode);
return new JavaProblem(message, type, tabIndex, lineNumber, priority);
}
public void setPDEOffsets(int startOffset, int stopOffset){
@@ -132,6 +195,10 @@ public class JavaProblem implements Problem {
importSuggestions = a;
}
public int getPriority() {
return priority;
}
@Override
public String toString() {
return "TAB " + tabIndex + ",LN " + lineNumber + "LN START OFF: "

View File

@@ -1126,7 +1126,10 @@ public class PDEX {
SketchInterval in = ps.mapJavaToSketch(start, stop);
if (in == SketchInterval.BEFORE_START) return null;
int line = ps.tabOffsetToTabLine(in.tabIndex, in.startTabOffset);
JavaProblem p = JavaProblem.fromIProblem(iproblem, in.tabIndex, line);
ps.sketch.updateSketchCodes(); // seems to be needed
String badCode = ps.sketch.getCode(in.tabIndex).getProgram()
.substring(in.startTabOffset, in.stopTabOffset);
JavaProblem p = JavaProblem.fromIProblem(iproblem, in.tabIndex, line, badCode);
p.setPDEOffsets(in.startTabOffset, in.stopTabOffset);
// Handle import suggestions

View File

@@ -355,7 +355,7 @@ public class SourceUtils {
if (depth < 0) {
JavaProblem problem =
new JavaProblem("Found one too many } characters without { to match it.",
JavaProblem.ERROR, tabIndex, lineNumber);
JavaProblem.ERROR, tabIndex, lineNumber, 8);
problem.setPDEOffsets(i - tabStartOffset, i - tabStartOffset + 1);
problems.add(problem);
continue tabLoop;
@@ -364,7 +364,7 @@ public class SourceUtils {
if (depth > 0) {
JavaProblem problem =
new JavaProblem("Found one too many { characters without } to match it.",
JavaProblem.ERROR, tabIndex, lineNumber - 1);
JavaProblem.ERROR, tabIndex, lineNumber - 1, 8);
problem.setPDEOffsets(tabEndOffset - tabStartOffset - 2, tabEndOffset - tabStartOffset - 1);
problems.add(problem);
}