| /* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ |
| |
| /* |
| Part of the Processing project - http://processing.org |
| |
| 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 |
| Bug fixes Copyright (c) 2005-09 Ben Fry and Casey Reas |
| |
| 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 |
| the Free Software Foundation, version 2. |
| |
| 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.app.tools; |
| |
| import processing.app.*; |
| import processing.core.PApplet; |
| import static processing.app.I18n._; |
| |
| import java.io.*; |
| |
| |
| /** |
| * Handler for dealing with auto format. |
| * Contributed by Martin Gomez, additional bug fixes by Ben Fry. |
| * |
| * After some further digging, this code in fact appears to be a modified |
| * version of Jason Pell's GPLed "Java Beautifier" class found here: |
| * http://www.geocities.com/jasonpell/programs.html |
| * Which is itself based on code from Van Di-Han Ho: |
| * http://www.geocities.com/~starkville/vancbj_idx.html |
| * [Ben Fry, August 2009] |
| */ |
| public class AutoFormat implements Tool { |
| Editor editor; |
| |
| static final int BLOCK_MAXLEN = 1024; |
| |
| StringBuffer strOut; |
| int indentValue; |
| String indentChar; |
| int EOF; |
| CharArrayReader reader; |
| int readCount, indexBlock, lineLength, lineNumber; |
| char chars[]; |
| String strBlock; |
| int s_level[]; |
| int c_level; |
| int sp_flg[][]; |
| int s_ind[][]; |
| int s_if_lev[]; |
| int s_if_flg[]; |
| int if_lev, if_flg, level; |
| int ind[]; |
| int e_flg, paren; |
| static int p_flg[]; |
| char l_char, p_char; |
| int a_flg, q_flg, ct; |
| int s_tabs[][]; |
| String w_if_, w_else, w_for, w_ds, w_case, w_cpp_comment, w_jdoc; |
| int jdoc, j; |
| char string[]; |
| char cc; |
| int s_flg; |
| int peek; |
| char peekc; |
| int tabs; |
| char last_char; |
| char c; |
| |
| String line_feed; |
| |
| |
| public void init(Editor editor) { |
| this.editor = editor; |
| } |
| |
| |
| public String getMenuTitle() { |
| return _("Auto Format"); |
| } |
| |
| public void comment() throws IOException { |
| int save_s_flg; |
| save_s_flg = s_flg; |
| |
| int done = 0; |
| c = string[j++] = getchr(); // extra char |
| while (done == 0) { |
| c = string[j++] = getchr(); |
| while ((c != '/') && (j < string.length)) { |
| if(c == '\n' || c == '\r') { |
| lineNumber++; |
| putcoms(); |
| s_flg = 1; |
| } |
| c = string[j++] = getchr(); |
| } |
| //String tmpstr = new String(string); |
| if (j>1 && string[j-2] == '*') { |
| done = 1; |
| jdoc = 0; |
| } |
| } |
| |
| putcoms(); |
| s_flg = save_s_flg; |
| jdoc = 0; |
| return; |
| } |
| |
| |
| public char get_string() throws IOException { |
| char ch; |
| ch = '*'; |
| while (true) { |
| switch (ch) { |
| default: |
| ch = string[j++] = getchr(); |
| if (ch == '\\') { |
| string[j++] = getchr(); |
| break; |
| } |
| if (ch == '\'' || ch == '"') { |
| cc = string[j++] = getchr(); |
| while (cc != ch) { |
| if (cc == '\\') string[j++] = getchr(); |
| cc = string[j++] = getchr(); |
| } |
| break; |
| } |
| if (ch == '\n' || ch == '\r') { |
| indent_puts(); |
| a_flg = 1; |
| break; |
| } else { |
| return(ch); |
| } |
| } |
| } |
| } |
| |
| |
| public void indent_puts() { |
| string[j] = '\0'; |
| if (j > 0) { |
| if (s_flg != 0) { |
| if((tabs > 0) && (string[0] != '{') && (a_flg == 1)) { |
| tabs++; |
| } |
| p_tabs(); |
| s_flg = 0; |
| if ((tabs > 0) && (string[0] != '{') && (a_flg == 1)) { |
| tabs--; |
| } |
| a_flg = 0; |
| } |
| String j_string = new String(string); |
| strOut.append(j_string.substring(0,j)); |
| for (int i=0; i<j; i++) string[i] = '\0'; |
| j = 0; |
| |
| } else { |
| if (s_flg != 0) { |
| s_flg = 0; |
| a_flg = 0; |
| } |
| } |
| } |
| |
| |
| //public void fprintf(int outfil, String out_string) { |
| public void fprintf(String out_string) { |
| //int out_len = out_string.length(); |
| //String j_string = new String(string); |
| strOut.append(out_string); |
| } |
| |
| |
| public int grabLines() { |
| return lineNumber; |
| } |
| |
| |
| /* special edition of put string for comment processing */ |
| public void putcoms() |
| { |
| int i = 0; |
| int sav_s_flg = s_flg; |
| if(j > 0) |
| { |
| if(s_flg != 0) |
| { |
| p_tabs(); |
| s_flg = 0; |
| } |
| string[j] = '\0'; |
| i = 0; |
| while (string[i] == ' ') i++; |
| if (lookup_com(w_jdoc) == 1) jdoc = 1; |
| String strBuffer = new String(string,0,j); |
| if (string[i] == '/' && string[i+1]=='*') |
| { |
| if ((last_char != ';') && (sav_s_flg==1) ) |
| { |
| //fprintf(outfil, strBuffer.substring(i,j)); |
| fprintf(strBuffer.substring(i,j)); |
| } |
| else |
| { |
| //fprintf(outfil, strBuffer); |
| fprintf(strBuffer); |
| } |
| } |
| else |
| { |
| if (string[i]=='*' || jdoc == 0) |
| //fprintf (outfil, " "+strBuffer.substring(i,j)); |
| fprintf (" "+strBuffer.substring(i,j)); |
| else |
| //fprintf (outfil, " * "+strBuffer.substring(i,j)); |
| fprintf (" * "+strBuffer.substring(i,j)); |
| } |
| j = 0; |
| string[0] = '\0'; |
| } |
| } |
| |
| |
| public void cpp_comment() throws IOException |
| { |
| c = getchr(); |
| while(c != '\n' && c != '\r' && j<133) |
| { |
| string[j++] = c; |
| c = getchr(); |
| } |
| lineNumber++; |
| indent_puts(); |
| s_flg = 1; |
| } |
| |
| |
| /* expand indentValue into tabs and spaces */ |
| public void p_tabs() |
| { |
| int i,k; |
| |
| if (tabs<0) tabs = 0; |
| if (tabs==0) return; |
| i = tabs * indentValue; // calc number of spaces |
| //j = i/8; /* calc number of tab chars */ |
| |
| for (k=0; k < i; k++) { |
| strOut.append(indentChar); |
| } |
| } |
| |
| |
| public char getchr() throws IOException |
| { |
| if((peek < 0) && (last_char != ' ') && (last_char != '\t')) |
| { |
| if((last_char != '\n') && (last_char != '\r')) |
| p_char = last_char; |
| } |
| if(peek > 0) /* char was read previously */ |
| { |
| last_char = peekc; |
| peek = -1; |
| } |
| else /* read next char in string */ |
| { |
| indexBlock++; |
| if (indexBlock >= lineLength) |
| { |
| for (int ib=0; ib<readCount; ib++) chars[ib] = '\0'; |
| |
| lineLength = readCount = 0; |
| reader.mark(1); |
| if (reader.read() != -1) |
| { |
| reader.reset(); // back to the mark |
| readCount = reader.read(chars); |
| lineLength = readCount; |
| strBlock = new String(chars); |
| indexBlock = 0; |
| last_char = strBlock.charAt(indexBlock); |
| peek = -1; |
| peekc = '`'; |
| } |
| else |
| { |
| EOF = 1; |
| peekc = '\0'; |
| } |
| } |
| else |
| { |
| last_char = strBlock.charAt(indexBlock); |
| } |
| } |
| peek = -1; |
| if (last_char == '\r') |
| { |
| last_char = getchr(); |
| } |
| |
| return last_char; |
| } |
| |
| /* else processing */ |
| public 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]; |
| if_flg = 1; |
| } |
| |
| /* read to new_line */ |
| public int getnl() throws IOException |
| { |
| int save_s_flg; |
| save_s_flg = tabs; |
| peekc = getchr(); |
| //while ((peekc == '\t' || peekc == ' ') && |
| // (j < string.length)) { |
| while (peekc == '\t' || peekc == ' ') { |
| string[j++] = peekc; |
| peek = -1; |
| peekc = '`'; |
| peekc = getchr(); |
| peek = 1; |
| } |
| peek = 1; |
| |
| if (peekc == '/') |
| { |
| peek = -1; |
| peekc = '`'; |
| peekc = getchr(); |
| if (peekc == '*') |
| { |
| string[j++] = '/'; |
| string[j++] = '*'; |
| peek = -1; |
| peekc = '`'; |
| comment(); |
| } |
| else if (peekc == '/') |
| { |
| string[j++] = '/'; |
| string[j++] = '/'; |
| peek = -1; |
| peekc = '`'; |
| cpp_comment(); |
| return (1); |
| } |
| else |
| { |
| string[j++] = '/'; |
| peek = 1; |
| } |
| } |
| peekc = getchr(); |
| if(peekc == '\n') |
| { |
| lineNumber++; |
| peek = -1; |
| peekc = '`'; |
| tabs = save_s_flg; |
| return(1); |
| } |
| else |
| { |
| peek = 1; |
| } |
| return 0; |
| } |
| |
| public int lookup (String keyword) |
| { |
| char r; |
| int l,kk; //,k,i; |
| String j_string = new String(string); |
| |
| if (j<1) return (0); |
| kk=0; |
| while(string[kk] == ' ')kk++; |
| l=0; |
| l = j_string.indexOf(keyword); |
| if (l<0 || l!=kk) |
| { |
| return 0; |
| } |
| r = string[kk+keyword.length()]; |
| if(r >= 'a' && r <= 'z') return(0); |
| if(r >= 'A' && r <= 'Z') return(0); |
| if(r >= '0' && r <= '9') return(0); |
| if(r == '_' || r == '&') return(0); |
| return (1); |
| } |
| |
| public int lookup_com (String keyword) |
| { |
| //char r; |
| int l,kk; //,k,i; |
| String j_string = new String(string); |
| |
| if (j<1) return (0); |
| kk=0; |
| while(string[kk] == ' ')kk++; |
| l=0; |
| l = j_string.indexOf(keyword); |
| if (l<0 || l!=kk) |
| { |
| return 0; |
| } |
| return (1); |
| } |
| |
| |
| public void run() { |
| StringBuffer onechar; |
| |
| // Adding an additional newline as a hack around other errors |
| String originalText = editor.getText() + "\n"; |
| strOut = new StringBuffer(); |
| indentValue = Preferences.getInteger("editor.tabs.size"); |
| indentChar = new String(" "); |
| |
| lineNumber = 0; |
| c_level = if_lev = level = e_flg = paren = 0; |
| a_flg = q_flg = j = tabs = 0; |
| if_flg = peek = -1; |
| peekc = '`'; |
| s_flg = 1; |
| jdoc = 0; |
| |
| 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 int[10]; |
| ind = new int[10]; |
| p_flg = new int[10]; |
| s_tabs = new int[20][10]; |
| |
| w_else = new String ("else"); |
| w_if_ = new String ("if"); |
| w_for = new String ("for"); |
| w_ds = new String ("default"); |
| w_case = new String ("case"); |
| w_cpp_comment = new String ("//"); |
| w_jdoc = new String ("/**"); |
| line_feed = new String ("\n"); |
| |
| // read as long as there is something to read |
| EOF = 0; // = 1 set in getchr when EOF |
| |
| chars = new char[BLOCK_MAXLEN]; |
| string = new char[BLOCK_MAXLEN]; |
| try { // the whole process |
| // open for input |
| reader = new CharArrayReader(originalText.toCharArray()); |
| |
| // add buffering to that InputStream |
| // bin = new BufferedInputStream(in); |
| |
| for (int ib = 0; ib < BLOCK_MAXLEN; ib++) chars[ib] = '\0'; |
| |
| lineLength = readCount = 0; |
| // read up a block - remember how many bytes read |
| readCount = reader.read(chars); |
| strBlock = new String(chars); |
| |
| lineLength = readCount; |
| lineNumber = 1; |
| indexBlock = -1; |
| j = 0; |
| while (EOF == 0) |
| { |
| c = getchr(); |
| switch(c) |
| { |
| default: |
| string[j++] = c; |
| if(c != ',') |
| { |
| l_char = c; |
| } |
| break; |
| |
| case ' ': |
| case '\t': |
| if(lookup(w_else) == 1) |
| { |
| gotelse(); |
| if(s_flg == 0 || j > 0)string[j++] = c; |
| indent_puts(); |
| s_flg = 0; |
| break; |
| } |
| if(s_flg == 0 || j > 0)string[j++] = c; |
| break; |
| |
| case '\r': // <CR> for MS Windows 95 |
| case '\n': |
| lineNumber++; |
| if (EOF==1) |
| { |
| break; |
| } |
| //String j_string = new String(string); |
| |
| e_flg = lookup(w_else); |
| if(e_flg == 1) gotelse(); |
| if (lookup_com(w_cpp_comment) == 1) |
| { |
| if (string[j] == '\n') |
| { |
| string[j] = '\0'; |
| j--; |
| } |
| } |
| |
| indent_puts(); |
| //fprintf(outfil, line_feed); |
| fprintf(line_feed); |
| s_flg = 1; |
| if(e_flg == 1) |
| { |
| p_flg[level]++; |
| tabs++; |
| } |
| else |
| if(p_char == l_char) |
| { |
| a_flg = 1; |
| } |
| break; |
| |
| case '{': |
| if(lookup(w_else) == 1)gotelse(); |
| if (s_if_lev.length == c_level) { |
| 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; |
| if_lev = if_flg = 0; |
| c_level++; |
| if(s_flg == 1 && p_flg[level] != 0) |
| { |
| p_flg[level]--; |
| tabs--; |
| } |
| string[j++] = c; |
| indent_puts(); |
| getnl() ; |
| indent_puts(); |
| //fprintf(outfil,"\n"); |
| fprintf("\n"); |
| tabs++; |
| s_flg = 1; |
| if(p_flg[level] > 0) |
| { |
| ind[level] = 1; |
| level++; |
| s_level[level] = c_level; |
| } |
| break; |
| |
| case '}': |
| c_level--; |
| if (c_level < 0) |
| { |
| EOF = 1; |
| //System.out.println("eof b"); |
| string[j++] = c; |
| indent_puts(); |
| break; |
| } |
| if ((if_lev = s_if_lev[c_level]-1) < 0) |
| if_lev = 0; |
| if_flg = s_if_flg[c_level]; |
| indent_puts(); |
| tabs--; |
| p_tabs(); |
| peekc = getchr(); |
| if( peekc == ';') |
| { |
| onechar = new StringBuffer(); |
| onechar.append(c); // the } |
| onechar.append(';'); |
| //fprintf(outfil, onechar.toString()); |
| fprintf(onechar.toString()); |
| peek = -1; |
| peekc = '`'; |
| } |
| else |
| { |
| onechar = new StringBuffer(); |
| onechar.append(c); |
| //fprintf(outfil, onechar.toString()); |
| fprintf(onechar.toString()); |
| peek = 1; |
| } |
| getnl(); |
| indent_puts(); |
| //fprintf(outfil,"\n"); |
| fprintf("\n"); |
| s_flg = 1; |
| if(c_level < s_level[level]) |
| if(level > 0) level--; |
| if(ind[level] != 0) |
| { |
| tabs -= p_flg[level]; |
| p_flg[level] = 0; |
| ind[level] = 0; |
| } |
| break; |
| |
| case '"': |
| case '\'': |
| string[j++] = c; |
| cc = getchr(); |
| while(cc != c) |
| { |
| // max. length of line should be 256 |
| string[j++] = cc; |
| |
| if(cc == '\\') |
| { |
| cc = string[j++] = getchr(); |
| } |
| if(cc == '\n') |
| { |
| lineNumber++; |
| indent_puts(); |
| s_flg = 1; |
| } |
| cc = getchr(); |
| |
| } |
| string[j++] = cc; |
| if(getnl() == 1) |
| { |
| l_char = cc; |
| peek = 1; |
| peekc = '\n'; |
| } |
| break; |
| |
| case ';': |
| string[j++] = c; |
| indent_puts(); |
| if(p_flg[level] > 0 && ind[level] == 0) |
| { |
| tabs -= p_flg[level]; |
| p_flg[level] = 0; |
| } |
| getnl(); |
| indent_puts(); |
| //fprintf(outfil,"\n"); |
| fprintf("\n"); |
| s_flg = 1; |
| if(if_lev > 0) |
| if(if_flg == 1) |
| { |
| if_lev--; |
| if_flg = 0; |
| } |
| else if_lev = 0; |
| break; |
| |
| case '\\': |
| string[j++] = c; |
| string[j++] = getchr(); |
| break; |
| |
| case '?': |
| q_flg = 1; |
| string[j++] = c; |
| break; |
| |
| case ':': |
| string[j++] = c; |
| peekc = getchr(); |
| if(peekc == ':') |
| { |
| indent_puts(); |
| //fprintf (outfil,":"); |
| fprintf(":"); |
| peek = -1; |
| peekc = '`'; |
| break; |
| } |
| else |
| { |
| //int double_colon = 0; |
| peek = 1; |
| } |
| |
| if(q_flg == 1) |
| { |
| q_flg = 0; |
| break; |
| } |
| if(lookup(w_ds) == 0 && lookup(w_case) == 0) |
| { |
| s_flg = 0; |
| indent_puts(); |
| } |
| else |
| { |
| tabs--; |
| indent_puts(); |
| tabs++; |
| } |
| peekc = getchr(); |
| if(peekc == ';') |
| { |
| fprintf(";"); |
| peek = -1; |
| peekc = '`'; |
| } |
| else |
| { |
| peek = 1; |
| } |
| getnl(); |
| indent_puts(); |
| fprintf("\n"); |
| s_flg = 1; |
| break; |
| |
| case '/': |
| string[j++] = c; |
| peekc = getchr(); |
| |
| if(peekc == '/') |
| { |
| string[j++] = peekc; |
| peekc = '`'; |
| peek = -1; |
| cpp_comment(); |
| //fprintf(outfil,"\n"); |
| fprintf("\n"); |
| break; |
| } |
| else |
| { |
| peek = 1; |
| } |
| |
| if(peekc != '*') { |
| break; |
| } |
| else |
| { |
| if (j > 0) string[j--] = '\0'; |
| if (j > 0) indent_puts(); |
| string[j++] = '/'; |
| string[j++] = '*'; |
| peek = -1; |
| peekc = '`'; |
| comment(); |
| break; |
| } |
| |
| case '#': |
| string[j++] = c; |
| cc = getchr(); |
| while(cc != '\n') |
| { |
| string[j++] = cc; |
| cc = getchr(); |
| } |
| string[j++] = cc; |
| s_flg = 0; |
| indent_puts(); |
| s_flg = 1; |
| break; |
| |
| case ')': |
| paren--; |
| if (paren < 0) |
| { |
| EOF = 1; |
| //System.out.println("eof c"); |
| } |
| string[j++] = c; |
| indent_puts(); |
| if(getnl() == 1) |
| { |
| peekc = '\n'; |
| peek = 1; |
| if(paren != 0) |
| { |
| a_flg = 1; |
| } |
| else if(tabs > 0) |
| { |
| p_flg[level]++; |
| tabs++; |
| ind[level] = 0; |
| } |
| } |
| break; |
| |
| case '(': |
| string[j++] = c; |
| paren++; |
| if ((lookup(w_for) == 1)) |
| { |
| c = get_string(); |
| while(c != ';') c = get_string(); |
| 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) |
| { |
| EOF = 1; |
| //System.out.println("eof d"); |
| } |
| indent_puts(); |
| if(getnl() == 1) |
| { |
| peekc = '\n'; |
| peek = 1; |
| p_flg[level]++; |
| tabs++; |
| ind[level] = 0; |
| } |
| break; |
| } |
| |
| if(lookup(w_if_) == 1) |
| { |
| indent_puts(); |
| s_tabs[c_level][if_lev] = tabs; |
| sp_flg[c_level][if_lev] = p_flg[level]; |
| s_ind[c_level][if_lev] = ind[level]; |
| if_lev++; |
| if_flg = 1; |
| } |
| } // end switch |
| |
| //System.out.println("string len is " + string.length); |
| //if (EOF == 1) System.out.println(string); |
| //String j_string = new String(string); |
| |
| } // end while not EOF |
| |
| /* |
| int bad; |
| while ((bad = bin.read()) != -1) { |
| System.out.print((char) bad); |
| } |
| */ |
| /* |
| char bad; |
| //while ((bad = getchr()) != 0) { |
| while (true) { |
| getchr(); |
| if (peek != -1) { |
| System.out.print(last_char); |
| } else { |
| break; |
| } |
| } |
| */ |
| |
| // save current (rough) selection point |
| int selectionEnd = editor.getSelectionStop(); |
| |
| // make sure the caret would be past the end of the text |
| if (strOut.length() < selectionEnd - 1) { |
| selectionEnd = strOut.length() - 1; |
| } |
| |
| reader.close(); // close buff |
| |
| String formattedText = strOut.toString(); |
| if (formattedText.equals(originalText)) { |
| editor.statusNotice(_("No changes necessary for Auto Format.")); |
| |
| } else if (paren != 0) { |
| // warn user if there are too many parens in either direction |
| if (paren < 0) { |
| editor.statusError( |
| _("Auto Format Canceled: Too many right parentheses.")); |
| } else { |
| editor.statusError( |
| _("Auto Format Canceled: Too many left parentheses.")); |
| } |
| |
| } else if (c_level != 0) { // check braces only if parens are ok |
| if (c_level < 0) { |
| editor.statusError( |
| _("Auto Format Canceled: Too many right curly braces.")); |
| } else { |
| editor.statusError( |
| _("Auto Format Canceled: Too many left curly braces.")); |
| } |
| |
| } else { |
| // replace with new bootiful text |
| // selectionEnd hopefully at least in the neighborhood |
| editor.setText(formattedText); |
| editor.setSelection(selectionEnd, selectionEnd); |
| editor.getSketch().setModified(true); |
| // mark as finished |
| editor.statusNotice(_("Auto Format finished.")); |
| } |
| |
| } catch (Exception e) { |
| editor.statusError(e); |
| } |
| } |
| } |