Skip to content

Commit c7b6ba6

Browse files
authored
Merge pull request #1 from JakubValtar/GKFX-featurefixbadquotes-pull
Curly quotes followup
2 parents 6ed11f4 + 71b8e5f commit c7b6ba6

File tree

7 files changed

+167
-171
lines changed

7 files changed

+167
-171
lines changed

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

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3120,20 +3120,14 @@ public void updateEditorStatus() {
31203120

31213121
/**
31223122
* @return the Problem for the most relevant error or warning on 'line',
3123-
* defaulting to the first.
3123+
* defaults to the first error, if there are no errors first warning.
31243124
*/
31253125
protected Problem findProblem(int line) {
3126-
int currentTab = getSketch().getCurrentCodeIndex();
3127-
return problems.stream()
3128-
.filter(p -> p.getTabIndex() == currentTab)
3129-
.filter(p -> {
3130-
int pStartLine = p.getLineNumber();
3131-
int pEndOffset = p.getStopOffset();
3132-
int pEndLine = textarea.getLineOfOffset(pEndOffset);
3133-
return line >= pStartLine && line <= pEndLine;
3134-
})
3135-
.findFirst()
3136-
.orElse(null);
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);
31373131
}
31383132

31393133

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

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2285,26 +2285,6 @@ 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-
23082288
/**
23092289
* Updates the error table in the Error Window.
23102290
* Overridden to handle the fugly import suggestions text.

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

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

2121
package processing.mode.java.pdex;
2222

23-
import java.util.Arrays;
24-
2523
import org.eclipse.jdt.core.compiler.IProblem;
26-
import static org.eclipse.jdt.core.compiler.IProblem.*;
2724

2825
import processing.app.Problem;
2926

@@ -56,65 +53,18 @@ public class JavaProblem implements Problem {
5653
*/
5754
private int type;
5855

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-
10556
/**
10657
* If the error is a 'cannot find type' contains the list of suggested imports
10758
*/
10859
private String[] importSuggestions;
10960

11061
public static final int ERROR = 1, WARNING = 2;
11162

112-
public JavaProblem(String message, int type, int tabIndex, int lineNumber, int priority) {
63+
public JavaProblem(String message, int type, int tabIndex, int lineNumber) {
11364
this.message = message;
11465
this.type = type;
11566
this.tabIndex = tabIndex;
11667
this.lineNumber = lineNumber;
117-
this.priority = priority;
11868
}
11969

12070
/**
@@ -127,24 +77,13 @@ public JavaProblem(String message, int type, int tabIndex, int lineNumber, int p
12777
public static JavaProblem fromIProblem(IProblem iProblem,
12878
int tabIndex, int lineNumber, String badCode) {
12979
int type = 0;
130-
int priority = 0;
131-
if (iProblem.isError()) {
80+
if(iProblem.isError()) {
13281
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-
}
14282
} else if (iProblem.isWarning()) {
14383
type = WARNING;
144-
priority = 4;
14584
}
14685
String message = ErrorMessageSimplifier.getSimplifiedErrorMessage(iProblem, badCode);
147-
return new JavaProblem(message, type, tabIndex, lineNumber, priority);
86+
return new JavaProblem(message, type, tabIndex, lineNumber);
14887
}
14988

15089
public void setPDEOffsets(int startOffset, int stopOffset){
@@ -195,10 +134,6 @@ public void setImportSuggestions(String[] a) {
195134
importSuggestions = a;
196135
}
197136

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

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

Lines changed: 134 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import java.util.concurrent.atomic.AtomicReference;
4949
import java.util.function.Consumer;
5050
import java.util.function.Predicate;
51+
import java.util.regex.Matcher;
5152
import java.util.regex.Pattern;
5253
import java.util.stream.Collectors;
5354

@@ -1083,32 +1084,18 @@ private void handleSketchProblems(PreprocessedSketch ps) {
10831084

10841085
IProblem[] iproblems = ps.compilationUnit.getProblems();
10851086

1086-
{ // Handle missing brace problems
1087-
IProblem missingBraceProblem = Arrays.stream(iproblems)
1088-
.filter(ErrorChecker::isMissingBraceProblem)
1089-
.findFirst()
1090-
// Ignore if it is at the end of file
1091-
.filter(p -> p.getSourceEnd() + 1 < ps.javaCode.length())
1092-
// Ignore if the tab number does not match our detected tab number
1093-
.filter(p -> ps.missingBraceProblems.isEmpty() ||
1094-
ps.missingBraceProblems.get(0).getTabIndex() ==
1095-
ps.mapJavaToSketch(p.getSourceStart(), p.getSourceEnd()+1).tabIndex
1096-
)
1097-
.orElse(null);
1098-
1099-
// If there is missing brace ignore all other problems
1100-
if (missingBraceProblem != null) {
1101-
// Prefer ECJ problem, shows location more accurately
1102-
iproblems = new IProblem[]{missingBraceProblem};
1103-
} else if (!ps.missingBraceProblems.isEmpty()) {
1104-
// Fallback to manual detection
1105-
problems.addAll(ps.missingBraceProblems);
1106-
}
1087+
{ // Check for curly quotes
1088+
List<JavaProblem> curlyQuoteProblems = checkForCurlyQuotes(ps);
1089+
problems.addAll(curlyQuoteProblems);
11071090
}
11081091

1109-
AtomicReference<ClassPath> searchClassPath = new AtomicReference<>(null);
1092+
if (problems.isEmpty()) { // Check for missing braces
1093+
List<JavaProblem> missingBraceProblems = checkForMissingBraces(ps);
1094+
problems.addAll(missingBraceProblems);
1095+
}
11101096

11111097
if (problems.isEmpty()) {
1098+
AtomicReference<ClassPath> searchClassPath = new AtomicReference<>(null);
11121099
List<Problem> cuProblems = Arrays.stream(iproblems)
11131100
// Filter Warnings if they are not enabled
11141101
.filter(iproblem -> !(iproblem.isWarning() && !JavaMode.warningsEnabled))
@@ -1121,19 +1108,10 @@ private void handleSketchProblems(PreprocessedSketch ps) {
11211108
.contains("Syntax error, insert \":: IdentifierOrNew\""))
11221109
// Transform into our Problems
11231110
.map(iproblem -> {
1124-
int start = iproblem.getSourceStart();
1125-
int stop = iproblem.getSourceEnd() + 1; // make it exclusive
1126-
SketchInterval in = ps.mapJavaToSketch(start, stop);
1127-
if (in == SketchInterval.BEFORE_START) return null;
1128-
int line = ps.tabOffsetToTabLine(in.tabIndex, in.startTabOffset);
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);
1133-
p.setPDEOffsets(in.startTabOffset, in.stopTabOffset);
1111+
JavaProblem p = convertIProblem(iproblem, ps);
11341112

11351113
// Handle import suggestions
1136-
if (JavaMode.importSuggestEnabled && isUndefinedTypeProblem(iproblem)) {
1114+
if (p != null && JavaMode.importSuggestEnabled && isUndefinedTypeProblem(iproblem)) {
11371115
ClassPath cp = searchClassPath.updateAndGet(prev -> prev != null ?
11381116
prev : new ClassPathFactory().createFromPaths(ps.searchClassPathArray));
11391117
String[] s = suggCache.computeIfAbsent(iproblem.getArguments()[0],
@@ -1163,6 +1141,16 @@ private void handleSketchProblems(PreprocessedSketch ps) {
11631141
TimeUnit.MILLISECONDS);
11641142
}
11651143

1144+
static private JavaProblem convertIProblem(IProblem iproblem, PreprocessedSketch ps) {
1145+
SketchInterval in = ps.mapJavaToSketch(iproblem);
1146+
if (in == SketchInterval.BEFORE_START) return null;
1147+
String badCode = ps.getPdeCode(in);
1148+
int line = ps.tabOffsetToTabLine(in.tabIndex, in.startTabOffset);
1149+
JavaProblem p = JavaProblem.fromIProblem(iproblem, in.tabIndex, line, badCode);
1150+
p.setPDEOffsets(in.startTabOffset, in.stopTabOffset);
1151+
return p;
1152+
}
1153+
11661154

11671155
static private boolean isUndefinedTypeProblem(IProblem iproblem) {
11681156
int id = iproblem.getID();
@@ -1188,6 +1176,119 @@ static private boolean isMissingBraceProblem(IProblem iproblem) {
11881176
}
11891177

11901178

1179+
private static final Pattern CURLY_QUOTE_REGEX =
1180+
Pattern.compile("([“”‘’])", Pattern.UNICODE_CHARACTER_CLASS);
1181+
1182+
static private List<JavaProblem> checkForCurlyQuotes(PreprocessedSketch ps) {
1183+
List<JavaProblem> problems = new ArrayList<>(0);
1184+
1185+
// Go through the scrubbed code and look for curly quotes (they should not be any)
1186+
Matcher matcher = CURLY_QUOTE_REGEX.matcher(ps.scrubbedPdeCode);
1187+
while (matcher.find()) {
1188+
int pdeOffset = matcher.start();
1189+
String q = matcher.group();
1190+
1191+
int tabIndex = ps.pdeOffsetToTabIndex(pdeOffset);
1192+
int tabOffset = ps.pdeOffsetToTabOffset(tabIndex, pdeOffset);
1193+
int tabLine = ps.tabOffsetToTabLine(tabIndex, tabOffset);
1194+
1195+
String message = Language.interpolate("editor.status.bad_curly_quote", q);
1196+
JavaProblem problem = new JavaProblem(message, JavaProblem.ERROR, tabIndex, tabLine);
1197+
problem.setPDEOffsets(tabOffset, tabOffset+1);
1198+
1199+
problems.add(problem);
1200+
}
1201+
1202+
1203+
// Go through iproblems and look for problems involving curly quotes
1204+
List<JavaProblem> problems2 = new ArrayList<>(0);
1205+
IProblem[] iproblems = ps.compilationUnit.getProblems();
1206+
1207+
for (IProblem iproblem : iproblems) {
1208+
switch (iproblem.getID()) {
1209+
case IProblem.ParsingErrorDeleteToken:
1210+
case IProblem.ParsingErrorDeleteTokens:
1211+
case IProblem.ParsingErrorInvalidToken:
1212+
case IProblem.ParsingErrorReplaceTokens:
1213+
case IProblem.UnterminatedString:
1214+
SketchInterval in = ps.mapJavaToSketch(iproblem);
1215+
if (in == SketchInterval.BEFORE_START) continue;
1216+
String badCode = ps.getPdeCode(in);
1217+
matcher.reset(badCode);
1218+
while (matcher.find()) {
1219+
int offset = matcher.start();
1220+
String q = matcher.group();
1221+
int tabStart = in.startTabOffset + offset;
1222+
int tabStop = tabStart + 1;
1223+
// Prevent duplicate problems
1224+
if (problems.stream().noneMatch(p -> p.getStartOffset() == tabStart)) {
1225+
int line = ps.tabOffsetToTabLine(in.tabIndex, tabStart);
1226+
String message;
1227+
if (iproblem.getID() == IProblem.UnterminatedString) {
1228+
message = Language.interpolate("editor.status.unterm_string_curly", q);
1229+
} else {
1230+
message = Language.interpolate("editor.status.bad_curly_quote", q);
1231+
}
1232+
JavaProblem p = new JavaProblem(message, JavaProblem.ERROR, in.tabIndex, line);
1233+
p.setPDEOffsets(tabStart, tabStop);
1234+
problems2.add(p);
1235+
}
1236+
}
1237+
}
1238+
}
1239+
1240+
problems.addAll(problems2);
1241+
1242+
return problems;
1243+
}
1244+
1245+
1246+
static private List<JavaProblem> checkForMissingBraces(PreprocessedSketch ps) {
1247+
List<JavaProblem> problems = new ArrayList<>(0);
1248+
for (int tabIndex = 0; tabIndex < ps.tabStartOffsets.length; tabIndex++) {
1249+
int tabStartOffset = ps.tabStartOffsets[tabIndex];
1250+
int tabEndOffset = (tabIndex < ps.tabStartOffsets.length - 1) ?
1251+
ps.tabStartOffsets[tabIndex + 1] : ps.scrubbedPdeCode.length();
1252+
int[] braceResult = SourceUtils.checkForMissingBraces(ps.scrubbedPdeCode, tabStartOffset, tabEndOffset);
1253+
if (braceResult[0] != 0) {
1254+
JavaProblem problem =
1255+
new JavaProblem(braceResult[0] < 0
1256+
? Language.interpolate("editor.status.missing.left_curly_bracket")
1257+
: Language.interpolate("editor.status.missing.right_curly_bracket"),
1258+
JavaProblem.ERROR, tabIndex, braceResult[1]);
1259+
problem.setPDEOffsets(braceResult[3], braceResult[3] + 1);
1260+
problems.add(problem);
1261+
}
1262+
}
1263+
1264+
if (problems.isEmpty()) {
1265+
return problems;
1266+
}
1267+
1268+
int problemTabIndex = problems.get(0).getTabIndex();
1269+
1270+
IProblem missingBraceProblem = Arrays.stream(ps.compilationUnit.getProblems())
1271+
.filter(ErrorChecker::isMissingBraceProblem)
1272+
// Ignore if it is at the end of file
1273+
.filter(p -> p.getSourceEnd() + 1 < ps.javaCode.length())
1274+
// Ignore if the tab number does not match our detected tab number
1275+
.filter(p -> problemTabIndex == ps.mapJavaToSketch(p).tabIndex)
1276+
.findFirst()
1277+
.orElse(null);
1278+
1279+
// Prefer ECJ problem, shows location more accurately
1280+
if (missingBraceProblem != null) {
1281+
JavaProblem p = convertIProblem(missingBraceProblem, ps);
1282+
if (p != null) {
1283+
problems.clear();
1284+
problems.add(p);
1285+
}
1286+
}
1287+
1288+
return problems;
1289+
}
1290+
1291+
11911292
static public String[] getImportSuggestions(ClassPath cp, String className) {
11921293
RegExpResourceFilter regf = new RegExpResourceFilter(
11931294
Pattern.compile(".*"),

0 commit comments

Comments
 (0)