org.fife.ui.rsyntaxtextarea.folding.HtmlFoldParser Maven / Gradle / Ivy
The newest version!
/*
* 09/30/2012
*
* HtmlFoldParser.java - Fold parser for HTML 5 and PHP.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea.folding;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import javax.swing.text.BadLocationException;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Token;
/**
* Fold parser for HTML 5, PHP and JSP. For HTML, we currently don't fold
* everything possible, just the "big" stuff. For PHP, we only fold
* the "big" HTML stuff and PHP regions, not code blocks in the actual PHP.
* For JSP we only fold the "big" HTML stuff and JSP blocks, not anything in
* the actual Java code.
*
* @author Robert Futrell
* @version 1.0
*/
public class HtmlFoldParser implements FoldParser {
/**
* Constant denoting we're folding HTML.
*/
public static final int LANGUAGE_HTML = -1;
/**
* Constant denoting we're folding PHP.
*/
public static final int LANGUAGE_PHP = 0;
/**
* Constant denoting we're folding JSP.
*/
public static final int LANGUAGE_JSP = 1;
/**
* The language we're folding.
*/
private final int language;
/**
* The set of tags we allow to be folded. These are tags that must have
* explicit close tags in both HTML 4 and HTML 5.
*/
private static final Set FOLDABLE_TAGS;
private static final char[] MARKUP_CLOSING_TAG_START = "".toCharArray();
//private static final char[] MARKUP_SHORT_TAG_END = "/>".toCharArray();
private static final char[] MLC_START = "".toCharArray();
private static final char[] PHP_START = "".toCharArray(); // and ".toCharArray();
// Scriptlets, declarations, and expressions all start the same way.
private static final char[] JSP_START = "<%".toCharArray();
private static final char[] JSP_END = "%>".toCharArray();
private static final char[][] LANG_START = { PHP_START, JSP_START };
private static final char[][] LANG_END = { PHP_END, JSP_END };
private static final char[] JSP_COMMENT_START = "<%--".toCharArray();
private static final char[] JSP_COMMENT_END = "--%>".toCharArray();
static {
FOLDABLE_TAGS = new HashSet();
FOLDABLE_TAGS.add("body");
FOLDABLE_TAGS.add("canvas");
FOLDABLE_TAGS.add("div");
FOLDABLE_TAGS.add("form");
FOLDABLE_TAGS.add("head");
FOLDABLE_TAGS.add("html");
FOLDABLE_TAGS.add("ol");
FOLDABLE_TAGS.add("pre");
FOLDABLE_TAGS.add("script");
FOLDABLE_TAGS.add("span");
FOLDABLE_TAGS.add("style");
FOLDABLE_TAGS.add("table");
FOLDABLE_TAGS.add("tfoot");
FOLDABLE_TAGS.add("thead");
FOLDABLE_TAGS.add("tr");
FOLDABLE_TAGS.add("td");
FOLDABLE_TAGS.add("ul");
}
/**
* Constructor.
*
* @param language The language to fold, such as {@link #LANGUAGE_PHP}.
*/
public HtmlFoldParser(int language) {
if (languageLANGUAGE_JSP) {
throw new IllegalArgumentException("Invalid language: " + language);
}
this.language = language;
}
/**
* {@inheritDoc}
*/
public List getFolds(RSyntaxTextArea textArea) {
List folds = new ArrayList();
Stack tagNameStack = new Stack();
boolean inSublanguage = false;
Fold currentFold = null;
int lineCount = textArea.getLineCount();
boolean inMLC = false;
boolean inJSMLC = false;
TagCloseInfo tci = new TagCloseInfo();
try {
for (int line=0; line=0 && t.getType()==Token.SEPARATOR) {
// , or %>
else if (t.startsWith(LANG_END[language])) {
int phpEnd = t.getEndOffset() - 1;
currentFold.setEndOffset(phpEnd);
Fold parentFold = currentFold.getParent();
// Don't add fold markers for single-line blocks
if (currentFold.isOnSingleLine()) {
removeFold(currentFold, folds);
}
currentFold = parentFold;
inSublanguage = false;
t = t.getNextToken();
continue;
}
}
if (!inSublanguage) {
if (t.getType()==Token.COMMENT_MULTILINE) {
// Continuing an MLC from a previous line
if (inMLC) {
// Found the end of the MLC starting on a previous line...
if (t.endsWith(MLC_END)) {
int mlcEnd = t.getEndOffset() - 1;
currentFold.setEndOffset(mlcEnd);
Fold parentFold = currentFold.getParent();
// Don't add fold markers for single-line blocks
if (currentFold.isOnSingleLine()) {
removeFold(currentFold, folds);
}
currentFold = parentFold;
inMLC = false;
}
// Otherwise, this MLC is continuing on to yet
// another line.
}
// Continuing a JS MLC from a previous line
else if (inJSMLC) {
// Found the end of the MLC starting on a previous line...
if (t.endsWith(JSP_COMMENT_END)) {
int mlcEnd = t.getEndOffset() - 1;
currentFold.setEndOffset(mlcEnd);
Fold parentFold = currentFold.getParent();
// Don't add fold markers for single-line blocks
if (currentFold.isOnSingleLine()) {
removeFold(currentFold, folds);
}
currentFold = parentFold;
inJSMLC = false;
}
// Otherwise, this MLC is continuing on to yet
// another line.
}
// Starting a MLC that ends on a later line...
else if (t.startsWith(MLC_START) && !t.endsWith(MLC_END)) {
if (currentFold==null) {
currentFold = new Fold(FoldType.COMMENT, textArea, t.getOffset());
folds.add(currentFold);
}
else {
currentFold = currentFold.createChild(FoldType.COMMENT, t.getOffset());
}
inMLC = true;
}
// Starting a JSP comment that ends on a later line...
else if (language==LANGUAGE_JSP &&
t.startsWith(JSP_COMMENT_START) &&
!t.endsWith(JSP_COMMENT_END)) {
if (currentFold==null) {
currentFold = new Fold(FoldType.COMMENT, textArea, t.getOffset());
folds.add(currentFold);
}
else {
currentFold = currentFold.createChild(FoldType.COMMENT, t.getOffset());
}
inJSMLC = true;
}
}
// If we're starting a new tag...
else if (t.isSingleChar(Token.MARKUP_TAG_DELIMITER, '<')) {
Token tagStartToken = t;
Token tagNameToken = t.getNextToken();
if (isFoldableTag(tagNameToken)) {
getTagCloseInfo(tagNameToken, textArea, line, tci);
if (tci.line==-1) { // EOF reached before end of tag
return folds;
}
// We have found either ">" or "/>" with tci.
Token tagCloseToken = tci.closeToken;
if (tagCloseToken.isSingleChar(Token.MARKUP_TAG_DELIMITER, '>')) {
if (currentFold==null) {
currentFold = new Fold(FoldType.CODE, textArea, tagStartToken.getOffset());
folds.add(currentFold);
}
else {
currentFold = currentFold.createChild(FoldType.CODE, tagStartToken.getOffset());
}
tagNameStack.push(tagNameToken.getLexeme());
}
t = tagCloseToken; // Continue parsing after tag
}
}
// If we've found a closing tag (e.g. "