diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java b/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java
index 90a937834fde..3a73d9285f9a 100644
--- a/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java
+++ b/platform/lang-impl/src/com/intellij/codeInsight/template/CustomTemplateCallback.java
@@ -61,7 +61,7 @@ public class CustomTemplateCallback {
fixInitialState();
}
- @Nullable
+ @NotNull
public PsiElement getContext() {
return getContext(myFile, myStartOffset > 0 ? myStartOffset - 1 : myStartOffset);
}
@@ -231,7 +231,8 @@ public class CustomTemplateCallback {
myEditor.getDocument().deleteString(caretAt - key.length(), caretAt);
}
- public static PsiElement getContext(PsiFile file, int offset) {
+ @NotNull
+ public static PsiElement getContext(@NotNull PsiFile file, int offset) {
PsiElement element = null;
if (!InjectedLanguageManager.getInstance(file.getProject()).isInjectedFragment(file)) {
element = InjectedLanguageUtil.findInjectedElementNoCommit(file, offset);
diff --git a/platform/platform-resources/src/META-INF/XmlPlugin.xml b/platform/platform-resources/src/META-INF/XmlPlugin.xml
index 3d7f6bf339da..b93e4875390f 100644
--- a/platform/platform-resources/src/META-INF/XmlPlugin.xml
+++ b/platform/platform-resources/src/META-INF/XmlPlugin.xml
@@ -37,6 +37,8 @@
+
+
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingInterpreter.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingInterpreter.java
index 45822cbe0f6f..670018f5b511 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingInterpreter.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingInterpreter.java
@@ -17,22 +17,18 @@ package com.intellij.codeInsight.template.zencoding;
import com.intellij.codeInsight.template.CustomTemplateCallback;
import com.intellij.codeInsight.template.impl.TemplateImpl;
-import com.intellij.lang.ASTNode;
-import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.StdFileTypes;
-import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.psi.*;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
-import com.intellij.psi.xml.*;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.psi.xml.XmlToken;
+import com.intellij.psi.xml.XmlTokenType;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.HashSet;
import com.intellij.util.containers.IntArrayList;
-import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@@ -43,7 +39,6 @@ import java.util.*;
class XmlZenCodingInterpreter {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.template.zencoding.XmlZenCodingInterpreter");
private static final String ATTRS = "ATTRS";
- private static final String NUMBER_IN_ITERATION_PLACE_HOLDER = "$";
private final List myTokens;
@@ -269,8 +264,11 @@ class XmlZenCodingInterpreter {
}
@Nullable
- private static Map buildPredefinedValues(List> attribute2value, int numberInIteration) {
- String attributes = buildAttributesString(attribute2value, numberInIteration);
+ private static Map buildPredefinedValues(List> attribute2value,
+ int numberInIteration,
+ CustomTemplateCallback callback) {
+ String attributes = buildAttributesString(attribute2value, numberInIteration, callback);
+ assert attributes != null;
attributes = attributes.length() > 0 ? ' ' + attributes : null;
Map predefinedValues = null;
if (attributes != null) {
@@ -280,28 +278,19 @@ class XmlZenCodingInterpreter {
return predefinedValues;
}
- @NotNull
- private static String buildAttributesString(List> attribute2value, int numberInIteration) {
- StringBuilder result = new StringBuilder();
- for (Iterator> it = attribute2value.iterator(); it.hasNext();) {
- Pair pair = it.next();
- String name = pair.first;
- String value = getValue(pair, numberInIteration);
- result.append(getAttributeString(name, value));
- if (it.hasNext()) {
- result.append(' ');
+ @Nullable
+ private static String buildAttributesString(List> attribute2value,
+ int numberInIteration,
+ CustomTemplateCallback callback) {
+ PsiElement context = callback.getContext();
+ for (ZenCodingFilter filter : ZenCodingFilter.EP_NAME.getExtensions()) {
+ if (filter.isMyContext(context)) {
+ return filter.buildAttributesString(attribute2value, numberInIteration);
}
}
- return result.toString();
+ return new ZenCodingFilterImpl().buildAttributesString(attribute2value, numberInIteration);
}
- private static String getAttributeString(String name, String value) {
- return name + "=\"" + value + '"';
- }
-
- private static String getValue(Pair pair, int numberInIteration) {
- return pair.second.replace(NUMBER_IN_ITERATION_PLACE_HOLDER, Integer.toString(numberInIteration + 1));
- }
private static void invokeTemplate(TemplateToken token,
final CustomTemplateCallback callback,
@@ -315,13 +304,15 @@ class XmlZenCodingInterpreter {
for (Iterator> iterator = attr2value.iterator(); iterator.hasNext();) {
Pair pair = iterator.next();
if (tag.getAttribute(pair.first) != null) {
- tag.setAttribute(pair.first, getValue(pair, numberInIteration));
+ tag.setAttribute(pair.first, ZenCodingUtil.getValue(pair, numberInIteration));
iterator.remove();
}
}
- modifiedTemplate.setString(filter(tag, callback));
+ String s = filter(tag, callback);
+ assert s != null;
+ modifiedTemplate.setString(s);
removeVariablesWhichHasNoSegment(modifiedTemplate);
- Map predefinedValues = buildPredefinedValues(attr2value, numberInIteration);
+ Map predefinedValues = buildPredefinedValues(attr2value, numberInIteration, callback);
callback.expandTemplate(modifiedTemplate, predefinedValues);
return;
}
@@ -330,60 +321,20 @@ class XmlZenCodingInterpreter {
}
else {
// for CSS
- Map predefinedValues = buildPredefinedValues(attr2value, numberInIteration);
+ Map predefinedValues = buildPredefinedValues(attr2value, numberInIteration, callback);
callback.expandTemplate(token.getKey(), predefinedValues);
}
}
+ @Nullable
private static String filter(XmlTag tag, CustomTemplateCallback callback) {
- if (XmlZenCodingTemplate.isTrueXml(callback)) {
- closeUnclosingTags(tag);
- }
- return tag.getContainingFile().getText();
- }
-
- private static boolean isTagClosed(@NotNull XmlTag tag) {
- ASTNode node = tag.getNode();
- assert node != null;
- final ASTNode emptyTagEnd = XmlChildRole.EMPTY_TAG_END_FINDER.findChild(node);
- final ASTNode endTagEnd = XmlChildRole.CLOSING_TAG_START_FINDER.findChild(node);
- return emptyTagEnd != null || endTagEnd != null;
- }
-
- @SuppressWarnings({"ConstantConditions"})
- private static void closeUnclosingTags(@NotNull XmlTag root) {
- final List> tagToClose = new ArrayList>();
- Project project = root.getProject();
- final SmartPointerManager pointerManager = SmartPointerManager.getInstance(project);
- root.accept(new XmlRecursiveElementVisitor() {
- @Override
- public void visitXmlTag(final XmlTag tag) {
- if (!isTagClosed(tag)) {
- tagToClose.add(pointerManager.createLazyPointer(tag));
- }
- }
- });
- PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
- for (final SmartPsiElementPointer pointer : tagToClose) {
- final XmlTag tag = pointer.getElement();
- if (tag != null) {
- final ASTNode child = XmlChildRole.START_TAG_END_FINDER.findChild(tag.getNode());
- if (child != null) {
- final int offset = child.getTextRange().getStartOffset();
- VirtualFile file = tag.getContainingFile().getVirtualFile();
- if (file != null) {
- final Document document = FileDocumentManager.getInstance().getDocument(file);
- documentManager.doPostponedOperationsAndUnblockDocument(document);
- ApplicationManager.getApplication().runWriteAction(new Runnable() {
- public void run() {
- document.replaceString(offset, tag.getTextRange().getEndOffset(), "/>");
- }
- });
- }
- }
+ PsiElement context = callback.getContext();
+ for (ZenCodingFilter filter : ZenCodingFilter.EP_NAME.getExtensions()) {
+ if (filter.isMyContext(context)) {
+ return filter.toString(tag, context);
}
}
- documentManager.commitAllDocuments();
+ return new ZenCodingFilterImpl().toString(tag, context);
}
private static void fail() {
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingTemplate.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingTemplate.java
index 9d4613080d16..76747d8664c4 100644
--- a/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingTemplate.java
+++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/XmlZenCodingTemplate.java
@@ -160,7 +160,10 @@ public class XmlZenCodingTemplate extends ZenCodingTemplate {
}
public static boolean isTrueXml(CustomTemplateCallback callback) {
- FileType type = callback.getFileType();
+ return isTrueXml(callback.getFileType());
+ }
+
+ public static boolean isTrueXml(FileType type) {
return type == StdFileTypes.XHTML || type == StdFileTypes.JSPX || type == StdFileTypes.XML;
}
@@ -265,11 +268,23 @@ public class XmlZenCodingTemplate extends ZenCodingTemplate {
if (PsiTreeUtil.getParentOfType(element, XmlComment.class) != null) {
return false;
}
+ if (!findApplicableFilter(element)) {
+ return false;
+ }
return true;
}
return false;
}
+ private static boolean findApplicableFilter(@NotNull PsiElement context) {
+ for (ZenCodingFilter filter : ZenCodingFilter.EP_NAME.getExtensions()) {
+ if (filter.isMyContext(context)) {
+ return true;
+ }
+ }
+ return new ZenCodingFilterImpl().isMyContext(context);
+ }
+
public static boolean startZenCoding(Editor editor, PsiFile file, String abbreviation) {
int caretAt = editor.getCaretModel().getOffset();
XmlZenCodingTemplate template = CustomLiveTemplate.EP_NAME.findExtension(XmlZenCodingTemplate.class);
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/ZenCodingFilter.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/ZenCodingFilter.java
new file mode 100644
index 000000000000..e6af19654fd4
--- /dev/null
+++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/ZenCodingFilter.java
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.util.Pair;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.xml.XmlTag;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author Eugene.Kudelevsky
+ */
+public interface ZenCodingFilter {
+ ExtensionPointName EP_NAME = new ExtensionPointName("com.intellij.xml.zenCodingFilter");
+
+ @NotNull
+ String toString(@NotNull XmlTag tag, @NotNull PsiElement context);
+
+ @NotNull
+ String buildAttributesString(@NotNull List> attribute2value, int numberInIteration);
+
+ boolean isMyContext(@NotNull PsiElement context);
+}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/ZenCodingFilterImpl.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/ZenCodingFilterImpl.java
new file mode 100644
index 000000000000..6fa11d738bff
--- /dev/null
+++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/ZenCodingFilterImpl.java
@@ -0,0 +1,114 @@
+/*
+ * 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;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.xml.XmlChildRole;
+import com.intellij.psi.xml.XmlTag;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * @author Eugene.Kudelevsky
+ */
+public class ZenCodingFilterImpl implements ZenCodingFilter {
+ @NotNull
+ public String toString(@NotNull XmlTag tag, @NotNull PsiElement context) {
+ FileType fileType = context.getContainingFile().getFileType();
+ if (XmlZenCodingTemplate.isTrueXml(fileType)) {
+ closeUnclosingTags(tag);
+ }
+ return tag.getContainingFile().getText();
+ }
+
+ @NotNull
+ public String buildAttributesString(@NotNull List> attribute2value, int numberInIteration) {
+ StringBuilder result = new StringBuilder();
+ for (Iterator> it = attribute2value.iterator(); it.hasNext();) {
+ Pair pair = it.next();
+ String name = pair.first;
+ String value = ZenCodingUtil.getValue(pair, numberInIteration);
+ result.append(getAttributeString(name, value));
+ if (it.hasNext()) {
+ result.append(' ');
+ }
+ }
+ return result.toString();
+ }
+
+ public boolean isMyContext(@NotNull PsiElement context) {
+ return true;
+ }
+
+ private static String getAttributeString(String name, String value) {
+ return name + "=\"" + value + '"';
+ }
+
+ @SuppressWarnings({"ConstantConditions"})
+ private static void closeUnclosingTags(@NotNull XmlTag root) {
+ final List> tagToClose = new ArrayList>();
+ Project project = root.getProject();
+ final SmartPointerManager pointerManager = SmartPointerManager.getInstance(project);
+ root.accept(new XmlRecursiveElementVisitor() {
+ @Override
+ public void visitXmlTag(final XmlTag tag) {
+ if (!isTagClosed(tag)) {
+ tagToClose.add(pointerManager.createLazyPointer(tag));
+ }
+ }
+ });
+ PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
+ for (final SmartPsiElementPointer pointer : tagToClose) {
+ final XmlTag tag = pointer.getElement();
+ if (tag != null) {
+ final ASTNode child = XmlChildRole.START_TAG_END_FINDER.findChild(tag.getNode());
+ if (child != null) {
+ final int offset = child.getTextRange().getStartOffset();
+ VirtualFile file = tag.getContainingFile().getVirtualFile();
+ if (file != null) {
+ final Document document = FileDocumentManager.getInstance().getDocument(file);
+ documentManager.doPostponedOperationsAndUnblockDocument(document);
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ public void run() {
+ document.replaceString(offset, tag.getTextRange().getEndOffset(), "/>");
+ }
+ });
+ }
+ }
+ }
+ }
+ documentManager.commitAllDocuments();
+ }
+
+ private static boolean isTagClosed(@NotNull XmlTag tag) {
+ ASTNode node = tag.getNode();
+ assert node != null;
+ final ASTNode emptyTagEnd = XmlChildRole.EMPTY_TAG_END_FINDER.findChild(node);
+ final ASTNode endTagEnd = XmlChildRole.CLOSING_TAG_START_FINDER.findChild(node);
+ return emptyTagEnd != null || endTagEnd != null;
+ }
+}
diff --git a/xml/impl/src/com/intellij/codeInsight/template/zencoding/ZenCodingUtil.java b/xml/impl/src/com/intellij/codeInsight/template/zencoding/ZenCodingUtil.java
new file mode 100644
index 000000000000..c29d17638811
--- /dev/null
+++ b/xml/impl/src/com/intellij/codeInsight/template/zencoding/ZenCodingUtil.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+import com.intellij.openapi.util.Pair;
+
+/**
+ * @author Eugene.Kudelevsky
+ */
+public class ZenCodingUtil {
+ static final String NUMBER_IN_ITERATION_PLACE_HOLDER = "$";
+
+ public static String getValue(Pair pair, int numberInIteration) {
+ return pair.second.replace(NUMBER_IN_ITERATION_PLACE_HOLDER, Integer.toString(numberInIteration + 1));
+ }
+}