blob: 5c23ba994e84a5fa4d450e10fb0d9950e27019e2 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.template.emmet.generators;
import com.intellij.codeInsight.template.CustomTemplateCallback;
import com.intellij.codeInsight.template.emmet.EmmetParser;
import com.intellij.codeInsight.template.emmet.XmlEmmetParser;
import com.intellij.codeInsight.template.emmet.ZenCodingTemplate;
import com.intellij.codeInsight.template.emmet.tokens.TemplateToken;
import com.intellij.codeInsight.template.emmet.tokens.ZenCodingToken;
import com.intellij.codeInsight.template.impl.TemplateImpl;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.options.UnnamedConfigurable;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiErrorElement;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.xml.XmlTokenType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author Eugene.Kudelevsky
*/
public abstract class ZenCodingGenerator {
private static final ExtensionPointName<ZenCodingGenerator> EP_NAME =
new ExtensionPointName<ZenCodingGenerator>("com.intellij.xml.zenCodingGenerator");
private static final TokenSet VALID_LEAF_TYPES = TokenSet.create(XmlTokenType.XML_DATA_CHARACTERS, XmlTokenType.XML_CHAR_ENTITY_REF);
public abstract TemplateImpl generateTemplate(@NotNull TemplateToken token, boolean hasChildren, @NotNull PsiElement context);
@Nullable
public TemplateImpl createTemplateByKey(@NotNull String key) {
return null;
}
public abstract boolean isMyContext(@NotNull PsiElement context, boolean wrapping);
@Nullable
public String getSuffix() {
return null;
}
public abstract boolean isAppliedByDefault(@NotNull PsiElement context);
public abstract boolean isEnabled();
public static List<ZenCodingGenerator> getInstances() {
List<ZenCodingGenerator> generators = new ArrayList<ZenCodingGenerator>();
Collections.addAll(generators, XmlZenCodingGeneratorImpl.INSTANCE);
Collections.addAll(generators, EP_NAME.getExtensions());
return generators;
}
@Nullable
public String computeTemplateKey(@NotNull CustomTemplateCallback callback) {
Editor editor = callback.getEditor();
final int currentOffset = editor.getCaretModel().getOffset();
final CharSequence documentText = editor.getDocument().getCharsSequence();
PsiElement element = callback.getContext();
int line = editor.getCaretModel().getLogicalPosition().line;
int lineStart = editor.getDocument().getLineStartOffset(line);
int elementStart = -1;
do {
PsiElement e = element;
while ((e instanceof LeafPsiElement && VALID_LEAF_TYPES.contains(((LeafPsiElement)e).getElementType())) ||
e instanceof PsiWhiteSpace || e instanceof PsiErrorElement) {
elementStart = e.getTextRange().getStartOffset();
e = e.getPrevSibling();
}
if (elementStart >= 0) {
int startOffset = Math.max(elementStart, lineStart);
String key = computeKey(startOffset, currentOffset, documentText);
if (key != null) {
while (key.length() > 0 && !ZenCodingTemplate.checkTemplateKey(key, callback, this)) {
key = key.substring(1);
}
if (key.length() > 0) {
return key;
}
}
}
element = element.getParent();
}
while (element != null && elementStart > lineStart);
return null;
}
@Nullable
protected static String computeKey(int startOffset, int currentOffset, CharSequence documentText) {
if (currentOffset < startOffset || startOffset > documentText.length() || currentOffset > documentText.length()) {
return null;
}
String s = documentText.subSequence(startOffset, currentOffset).toString();
int index = 0;
while (index < s.length() && Character.isWhitespace(s.charAt(index))) {
index++;
}
String key = s.substring(index);
int lastWhitespaceIndex = -1;
int lastQuoteIndex = -1;
int lastApostropheIndex = -1;
boolean inBrackets = false;
int bracesStack = 0;
for (int i = 0; i < key.length(); i++) {
char c = key.charAt(i);
if (lastQuoteIndex >= 0 || lastApostropheIndex >= 0) {
if (c == '"') {
lastQuoteIndex = -1;
}
else if (c == '\'') lastApostropheIndex = -1;
}
else if (Character.isWhitespace(c)) {
lastWhitespaceIndex = i;
}
else if (c == '"') {
lastQuoteIndex = i;
}
else if (c == '\'') {
lastApostropheIndex = i;
}
else if (c == '[') {
inBrackets = true;
}
else if (c == ']' && inBrackets) {
lastWhitespaceIndex = -1;
inBrackets = false;
}
else if (c == '{') {
bracesStack++;
}
else if (c == '}' && bracesStack > 0) {
bracesStack--;
if (bracesStack == 0) {
lastWhitespaceIndex = -1;
}
}
}
if (lastQuoteIndex >= 0 || lastApostropheIndex >= 0) {
int max = Math.max(lastQuoteIndex, lastApostropheIndex);
return max < key.length() - 1 ? key.substring(max) : null;
}
if (lastWhitespaceIndex >= 0 && lastWhitespaceIndex < key.length() - 1) {
return key.substring(lastWhitespaceIndex + 1);
}
return key;
}
@NotNull
public EmmetParser createParser(List<ZenCodingToken> tokens,
CustomTemplateCallback callback,
ZenCodingGenerator generator,
boolean surroundWithTemplate) {
return new XmlEmmetParser(tokens, callback, generator, surroundWithTemplate);
}
@Nullable
public UnnamedConfigurable createConfigurable() {
return null;
}
}