jsx & html support: Provide proper attr value for actions

fix "insert attribute" quick fix (it must respect the default quote type)
fix "=" type handler (it must use the attribute presentation for value)
add tests for "insert attribute" quick fix
This commit is contained in:
anstarovoyt
2017-08-03 14:19:12 +03:00
parent 6228f00d1a
commit 290d5f623f
7 changed files with 80 additions and 24 deletions
@@ -35,7 +35,6 @@ import com.intellij.psi.xml.XmlTag;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.xml.XmlNamespaceHelper;
import com.intellij.xml.util.HtmlUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -68,7 +67,7 @@ public class XmlAttributeInsertHandler implements InsertHandler<LookupElement> {
final PsiFile file = context.getFile();
final CharSequence chars = document.getCharsSequence();
final String quote = XmlEditUtil.getAttributeQuote(HtmlUtil.hasHtml(file) || HtmlUtil.supportsXmlTypedHandlers(file));
final String quote = XmlEditUtil.getAttributeQuote(file);
final boolean insertQuotes = WebEditorOptions.getInstance().isInsertQuotesForAttributeValue() && StringUtil.isNotEmpty(quote);
final boolean hasQuotes = CharArrayUtil.regionMatches(chars, caretOffset, "=\"") ||
CharArrayUtil.regionMatches(chars, caretOffset, "='");
@@ -17,6 +17,7 @@ package com.intellij.codeInsight.completion;
import com.intellij.application.options.editor.WebEditorOptions;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.daemon.impl.quickfix.EmptyExpression;
import com.intellij.codeInsight.editorActions.XmlEditUtil;
import com.intellij.codeInsight.lookup.Lookup;
import com.intellij.codeInsight.lookup.LookupElement;
@@ -238,7 +239,7 @@ public class XmlTagInsertHandler implements InsertHandler<LookupElement> {
if (!notRequiredAttributes.contains(attributeName)) {
if (!extension.isIndirectSyntax(attributeDecl)) {
template.addTextSegment(" " + attributeName + "=" + presenter.getPrefix());
template.addVariable(new MacroCallNode(new CompleteMacro()), true);
template.addVariable(presenter.showAutoPopup() ? new MacroCallNode(new CompleteMacro()) : new EmptyExpression(), true);
template.addTextSegment(presenter.getPostfix());
}
else {
@@ -16,6 +16,7 @@
package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.codeInsight.daemon.XmlErrorMessages;
import com.intellij.codeInsight.editorActions.XmlEditUtil;
import com.intellij.codeInsight.intention.HighPriorityAction;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
@@ -35,6 +36,7 @@ import com.intellij.psi.xml.XmlTag;
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.util.HtmlUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -96,11 +98,20 @@ public class InsertRequiredAttributeFix extends LocalQuickFixAndIntentionActionO
if (anchor == null) return;
final Template template = TemplateManager.getInstance(project).createTemplate("", "");
String valuePostfix = "\"";
if (indirectSyntax) {
if (anchorIsEmptyTag) template.addTextSegment(">");
template.addTextSegment("<jsp:attribute name=\"" + myAttrName + "\">");
} else {
template.addTextSegment(" " + myAttrName + (!insertShorthand ? "=\"" : ""));
}
else {
template.addTextSegment(" " + myAttrName);
if (!insertShorthand) {
String quote = XmlEditUtil.getAttributeQuote(file);
AttributeValuePresentation presentation = XmlExtension.getExtension(file).getAttributeValuePresentation(attrDescriptor, quote);
valuePostfix = presentation.getPostfix();
template.addTextSegment("=" + presentation.getPrefix());
}
}
Expression expression = new Expression() {
@@ -133,7 +144,7 @@ public class InsertRequiredAttributeFix extends LocalQuickFixAndIntentionActionO
template.addEndVariable();
if (anchorIsEmptyTag) template.addTextSegment("</" + myTag.getName() + ">");
} else if (!insertShorthand) {
template.addTextSegment("\"");
template.addTextSegment(valuePostfix);
}
final PsiElement anchor1 = anchor;
@@ -22,6 +22,9 @@ import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import org.jetbrains.annotations.NotNull;
import static com.intellij.xml.util.HtmlUtil.hasHtml;
import static com.intellij.xml.util.HtmlUtil.supportsXmlTypedHandlers;
public class XmlEditUtil {
/**
* Calculates quote style to use in a particular file depends on user's settings and injections
@@ -39,4 +42,8 @@ public class XmlEditUtil {
public static String getAttributeQuote(boolean html) {
return html ? CodeStyleSchemes.getInstance().getCurrentScheme().getCodeStyleSettings().HTML_QUOTE_STYLE.quote : "\"";
}
public static String getAttributeQuote(@NotNull PsiFile file) {
return getAttributeQuote(hasHtml(file) || supportsXmlTypedHandlers(file));
}
}
@@ -24,8 +24,11 @@ import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.xml.util.HtmlUtil;
import com.intellij.xml.XmlAttributeDescriptor;
import com.intellij.xml.XmlExtension;
import com.intellij.xml.XmlExtension.AttributeValuePresentation;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class XmlEqTypedHandler extends TypedHandlerDelegate {
private boolean needToInsertQuotes = false;
@@ -35,10 +38,9 @@ public class XmlEqTypedHandler extends TypedHandlerDelegate {
if (c == '=' && WebEditorOptions.getInstance().isInsertQuotesForAttributeValue()) {
if (XmlGtTypedHandler.fileContainsXmlLanguage(file)) {
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
PsiElement at = file.findElementAt(editor.getCaretModel().getOffset() - 1);
PsiElement atParent = at != null ? at.getParent() : null;
if(atParent instanceof XmlAttribute && ((XmlAttribute)atParent).getValueElement() == null) {
PsiElement atParent = getAttributeCandidate(editor, file, false);
if (atParent instanceof XmlAttribute && ((XmlAttribute)atParent).getValueElement() == null) {
needToInsertQuotes = ((XmlAttribute)atParent).getValueElement() == null;
}
}
@@ -47,26 +49,57 @@ public class XmlEqTypedHandler extends TypedHandlerDelegate {
return super.beforeCharTyped(c, project, editor, file, fileType);
}
@Nullable
private static PsiElement getAttributeCandidate(@NotNull Editor editor, @NotNull PsiFile file, boolean typed) {
int newOffset = editor.getCaretModel().getOffset() - (typed ? 2 : 1);
if (newOffset < 0) return null;
PsiElement at = file.findElementAt(newOffset);
return at != null ? at.getParent() : null;
}
@Override
public Result charTyped(char c, Project project, @NotNull Editor editor, @NotNull PsiFile file) {
if (needToInsertQuotes) {
int offset = editor.getCaretModel().getOffset();
PsiElement fileContext = file.getContext();
String toInsert = null;
if(fileContext != null) {
if (fileContext.getText().startsWith("\"")) toInsert = "''";
if (fileContext.getText().startsWith("\'")) toInsert = "\"\"";
}
String toInsert = tryCompleteQuotes(fileContext);
boolean showPopup = true;
if (toInsert == null) {
final String quote = XmlEditUtil.getAttributeQuote(HtmlUtil.hasHtml(file) || HtmlUtil.supportsXmlTypedHandlers(file));
toInsert = quote + quote;
final String quote = getDefaultQuote(file);
AttributeValuePresentation presentation = getValuePresentation(editor, file, quote);
toInsert = presentation.getPrefix() + presentation.getPostfix();
showPopup = presentation.showAutoPopup();
}
editor.getDocument().insertString(offset, toInsert);
editor.getCaretModel().moveToOffset(offset + toInsert.length() / 2);
AutoPopupController.getInstance(project).scheduleAutoPopup(editor);
if (showPopup) {
AutoPopupController.getInstance(project).scheduleAutoPopup(editor);
}
needToInsertQuotes = false;
}
needToInsertQuotes = false;
return super.charTyped(c, project, editor, file);
}
@Nullable
private static String tryCompleteQuotes(@Nullable PsiElement fileContext) {
if (fileContext != null) {
if (fileContext.getText().startsWith("\"")) return "''";
if (fileContext.getText().startsWith("\'")) return "\"\"";
}
return null;
}
@NotNull
private static String getDefaultQuote(@NotNull PsiFile file) {
return XmlEditUtil.getAttributeQuote(file);
}
@NotNull
private static AttributeValuePresentation getValuePresentation(@NotNull Editor editor, @NotNull PsiFile file, @NotNull String quote) {
PsiElement atParent = getAttributeCandidate(editor, file, true);
XmlAttributeDescriptor descriptor = atParent instanceof XmlAttribute ? ((XmlAttribute)atParent).getDescriptor() : null;
return XmlExtension.getExtension(file).getAttributeValuePresentation(descriptor, quote);
}
}
@@ -54,6 +54,10 @@ public abstract class XmlExtension {
@NotNull
String getPostfix();
default boolean showAutoPopup() {
return true;
}
}
private static XmlExtension calcExtension(PsiFile file) {
@@ -168,7 +172,8 @@ public abstract class XmlExtension {
return descriptor.isRequired();
}
public AttributeValuePresentation getAttributeValuePresentation(@NotNull XmlAttributeDescriptor descriptor,
@NotNull
public AttributeValuePresentation getAttributeValuePresentation(@Nullable XmlAttributeDescriptor descriptor,
@NotNull String defaultAttributeQuote) {
return new AttributeValuePresentation() {
@NotNull
@@ -612,11 +612,11 @@ public class HtmlUtil {
return tagName != null && "br".equalsIgnoreCase(tagName);
}
public static boolean hasHtml(PsiFile file) {
public static boolean hasHtml(@NotNull PsiFile file) {
return isHtmlFile(file) || file.getViewProvider() instanceof TemplateLanguageFileViewProvider;
}
public static boolean supportsXmlTypedHandlers(PsiFile file) {
public static boolean supportsXmlTypedHandlers(@NotNull PsiFile file) {
Language language = file.getLanguage();
while (language != null) {
if ("JavaScript".equals(language.getID())) return true;