From ac163e123568a0766ade21159ea5122cf76f825f Mon Sep 17 00:00:00 2001 From: Piotr Tomiak Date: Thu, 19 Mar 2020 16:49:56 +0100 Subject: [PATCH] WEB-44210 Update behavior of tag completion in HTML, JSX, and XML GitOrigin-RevId: 721caf7344143b6a6254fb0b87d3a7e98d7ab939 --- .../completion/XmlTagInsertHandler.java | 22 +++++++++++++++---- .../src/com/intellij/xml/XmlExtension.java | 4 ++++ .../src/com/intellij/xml/util/HtmlUtil.java | 12 +++++----- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/xml/impl/src/com/intellij/codeInsight/completion/XmlTagInsertHandler.java b/xml/impl/src/com/intellij/codeInsight/completion/XmlTagInsertHandler.java index ca419be88026..af808dc93ba5 100644 --- a/xml/impl/src/com/intellij/codeInsight/completion/XmlTagInsertHandler.java +++ b/xml/impl/src/com/intellij/codeInsight/completion/XmlTagInsertHandler.java @@ -36,12 +36,17 @@ import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.formatter.xml.HtmlCodeStyleSettings; import com.intellij.psi.formatter.xml.XmlCodeStyleSettings; import com.intellij.psi.html.HtmlTag; +import com.intellij.psi.impl.source.html.dtd.HtmlAttributeDescriptorImpl; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlTag; import com.intellij.psi.xml.XmlTokenType; -import com.intellij.xml.*; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.xml.XmlAttributeDescriptor; +import com.intellij.xml.XmlElementDescriptor; +import com.intellij.xml.XmlExtension; import com.intellij.xml.XmlExtension.AttributeValuePresentation; +import com.intellij.xml.XmlTagRuleProvider; import com.intellij.xml.actions.GenerateXmlTagAction; import com.intellij.xml.impl.schema.XmlElementDescriptorImpl; import com.intellij.xml.util.HtmlUtil; @@ -274,10 +279,13 @@ public class XmlTagInsertHandler implements InsertHandler { } else if (completionChar == Lookup.AUTO_INSERT_SELECT_CHAR || completionChar == Lookup.NORMAL_SELECT_CHAR || completionChar == Lookup.REPLACE_SELECT_CHAR) { if (WebEditorOptions.getInstance().isAutomaticallyInsertClosingTag() && isHtmlCode && HtmlUtil.isSingleHtmlTag(tag, true)) { + if (hasOwnAttributes(descriptor, tag)) { + template.addEndVariable(); + } template.addTextSegment(HtmlUtil.isHtmlTag(tag) ? ">" : closeTag(tag)); } else { - if (needAlLeastOneAttribute(tag) && WebEditorOptions.getInstance().isAutomaticallyStartAttribute() && tag.getAttributes().length == 0 + if (needAtLeastOneAttribute(tag) && WebEditorOptions.getInstance().isAutomaticallyStartAttribute() && tag.getAttributes().length == 0 && template.getSegmentsCount() == 0) { completeAttribute(tag.getContainingFile(), template); return true; @@ -308,7 +316,7 @@ public class XmlTagInsertHandler implements InsertHandler { template.addTextSegment(XmlEditUtil.getAttributeQuote(file)); } - private static boolean needAlLeastOneAttribute(XmlTag tag) { + private static boolean needAtLeastOneAttribute(XmlTag tag) { for (XmlTagRuleProvider ruleProvider : XmlTagRuleProvider.EP_NAME.getExtensionList()) { for (XmlTagRuleProvider.Rule rule : ruleProvider.getTagRule(tag)) { if (rule.needAtLeastOneAttribute(tag)) { @@ -352,7 +360,8 @@ public class XmlTagInsertHandler implements InsertHandler { } private static void completeTagTail(Template template, XmlElementDescriptor descriptor, PsiFile file, XmlTag context, boolean firstLevel) { - boolean completeIt = !firstLevel || !canHaveAttributes(descriptor, context); + boolean completeIt = (!firstLevel || !canHaveAttributes(descriptor, context)) + && (file == null || XmlExtension.getExtension(file).shouldCompleteTag(context)); switch (descriptor.getContentType()) { case XmlElementDescriptor.CONTENT_TYPE_UNKNOWN: return; @@ -385,6 +394,11 @@ public class XmlTagInsertHandler implements InsertHandler { } } + private static boolean hasOwnAttributes(XmlElementDescriptor descriptor, XmlTag tag) { + return ContainerUtil.find(descriptor.getAttributesDescriptors(tag), + attr -> attr instanceof HtmlAttributeDescriptorImpl && HtmlUtil.isOwnHtmlAttribute(attr)) != null; + } + private static boolean canHaveAttributes(XmlElementDescriptor descriptor, XmlTag context) { XmlAttributeDescriptor[] attributes = descriptor.getAttributesDescriptors(context); int required = WebEditorOptions.getInstance().isAutomaticallyInsertRequiredAttributes() ? diff --git a/xml/xml-psi-impl/src/com/intellij/xml/XmlExtension.java b/xml/xml-psi-impl/src/com/intellij/xml/XmlExtension.java index 23e5b4bc683f..e546f8ddeb4b 100644 --- a/xml/xml-psi-impl/src/com/intellij/xml/XmlExtension.java +++ b/xml/xml-psi-impl/src/com/intellij/xml/XmlExtension.java @@ -170,6 +170,10 @@ public abstract class XmlExtension { public boolean shouldBeInserted(final XmlAttributeDescriptor descriptor) { return descriptor.isRequired(); } + + public boolean shouldCompleteTag(XmlTag context) { + return true; + } @NotNull public AttributeValuePresentation getAttributeValuePresentation(@Nullable XmlTag tag, diff --git a/xml/xml-psi-impl/src/com/intellij/xml/util/HtmlUtil.java b/xml/xml-psi-impl/src/com/intellij/xml/util/HtmlUtil.java index 6ebde141bf89..d55766074fa9 100644 --- a/xml/xml-psi-impl/src/com/intellij/xml/util/HtmlUtil.java +++ b/xml/xml-psi-impl/src/com/intellij/xml/util/HtmlUtil.java @@ -466,7 +466,9 @@ public class HtmlUtil { final PsiElement declaration = descriptor.getDeclaration(); final PsiFile file = declaration != null ? declaration.getContainingFile() : null; final String name = file != null ? file.getName() : null; - return "meta.rnc".equals(name); + return "meta.rnc".equals(name) || "web-forms.rnc".equals(name) + || "embed.rnc".equals(name) || "tables.rnc".equals(name) + || "media.rnc".equals(name); } private static class TerminateException extends RuntimeException { @@ -491,7 +493,7 @@ public class HtmlUtil { new HtmlBuilderDriver(content).build(new XmlBuilder() { @NonNls final Set inTag = new THashSet<>(); boolean metHttpEquiv; - boolean metHttml5Charset; + boolean metHtml5Charset; @Override public void doctype(@Nullable final CharSequence publicId, @@ -516,7 +518,7 @@ public class HtmlUtil { @Override public void endTag(final CharSequence localName, final String namespace, final int startoffset, final int endoffset) { @NonNls final String name = StringUtil.toLowerCase(localName.toString()); - if ("meta".equals(name) && (metHttpEquiv || metHttml5Charset) && contentAttributeValue != null) { + if ("meta".equals(name) && (metHttpEquiv || metHtml5Charset) && contentAttributeValue != null) { String charsetName; if (metHttpEquiv) { int start = contentAttributeValue.indexOf(CHARSET_PREFIX); @@ -537,7 +539,7 @@ public class HtmlUtil { } inTag.remove(name); metHttpEquiv = false; - metHttml5Charset = false; + metHtml5Charset = false; contentAttributeValue = null; } @@ -552,7 +554,7 @@ public class HtmlUtil { metHttpEquiv |= value.equals("content-type"); } else if (name.equals(CHARSET)) { - metHttml5Charset = true; + metHtml5Charset = true; contentAttributeValue = value; } if (name.equals("content")) {