From d49291919b88a3cd23c7ea53da015d9b91f86e21 Mon Sep 17 00:00:00 2001 From: "Eugene.Kudelevsky" Date: Tue, 11 May 2010 03:34:55 +0400 Subject: [PATCH] refactoring, extract zen-coding filter EP --- .../template/CustomTemplateCallback.java | 5 +- .../src/META-INF/XmlPlugin.xml | 2 + .../zencoding/XmlZenCodingInterpreter.java | 111 +++++------------ .../zencoding/XmlZenCodingTemplate.java | 17 ++- .../template/zencoding/ZenCodingFilter.java | 39 ++++++ .../zencoding/ZenCodingFilterImpl.java | 114 ++++++++++++++++++ .../template/zencoding/ZenCodingUtil.java | 29 +++++ 7 files changed, 234 insertions(+), 83 deletions(-) create mode 100644 xml/impl/src/com/intellij/codeInsight/template/zencoding/ZenCodingFilter.java create mode 100644 xml/impl/src/com/intellij/codeInsight/template/zencoding/ZenCodingFilterImpl.java create mode 100644 xml/impl/src/com/intellij/codeInsight/template/zencoding/ZenCodingUtil.java 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)); + } +}