From 851bd9faa26ee71be54d090861c9216d89f04d83 Mon Sep 17 00:00:00 2001 From: GKFX Date: Wed, 4 Jun 2014 14:45:38 +0100 Subject: [PATCH 1/7] Autoformat bug fixes Miscellaneous fixes. I broke the indent-on-continued-lines feature, but it was bad to start with. --- app/src/processing/mode/java/AutoFormat.java | 322 +++++++++---------- 1 file changed, 157 insertions(+), 165 deletions(-) diff --git a/app/src/processing/mode/java/AutoFormat.java b/app/src/processing/mode/java/AutoFormat.java index 91d8a7cecd..b862510b55 100644 --- a/app/src/processing/mode/java/AutoFormat.java +++ b/app/src/processing/mode/java/AutoFormat.java @@ -48,33 +48,42 @@ public class AutoFormat implements Formatter { private final StringBuilder buf = new StringBuilder(); private final StringBuilder result = new StringBuilder(); + /** The number of spaces in one indent. Constant. */ private int indentValue; + + /** Set when the end of the chars array is reached */ private boolean EOF; - private boolean a_flg, if_flg, s_flag, elseFlag; + + private boolean if_flg; + private boolean s_flag; + private boolean elseFlag; /** Number of ? entered without exiting at : of a?b:c structures. */ private int conditionalLevel; private int pos; - private int c_level; + private int level; private int[][] sp_flg; private int[][] s_ind; private int if_lev; /** Number of curly brackets entered and not exited. */ - private int level; + private int curlyLvl; /** Number of parentheses entered and not exited. */ private int parenLevel; private int[] ind; private int[] p_flg; - private char l_char; private int[][] s_tabs; private boolean jdoc_flag; - private char cc; + + /** The number of times to indent at a given point */ private int tabs; - private char c; + + /** + * The last non-space seen by nextChar() + */ private char lastNonWhitespace = 0; private final Stack castFlags = new Stack(); @@ -85,9 +94,9 @@ private void handleMultiLineComment() { char ch; buf.append(ch = nextChar()); // extra char - while (true) { + while (!EOF) { buf.append(ch = nextChar()); - while (ch != '/') { + while (ch != '/' && !EOF) { if (ch == '\n') { // lineNumber++; writeIndentedComment(); @@ -107,10 +116,14 @@ private void handleMultiLineComment() { return; } - + + /** + * Pumps nextChar into buf until \n or EOF, then calls + * writeIndentedLine() and set s_flag to true. + */ private void handleSingleLineComment() { char ch = nextChar(); - while (ch != '\n') { + while (ch != '\n' && !EOF) { buf.append(ch); ch = nextChar(); } @@ -119,57 +132,25 @@ private void handleSingleLineComment() { s_flag = true; } - -// /** -// * Transfers buf to result until a character is reached that -// * is neither \, \n, or in a string. \n is not written, but -// * writeIndentedLine is called on finding it. -// * @return The first character not listed above. -// */ -// private char get_string() { -// char ch1, ch2; -// while (true) { -// buf.append(ch1 = nextChar()); -// if (ch1 == '\\') { -// buf.append(nextChar()); -// } else if (ch1 == '\'' || ch1 == '\"') { -// buf.append(ch2 = nextChar()); -// while (!EOF && ch2 != ch1) { -// if (ch2 == '\\') { -// // Next char is escaped, ignore. -// buf.append(nextChar()); -// } -// buf.append(ch2 = nextChar()); -// } -// } else if (ch1 == '\n') { -// writeIndentedLine(); -// a_flg = true; -// } else { -// return ch1; -// } -// } -// } - + /** + * Prints correct indent and the contents of buf to result, + * and sets elseFlag and s_flag to new values. + * If buf is empty, prints nothing but can change flags. + */ private void writeIndentedLine() { if (buf.length() == 0) { if (s_flag) { - s_flag = a_flg = elseFlag = false; + s_flag = indentFlag = elseFlag = false; } return; } if (s_flag) { - final boolean shouldIndent = - (tabs > 0) && (buf.charAt(0) != '{') && a_flg; - if (shouldIndent) { - tabs++; - } + boolean shouldIndent = (buf.charAt(0) != '{'); + if (shouldIndent) tabs++; printIndentation(); s_flag = false; - if (shouldIndent) { - tabs--; - } - a_flg = false; + if (shouldIndent) tabs--; } if (elseFlag) { if (lastNonSpaceChar() == '}') { @@ -181,120 +162,120 @@ private void writeIndentedLine() { result.append(buf); buf.setLength(0); } - + + /** + * @return the last character in result not ' ' or '\n'. + */ private char lastNonSpaceChar() { for (int i = result.length() - 1; i >= 0; i--) { - char c_i = result.charAt(i); - if (c_i != ' ' && c_i != '\n') return c_i; + char chI = result.charAt(i); + if (chI != ' ' && chI != '\n') return chI; } return 0; } + /** + * Called by handleMultilineComment.
+ * Sets jdoc_flag if at the start of a doc comment. + * Sends buf to result with proper indents, then clears buf.
+ * Does nothing if buf is empty. + */ private void writeIndentedComment() { - final boolean saved_s_flag = s_flag; - if (buf.length() > 0) { - if (s_flag) { - printIndentation(); - s_flag = false; - } - int i = 0; - while (buf.charAt(i) == ' ') { - i++; - } - if (lookup_com("/**")) { - jdoc_flag = true; - } - if (buf.charAt(i) == '/' && buf.charAt(i + 1) == '*') { - if (saved_s_flag && getLastNonWhitespace() != ';') { - result.append(buf.substring(i)); - } else { - result.append(buf); - } + if (buf.length() == 0) return; + + int firstNonSpace = 0; + while (buf.charAt(firstNonSpace) == ' ') firstNonSpace++; + if (lookup_com("/**")) jdoc_flag = true; + + if (s_flag) printIndentation(); + + if (buf.charAt(firstNonSpace) == '/' && buf.charAt(firstNonSpace+1) == '*') { + if (s_flag && lastNonWhitespace != ';') { + result.append(buf.substring(firstNonSpace)); + } else { result.append(buf); } + } else { + if (buf.charAt(firstNonSpace) == '*' || !jdoc_flag) { + result.append(" " + buf.substring(firstNonSpace)); } else { - if (buf.charAt(i) == '*' || !jdoc_flag) { - result.append((" " + buf.substring(i))); - } else { - result.append((" * " + buf.substring(i))); - } + result.append(" * " + buf.substring(firstNonSpace)); } - buf.setLength(0); } + buf.setLength(0); } + /** + * Makes tabs >= 0 and appends tabs*indentValue + * spaces to result. + */ private void printIndentation() { - if (tabs < 0) { + if (tabs <= 0) { tabs = 0; - } - if (tabs == 0) { return; } final int spaces = tabs * indentValue; - for (int k = 0; k < spaces; k++) { + for (int i = 0; i < spaces; i++) { result.append(' '); } } + /** + * @return chars[pos+1] or '\0' if out-of-bounds. + */ private char peek() { - if (pos + 1 >= chars.length) { - return 0; - } - return chars[pos + 1]; - } - - - private int getLastNonWhitespace() { - return lastNonWhitespace; + return (pos + 1 >= chars.length) ? 0 : chars[pos + 1]; } + /** + * Sets pos to the position of the next character that is not ' ' + * in chars. If chars[pos] != ' ' already, it will still move on. + * Then sets EOF if pos has reached the end, or reverses pos by 1 if it + * has not. + *
Does nothing if EOF. + */ private void advanceToNonSpace() { - if (EOF) { - return; - } + if (EOF) return; + do { pos++; } while (pos < chars.length && chars[pos] == ' '); - if (pos == chars.length - 1) { - EOF = true; - } else { - pos--; // reset for nextChar() - } + + if (pos == chars.length - 1) EOF = true; + else pos--; // reset for nextChar() } + /** + * Increments pos, sets EOF if needed, and returns the new + * chars[pos] or zero if out-of-bounds. + * Sets lastNonWhitespace if chars[pos] isn't whitespace. + * Does nothing and returns zero if EOF was set when it was called. + */ private char nextChar() { - if (EOF) { - return '\0'; - } + if (EOF) return 0; pos++; - char retVal; - if (pos < chars.length) { - retVal = chars[pos]; - if (!Character.isWhitespace(retVal)) - lastNonWhitespace = retVal; - } else { - retVal = '\0'; - } - if (pos == chars.length-1) { - EOF = true; - } + if (pos == chars.length-1) EOF = true; + if (pos >= chars.length) return 0; + + char retVal = chars[pos]; + if (!Character.isWhitespace(retVal)) lastNonWhitespace = retVal; return retVal; } private void gotElse() { - tabs = s_tabs[c_level][if_lev]; - p_flg[level] = sp_flg[c_level][if_lev]; - ind[level] = s_ind[c_level][if_lev]; + tabs = s_tabs[curlyLvl][if_lev]; + p_flg[level] = sp_flg[curlyLvl][if_lev]; + ind[level] = s_ind[curlyLvl][if_lev]; if_flg = true; } - private boolean readUntilNewLine() { + private boolean readForNewLine() { final int savedTabs = tabs; char c = peek(); while (!EOF && (c == '\t' || c == ' ')) { @@ -327,24 +308,39 @@ private boolean readUntilNewLine() { } + /** + * Sees if buf is of the form [optional whitespace][keyword][optional anything]. + * It won't allow keyword to be directly followed by an alphanumeric, _, or &. + * Will be different if keyword contains regex codes. + */ private boolean lookup(final String keyword) { return Pattern.matches("^\\s*" + keyword + "(?![a-zA-Z0-9_&]).*$", buf); } + /** + * Sees if buf is of the form [optional whitespace][keyword][optional anything]. + * It *will* allow keyword to be directly followed by an alphanumeric, _, or &. + * Will be different if keyword contains regex codes (except *, which is fine). + */ private boolean lookup_com(final String keyword) { - final String regex = "^\\s*" + keyword.replaceAll("\\*", "\\\\*") + ".*$"; + final String regex = "^\\s*" + keyword.replace("*", "\\*") + ".*$"; return Pattern.matches(regex, buf); } + /** + * Takes all whitespace off the end of its argument. + */ private void trimRight(final StringBuilder sb) { - while (sb.length() >= 1 - && Character.isWhitespace(sb.charAt(sb.length() - 1))) + while (sb.length() >= 1 && Character.isWhitespace(sb.charAt(sb.length() - 1))) sb.setLength(sb.length() - 1); } + /** + * Entry point + */ public String format(final String source) { final String normalizedText = source.replaceAll("\r", ""); final String cleanText = @@ -354,12 +350,13 @@ public String format(final String source) { indentValue = Preferences.getInteger("editor.tabs.size"); // lineNumber = 0; - boolean forFlag = a_flg = if_flg = false; + boolean forFlag = if_flg = false; s_flag = true; int forParenthLevel = 0; - conditionalLevel = parenLevel = c_level = if_lev = level = 0; + conditionalLevel = parenLevel = curlyLvl = if_lev = level = 0; tabs = 0; jdoc_flag = false; + char cc; int[] s_level = new int[10]; sp_flg = new int[20][10]; @@ -376,11 +373,10 @@ public String format(final String source) { EOF = false; // set in nextChar() when EOF while (!EOF) { - c = nextChar(); + char c = nextChar(); switch (c) { default: buf.append(c); - l_char = c; break; case ',': @@ -411,15 +407,14 @@ public String format(final String source) { case '\n': // lineNumber++; if (EOF) { + writeIndentedLine(); break; } elseFlag = lookup("else"); - if (elseFlag) { - gotElse(); - } + if (elseFlag) gotElse(); + if (lookup_com("//")) { - final char lastChar = buf.charAt(buf.length() - 1); - if (lastChar == '\n') { + if (buf.charAt(buf.length() - 1) == '\n') { buf.setLength(buf.length() - 1); } } @@ -430,24 +425,22 @@ public String format(final String source) { if (elseFlag) { p_flg[level]++; tabs++; - } else if (getLastNonWhitespace() == l_char) { - a_flg = true; - } + } break; case '{': elseFlag = lookup("else"); if (elseFlag) gotElse(); - if (s_if_lev.length == c_level) { + if (s_if_lev.length == curlyLvl) { s_if_lev = PApplet.expand(s_if_lev); s_if_flg = PApplet.expand(s_if_flg); } - s_if_lev[c_level] = if_lev; - s_if_flg[c_level] = if_flg; + s_if_lev[curlyLvl] = if_lev; + s_if_flg[curlyLvl] = if_flg; if_lev = 0; if_flg = false; - c_level++; + curlyLvl++; if (s_flag && p_flg[level] != 0) { p_flg[level]--; tabs--; @@ -458,7 +451,7 @@ public String format(final String source) { buf.append(" "); buf.append(c); writeIndentedLine(); - readUntilNewLine(); + readForNewLine(); writeIndentedLine(); result.append("\n"); @@ -467,23 +460,22 @@ public String format(final String source) { if (p_flg[level] > 0) { ind[level] = 1; level++; - s_level[level] = c_level; + s_level[level] = curlyLvl; } break; case '}': - c_level--; - if (c_level < 0) { - c_level = 0; + curlyLvl--; + if (curlyLvl < 0) { + curlyLvl = 0; buf.append(c); writeIndentedLine(); - } else { - if_lev = s_if_lev[c_level] - 1; + if_lev = s_if_lev[curlyLvl] - 1; if (if_lev < 0) { if_lev = 0; } - if_flg = s_if_flg[c_level]; + if_flg = s_if_flg[curlyLvl]; trimRight(buf); writeIndentedLine(); tabs--; @@ -496,11 +488,11 @@ public String format(final String source) { result.append(nextChar()); } - readUntilNewLine(); + readForNewLine(); writeIndentedLine(); result.append('\n'); s_flag = true; - if (c_level < s_level[level]) { + if (curlyLvl < s_level[level]) { if (level > 0) { level--; } @@ -530,8 +522,7 @@ public String format(final String source) { cc = nextChar(); } buf.append(cc); - if (readUntilNewLine()) { - l_char = cc; + if (readForNewLine()) { // push a newline into the stream chars[pos--] = '\n'; } @@ -552,7 +543,7 @@ public String format(final String source) { tabs -= p_flg[level]; p_flg[level] = 0; } - readUntilNewLine(); + readForNewLine(); writeIndentedLine(); result.append("\n"); s_flag = true; @@ -579,13 +570,12 @@ public String format(final String source) { case ':': // Java 8 :: operator. if (peek() == ':') { - writeIndentedLine(); result.append(c).append(nextChar()); break; } // End a?b:c structures. - else if (conditionalLevel>0) { + else if (conditionalLevel > 0) { conditionalLevel--; buf.append(c); break; @@ -612,7 +602,7 @@ else if (forFlag) { if (peek() == ';') { result.append(nextChar()); } - readUntilNewLine(); + readForNewLine(); writeIndentedLine(); result.append('\n'); s_flag = true; @@ -637,7 +627,6 @@ else if (forFlag) { break; case ')': - final boolean isCast = castFlags.isEmpty() ? false : castFlags.pop(); parenLevel--; @@ -648,16 +637,13 @@ else if (forFlag) { forFlag = false; } - if (parenLevel < 0) { - parenLevel = 0; - } + if (parenLevel < 0) parenLevel = 0; buf.append(c); writeIndentedLine(); - if (readUntilNewLine()) { + if (readForNewLine()) { chars[pos--] = '\n'; if (parenLevel != 0) { - a_flg = true; } else if (tabs > 0 && !isCast) { p_flg[level]++; tabs++; @@ -681,26 +667,32 @@ else if (forFlag) { buf.append(c); parenLevel++; - // isFor says "is it the start of a for?". If it is, we set forFlag and + // isFor says "Is it the start of a for?". If it is, we set forFlag and // forParenthLevel. If it is not parenth_lvl was incremented above and // that's it. if (isFor) { - if (!forFlag) { + if (!forFlag) { forParenthLevel = parenLevel; forFlag = true; } } else if (isIf) { writeIndentedLine(); - s_tabs[c_level][if_lev] = tabs; - sp_flg[c_level][if_lev] = p_flg[level]; - s_ind[c_level][if_lev] = ind[level]; + s_tabs[curlyLvl][if_lev] = tabs; + sp_flg[curlyLvl][if_lev] = p_flg[level]; + s_ind[curlyLvl][if_lev] = ind[level]; if_lev++; if_flg = true; } } // end switch } // end while not EOF - + final String formatted = result.toString(); + if (!formatted.replaceAll("[\\s\\*/]","") + .equals(source.replaceAll("[\\s\\*/]",""))) { + // Double-check - this thing's so buggy it might be best to have one. + throw new RuntimeException("The autoformatter did something funny. Please" + + "copy and paste your sketch into a new issue on GitHub."); + } return formatted.equals(cleanText) ? source : formatted; } } From b33b047ed1b8e4191a349f242d96acfd0f623a51 Mon Sep 17 00:00:00 2001 From: GKFX Date: Wed, 4 Jun 2014 16:09:11 +0100 Subject: [PATCH 2/7] Add indents on statements continued over new lines This wraps up my changes to the autoformatter. --- app/src/processing/mode/java/AutoFormat.java | 50 +++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/app/src/processing/mode/java/AutoFormat.java b/app/src/processing/mode/java/AutoFormat.java index b862510b55..38c29a512a 100644 --- a/app/src/processing/mode/java/AutoFormat.java +++ b/app/src/processing/mode/java/AutoFormat.java @@ -51,7 +51,7 @@ public class AutoFormat implements Formatter { /** The number of spaces in one indent. Constant. */ private int indentValue; - /** Set when the end of the chars array is reached */ + /** Set when the end of the chars array is reached. */ private boolean EOF; private boolean if_flg; @@ -67,6 +67,12 @@ public class AutoFormat implements Formatter { private int[][] s_ind; private int if_lev; + /** Is this statement (or whatever) unfinished? */ + private boolean inStatementFlag; + + /** Did the previous line overrun? */ + private boolean continuationFlag; + /** Number of curly brackets entered and not exited. */ private int curlyLvl; @@ -81,9 +87,7 @@ public class AutoFormat implements Formatter { /** The number of times to indent at a given point */ private int tabs; - /** - * The last non-space seen by nextChar() - */ + /** The last non-space seen by nextChar(). */ private char lastNonWhitespace = 0; private final Stack castFlags = new Stack(); @@ -141,16 +145,15 @@ private void handleSingleLineComment() { private void writeIndentedLine() { if (buf.length() == 0) { if (s_flag) { - s_flag = indentFlag = elseFlag = false; + s_flag = elseFlag = false; } return; } if (s_flag) { - boolean shouldIndent = (buf.charAt(0) != '{'); - if (shouldIndent) tabs++; + if (continuationFlag) tabs++; printIndentation(); s_flag = false; - if (shouldIndent) tabs--; + if (continuationFlag) tabs--; } if (elseFlag) { if (lastNonSpaceChar() == '}') { @@ -355,7 +358,7 @@ public String format(final String source) { int forParenthLevel = 0; conditionalLevel = parenLevel = curlyLvl = if_lev = level = 0; tabs = 0; - jdoc_flag = false; + jdoc_flag = continuationFlag = inStatementFlag = false; char cc; int[] s_level = new int[10]; @@ -376,6 +379,7 @@ public String format(final String source) { char c = nextChar(); switch (c) { default: + inStatementFlag = true; buf.append(c); break; @@ -425,10 +429,12 @@ public String format(final String source) { if (elseFlag) { p_flg[level]++; tabs++; - } + } + continuationFlag = inStatementFlag; break; case '{': + inStatementFlag = false; elseFlag = lookup("else"); if (elseFlag) gotElse(); @@ -465,7 +471,8 @@ public String format(final String source) { break; case '}': - curlyLvl--; + inStatementFlag = false; + curlyLvl--; if (curlyLvl < 0) { curlyLvl = 0; buf.append(c); @@ -507,6 +514,7 @@ public String format(final String source) { case '"': case '\'': + inStatementFlag = true; buf.append(c); cc = nextChar(); while (!EOF && cc != c) { @@ -537,6 +545,7 @@ public String format(final String source) { advanceToNonSpace(); break; } + inStatementFlag = false; buf.append(c); writeIndentedLine(); if (p_flg[level] > 0 && ind[level] == 0) { @@ -628,14 +637,11 @@ else if (forFlag) { case ')': final boolean isCast = castFlags.isEmpty() ? false : castFlags.pop(); - parenLevel--; // If we're further back than the start of a for loop, we've // left it. - if (forFlag && forParenthLevel > parenLevel) { - forFlag = false; - } + if (forFlag && forParenthLevel > parenLevel) forFlag = false; if (parenLevel < 0) parenLevel = 0; @@ -643,8 +649,7 @@ else if (forFlag) { writeIndentedLine(); if (readForNewLine()) { chars[pos--] = '\n'; - if (parenLevel != 0) { - } else if (tabs > 0 && !isCast) { + if (parenLevel == 0 && tabs > 0 && !isCast) { p_flg[level]++; tabs++; ind[level] = 0; @@ -670,11 +675,9 @@ else if (forFlag) { // isFor says "Is it the start of a for?". If it is, we set forFlag and // forParenthLevel. If it is not parenth_lvl was incremented above and // that's it. - if (isFor) { - if (!forFlag) { - forParenthLevel = parenLevel; - forFlag = true; - } + if (isFor && !forFlag) { + forParenthLevel = parenLevel; + forFlag = true; } else if (isIf) { writeIndentedLine(); s_tabs[curlyLvl][if_lev] = tabs; @@ -691,7 +694,8 @@ else if (forFlag) { .equals(source.replaceAll("[\\s\\*/]",""))) { // Double-check - this thing's so buggy it might be best to have one. throw new RuntimeException("The autoformatter did something funny. Please" + - "copy and paste your sketch into a new issue on GitHub."); + "copy and paste your sketch into a new issue on GitHub. \n" + + "https://github.com/processing/processing/issues/new"); } return formatted.equals(cleanText) ? source : formatted; } From d62425901018551abac297198a92001422a982ba Mon Sep 17 00:00:00 2001 From: GKFX Date: Wed, 4 Jun 2014 16:21:02 +0100 Subject: [PATCH 3/7] Autoformatter: avoid code being left in buf issue #2540 --- app/src/processing/mode/java/AutoFormat.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app/src/processing/mode/java/AutoFormat.java b/app/src/processing/mode/java/AutoFormat.java index 38c29a512a..5e4080ecac 100644 --- a/app/src/processing/mode/java/AutoFormat.java +++ b/app/src/processing/mode/java/AutoFormat.java @@ -688,15 +688,10 @@ else if (forFlag) { } } // end switch } // end while not EOF + + if (buf.length() > 0) writeIndentedLine(); final String formatted = result.toString(); - if (!formatted.replaceAll("[\\s\\*/]","") - .equals(source.replaceAll("[\\s\\*/]",""))) { - // Double-check - this thing's so buggy it might be best to have one. - throw new RuntimeException("The autoformatter did something funny. Please" + - "copy and paste your sketch into a new issue on GitHub. \n" + - "https://github.com/processing/processing/issues/new"); - } return formatted.equals(cleanText) ? source : formatted; } } From f8b296c85c2870d082e8f0677eeaa6255cee39d6 Mon Sep 17 00:00:00 2001 From: GKFX Date: Wed, 18 Jun 2014 14:28:46 +0100 Subject: [PATCH 4/7] Mostly finished AutoFormat.java - a few bugs left. Am commiting before attempting to fix by mass deletion of code. --- app/src/processing/mode/java/AutoFormat.java | 212 +++++++++++-------- 1 file changed, 122 insertions(+), 90 deletions(-) diff --git a/app/src/processing/mode/java/AutoFormat.java b/app/src/processing/mode/java/AutoFormat.java index 5e4080ecac..63f6f98468 100644 --- a/app/src/processing/mode/java/AutoFormat.java +++ b/app/src/processing/mode/java/AutoFormat.java @@ -54,31 +54,30 @@ public class AutoFormat implements Formatter { /** Set when the end of the chars array is reached. */ private boolean EOF; + private boolean inStatementFlag; // in a line of code + private boolean overflowFlag; // line overrunning? + private boolean arrayFlag; // in an array/after it in same statement? private boolean if_flg; - private boolean s_flag; + private boolean startFlag; // No buf has yet been writen to this line. private boolean elseFlag; - + /** Number of ? entered without exiting at : of a?b:c structures. */ private int conditionalLevel; - + + /** chars[pos] is where we're at. */ private int pos; private int level; private int[][] sp_flg; private int[][] s_ind; private int if_lev; - /** Is this statement (or whatever) unfinished? */ - private boolean inStatementFlag; - - /** Did the previous line overrun? */ - private boolean continuationFlag; - - /** Number of curly brackets entered and not exited. */ + /** Number of curly brackets entered and not exited, + excluding arrays. */ private int curlyLvl; /** Number of parentheses entered and not exited. */ private int parenLevel; - + private int[] ind; private int[] p_flg; private int[][] s_tabs; @@ -94,17 +93,15 @@ public class AutoFormat implements Formatter { private void handleMultiLineComment() { - final boolean saved_s_flag = s_flag; + final boolean saved_startFlag = startFlag; + buf.append(nextChar()); // So /*/ isn't self-closing. - char ch; - buf.append(ch = nextChar()); // extra char - while (!EOF) { - buf.append(ch = nextChar()); + for (char ch = nextChar(); !EOF; ch = nextChar()) { + buf.append(ch); while (ch != '/' && !EOF) { if (ch == '\n') { -// lineNumber++; writeIndentedComment(); - s_flag = true; + startFlag = true; } buf.append(ch = nextChar()); } @@ -115,15 +112,15 @@ private void handleMultiLineComment() { } writeIndentedComment(); - s_flag = saved_s_flag; + startFlag = saved_startFlag; jdoc_flag = false; return; } /** - * Pumps nextChar into buf until \n or EOF, then calls - * writeIndentedLine() and set s_flag to true. + * Pumps nextChar into buf until \n or EOF, then calls + * writeIndentedLine() and sets startFlag to true. */ private void handleSingleLineComment() { char ch = nextChar(); @@ -131,29 +128,28 @@ private void handleSingleLineComment() { buf.append(ch); ch = nextChar(); } -// lineNumber++; writeIndentedLine(); - s_flag = true; + startFlag = true; } - /** - * Prints correct indent and the contents of buf to result, - * and sets elseFlag and s_flag to new values. - * If buf is empty, prints nothing but can change flags. - */ private void writeIndentedLine() { + System.out.print(buf); if (buf.length() == 0) { - if (s_flag) { - s_flag = elseFlag = false; - } + if (startFlag) startFlag = elseFlag = false; return; } - if (s_flag) { - if (continuationFlag) tabs++; + if (startFlag) { + // Indent suppressed at eg. if{ and when + // buf is close-brackets only followed by ';'. + boolean indentMore = !buf.toString().matches("[\\s\\]\\}\\)]*;") + && (arrayFlag || buf.charAt(0) != '{') + && overflowFlag; + System.out.println(indentMore + ", " + overflowFlag); + if (indentMore) tabs++; printIndentation(); - s_flag = false; - if (continuationFlag) tabs--; + startFlag = false; + if (indentMore) tabs--; } if (elseFlag) { if (lastNonSpaceChar() == '}') { @@ -192,12 +188,14 @@ private void writeIndentedComment() { while (buf.charAt(firstNonSpace) == ' ') firstNonSpace++; if (lookup_com("/**")) jdoc_flag = true; - if (s_flag) printIndentation(); - + if (startFlag) printIndentation(); + if (buf.charAt(firstNonSpace) == '/' && buf.charAt(firstNonSpace+1) == '*') { - if (s_flag && lastNonWhitespace != ';') { + if (startFlag && lastNonWhitespace != ';') { result.append(buf.substring(firstNonSpace)); - } else { result.append(buf); } + } else { + result.append(buf); + } } else { if (buf.charAt(firstNonSpace) == '*' || !jdoc_flag) { result.append(" " + buf.substring(firstNonSpace)); @@ -210,7 +208,7 @@ private void writeIndentedComment() { /** - * Makes tabs >= 0 and appends tabs*indentValue + * Makes tabs >= 0 and appends tabs*indentValue * spaces to result. */ private void printIndentation() { @@ -259,7 +257,7 @@ private void advanceToNonSpace() { * Does nothing and returns zero if EOF was set when it was called. */ private char nextChar() { - if (EOF) return 0; + if (EOF) return 0; pos++; if (pos == chars.length-1) EOF = true; if (pos >= chars.length) return 0; @@ -303,13 +301,44 @@ private boolean readForNewLine() { if (c == '\n') { // eat it nextChar(); -// lineNumber++; tabs = savedTabs; return true; } return false; } + /** + * Called at '\n' in the main loop + * @returns whether the statement (or whatever) had ended. + */ + private boolean isOverflowing() { + if (lastNonWhitespace == 0 || lastNonWhitespace == ';') { + return false; //start of file or end of statement + } + if (lastNonWhitespace != '{' && lastNonWhitespace != '}') { + return true; // evidently in a statement; + } + return arrayFlag; // there was an array, {} aren't blocks. + // If {} were blocks for an odd, valid reason... this will + // fail, but I reckon that anyone putting blocks in arrays + // is in a small minority. + } + + + /** + * @returns last non-wsp in result+buf, or 0 on error. + */ + private char prevNonWhitespace() { + StringBuffer tot = new StringBuffer(); + tot.append(result); + tot.append(buf); + for (int i = tot.length()-1; i >= 0; i--) { + if (!Character.isWhitespace(tot.charAt(i))) + return tot.charAt(i); + } + return 0; + } + /** * Sees if buf is of the form [optional whitespace][keyword][optional anything]. @@ -346,19 +375,18 @@ private void trimRight(final StringBuilder sb) { */ public String format(final String source) { final String normalizedText = source.replaceAll("\r", ""); - final String cleanText = + final String cleanText = normalizedText + (normalizedText.endsWith("\n") ? "" : "\n"); result.setLength(0); indentValue = Preferences.getInteger("editor.tabs.size"); -// lineNumber = 0; boolean forFlag = if_flg = false; - s_flag = true; + startFlag = true; int forParenthLevel = 0; conditionalLevel = parenLevel = curlyLvl = if_lev = level = 0; tabs = 0; - jdoc_flag = continuationFlag = inStatementFlag = false; + jdoc_flag = overflowFlag = inStatementFlag = false; char cc; int[] s_level = new int[10]; @@ -371,7 +399,6 @@ public String format(final String source) { s_tabs = new int[20][10]; pos = -1; chars = cleanText.toCharArray(); -// lineNumber = 1; EOF = false; // set in nextChar() when EOF @@ -384,9 +411,9 @@ public String format(final String source) { break; case ',': + inStatementFlag = true; trimRight(buf); - buf.append(c); - buf.append(' '); + buf.append(", "); advanceToNonSpace(); break; @@ -395,28 +422,20 @@ public String format(final String source) { elseFlag = lookup("else"); if (elseFlag) { gotElse(); - if ((!s_flag) || buf.length() > 0) { - buf.append(c); - } - + if (!startFlag || buf.length() > 0) buf.append(c); writeIndentedLine(); - s_flag = false; + startFlag = false; break; } - if ((!s_flag) || buf.length() > 0) { - buf.append(c); - } + if (!startFlag || buf.length() > 0) buf.append(c); break; case '\n': -// lineNumber++; - if (EOF) { - writeIndentedLine(); - break; - } + if (EOF) break; + elseFlag = lookup("else"); if (elseFlag) gotElse(); - + if (lookup_com("//")) { if (buf.charAt(buf.length() - 1) == '\n') { buf.setLength(buf.length() - 1); @@ -425,44 +444,50 @@ public String format(final String source) { writeIndentedLine(); result.append("\n"); - s_flag = true; + startFlag = true; if (elseFlag) { p_flg[level]++; tabs++; - } - continuationFlag = inStatementFlag; + } else overflowFlag = inStatementFlag; break; case '{': - inStatementFlag = false; + char prevChar = prevNonWhitespace(); + if (prevChar == '=' || prevChar == ']') arrayFlag = true; + if (arrayFlag) { + buf.append(c); + break; // No special treatment. TODO: indents. + } else inStatementFlag = false; // eg class declaration ends + elseFlag = lookup("else"); if (elseFlag) gotElse(); - if (s_if_lev.length == curlyLvl) { s_if_lev = PApplet.expand(s_if_lev); s_if_flg = PApplet.expand(s_if_flg); } + s_if_lev[curlyLvl] = if_lev; s_if_flg[curlyLvl] = if_flg; if_lev = 0; if_flg = false; curlyLvl++; - if (s_flag && p_flg[level] != 0) { + if (startFlag && p_flg[level] != 0) { p_flg[level]--; tabs--; } trimRight(buf); - if (buf.length() > 0 || - (result.length() > 0 && !Character.isWhitespace(result.charAt(result.length() - 1)))) + if (buf.length() > 0 || (result.length() > 0 && + !Character.isWhitespace(result.charAt(result.length() - 1)))) { buf.append(" "); + } buf.append(c); writeIndentedLine(); readForNewLine(); writeIndentedLine(); - + result.append("\n"); tabs++; - s_flag = true; + startFlag = true; if (p_flg[level] > 0) { ind[level] = 1; level++; @@ -471,8 +496,12 @@ public String format(final String source) { break; case '}': - inStatementFlag = false; - curlyLvl--; + if (arrayFlag) { + buf.append(c); + break; + } else inStatementFlag = false; + + curlyLvl--; if (curlyLvl < 0) { curlyLvl = 0; buf.append(c); @@ -491,18 +520,18 @@ public String format(final String source) { result.append("\n"); printIndentation(); result.append(c); - if (peek() == ';') { + if (peek() == ';' || arrayFlag) { + //Now does commas etc. + if (peek() == ' ') advanceToNonSpace(); result.append(nextChar()); } readForNewLine(); writeIndentedLine(); result.append('\n'); - s_flag = true; - if (curlyLvl < s_level[level]) { - if (level > 0) { - level--; - } + startFlag = true; + if (curlyLvl < s_level[level] && level > 0) { + level--; } if (ind[level] != 0) { tabs -= p_flg[level]; @@ -523,9 +552,8 @@ public String format(final String source) { buf.append(cc = nextChar()); } if (cc == '\n') { -// lineNumber++; writeIndentedLine(); - s_flag = true; + startFlag = true; } cc = nextChar(); } @@ -545,7 +573,6 @@ public String format(final String source) { advanceToNonSpace(); break; } - inStatementFlag = false; buf.append(c); writeIndentedLine(); if (p_flg[level] > 0 && ind[level] == 0) { @@ -555,7 +582,7 @@ public String format(final String source) { readForNewLine(); writeIndentedLine(); result.append("\n"); - s_flag = true; + startFlag = true; if (if_lev > 0) { if (if_flg) { if_lev--; @@ -564,6 +591,10 @@ public String format(final String source) { if_lev = 0; } } + // Array behaviour ends at the end + // of a statement. + arrayFlag = false; + inStatementFlag = false; break; case '\\': @@ -577,7 +608,7 @@ public String format(final String source) { break; case ':': - // Java 8 :: operator. + // Java 8 :: operator. if (peek() == ':') { result.append(c).append(nextChar()); break; @@ -599,9 +630,10 @@ else if (forFlag) { } buf.append(c); + inStatementFlag = false; if (!lookup("default") && !lookup("case")) { - s_flag = false; + startFlag = false; writeIndentedLine(); } else { tabs--; @@ -614,7 +646,7 @@ else if (forFlag) { readForNewLine(); writeIndentedLine(); result.append('\n'); - s_flag = true; + startFlag = true; break; case '/': @@ -638,7 +670,7 @@ else if (forFlag) { case ')': final boolean isCast = castFlags.isEmpty() ? false : castFlags.pop(); parenLevel--; - + // If we're further back than the start of a for loop, we've // left it. if (forFlag && forParenthLevel > parenLevel) forFlag = false; @@ -690,7 +722,7 @@ else if (forFlag) { } // end while not EOF if (buf.length() > 0) writeIndentedLine(); - + final String formatted = result.toString(); return formatted.equals(cleanText) ? source : formatted; } From 9a6160e9fdbd70b6291f5b0a457b4d515e16800c Mon Sep 17 00:00:00 2001 From: George Bateman Date: Sun, 28 Sep 2014 17:58:58 +0100 Subject: [PATCH 5/7] Fixed #2540, #1041, and EOF checks in autoformat This adds proper support for indents caused by continuing to a new line, allows arrays to be expressed in more compact form, prevents the final line being eaten if it isn't properly ended, and neatens code. --- app/src/processing/mode/java/AutoFormat.java | 258 ++++++------------- 1 file changed, 79 insertions(+), 179 deletions(-) diff --git a/app/src/processing/mode/java/AutoFormat.java b/app/src/processing/mode/java/AutoFormat.java index 63f6f98468..778aaba85d 100644 --- a/app/src/processing/mode/java/AutoFormat.java +++ b/app/src/processing/mode/java/AutoFormat.java @@ -56,20 +56,17 @@ public class AutoFormat implements Formatter { private boolean inStatementFlag; // in a line of code private boolean overflowFlag; // line overrunning? - private boolean arrayFlag; // in an array/after it in same statement? - private boolean if_flg; - private boolean startFlag; // No buf has yet been writen to this line. - private boolean elseFlag; + private boolean startFlag; // No buf has yet been writen to this line. + + /** -1 if not in array or just after it, otherwise increases from 0. */ + private int arrayLevel; + private int arrayIndent; // Lowest value of the above for this line. /** Number of ? entered without exiting at : of a?b:c structures. */ private int conditionalLevel; /** chars[pos] is where we're at. */ private int pos; - private int level; - private int[][] sp_flg; - private int[][] s_ind; - private int if_lev; /** Number of curly brackets entered and not exited, excluding arrays. */ @@ -78,9 +75,6 @@ public class AutoFormat implements Formatter { /** Number of parentheses entered and not exited. */ private int parenLevel; - private int[] ind; - private int[] p_flg; - private int[][] s_tabs; private boolean jdoc_flag; /** The number of times to indent at a given point */ @@ -89,11 +83,9 @@ public class AutoFormat implements Formatter { /** The last non-space seen by nextChar(). */ private char lastNonWhitespace = 0; - private final Stack castFlags = new Stack(); - private void handleMultiLineComment() { - final boolean saved_startFlag = startFlag; + final boolean savedStartFlag = startFlag; buf.append(nextChar()); // So /*/ isn't self-closing. for (char ch = nextChar(); !EOF; ch = nextChar()) { @@ -112,7 +104,7 @@ private void handleMultiLineComment() { } writeIndentedComment(); - startFlag = saved_startFlag; + startFlag = savedStartFlag; jdoc_flag = false; return; } @@ -134,31 +126,36 @@ private void handleSingleLineComment() { private void writeIndentedLine() { - System.out.print(buf); if (buf.length() == 0) { - if (startFlag) startFlag = elseFlag = false; + startFlag = false; return; } if (startFlag) { // Indent suppressed at eg. if{ and when // buf is close-brackets only followed by ';'. - boolean indentMore = !buf.toString().matches("[\\s\\]\\}\\)]*;") - && (arrayFlag || buf.charAt(0) != '{') + boolean indentMore = !buf.toString().matches("[\\s\\]\\}\\)]*;") && + (buf.charAt(0) != '{' | arrayLevel >= 0) && overflowFlag; - System.out.println(indentMore + ", " + overflowFlag); - if (indentMore) tabs++; + if (indentMore) { + tabs++; + if (arrayIndent > 0) tabs += arrayIndent; + } printIndentation(); startFlag = false; - if (indentMore) tabs--; - } - if (elseFlag) { - if (lastNonSpaceChar() == '}') { - trimRight(result); - result.append(' '); + if (indentMore) { + tabs--; + if (arrayIndent > 0) tabs -= arrayIndent; } - elseFlag = false; } + if (lastNonSpaceChar() == '}' && (bufStarts("else") || + bufStarts("while"))) { + result.append(' '); + } + // If we're still in a statement at \n, that's overflow. + overflowFlag = inStatementFlag; + arrayIndent = arrayLevel; result.append(buf); + buf.setLength(0); } @@ -208,7 +205,7 @@ private void writeIndentedComment() { /** - * Makes tabs >= 0 and appends tabs*indentValue + * Makes tabs >= 0 and appends tabs*indentValue * spaces to result. */ private void printIndentation() { @@ -254,7 +251,7 @@ private void advanceToNonSpace() { * Increments pos, sets EOF if needed, and returns the new * chars[pos] or zero if out-of-bounds. * Sets lastNonWhitespace if chars[pos] isn't whitespace. - * Does nothing and returns zero if EOF was set when it was called. + * Does nothing and returns zero if already at EOF. */ private char nextChar() { if (EOF) return 0; @@ -268,14 +265,6 @@ private char nextChar() { } - private void gotElse() { - tabs = s_tabs[curlyLvl][if_lev]; - p_flg[level] = sp_flg[curlyLvl][if_lev]; - ind[level] = s_ind[curlyLvl][if_lev]; - if_flg = true; - } - - private boolean readForNewLine() { final int savedTabs = tabs; char c = peek(); @@ -307,26 +296,9 @@ private boolean readForNewLine() { return false; } - /** - * Called at '\n' in the main loop - * @returns whether the statement (or whatever) had ended. - */ - private boolean isOverflowing() { - if (lastNonWhitespace == 0 || lastNonWhitespace == ';') { - return false; //start of file or end of statement - } - if (lastNonWhitespace != '{' && lastNonWhitespace != '}') { - return true; // evidently in a statement; - } - return arrayFlag; // there was an array, {} aren't blocks. - // If {} were blocks for an odd, valid reason... this will - // fail, but I reckon that anyone putting blocks in arrays - // is in a small minority. - } - /** - * @returns last non-wsp in result+buf, or 0 on error. + * @return last non-wsp in result+buf, or 0 on error. */ private char prevNonWhitespace() { StringBuffer tot = new StringBuffer(); @@ -342,17 +314,28 @@ private char prevNonWhitespace() { /** * Sees if buf is of the form [optional whitespace][keyword][optional anything]. - * It won't allow keyword to be directly followed by an alphanumeric, _, or &. + * It won't allow keyword to be directly followed by an alphanumeric, _, or &. * Will be different if keyword contains regex codes. */ - private boolean lookup(final String keyword) { + private boolean bufStarts(final String keyword) { return Pattern.matches("^\\s*" + keyword + "(?![a-zA-Z0-9_&]).*$", buf); } + + + + /** + * Sees if buf is of the form [optional anything][keyword][optional whitespace]. + * It won't allow keyword to be directly preceded by an alphanumeric, _, or &. + * Will be different if keyword contains regex codes. + */ + private boolean bufEnds(final String keyword) { + return Pattern.matches("^.*(? 0) buf.append(c); - writeIndentedLine(); - startFlag = false; - break; - } + // Nuke old indent. if (!startFlag || buf.length() > 0) buf.append(c); break; case '\n': if (EOF) break; - elseFlag = lookup("else"); - if (elseFlag) gotElse(); - if (lookup_com("//")) { if (buf.charAt(buf.length() - 1) == '\n') { buf.setLength(buf.length() - 1); } } - + writeIndentedLine(); + result.append("\n"); startFlag = true; - if (elseFlag) { - p_flg[level]++; - tabs++; - } else overflowFlag = inStatementFlag; break; case '{': char prevChar = prevNonWhitespace(); - if (prevChar == '=' || prevChar == ']') arrayFlag = true; - if (arrayFlag) { + if (arrayLevel >= 0 || prevChar == '=' || prevChar == ']') { + // If we're already in an array (lvl >= 0), increment level. + // Otherwise, the presence of a = or ] indicates an array is starting + // and we should start counting (set lvl=0). + arrayLevel++; buf.append(c); - break; // No special treatment. TODO: indents. - } else inStatementFlag = false; // eg class declaration ends - - elseFlag = lookup("else"); - if (elseFlag) gotElse(); - if (s_if_lev.length == curlyLvl) { - s_if_lev = PApplet.expand(s_if_lev); - s_if_flg = PApplet.expand(s_if_flg); - } + break; // Nothing fancy. + } else inStatementFlag = false; // eg. class declaration ends - s_if_lev[curlyLvl] = if_lev; - s_if_flg[curlyLvl] = if_flg; - if_lev = 0; - if_flg = false; curlyLvl++; - if (startFlag && p_flg[level] != 0) { - p_flg[level]--; - tabs--; - } trimRight(buf); if (buf.length() > 0 || (result.length() > 0 && !Character.isWhitespace(result.charAt(result.length() - 1)))) { @@ -485,18 +435,17 @@ public String format(final String source) { readForNewLine(); writeIndentedLine(); - result.append("\n"); + result.append('\n'); tabs++; startFlag = true; - if (p_flg[level] > 0) { - ind[level] = 1; - level++; - s_level[level] = curlyLvl; - } break; case '}': - if (arrayFlag) { + if (arrayLevel >= 0) { + // Even less fancy. Note that }s cannot end array behaviour; + // a semicolon is needed. + if (arrayLevel > 0) arrayLevel--; + if (arrayIndent > arrayLevel) arrayIndent = arrayLevel; buf.append(c); break; } else inStatementFlag = false; @@ -507,37 +456,20 @@ public String format(final String source) { buf.append(c); writeIndentedLine(); } else { - if_lev = s_if_lev[curlyLvl] - 1; - if (if_lev < 0) { - if_lev = 0; - } - if_flg = s_if_flg[curlyLvl]; trimRight(buf); writeIndentedLine(); tabs--; trimRight(result); - result.append("\n"); + result.append('\n'); printIndentation(); result.append(c); - if (peek() == ';' || arrayFlag) { - //Now does commas etc. - if (peek() == ' ') advanceToNonSpace(); - result.append(nextChar()); - } - + if (peek() == ';') result.append(nextChar()); + readForNewLine(); writeIndentedLine(); result.append('\n'); startFlag = true; - if (curlyLvl < s_level[level] && level > 0) { - level--; - } - if (ind[level] != 0) { - tabs -= p_flg[level]; - p_flg[level] = 0; - ind[level] = 0; - } } break; @@ -574,27 +506,14 @@ public String format(final String source) { break; } buf.append(c); + inStatementFlag = false; writeIndentedLine(); - if (p_flg[level] > 0 && ind[level] == 0) { - tabs -= p_flg[level]; - p_flg[level] = 0; - } readForNewLine(); writeIndentedLine(); result.append("\n"); startFlag = true; - if (if_lev > 0) { - if (if_flg) { - if_lev--; - if_flg = false; - } else { - if_lev = 0; - } - } - // Array behaviour ends at the end - // of a statement. - arrayFlag = false; - inStatementFlag = false; + // Array behaviour ends at the end of a statement. + arrayLevel = -1; break; case '\\': @@ -632,17 +551,17 @@ else if (forFlag) { buf.append(c); inStatementFlag = false; - if (!lookup("default") && !lookup("case")) { - startFlag = false; - writeIndentedLine(); - } else { + if (bufEnds("default") || bufEnds("case")) { tabs--; writeIndentedLine(); tabs++; + } else { + startFlag = false; // Label; zero indent. + writeIndentedLine(); } - if (peek() == ';') { - result.append(nextChar()); - } + + if (peek() == ';') result.append(nextChar()); + readForNewLine(); writeIndentedLine(); result.append('\n'); @@ -658,7 +577,7 @@ else if (forFlag) { result.append("\n"); } else if (next == '*') { if (buf.length() > 0) { - writeIndentedLine(); + writeIndentedLine(); // TODO } buf.append(c).append(nextChar()); handleMultiLineComment(); @@ -668,7 +587,6 @@ else if (forFlag) { break; case ')': - final boolean isCast = castFlags.isEmpty() ? false : castFlags.pop(); parenLevel--; // If we're further back than the start of a for loop, we've @@ -678,24 +596,13 @@ else if (forFlag) { if (parenLevel < 0) parenLevel = 0; buf.append(c); - writeIndentedLine(); - if (readForNewLine()) { - chars[pos--] = '\n'; - if (parenLevel == 0 && tabs > 0 && !isCast) { - p_flg[level]++; - tabs++; - ind[level] = 0; - } - } break; case '(': - castFlags.push(Pattern.matches("^.*?(?:int|color|float)\\s*$", buf)); + final boolean isFor = bufEnds("for"); + final boolean isIf = bufEnds("if"); - final boolean isFor = lookup("for"); - final boolean isIf = lookup("if"); - - if (isFor || isIf || lookup("while")) { + if (isFor || isIf || bufEnds("while")) { if (!Character.isWhitespace(buf.charAt(buf.length() - 1))) { buf.append(' '); } @@ -710,13 +617,6 @@ else if (forFlag) { if (isFor && !forFlag) { forParenthLevel = parenLevel; forFlag = true; - } else if (isIf) { - writeIndentedLine(); - s_tabs[curlyLvl][if_lev] = tabs; - sp_flg[curlyLvl][if_lev] = p_flg[level]; - s_ind[curlyLvl][if_lev] = ind[level]; - if_lev++; - if_flg = true; } } // end switch } // end while not EOF From 555851b3b63324d1311aeafad247e1a445756252 Mon Sep 17 00:00:00 2001 From: George Bateman Date: Fri, 5 Dec 2014 19:01:21 +0000 Subject: [PATCH 6/7] Finish AutoFormat.java Sort out arrays and do-while loops. Fix some more bugs. --- app/src/processing/mode/java/AutoFormat.java | 340 ++++++++++++------- 1 file changed, 223 insertions(+), 117 deletions(-) diff --git a/app/src/processing/mode/java/AutoFormat.java b/app/src/processing/mode/java/AutoFormat.java index 63f6f98468..1c185910e3 100644 --- a/app/src/processing/mode/java/AutoFormat.java +++ b/app/src/processing/mode/java/AutoFormat.java @@ -56,20 +56,24 @@ public class AutoFormat implements Formatter { private boolean inStatementFlag; // in a line of code private boolean overflowFlag; // line overrunning? - private boolean arrayFlag; // in an array/after it in same statement? + private boolean startFlag; // No buf has yet been writen to this line. private boolean if_flg; - private boolean startFlag; // No buf has yet been writen to this line. private boolean elseFlag; + /** -1 if not in array or if just after it, otherwise increases from 0. */ + private int arrayLevel; + private int arrayIndent; // Lowest value of the above for this line. + /** Number of ? entered without exiting at : of a?b:c structures. */ private int conditionalLevel; + private int[][] sp_flg; + private boolean[][] s_ind; + private int if_lev; + /** chars[pos] is where we're at. */ private int pos; private int level; - private int[][] sp_flg; - private int[][] s_ind; - private int if_lev; /** Number of curly brackets entered and not exited, excluding arrays. */ @@ -78,9 +82,18 @@ public class AutoFormat implements Formatter { /** Number of parentheses entered and not exited. */ private int parenLevel; - private int[] ind; + private boolean[] ind; private int[] p_flg; private int[][] s_tabs; + + /** At every {, this has a true pushed if it's a do-while, + and a false otherwise. It is then popped at }. */ + private Stack doWhileFlags; + + /** At every (, this has a true pushed for if, while, or for, + and a false otherwise. Popped at ). */ + private Stack ifWhileForFlags; + private boolean jdoc_flag; /** The number of times to indent at a given point */ @@ -89,11 +102,9 @@ public class AutoFormat implements Formatter { /** The last non-space seen by nextChar(). */ private char lastNonWhitespace = 0; - private final Stack castFlags = new Stack(); - private void handleMultiLineComment() { - final boolean saved_startFlag = startFlag; + final boolean savedStartFlag = startFlag; buf.append(nextChar()); // So /*/ isn't self-closing. for (char ch = nextChar(); !EOF; ch = nextChar()) { @@ -112,7 +123,7 @@ private void handleMultiLineComment() { } writeIndentedComment(); - startFlag = saved_startFlag; + startFlag = savedStartFlag; jdoc_flag = false; return; } @@ -134,23 +145,31 @@ private void handleSingleLineComment() { private void writeIndentedLine() { - System.out.print(buf); if (buf.length() == 0) { if (startFlag) startFlag = elseFlag = false; return; } if (startFlag) { - // Indent suppressed at eg. if{ and when + // Indent suppressed at eg. if{ and when // buf is close-brackets only followed by ';'. - boolean indentMore = !buf.toString().matches("[\\s\\]\\}\\)]*;") - && (arrayFlag || buf.charAt(0) != '{') + boolean indentMore = !buf.toString().matches("[\\s\\]\\}\\)]+;") + && (buf.charAt(0) != '{' || arrayLevel >= 0) && overflowFlag; - System.out.println(indentMore + ", " + overflowFlag); - if (indentMore) tabs++; + if (indentMore) { + tabs++; + if (arrayIndent > 0) tabs += arrayIndent; + } printIndentation(); startFlag = false; - if (indentMore) tabs--; + if (indentMore) { + tabs--; + if (arrayIndent > 0) tabs -= arrayIndent; + } + } + if (lastNonSpaceChar() == '}' && bufStarts("else")) { + result.append(' '); } + if (elseFlag) { if (lastNonSpaceChar() == '}') { trimRight(result); @@ -158,7 +177,12 @@ private void writeIndentedLine() { } elseFlag = false; } + + // If we're still in a statement at \n, that's overflow. + overflowFlag = inStatementFlag; + arrayIndent = arrayLevel; result.append(buf); + buf.setLength(0); } @@ -193,7 +217,7 @@ private void writeIndentedComment() { if (buf.charAt(firstNonSpace) == '/' && buf.charAt(firstNonSpace+1) == '*') { if (startFlag && lastNonWhitespace != ';') { result.append(buf.substring(firstNonSpace)); - } else { + } else { result.append(buf); } } else { @@ -208,7 +232,7 @@ private void writeIndentedComment() { /** - * Makes tabs >= 0 and appends tabs*indentValue + * Makes tabs >= 0 and appends tabs*indentValue * spaces to result. */ private void printIndentation() { @@ -237,16 +261,26 @@ private char peek() { * Then sets EOF if pos has reached the end, or reverses pos by 1 if it * has not. *
Does nothing if EOF. + * @param allWsp (boolean) Eat newlines too (all of Character.isWhiteSpace()). */ - private void advanceToNonSpace() { + private void advanceToNonSpace(boolean allWsp) { if (EOF) return; - do { - pos++; - } while (pos < chars.length && chars[pos] == ' '); + if (allWsp) { + do { + pos++; + } while (pos < chars.length && Character.isWhitespace(chars[pos])); + } else { + do { + pos++; + } while (pos < chars.length && chars[pos] == ' '); + } - if (pos == chars.length - 1) EOF = true; - else pos--; // reset for nextChar() + if (pos == chars.length - 1) { + EOF = true; + } else { + pos--; // reset for nextChar() + } } @@ -254,12 +288,12 @@ private void advanceToNonSpace() { * Increments pos, sets EOF if needed, and returns the new * chars[pos] or zero if out-of-bounds. * Sets lastNonWhitespace if chars[pos] isn't whitespace. - * Does nothing and returns zero if EOF was set when it was called. + * Does nothing and returns zero if already at EOF. */ private char nextChar() { if (EOF) return 0; pos++; - if (pos == chars.length-1) EOF = true; + if (pos >= chars.length - 1) EOF = true; if (pos >= chars.length) return 0; char retVal = chars[pos]; @@ -273,9 +307,17 @@ private void gotElse() { p_flg[level] = sp_flg[curlyLvl][if_lev]; ind[level] = s_ind[curlyLvl][if_lev]; if_flg = true; + // We can't expect else to be followed by a semicolon, so must + // end the statemant manually. + inStatementFlag = false; } + /** + * Pump any '\t' and ' ' to buf, handle any following comment, + * and if the next character is '\n', discard it. + * @return Whether a '\n' was found and discarded. + */ private boolean readForNewLine() { final int savedTabs = tabs; char c = peek(); @@ -307,26 +349,9 @@ private boolean readForNewLine() { return false; } - /** - * Called at '\n' in the main loop - * @returns whether the statement (or whatever) had ended. - */ - private boolean isOverflowing() { - if (lastNonWhitespace == 0 || lastNonWhitespace == ';') { - return false; //start of file or end of statement - } - if (lastNonWhitespace != '{' && lastNonWhitespace != '}') { - return true; // evidently in a statement; - } - return arrayFlag; // there was an array, {} aren't blocks. - // If {} were blocks for an odd, valid reason... this will - // fail, but I reckon that anyone putting blocks in arrays - // is in a small minority. - } - /** - * @returns last non-wsp in result+buf, or 0 on error. + * @return last non-wsp in result+buf, or 0 on error. */ private char prevNonWhitespace() { StringBuffer tot = new StringBuffer(); @@ -338,21 +363,66 @@ private char prevNonWhitespace() { } return 0; } - + /** * Sees if buf is of the form [optional whitespace][keyword][optional anything]. - * It won't allow keyword to be directly followed by an alphanumeric, _, or &. + * It won't allow keyword to be directly followed by an alphanumeric, _, or &. * Will be different if keyword contains regex codes. */ - private boolean lookup(final String keyword) { + private boolean bufStarts(final String keyword) { return Pattern.matches("^\\s*" + keyword + "(?![a-zA-Z0-9_&]).*$", buf); } + + /** + * Sees if result+"\n"+buf is of the form [optional anything][keyword][optional whitespace]. + * It won't allow keyword to be directly preceded by an alphanumeric, _, or &. + * Will be different if keyword contains regex codes. + */ + private boolean textEnds(final String keyword) { + return Pattern.matches("^.*(?(); + ifWhileForFlags = new Stack(); + chars = cleanText.toCharArray(); EOF = false; // set in nextChar() when EOF @@ -414,26 +488,30 @@ public String format(final String source) { inStatementFlag = true; trimRight(buf); buf.append(", "); - advanceToNonSpace(); + advanceToNonSpace(false); break; case ' ': case '\t': - elseFlag = lookup("else"); + elseFlag = bufEnds("else"); if (elseFlag) { gotElse(); - if (!startFlag || buf.length() > 0) buf.append(c); + if (!startFlag || buf.length() > 0) { + buf.append(c); + } + writeIndentedLine(); startFlag = false; break; } + // Only allow in the line, to nuke the old indent. if (!startFlag || buf.length() > 0) buf.append(c); break; case '\n': if (EOF) break; - - elseFlag = lookup("else"); + + elseFlag = bufEnds("else"); if (elseFlag) gotElse(); if (lookup_com("//")) { @@ -443,29 +521,37 @@ public String format(final String source) { } writeIndentedLine(); + result.append("\n"); - startFlag = true; if (elseFlag) { p_flg[level]++; tabs++; - } else overflowFlag = inStatementFlag; + } + startFlag = true; break; case '{': + elseFlag = bufEnds("else"); + if (elseFlag) gotElse(); + + doWhileFlags.push(Boolean.valueOf(bufEnds("do"))); + char prevChar = prevNonWhitespace(); - if (prevChar == '=' || prevChar == ']') arrayFlag = true; - if (arrayFlag) { + if (arrayLevel >= 0 || prevChar == '=' || prevChar == ']') { + // If we're already in an array (lvl >= 0), increment level. + // Otherwise, the presence of a = or ] indicates an array is starting + // and we should start counting (set lvl=0). + arrayLevel++; buf.append(c); - break; // No special treatment. TODO: indents. - } else inStatementFlag = false; // eg class declaration ends + break; // Nothing fancy. + } + + inStatementFlag = false; // eg. class declaration ends - elseFlag = lookup("else"); - if (elseFlag) gotElse(); if (s_if_lev.length == curlyLvl) { s_if_lev = PApplet.expand(s_if_lev); s_if_flg = PApplet.expand(s_if_flg); } - s_if_lev[curlyLvl] = if_lev; s_if_flg[curlyLvl] = if_flg; if_lev = 0; @@ -475,6 +561,7 @@ public String format(final String source) { p_flg[level]--; tabs--; } + trimRight(buf); if (buf.length() > 0 || (result.length() > 0 && !Character.isWhitespace(result.charAt(result.length() - 1)))) { @@ -485,21 +572,28 @@ public String format(final String source) { readForNewLine(); writeIndentedLine(); - result.append("\n"); + result.append('\n'); tabs++; startFlag = true; + if (p_flg[level] > 0) { - ind[level] = 1; + ind[level] = true; level++; s_level[level] = curlyLvl; } break; case '}': - if (arrayFlag) { + if (arrayLevel >= 0) { + // Even less fancy. Note that }s cannot end array behaviour; + // a semicolon is needed. + if (arrayLevel > 0) arrayLevel--; + if (arrayIndent > arrayLevel) arrayIndent = arrayLevel; buf.append(c); break; - } else inStatementFlag = false; + } + + inStatementFlag = false; curlyLvl--; if (curlyLvl < 0) { @@ -508,35 +602,42 @@ public String format(final String source) { writeIndentedLine(); } else { if_lev = s_if_lev[curlyLvl] - 1; - if (if_lev < 0) { - if_lev = 0; - } + if (if_lev < 0) if_lev = 0; + if_levSafe(); + if_flg = s_if_flg[curlyLvl]; trimRight(buf); writeIndentedLine(); tabs--; trimRight(result); - result.append("\n"); + result.append('\n'); printIndentation(); result.append(c); - if (peek() == ';' || arrayFlag) { - //Now does commas etc. - if (peek() == ' ') advanceToNonSpace(); - result.append(nextChar()); + if (peek() == ';') result.append(nextChar()); + + // doWhileFlags contains a TRUE if and only if the + // corresponding { was preceeded (bufEnds) by "do". + // Just checking for while would fail on if(){} while(){}. + if (doWhileFlags.empty() || !doWhileFlags.pop().booleanValue() + || !new String(chars, pos+1, chars.length-pos-1).trim().startsWith("while")) { + readForNewLine(); + writeIndentedLine(); + result.append('\n'); + startFlag = true; + } else { + // Correct spacing in "} while". + result.append(' '); + advanceToNonSpace(true); + startFlag = false; } - readForNewLine(); - writeIndentedLine(); - result.append('\n'); - startFlag = true; - if (curlyLvl < s_level[level] && level > 0) { - level--; - } - if (ind[level] != 0) { + if (curlyLvl < s_level[level] && level > 0) level--; + + if (ind[level]) { tabs -= p_flg[level]; p_flg[level] = 0; - ind[level] = 0; + ind[level] = false; } } break; @@ -570,12 +671,13 @@ public String format(final String source) { trimRight(buf); buf.append("; "); // Not non-whitespace: allow \n. - advanceToNonSpace(); + advanceToNonSpace(false); break; } buf.append(c); + inStatementFlag = false; writeIndentedLine(); - if (p_flg[level] > 0 && ind[level] == 0) { + if (p_flg[level] > 0 && !ind[level]) { tabs -= p_flg[level]; p_flg[level] = 0; } @@ -583,6 +685,9 @@ public String format(final String source) { writeIndentedLine(); result.append("\n"); startFlag = true; + // Array behaviour ends at the end of a statement. + arrayLevel = -1; + if (if_lev > 0) { if (if_flg) { if_lev--; @@ -591,10 +696,6 @@ public String format(final String source) { if_lev = 0; } } - // Array behaviour ends at the end - // of a statement. - arrayFlag = false; - inStatementFlag = false; break; case '\\': @@ -625,24 +726,19 @@ else if (forFlag) { trimRight(buf); buf.append(" : "); // Not to non-whitespace: allow \n. - advanceToNonSpace(); + advanceToNonSpace(false); break; } buf.append(c); inStatementFlag = false; + arrayLevel = -1; // Unlikely to be needed; just in case. + + //Same format for case, default, and other labels. + tabs--; + writeIndentedLine(); + tabs++; - if (!lookup("default") && !lookup("case")) { - startFlag = false; - writeIndentedLine(); - } else { - tabs--; - writeIndentedLine(); - tabs++; - } - if (peek() == ';') { - result.append(nextChar()); - } readForNewLine(); writeIndentedLine(); result.append('\n'); @@ -668,7 +764,6 @@ else if (forFlag) { break; case ')': - final boolean isCast = castFlags.isEmpty() ? false : castFlags.pop(); parenLevel--; // If we're further back than the start of a for loop, we've @@ -676,29 +771,39 @@ else if (forFlag) { if (forFlag && forParenthLevel > parenLevel) forFlag = false; if (parenLevel < 0) parenLevel = 0; - buf.append(c); + + boolean wasIfEtc = !ifWhileForFlags.empty() && ifWhileForFlags.pop().booleanValue(); + if (wasIfEtc) { + inStatementFlag = false; + arrayLevel = -1; // This is important as it allows arrays in if statements. + } + writeIndentedLine(); - if (readForNewLine()) { - chars[pos--] = '\n'; - if (parenLevel == 0 && tabs > 0 && !isCast) { + // Short-circuiting means readForNewLine is only called for if/while/for; + // this is important. + if (wasIfEtc && readForNewLine()) { + chars[pos] = '\n'; + pos--; // Make nextChar() return the new '\n'. + if (parenLevel == 0) { p_flg[level]++; tabs++; - ind[level] = 0; + ind[level] = false; } } break; case '(': - castFlags.push(Pattern.matches("^.*?(?:int|color|float)\\s*$", buf)); - - final boolean isFor = lookup("for"); - final boolean isIf = lookup("if"); + final boolean isFor = bufEnds("for"); + final boolean isIf = bufEnds("if"); - if (isFor || isIf || lookup("while")) { + if (isFor || isIf || bufEnds("while")) { if (!Character.isWhitespace(buf.charAt(buf.length() - 1))) { buf.append(' '); } + ifWhileForFlags.push(true); + } else { + ifWhileForFlags.push(false); } buf.append(c); @@ -716,6 +821,7 @@ else if (forFlag) { sp_flg[curlyLvl][if_lev] = p_flg[level]; s_ind[curlyLvl][if_lev] = ind[level]; if_lev++; + if_levSafe(); if_flg = true; } } // end switch From 1a6a6712e1e481766a898ec508ec97ca0638c028 Mon Sep 17 00:00:00 2001 From: George Bateman Date: Fri, 9 Jan 2015 20:26:58 +0000 Subject: [PATCH 7/7] AutoFormat.java Fix if-else regression --- app/src/processing/mode/java/AutoFormat.java | 35 +++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/app/src/processing/mode/java/AutoFormat.java b/app/src/processing/mode/java/AutoFormat.java index 1c185910e3..db7b80a8ff 100644 --- a/app/src/processing/mode/java/AutoFormat.java +++ b/app/src/processing/mode/java/AutoFormat.java @@ -7,6 +7,7 @@ Original Copyright (c) 1997, 1998 Van Di-Han HO. All Rights Reserved. Updates Copyright (c) 2001 Jason Pell. Further updates Copyright (c) 2003 Martin Gomez, Ateneo de Manila University Additional updates Copyright (c) 2005-10 Ben Fry and Casey Reas + Even more updates Copyright (c) 2014 George Bateman This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -302,6 +303,9 @@ private char nextChar() { } + /** + * Called after else. + */ private void gotElse() { tabs = s_tabs[curlyLvl][if_lev]; p_flg[level] = sp_flg[curlyLvl][if_lev]; @@ -375,18 +379,6 @@ private boolean bufStarts(final String keyword) { } - - /** - * Sees if result+"\n"+buf is of the form [optional anything][keyword][optional whitespace]. - * It won't allow keyword to be directly preceded by an alphanumeric, _, or &. - * Will be different if keyword contains regex codes. - */ - private boolean textEnds(final String keyword) { - return Pattern.matches("^.*(?