mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 22:51:17 +07:00
WEB-49060 Completion without typing < - code cleanup, make it work in an empty document and no middle match in char entities when auto-popup
GitOrigin-RevId: b55f56cd49052ecd1e656f209507a419397e7dc8
This commit is contained in:
committed by
intellij-monorepo-bot
parent
37e1a05dac
commit
eb09877c1f
@@ -11,7 +11,9 @@ import com.intellij.openapi.project.DumbAware;
|
|||||||
import com.intellij.openapi.project.DumbService;
|
import com.intellij.openapi.project.DumbService;
|
||||||
import com.intellij.openapi.util.text.StringUtil;
|
import com.intellij.openapi.util.text.StringUtil;
|
||||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||||
|
import com.intellij.patterns.ElementPattern;
|
||||||
import com.intellij.patterns.PlatformPatterns;
|
import com.intellij.patterns.PlatformPatterns;
|
||||||
|
import com.intellij.patterns.StandardPatterns;
|
||||||
import com.intellij.patterns.XmlPatterns;
|
import com.intellij.patterns.XmlPatterns;
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import com.intellij.psi.PsiFile;
|
import com.intellij.psi.PsiFile;
|
||||||
@@ -28,10 +30,7 @@ import com.intellij.util.containers.ContainerUtil;
|
|||||||
import com.intellij.xml.util.HtmlUtil;
|
import com.intellij.xml.util.HtmlUtil;
|
||||||
import com.intellij.xml.util.XmlUtil;
|
import com.intellij.xml.util.XmlUtil;
|
||||||
import com.intellij.xml.util.documentation.MimeTypeDictionary;
|
import com.intellij.xml.util.documentation.MimeTypeDictionary;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.*;
|
||||||
import org.jetbrains.annotations.NonNls;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
@@ -86,9 +85,7 @@ public class HtmlCompletionContributor extends CompletionContributor implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
extend(CompletionType.BASIC, psiElement(XmlTokenType.XML_DATA_CHARACTERS)
|
extend(CompletionType.BASIC, getHtmlElementInTextPattern(),
|
||||||
.withParent(psiElement(XmlText.class))
|
|
||||||
.inFile(PlatformPatterns.psiFile(HtmlFileImpl.class)),
|
|
||||||
new HtmlElementInTextCompletionProvider());
|
new HtmlElementInTextCompletionProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +193,7 @@ public class HtmlCompletionContributor extends CompletionContributor implements
|
|||||||
|
|
||||||
if ((elementType == XmlTokenType.XML_DATA_CHARACTERS
|
if ((elementType == XmlTokenType.XML_DATA_CHARACTERS
|
||||||
|| element.getNode().getElementType() == XmlTokenType.XML_WHITE_SPACE)
|
|| element.getNode().getElementType() == XmlTokenType.XML_WHITE_SPACE)
|
||||||
&& element.getParent() instanceof XmlText
|
&& (element.getParent() instanceof XmlText || element.getParent() instanceof XmlDocument)
|
||||||
) {
|
) {
|
||||||
return !element.getText().endsWith("<");
|
return !element.getText().endsWith("<");
|
||||||
}
|
}
|
||||||
@@ -207,9 +204,23 @@ public class HtmlCompletionContributor extends CompletionContributor implements
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompletionResultSet withoutLiveTemplatesWeigher(@NotNull CompletionResultSet result,
|
@ApiStatus.Internal
|
||||||
@NotNull CompletionParameters parameters) {
|
public static ElementPattern<PsiElement> getHtmlElementInTextPattern() {
|
||||||
return result.withRelevanceSorter(withoutLiveTemplatesWeigher(null, parameters, result.getPrefixMatcher()));
|
return psiElement(XmlTokenType.XML_DATA_CHARACTERS)
|
||||||
|
.withParent(StandardPatterns.or(psiElement(XmlText.class), psiElement(XmlDocument.class)))
|
||||||
|
.inFile(PlatformPatterns.psiFile(HtmlFileImpl.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public static CompletionResultSet patchResultSetForHtmlElementInTextCompletion(@NotNull CompletionResultSet result,
|
||||||
|
@NotNull CompletionParameters parameters) {
|
||||||
|
// We want live templates to be mixed with tags and other contributions
|
||||||
|
result = result.withRelevanceSorter(withoutLiveTemplatesWeigher(null, parameters, result.getPrefixMatcher()));
|
||||||
|
if (parameters.getInvocationCount() == 0) {
|
||||||
|
// We only want results which start with the prefix first char
|
||||||
|
result = result.withPrefixMatcher(new StartOnlyMatcher(result.getPrefixMatcher()));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CompletionSorter withoutLiveTemplatesWeigher(@Nullable CompletionSorter sorter,
|
private static CompletionSorter withoutLiveTemplatesWeigher(@Nullable CompletionSorter sorter,
|
||||||
@@ -235,13 +246,7 @@ public class HtmlCompletionContributor extends CompletionContributor implements
|
|||||||
offsets = offsets.copyWithReplacement(offset, offset, "<");
|
offsets = offsets.copyWithReplacement(offset, offset, "<");
|
||||||
PsiElement tag = doIfNotNull(offsets.getFile().findElementAt(offset + 1), PsiElement::getParent);
|
PsiElement tag = doIfNotNull(offsets.getFile().findElementAt(offset + 1), PsiElement::getParent);
|
||||||
if (tag instanceof XmlTag) {
|
if (tag instanceof XmlTag) {
|
||||||
// We want live templates to be mixed with tags and other contributions
|
CompletionResultSet patchedResultSet = patchResultSetForHtmlElementInTextCompletion(result, parameters);
|
||||||
result = withoutLiveTemplatesWeigher(result, parameters);
|
|
||||||
if (parameters.getInvocationCount() == 0) {
|
|
||||||
// We only want results which start with the prefix first char
|
|
||||||
result = result.withPrefixMatcher(new StartOnlyMatcher(result.getPrefixMatcher()));
|
|
||||||
}
|
|
||||||
CompletionResultSet patchedResultSet = result;
|
|
||||||
for (LookupElement variant : TagNameReferenceCompletionProvider.getTagNameVariants((XmlTag)tag, "")) {
|
for (LookupElement variant : TagNameReferenceCompletionProvider.getTagNameVariants((XmlTag)tag, "")) {
|
||||||
LookupElement decorated = new LookupElementDecorator<>(variant) {
|
LookupElement decorated = new LookupElementDecorator<>(variant) {
|
||||||
|
|
||||||
@@ -289,7 +294,7 @@ public class HtmlCompletionContributor extends CompletionContributor implements
|
|||||||
@Override
|
@Override
|
||||||
public void afterAppend(char c) {
|
public void afterAppend(char c) {
|
||||||
// Select first item when two chars are typed after '&'
|
// Select first item when two chars are typed after '&'
|
||||||
if (lookup.getCurrentItemOrEmpty() == null && hasTwoCharAfterAmp(lookup)) {
|
if (lookup.getCurrentItemOrEmpty() == null && hasTwoCharsAfterAmp(lookup)) {
|
||||||
lookup.setSelectedIndex(0);
|
lookup.setSelectedIndex(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -301,7 +306,7 @@ public class HtmlCompletionContributor extends CompletionContributor implements
|
|||||||
if (currentCompletion != null
|
if (currentCompletion != null
|
||||||
&& currentCompletion.isAutopopupCompletion()
|
&& currentCompletion.isAutopopupCompletion()
|
||||||
&& !lookup.isSelectionTouched()
|
&& !lookup.isSelectionTouched()
|
||||||
&& !hasTwoCharAfterAmp(lookup)) {
|
&& !hasTwoCharsAfterAmp(lookup)) {
|
||||||
// Deselect topmost item
|
// Deselect topmost item
|
||||||
lookup.getList().setSelectedValue(null, false);
|
lookup.getList().setSelectedValue(null, false);
|
||||||
ListSelectionModel selectionModel = lookup.getList().getSelectionModel();
|
ListSelectionModel selectionModel = lookup.getList().getSelectionModel();
|
||||||
@@ -313,7 +318,7 @@ public class HtmlCompletionContributor extends CompletionContributor implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasTwoCharAfterAmp(LookupImpl lookup) {
|
private static boolean hasTwoCharsAfterAmp(LookupImpl lookup) {
|
||||||
int start = Math.max(lookup.getLookupStart() - 1, 0);
|
int start = Math.max(lookup.getLookupStart() - 1, 0);
|
||||||
int end = lookup.getEditor().getCaretModel().getOffset();
|
int end = lookup.getEditor().getCaretModel().getOffset();
|
||||||
if (end - start < 3) return false;
|
if (end - start < 3) return false;
|
||||||
|
|||||||
@@ -100,8 +100,11 @@ public final class XmlCompletionContributor extends CompletionContributor {
|
|||||||
else if (prefix.contains("&")) {
|
else if (prefix.contains("&")) {
|
||||||
prefix = prefix.substring(prefix.indexOf("&") + 1);
|
prefix = prefix.substring(prefix.indexOf("&") + 1);
|
||||||
}
|
}
|
||||||
|
matcher = matcher.cloneWithPrefix(prefix);
|
||||||
addEntityRefCompletions(position, result.withPrefixMatcher(prefix));
|
if (parameters.getInvocationCount() == 0) {
|
||||||
|
matcher = new StartOnlyMatcher(matcher);
|
||||||
|
}
|
||||||
|
addEntityRefCompletions(position, result.withPrefixMatcher(matcher));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import com.intellij.openapi.fileTypes.FileType;
|
|||||||
import com.intellij.testFramework.fixtures.CompletionAutoPopupTestCase;
|
import com.intellij.testFramework.fixtures.CompletionAutoPopupTestCase;
|
||||||
import com.intellij.util.containers.ContainerUtil;
|
import com.intellij.util.containers.ContainerUtil;
|
||||||
|
|
||||||
public class HtmlAutopopupTest extends CompletionAutoPopupTestCase {
|
public class HtmlAutoPopupTest extends CompletionAutoPopupTestCase {
|
||||||
|
|
||||||
public void testAfterTagOpen() {
|
public void testAfterTagOpen() {
|
||||||
doTestPopup(HtmlFileType.INSTANCE, "<div><caret></div>", "<");
|
doTestPopup(HtmlFileType.INSTANCE, "<div><caret></div>", "<");
|
||||||
@@ -55,6 +55,15 @@ public class HtmlAutopopupTest extends CompletionAutoPopupTestCase {
|
|||||||
myFixture.checkResult("<div>☆</div>");
|
myFixture.checkResult("<div>☆</div>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testAfterAmpersandEmptyFile() {
|
||||||
|
doTestPopup(HtmlFileType.INSTANCE, "&<caret>", "s");
|
||||||
|
assertNull(getLookup().getCurrentItemOrEmpty());
|
||||||
|
myFixture.type("t");
|
||||||
|
type("a");
|
||||||
|
myFixture.type("\n");
|
||||||
|
myFixture.checkResult("☆");
|
||||||
|
}
|
||||||
|
|
||||||
public void testAfterAmpersandZeroChars() {
|
public void testAfterAmpersandZeroChars() {
|
||||||
doTestPopup(HtmlFileType.INSTANCE, "<div><caret></div>", "&");
|
doTestPopup(HtmlFileType.INSTANCE, "<div><caret></div>", "&");
|
||||||
myFixture.type("\n");
|
myFixture.type("\n");
|
||||||
@@ -73,6 +82,13 @@ public class HtmlAutopopupTest extends CompletionAutoPopupTestCase {
|
|||||||
myFixture.checkResult("<div>⌖</div>");
|
myFixture.checkResult("<div>⌖</div>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testAfterAmpersandContents() {
|
||||||
|
doTestPopup(HtmlFileType.INSTANCE, "<div><caret></div>", "&bb");
|
||||||
|
assertSameElements(myFixture.getLookupElementStrings(), "bbrk", "bbrktbrk");
|
||||||
|
myFixture.completeBasic();
|
||||||
|
assertSameElements(myFixture.getLookupElementStrings(), "bbrk", "bbrktbrk", "lbbrk", "rbbrk");
|
||||||
|
}
|
||||||
|
|
||||||
public void testAfterAmpersandWithPrefix() {
|
public void testAfterAmpersandWithPrefix() {
|
||||||
doTestPopup(HtmlFileType.INSTANCE, "<div><caret></div>", "the&n");
|
doTestPopup(HtmlFileType.INSTANCE, "<div><caret></div>", "the&n");
|
||||||
myFixture.type("bs\n");
|
myFixture.type("bs\n");
|
||||||
@@ -83,9 +99,7 @@ public class HtmlAutopopupTest extends CompletionAutoPopupTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testAmpersandInsideEmptyAttributeValue() {
|
public void testAmpersandInsideEmptyAttributeValue() {
|
||||||
doTestPopup(HtmlFileType.INSTANCE, "<div title='<caret>'></div>", "&");
|
doTestPopup(HtmlFileType.INSTANCE, "<div title='<caret>'></div>", "&tar");
|
||||||
myFixture.type("ta");
|
|
||||||
type("r");
|
|
||||||
myFixture.type("\n");
|
myFixture.type("\n");
|
||||||
myFixture.checkResult("<div title='⌖'></div>");
|
myFixture.checkResult("<div title='⌖'></div>");
|
||||||
}
|
}
|
||||||
@@ -109,9 +123,9 @@ public class HtmlAutopopupTest extends CompletionAutoPopupTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testAmpersandInsideAttributeValue() {
|
public void testAmpersandInsideAttributeValue() {
|
||||||
doTestPopup(HtmlFileType.INSTANCE, "<div title='v<caret>a'></div>", "&");
|
doTestPopup(HtmlFileType.INSTANCE, "<div title='v<caret>a'></div>", "&tar");
|
||||||
myFixture.type("tar\n");
|
myFixture.type("\n");
|
||||||
myFixture.checkResult("<div title='v★a'></div>");
|
myFixture.checkResult("<div title='v⌖a'></div>");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDoNotShowPopupAfterQuotedSymbolXhtml() {
|
public void testDoNotShowPopupAfterQuotedSymbolXhtml() {
|
||||||
@@ -141,6 +155,14 @@ public class HtmlAutopopupTest extends CompletionAutoPopupTestCase {
|
|||||||
myFixture.checkResult("<div>The p\n</div>");
|
myFixture.checkResult("<div>The p\n</div>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testTypingInHtmlTextEmptyFile() {
|
||||||
|
myFixture.configureByText(HtmlFileType.INSTANCE, "<caret>");
|
||||||
|
type("p");
|
||||||
|
assertNull(getLookup().getCurrentItemOrEmpty());
|
||||||
|
myFixture.type("\n");
|
||||||
|
myFixture.checkResult("p\n");
|
||||||
|
}
|
||||||
|
|
||||||
public void testStartsWithCharMatchingAutoPopup() {
|
public void testStartsWithCharMatchingAutoPopup() {
|
||||||
myFixture.configureByText(HtmlFileType.INSTANCE, "<html lang='en'><caret>");
|
myFixture.configureByText(HtmlFileType.INSTANCE, "<html lang='en'><caret>");
|
||||||
type("la");
|
type("la");
|
||||||
Reference in New Issue
Block a user