diff --git a/java/src/processing/mode/java/ProblemFactory.java b/java/src/processing/mode/java/ProblemFactory.java index 88caa5cb21..e7ec067296 100644 --- a/java/src/processing/mode/java/ProblemFactory.java +++ b/java/src/processing/mode/java/ProblemFactory.java @@ -6,7 +6,7 @@ import processing.app.Problem; import processing.app.ui.Editor; -import processing.mode.java.preproc.issue.PdePreprocessIssue; +import processing.mode.java.preproc.PdePreprocessIssue; /** diff --git a/java/src/processing/mode/java/preproc/PdeIssueEmitter.java b/java/src/processing/mode/java/preproc/PdeIssueEmitter.java new file mode 100644 index 0000000000..7e5b48c5aa --- /dev/null +++ b/java/src/processing/mode/java/preproc/PdeIssueEmitter.java @@ -0,0 +1,357 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2019 The Processing Foundation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.mode.java.preproc; + +import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; +import processing.mode.java.SourceUtil; + +import java.util.Optional; + + +/** + * ANTLR error listener to inform a preprocess issue listener when syntax errors are encountered. + * + *

+ * A {BaseErrorListener} which looks for syntax errors reported by ANTLR and converts them to + * {PdePreprocessIssue}s that are consumable by a {PdePreprocessIssueListener}. It does this by + * running the {PreprocessIssueMessageSimplifier} to generate a more user-friendly error message + * before informing the provided listener. + *

+ */ +public class PdeIssueEmitter extends BaseErrorListener { + + private final PdePreprocessIssueListener listener; + private final Optional sourceMaybe; + + /** + * Create a new issue emitter. + * + *

+ * Create a new issue emitter when access to the processing sketch source is not available. + * Note that this will not allow some error beautification and, if sketch source is available, + * use other constructor. + *

+ * + * @param newListener The listener to inform when encountering a syntax error. + */ + public PdeIssueEmitter(PdePreprocessIssueListener newListener) { + listener = newListener; + sourceMaybe = Optional.empty(); + } + + /** + * Create a new issue emitter. + * + * @param newListener The listener to inform when encountering a syntax error. + * @param newSourceEmitter The sketch source to use when helping beautify certain syntax error + * messages. + */ + public PdeIssueEmitter(PdePreprocessIssueListener newListener, SourceEmitter newSourceEmitter) { + listener = newListener; + sourceMaybe = Optional.of(newSourceEmitter); + } + + @Override + public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, + int charPositionInLine, String msg, RecognitionException e) { + + PreprocessIssueMessageSimplifier facade = PreprocessIssueMessageSimplifier.get(); + IssueMessageSimplification simplification = facade.simplify(msg); + + IssueLocation issueLocation; + + if (sourceMaybe.isPresent()) { + issueLocation = IssueLocationFactory.getLineWithOffset( + simplification, + line, + charPositionInLine, + sourceMaybe.get().getSource() + ); + } else { + issueLocation = new IssueLocation(line, charPositionInLine); + } + + listener.onIssue(new PdePreprocessIssue( + issueLocation.getLine(), + issueLocation.getCharPosition(), + simplification.getMessage() + )); + } + + /** + * Simple interface for strategy which can emit the full body of a processing sketch. + */ + public static interface SourceEmitter { + + /** + * Get the full body of the processing sketch. + * + * @return String processing sketch source code across all tabs. + */ + String getSource(); + + } + + /** + * Interface for listener that responds to issues reported by the preprocessor. + */ + public static interface PdePreprocessIssueListener { + + /** + * Callback to invoke when an issue is encountered in preprocesing. + * + * @param issue Description of the issue. + */ + void onIssue(PdePreprocessIssue issue); + + } + + /** + * ================================ + * == Supporting data structures == + * ================================ + */ + + /** + * Data structure describing an issue simplification or explanation. + * + *

+ * Data structure describing an edit that was made to an error message or warning to be shown to + * the user based on a series of rules that attempts to make error messages easier to understand + * for the user. + *

+ */ + public static class IssueMessageSimplification { + + private final String message; + private final boolean attributeToPriorToken; + + /** + * Create a new issue message simplification. + * + *

+ * Create a new issue message simplification that leaves the token attribution alone (the token + * on which the error was reported will be the same before error message simplification). + *

+ * + * @param newMessage The message to show to the user. + */ + public IssueMessageSimplification(String newMessage) { + message = newMessage; + attributeToPriorToken = false; + } + + /** + * Create a new issue message simplification. + * + *

+ * Create a new issue message simplification. Note that there is an option to have the error + * attributed to the "prior token". This is helpful, for example, when a semicolon is missing. + * The error is generated on the token after the line on which the semicolon was omitted so, + * while the error technically emerges on the next line, it is better for the user for it to + * appear earlier. Specifically, it is most sensible for it to appear on the "prior token". + *

+ * + * @param newMessage The message to show to the user. + * @param newAttributeToPriorToken Boolean flag indicating if the error should be shown on the + * token prior to the one on which the error was originally generated. True if the error should + * be attributed to the prior token. False otherwise. + */ + public IssueMessageSimplification(String newMessage, boolean newAttributeToPriorToken) { + message = newMessage; + attributeToPriorToken = newAttributeToPriorToken; + } + + /** + * Get the error message text that should be shown to the user. + * + * @return The error message text that should be shown to the user. + */ + public String getMessage() { + return message; + } + + /** + * Flag indicating if the error should be attributed to the prior token. + * + * @return True if the error should be attributed to the prior non-skip token (not whitepsace or + * comment). This is useful when a mistake on a prior line like omitted semicolon causes an + * error on a later line but one wants error highlighting closer to the mistake itself. False + * if the error should be attributed to the original offending token. + */ + public boolean getAttributeToPriorToken() { + return attributeToPriorToken; + } + + } + + /** + * Data structure describing where an issue occurred. + */ + public static class IssueLocation { + + private final int line; + private final int charPosition; + + /** + * Create a new issue location structure. + * + * @param newLine The line (1-indexed) where the issue occurred. This should be in the global file + * generated by the preprocessor and not relative to the start of the tab. + * @param newCharPosition The position on the line. + */ + public IssueLocation(int newLine, int newCharPosition) { + line = newLine; + charPosition = newCharPosition; + } + + /** + * Get the 1-indexed line on which this error occurred. + * + * @return The line on which this error occurred. Note that this will be relative to the global + * file generated by the preprocessor and not relative to the start of the tab. + */ + public int getLine() { + return line; + } + + /** + * The the position of the error within the line. + * + * @return The number of characters including whitespace from the start of the line at which the + * error occurred. + */ + public int getCharPosition() { + return charPosition; + } + + } + + /** + * ===================== + * == Utility classes == + * ===================== + */ + + /** + * Utility that can help clean up where in source an issue should be reported. + * + *

+ * For some errors, the location of the "mistake" does not appear close to where the actual error + * is generated. For example, consider omitting a semicolon. Though the "mistake" is arguably on + * the line on which a semicolon is forgotten, the grammatical error appears in the first + * non-skip token after the omitted character. This means that the issue shown to the user may + * be far away from the line they would want to edit. This utility helps determine if an issue + * requires a new location and, if so, where the location should be. + *

+ */ + public static class IssueLocationFactory { + + /** + * Determine where an issue should be reported. + * + * @param simplification The issue simplification generated from {PreprocessIssueMessageSimplifier}. + * @param originalLine The original line (1 indexed) on which the issue was reported. + * @param originalOffset The original number of characters from the start of the line where the + * the issue was reported. + * @param source The full concatenated source of the sketch being built. + * @param lineCount The total + * @return The new location where the issue should be reported. This may be identical to the + * original location if the issue was not moved. + */ + public static IssueLocation getLineWithOffset(IssueMessageSimplification simplification, + int originalLine, int originalOffset, String source) { + + // Determine if the issue should be relocated + boolean shouldAttributeToPrior = simplification.getAttributeToPriorToken(); + shouldAttributeToPrior = shouldAttributeToPrior && originalLine != 0; + + if (!shouldAttributeToPrior) { + return new IssueLocation(originalLine, originalOffset); + } + + // Find the code prior the issue + String priorCode = getContentsUpToLine(source, originalLine); + + // Find the token immediately prior to the issue + PreprocessIssueMessageSimplifier.PriorTokenFinder finder = new PreprocessIssueMessageSimplifier.PriorTokenFinder(); + int charPos = priorCode.length(); + while (!finder.isDone() && charPos > 0) { + charPos--; + finder.step(priorCode.charAt(charPos)); + } + + // Find the location offset depending on if the prior token could be found + Optional foundStartOfMatchMaybe = finder.getTokenPositionMaybe(); + int startOfMatch; + int linesOffset; + + if (foundStartOfMatchMaybe.isPresent()) { + startOfMatch = priorCode.length() - foundStartOfMatchMaybe.get(); + String contentsOfMatch = priorCode.substring(startOfMatch); + linesOffset = SourceUtil.getCount(contentsOfMatch, "\n"); + } else { + startOfMatch = priorCode.length(); + linesOffset = 0; + } + + // Apply the location offset and highlight to the end of the line + String contentsPriorToMatch = priorCode.substring(0, startOfMatch); + int newLine = originalLine - linesOffset; + int lengthIncludingLine = contentsPriorToMatch.length(); + int lengthExcludingLine = contentsPriorToMatch.lastIndexOf('\n'); + int lineLength = lengthIncludingLine - lengthExcludingLine; + int col = lineLength - 1; // highlight from start of line to end + + // Build the new issue location + return new IssueLocation(newLine, col); + } + + /** + * Get all of the contents of source leading up to a line. + * + * @param source The full concatenated sketch source. + * @param endLineExclusive The line up to which code should be returned. Note that this is an + * "exclusive" boundary. Code from this line itself will not be included. + * @return All of the sketch code leading up to but not including the line given. + */ + private static String getContentsUpToLine(String source, int endLineExclusive) { + int line = 0; + int stringCursor = 0; + int strLength = source.length(); + + while (line < endLineExclusive-1 && stringCursor < strLength) { + if (source.charAt(stringCursor) == '\n') { + line++; + } + + stringCursor++; + } + + return source.substring(0, stringCursor); + } + + } + +} diff --git a/java/src/processing/mode/java/preproc/PdeParseTreeListener.java b/java/src/processing/mode/java/preproc/PdeParseTreeListener.java index 74abacb3bf..00bca75b73 100644 --- a/java/src/processing/mode/java/preproc/PdeParseTreeListener.java +++ b/java/src/processing/mode/java/preproc/PdeParseTreeListener.java @@ -35,8 +35,6 @@ import processing.mode.java.SourceUtil; import processing.mode.java.TextTransform; import processing.mode.java.preproc.PdePreprocessor.Mode; -import processing.mode.java.preproc.issue.PdePreprocessIssue; -import processing.mode.java.preproc.issue.PreprocessIssueMessageSimplifier; /** * ANTLR tree traversal listener that preforms code rewrites as part of sketch preprocessing. diff --git a/java/src/processing/mode/java/preproc/PdePreprocessIssue.java b/java/src/processing/mode/java/preproc/PdePreprocessIssue.java new file mode 100644 index 0000000000..77df2a6f26 --- /dev/null +++ b/java/src/processing/mode/java/preproc/PdePreprocessIssue.java @@ -0,0 +1,52 @@ +package processing.mode.java.preproc; + +/** + * Issue emitted from the preprocessor. + */ +public class PdePreprocessIssue { + + private final int line; + private final int charPositionInLine; + private final String msg; + + /** + * Create a new record of an issue emitted from the preprocessor. + * + * @param newLine The line in the generated java file. + * @param newCharPositionInLine The character position in the source line. + * @param newMsg Description of the issue. + */ + public PdePreprocessIssue(int newLine, int newCharPositionInLine, String newMsg) { + line = newLine; + charPositionInLine = newCharPositionInLine; + msg = newMsg; + } + + /** + * Get the unified source line where the issue was found. + * + * @return The line in the output java source where the issue was found. + */ + public int getLine() { + return line; + } + + /** + * Get the character position in the source line of the issue. + * + * @return The source column where the issue was found. + */ + public int getCharPositionInLine() { + return charPositionInLine; + } + + /** + * Get a description of the issue found. + * + * @return Human-readable localized message describing the issue. + */ + public String getMsg() { + return msg; + } + +} diff --git a/java/src/processing/mode/java/preproc/PdePreprocessIssueException.java b/java/src/processing/mode/java/preproc/PdePreprocessIssueException.java new file mode 100644 index 0000000000..8264ecf5bd --- /dev/null +++ b/java/src/processing/mode/java/preproc/PdePreprocessIssueException.java @@ -0,0 +1,32 @@ +package processing.mode.java.preproc; + + +import processing.mode.java.preproc.PdePreprocessIssue; + +/** + * Exception indicating that a preprocessor issue was found. + */ +public class PdePreprocessIssueException extends RuntimeException { + + private final PdePreprocessIssue preprocessIssue; + + /** + * Create a new exception indicating that there was a preprocessing issue. + * + * @param newPreprocessIssue Issue encountered. + */ + public PdePreprocessIssueException(PdePreprocessIssue newPreprocessIssue) { + super(newPreprocessIssue.getMsg()); + preprocessIssue = newPreprocessIssue; + } + + /** + * Get information about the preprocessing issue found. + * + * @return Record of the preprocessor issue. + */ + public PdePreprocessIssue getIssue() { + return preprocessIssue; + } + +} diff --git a/java/src/processing/mode/java/preproc/PdePreprocessor.java b/java/src/processing/mode/java/preproc/PdePreprocessor.java index 6b2b4c5263..b7961cef49 100644 --- a/java/src/processing/mode/java/preproc/PdePreprocessor.java +++ b/java/src/processing/mode/java/preproc/PdePreprocessor.java @@ -34,8 +34,6 @@ import processing.app.Preferences; import processing.app.SketchException; -import processing.mode.java.preproc.issue.PdeIssueEmitter; -import processing.mode.java.preproc.issue.PdePreprocessIssue; /** diff --git a/java/src/processing/mode/java/preproc/issue/PreprocessIssueMessageSimplifier.java b/java/src/processing/mode/java/preproc/PreprocessIssueMessageSimplifier.java similarity index 57% rename from java/src/processing/mode/java/preproc/issue/PreprocessIssueMessageSimplifier.java rename to java/src/processing/mode/java/preproc/PreprocessIssueMessageSimplifier.java index 762dcadf31..39ab6d6ffc 100644 --- a/java/src/processing/mode/java/preproc/issue/PreprocessIssueMessageSimplifier.java +++ b/java/src/processing/mode/java/preproc/PreprocessIssueMessageSimplifier.java @@ -19,28 +19,26 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -package processing.mode.java.preproc.issue; +package processing.mode.java.preproc; import processing.app.Language; import processing.app.Platform; import processing.mode.java.SourceUtil; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * Facade that tries to create a better error message for syntax issues in input source. + * Utility class that generates localized error messages for incorrect sketch syntax. * *

- * Facade that interprets error messages from ANTLR in an attempt to generate an improved error + * Utility that interprets error messages from ANTLR in an attempt to generate an improved error * message when describing grammatically incorrect input. This is distinct from compiler errors - * caused after generating an AST. + * caused after generating an AST. This is required to produce the localized error messages. *

* *

@@ -100,19 +98,21 @@ public static String getLocalStr(String stringName) { * @param originalMessage Error message generated from ANTLR. * @return An improved error message or the originalMessage if no improvements could be made. */ - public IssueMessageSimplification simplify(String originalMessage) { - Optional matching = strategies.stream() + public PdeIssueEmitter.IssueMessageSimplification simplify(String originalMessage) { + Optional matching = strategies.stream() .map((x) -> x.simplify(originalMessage)) .filter(Optional::isPresent) .map(Optional::get) .findFirst(); - return matching.orElse(new IssueMessageSimplification(originalMessage)); + return matching.orElse(new PdeIssueEmitter.IssueMessageSimplification(originalMessage)); } /* ============================ * === Enumerate strategies === * ============================ + * + * */ /** @@ -216,7 +216,7 @@ protected interface PreprocIssueMessageSimplifierStrategy { * @return An optional with an improved message or an empty optional if no improvements could be * made by this strategy. */ - Optional simplify(String message); + Optional simplify(String message); } @@ -266,7 +266,7 @@ protected EvenCountTemplateMessageSimplifierStrategy(String newToken, String new } @Override - public Optional simplify(String message) { + public Optional simplify(String message) { String messageContent = getOffendingArea(message); if (filter.isPresent()) { @@ -283,7 +283,7 @@ public Optional simplify(String message) { token ); return Optional.of( - new IssueMessageSimplification(newMessage) + new PdeIssueEmitter.IssueMessageSimplification(newMessage) ); } } @@ -339,7 +339,7 @@ public TokenPairTemplateMessageSimplifierStrategy(String newToken1, String newTo } @Override - public Optional simplify(String message) { + public Optional simplify(String message) { String messageContent = getOffendingArea(message); int count1 = SourceUtil.getCount(messageContent, token1); @@ -361,7 +361,7 @@ public Optional simplify(String message) { .replace("%c", "%s"), missingToken); return Optional.of( - new IssueMessageSimplification(newMessage) + new PdeIssueEmitter.IssueMessageSimplification(newMessage) ); } @@ -420,7 +420,7 @@ public RegexTemplateMessageSimplifierStrategy(String newRegex, String newHintTem } @Override - public Optional simplify(String message) { + public Optional simplify(String message) { if (pattern.matcher(message).find()) { String newMessage = String.format( hintTemplate, @@ -428,7 +428,7 @@ public Optional simplify(String message) { ); return Optional.of( - new IssueMessageSimplification(newMessage, getAttributeToPrior()) + new PdeIssueEmitter.IssueMessageSimplification(newMessage, getAttributeToPrior()) ); } else { return Optional.empty(); @@ -553,7 +553,7 @@ protected PreprocIssueMessageSimplifierStrategy createMissingCurlyAtStartSimplif return Optional.empty(); } - return Optional.of(new IssueMessageSimplification( + return Optional.of(new PdeIssueEmitter.IssueMessageSimplification( getLocalStr("editor.status.missing.left_curly_bracket") )); }; @@ -568,7 +568,7 @@ protected PreprocIssueMessageSimplifierStrategy createMissingCurlyAtSemicolonSim return Optional.empty(); } - return Optional.of(new IssueMessageSimplification( + return Optional.of(new PdeIssueEmitter.IssueMessageSimplification( getLocalStr("editor.status.missing.right_curly_bracket") )); }; @@ -585,7 +585,7 @@ protected PreprocIssueMessageSimplifierStrategy createMissingIdentifierSimplifie message.replace("missing Identifier at", "") ); return Optional.of( - new IssueMessageSimplification(newMessage) + new PdeIssueEmitter.IssueMessageSimplification(newMessage) ); } else { return Optional.empty(); @@ -613,7 +613,7 @@ protected PreprocIssueMessageSimplifierStrategy createKnownMissingSimplifierStra String newMessage = String.format(langTemplate, missingPiece); - return Optional.of(new IssueMessageSimplification(newMessage)); + return Optional.of(new PdeIssueEmitter.IssueMessageSimplification(newMessage)); } else { return Optional.empty(); } @@ -632,7 +632,7 @@ protected PreprocIssueMessageSimplifierStrategy createExtraneousInputSimplifierS String newMessage = String.format(newMessageOuter, innerMsg); return Optional.of( - new IssueMessageSimplification(newMessage) + new PdeIssueEmitter.IssueMessageSimplification(newMessage) ); } else { return Optional.empty(); @@ -655,7 +655,7 @@ protected PreprocIssueMessageSimplifierStrategy createMismatchedInputSimplifierS ); return Optional.of( - new IssueMessageSimplification( + new PdeIssueEmitter.IssueMessageSimplification( newMessage ) ); @@ -671,21 +671,382 @@ protected PreprocIssueMessageSimplifierStrategy createMismatchedInputSimplifierS protected static class DefaultMessageSimplifier implements PreprocIssueMessageSimplifierStrategy { @Override - public Optional simplify(String message) { + public Optional simplify(String message) { if (message.contains("viable alternative")) { String newMessage = String.format( getLocalizedGenericError("%s"), getOffendingArea(message) ); return Optional.of( - new IssueMessageSimplification(newMessage) + new PdeIssueEmitter.IssueMessageSimplification(newMessage) ); } else { return Optional.of( - new IssueMessageSimplification(message) + new PdeIssueEmitter.IssueMessageSimplification(message) ); } } } + /** + * ===================== + * == Utility classes == + * ===================== + */ + + /** + * Simple automaton that reads backwards from a position in source to find the prior token. + * + *

+ * When helping generate messages for the user, it is often useful to be able to locate the + * position of the first token immediately before another location in source. For example, + * consider error reporting when a semicolon is missing. The error is generated on the token after + * the line on which the semicolon was omitted so, while the error technically emerges on the next + * line, it is better for the user for it to appear earlier. Specifically, it is most sensible for + * it to appear on the "prior token" because this is where it was forgotten. + *

+ * + *

+ * To that end, this finite state automaton can read backwards from a position in source to locate + * the first "non-skip token" preceding that location. Here a "skip" token means one that is + * ignored by the preprocessor and does not impact output code (this includes comments and + * whitespace). This automaton will read character by character from source until it knows it has + * seen a non-skip token, returning the location of that non-skip token. + *

+ * + *

+ * A formalized FSA is useful here in order to traverse code which can have a complex grammar. + * As there are a number of ways in the Java / Processing grammar one can encounter skip tokens, + * this formalized implementation describes the state machine directly in order to provide + * hopefully more readability / transparency compared to a regex without requiring the use of + * something heavier like ANTLR. + *

+ */ + public static class PriorTokenFinder { + + // Simple regex matching all "whitespace" characters recognized by the ANTLR grammar. + private static final String WS_PATTERN = "[ \\t\\r\\n\\u000C]"; + + // Possible states for this FSA + private enum AutomatonState { + + // Automaton is not certain if it is parsing a skip or non-skip character + UNKNOWN, + + // Automaton has found a possible token but it is not sure if inside a comment + POSSIBLE_TOKEN, + + // Automaton has found a token but also a forward slash so, if the next character is also a "/", + // it is inside a single line comment. + TOKEN_OR_MAYBE_SL_COMMENT, + + // Automaton has found a forward slash so, depending on the next character, it may be inside a + // single line comment, multi-line comment, or it may have found a standalone token. + TOKEN_OR_MAYBE_COMMENT, + + // Automaton has found a token and hit its terminal state. + TOKEN, + + // Automaton is current traversing a multi-line comment. + MULTI_LINE_COMMENT, + + // Automaton is maybe leaving a multi line comment because it found an "*". If it picks up a "/" + // next, the automaton knows it is no longer within a multi-line comment. + MAYBE_LEAVE_MULTI_LINE_COMMENT + } + + private boolean done; + private Optional tokenPosition; + private AutomatonState state; + private int charPosition; + private Pattern whitespacePattern; + + /** + * Create a new automaton in unknown state and a character position of zero. + */ + PriorTokenFinder() { + whitespacePattern = Pattern.compile(WS_PATTERN); + reset(); + } + + /** + * Determine if this automaton has found a token. + * + * @return True if this automaton has found a token and, thus, is in terminal state (so will + * ignore all future input). False if this autoamton has not yet found a token since creation + * or last call to reset. + */ + boolean isDone() { + return done; + } + + /** + * Get the position of the token found. + * + * @return Optional containing the number of characters processed prior to finding the token or + * empty if no token found. Note that this is different the number of total characters + * processed as some extra characters have to be read prior to the token itself to ensure it is + * not part of a comment or something similar. + */ + Optional getTokenPositionMaybe() { + return tokenPosition; + } + + /** + * Reset this automaton to UNKNOWN state with a character count of zero. + */ + void reset() { + done = false; + tokenPosition = Optional.empty(); + state = AutomatonState.UNKNOWN; + charPosition = 0; + } + + /** + * Process a character. + * + *

+ * Process the next character in an effort to find the "prior token". Note that this is + * expecting the processing sketch source code to be fed one character at a time + * backwards from the starting position in code. This is because it is looking for the + * first non-skip token immediately preceding a position in source. + *

+ * + * @param input The next character to process. + */ + void step(char input) { + switch(state) { + case UNKNOWN: stepUnknown(input); break; + case POSSIBLE_TOKEN: stepPossibleToken(input); break; + case TOKEN_OR_MAYBE_SL_COMMENT: stepTokenOrMaybeSingleLineComment(input); break; + case TOKEN_OR_MAYBE_COMMENT: stepTokenOrMaybeComment(input); break; + case MULTI_LINE_COMMENT: stepMultiLineComment(input); break; + case MAYBE_LEAVE_MULTI_LINE_COMMENT: stepMaybeLeaveMultiLineComment(input); break; + case TOKEN: /* Already have token. Nothing to be done. */ break; + } + + charPosition++; + } + + /** + * Process the next character while in the UNKNOWN state. + * + *

+ * While not certain if looking at a skip or non-skip token, read the next character. If + * whitespace, can ignore. If a forward slash, could indicate either a comment or a possible + * token (move to TOKEN_OR_MAYBE_COMMENT). If anything else, may have found token but need to + * ensure this line isn't part of a comment (move to POSSIBLE_TOKEN). + *

+ * + * @param input The next character to process. + */ + private void stepUnknown(char input) { + if (isWhitespace(input)) { + return; + } + + tokenPosition = Optional.of(charPosition); + + if (input == '/') { + state = AutomatonState.TOKEN_OR_MAYBE_COMMENT; + } else { + state = AutomatonState.POSSIBLE_TOKEN; + } + } + + /** + * Process the next character while in the POSSIBLE_TOKEN state. + * + *

+ * After having found a character that could indicate a token, need to ensure that the token + * wasn't actually part of a single line comment ("//") so look for forward slashes (if found + * move to TOKEN_OR_MAYBE_SL_COMMENT). If encountered a newline, the earlier found token was + * not part of a comment so enter TOKEN state. + *

+ * + * @param input The next character to process. + */ + private void stepPossibleToken(char input) { + if (input == '\n') { + enterNonSkipTokenState(); + } else if (input == '/') { + state = AutomatonState.TOKEN_OR_MAYBE_SL_COMMENT; + } + + // Else stay put + } + + /** + * Process the next character while in the TOKEN_OR_MAYBE_SL_COMMENT state. + * + *

+ * After having found a forward slash after encountering something else which may be a non-skip + * token, one needs to check that it is preceded by another forward slash to have detected a + * single line comment (return to UNKNOWN state). If found a new line, that forward slash was + * actually a non-skip token itself so enter TOKEN state. Finally, if anything else, it is still + * possible that we are traversing a single line comment so return to POSSIBLE_TOKEN state. + *

+ * + * @param input The next character to process. + */ + private void stepTokenOrMaybeSingleLineComment(char input) { + if (input == '\n') { + enterNonSkipTokenState(); + } else if (input == '/') { + returnToUnknownState(); + } else { + state = AutomatonState.POSSIBLE_TOKEN; + } + } + + /** + * Process the next character while in the TOKEN_OR_MAYBE_COMMENT state. + * + *

+ * After having found a forward slash without encountering something else that may be a non-skip + * token: that forward slash is a non-skip token if preceded by a newline, could be a single + * line comment if preceded by a forward slash, could be a multi-line comment if preceded + * by an asterisk, or could by a non-skip token otherwise. + *

+ * + * @param input The next character to process. + */ + private void stepTokenOrMaybeComment(char input) { + if (input == '\n') { + enterNonSkipTokenState(); + } else if (input == '/') { + returnToUnknownState(); + } else if (input == '*') { + enterMultilineComment(); + } else { + state = AutomatonState.POSSIBLE_TOKEN; + } + } + + /** + * Process the next character while in the MULTI_LINE_COMMENT state. + * + *

+ * Process the next character while traversing a multi-line comment. If an asterisk, we may be + * encountering the end of the multiline comment (move to MAYBE_LEAVE_MULTI_LINE_COMMENT). + * Otherwise, can ignore character. + *

+ * + * @param input The next character to process. + */ + private void stepMultiLineComment(char input) { + if (input == '*') { + state = AutomatonState.MAYBE_LEAVE_MULTI_LINE_COMMENT; + } + + // else stay put + } + + /** + * Process the next character while in the MAYBE_LEAVE_MULTI_LINE_COMMENT state. + * + *

+ * If already found an asterisk while inside a multi-line comment, one may be leaving the multi- + * line comment depending on the next character. If forward slash, at end of comment (return to + * UNKNOWN state). If another asterisk, could still end comment depending on next character + * (stay in current state). Finally, if anything else, we are still in the body of the multi- + * line comment and not about to leave (return to MULTI_LINE_COMMENT state). + *

+ * + * @param input + */ + private void stepMaybeLeaveMultiLineComment(char input) { + if (input == '/') { + state = AutomatonState.UNKNOWN; + } else if (input != '*') { + state = AutomatonState.MULTI_LINE_COMMENT; + } + + // If * stay put + } + + /** + * Convenience function to set up internal FSA state when entering a multi-line comment. + */ + private void enterMultilineComment() { + tokenPosition = Optional.of(charPosition); + state = AutomatonState.MULTI_LINE_COMMENT; + } + + /** + * Convenience function to set up internal FSA state when having found a non-skip token. + */ + private void enterNonSkipTokenState() { + done = true; + state = AutomatonState.TOKEN; + } + + /** + * Convenience function to set up internal FSA state when entering UNKNOWN state. + */ + private void returnToUnknownState() { + tokenPosition = Optional.empty(); + state = AutomatonState.UNKNOWN; + } + + /** + * Convenience function which determines if a character is whitespace. + * + * @param input The character to test. + * @return True if whitespace. False otherwise. + */ + private boolean isWhitespace(char input) { + return whitespacePattern.matcher("" + input).find(); + } + + } + + /** + * Singleton with fallback error localizations. + */ + public static class DefaultErrorLocalStrSet { + + private static final AtomicReference instance = new AtomicReference<>(); + + private final Map localizations = new HashMap<>(); + + /** + * Get shared copy of this singleton. + * + * @return Shared singleton copy. + */ + public static DefaultErrorLocalStrSet get() { + instance.compareAndSet(null, new DefaultErrorLocalStrSet()); + return instance.get(); + } + + /** + * Private hidden constructor. + */ + private DefaultErrorLocalStrSet() { + localizations.put("editor.status.error", "Error"); + localizations.put("editor.status.error.syntax", "Syntax Error - %s"); + localizations.put("editor.status.bad.assignment", "Error on variable assignment near %s?"); + localizations.put("editor.status.bad.identifier", "Identifier cannot start with digits near %s?"); + localizations.put("editor.status.bad.parameter", "Error on parameter or method declaration near %s?"); + localizations.put("editor.status.extraneous", "Unexpected extra code near %s?"); + localizations.put("editor.status.mismatched", "Missing operator or semicolon near %s?"); + localizations.put("editor.status.missing.name", "Missing name near %s?"); + localizations.put("editor.status.missing.type", "Missing name or type near %s?"); + localizations.put("editor.status.missing.default", "Missing '%s'?"); + localizations.put("editor.status.missing.right_curly_bracket", "Missing '}'"); + localizations.put("editor.status.missing.left_curly_bracket", "Missing '{'"); + } + + /** + * Lookup localization. + * + * @param key Name of string. + * @return Value of string or empty if not given. + */ + public Optional get(String key) { + return Optional.ofNullable(localizations.getOrDefault(key, null)); + } + + } } diff --git a/java/src/processing/mode/java/preproc/PreprocessorResult.java b/java/src/processing/mode/java/preproc/PreprocessorResult.java index e8eff3d2bf..2c2d8b7295 100644 --- a/java/src/processing/mode/java/preproc/PreprocessorResult.java +++ b/java/src/processing/mode/java/preproc/PreprocessorResult.java @@ -27,7 +27,6 @@ import processing.mode.java.ImportStatement; import processing.mode.java.TextTransform; -import processing.mode.java.preproc.issue.PdePreprocessIssue; /** diff --git a/java/src/processing/mode/java/preproc/issue/DefaultErrorLocalStrSet.java b/java/src/processing/mode/java/preproc/issue/DefaultErrorLocalStrSet.java deleted file mode 100644 index 54fe3686da..0000000000 --- a/java/src/processing/mode/java/preproc/issue/DefaultErrorLocalStrSet.java +++ /dev/null @@ -1,55 +0,0 @@ -package processing.mode.java.preproc.issue; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.atomic.AtomicReference; - -/** - * Singleton with fallback error localizations. - */ -public class DefaultErrorLocalStrSet { - - private static final AtomicReference instance = new AtomicReference<>(); - - private final Map localizations = new HashMap<>(); - - /** - * Get shared copy of this singleton. - * - * @return Shared singleton copy. - */ - public static DefaultErrorLocalStrSet get() { - instance.compareAndSet(null, new DefaultErrorLocalStrSet()); - return instance.get(); - } - - /** - * Private hidden constructor. - */ - private DefaultErrorLocalStrSet() { - localizations.put("editor.status.error", "Error"); - localizations.put("editor.status.error.syntax", "Syntax Error - %s"); - localizations.put("editor.status.bad.assignment", "Error on variable assignment near %s?"); - localizations.put("editor.status.bad.identifier", "Identifier cannot start with digits near %s?"); - localizations.put("editor.status.bad.parameter", "Error on parameter or method declaration near %s?"); - localizations.put("editor.status.extraneous", "Unexpected extra code near %s?"); - localizations.put("editor.status.mismatched", "Missing operator or semicolon near %s?"); - localizations.put("editor.status.missing.name", "Missing name near %s?"); - localizations.put("editor.status.missing.type", "Missing name or type near %s?"); - localizations.put("editor.status.missing.default", "Missing '%s'?"); - localizations.put("editor.status.missing.right_curly_bracket", "Missing '}'"); - localizations.put("editor.status.missing.left_curly_bracket", "Missing '{'"); - } - - /** - * Lookup localization. - * - * @param key Name of string. - * @return Value of string or empty if not given. - */ - public Optional get(String key) { - return Optional.ofNullable(localizations.getOrDefault(key, null)); - } - -} diff --git a/java/src/processing/mode/java/preproc/issue/IssueLocation.java b/java/src/processing/mode/java/preproc/issue/IssueLocation.java deleted file mode 100644 index 7188e26df3..0000000000 --- a/java/src/processing/mode/java/preproc/issue/IssueLocation.java +++ /dev/null @@ -1,65 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* -Part of the Processing project - http://processing.org - -Copyright (c) 2012-19 The Processing Foundation - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License version 2 -as published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software Foundation, -Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -package processing.mode.java.preproc.issue; - - -/** - * Data structure describing where an issue occurred. - */ -public class IssueLocation { - - private final int line; - private final int charPosition; - - /** - * Create a new issue location structure. - * - * @param newLine The line (1-indexed) where the issue occurred. This should be in the global file - * generated by the preprocessor and not relative to the start of the tab. - * @param newCharPosition The position on the line. - */ - public IssueLocation(int newLine, int newCharPosition) { - line = newLine; - charPosition = newCharPosition; - } - - /** - * Get the 1-indexed line on which this error occurred. - * - * @return The line on which this error occurred. Note that this will be relative to the global - * file generated by the preprocessor and not relative to the start of the tab. - */ - public int getLine() { - return line; - } - - /** - * The the position of the error within the line. - * - * @return The number of characters including whitespace from the start of the line at which the - * error occurred. - */ - public int getCharPosition() { - return charPosition; - } - -} diff --git a/java/src/processing/mode/java/preproc/issue/IssueLocationFactory.java b/java/src/processing/mode/java/preproc/issue/IssueLocationFactory.java deleted file mode 100644 index 9780d3e2c0..0000000000 --- a/java/src/processing/mode/java/preproc/issue/IssueLocationFactory.java +++ /dev/null @@ -1,127 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* -Part of the Processing project - http://processing.org - -Copyright (c) 2012-19 The Processing Foundation - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License version 2 -as published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software Foundation, -Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -package processing.mode.java.preproc.issue; - -import java.util.Optional; - -import processing.mode.java.SourceUtil; - - -/** - * Utility that can help clean up where in source an issue should be reported. - * - *

- * For some errors, the location of the "mistake" does not appear close to where the actual error - * is generated. For example, consider omitting a semicolon. Though the "mistake" is arguably on - * the line on which a semicolon is forgotten, the grammatical error appears in the first - * non-skip token after the omitted character. This means that the issue shown to the user may - * be far away from the line they would want to edit. This utility helps determine if an issue - * requires a new location and, if so, where the location should be. - *

- */ -public class IssueLocationFactory { - - /** - * Determine where an issue should be reported. - * - * @param simplification The issue simplification generated from {PreprocessIssueMessageSimplifier}. - * @param originalLine The original line (1 indexed) on which the issue was reported. - * @param originalOffset The original number of characters from the start of the line where the - * the issue was reported. - * @param source The full concatenated source of the sketch being built. - * @param lineCount The total - * @return The new location where the issue should be reported. This may be identical to the - * original location if the issue was not moved. - */ - public static IssueLocation getLineWithOffset(IssueMessageSimplification simplification, - int originalLine, int originalOffset, String source) { - - // Determine if the issue should be relocated - boolean shouldAttributeToPrior = simplification.getAttributeToPriorToken(); - shouldAttributeToPrior = shouldAttributeToPrior && originalLine != 0; - - if (!shouldAttributeToPrior) { - return new IssueLocation(originalLine, originalOffset); - } - - // Find the code prior the issue - String priorCode = getContentsUpToLine(source, originalLine); - - // Find the token immediately prior to the issue - PriorTokenFinder finder = new PriorTokenFinder(); - int charPos = priorCode.length(); - while (!finder.isDone() && charPos > 0) { - charPos--; - finder.step(priorCode.charAt(charPos)); - } - - // Find the location offset depending on if the prior token could be found - Optional foundStartOfMatchMaybe = finder.getTokenPositionMaybe(); - int startOfMatch; - int linesOffset; - - if (foundStartOfMatchMaybe.isPresent()) { - startOfMatch = priorCode.length() - foundStartOfMatchMaybe.get(); - String contentsOfMatch = priorCode.substring(startOfMatch); - linesOffset = SourceUtil.getCount(contentsOfMatch, "\n"); - } else { - startOfMatch = priorCode.length(); - linesOffset = 0; - } - - // Apply the location offset and highlight to the end of the line - String contentsPriorToMatch = priorCode.substring(0, startOfMatch); - int newLine = originalLine - linesOffset; - int lengthIncludingLine = contentsPriorToMatch.length(); - int lengthExcludingLine = contentsPriorToMatch.lastIndexOf('\n'); - int lineLength = lengthIncludingLine - lengthExcludingLine; - int col = lineLength - 1; // highlight from start of line to end - - // Build the new issue location - return new IssueLocation(newLine, col); - } - - /** - * Get all of the contents of source leading up to a line. - * - * @param source The full concatenated sketch source. - * @param endLineExclusive The line up to which code should be returned. Note that this is an - * "exclusive" boundary. Code from this line itself will not be included. - * @return All of the sketch code leading up to but not including the line given. - */ - private static String getContentsUpToLine(String source, int endLineExclusive) { - int line = 0; - int stringCursor = 0; - int strLength = source.length(); - - while (line < endLineExclusive-1 && stringCursor < strLength) { - if (source.charAt(stringCursor) == '\n') { - line++; - } - - stringCursor++; - } - - return source.substring(0, stringCursor); - } - -} diff --git a/java/src/processing/mode/java/preproc/issue/IssueMessageSimplification.java b/java/src/processing/mode/java/preproc/issue/IssueMessageSimplification.java deleted file mode 100644 index eb8377584a..0000000000 --- a/java/src/processing/mode/java/preproc/issue/IssueMessageSimplification.java +++ /dev/null @@ -1,96 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* -Part of the Processing project - http://processing.org - -Copyright (c) 2012-19 The Processing Foundation - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License version 2 -as published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software Foundation, -Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -package processing.mode.java.preproc.issue; - - -/** - * Data structure describing an issue simplification or explanation. - * - *

- * Data structure describing an edit that was made to an error message or warning to be shown to - * the user based on a series of rules that attempts to make error messages easier to understand - * for the user. - *

- */ -public class IssueMessageSimplification { - - private final String message; - private final boolean attributeToPriorToken; - - /** - * Create a new issue message simplification. - * - *

- * Create a new issue message simplification that leaves the token attribution alone (the token - * on which the error was reported will be the same before error message simplification). - *

- * - * @param newMessage The message to show to the user. - */ - public IssueMessageSimplification(String newMessage) { - message = newMessage; - attributeToPriorToken = false; - } - - /** - * Create a new issue message simplification. - * - *

- * Create a new issue message simplification. Note that there is an option to have the error - * attributed to the "prior token". This is helpful, for example, when a semicolon is missing. - * The error is generated on the token after the line on which the semicolon was omitted so, - * while the error technically emerges on the next line, it is better for the user for it to - * appear earlier. Specifically, it is most sensible for it to appear on the "prior token". - *

- * - * @param newMessage The message to show to the user. - * @param newAttributeToPriorToken Boolean flag indicating if the error should be shown on the - * token prior to the one on which the error was originally generated. True if the error should - * be attributed to the prior token. False otherwise. - */ - public IssueMessageSimplification(String newMessage, boolean newAttributeToPriorToken) { - message = newMessage; - attributeToPriorToken = newAttributeToPriorToken; - } - - /** - * Get the error message text that should be shown to the user. - * - * @return The error message text that should be shown to the user. - */ - public String getMessage() { - return message; - } - - /** - * Flag indicating if the error should be attributed to the prior token. - * - * @return True if the error should be attributed to the prior non-skip token (not whitepsace or - * comment). This is useful when a mistake on a prior line like omitted semicolon causes an - * error on a later line but one wants error highlighting closer to the mistake itself. False - * if the error should be attributed to the original offending token. - */ - public boolean getAttributeToPriorToken() { - return attributeToPriorToken; - } - -} diff --git a/java/src/processing/mode/java/preproc/issue/PdeIssueEmitter.java b/java/src/processing/mode/java/preproc/issue/PdeIssueEmitter.java deleted file mode 100644 index d7f3acc75b..0000000000 --- a/java/src/processing/mode/java/preproc/issue/PdeIssueEmitter.java +++ /dev/null @@ -1,128 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2019 The Processing Foundation - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 - as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -package processing.mode.java.preproc.issue; - -import org.antlr.v4.runtime.BaseErrorListener; -import org.antlr.v4.runtime.RecognitionException; -import org.antlr.v4.runtime.Recognizer; - -import java.util.Optional; - - -/** - * ANTLR error listener to inform a preprocess issue listener when syntax errors are encountered. - * - *

- * A {BaseErrorListener} which looks for syntax errors reported by ANTLR and converts them to - * {PdePreprocessIssue}s that are consumable by a {PdePreprocessIssueListener}. It does this by - * running the {PreprocessIssueMessageSimplifier} to generate a more user-friendly error message - * before informing the provided listener. - *

- */ -public class PdeIssueEmitter extends BaseErrorListener { - - private final PdePreprocessIssueListener listener; - private final Optional sourceMaybe; - - /** - * Create a new issue emitter. - * - *

- * Create a new issue emitter when access to the processing sketch source is not available. - * Note that this will not allow some error beautification and, if sketch source is available, - * use other constructor. - *

- * - * @param newListener The listener to inform when encountering a syntax error. - */ - public PdeIssueEmitter(PdePreprocessIssueListener newListener) { - listener = newListener; - sourceMaybe = Optional.empty(); - } - - /** - * Create a new issue emitter. - * - * @param newListener The listener to inform when encountering a syntax error. - * @param newSourceEmitter The sketch source to use when helping beautify certain syntax error - * messages. - */ - public PdeIssueEmitter(PdePreprocessIssueListener newListener, SourceEmitter newSourceEmitter) { - listener = newListener; - sourceMaybe = Optional.of(newSourceEmitter); - } - - @Override - public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, - int charPositionInLine, String msg, RecognitionException e) { - - PreprocessIssueMessageSimplifier facade = PreprocessIssueMessageSimplifier.get(); - IssueMessageSimplification simplification = facade.simplify(msg); - - IssueLocation issueLocation; - - if (sourceMaybe.isPresent()) { - issueLocation = IssueLocationFactory.getLineWithOffset( - simplification, - line, - charPositionInLine, - sourceMaybe.get().getSource() - ); - } else { - issueLocation = new IssueLocation(line, charPositionInLine); - } - - listener.onIssue(new PdePreprocessIssue( - issueLocation.getLine(), - issueLocation.getCharPosition(), - simplification.getMessage() - )); - } - - /** - * Simple interface for strategy which can emit the full body of a processing sketch. - */ - public static interface SourceEmitter { - - /** - * Get the full body of the processing sketch. - * - * @return String processing sketch source code across all tabs. - */ - String getSource(); - - } - - /** - * Interface for listener that responds to issues reported by the preprocessor. - */ - public static interface PdePreprocessIssueListener { - - /** - * Callback to invoke when an issue is encountered in preprocesing. - * - * @param issue Description of the issue. - */ - void onIssue(PdePreprocessIssue issue); - - } -} diff --git a/java/src/processing/mode/java/preproc/issue/PdePreprocessIssue.java b/java/src/processing/mode/java/preproc/issue/PdePreprocessIssue.java deleted file mode 100644 index df40536035..0000000000 --- a/java/src/processing/mode/java/preproc/issue/PdePreprocessIssue.java +++ /dev/null @@ -1,27 +0,0 @@ -package processing.mode.java.preproc.issue; - -public class PdePreprocessIssue { - - private final int line; - private final int charPositionInLine; - private final String msg; - - public PdePreprocessIssue(int newLine, int newCharPositionInLine, String newMsg) { - line = newLine; - charPositionInLine = newCharPositionInLine; - msg = newMsg; - } - - public int getLine() { - return line; - } - - public int getCharPositionInLine() { - return charPositionInLine; - } - - public String getMsg() { - return msg; - } - -} diff --git a/java/src/processing/mode/java/preproc/issue/PdePreprocessIssueException.java b/java/src/processing/mode/java/preproc/issue/PdePreprocessIssueException.java deleted file mode 100644 index 72aeae9af2..0000000000 --- a/java/src/processing/mode/java/preproc/issue/PdePreprocessIssueException.java +++ /dev/null @@ -1,17 +0,0 @@ -package processing.mode.java.preproc.issue; - - -public class PdePreprocessIssueException extends RuntimeException { - - private final PdePreprocessIssue preprocessIssue; - - public PdePreprocessIssueException(PdePreprocessIssue newPreprocessIssue) { - super(newPreprocessIssue.getMsg()); - preprocessIssue = newPreprocessIssue; - } - - public PdePreprocessIssue getIssue() { - return preprocessIssue; - } - -} diff --git a/java/src/processing/mode/java/preproc/issue/PriorTokenFinder.java b/java/src/processing/mode/java/preproc/issue/PriorTokenFinder.java deleted file mode 100644 index 51e8106a81..0000000000 --- a/java/src/processing/mode/java/preproc/issue/PriorTokenFinder.java +++ /dev/null @@ -1,333 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* -Part of the Processing project - http://processing.org - -Copyright (c) 2012-19 The Processing Foundation - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License version 2 -as published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software Foundation, -Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -package processing.mode.java.preproc.issue; - -import java.util.Optional; -import java.util.regex.Pattern; - - -/** - * Simple automaton that reads backwards from a position in source to find the prior token. - * - *

- * When helping generate messages for the user, it is often useful to be able to locate the - * position of the first token immediately before another location in source. For example, - * consider error reporting when a semicolon is missing. The error is generated on the token after - * the line on which the semicolon was omitted so, while the error technically emerges on the next - * line, it is better for the user for it to appear earlier. Specifically, it is most sensible for - * it to appear on the "prior token" because this is where it was forgotten. - *

- * - *

- * To that end, this finite state automaton can read backwards from a position in source to locate - * the first "non-skip token" preceding that location. Here a "skip" token means one that is - * ignored by the preprocessor and does not impact output code (this includes comments and - * whitespace). This automaton will read character by character from source until it knows it has - * seen a non-skip token, returning the location of that non-skip token. - *

- * - *

- * A formalized FSA is useful here in order to traverse code which can have a complex grammar. - * As there are a number of ways in the Java / Processing grammar one can encounter skip tokens, - * this formalized implementation describes the state machine directly in order to provide - * hopefully more readability / transparency compared to a regex without requiring the use of - * something heavier like ANTLR. - *

- */ -public class PriorTokenFinder { - - // Simple regex matching all "whitespace" characters recognized by the ANTLR grammar. - private static final String WS_PATTERN = "[ \\t\\r\\n\\u000C]"; - - // Possible states for this FSA - private enum AutomatonState { - - // Automaton is not certain if it is parsing a skip or non-skip character - UNKNOWN, - - // Automaton has found a possible token but it is not sure if inside a comment - POSSIBLE_TOKEN, - - // Automaton has found a token but also a forward slash so, if the next character is also a "/", - // it is inside a single line comment. - TOKEN_OR_MAYBE_SL_COMMENT, - - // Automaton has found a forward slash so, depending on the next character, it may be inside a - // single line comment, multi-line comment, or it may have found a standalone token. - TOKEN_OR_MAYBE_COMMENT, - - // Automaton has found a token and hit its terminal state. - TOKEN, - - // Automaton is current traversing a multi-line comment. - MULTI_LINE_COMMENT, - - // Automaton is maybe leaving a multi line comment because it found an "*". If it picks up a "/" - // next, the automaton knows it is no longer within a multi-line comment. - MAYBE_LEAVE_MULTI_LINE_COMMENT - } - - private boolean done; - private Optional tokenPosition; - private AutomatonState state; - private int charPosition; - private Pattern whitespacePattern; - - /** - * Create a new automaton in unknown state and a character position of zero. - */ - public PriorTokenFinder() { - whitespacePattern = Pattern.compile(WS_PATTERN); - reset(); - } - - /** - * Determine if this automaton has found a token. - * - * @return True if this automaton has found a token and, thus, is in terminal state (so will - * ignore all future input). False if this autoamton has not yet found a token since creation - * or last call to reset. - */ - public boolean isDone() { - return done; - } - - /** - * Get the position of the token found. - * - * @return Optional containing the number of characters processed prior to finding the token or - * empty if no token found. Note that this is different the number of total characters - * processed as some extra characters have to be read prior to the token itself to ensure it is - * not part of a comment or something similar. - */ - public Optional getTokenPositionMaybe() { - return tokenPosition; - } - - /** - * Reset this automaton to UNKNOWN state with a character count of zero. - */ - public void reset() { - done = false; - tokenPosition = Optional.empty(); - state = AutomatonState.UNKNOWN; - charPosition = 0; - } - - /** - * Process a character. - * - *

- * Process the next character in an effort to find the "prior token". Note that this is - * expecting the processing sketch source code to be fed one character at a time - * backwards from the starting position in code. This is because it is looking for the - * first non-skip token immediately preceding a position in source. - *

- * - * @param input The next character to process. - */ - public void step(char input) { - switch(state) { - case UNKNOWN: stepUnknown(input); break; - case POSSIBLE_TOKEN: stepPossibleToken(input); break; - case TOKEN_OR_MAYBE_SL_COMMENT: stepTokenOrMaybeSingleLineComment(input); break; - case TOKEN_OR_MAYBE_COMMENT: stepTokenOrMaybeComment(input); break; - case MULTI_LINE_COMMENT: stepMultiLineComment(input); break; - case MAYBE_LEAVE_MULTI_LINE_COMMENT: stepMaybeLeaveMultiLineComment(input); break; - case TOKEN: /* Already have token. Nothing to be done. */ break; - } - - charPosition++; - } - - /** - * Process the next character while in the UNKNOWN state. - * - *

- * While not certain if looking at a skip or non-skip token, read the next character. If - * whitespace, can ignore. If a forward slash, could indicate either a comment or a possible - * token (move to TOKEN_OR_MAYBE_COMMENT). If anything else, may have found token but need to - * ensure this line isn't part of a comment (move to POSSIBLE_TOKEN). - *

- * - * @param input The next character to process. - */ - private void stepUnknown(char input) { - if (isWhitespace(input)) { - return; - } - - tokenPosition = Optional.of(charPosition); - - if (input == '/') { - state = AutomatonState.TOKEN_OR_MAYBE_COMMENT; - } else { - state = AutomatonState.POSSIBLE_TOKEN; - } - } - - /** - * Process the next character while in the POSSIBLE_TOKEN state. - * - *

- * After having found a character that could indicate a token, need to ensure that the token - * wasn't actually part of a single line comment ("//") so look for forward slashes (if found - * move to TOKEN_OR_MAYBE_SL_COMMENT). If encountered a newline, the earlier found token was - * not part of a comment so enter TOKEN state. - *

- * - * @param input The next character to process. - */ - private void stepPossibleToken(char input) { - if (input == '\n') { - enterNonSkipTokenState(); - } else if (input == '/') { - state = AutomatonState.TOKEN_OR_MAYBE_SL_COMMENT; - } - - // Else stay put - } - - /** - * Process the next character while in the TOKEN_OR_MAYBE_SL_COMMENT state. - * - *

- * After having found a forward slash after encountering something else which may be a non-skip - * token, one needs to check that it is preceded by another forward slash to have detected a - * single line comment (return to UNKNOWN state). If found a new line, that forward slash was - * actually a non-skip token itself so enter TOKEN state. Finally, if anything else, it is still - * possible that we are traversing a single line comment so return to POSSIBLE_TOKEN state. - *

- * - * @param input The next character to process. - */ - private void stepTokenOrMaybeSingleLineComment(char input) { - if (input == '\n') { - enterNonSkipTokenState(); - } else if (input == '/') { - returnToUnknownState(); - } else { - state = AutomatonState.POSSIBLE_TOKEN; - } - } - - /** - * Process the next character while in the TOKEN_OR_MAYBE_COMMENT state. - * - *

- * After having found a forward slash without encountering something else that may be a non-skip - * token: that forward slash is a non-skip token if preceded by a newline, could be a single - * line comment if preceded by a forward slash, could be a multi-line comment if preceded - * by an asterisk, or could by a non-skip token otherwise. - *

- * - * @param input The next character to process. - */ - private void stepTokenOrMaybeComment(char input) { - if (input == '\n') { - enterNonSkipTokenState(); - } else if (input == '/') { - returnToUnknownState(); - } else if (input == '*') { - enterMultilineComment(); - } else { - state = AutomatonState.POSSIBLE_TOKEN; - } - } - - /** - * Process the next character while in the MULTI_LINE_COMMENT state. - * - *

- * Process the next character while traversing a multi-line comment. If an asterisk, we may be - * encountering the end of the multiline comment (move to MAYBE_LEAVE_MULTI_LINE_COMMENT). - * Otherwise, can ignore character. - *

- * - * @param input The next character to process. - */ - private void stepMultiLineComment(char input) { - if (input == '*') { - state = AutomatonState.MAYBE_LEAVE_MULTI_LINE_COMMENT; - } - - // else stay put - } - - /** - * Process the next character while in the MAYBE_LEAVE_MULTI_LINE_COMMENT state. - * - *

- * If already found an asterisk while inside a multi-line comment, one may be leaving the multi- - * line comment depending on the next character. If forward slash, at end of comment (return to - * UNKNOWN state). If another asterisk, could still end comment depending on next character - * (stay in current state). Finally, if anything else, we are still in the body of the multi- - * line comment and not about to leave (return to MULTI_LINE_COMMENT state). - *

- * - * @param input - */ - private void stepMaybeLeaveMultiLineComment(char input) { - if (input == '/') { - state = AutomatonState.UNKNOWN; - } else if (input != '*') { - state = AutomatonState.MULTI_LINE_COMMENT; - } - - // If * stay put - } - - /** - * Convenience function to set up internal FSA state when entering a multi-line comment. - */ - private void enterMultilineComment() { - tokenPosition = Optional.of(charPosition); - state = AutomatonState.MULTI_LINE_COMMENT; - } - - /** - * Convenience function to set up internal FSA state when having found a non-skip token. - */ - private void enterNonSkipTokenState() { - done = true; - state = AutomatonState.TOKEN; - } - - /** - * Convenience function to set up internal FSA state when entering UNKNOWN state. - */ - private void returnToUnknownState() { - tokenPosition = Optional.empty(); - state = AutomatonState.UNKNOWN; - } - - /** - * Convenience function which determines if a character is whitespace. - * - * @param input The character to test. - * @return True if whitespace. False otherwise. - */ - private boolean isWhitespace(char input) { - return whitespacePattern.matcher("" + input).find(); - } - -} diff --git a/java/test/processing/mode/java/ParserTests.java b/java/test/processing/mode/java/ParserTests.java index 922b5bdeef..7be03a6d86 100644 --- a/java/test/processing/mode/java/ParserTests.java +++ b/java/test/processing/mode/java/ParserTests.java @@ -8,19 +8,14 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.util.Arrays; import java.util.Optional; -import org.eclipse.jdt.core.compiler.IProblem; -import org.eclipse.jdt.core.dom.AST; -import org.eclipse.jdt.core.dom.ASTParser; -import org.eclipse.jdt.core.dom.CompilationUnit; import org.junit.BeforeClass; import org.junit.Test; import processing.app.SketchException; import processing.mode.java.preproc.PreprocessorResult; -import processing.mode.java.preproc.issue.PdePreprocessIssueException; +import processing.mode.java.preproc.PdePreprocessIssueException; public class ParserTests { diff --git a/java/test/processing/mode/java/ProblemFactoryTest.java b/java/test/processing/mode/java/ProblemFactoryTest.java index 7090590302..2b43f62c8f 100644 --- a/java/test/processing/mode/java/ProblemFactoryTest.java +++ b/java/test/processing/mode/java/ProblemFactoryTest.java @@ -6,13 +6,11 @@ import org.mockito.Mockito; import processing.app.Problem; import processing.app.ui.Editor; -import processing.mode.java.preproc.issue.PdePreprocessIssue; +import processing.mode.java.preproc.PdePreprocessIssue; import java.util.ArrayList; import java.util.List; -import static org.junit.Assert.*; - public class ProblemFactoryTest { private PdePreprocessIssue pdePreprocessIssue; diff --git a/java/test/processing/mode/java/ProcessingTestUtil.java b/java/test/processing/mode/java/ProcessingTestUtil.java index 97deb41475..cd8e43e9e7 100644 --- a/java/test/processing/mode/java/ProcessingTestUtil.java +++ b/java/test/processing/mode/java/ProcessingTestUtil.java @@ -2,7 +2,6 @@ import java.io.File; import java.io.FileInputStream; -import java.io.IOException; import java.io.InputStreamReader; import java.io.StringWriter; import java.util.Optional; @@ -11,7 +10,7 @@ import processing.app.SketchException; import processing.mode.java.preproc.PdePreprocessor; import processing.mode.java.preproc.PreprocessorResult; -import processing.mode.java.preproc.issue.PdePreprocessIssueException; +import processing.mode.java.preproc.PdePreprocessIssueException; public class ProcessingTestUtil { diff --git a/java/test/processing/mode/java/preproc/issue/AssignmentMessageSimplifierStrategyTest.java b/java/test/processing/mode/java/preproc/AssignmentMessageSimplifierStrategyTest.java similarity index 58% rename from java/test/processing/mode/java/preproc/issue/AssignmentMessageSimplifierStrategyTest.java rename to java/test/processing/mode/java/preproc/AssignmentMessageSimplifierStrategyTest.java index 24d2bb6a1b..22a2f53df4 100644 --- a/java/test/processing/mode/java/preproc/issue/AssignmentMessageSimplifierStrategyTest.java +++ b/java/test/processing/mode/java/preproc/AssignmentMessageSimplifierStrategyTest.java @@ -1,8 +1,10 @@ -package processing.mode.java.preproc.issue; +package processing.mode.java.preproc; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import processing.mode.java.preproc.PdeIssueEmitter; +import processing.mode.java.preproc.PreprocessIssueMessageSimplifier; import java.util.Optional; @@ -18,19 +20,19 @@ public void setup() { @Test public void testPresent() { - Optional msg = strategy.simplify(" int x ="); + Optional msg = strategy.simplify(" int x ="); Assert.assertTrue(msg.isPresent()); } @Test public void testPresentDiamond() { - Optional msg = strategy.simplify(" List x ="); + Optional msg = strategy.simplify(" List x ="); Assert.assertTrue(msg.isPresent()); } @Test public void testNotPresent() { - Optional msg = strategy.simplify("class {"); + Optional msg = strategy.simplify("class {"); Assert.assertTrue(msg.isEmpty()); } diff --git a/java/test/processing/mode/java/preproc/issue/BadIdentifierMessageSimplifierStrategyTest.java b/java/test/processing/mode/java/preproc/BadIdentifierMessageSimplifierStrategyTest.java similarity index 61% rename from java/test/processing/mode/java/preproc/issue/BadIdentifierMessageSimplifierStrategyTest.java rename to java/test/processing/mode/java/preproc/BadIdentifierMessageSimplifierStrategyTest.java index 523d7f517d..7a6e51997b 100644 --- a/java/test/processing/mode/java/preproc/issue/BadIdentifierMessageSimplifierStrategyTest.java +++ b/java/test/processing/mode/java/preproc/BadIdentifierMessageSimplifierStrategyTest.java @@ -1,8 +1,10 @@ -package processing.mode.java.preproc.issue; +package processing.mode.java.preproc; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import processing.mode.java.preproc.PdeIssueEmitter; +import processing.mode.java.preproc.PreprocessIssueMessageSimplifier; import java.util.Optional; @@ -18,13 +20,13 @@ public void setup() { @Test public void testPresent() { - Optional msg = strategy.simplify("test(a,01a"); + Optional msg = strategy.simplify("test(a,01a"); Assert.assertTrue(msg.isPresent()); } @Test public void testNotPresent() { - Optional msg = strategy.simplify("class {"); + Optional msg = strategy.simplify("class {"); Assert.assertTrue(msg.isEmpty()); } diff --git a/java/test/processing/mode/java/preproc/issue/BadParamMessageSimplifierStrategyTest.java b/java/test/processing/mode/java/preproc/BadParamMessageSimplifierStrategyTest.java similarity index 55% rename from java/test/processing/mode/java/preproc/issue/BadParamMessageSimplifierStrategyTest.java rename to java/test/processing/mode/java/preproc/BadParamMessageSimplifierStrategyTest.java index 3204df4779..b0fb9a0b11 100644 --- a/java/test/processing/mode/java/preproc/issue/BadParamMessageSimplifierStrategyTest.java +++ b/java/test/processing/mode/java/preproc/BadParamMessageSimplifierStrategyTest.java @@ -1,8 +1,10 @@ -package processing.mode.java.preproc.issue; +package processing.mode.java.preproc; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import processing.mode.java.preproc.PdeIssueEmitter; +import processing.mode.java.preproc.PreprocessIssueMessageSimplifier; import java.util.Optional; @@ -18,25 +20,25 @@ public void setup() { @Test public void testPresent() { - Optional msg = strategy.simplify("void test (int x,\ny) \n{"); + Optional msg = strategy.simplify("void test (int x,\ny) \n{"); Assert.assertTrue(msg.isPresent()); } @Test public void testPresentUnderscore() { - Optional msg = strategy.simplify("void test (int x,\ny_y) \n{"); + Optional msg = strategy.simplify("void test (int x,\ny_y) \n{"); Assert.assertTrue(msg.isPresent()); } @Test public void testPresentVarType() { - Optional msg = strategy.simplify("void test (int x,\nint) \n{"); + Optional msg = strategy.simplify("void test (int x,\nint) \n{"); Assert.assertTrue(msg.isPresent()); } @Test public void testNotPresent() { - Optional msg = strategy.simplify("int x = y"); + Optional msg = strategy.simplify("int x = y"); Assert.assertTrue(msg.isEmpty()); } diff --git a/java/test/processing/mode/java/preproc/issue/ExtraneousInputMessageSimplifierStrategyTest.java b/java/test/processing/mode/java/preproc/ExtraneousInputMessageSimplifierStrategyTest.java similarity index 59% rename from java/test/processing/mode/java/preproc/issue/ExtraneousInputMessageSimplifierStrategyTest.java rename to java/test/processing/mode/java/preproc/ExtraneousInputMessageSimplifierStrategyTest.java index fce06fbfd7..69b8d09f09 100644 --- a/java/test/processing/mode/java/preproc/issue/ExtraneousInputMessageSimplifierStrategyTest.java +++ b/java/test/processing/mode/java/preproc/ExtraneousInputMessageSimplifierStrategyTest.java @@ -1,8 +1,10 @@ -package processing.mode.java.preproc.issue; +package processing.mode.java.preproc; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import processing.mode.java.preproc.PdeIssueEmitter; +import processing.mode.java.preproc.PreprocessIssueMessageSimplifier; import java.util.Optional; @@ -19,13 +21,13 @@ public void setup() { @Test public void testPresent() { - Optional msg = strategy.simplify("extraneous input 'test' expecting ';'"); + Optional msg = strategy.simplify("extraneous input 'test' expecting ';'"); Assert.assertTrue(msg.isPresent()); } @Test public void testNotPresent() { - Optional msg = strategy.simplify("String x = \" \\\" \""); + Optional msg = strategy.simplify("String x = \" \\\" \""); Assert.assertTrue(msg.isEmpty()); } diff --git a/java/test/processing/mode/java/preproc/issue/KnownMissingMessageSimplifierStrategyTest.java b/java/test/processing/mode/java/preproc/KnownMissingMessageSimplifierStrategyTest.java similarity index 59% rename from java/test/processing/mode/java/preproc/issue/KnownMissingMessageSimplifierStrategyTest.java rename to java/test/processing/mode/java/preproc/KnownMissingMessageSimplifierStrategyTest.java index 40efe75365..587cd45c42 100644 --- a/java/test/processing/mode/java/preproc/issue/KnownMissingMessageSimplifierStrategyTest.java +++ b/java/test/processing/mode/java/preproc/KnownMissingMessageSimplifierStrategyTest.java @@ -1,8 +1,10 @@ -package processing.mode.java.preproc.issue; +package processing.mode.java.preproc; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import processing.mode.java.preproc.PdeIssueEmitter; +import processing.mode.java.preproc.PreprocessIssueMessageSimplifier; import java.util.Optional; @@ -18,13 +20,13 @@ public void setup() { @Test public void testPresent() { - Optional msg = strategy.simplify("missing ';' at 'addCircle'"); + Optional msg = strategy.simplify("missing ';' at 'addCircle'"); Assert.assertTrue(msg.isPresent()); } @Test public void testNotPresent() { - Optional msg = strategy.simplify("String x = \" \\\" \""); + Optional msg = strategy.simplify("String x = \" \\\" \""); Assert.assertTrue(msg.isEmpty()); } diff --git a/java/test/processing/mode/java/preproc/issue/MessageSimplifierUtilTest.java b/java/test/processing/mode/java/preproc/MessageSimplifierUtilTest.java similarity index 83% rename from java/test/processing/mode/java/preproc/issue/MessageSimplifierUtilTest.java rename to java/test/processing/mode/java/preproc/MessageSimplifierUtilTest.java index 0072d2cb47..d663964d1c 100644 --- a/java/test/processing/mode/java/preproc/issue/MessageSimplifierUtilTest.java +++ b/java/test/processing/mode/java/preproc/MessageSimplifierUtilTest.java @@ -1,8 +1,8 @@ -package processing.mode.java.preproc.issue; +package processing.mode.java.preproc; import org.junit.Assert; import org.junit.Test; -import processing.mode.java.preproc.issue.PreprocessIssueMessageSimplifier; +import processing.mode.java.preproc.PreprocessIssueMessageSimplifier; public class MessageSimplifierUtilTest { diff --git a/java/test/processing/mode/java/preproc/issue/MismatchedInputMessageSimplifierStrategyTest.java b/java/test/processing/mode/java/preproc/MismatchedInputMessageSimplifierStrategyTest.java similarity index 52% rename from java/test/processing/mode/java/preproc/issue/MismatchedInputMessageSimplifierStrategyTest.java rename to java/test/processing/mode/java/preproc/MismatchedInputMessageSimplifierStrategyTest.java index da38118bd5..c3da11d8db 100644 --- a/java/test/processing/mode/java/preproc/issue/MismatchedInputMessageSimplifierStrategyTest.java +++ b/java/test/processing/mode/java/preproc/MismatchedInputMessageSimplifierStrategyTest.java @@ -1,8 +1,10 @@ -package processing.mode.java.preproc.issue; +package processing.mode.java.preproc; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import processing.mode.java.preproc.PdeIssueEmitter; +import processing.mode.java.preproc.PreprocessIssueMessageSimplifier; import java.util.Optional; @@ -19,13 +21,13 @@ public void setup() { @Test public void testPresent() { - Optional msg = strategy.simplify("mismatched input 'final' expecting {'instanceof', ';', ',', '.', '>', '<', '==', '<=', '>=', '!=', '&&', '||', '++', '--', '+', '-', '*', '/', '&', '|', '^', '%', '::'}"); + Optional msg = strategy.simplify("mismatched input 'final' expecting {'instanceof', ';', ',', '.', '>', '<', '==', '<=', '>=', '!=', '&&', '||', '++', '--', '+', '-', '*', '/', '&', '|', '^', '%', '::'}"); Assert.assertTrue(msg.isPresent()); } @Test public void testNotPresent() { - Optional msg = strategy.simplify("String x = \" \\\" \""); + Optional msg = strategy.simplify("String x = \" \\\" \""); Assert.assertTrue(msg.isEmpty()); } diff --git a/java/test/processing/mode/java/preproc/issue/MissingChevMessageSimplifierStrategyTest.java b/java/test/processing/mode/java/preproc/MissingChevMessageSimplifierStrategyTest.java similarity index 60% rename from java/test/processing/mode/java/preproc/issue/MissingChevMessageSimplifierStrategyTest.java rename to java/test/processing/mode/java/preproc/MissingChevMessageSimplifierStrategyTest.java index a40b9eef36..9b6d4d6c8a 100644 --- a/java/test/processing/mode/java/preproc/issue/MissingChevMessageSimplifierStrategyTest.java +++ b/java/test/processing/mode/java/preproc/MissingChevMessageSimplifierStrategyTest.java @@ -1,9 +1,10 @@ -package processing.mode.java.preproc.issue; +package processing.mode.java.preproc; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import processing.mode.java.preproc.issue.IssueMessageSimplification; +import processing.mode.java.preproc.PdeIssueEmitter; +import processing.mode.java.preproc.PreprocessIssueMessageSimplifier; import java.util.Optional; @@ -19,13 +20,13 @@ public void setup() { @Test public void testPresent() { - Optional msg = strategy.simplify("class Test msg = strategy.simplify("class Test msg = strategy.simplify("class {"); + Optional msg = strategy.simplify("class {"); Assert.assertTrue(msg.isEmpty()); } diff --git a/java/test/processing/mode/java/preproc/issue/MissingClassNameMessageSimplifierStrategyTest.java b/java/test/processing/mode/java/preproc/MissingClassNameMessageSimplifierStrategyTest.java similarity index 58% rename from java/test/processing/mode/java/preproc/issue/MissingClassNameMessageSimplifierStrategyTest.java rename to java/test/processing/mode/java/preproc/MissingClassNameMessageSimplifierStrategyTest.java index 94b2d99609..ebbfe6659c 100644 --- a/java/test/processing/mode/java/preproc/issue/MissingClassNameMessageSimplifierStrategyTest.java +++ b/java/test/processing/mode/java/preproc/MissingClassNameMessageSimplifierStrategyTest.java @@ -1,9 +1,10 @@ -package processing.mode.java.preproc.issue; +package processing.mode.java.preproc; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import processing.mode.java.preproc.issue.IssueMessageSimplification; +import processing.mode.java.preproc.PdeIssueEmitter; +import processing.mode.java.preproc.PreprocessIssueMessageSimplifier; import java.util.Optional; @@ -19,19 +20,19 @@ public void setup() { @Test public void testPresentExtends() { - Optional msg = strategy.simplify("class extends Base\n{"); + Optional msg = strategy.simplify("class extends Base\n{"); Assert.assertTrue(msg.isPresent()); } @Test public void testPresentNoExtends() { - Optional msg = strategy.simplify("class \n{"); + Optional msg = strategy.simplify("class \n{"); Assert.assertTrue(msg.isPresent()); } @Test public void testNotPresent() { - Optional msg = strategy.simplify("int x = y"); + Optional msg = strategy.simplify("int x = y"); Assert.assertTrue(msg.isEmpty()); } diff --git a/java/test/processing/mode/java/preproc/issue/MissingCurlyMessageSimplifierStrategyTest.java b/java/test/processing/mode/java/preproc/MissingCurlyMessageSimplifierStrategyTest.java similarity index 60% rename from java/test/processing/mode/java/preproc/issue/MissingCurlyMessageSimplifierStrategyTest.java rename to java/test/processing/mode/java/preproc/MissingCurlyMessageSimplifierStrategyTest.java index 81b005f0de..c63e33f134 100644 --- a/java/test/processing/mode/java/preproc/issue/MissingCurlyMessageSimplifierStrategyTest.java +++ b/java/test/processing/mode/java/preproc/MissingCurlyMessageSimplifierStrategyTest.java @@ -1,10 +1,10 @@ -package processing.mode.java.preproc.issue; +package processing.mode.java.preproc; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import processing.mode.java.preproc.issue.IssueMessageSimplification; -import processing.mode.java.preproc.issue.PreprocessIssueMessageSimplifier; +import processing.mode.java.preproc.PdeIssueEmitter; +import processing.mode.java.preproc.PreprocessIssueMessageSimplifier; import java.util.Optional; @@ -20,13 +20,13 @@ public void setup() { @Test public void testPresent() { - Optional msg = strategy.simplify("class Test {"); + Optional msg = strategy.simplify("class Test {"); Assert.assertTrue(msg.isPresent()); } @Test public void testNotPresent() { - Optional msg = strategy.simplify("class Test { }"); + Optional msg = strategy.simplify("class Test { }"); Assert.assertTrue(msg.isEmpty()); } diff --git a/java/test/processing/mode/java/preproc/issue/MissingDoubleQuoteMessageSimplifierStrategyTest.java b/java/test/processing/mode/java/preproc/MissingDoubleQuoteMessageSimplifierStrategyTest.java similarity index 59% rename from java/test/processing/mode/java/preproc/issue/MissingDoubleQuoteMessageSimplifierStrategyTest.java rename to java/test/processing/mode/java/preproc/MissingDoubleQuoteMessageSimplifierStrategyTest.java index 17b3d47a37..bb4e79c3bb 100644 --- a/java/test/processing/mode/java/preproc/issue/MissingDoubleQuoteMessageSimplifierStrategyTest.java +++ b/java/test/processing/mode/java/preproc/MissingDoubleQuoteMessageSimplifierStrategyTest.java @@ -1,9 +1,10 @@ -package processing.mode.java.preproc.issue; +package processing.mode.java.preproc; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import processing.mode.java.preproc.issue.IssueMessageSimplification; +import processing.mode.java.preproc.PdeIssueEmitter; +import processing.mode.java.preproc.PreprocessIssueMessageSimplifier; import java.util.Optional; @@ -19,13 +20,13 @@ public void setup() { @Test public void testPresent() { - Optional msg = strategy.simplify("String x = \" \" \""); + Optional msg = strategy.simplify("String x = \" \" \""); Assert.assertTrue(msg.isPresent()); } @Test public void testNotPresent() { - Optional msg = strategy.simplify("String x = \" \\\" \""); + Optional msg = strategy.simplify("String x = \" \\\" \""); Assert.assertTrue(msg.isEmpty()); } diff --git a/java/test/processing/mode/java/preproc/issue/MissingGenericTypeMessageSimplifierStrategyTest.java b/java/test/processing/mode/java/preproc/MissingGenericTypeMessageSimplifierStrategyTest.java similarity index 62% rename from java/test/processing/mode/java/preproc/issue/MissingGenericTypeMessageSimplifierStrategyTest.java rename to java/test/processing/mode/java/preproc/MissingGenericTypeMessageSimplifierStrategyTest.java index 2393a586bd..e6847470f9 100644 --- a/java/test/processing/mode/java/preproc/issue/MissingGenericTypeMessageSimplifierStrategyTest.java +++ b/java/test/processing/mode/java/preproc/MissingGenericTypeMessageSimplifierStrategyTest.java @@ -1,9 +1,10 @@ -package processing.mode.java.preproc.issue; +package processing.mode.java.preproc; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import processing.mode.java.preproc.issue.IssueMessageSimplification; +import processing.mode.java.preproc.PdeIssueEmitter; +import processing.mode.java.preproc.PreprocessIssueMessageSimplifier; import java.util.Optional; @@ -19,13 +20,13 @@ public void setup() { @Test public void testPresent() { - Optional msg = strategy.simplify("<>'"); + Optional msg = strategy.simplify("<>'"); Assert.assertTrue(msg.isPresent()); } @Test public void testNotPresent() { - Optional msg = strategy.simplify("class {"); + Optional msg = strategy.simplify("class {"); Assert.assertTrue(msg.isEmpty()); } diff --git a/java/test/processing/mode/java/preproc/issue/MissingIdentifierMessageSimplifierStrategyTest.java b/java/test/processing/mode/java/preproc/MissingIdentifierMessageSimplifierStrategyTest.java similarity index 60% rename from java/test/processing/mode/java/preproc/issue/MissingIdentifierMessageSimplifierStrategyTest.java rename to java/test/processing/mode/java/preproc/MissingIdentifierMessageSimplifierStrategyTest.java index 9333b961d2..924aec011f 100644 --- a/java/test/processing/mode/java/preproc/issue/MissingIdentifierMessageSimplifierStrategyTest.java +++ b/java/test/processing/mode/java/preproc/MissingIdentifierMessageSimplifierStrategyTest.java @@ -1,8 +1,10 @@ -package processing.mode.java.preproc.issue; +package processing.mode.java.preproc; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import processing.mode.java.preproc.PdeIssueEmitter; +import processing.mode.java.preproc.PreprocessIssueMessageSimplifier; import java.util.Optional; @@ -19,13 +21,13 @@ public void setup() { @Test public void testPresent() { - Optional msg = strategy.simplify("Missing identifier at ';'"); + Optional msg = strategy.simplify("Missing identifier at ';'"); Assert.assertTrue(msg.isPresent()); } @Test public void testNotPresent() { - Optional msg = strategy.simplify("String x = \" \\\" \""); + Optional msg = strategy.simplify("String x = \" \\\" \""); Assert.assertTrue(msg.isEmpty()); } diff --git a/java/test/processing/mode/java/preproc/issue/MissingMethodNameMessageSimplifierStrategyTest.java b/java/test/processing/mode/java/preproc/MissingMethodNameMessageSimplifierStrategyTest.java similarity index 56% rename from java/test/processing/mode/java/preproc/issue/MissingMethodNameMessageSimplifierStrategyTest.java rename to java/test/processing/mode/java/preproc/MissingMethodNameMessageSimplifierStrategyTest.java index ac9e315e3a..e94dba63bd 100644 --- a/java/test/processing/mode/java/preproc/issue/MissingMethodNameMessageSimplifierStrategyTest.java +++ b/java/test/processing/mode/java/preproc/MissingMethodNameMessageSimplifierStrategyTest.java @@ -1,9 +1,10 @@ -package processing.mode.java.preproc.issue; +package processing.mode.java.preproc; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import processing.mode.java.preproc.issue.IssueMessageSimplification; +import processing.mode.java.preproc.PdeIssueEmitter; +import processing.mode.java.preproc.PreprocessIssueMessageSimplifier; import java.util.Optional; @@ -19,25 +20,25 @@ public void setup() { @Test public void testPresent() { - Optional msg = strategy.simplify("void (int x) \n{"); + Optional msg = strategy.simplify("void (int x) \n{"); Assert.assertTrue(msg.isPresent()); } @Test public void testPresentNoSpace() { - Optional msg = strategy.simplify("test(int x) \n{"); + Optional msg = strategy.simplify("test(int x) \n{"); Assert.assertTrue(msg.isPresent()); } @Test public void testPresentUnderscore() { - Optional msg = strategy.simplify("void (int x_y) \n{"); + Optional msg = strategy.simplify("void (int x_y) \n{"); Assert.assertTrue(msg.isPresent()); } @Test public void testNotPresent() { - Optional msg = strategy.simplify("int x = y"); + Optional msg = strategy.simplify("int x = y"); Assert.assertTrue(msg.isEmpty()); } diff --git a/java/test/processing/mode/java/preproc/issue/MissingParenMessageSimplifierStrategyTest.java b/java/test/processing/mode/java/preproc/MissingParenMessageSimplifierStrategyTest.java similarity index 59% rename from java/test/processing/mode/java/preproc/issue/MissingParenMessageSimplifierStrategyTest.java rename to java/test/processing/mode/java/preproc/MissingParenMessageSimplifierStrategyTest.java index c71a2070a0..f6e8e3de27 100644 --- a/java/test/processing/mode/java/preproc/issue/MissingParenMessageSimplifierStrategyTest.java +++ b/java/test/processing/mode/java/preproc/MissingParenMessageSimplifierStrategyTest.java @@ -1,9 +1,10 @@ -package processing.mode.java.preproc.issue; +package processing.mode.java.preproc; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import processing.mode.java.preproc.issue.IssueMessageSimplification; +import processing.mode.java.preproc.PdeIssueEmitter; +import processing.mode.java.preproc.PreprocessIssueMessageSimplifier; import java.util.Optional; @@ -19,13 +20,13 @@ public void setup() { @Test public void testPresent() { - Optional msg = strategy.simplify("int x = ((5 + 4) / 3"); + Optional msg = strategy.simplify("int x = ((5 + 4) / 3"); Assert.assertTrue(msg.isPresent()); } @Test public void testNotPresent() { - Optional msg = strategy.simplify("int x = (y/5)/(\n4)"); + Optional msg = strategy.simplify("int x = (y/5)/(\n4)"); Assert.assertTrue(msg.isEmpty()); } diff --git a/java/test/processing/mode/java/preproc/issue/MissingSingleQuoteMessageSimplifierStrategyTest.java b/java/test/processing/mode/java/preproc/MissingSingleQuoteMessageSimplifierStrategyTest.java similarity index 60% rename from java/test/processing/mode/java/preproc/issue/MissingSingleQuoteMessageSimplifierStrategyTest.java rename to java/test/processing/mode/java/preproc/MissingSingleQuoteMessageSimplifierStrategyTest.java index 68cc1e15b2..0c53c1df65 100644 --- a/java/test/processing/mode/java/preproc/issue/MissingSingleQuoteMessageSimplifierStrategyTest.java +++ b/java/test/processing/mode/java/preproc/MissingSingleQuoteMessageSimplifierStrategyTest.java @@ -1,9 +1,10 @@ -package processing.mode.java.preproc.issue; +package processing.mode.java.preproc; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import processing.mode.java.preproc.issue.IssueMessageSimplification; +import processing.mode.java.preproc.PdeIssueEmitter; +import processing.mode.java.preproc.PreprocessIssueMessageSimplifier; import java.util.Optional; @@ -18,13 +19,13 @@ public void setup() { @Test public void testPresent() { - Optional msg = strategy.simplify("char x = '"); + Optional msg = strategy.simplify("char x = '"); Assert.assertTrue(msg.isPresent()); } @Test public void testNotPresent() { - Optional msg = strategy.simplify("char x = '\\''"); + Optional msg = strategy.simplify("char x = '\\''"); Assert.assertTrue(msg.isEmpty()); } diff --git a/java/test/processing/mode/java/preproc/issue/MissingVariableNameMessageSimplifierStrategyTest.java b/java/test/processing/mode/java/preproc/MissingVariableNameMessageSimplifierStrategyTest.java similarity index 61% rename from java/test/processing/mode/java/preproc/issue/MissingVariableNameMessageSimplifierStrategyTest.java rename to java/test/processing/mode/java/preproc/MissingVariableNameMessageSimplifierStrategyTest.java index 7f95a65c6c..e2c69757a4 100644 --- a/java/test/processing/mode/java/preproc/issue/MissingVariableNameMessageSimplifierStrategyTest.java +++ b/java/test/processing/mode/java/preproc/MissingVariableNameMessageSimplifierStrategyTest.java @@ -1,9 +1,10 @@ -package processing.mode.java.preproc.issue; +package processing.mode.java.preproc; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import processing.mode.java.preproc.issue.IssueMessageSimplification; +import processing.mode.java.preproc.PdeIssueEmitter; +import processing.mode.java.preproc.PreprocessIssueMessageSimplifier; import java.util.Optional; @@ -19,13 +20,13 @@ public void setup() { @Test public void testPresent() { - Optional msg = strategy.simplify("char = ';"); + Optional msg = strategy.simplify("char = ';"); Assert.assertTrue(msg.isPresent()); } @Test public void testNotPresent() { - Optional msg = strategy.simplify("class test {"); + Optional msg = strategy.simplify("class test {"); Assert.assertTrue(msg.isEmpty()); } diff --git a/java/test/processing/mode/java/preproc/util/IssueLocationFactoryTest.java b/java/test/processing/mode/java/preproc/util/IssueLocationFactoryTest.java index 553ce869ea..67ad299745 100644 --- a/java/test/processing/mode/java/preproc/util/IssueLocationFactoryTest.java +++ b/java/test/processing/mode/java/preproc/util/IssueLocationFactoryTest.java @@ -3,15 +3,13 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import processing.mode.java.preproc.issue.IssueLocation; -import processing.mode.java.preproc.issue.IssueLocationFactory; -import processing.mode.java.preproc.issue.IssueMessageSimplification; +import processing.mode.java.preproc.PdeIssueEmitter; public class IssueLocationFactoryTest { private String source; - private IssueLocation issueLocation; + private PdeIssueEmitter.IssueLocation issueLocation; @Before public void setup() { @@ -33,8 +31,8 @@ public void setup() { @Test public void getLineWithOffsetApplies() { - issueLocation = IssueLocationFactory.getLineWithOffset( - new IssueMessageSimplification("test message", true), + issueLocation = PdeIssueEmitter.IssueLocationFactory.getLineWithOffset( + new PdeIssueEmitter.IssueMessageSimplification("test message", true), 15, 0, source @@ -46,8 +44,8 @@ public void getLineWithOffsetApplies() { @Test public void getLineWithOffsetNotApplies() { - issueLocation = IssueLocationFactory.getLineWithOffset( - new IssueMessageSimplification("test message", false), + issueLocation = PdeIssueEmitter.IssueLocationFactory.getLineWithOffset( + new PdeIssueEmitter.IssueMessageSimplification("test message", false), 15, 0, source @@ -59,8 +57,8 @@ public void getLineWithOffsetNotApplies() { @Test public void getLineWithOffsetEndWhite() { - issueLocation = IssueLocationFactory.getLineWithOffset( - new IssueMessageSimplification("test message", true), + issueLocation = PdeIssueEmitter.IssueLocationFactory.getLineWithOffset( + new PdeIssueEmitter.IssueMessageSimplification("test message", true), 14, 0, "\n\n\n\n\n\n\n\n\n\n\nnoFill()\nellipse(50,50,50,50)\n" diff --git a/java/test/processing/mode/java/preproc/util/PreprocessIssueMessageSimplifierTest.java b/java/test/processing/mode/java/preproc/util/PreprocessIssueMessageSimplifierTest.java index 8131ba96e1..617313b468 100644 --- a/java/test/processing/mode/java/preproc/util/PreprocessIssueMessageSimplifierTest.java +++ b/java/test/processing/mode/java/preproc/util/PreprocessIssueMessageSimplifierTest.java @@ -2,7 +2,7 @@ import org.junit.Assert; import org.junit.Test; -import processing.mode.java.preproc.issue.PreprocessIssueMessageSimplifier; +import processing.mode.java.preproc.PreprocessIssueMessageSimplifier; public class PreprocessIssueMessageSimplifierTest {