diff --git a/xml/dom-impl/src/com/intellij/xml/impl/dom/DomAttributeXmlDescriptor.java b/xml/dom-impl/src/com/intellij/xml/impl/dom/DomAttributeXmlDescriptor.java index eb79b6e56acc..8751f8ac8114 100644 --- a/xml/dom-impl/src/com/intellij/xml/impl/dom/DomAttributeXmlDescriptor.java +++ b/xml/dom-impl/src/com/intellij/xml/impl/dom/DomAttributeXmlDescriptor.java @@ -20,11 +20,11 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.xml.XmlElement; import com.intellij.psi.xml.XmlTag; +import com.intellij.util.xml.XmlName; import com.intellij.util.xml.impl.DomInvocationHandler; import com.intellij.util.xml.impl.DomManagerImpl; import com.intellij.util.xml.reflect.DomAttributeChildDescription; -import com.intellij.util.xml.XmlName; -import com.intellij.xml.XmlAttributeDescriptor; +import com.intellij.xml.NamespaceAwareXmlAttributeDescriptor; import com.intellij.xml.util.XmlUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; @@ -32,7 +32,7 @@ import org.jetbrains.annotations.Nullable; /** * @author mike */ -public class DomAttributeXmlDescriptor implements XmlAttributeDescriptor { +public class DomAttributeXmlDescriptor implements NamespaceAwareXmlAttributeDescriptor { private final DomAttributeChildDescription myDescription; private final Project myProject; @@ -114,6 +114,11 @@ public class DomAttributeXmlDescriptor implements XmlAttributeDescriptor { return myDescription.getXmlName().getLocalName(); } + @Nullable + public String getNamespace() { + return myDescription.getXmlName().getNamespaceKey(); + } + public void init(final PsiElement element) { throw new UnsupportedOperationException("Method init not implemented in " + getClass()); } diff --git a/xml/impl/src/com/intellij/codeInsight/completion/XmlAttributeInsertHandler.java b/xml/impl/src/com/intellij/codeInsight/completion/XmlAttributeInsertHandler.java index 476b5e5913ba..296c4160c187 100644 --- a/xml/impl/src/com/intellij/codeInsight/completion/XmlAttributeInsertHandler.java +++ b/xml/impl/src/com/intellij/codeInsight/completion/XmlAttributeInsertHandler.java @@ -21,10 +21,16 @@ import com.intellij.lang.html.HTMLLanguage; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ScrollType; +import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.psi.xml.XmlFile; import com.intellij.util.text.CharArrayUtil; +import com.intellij.xml.XmlNamespaceHelper; import com.intellij.xml.util.HtmlUtil; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; /** * @author peter @@ -32,6 +38,18 @@ import com.intellij.xml.util.HtmlUtil; public class XmlAttributeInsertHandler implements InsertHandler { public static final XmlAttributeInsertHandler INSTANCE = new XmlAttributeInsertHandler(); + private final String myNamespacePrefixToInsert; + private final String myNamespaceToInsert; + + public XmlAttributeInsertHandler() { + this(null, null); + } + + public XmlAttributeInsertHandler(@Nullable String namespaceToInsert, @Nullable String namespacePrefixToInsert) { + myNamespaceToInsert = namespaceToInsert; + myNamespacePrefixToInsert = namespacePrefixToInsert; + } + public void handleInsert(InsertionContext context, LookupElement item) { final Editor editor = context.getEditor(); @@ -68,5 +86,15 @@ public class XmlAttributeInsertHandler implements InsertHandler { editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); editor.getSelectionModel().removeSelection(); AutoPopupController.getInstance(editor.getProject()).scheduleAutoPopup(editor); + + if (myNamespaceToInsert != null && file instanceof XmlFile) { + final XmlNamespaceHelper helper = XmlNamespaceHelper.getHelper(context.getFile()); + + if (helper != null) { + PsiDocumentManager.getInstance(context.getProject()).commitDocument(document); + helper.insertNamespaceDeclaration((XmlFile)file, editor, Collections.singleton(myNamespaceToInsert), + myNamespacePrefixToInsert, null); + } + } } } diff --git a/xml/impl/src/com/intellij/codeInsight/completion/XmlAttributeReferenceCompletionProvider.java b/xml/impl/src/com/intellij/codeInsight/completion/XmlAttributeReferenceCompletionProvider.java index 4f102c185312..c195d412b026 100644 --- a/xml/impl/src/com/intellij/codeInsight/completion/XmlAttributeReferenceCompletionProvider.java +++ b/xml/impl/src/com/intellij/codeInsight/completion/XmlAttributeReferenceCompletionProvider.java @@ -18,13 +18,16 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; import com.intellij.psi.impl.source.xml.XmlAttributeImpl; import com.intellij.psi.impl.source.xml.XmlAttributeReference; import com.intellij.psi.meta.PsiPresentableMetaData; import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.intellij.util.ProcessingContext; +import com.intellij.xml.NamespaceAwareXmlAttributeDescriptor; import com.intellij.xml.XmlAttributeDescriptor; import com.intellij.xml.XmlElementDescriptor; import com.intellij.xml.XmlExtension; @@ -68,7 +71,8 @@ public class XmlAttributeReferenceCompletionProvider extends CompletionProvider< XmlAttribute attribute, @Nullable InsertHandler replacementInsertHandler) { final XmlTag tag = attribute.getParent(); - final XmlExtension extension = XmlExtension.getExtension(tag.getContainingFile()); + final PsiFile file = tag.getContainingFile(); + final XmlExtension extension = XmlExtension.getExtension(file); final String prefix = attribute.getName().contains(":") && ((XmlAttributeImpl) attribute).getRealLocalName().length() > 0 ? attribute.getNamespacePrefix() + ":" : null; @@ -80,6 +84,29 @@ public class XmlAttributeReferenceCompletionProvider extends CompletionProvider< for (XmlAttributeDescriptor descriptor : descriptors) { if (isValidVariant(attribute, descriptor, attributes, extension)) { String name = descriptor.getName(tag); + + InsertHandler insertHandler = XmlAttributeInsertHandler.INSTANCE; + + if (replacementInsertHandler != null) { + insertHandler = replacementInsertHandler; + } + else if (descriptor instanceof NamespaceAwareXmlAttributeDescriptor) { + final String namespace = ((NamespaceAwareXmlAttributeDescriptor)descriptor).getNamespace(); + + if (file instanceof XmlFile && + namespace != null && + namespace.length() > 0 && + !name.contains(":") && + tag.getPrefixByNamespace(namespace) == null) { + String suggestedPrefix = ExtendedTagInsertHandler.suggestPrefix((XmlFile)file, namespace); + + if (suggestedPrefix != null) { + suggestedPrefix = makePrefixUnique(suggestedPrefix, tag); + name = suggestedPrefix + ":" + name; + insertHandler = new XmlAttributeInsertHandler(namespace, suggestedPrefix); + } + } + } if (prefix == null || name.startsWith(prefix)) { if (prefix != null && name.length() > prefix.length()) { name = descriptor.getName(tag).substring(prefix.length()); @@ -94,7 +121,7 @@ public class XmlAttributeReferenceCompletionProvider extends CompletionProvider< } element = element .withCaseSensitivity(caseSensitive) - .withInsertHandler(replacementInsertHandler != null ? replacementInsertHandler : XmlAttributeInsertHandler.INSTANCE); + .withInsertHandler(insertHandler); result.addElement( descriptor.isRequired() ? PrioritizedLookupElement.withPriority(element.appendTailText("(required)", true), 100) : element); } @@ -102,6 +129,19 @@ public class XmlAttributeReferenceCompletionProvider extends CompletionProvider< } } + @NotNull + private static String makePrefixUnique(@NotNull String basePrefix, @NotNull XmlTag context) { + if (context.getNamespaceByPrefix(basePrefix).isEmpty()) { + return basePrefix; + } + int i = 1; + + while (!context.getNamespaceByPrefix(basePrefix + i).isEmpty()) { + i++; + } + return basePrefix + i; + } + private static boolean isValidVariant(XmlAttribute attribute, @NotNull XmlAttributeDescriptor descriptor, final XmlAttribute[] attributes, diff --git a/xml/xml-psi-impl/src/com/intellij/xml/NamespaceAwareXmlAttributeDescriptor.java b/xml/xml-psi-impl/src/com/intellij/xml/NamespaceAwareXmlAttributeDescriptor.java new file mode 100644 index 000000000000..d0e00517186d --- /dev/null +++ b/xml/xml-psi-impl/src/com/intellij/xml/NamespaceAwareXmlAttributeDescriptor.java @@ -0,0 +1,11 @@ +package com.intellij.xml; + +import org.jetbrains.annotations.Nullable; + +/** + * @author Eugene.Kudelevsky + */ +public interface NamespaceAwareXmlAttributeDescriptor extends XmlAttributeDescriptor { + @Nullable + String getNamespace(); +}