diff --git a/app/src/processing/mode/java/AutoFormat.java b/app/src/processing/mode/java/AutoFormat.java index 935e2a903..91d8a7cec 100644 --- a/app/src/processing/mode/java/AutoFormat.java +++ b/app/src/processing/mode/java/AutoFormat.java @@ -38,56 +38,62 @@ import processing.core.PApplet; *

* After some further digging, this code in fact appears to be a modified * version of Jason Pell's GPLed "Java Beautifier" class found - * here. - * Which is itself based on code from Van Di-Han Ho from - * here. + * here, + * which is itself based on code from Van Di-Han Ho from + * here. * [Ben Fry, August 2009] */ public class AutoFormat implements Formatter { private char[] chars; private final StringBuilder buf = new StringBuilder(); - private final StringBuilder result = new StringBuilder(); private int indentValue; private boolean EOF; - private boolean a_flg, e_flg, if_flg, s_flag, q_flg; - private boolean s_if_flg[]; + private boolean a_flg, if_flg, s_flag, elseFlag; + + /** Number of ? entered without exiting at : of a?b:c structures. */ + private int conditionalLevel; + private int pos; -// private int lineNumber; - private int s_level[]; private int c_level; - private int sp_flg[][]; - private int s_ind[][]; - private int s_if_lev[]; - private int if_lev, level; - private int ind[]; - private int paren; - private int p_flg[]; + private int[][] sp_flg; + private int[][] s_ind; + private int if_lev; + + /** Number of curly brackets entered and not exited. */ + private int level; + + /** Number of parentheses entered and not exited. */ + private int parenLevel; + + private int[] ind; + private int[] p_flg; private char l_char; - private int ct; - private int s_tabs[][]; + private int[][] s_tabs; private boolean jdoc_flag; private char cc; private int tabs; private char c; + private char lastNonWhitespace = 0; private final Stack castFlags = new Stack(); - private void comment() { - final boolean save_s_flg = s_flag; + private void handleMultiLineComment() { + final boolean saved_s_flag = s_flag; - buf.append(c = next()); // extra char + char ch; + buf.append(ch = nextChar()); // extra char while (true) { - buf.append(c = next()); - while ((c != '/')) { - if (c == '\n') { -// lineNumber++; + buf.append(ch = nextChar()); + while (ch != '/') { + if (ch == '\n') { +// lineNumber++; writeIndentedComment(); s_flag = true; } - buf.append(c = next()); + buf.append(ch = nextChar()); } if (buf.length() >= 2 && buf.charAt(buf.length() - 2) == '*') { jdoc_flag = false; @@ -96,50 +102,65 @@ public class AutoFormat implements Formatter { } writeIndentedComment(); - s_flag = save_s_flg; + s_flag = saved_s_flag; jdoc_flag = false; return; } - - private char get_string() { - char ch; - while (true) { - buf.append(ch = next()); - if (ch == '\\') { - buf.append(next()); - continue; - } - if (ch == '\'' || ch == '"') { - buf.append(cc = next()); - while (!EOF && cc != ch) { - if (cc == '\\') { - buf.append(next()); - } - buf.append(cc = next()); - } - continue; - } - if (ch == '\n') { - writeIndentedLine(); - a_flg = true; - continue; - } - return ch; + + private void handleSingleLineComment() { + char ch = nextChar(); + while (ch != '\n') { + buf.append(ch); + ch = nextChar(); } +// lineNumber++; + writeIndentedLine(); + 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; +// } +// } +// } + private void writeIndentedLine() { if (buf.length() == 0) { if (s_flag) { - s_flag = a_flg = e_flg = false; + s_flag = a_flg = elseFlag = false; } return; } if (s_flag) { - final boolean shouldIndent = (tabs > 0) && (buf.charAt(0) != '{') - && a_flg; + final boolean shouldIndent = + (tabs > 0) && (buf.charAt(0) != '{') && a_flg; if (shouldIndent) { tabs++; } @@ -150,23 +171,22 @@ public class AutoFormat implements Formatter { } a_flg = false; } - if (e_flg) { + if (elseFlag) { if (lastNonSpaceChar() == '}') { trimRight(result); - result.append(" "); + result.append(' '); } - e_flg = false; + elseFlag = false; } result.append(buf); buf.setLength(0); } - + private char lastNonSpaceChar() { - for (int i=result.length()-1; i>=0; i--) { + for (int i = result.length() - 1; i >= 0; i--) { char c_i = result.charAt(i); - if (c_i == ' ' || c_i == '\n') continue; - else return c_i; + if (c_i != ' ' && c_i != '\n') return c_i; } return 0; } @@ -187,7 +207,7 @@ public class AutoFormat implements Formatter { jdoc_flag = true; } if (buf.charAt(i) == '/' && buf.charAt(i + 1) == '*') { - if (saved_s_flag && prev() != ';') { + if (saved_s_flag && getLastNonWhitespace() != ';') { result.append(buf.substring(i)); } else { result.append(buf); @@ -204,18 +224,6 @@ public class AutoFormat implements Formatter { } - private void handleSingleLineComment() { - c = next(); - while (c != '\n') { - buf.append(c); - c = next(); - } -// lineNumber++; - writeIndentedLine(); - s_flag = true; - } - - private void printIndentation() { if (tabs < 0) { tabs = 0; @@ -225,7 +233,7 @@ public class AutoFormat implements Formatter { } final int spaces = tabs * indentValue; for (int k = 0; k < spaces; k++) { - result.append(" "); + result.append(' '); } } @@ -238,10 +246,7 @@ public class AutoFormat implements Formatter { } - private char lastNonWhitespace = 0; - - - private int prev() { + private int getLastNonWhitespace() { return lastNonWhitespace; } @@ -256,33 +261,32 @@ public class AutoFormat implements Formatter { if (pos == chars.length - 1) { EOF = true; } else { - pos--; // reset for next() + pos--; // reset for nextChar() } } - private char next() { + private char nextChar() { if (EOF) { - return 0; + return '\0'; } pos++; - final char c; + char retVal; if (pos < chars.length) { - c = chars[pos]; - if (!Character.isWhitespace(c)) - lastNonWhitespace = c; + retVal = chars[pos]; + if (!Character.isWhitespace(retVal)) + lastNonWhitespace = retVal; } else { - c = 0; + retVal = '\0'; } - if (pos == chars.length - 1) { + if (pos == chars.length-1) { EOF = true; } - return c; + return retVal; } - /* else processing */ - private void gotelse() { + 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]; @@ -290,23 +294,22 @@ public class AutoFormat implements Formatter { } - /* read to new_line */ - private boolean getnl() { + private boolean readUntilNewLine() { final int savedTabs = tabs; char c = peek(); while (!EOF && (c == '\t' || c == ' ')) { - buf.append(next()); + buf.append(nextChar()); c = peek(); } if (c == '/') { - buf.append(next()); + buf.append(nextChar()); c = peek(); if (c == '*') { - buf.append(next()); - comment(); + buf.append(nextChar()); + handleMultiLineComment(); } else if (c == '/') { - buf.append(next()); + buf.append(nextChar()); handleSingleLineComment(); return true; } @@ -315,7 +318,7 @@ public class AutoFormat implements Formatter { c = peek(); if (c == '\n') { // eat it - next(); + nextChar(); // lineNumber++; tabs = savedTabs; return true; @@ -344,24 +347,25 @@ public class AutoFormat implements Formatter { public String format(final String source) { final String normalizedText = source.replaceAll("\r", ""); - final String cleanText = normalizedText - + (normalizedText.endsWith("\n") ? "" : "\n"); + final String cleanText = + normalizedText + (normalizedText.endsWith("\n") ? "" : "\n"); result.setLength(0); indentValue = Preferences.getInteger("editor.tabs.size"); -// lineNumber = 0; - q_flg = e_flg = a_flg = if_flg = false; +// lineNumber = 0; + boolean forFlag = a_flg = if_flg = false; s_flag = true; - c_level = if_lev = level = paren = 0; + int forParenthLevel = 0; + conditionalLevel = parenLevel = c_level = if_lev = level = 0; tabs = 0; jdoc_flag = false; - s_level = new int[10]; + int[] s_level = new int[10]; sp_flg = new int[20][10]; s_ind = new int[20][10]; - s_if_lev = new int[10]; - s_if_flg = new boolean[10]; + int[] s_if_lev = new int[10]; + boolean[] s_if_flg = new boolean[10]; ind = new int[10]; p_flg = new int[10]; s_tabs = new int[20][10]; @@ -369,10 +373,10 @@ public class AutoFormat implements Formatter { chars = cleanText.toCharArray(); // lineNumber = 1; - EOF = false; // set in next() when EOF + EOF = false; // set in nextChar() when EOF while (!EOF) { - c = next(); + c = nextChar(); switch (c) { default: buf.append(c); @@ -388,12 +392,13 @@ public class AutoFormat implements Formatter { case ' ': case '\t': - e_flg = lookup("else"); - if (e_flg) { - gotelse(); + elseFlag = lookup("else"); + if (elseFlag) { + gotElse(); if ((!s_flag) || buf.length() > 0) { buf.append(c); } + writeIndentedLine(); s_flag = false; break; @@ -408,9 +413,9 @@ public class AutoFormat implements Formatter { if (EOF) { break; } - e_flg = lookup("else"); - if (e_flg) { - gotelse(); + elseFlag = lookup("else"); + if (elseFlag) { + gotElse(); } if (lookup_com("//")) { final char lastChar = buf.charAt(buf.length() - 1); @@ -422,19 +427,18 @@ public class AutoFormat implements Formatter { writeIndentedLine(); result.append("\n"); s_flag = true; - if (e_flg) { + if (elseFlag) { p_flg[level]++; tabs++; - } else if (prev() == l_char) { + } else if (getLastNonWhitespace() == l_char) { a_flg = true; } break; case '{': - e_flg = lookup("else"); - if (e_flg) { - gotelse(); - } + elseFlag = lookup("else"); + if (elseFlag) gotElse(); + if (s_if_lev.length == c_level) { s_if_lev = PApplet.expand(s_if_lev); s_if_flg = PApplet.expand(s_if_flg); @@ -454,9 +458,9 @@ public class AutoFormat implements Formatter { buf.append(" "); buf.append(c); writeIndentedLine(); - getnl(); + readUntilNewLine(); writeIndentedLine(); - //fprintf(outfil,"\n"); + result.append("\n"); tabs++; s_flag = true; @@ -489,11 +493,12 @@ public class AutoFormat implements Formatter { printIndentation(); result.append(c); if (peek() == ';') { - result.append(next()); + result.append(nextChar()); } - getnl(); + + readUntilNewLine(); writeIndentedLine(); - result.append("\n"); + result.append('\n'); s_flag = true; if (c_level < s_level[level]) { if (level > 0) { @@ -511,21 +516,21 @@ public class AutoFormat implements Formatter { case '"': case '\'': buf.append(c); - cc = next(); + cc = nextChar(); while (!EOF && cc != c) { buf.append(cc); if (cc == '\\') { - buf.append(cc = next()); + buf.append(cc = nextChar()); } if (cc == '\n') { // lineNumber++; writeIndentedLine(); s_flag = true; } - cc = next(); + cc = nextChar(); } buf.append(cc); - if (getnl()) { + if (readUntilNewLine()) { l_char = cc; // push a newline into the stream chars[pos--] = '\n'; @@ -533,13 +538,21 @@ public class AutoFormat implements Formatter { break; case ';': + if (forFlag) { + // This is like a comma. + trimRight(buf); + buf.append("; "); + // Not non-whitespace: allow \n. + advanceToNonSpace(); + break; + } buf.append(c); writeIndentedLine(); if (p_flg[level] > 0 && ind[level] == 0) { tabs -= p_flg[level]; p_flg[level] = 0; } - getnl(); + readUntilNewLine(); writeIndentedLine(); result.append("\n"); s_flag = true; @@ -555,26 +568,39 @@ public class AutoFormat implements Formatter { case '\\': buf.append(c); - buf.append(next()); + buf.append(nextChar()); break; case '?': - q_flg = true; + conditionalLevel++; buf.append(c); break; case ':': - buf.append(c); + // Java 8 :: operator. if (peek() == ':') { writeIndentedLine(); - result.append(next()); + result.append(c).append(nextChar()); break; } - if (q_flg) { - q_flg = false; + // End a?b:c structures. + else if (conditionalLevel>0) { + conditionalLevel--; + buf.append(c); break; } + + else if (forFlag) { + trimRight(buf); + buf.append(" : "); + // Not to non-whitespace: allow \n. + advanceToNonSpace(); + break; + } + + buf.append(c); + if (!lookup("default") && !lookup("case")) { s_flag = false; writeIndentedLine(); @@ -584,26 +610,27 @@ public class AutoFormat implements Formatter { tabs++; } if (peek() == ';') { - result.append(next()); + result.append(nextChar()); } - getnl(); + readUntilNewLine(); writeIndentedLine(); - result.append("\n"); + result.append('\n'); s_flag = true; break; case '/': - final char la = peek(); - if (la == '/') { - buf.append(c).append(next()); + final char next = peek(); + if (next == '/') { + // call nextChar to move on. + buf.append(c).append(nextChar()); handleSingleLineComment(); result.append("\n"); - } else if (la == '*') { + } else if (next == '*') { if (buf.length() > 0) { writeIndentedLine(); } - buf.append(c).append(next()); - comment(); + buf.append(c).append(nextChar()); + handleMultiLineComment(); } else { buf.append(c); } @@ -613,15 +640,23 @@ public class AutoFormat implements Formatter { final boolean isCast = castFlags.isEmpty() ? false : castFlags.pop(); - paren--; - if (paren < 0) { - paren = 0; + parenLevel--; + + // If we're further back than the start of a for loop, we've + // left it. + if (forFlag && forParenthLevel > parenLevel) { + forFlag = false; } + + if (parenLevel < 0) { + parenLevel = 0; + } + buf.append(c); writeIndentedLine(); - if (getnl()) { + if (readUntilNewLine()) { chars[pos--] = '\n'; - if (paren != 0) { + if (parenLevel != 0) { a_flg = true; } else if (tabs > 0 && !isCast) { p_flg[level]++; @@ -644,42 +679,16 @@ public class AutoFormat implements Formatter { } buf.append(c); - paren++; + parenLevel++; + // 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) { - // TODO(feinberg): handle new-style for loops - c = get_string(); - while (c != ';' && c != ':') { - c = get_string(); + if (!forFlag) { + forParenthLevel = parenLevel; + forFlag = true; } - ct = 0; - int for_done = 0; - while (for_done == 0) { - c = get_string(); - while (c != ')') { - if (c == '(') { - ct++; - } - c = get_string(); - } - if (ct != 0) { - ct--; - } else { - for_done = 1; - } - } // endwhile for_done - paren--; - if (paren < 0) { - paren = 0; - } - writeIndentedLine(); - if (getnl()) { - chars[pos--] = '\n'; - p_flg[level]++; - tabs++; - ind[level] = 0; - } - break; } else if (isIf) { writeIndentedLine(); s_tabs[c_level][if_lev] = tabs;