/** * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ /* * Created on Feb 22, 2005 * * @author Fabio Zadrozny */ package org.python.pydev.editor.actions; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.action.IAction; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.DocumentRewriteSession; import org.eclipse.jface.text.DocumentRewriteSessionType; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentExtension4; import org.eclipse.jface.text.IRegion; import org.python.pydev.core.ExtensionHelper; import org.python.pydev.core.IPyEdit; import org.python.pydev.core.Tuple3; import org.python.pydev.core.docutils.ParsingUtils; import org.python.pydev.core.docutils.PySelection; import org.python.pydev.core.docutils.SyntaxErrorException; import org.python.pydev.core.log.Log; import org.python.pydev.editor.PyEdit; import org.python.pydev.parser.prettyprinterv2.IFormatter; import org.python.pydev.plugin.preferences.PyCodeFormatterPage; import com.aptana.shared_core.io.FileUtils; import com.aptana.shared_core.string.FastStringBuffer; /** * @author Fabio Zadrozny */ public class PyFormatStd extends PyAction implements IFormatter { /** * Class that defines the format standard to be used * * @author Fabio */ public static class FormatStd { /** * Defines whether spaces should be added after a comma */ public boolean spaceAfterComma; /** * Defines whether ( and ) should have spaces */ public boolean parametersWithSpace; /** * Defines whether = should be spaces surrounded when inside of a parens (function call) * (as well as others related: *= +=, -=, !=, ==, etc). */ public boolean assignWithSpaceInsideParens; /** * Defines whether operators should be spaces surrounded: * + - * / // ** | & ^ ~ = */ public boolean operatorsWithSpace; public boolean addNewLineAtEndOfFile; public boolean trimLines; public boolean trimMultilineLiterals; public static final int DONT_HANDLE_SPACES = -1; /** * -1 = don't handle * 0 = 0 space * 1 = 1 space * 2 = 2 spaces * ... */ public int spacesBeforeComment = DONT_HANDLE_SPACES; /** * Spaces after the '#' in a comment. -1 = don't handle. */ public int spacesInStartComment = DONT_HANDLE_SPACES; } /** * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction) */ public void run(IAction action) { try { if (!canModifyEditor()) { return; } PyEdit pyEdit = getPyEdit(); PySelection ps = new PySelection(pyEdit); try { int[] regionsToFormat = null; if (ps.getSelLength() > 0) { int startLineIndex = ps.getStartLineIndex(); int endLineIndex = ps.getEndLineIndex(); regionsToFormat = new int[endLineIndex - startLineIndex + 1]; for (int i = startLineIndex, j = 0; i <= endLineIndex; i++, j++) { regionsToFormat[j] = i; } } else { //For full-formatting, we cannot have a syntax error. if (pyEdit.hasSyntaxError(ps.getDoc())) { return; } } applyFormatAction(pyEdit, ps, regionsToFormat, true); } catch (SyntaxErrorException e) { pyEdit.getStatusLineManager().setErrorMessage(e.getMessage()); } } catch (Exception e) { beep(e); } } /** * This method applies the code-formatting to the document in the PySelection * * @param pyEdit used to restore the selection * @param ps the selection used (contains the document that'll be changed) * @param regionsToFormat if null or empty, the whole document will be formatted, otherwise, only the passed ranges will * be formatted. * @throws SyntaxErrorException */ public void applyFormatAction(PyEdit pyEdit, PySelection ps, int[] regionsToFormat, boolean throwSyntaxError) throws BadLocationException, SyntaxErrorException { final IFormatter participant = getFormatter(); final IDocument doc = ps.getDoc(); final SelectionKeeper selectionKeeper = new SelectionKeeper(ps); DocumentRewriteSession session = null; try { if (regionsToFormat == null || regionsToFormat.length == 0) { if (doc instanceof IDocumentExtension4) { IDocumentExtension4 ext = (IDocumentExtension4) doc; session = ext.startRewriteSession(DocumentRewriteSessionType.STRICTLY_SEQUENTIAL); } participant.formatAll(doc, pyEdit, true, throwSyntaxError); } else { if (doc instanceof IDocumentExtension4) { IDocumentExtension4 ext = (IDocumentExtension4) doc; session = ext.startRewriteSession(DocumentRewriteSessionType.SEQUENTIAL); } participant.formatSelection(doc, regionsToFormat, pyEdit, ps); } } finally { if (session != null) { ((IDocumentExtension4) doc).stopRewriteSession(session); } } selectionKeeper.restoreSelection(pyEdit.getSelectionProvider(), doc); } /** * @return the source code formatter to be used. */ public IFormatter getFormatter() { IFormatter participant = (IFormatter) ExtensionHelper.getParticipant(ExtensionHelper.PYDEV_FORMATTER, false); if (participant == null) { participant = this; } return participant; } public void formatSelection(IDocument doc, int[] regionsForSave, IPyEdit edit, PySelection ps) { FormatStd formatStd = getFormat(); formatSelection(doc, regionsForSave, edit, ps, formatStd); } /** * Formats the given selection * @see IFormatter */ public void formatSelection(IDocument doc, int[] regionsForSave, IPyEdit edit, PySelection ps, FormatStd formatStd) { // Formatter formatter = new Formatter(); // formatter.formatSelection(doc, startLine, endLineIndex, edit, ps); @SuppressWarnings({ "rawtypes", "unchecked" }) List<Tuple3<Integer, Integer, String>> replaces = new ArrayList(); String docContents = doc.get(); String delimiter = PySelection.getDelimiter(doc); IDocument formatted; try { formatted = new Document(formatAll(formatStd, true, docContents, delimiter)); } catch (SyntaxErrorException e) { return; } //Actually replace the formatted lines: in our formatting, lines don't change, so, this is OK :) try { for (int i : regionsForSave) { IRegion r = doc.getLineInformation(i); int iStart = r.getOffset(); int iEnd = r.getOffset() + r.getLength(); String line = PySelection.getLine(formatted, i); replaces.add(new Tuple3<Integer, Integer, String>(iStart, iEnd - iStart, line)); } } catch (BadLocationException e) { Log.log(e); return; } //Apply the formatting from bottom to top (so that the indexes are still valid). Collections.reverse(replaces); for (Tuple3<Integer, Integer, String> tup : replaces) { try { doc.replace(tup.o1, tup.o2, tup.o3); } catch (BadLocationException e) { Log.log(e); } } if (formatStd.addNewLineAtEndOfFile) { try { int len = doc.getLength(); if (len > 0) { char lastChar = doc.getChar(len - 1); if (lastChar != '\r' && lastChar != '\n') { doc.replace(len, 0, PySelection.getDelimiter(doc)); } } } catch (Throwable e) { Log.log(e); } } } /** * Formats the whole document * @throws SyntaxErrorException * @see IFormatter */ public void formatAll(IDocument doc, IPyEdit edit, boolean isOpenedFile, boolean throwSyntaxError) throws SyntaxErrorException { // Formatter formatter = new Formatter(); // formatter.formatAll(doc, edit); FormatStd formatStd = getFormat(); formatAll(doc, edit, isOpenedFile, formatStd, throwSyntaxError); } public void formatAll(IDocument doc, IPyEdit edit, boolean isOpenedFile, FormatStd formatStd, boolean throwSyntaxError) throws SyntaxErrorException { String d = doc.get(); String delimiter = PySelection.getDelimiter(doc); String formatted = formatAll(formatStd, throwSyntaxError, d, delimiter); String contents = doc.get(); if (contents.equals(formatted)) { return; //it's the same: nothing to do. } if (!isOpenedFile) { doc.set(formatted); } else { //let's try to apply only the differences int minorLen; int contentsLen = contents.length(); if (contentsLen > formatted.length()) { minorLen = formatted.length(); } else { minorLen = contentsLen; } int applyFrom = 0; for (; applyFrom < minorLen; applyFrom++) { if (contents.charAt(applyFrom) == formatted.charAt(applyFrom)) { continue; } else { //different break; } } try { doc.replace(applyFrom, contentsLen - applyFrom, formatted.substring(applyFrom)); } catch (BadLocationException e) { Log.log(e); } } } private String formatAll(FormatStd formatStd, boolean throwSyntaxError, String d, String delimiter) throws SyntaxErrorException { String formatted = formatStr(d, formatStd, delimiter, throwSyntaxError); //To finish, check the end of line. if (formatStd.addNewLineAtEndOfFile) { try { int len = formatted.length(); if (len > 0) { char lastChar = formatted.charAt(len - 1); if (lastChar != '\r' && lastChar != '\n') { formatted += delimiter; } } } catch (Throwable e) { Log.log(e); } } return formatted; } /** * @return the format standard that should be used to do the formatting */ public static FormatStd getFormat() { FormatStd formatStd = new FormatStd(); formatStd.assignWithSpaceInsideParens = PyCodeFormatterPage.useAssignWithSpacesInsideParenthesis(); formatStd.operatorsWithSpace = PyCodeFormatterPage.useOperatorsWithSpace(); formatStd.parametersWithSpace = PyCodeFormatterPage.useSpaceForParentesis(); formatStd.spaceAfterComma = PyCodeFormatterPage.useSpaceAfterComma(); formatStd.addNewLineAtEndOfFile = PyCodeFormatterPage.getAddNewLineAtEndOfFile(); formatStd.trimLines = PyCodeFormatterPage.getTrimLines(); formatStd.trimMultilineLiterals = PyCodeFormatterPage.getTrimMultilineLiterals(); formatStd.spacesBeforeComment = PyCodeFormatterPage.getSpacesBeforeComment(); formatStd.spacesInStartComment = PyCodeFormatterPage.getSpacesInStartComment(); return formatStd; } /** * This method formats a string given some standard. * * @param str the string to be formatted * @param std the standard to be used * @return a new (formatted) string * @throws SyntaxErrorException */ /*default*/String formatStr(String str, FormatStd std, String delimiter, boolean throwSyntaxError) throws SyntaxErrorException { return formatStr(str, std, 0, delimiter, throwSyntaxError); } /** * This method formats a string given some standard. * * @param str the string to be formatted * @param std the standard to be used * @param parensLevel the level of the parenthesis available. * @return a new (formatted) string * @throws SyntaxErrorException */ private String formatStr(String str, FormatStd std, int parensLevel, String delimiter, boolean throwSyntaxError) throws SyntaxErrorException { char[] cs = str.toCharArray(); FastStringBuffer buf = new FastStringBuffer(); //Temporary buffer for some operations. Must always be cleared before it's used. FastStringBuffer tempBuf = new FastStringBuffer(); ParsingUtils parsingUtils = ParsingUtils.create(cs, throwSyntaxError); char lastChar = '\0'; for (int i = 0; i < cs.length; i++) { char c = cs[i]; switch (c) { case '\'': case '"': //ignore literals and multi-line literals, including comments... i = parsingUtils.eatLiterals(buf, i, std.trimMultilineLiterals); break; case '#': i = handleComment(std, cs, buf, tempBuf, parsingUtils, i); break; case ',': i = formatForComma(std, cs, buf, i, tempBuf); break; case '(': i = formatForPar(parsingUtils, cs, i, std, buf, parensLevel + 1, delimiter, throwSyntaxError); break; //Things to treat: //+, -, *, /, % //** // << >> //<, >, !=, <>, <=, >=, //=, *=, /=, //& ^ ~ | case '*': //for *, we also need to treat when it's used in varargs, kwargs and list expansion boolean isOperator = false; for (int j = buf.length() - 1; j >= 0; j--) { char localC = buf.charAt(j); if (Character.isWhitespace(localC)) { continue; } if (localC == '(' || localC == ',') { //it's not an operator, but vararg. kwarg or list expansion } if (Character.isJavaIdentifierPart(localC)) { //ok, there's a chance that it can be an operator, but we still have to check //the chance that it's a wild import tempBuf.clear(); while (Character.isJavaIdentifierPart(localC)) { tempBuf.append(localC); j--; if (j < 0) { break; //break while } localC = buf.charAt(j); } String reversed = tempBuf.reverse().toString(); if (!reversed.equals("import") && !reversed.equals("lambda")) { isOperator = true; } } if (localC == '\'' || localC == ')' || localC == ']') { isOperator = true; } //If it got here (i.e.: not whitespace), get out of the for loop. break; } if (!isOperator) { buf.append('*'); break;//break switch } //Otherwise, FALLTHROUGH case '+': case '-': if (c == '-' || c == '+') { // could also be * //handle exponentials correctly: e.g.: 1e-6 cannot have a space tempBuf.clear(); boolean started = false; for (int j = buf.length() - 1;; j--) { if (j < 0) { break; } char localC = buf.charAt(j); if (localC == ' ' || localC == '\t') { if (!started) { continue; } else { break; } } started = true; if (Character.isJavaIdentifierPart(localC) || localC == '.') { tempBuf.append(localC); } else { break;//break for } } boolean isExponential = true; String partialNumber = tempBuf.reverse().toString(); int partialLen = partialNumber.length(); if (partialLen < 2 || !Character.isDigit(partialNumber.charAt(0))) { //at least 2 chars: the number and the 'e' isExponential = false; } else { //first char checked... now, if the last is an 'e', we must leave it together no matter what if (partialNumber.charAt(partialLen - 1) != 'e' && partialNumber.charAt(partialLen - 1) != 'E') { isExponential = false; } } if (isExponential) { buf.rightTrim(); buf.append(c); //skip the next whitespaces from the buffer int initial = i; do { i++; } while (i < cs.length && (c = cs[i]) == ' ' || c == '\t'); if (i > initial) { i--;//backup 1 because we walked 1 too much. } break;//break switch } //Otherwise, FALLTHROUGH } case '/': case '%': case '<': case '>': case '!': case '&': case '^': case '~': case '|': i = handleOperator(std, cs, buf, parsingUtils, i, c); c = cs[i]; break; //check for = and == (other cases that have an = as the operator should already be treated) case '=': if (i < cs.length - 1 && cs[i + 1] == '=') { //if == handle as if a regular operator i = handleOperator(std, cs, buf, parsingUtils, i, c); c = cs[i]; break; } while (buf.length() > 0 && buf.lastChar() == ' ') { buf.deleteLast(); } boolean surroundWithSpaces = std.operatorsWithSpace; if (parensLevel > 0) { surroundWithSpaces = std.assignWithSpaceInsideParens; } //add space before if (surroundWithSpaces) { buf.append(' '); } //add the operator and the '=' buf.append('='); //add space after if (surroundWithSpaces) { buf.append(' '); } i = parsingUtils.eatWhitespaces(null, i + 1); break; default: if (c == '\r' || c == '\n') { if (lastChar == ',' && std.spaceAfterComma && buf.lastChar() == ' ') { buf.deleteLast(); } if (std.trimLines) { buf.rightTrim(); } } buf.append(c); } lastChar = c; } if (parensLevel == 0 && std.trimLines) { buf.rightTrim(); } return buf.toString(); } /** * Handles the case where we found a '#' in the code. */ private int handleComment(FormatStd std, char[] cs, FastStringBuffer buf, FastStringBuffer tempBuf, ParsingUtils parsingUtils, int i) { if (std.spacesBeforeComment != FormatStd.DONT_HANDLE_SPACES) { for (int j = i - 1; j >= 0; j--) { char cj = cs[j]; if (cj == '\t' || cj == ' ') { continue; } //Ok, found a non-whitespace -- if it's not a new line, we're after some //code, in which case we have to put the configured amount of spaces. if (cj != '\r' && cj != '\n') { buf.rightTrim(); buf.appendN(' ', std.spacesBeforeComment); } break; } } tempBuf.clear(); i = parsingUtils.eatComments(tempBuf, i); if (std.trimLines) { String endLine = ""; if (tempBuf.endsWith("\r\n")) { endLine = "\r\n"; tempBuf.deleteLastChars(2); } else if (tempBuf.endsWith('\r') || tempBuf.endsWith('\n')) { endLine += tempBuf.lastChar(); tempBuf.deleteLast(); } tempBuf.rightTrim(); tempBuf.append(endLine); } formatComment(std, tempBuf); buf.append(tempBuf); return i; } /** * A comment line starting or ending with one of the following will be skipped when adding * spaces to the start of a comment. */ private final static String[] BLOCK_COMMENT_SKIPS = new String[] { "###", "***", "---", "===", "+++", "@@@", "!!!", "~~~", }; /** * Adds spaces after the '#' according to the configured settings. The first char of the * buffer passed (which is also the output) should always start with a '#'. */ public static void formatComment(FormatStd std, FastStringBuffer bufWithComment) { if (std.spacesInStartComment > 0) { Assert.isTrue(bufWithComment.charAt(0) == '#'); int len = bufWithComment.length(); char firstCharFound = '\n'; String bufAsString = bufWithComment.toString(); //handle cases where the code-formatting should not take place if (FileUtils.isPythonShebangLine(bufAsString)) { return; } int spacesFound = 0; String remainingStringContent = ""; for (int j = 1; j < len; j++) { //start at 1 because 0 should always be '#' if ((firstCharFound = bufWithComment.charAt(j)) != ' ') { remainingStringContent = bufAsString.substring(j).trim(); break; } spacesFound += 1; } if (firstCharFound != '\r' && firstCharFound != '\n') { //Only add spaces if it wasn't an empty line. //handle cases where the code-formatting should not take place for (String s : BLOCK_COMMENT_SKIPS) { if (remainingStringContent.endsWith(s) || remainingStringContent.startsWith(s)) { return; } } int diff = std.spacesInStartComment - spacesFound; if (diff > 0) { bufWithComment.insertN(1, ' ', diff); } } } } /** * Handles having an operator * * @param std the coding standard to be used * @param cs the contents of the string * @param buf the buffer where the contents should be added * @param parsingUtils helper to get the contents * @param i current index * @param c current char * @return the new index after handling the operator */ private int handleOperator(FormatStd std, char[] cs, FastStringBuffer buf, ParsingUtils parsingUtils, int i, char c) { //let's discover if it's an unary operator (~ + -) boolean isUnaryWithContents = true; boolean isUnary = false; boolean changeWhitespacesBefore = true; if (c == '~' || c == '+' || c == '-') { //could be an unary operator... String trimmedLastWord = buf.getLastWord().trim(); isUnary = trimmedLastWord.length() == 0 || PySelection.ALL_KEYWORD_TOKENS.contains(trimmedLastWord); if (!isUnary) { for (char itChar : buf.reverseIterator()) { if (itChar == ' ' || itChar == '\t') { continue; } if (itChar == '=' || itChar == ',') { isUnary = true; } switch (itChar) { case '[': case '{': changeWhitespacesBefore = false; case '(': case ':': isUnaryWithContents = false; case '>': case '<': case '-': case '+': case '~': case '*': case '/': case '%': case '!': case '&': case '^': case '|': case '=': isUnary = true; } break; } } else { isUnaryWithContents = buf.length() > 0; } } if (!isUnary) { //We don't want to change whitespaces before in a binary operator that is in a new line. for (char ch : buf.reverseIterator()) { if (!Character.isWhitespace(ch)) { break; } if (ch == '\r' || ch == '\n') { changeWhitespacesBefore = false; break; } } } if (changeWhitespacesBefore) { while (buf.length() > 0 && (buf.lastChar() == ' ' || buf.lastChar() == ' ')) { buf.deleteLast(); } } boolean surroundWithSpaces = std.operatorsWithSpace; if (changeWhitespacesBefore) { //add spaces before if (isUnaryWithContents && surroundWithSpaces) { buf.append(' '); } } char localC = c; char prev = '\0'; boolean backOne = true; while (isOperatorPart(localC, prev)) { buf.append(localC); prev = localC; i++; if (i == cs.length) { break; } localC = cs[i]; if (localC == '=') { //when we get to an assign, we have found a full stmt (with assign) -- e.g.: a \\= a += a == buf.append(localC); backOne = false; break; } } if (backOne) { i--; } //add space after only if it's not unary if (!isUnary && surroundWithSpaces) { buf.append(' '); } i = parsingUtils.eatWhitespaces(null, i + 1); return i; } /** * @param c the char to be checked * @param prev * @return true if the passed char is part of an operator */ private boolean isOperatorPart(char c, char prev) { switch (c) { case '+': case '-': case '~': if (prev == '\0') { return true; } return false; } switch (c) { case '*': case '/': case '%': case '<': case '>': case '!': case '&': case '^': case '~': case '|': case '=': return true; } return false; } /** * Formats the contents for when a parenthesis is found (so, go until the closing parens and format it accordingly) * @param throwSyntaxError * @throws SyntaxErrorException */ private int formatForPar(final ParsingUtils parsingUtils, final char[] cs, final int i, final FormatStd std, final FastStringBuffer buf, final int parensLevel, final String delimiter, boolean throwSyntaxError) throws SyntaxErrorException { char c = ' '; FastStringBuffer locBuf = new FastStringBuffer(); int j = i + 1; int start = j; int end = start; while (j < cs.length && (c = cs[j]) != ')') { j++; if (c == '\'' || c == '"') { //ignore comments or multiline comments... j = parsingUtils.eatLiterals(null, j - 1, std.trimMultilineLiterals) + 1; end = j; } else if (c == '#') { j = parsingUtils.eatComments(null, j - 1) + 1; end = j; } else if (c == '(') { //open another par. if (end > start) { locBuf.append(cs, start, end - start); start = end; } j = formatForPar(parsingUtils, cs, j - 1, std, locBuf, parensLevel + 1, delimiter, throwSyntaxError) + 1; start = j; } else { end = j; } } if (end > start) { locBuf.append(cs, start, end - start); start = end; } if (c == ')') { //Now, when a closing parens is found, let's see the contents of the line where that parens was found //and if it's only whitespaces, add all those whitespaces (to handle the following case: //a(a, // b // ) <-- we don't want to change this one. char c1; FastStringBuffer buf1 = new FastStringBuffer(); if (locBuf.indexOf('\n') != -1 || locBuf.indexOf('\r') != -1) { for (int k = locBuf.length(); k > 0 && (c1 = locBuf.charAt(k - 1)) != '\n' && c1 != '\r'; k--) { buf1.insert(0, c1); } } String formatStr = formatStr(trim(locBuf).toString(), std, parensLevel, delimiter, throwSyntaxError); FastStringBuffer formatStrBuf = trim(new FastStringBuffer(formatStr, 10)); String closing = ")"; if (buf1.length() > 0 && PySelection.containsOnlyWhitespaces(buf1.toString())) { formatStrBuf.append(buf1); } else if (std.parametersWithSpace) { closing = " )"; } if (std.parametersWithSpace) { if (formatStrBuf.length() == 0) { buf.append("()"); } else { buf.append("( "); buf.append(formatStrBuf); buf.append(closing); } } else { buf.append('('); buf.append(formatStrBuf); buf.append(closing); } return j; } else { if (throwSyntaxError) { throw new SyntaxErrorException("No closing ')' found."); } //we found no closing parens but we finished looking already, so, let's just add anything without //more formatting... buf.append('('); buf.append(locBuf); return j; } } /** * We just want to trim whitespaces, not newlines! * @param locBuf the buffer to be trimmed * @return the same buffer passed as a parameter */ private FastStringBuffer trim(FastStringBuffer locBuf) { while (locBuf.length() > 0 && (locBuf.firstChar() == ' ' || locBuf.firstChar() == '\t')) { locBuf.deleteCharAt(0); } rtrim(locBuf); return locBuf; } /** * We just want to trim whitespaces, not newlines! * @param locBuf the buffer to be trimmed * @return the same buffer passed as a parameter */ private FastStringBuffer rtrim(FastStringBuffer locBuf) { while (locBuf.length() > 0 && (locBuf.lastChar() == ' ' || locBuf.lastChar() == '\t')) { locBuf.deleteLast(); } return locBuf; } /** * When a comma is found, it's formatted accordingly (spaces added after it). * * @param std the coding standard to be used * @param cs the contents of the document to be formatted * @param buf the buffer where the comma should be added * @param i the current index * @return the new index on the original doc. */ private int formatForComma(FormatStd std, char[] cs, FastStringBuffer buf, int i, FastStringBuffer formatForCommaTempBuf) { formatForCommaTempBuf.clear(); char c = '\0'; while (i < cs.length - 1 && (c = cs[i + 1]) == ' ') { formatForCommaTempBuf.append(c); i++; } if (c == '#') { //Ok, we have a comment after a comma, let's handle it according to preferences. buf.append(','); if (std.spacesBeforeComment == FormatStd.DONT_HANDLE_SPACES) { //Note: other cases we won't handle here as it should be handled when the start of //a comment is found. buf.append(formatForCommaTempBuf); } } else { //Default: handle it as usual. if (std.spaceAfterComma) { buf.append(", "); } else { buf.append(','); } } return i; } }