/** * This file Copyright (c) 2005-2008 Aptana, Inc. This program is * dual-licensed under both the Aptana Public License and the GNU General * Public license. You may elect to use one or the other of these licenses. * * This program is distributed in the hope that it will be useful, but * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. Redistribution, except as permitted by whichever of * the GPL or APL you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or modify this * program under the terms of the GNU General Public License, * Version 3, as published by the Free Software Foundation. You should * have received a copy of the GNU General Public License, Version 3 along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Aptana provides a special exception to allow redistribution of this file * with certain other free and open source software ("FOSS") code and certain additional terms * pursuant to Section 7 of the GPL. You may view the exception and these * terms on the web at http://www.aptana.com/legal/gpl/. * * 2. For the Aptana Public License (APL), this program and the * accompanying materials are made available under the terms of the APL * v1.0 which accompanies this distribution, and is available at * http://www.aptana.com/legal/apl/. * * You may view the GPL, Aptana's exception and additional terms, and the * APL in the file titled license.html at the root of the corresponding * plugin containing this source file. * * Any modifications to this file must keep this entire header intact. */ package com.aptana.ide.editor.html.formatting; import java.util.ArrayList; import java.util.HashSet; import java.util.Map; import java.util.Stack; import org.eclipse.core.resources.IProject; import com.aptana.ide.core.IdeLog; import com.aptana.ide.editor.html.HTMLPlugin; import com.aptana.ide.editor.html.lexing.HTMLTokenTypes; import com.aptana.ide.editor.html.parsing.HTMLMimeType; import com.aptana.ide.editor.html.parsing.HTMLParseState; import com.aptana.ide.editors.unified.BaseFormatter; import com.aptana.ide.editors.unified.ICodeFormatter; import com.aptana.ide.editors.unified.LanguageRegistry; import com.aptana.ide.io.SourceWriter; import com.aptana.ide.lexer.Lexeme; import com.aptana.ide.lexer.LexemeList; import com.aptana.ide.parsing.IParser; /** * @author Pavel Petrochenko */ public class HTMLCodeFormatter extends BaseFormatter { private Stack tagNames = new Stack(); private Stack stillWrap = new Stack(); private HTMLCodeFormatterOptions codeoptions; private boolean isSelfClosing(int offset, LexemeList list) { for (int a = offset; a < list.size(); a++) { Lexeme lexeme = list.get(a); if (lexeme.getToken().getTypeIndex() == HTMLTokenTypes.GREATER_THAN) { return false; } if (lexeme.getToken().getTypeIndex() == HTMLTokenTypes.SLASH_GREATER_THAN) { return true; } } return false; } boolean hasType(LexemeList list, int offset, int tokenType) { if (list.size() > offset) { return list.get(offset).getToken().getTypeIndex() == tokenType; } return false; } private boolean isSmartNoWrap(LexemeList lexemeList, int a, HTMLCodeFormatterOptions codeoptions2) { if (codeoptions2.doNotWrapSimple) { if (a > 0) { Lexeme fL = lexemeList.get(a - 1); int typeIndex = fL.getToken().getTypeIndex(); if (typeIndex == HTMLTokenTypes.END_TAG) { return false; } } else { return false; } a++; while (a < lexemeList.size()) { Lexeme lexeme = lexemeList.get(a); int typeIndex = lexeme.getToken().getTypeIndex(); if (typeIndex != HTMLTokenTypes.TEXT) { if (typeIndex == HTMLTokenTypes.END_TAG) { return true; } return false; } a++; } } return false; } private String[] splitOnLines(String comment) { String[] result = null; ArrayList lines = new ArrayList(); boolean lastSlash = false; int lastPos = 0; for (int a = 0; a < comment.length(); a++) { char c = comment.charAt(a); if (c == '\n' || c == '\r') { if (!lastSlash) { lastSlash = true; lines.add(comment.substring(lastPos, a)); lastPos = a + 1; } continue; } lastSlash = false; } lines.add(comment.substring(lastPos, comment.length())); result = new String[lines.size()]; lines.toArray(result); return result; } /** * @param string * @return indentation string as it is on previous line; */ public String getCurrentIndentationString(String string) { int pos = string.length(); int startLine = 0; for (int a = string.length() - 1; a >= 0; a--) { char charAt = string.charAt(a); if (charAt == '\n' || charAt == '\r') { startLine = a + 1; break; } if (!Character.isWhitespace(charAt)) { pos = a; } } if (string.length() == 0) { return ""; //$NON-NLS-1$ } return string.substring(startLine, pos); } /** * @see com.aptana.ide.editors.unified.ICodeFormatter#format(java.lang.String, * boolean, java.util.Map, org.eclipse.core.resources.IProject, * java.lang.String) */ public String format(String notFormatted, boolean isSelection, Map options, IProject project, String linedelimeters) { String doc = null; doc = notFormatted; level = 0; tagNames.clear(); try { IParser parser = LanguageRegistry.getParser(HTMLMimeType.MimeType); HTMLParseState createParseState = (HTMLParseState) parser .createParseState(null); createParseState.setEditState(doc, doc, 0, 0); parser.parse(createParseState); LexemeList lexemeList = createParseState.getLexemeList(); Stack clevel = new Stack(); HashSet notClosed = new HashSet(); for (int a = 0; a < lexemeList.size(); a++) { Lexeme lexeme = lexemeList.get(a); if (lexeme.getToken().getTypeIndex() == HTMLTokenTypes.START_TAG) { if (!isSelfClosing(a, lexemeList)) { clevel.push(lexeme); } } if (lexeme.getToken().getTypeIndex() == HTMLTokenTypes.END_TAG) { if (!clevel.isEmpty()) { Lexeme lex = (Lexeme) clevel.pop(); String ltagName = lex.getText().substring(1); String ktagName = lexeme.getText().substring(2); if (!ltagName.equals(ktagName)) { clevel.push(lex); notClosed.add(lex); } } } } codeoptions = new HTMLCodeFormatterOptions(options, project); if (!codeoptions.doFormatting) { return notFormatted; } String indent = codeoptions.formatterTabChar; if (indent.length() == 0) { indent = " "; //$NON-NLS-1$ } if (indent.charAt(0) == ' ') { StringBuffer bf = new StringBuffer(); for (int a = 0; a < codeoptions.tabSize; a++) { bf.append(' '); } indent = bf.toString(); } SourceWriter buf = new SourceWriter(0, indent, codeoptions.tabSize); if (linedelimeters != null) { buf.setLineDelimeter(linedelimeters); } boolean inOtherLanguage = false; int startOtherLanguage = -1; String language = null; boolean isInAttr = false; boolean isSelfClosing = false; int quoteStart = 0; boolean printLine = false; boolean lastTextEndsWithSpace = false; boolean inQuote = false; boolean isInPre = false; boolean inConditional = false; boolean isSmartNoWrap = false; int prePositon = -1; boolean lastError = false; boolean isInTag = false; int preCount = 0; String tString = null; int htmlPosition = -1; String lastTagName = ""; //$NON-NLS-1$ String realLastTagName = ""; //$NON-NLS-1$ for (int a = 0; a < lexemeList.size(); a++) { Lexeme lexeme = lexemeList.get(a); int i = lexeme.getStartingOffset() - 1; int typeIndex = lexeme.getToken().getTypeIndex(); if (inOtherLanguage && lexeme.getLanguage().equals(HTMLMimeType.MimeType)) { inOtherLanguage = false; String substring = doc.substring(startOtherLanguage, lexeme .getStartingOffset()); // only format non-attribute text if (!isInAttr) { formatAnotherLanguage(isSelection, options, project, linedelimeters, buf, language, substring); if (tString != null) { buf.print(tString); tString = null; } } } if (isInPre) { if (typeIndex == HTMLTokenTypes.START_TAG) { String tagName = lexeme.getText().substring(1).trim(); if (tagName.equals("pre")) { //$NON-NLS-1$ preCount++; } continue; } if (typeIndex == HTMLTokenTypes.END_TAG) { String tagName = lexeme.getText().substring(2).trim(); if (tagName.equals("pre")) { //$NON-NLS-1$ if (preCount == 0) { if (buf.getCurrentIndentLevel() == 0) { buf.printIndent(); } buf.print(notFormatted.substring(prePositon, lexeme.getStartingOffset())); isInPre = false; buf.print(lexeme.getText()); } else { preCount--; } continue; } continue; } else { continue; } } if (!lexeme.getLanguage().equals(HTMLMimeType.MimeType)) { if (!inOtherLanguage) { if (!codeoptions.doFormatting) { String txt = notFormatted.substring(htmlPosition, lexeme.getStartingOffset()); tString = getCurrentIndentationString(txt); buf.print(txt); htmlPosition = -1; } inOtherLanguage = true; Lexeme previous = a > 0 ? lexemeList.get(a - 1) : null; isInAttr = previous != null ? (previous.getToken() .getTypeIndex() == HTMLTokenTypes.QUOTE) : false; if (previous != null) { startOtherLanguage = previous.getEndingOffset(); } else { startOtherLanguage = lexeme.getStartingOffset(); } language = lexeme.getLanguage(); continue; } else { continue; } } if (typeIndex == HTMLTokenTypes.PI_TEXT) { // if (!codeoptions.doFormatting) // { // buf.println(notFormatted.substring(htmlPosition, // lexeme.getStartingOffset())); // } // buf.print(' '); if (!isInAttr) { formatAnotherLanguage(false, options, project, linedelimeters, buf, "text/php", lexeme.getText()); //$NON-NLS-1$ } // buf.println(); htmlPosition = -1; continue; } if (!codeoptions.doFormatting) { if (htmlPosition == -1) { htmlPosition = lexeme.getStartingOffset(); } continue; } if (typeIndex == HTMLTokenTypes.PI_OPEN) { int iLevel = buf.getCurrentIndentLevel(); if (iLevel != 0) { buf.println(); } buf.printIndent(); buf.print(lexeme.getText()); // interior text will handle own carriage returns // buf.println(); continue; } if (typeIndex == HTMLTokenTypes.QUESTION_GREATER_THAN) { // interior text will handle own carriage returns // buf.println(); // buf.printIndent(); if (buf.getCurrentIndentLevel() == 0) { buf.printIndent(); } buf.print(lexeme.getText()); buf.println(); continue; } if (typeIndex == HTMLTokenTypes.ERROR || typeIndex == HTMLTokenTypes.STRING && lexeme.getCategoryIndex() == HTMLTokenTypes.ERROR) { // TODO REMOVE ME AFTER #STU-276 will be fixed if (lexeme.getText().equals("<![endif]")) { //$NON-NLS-1$ buf.print(lexeme.getText()); inConditional = true; buf.decreaseIndent(); continue; } if (inConditional) { if (lexeme.getText().endsWith("]")) //$NON-NLS-1$ { buf.print(lexeme.getText()); inConditional = false; continue; } } if (lexeme.getText().startsWith("<![if")) { //$NON-NLS-1$ buf.print(lexeme.getText()); buf.increaseIndent(); continue; } if (a > 0) { Lexeme pl = lexemeList.get(a - 1); String substring = notFormatted.substring(pl .getEndingOffset(), lexeme.getStartingOffset()); if (substring.length() > 0) { if (substring.contains("\r") //$NON-NLS-1$ || substring.contains("\n")) //$NON-NLS-1$ { buf.println(); } else if (substring.contains(" ") //$NON-NLS-1$ || substring.contains("\t")) //$NON-NLS-1$ { buf.print(' '); } } } if (!isInTag) { if (!codeoptions.notWrappingTags.contains(lastTagName)) { if (buf.getCurrentIndentLevel() != 0) { buf.println(); } buf.printIndent(); } } lastError = true; buf.print(lexeme.getText()); if (a < lexemeList.size() - 1) { Lexeme l1 = lexemeList.get(a + 1); if (l1.getLanguage().equals(HTMLMimeType.MimeType) && l1.typeIndex != HTMLTokenTypes.ERROR) { String substring = notFormatted.substring(lexeme .getEndingOffset(), l1.getStartingOffset()); if (substring.length() > 0) { if (substring.contains("\r") //$NON-NLS-1$ || substring.contains("\n")) //$NON-NLS-1$ { buf.println(); } else if (substring.contains(" ") //$NON-NLS-1$ || substring.contains("\t")) //$NON-NLS-1$ { buf.print(' '); } } } } if (!isInTag) { if (!codeoptions.notWrappingTags.contains(lastTagName)) { if (a < lexemeList.size() - 1 && lexemeList.get(a + 1).getToken() .getTypeIndex() == HTMLTokenTypes.ERROR) { continue; } else { buf.println(); } } else { if (a < lexemeList.size() - 1 && lexemeList.get(a + 1).getToken() .getTypeIndex() != HTMLTokenTypes.ERROR) { buf.print(' '); } } } continue; } lastError = false; if (typeIndex == HTMLTokenTypes.COMMENT) { appendComment(lexemeList, codeoptions, buf, a, lexeme); continue; } if (typeIndex == HTMLTokenTypes.TEXT) { if (lexeme.getText().startsWith("<![endif]>")) { //$NON-NLS-1$ buf.print(lexeme.getText()); inConditional = true; buf.decreaseIndent(); continue; } if (inConditional) { if (lexeme.getText().endsWith("]")) //$NON-NLS-1$ { buf.print(lexeme.getText()); inConditional = false; continue; } } if (lexeme.getText().startsWith("<![if")) { //$NON-NLS-1$ buf.print(lexeme.getText()); buf.increaseIndent(); continue; } String[] splitOnLines = splitOnLines(lexeme.getText()); boolean hasNotEmpty = false; ArrayList withoutEmpty = new ArrayList(); boolean lastHasSpace = false; if (isSmartNoWrap) { if (splitOnLines.length == 1) { buf.print(splitOnLines[0]); continue; } if (splitOnLines.length == 2 && splitOnLines[1].trim().length() == 0) { buf.print(splitOnLines[0]); continue; } } for (int b = 0; b < splitOnLines.length; b++) { String string = splitOnLines[b]; String text = string.trim(); boolean hasSpace = false; for (int j = string.length() - 1; j > 0; j--) { char c = string.charAt(j); if (c == ' ' || c == '\t') { hasSpace = true; } if (!Character.isWhitespace(c)) { break; } } if (hasSpace) { String trim = text.trim(); int length = trim.length(); if (length > 0) { text = text + ' '; } if (lastHasSpace) { hasSpace = false; } if (hasSpace) { if (!withoutEmpty.isEmpty()) { String last = (String) withoutEmpty .get(withoutEmpty.size() - 1); if (length > 0) { last = last + ' '; } withoutEmpty.set(withoutEmpty.size() - 1, last); } } } if (text.length() > 0) { if (!hasNotEmpty) { if (!lastTextEndsWithSpace) { if (buf.getCurrentIndentLevel() != 0) { if (Character.isWhitespace(string .charAt(0))) { text = ' ' + text; } } } } withoutEmpty.add(text); hasNotEmpty = true; } lastHasSpace = hasSpace; } lastTextEndsWithSpace = lastHasSpace; splitOnLines = new String[withoutEmpty.size()]; withoutEmpty.toArray(splitOnLines); if (!hasNotEmpty) { continue; } boolean nextIndent = true; if (buf.getCurrentIndentLevel() == 0) { buf.printIndent(); nextIndent = false; } if (splitOnLines[0].length() > 0) { buf.print(splitOnLines[0]); if (splitOnLines.length > 1) { buf.println(); buf.printIndent(); nextIndent = false; } } for (int b = 1; b < splitOnLines.length; b++) { String text = splitOnLines[b]; if (text.length() == 0) { nextIndent = false; continue; } if (nextIndent || buf.getCurrentIndentLevel() == 0) { buf.printIndent(); } if (b == splitOnLines.length - 1) { boolean needContinue = false; needContinue = shouldInsertNewLine(lexemeList, codeoptions, a, needContinue); if (needContinue) { buf.print(text); continue; } } buf.println(text); nextIndent = true; } continue; } if (typeIndex == HTMLTokenTypes.PERCENT_TEXT) { appendPercentText(lexemeList, codeoptions, buf, a, lexeme); continue; } if (typeIndex == HTMLTokenTypes.GREATER_THAN) { buf.print('>'); boolean keepWithNext = false; if (!isInTag && a + 1 < lexemeList.size()) { Lexeme next = lexemeList.get(a + 1); if (next != null && isLexemeOfType(next, HTMLTokenTypes.TEXT) && (!next.getText().startsWith(" ") && !next.getText().startsWith("\t") //$NON-NLS-1$ //$NON-NLS-2$ && !next.getText().startsWith("\r") && !next.getText().startsWith("\n"))) //$NON-NLS-1$ //$NON-NLS-2$ { keepWithNext = true; } } if (isInTag && a + 1 < lexemeList.size()) { Lexeme next = lexemeList.get(a + 1); keepWithNext = switchingLanguages(lexeme, next); } boolean wrapContent = !codeoptions.notWrappingTags .contains(lastTagName); boolean alwaysWrapAfterwards = codeoptions.allwaysWrap .contains(realLastTagName); isSmartNoWrap = isSmartNoWrap(lexemeList, a, codeoptions); if (!isSmartNoWrap) { // if I am just finishing a start tag and I don't wrap // interior content, or I should print a // line if ((wrapContent || printLine) && !keepWithNext) { // if I am not self closing, or I must always wrap if (!isSelfClosing || alwaysWrapAfterwards) { buf.println(); } } else if (alwaysWrapAfterwards) { buf.println(); stillWrap.push(this); } } // No longer inside a start tag (if we were inside one // before) isInTag = false; continue; } if (typeIndex == HTMLTokenTypes.SLASH_GREATER_THAN) { checkPreviousTrail(notFormatted, lexemeList, buf, a, lexeme); buf.print('/'); buf.print('>'); if (codeoptions.allwaysWrap.contains(realLastTagName)) { if (stillWrap.size() > 0) { stillWrap.pop(); } stillWrap.push(this); buf.println(); } // No longer inside a start tag (if we were inside one // before) isInTag = false; continue; } if (typeIndex == HTMLTokenTypes.START_TAG) { isInTag = true; String tagName = lexeme.getText().substring(1).trim() .toLowerCase(); int iL = buf.getCurrentIndentLevel(); if (tagName.equals("pre")) { //$NON-NLS-1$ isInPre = true; prePositon = lexeme.getStartingOffset(); if (iL == 0) { buf.printIndent(); } continue; } realLastTagName = tagName; boolean selfClosing = isSelfClosing(a, lexemeList); isSelfClosing = selfClosing; boolean wrap = !codeoptions.notWrappingTags .contains(tagName) && !selfClosing && !createParseState.isEmptyTagType(tagName); wrap |= codeoptions.allwaysWrap.contains(tagName); if (wrap) { if (iL != 0) { buf.println(); iL = 0; } } if (iL == 0) { buf.printIndent(); } buf.print(lexeme.getText()); if (!selfClosing) { if (!createParseState.isEmptyTagType(tagName)) { if (!notClosed.contains(lexeme)) { if (!codeoptions.doNotIndent .contains(realLastTagName)) { buf.increaseIndent(); } tagNames.push(lastTagName); stillWrap.push(null); lastTagName = tagName; } } else { isSelfClosing = true; } } printLine = false; continue; } else if (typeIndex == HTMLTokenTypes.END_TAG) { String pTag = lastTagName; int iLevel = buf.getCurrentIndentLevel(); String tagName = lexeme.getText().substring(2).trim() .toLowerCase(); boolean isBroken = !(pTag.equals(tagName) || pTag.length() == 0); lastTagName = tagName; boolean contains = codeoptions.notWrappingTags .contains(tagName); contains &= (stillWrap.size() == 0 || stillWrap.peek() == null); if (iLevel != 0) { if (!contains || printLine) { if (!isSmartNoWrap) { buf.println(); iLevel = 0; } } } printLine = isSmartNoWrap; if (!createParseState.isEmptyTagType(tagName)) { // if // (!codeoptions.notWrappingTags.contains(lastTagName)){ if (!isBroken) { if (!codeoptions.doNotIndent.contains(pTag)) { buf.decreaseIndent(); } } // } } if (iLevel == 0) { buf.printIndent(); } buf.print(lexeme.getText()); if (!tagNames.isEmpty() && !isBroken) { lastTagName = (String) tagNames.pop(); if (!stillWrap.isEmpty()) { stillWrap.pop(); } boolean lastNotWrap = codeoptions.notWrappingTags .contains(lastTagName); if (lastNotWrap && !contains) { printLine = true; } else if (isSmartNoWrap && tagName.length() >= 0) { printLine = codeoptions.notWrappingTags .contains((String) tagNames.peek()); } } else { lastTagName = ""; //$NON-NLS-1$ } isSmartNoWrap = false; continue; } else if (typeIndex == HTMLTokenTypes.PERCENT_OPEN) { if (buf.getCurrentIndentLevel() == 0) { buf.printIndent(); } buf.print(lexeme.getText()); if (notFormatted.length() > lexeme.getEndingOffset()) { char charAt = notFormatted.charAt(lexeme .getEndingOffset()); if (Character.isWhitespace(charAt)) { buf.print(charAt); } } continue; } else if (typeIndex == HTMLTokenTypes.CDATA_START) { buf.print(lexeme.getText()); continue; } else if (typeIndex == HTMLTokenTypes.CDATA_END) { buf.print(lexeme.getText()); continue; } else if (typeIndex == HTMLTokenTypes.PERCENT_GREATER) { buf.print(' '); buf.print(lexeme.getText()); int pos = lexeme.getEndingOffset(); while (pos < notFormatted.length()) { char charAt = notFormatted.charAt(pos); if (!Character.isWhitespace(charAt)) { break; } if (charAt == '\r' || charAt == '\n') { buf.println(); break; } pos++; } continue; } else if (typeIndex == HTMLTokenTypes.QUOTE) { if (isInAttr) { isInAttr = false; } inQuote = !inQuote; if (inQuote) { quoteStart = lexeme.getEndingOffset(); } if (!inQuote) { buf.print(notFormatted.substring(quoteStart, lexeme .getStartingOffset())); } buf.print(lexeme.getText()); continue; } else if (i > 0) { char charAt = doc.charAt(i); if (Character.isWhitespace(charAt)) { // if (charAt != '\n' && charAt != '\r') // { buf.print(' '); // break; // } } } buf.print(lexeme.getText()); } if (isInPre) { String substring = doc.substring(prePositon, doc.length()); buf.print(substring); } if (inOtherLanguage) { String substring = doc.substring(startOtherLanguage, doc .length()); if (!isInAttr) { formatAnotherLanguage(isSelection, options, project, linedelimeters, buf, language, substring); } } if (!codeoptions.doFormatting) { if (htmlPosition != -1) { String substring = doc .substring(htmlPosition, doc.length()); buf.print(substring); } } String end = buf.toString(); if (!isFormattingCorrect(lexemeList, parser, notFormatted, end, new int[] { HTMLTokenTypes.TEXT }, new int[] { HTMLTokenTypes.TEXT })) { end = notFormatted; } return end; } catch (Exception e) { IdeLog .logError(HTMLPlugin.getDefault(), "Unable to format code", e); //$NON-NLS-1$ return notFormatted; } } private void checkPreviousTrail(String notFormatted, LexemeList lexemeList, SourceWriter buf, int a, Lexeme lexeme) { if (a > 0) { Lexeme prev = lexemeList.get(a - 1); if (prev.getToken().getTypeIndex() == HTMLTokenTypes.STRING) { if (!Character.isWhitespace(buf.getBuffer().charAt( buf.getBuffer().length() - 1))) { if (Character.isWhitespace(notFormatted .charAt(lexeme.offset - 1))) { buf.print(' '); } } } } } private boolean switchingLanguages(Lexeme lexeme, Lexeme next) { return next != null && !lexeme.getLanguage().equals(next.getLanguage()); } private void appendPercentText(LexemeList lexemeList, HTMLCodeFormatterOptions codeoptions, SourceWriter buf, int a, Lexeme lexeme) { ArrayList withoutEmpty = new ArrayList(); String[] splitOnLines = splitOnLines(lexeme.getText()); for (int b = 0; b < splitOnLines.length; b++) { String string = splitOnLines[b]; String text = string.trim(); if (text.length() > 0) { withoutEmpty.add(text); } } splitOnLines = new String[withoutEmpty.size()]; withoutEmpty.toArray(splitOnLines); if (buf.getCurrentIndentLevel() == 0) { buf.printIndent(); } if (splitOnLines[0].length() > 0) { buf.print(splitOnLines[0]); if (splitOnLines.length > 1) { buf.println(); buf.printIndent(); } } for (int b = 1; b < splitOnLines.length; b++) { String text = splitOnLines[b].trim(); if (text.length() == 0) { continue; } if (b == splitOnLines.length - 1) { boolean needContinue = false; needContinue = shouldInsertNewLine(lexemeList, codeoptions, a, needContinue); if (needContinue) { buf.print(text); continue; } } if (b != splitOnLines.length - 1) { buf.println(text); buf.printIndent(); } else { buf.print(text); } } } int level = 0; private void appendComment(LexemeList lexemeList, HTMLCodeFormatterOptions codeoptions, SourceWriter buf, int a, Lexeme lexeme) { String[] splitOnLines = splitOnLines(lexeme.getText()); for (int b = 0; b < splitOnLines.length; b++) { String text = splitOnLines[b].trim(); if (text.length() == 0) { continue; } int eif = countConditionalEnd(text); int iif = countConditionalStart(text); int delta = iif - eif; for (int i = delta; i < 0; i++) { if (level > 0) { buf.decreaseIndent(); } level--; } if (delta != 0 && buf.getCurrentIndentLevel() != 0 && b == 0) { buf.println(); } if (buf.getCurrentIndentLevel() == 0) { buf.printIndent(); } for (int i = 0; i < delta; i++) { level++; buf.increaseIndent(); } if (b == splitOnLines.length - 1) { boolean needContinue = false; needContinue = shouldInsertNewLine(lexemeList, codeoptions, a, needContinue); if (needContinue) { buf.print(text); continue; } } buf.println(text); } } private int countConditionalStart(String text) { int count = 0; int indexOf = text.indexOf("[if"); //$NON-NLS-1$ while (indexOf != -1) { boolean shouldInd = isTrueConditional(text, indexOf); if (shouldInd) { count++; } indexOf = text.indexOf("[if", indexOf + 3); //$NON-NLS-1$ } return count; } private boolean isTrueConditional(String text, int indexOf) { boolean shouldInd = false; for (int i = indexOf - 1; i > 0; i--) { char c = text.charAt(i); if (c == '!') { if (i > 0) { if (text.charAt(i - 1) != '<') { shouldInd = false; } else { shouldInd = true; } } break; } if (c == '<') { if (text.charAt(i + 1) == '[') { shouldInd = true; break; } } if (c != '-') { shouldInd = false; break; } } return shouldInd; } private int countConditionalEnd(String text) { int count = 0; int indexOf = text.indexOf("[endif]"); //$NON-NLS-1$ while (indexOf != -1) { boolean shouldInd = isTrueConditional(text, indexOf); if (shouldInd) { count++; } indexOf = text.indexOf("[endif]", indexOf + 3); //$NON-NLS-1$ } return count; } private boolean shouldInsertNewLine(LexemeList lexemeList, HTMLCodeFormatterOptions codeoptions, int a, boolean needContinue) { int ii = a + 1; while (ii < lexemeList.size()) { Lexeme lexeme2 = lexemeList.get(ii); if (lexeme2.getToken().getTypeIndex() == HTMLTokenTypes.START_TAG) { String tagName = lexeme2.getText().substring(1).trim(); if (codeoptions.notWrappingTags.contains(tagName.toLowerCase()) || isSelfClosing(a + 1, lexemeList)) { needContinue = true; break; } } if (lexeme2.getToken().getTypeIndex() == HTMLTokenTypes.END_TAG) { String tagName = lexeme2.getText().substring(2).trim(); if (codeoptions.notWrappingTags.contains(tagName.toLowerCase()) || isSelfClosing(a + 1, lexemeList)) { needContinue = true; break; } } ii++; } return needContinue; } private void formatAnotherLanguage(boolean isSelection, Map options, IProject project, String linedelimeters, SourceWriter buf, String language, String substring) { ICodeFormatter formatter = LanguageRegistry.getCodeFormatter(language); if (formatter != null) { String formatted = formatter.format(substring, isSelection, options, project, linedelimeters); formatted = normalizeCarriageReturns(formatted); String[] splitOnLines = formatted.split("\n", -1); //$NON-NLS-1$ if (splitOnLines.length == 1) { buf.print(splitOnLines[0]); } else { if (splitOnLines[0].length() > 0) { if (!Character.isWhitespace(splitOnLines[0].charAt(0))) { buf.println(); buf.printIndent(); } } buf.println(splitOnLines[0]); for (int b = 1; b < splitOnLines.length - 1; b++) { if (b == splitOnLines.length - 1 && splitOnLines[b].trim().length() == 0) { continue; } buf.printlnWithIndent(splitOnLines[b]); } if (splitOnLines.length >= 2 && !splitOnLines[splitOnLines.length - 1].equals("")) //$NON-NLS-1$ { buf .printlnWithIndent(splitOnLines[splitOnLines.length - 1]); } } } else { buf.print(substring); } } /** * @see com.aptana.ide.editors.unified.ICodeFormatter#handlesNested() */ public boolean handlesNested() { return true; } /** * @see com.aptana.ide.editors.unified.ICodeFormatter#createNestedMark() */ public String createNestedMark() { return "nested" + System.currentTimeMillis(); //$NON-NLS-1$ } }