package org.angularjs.html;
import com.intellij.lang.javascript.JSElementTypes;
import com.intellij.lexer.FlexAdapter;
import com.intellij.lexer.HtmlLexer;
import com.intellij.lexer.Lexer;
import com.intellij.lexer.MergingLexerAdapter;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.xml.XmlTokenType;
import org.angularjs.lang.parser.AngularJSElementTypes;
import org.jetbrains.annotations.NotNull;
/**
* @author Dennis.Ushakov
*/
public class Angular2HTMLLexer extends HtmlLexer {
private static final int SEEN_ANGULAR_SCRIPT = 0x1000;
private Lexer myInterpolationLexer;
private int interpolationStart = -1;
private boolean seenAngularScript;
public Angular2HTMLLexer() {
TokenHandler value = new TokenHandler() {
@Override
public void handleElement(Lexer lexer) {
if (!isHtmlTagState(lexer.getState())) {
final String text = lexer.getTokenText();
if (text.startsWith("(") || text.startsWith("[") || text.startsWith("*")) {
seenAttribute = true;
seenScript = true;
seenAngularScript = true;
} else {
seenAngularScript = false;
}
}
}
};
registerHandler(XmlTokenType.XML_NAME, value);
final TokenHandler scriptCleaner = new TokenHandler() {
@Override
public void handleElement(Lexer lexer) {
seenAngularScript = false;
}
};
registerHandler(XmlTokenType.XML_TAG_END, scriptCleaner);
registerHandler(XmlTokenType.XML_END_TAG_START, scriptCleaner);
registerHandler(XmlTokenType.XML_EMPTY_ELEMENT_END, scriptCleaner);
registerHandler(XmlTokenType.XML_ATTRIBUTE_VALUE_END_DELIMITER, scriptCleaner);
}
@Override
public void advance() {
if (myInterpolationLexer != null) {
myInterpolationLexer.advance();
try {
if (myInterpolationLexer.getTokenType() != null) {
return;
}
} catch (Error error) {
Logger.getInstance(Angular2HTMLLexer.class).error(myInterpolationLexer.getBufferSequence());
}
myInterpolationLexer = null;
interpolationStart = -1;
return;
}
super.advance();
final IElementType originalType = super.getTokenType();
if (originalType == XmlTokenType.XML_DATA_CHARACTERS || originalType == XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN) {
IElementType type = originalType;
interpolationStart = super.getTokenStart();
final StringBuilder text = new StringBuilder();
while (type == XmlTokenType.XML_DATA_CHARACTERS ||
type == XmlTokenType.XML_REAL_WHITE_SPACE ||
type == XmlTokenType.XML_WHITE_SPACE ||
type == XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN ||
type == XmlTokenType.XML_CHAR_ENTITY_REF ||
type == XmlTokenType.XML_ENTITY_REF_TOKEN) {
text.append(super.getTokenText());
super.advance();
type = getTokenType();
}
final Lexer lexer = createLexer(originalType);
lexer.start(text);
myInterpolationLexer = lexer;
}
}
@Override
public IElementType getTokenType() {
final IElementType type = super.getTokenType();
if (type == JSElementTypes.EMBEDDED_CONTENT && seenAngularScript) {
return AngularJSElementTypes.EMBEDDED_CONTENT;
}
if (myInterpolationLexer != null) {
return myInterpolationLexer.getTokenType();
}
return type;
}
@Override
public int getTokenStart() {
if (myInterpolationLexer != null) {
return interpolationStart + myInterpolationLexer.getTokenStart();
}
return super.getTokenStart();
}
@Override
public int getTokenEnd() {
if (myInterpolationLexer != null) {
return interpolationStart + myInterpolationLexer.getTokenEnd();
}
return super.getTokenEnd();
}
@Override
public void start(@NotNull CharSequence buffer, int startOffset, int endOffset, int initialState) {
myInterpolationLexer = null;
interpolationStart = -1;
seenAngularScript = (initialState & SEEN_ANGULAR_SCRIPT) != 0;
super.start(buffer, startOffset, endOffset, initialState);
}
@Override
public int getState() {
final int state = super.getState();
return state | ((seenAngularScript) ? SEEN_ANGULAR_SCRIPT : 0);
}
private static Lexer createLexer(IElementType type) {
final _AngularJSInterpolationsLexer lexer = new _AngularJSInterpolationsLexer(null);
lexer.setType(type);
return new MergingLexerAdapter(new FlexAdapter(lexer), TokenSet.create(AngularJSElementTypes.EMBEDDED_CONTENT,
XmlTokenType.XML_DATA_CHARACTERS,
XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN));
}
}