support for |e and |c zen-coding filters, "|xsl" suffix should be filter but not generator

This commit is contained in:
Eugene Kudelevsky
2010-07-09 17:57:01 +04:00
parent 71c05d1c16
commit 862c05f100
8 changed files with 330 additions and 54 deletions
@@ -41,6 +41,7 @@
interface="com.intellij.javaee.ImplicitNamespaceDescriptorProvider"/>
<extensionPoint name="xml.zenCodingGenerator" interface="com.intellij.codeInsight.template.zencoding.filters.ZenCodingGenerator"/>
<extensionPoint name="xml.zenCodingFilter" interface="com.intellij.codeInsight.template.zencoding.filters.ZenCodingFilter"/>
</extensionPoints>
<extensions defaultExtensionNs="com.intellij">
@@ -21,6 +21,7 @@ import com.intellij.codeInsight.template.CustomLiveTemplate;
import com.intellij.codeInsight.template.CustomTemplateCallback;
import com.intellij.codeInsight.template.LiveTemplateBuilder;
import com.intellij.codeInsight.template.impl.TemplateImpl;
import com.intellij.codeInsight.template.zencoding.filters.ZenCodingFilter;
import com.intellij.codeInsight.template.zencoding.filters.ZenCodingGenerator;
import com.intellij.codeInsight.template.zencoding.nodes.*;
import com.intellij.codeInsight.template.zencoding.tokens.*;
@@ -38,6 +39,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
@@ -70,11 +72,11 @@ public abstract class ZenCodingTemplate implements CustomLiveTemplate {
@Nullable
private List<ZenCodingToken> lex(@NotNull String text, @NotNull CustomTemplateCallback callback) {
String filter = null;
String filterString = null;
int filterDelim = text.indexOf('|');
if (filterDelim >= 0 && filterDelim < text.length() - 1) {
filter = text.substring(filterDelim + 1);
filterString = text.substring(filterDelim + 1);
text = text.substring(0, filterDelim);
}
@@ -136,8 +138,11 @@ public abstract class ZenCodingTemplate implements CustomLiveTemplate {
// at least MarkerToken
assert result.size() > 0;
if (filter != null) {
result.add(result.size() - 1, new FilterToken(filter));
if (filterString != null) {
String[] filterSuffixes = filterString.split("\\|");
for (String filterSuffix : filterSuffixes) {
result.add(result.size() - 1, new FilterToken(filterSuffix));
}
}
return result;
}
@@ -216,6 +221,31 @@ public abstract class ZenCodingTemplate implements CustomLiveTemplate {
return defaultGenerator;
}
private static List<ZenCodingFilter> getFilters(ZenCodingNode node, PsiElement context) {
List<ZenCodingFilter> result = new ArrayList<ZenCodingFilter>();
while (node instanceof FilterNode) {
FilterNode filterNode = (FilterNode)node;
String filterSuffix = filterNode.getFilter();
for (ZenCodingFilter filter : ZenCodingFilter.getInstances()) {
if (filter.isMyContext(context) && filter.getSuffix().equals(filterSuffix)) {
result.add(filter);
}
}
node = filterNode.getNode();
}
for (ZenCodingFilter filter : ZenCodingFilter.getInstances()) {
if (filter.isMyContext(context) && filter.isAppliedByDefault(context)) {
result.add(filter);
}
}
Collections.reverse(result);
return result;
}
private void expand(String key,
@NotNull CustomTemplateCallback callback,
String surroundedText) {
@@ -231,14 +261,16 @@ public abstract class ZenCodingTemplate implements CustomLiveTemplate {
callback.deleteTemplateKey(key);
}
ZenCodingGenerator generator = findApplicableGenerator(node, callback.getContext());
PsiElement context = callback.getContext();
ZenCodingGenerator generator = findApplicableGenerator(node, context);
List<ZenCodingFilter> filters = getFilters(node, context);
List<GenerationNode> genNodes = node.expand(-1);
LiveTemplateBuilder builder = new LiveTemplateBuilder();
int end = -1;
for (int i = 0, genNodesSize = genNodes.size(); i < genNodesSize; i++) {
GenerationNode genNode = genNodes.get(i);
TemplateImpl template = genNode.generate(callback, surroundedText, generator);
TemplateImpl template = genNode.generate(callback, surroundedText, generator, filters);
int e = builder.insertTemplate(builder.length(), template, null);
if (end == -1 && end < builder.length()) {
end = e;
@@ -343,11 +375,13 @@ public abstract class ZenCodingTemplate implements CustomLiveTemplate {
return null;
}
ZenCodingToken filter = nextToken();
if (filter instanceof FilterToken) {
ZenCodingNode result = add;
while (filter instanceof FilterToken) {
result = new FilterNode(result, ((FilterToken)filter).getSuffix());
myIndex++;
return new FilterNode(add, ((FilterToken)filter).getSuffix());
filter = nextToken();
}
return add;
return result;
}
@Nullable
@@ -0,0 +1,72 @@
/*
* Copyright 2000-2010 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.zencoding.filters;
import com.intellij.codeInsight.template.zencoding.tokens.TemplateToken;
import com.intellij.codeInsight.template.zencoding.tokens.XmlTemplateToken;
import com.intellij.lang.xml.XMLLanguage;
import com.intellij.psi.PsiElement;
import com.intellij.psi.xml.XmlDocument;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
public class CommentZenCodingFilter extends ZenCodingFilter {
private static String buildCommentString(@Nullable String classAttr, @Nullable String idAttr) {
StringBuilder builder = new StringBuilder();
if (idAttr != null) {
builder.append('#').append(idAttr);
}
if (classAttr != null) {
builder.append('.').append(classAttr);
}
return builder.toString();
}
@NotNull
@Override
public String filterText(@NotNull String text, @NotNull TemplateToken token) {
if (token instanceof XmlTemplateToken) {
XmlDocument document = ((XmlTemplateToken)token).getFile().getDocument();
if (document != null) {
XmlTag tag = document.getRootTag();
if (tag != null) {
String classAttr = tag.getAttributeValue("class");
String idAttr = tag.getAttributeValue("id");
if (classAttr != null || idAttr != null) {
String commentString = buildCommentString(classAttr, idAttr);
return "<!-- " + commentString + " -->\n" + text + "\n<!-- /" + commentString + " -->";
}
}
}
}
return text;
}
@NotNull
@Override
public String getSuffix() {
return "c";
}
@Override
public boolean isMyContext(@NotNull PsiElement context) {
return context.getLanguage() instanceof XMLLanguage;
}
}
@@ -0,0 +1,46 @@
/*
* Copyright 2000-2010 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.zencoding.filters;
import com.intellij.codeInsight.template.zencoding.tokens.TemplateToken;
import com.intellij.lang.xml.XMLLanguage;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
/**
* @author Eugene.Kudelevsky
*/
public class EscapeZenCodingFilter extends ZenCodingFilter {
@NotNull
@Override
public String filterText(@NotNull String s, @NotNull TemplateToken token) {
s = s.replace("&", "&amp;");
s = s.replace("<", "&lt;");
s = s.replace(">", "&gt;");
return s;
}
@NotNull
@Override
public String getSuffix() {
return "e";
}
@Override
public boolean isMyContext(@NotNull PsiElement context) {
return context.getLanguage() instanceof XMLLanguage;
}
}
@@ -15,46 +15,57 @@
*/
package com.intellij.codeInsight.template.zencoding.filters;
import com.intellij.codeInsight.template.zencoding.nodes.GenerationNode;
import com.intellij.codeInsight.template.zencoding.tokens.TemplateToken;
import com.intellij.codeInsight.template.zencoding.tokens.XmlTemplateToken;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlDocument;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* @author Eugene.Kudelevsky
*/
public class XslZenCodingGenerator extends XmlZenCodingGenerator {
public class XslZenCodingFilter extends ZenCodingFilter {
private final XmlZenCodingGenerator myDelegate = new XmlZenCodingGeneratorImpl();
@NonNls private static final String SELECT_ATTR_NAME = "select";
@NotNull
@Override
public String toString(@NotNull final XmlTag tag,
@NotNull List<Pair<String, String>> attribute2Value,
final boolean hasChildren,
@NotNull PsiElement context) {
for (Pair<String, String> pair : attribute2Value) {
if (SELECT_ATTR_NAME.equals(pair.first)) {
return myDelegate.toString(tag, attribute2Value, hasChildren, context);
}
}
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
if (isOurTag(tag, hasChildren)) {
XmlAttribute attribute = tag.getAttribute(SELECT_ATTR_NAME);
if (attribute != null) {
attribute.delete();
public GenerationNode filterNode(@NotNull final GenerationNode node) {
TemplateToken token = node.getTemplateToken();
if (token instanceof XmlTemplateToken) {
XmlTemplateToken xmlToken = (XmlTemplateToken)token;
XmlDocument document = xmlToken.getFile().getDocument();
if (document != null) {
final XmlTag tag = document.getRootTag();
if (tag != null) {
for (Pair<String, String> pair : xmlToken.getAttribute2Value()) {
if (SELECT_ATTR_NAME.equals(pair.first)) {
return node;
}
}
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
if (isOurTag(tag, node.getChildren().size() > 0)) {
XmlAttribute attribute = tag.getAttribute(SELECT_ATTR_NAME);
if (attribute != null) {
attribute.delete();
}
}
}
});
return node;
}
}
});
return myDelegate.toString(tag, attribute2Value, hasChildren, context);
}
return node;
}
private static boolean isOurTag(XmlTag tag, boolean hasChildren) {
@@ -65,17 +76,12 @@ public class XslZenCodingGenerator extends XmlZenCodingGenerator {
return false;
}
@NotNull
@Override
public String buildAttributesString(@NotNull List<Pair<String, String>> attribute2value, boolean hasChildren, int numberInIteration) {
return myDelegate.buildAttributesString(attribute2value, hasChildren, numberInIteration);
}
@Override
public boolean isMyContext(@NotNull PsiElement context) {
return myDelegate.isMyContext(context);
}
@NotNull
@Override
public String getSuffix() {
return "xsl";
@@ -0,0 +1,66 @@
/*
* Copyright 2000-2010 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.zencoding.filters;
import com.intellij.codeInsight.template.zencoding.nodes.GenerationNode;
import com.intellij.codeInsight.template.zencoding.tokens.TemplateToken;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author Eugene.Kudelevsky
*/
public abstract class ZenCodingFilter {
public static final ExtensionPointName<ZenCodingFilter> EP_NAME =
new ExtensionPointName<ZenCodingFilter>("com.intellij.xml.zenCodingFilter");
private static final ZenCodingFilter[] ourStandartFilters = new ZenCodingFilter[]{
new XslZenCodingFilter(),
new CommentZenCodingFilter(),
new EscapeZenCodingFilter()
};
@NotNull
public String filterText(@NotNull String text, @NotNull TemplateToken token) {
return text;
}
@NotNull
public GenerationNode filterNode(@NotNull GenerationNode node) {
return node;
}
@NotNull
public abstract String getSuffix();
public abstract boolean isMyContext(@NotNull PsiElement context);
public boolean isAppliedByDefault(@NotNull PsiElement context) {
return false;
}
public static List<ZenCodingFilter> getInstances() {
List<ZenCodingFilter> generators = new ArrayList<ZenCodingFilter>();
Collections.addAll(generators, ourStandartFilters);
Collections.addAll(generators, EP_NAME.getExtensions());
return generators;
}
}
@@ -43,7 +43,7 @@ public abstract class ZenCodingGenerator {
public static List<ZenCodingGenerator> getInstances() {
List<ZenCodingGenerator> generators = new ArrayList<ZenCodingGenerator>();
Collections.addAll(generators, new XslZenCodingGenerator(), XmlZenCodingGeneratorImpl.INSTANCE);
Collections.addAll(generators, XmlZenCodingGeneratorImpl.INSTANCE);
Collections.addAll(generators, EP_NAME.getExtensions());
return generators;
}
@@ -22,6 +22,7 @@ import com.intellij.codeInsight.template.zencoding.ZenCodingTemplate;
import com.intellij.codeInsight.template.zencoding.ZenCodingUtil;
import com.intellij.codeInsight.template.zencoding.filters.XmlZenCodingGenerator;
import com.intellij.codeInsight.template.zencoding.filters.XmlZenCodingGeneratorImpl;
import com.intellij.codeInsight.template.zencoding.filters.ZenCodingFilter;
import com.intellij.codeInsight.template.zencoding.filters.ZenCodingGenerator;
import com.intellij.codeInsight.template.zencoding.tokens.TemplateToken;
import com.intellij.codeInsight.template.zencoding.tokens.XmlTemplateToken;
@@ -34,6 +35,8 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
import com.intellij.psi.XmlElementFactory;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.*;
import com.intellij.util.LocalTimeCounter;
@@ -103,17 +106,45 @@ public class GenerationNode {
@NotNull
public TemplateImpl generate(@NotNull CustomTemplateCallback callback,
@Nullable String surroundedText,
@Nullable ZenCodingGenerator generator) {
@Nullable ZenCodingGenerator generator,
@NotNull Collection<ZenCodingFilter> filters) {
GenerationNode generationNode = this;
for (ZenCodingFilter filter : filters) {
generationNode = filter.filterNode(generationNode);
}
if (generationNode != this) {
return generationNode.generate(callback, surroundedText, generator, Collections.<ZenCodingFilter>emptyList());
}
LiveTemplateBuilder builder = new LiveTemplateBuilder();
int end = -1;
boolean hasChildren = myChildren.size() > 0;
String txt = !hasChildren && myToInsertChildren ? surroundedText : null;
TemplateImpl parentTemplate = myTemplateToken instanceof XmlTemplateToken ?
invokeXmlTemplate((XmlTemplateToken)myTemplateToken, callback, myNumberInIteration, generator, txt,
hasChildren) :
invokeTemplate(myTemplateToken, hasChildren, callback, generator, txt);
TemplateImpl parentTemplate;
Map<String, String> predefinedValues;
if (myTemplateToken instanceof XmlTemplateToken) {
XmlTemplateToken xmlTemplateToken = (XmlTemplateToken)myTemplateToken;
List<Pair<String, String>> attr2value = new ArrayList<Pair<String, String>>(xmlTemplateToken.getAttribute2Value());
parentTemplate = invokeXmlTemplate(xmlTemplateToken, callback, myNumberInIteration, generator,
hasChildren, attr2value);
predefinedValues = buildPredefinedValues(attr2value, myNumberInIteration, (XmlZenCodingGenerator)generator, hasChildren);
}
else {
parentTemplate = invokeTemplate(myTemplateToken, hasChildren, callback, generator);
predefinedValues = null;
}
String s = parentTemplate.getString();
for (ZenCodingFilter filter : filters) {
s = filter.filterText(s, myTemplateToken);
}
parentTemplate = parentTemplate.copy();
parentTemplate.setString(s);
parentTemplate = expandTemplate(parentTemplate, predefinedValues, txt);
int offset = builder.insertTemplate(0, parentTemplate, null);
int newOffset = gotoChild(callback.getProject(), builder.getText(), offset, 0, builder.length());
@@ -125,21 +156,41 @@ public class GenerationNode {
end = offset;
}
LiveTemplateBuilder.Marker marker = offset < builder.length() ? builder.createMarker(offset) : null;
CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(callback.getProject());
String tabStr;
if (callback.isInInjectedFragment()) {
tabStr = "";
}
else if (settings.useTabCharacter(callback.getFileType())) {
tabStr = "\t";
}
else {
StringBuilder tab = new StringBuilder();
int tabSize = settings.getTabSize(callback.getFileType());
while (tabSize-- > 0) {
tab.append(' ');
}
tabStr = tab.toString();
}
for (int i = 0, myChildrenSize = myChildren.size(); i < myChildrenSize; i++) {
GenerationNode child = myChildren.get(i);
TemplateImpl childTemplate = child.generate(callback, surroundedText, generator);
TemplateImpl childTemplate = child.generate(callback, surroundedText, generator, filters);
boolean blockTag = child.isBlockTag();
if (blockTag && !isNewLineBefore(builder.getText(), offset)) {
builder.insertText(offset++, "\n");
builder.insertText(offset, "\n" + tabStr);
offset += tabStr.length() + 1;
}
int e = builder.insertTemplate(offset, childTemplate, null);
offset = marker != null ? marker.getEndOffset() : builder.length();
if (blockTag && !isNewLineAfter(builder.getText(), offset)) {
builder.insertText(offset++, "\n");
builder.insertText(offset, "\n" + tabStr);
offset += tabStr.length() + 1;
}
if (end == -1 && e < offset) {
@@ -155,8 +206,7 @@ public class GenerationNode {
private static TemplateImpl invokeTemplate(TemplateToken token,
boolean hasChildren,
final CustomTemplateCallback callback,
@Nullable ZenCodingGenerator generator,
String surroundedText) {
@Nullable ZenCodingGenerator generator) {
TemplateImpl template = token.getTemplate();
if (generator != null) {
assert template != null;
@@ -166,22 +216,21 @@ public class GenerationNode {
removeVariablesWhichHasNoSegment(template);
}
return expandTemplate(template, null, surroundedText);
return template;
}
private static TemplateImpl invokeXmlTemplate(XmlTemplateToken token,
CustomTemplateCallback callback,
final int numberInIteration,
@Nullable ZenCodingGenerator generator,
String surroundedText,
final boolean hasChildren) {
final boolean hasChildren,
final List<Pair<String, String>> attr2value) {
assert generator == null || generator instanceof XmlZenCodingGenerator :
"The generator cannot process XmlTemplateToken because it doesn't inherit XmlZenCodingGenerator";
TemplateImpl template = token.getTemplate();
assert template != null;
final List<Pair<String, String>> attr2value = new ArrayList<Pair<String, String>>(token.getAttribute2Value());
final XmlFile xmlFile = token.getFile();
XmlDocument document = xmlFile.getDocument();
if (document != null) {
@@ -199,9 +248,7 @@ public class GenerationNode {
template = template.copy();
template.setString(s);
removeVariablesWhichHasNoSegment(template);
Map<String, String> predefinedValues =
buildPredefinedValues(attr2value, numberInIteration, (XmlZenCodingGenerator)generator, hasChildren);
return expandTemplate(template, predefinedValues, surroundedText);
return template;
}
@NotNull
@@ -333,4 +380,8 @@ public class GenerationNode {
}
return i == text.length();
}
public TemplateToken getTemplateToken() {
return myTemplateToken;
}
}