Why is it that a Java String ending with "." cannot enter the getVariants method in Reference?

Answered

As shown in the image below, if a string ends with "." or other special characters, it will not autocomplete. Normal English characters will autocomplete automatically.

my Reference

/**
* Returns the array of String, {@link PsiElement} and/or {@link LookupElement}
* instances representing all identifiers that are visible at the location of the reference. The contents
* of the returned array are used to build the lookup list for basic code completion. (The list
* of visible identifiers may not be filtered by the completion prefix string - the
* filtering is performed later by the IDE.)
* <p>
* This method is default since 2018.3.
*
* @return the array of available identifiers.
*/
@Override
public Object @NotNull [] getVariants() {
if (StringUtils.isNotBlank(rqlxKey) && rqlxKey.contains(".")) {
Module module = ModuleUtil.findModuleForPsiElement(myElement);
if (module == null) {
return super.getVariants();
}
String beforeRqlxKey = StringUtils.substringBeforeLast(rqlxKey, CompletionUtil.DUMMY_IDENTIFIER);
String prefixKey = StringUtils.substringBeforeLast(beforeRqlxKey, ".");
Optional<PsiPackage> aPackage = SnowJavaUtils.findPackage(module.getProject(), prefixKey);
List<PsiFile> rqlxFiles = RqlxUtils.findFilesByPath(prefixKey, module.getModuleScope());
ArrayList<SnowLookUpElement> appendElement = new ArrayList<>();
if (aPackage.isPresent()) {
PsiPackage psiPackage = aPackage.get();
PsiPackage[] subPackages = psiPackage.getSubPackages(module.getModuleScope());
PsiFile[] files = psiPackage.getFiles(module.getModuleScope());
for (PsiPackage subPackage : subPackages) {
appendElement.add(new SnowLookUpElement(subPackage.getName(), subPackage));
}
for (PsiFile file : files) {
if (RqlxUtils.isRqlxFile(file)) {
appendElement.add(new SnowLookUpElement(FilenameUtils.getBaseName(file.getName()), file));
}
}

}

if (CollectionUtils.isNotEmpty(rqlxFiles)) {
DomManager domManager = DomManager.getDomManager(module.getProject());
for (PsiFile file : rqlxFiles) {
XmlFile xmlFile = (XmlFile) file;
DomFileElement<Mapper> mapperDomFileElement = domManager.getFileElement(xmlFile, Mapper.class);
if (mapperDomFileElement != null) {
Mapper mapper = mapperDomFileElement.getRootElement();
List<Rql> rqls = mapper.getRqls();
for (Rql rql : rqls) {
XmlAttribute xmlAttribute = rql.getId().getXmlAttribute();
GenericAttributeValue<String> paramType = rql.getParamType();
String resultType = null;
if (rql instanceof Select) {
Select select = (Select) rql;
resultType = select.getResultType().getValue();
if (StringUtils.isNotBlank(resultType)) {
resultType = StringUtils.substringAfterLast(resultType, ".");
}
}
if (xmlAttribute != null) {
appendElement.add(
new SnowLookUpElement(xmlAttribute.getValue(), xmlAttribute, paramType.getValue(),
resultType));
}
}
}
}
}
String s = StringUtils.substringBeforeLast(myElement.getText(), CompletionUtil.DUMMY_IDENTIFIER);
String text = StringUtils.removeQuot(s);
ArrayList<LookupElement> result = new ArrayList<>();
if (text.contains(".")) {
String prefix = StringUtils.substringBeforeLast(text, ".");
for (SnowLookUpElement snowLookUpElement : appendElement) {
snowLookUpElement.setLookUpText(prefix + "." + snowLookUpElement.getLookupString());
result.add(snowLookUpElement);
}
} else {
result.addAll(appendElement);
}
return result.toArray();
}
return super.getVariants();
}

 

my plugin.xml

<completion.confidence language="JAVA"
implementationClass="com.ruimin.helper.java.contributor.SnowLiteralCompletionConfidence"
order="before javaSkipAutopopupInStrings"/>

my completion confidence

@NotNull
@Override
public ThreeState shouldSkipAutopopup(@NotNull PsiElement contextElement, @NotNull PsiFile psiFile, int offset) {
if (isInStringLiteral(contextElement)) {
PsiLiteralExpression expression;
if (contextElement instanceof PsiJavaToken) {
expression = (PsiLiteralExpression) contextElement.getParent();
} else if (contextElement instanceof PsiLiteralExpression) {
expression = (PsiLiteralExpression) contextElement;
} else if (contextElement instanceof PsiLiteral) {
expression = (PsiLiteralExpression) contextElement.getParent();
} else {
return ThreeState.YES;
}
PsiMethodCallExpression callExpression = RqlxUtils.getLatestMethodCallExpressionFromParent(expression);
if (callExpression != null) {
PsiElement referenceExpression = callExpression.getFirstChild();
if (RqlxUtils.isRqlxMethodName(referenceExpression.getText()) || RqlxUtils.isSpliceRqlxKey(
referenceExpression)) {
return ThreeState.NO;
}
}
return ThreeState.YES;
}
return ThreeState.UNSURE;
}

public static boolean isInStringLiteral(PsiElement element) {
ParserDefinition definition = LanguageParserDefinitions.INSTANCE.forLanguage(
PsiUtilCore.findLanguageFromElement(element));
return definition != null && (isStringLiteral(element, definition) || isStringLiteral(element.getParent(),
definition));
}

private static boolean isStringLiteral(PsiElement element, ParserDefinition definition) {
return PlatformPatterns.psiElement().withElementType(definition.getStringLiteralElements()).accepts(element);
}
0
11 comments

Hi,

It is hard to follow the implementation. I suggest debugging why it happens. Try to set a breakpoint in:
com.intellij.codeInsight.completion.CompletionData.completeReference(PsiReference, PsiElement, Set, TailType, ElementFilter, CompletionVariant)

If you are not able to find the reason, please simplify it and share the minimal reproducible example and a test project.

0

Karol Lewandowski

Thank you for your answer, I tracked down the cause of the problem using breakpoints according to your suggestion, but I don't know how to fix it. I've pasted the code below with a line comment

com.intellij.codeInsight.completion.CompletionPhase.CommittingDocuments#scheduleAsyncCompletion

ReadAction
.nonBlocking(() -> {
if (phase.isExpired()) return null;

// retrieve the injected file from scratch since our typing might have destroyed the old one completely
PsiFile topLevelFile = PsiDocumentManager.getInstance(project).getPsiFile(topLevelEditor.getDocument());
Editor completionEditor = InjectedLanguageUtil.getEditorForInjectedLanguageNoCommit(topLevelEditor, topLevelFile, offset);
PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(completionEditor.getDocument());
if (file == null ||
autopopup && shouldSkipAutoPopup(completionEditor, file) ||
condition != null && !condition.value(file)) {
return null;
}

loadContributorsOutsideEdt(completionEditor, file);

return completionEditor;
})
.withDocumentsCommitted(project)
.expireWith(phase)
.finishOnUiThread(ModalityState.current(), completionEditor -> {
// completionEditor is null when the string ends with ".",normal English characters completionEditor is not null
if (completionEditor != null && !phase.isExpired()) {
int time = prevIndicator == null ? 0 : prevIndicator.getInvocationCount();
CodeCompletionHandlerBase handler = CodeCompletionHandlerBase.createHandler(completionType, false, autopopup, false);
handler.invokeCompletion(project, completionEditor, time, false);
}
else if (phase == CompletionServiceImpl.getCompletionPhase()) {
CompletionServiceImpl.setCompletionPhase(NoCompletion);
}
})
.submit(ourExecutor);
0

Hi,

This is not the reason but the result of previous computations. I suggest focusing on:

if (file == null ||
autopopup && shouldSkipAutoPopup(completionEditor, file) ||
condition != null && !condition.value(file)) {
return null;
}

I guess this condition is true, and `completionEditor` in the place you comment is null because of that. Maybe one of `CompletionConfidence` extensions prevents the completion?

0

Hi,

I found the reason, the "." character is judged in the condition, and the Reference must extend JavaClassReference class

 

f (file == null ||
//this is false
autopopup && shouldSkipAutoPopup(completionEditor, file) ||
// this is true
condition != null && !condition.value(file)) {
return null;
}

 

private static void autoPopupMemberLookup(Project project, Editor editor) {
AutoPopupController.getInstance(project).autoPopupMemberLookup(editor, (file) -> {
int offset = editor.getCaretModel().getOffset();
PsiElement lastElement = file.findElementAt(offset - 1);
if (lastElement == null) {
return false;
} else {
PsiElement prevSibling = PsiTreeUtil.prevVisibleLeaf(lastElement);
if (prevSibling != null && !".".equals(prevSibling.getText())) {
PsiElement parent = prevSibling;

do {
do {
parent = parent.getParent();
} while(parent instanceof PsiJavaCodeReferenceElement);
} while(parent instanceof PsiTypeElement);

if (!(parent instanceof PsiParameterList) && !(parent instanceof PsiParameter)) {
if (!".".equals(lastElement.getText()) && !"#".equals(lastElement.getText())) {
//   return false here
return JavaClassReferenceCompletionContributor.findJavaClassReference(file, offset - 1) != null;
} else {
PsiElement element = file.findElementAt(offset);
return element == null || !"#".equals(lastElement.getText()) || PsiTreeUtil.getParentOfType(element, PsiDocComment.class) != null;
}
} else {
return false;
}
} else {
return false;
}
}
});
}
 
public static @Nullable JavaClassReference findJavaClassReference(PsiFile file, int offset) {
PsiReference reference = file.findReferenceAt(offset);
if (reference instanceof PsiMultiReference) {
PsiReference[] var3 = ((PsiMultiReference)reference).getReferences();
int var4 = var3.length;

for(int var5 = 0; var5 < var4; ++var5) {
PsiReference psiReference = var3[var5];
if (psiReference instanceof JavaClassReference) {
return (JavaClassReference)psiReference;
}
}
}
// return null here
return reference instanceof JavaClassReference ? (JavaClassReference)reference : null;
}
0

Sorry, forgot to attach the class name of condition

com.intellij.codeInsight.editorActions.JavaTypedHandler

0

Please share the minimal reproducible plugin and test projects. It's hard to diagnose what is possible in your case without having the full context and without debugging it.

I don't understand the big picture, and analyzing the problem with the information you provided via the forum is just too time-consuming.

0

Sorry for not providing easy to analyze information, I have now added test project on my github repo. https://github.com/SvenShi/IfsSnowHelper
The path of the test project is sample/
The source code path of the plugin is in src/
Here are some classes that can cause this problem
my Completion Confidence
com.ruimin.helper.java.contributor.SnowLiteralCompletionConfidence

https://github.com/SvenShi/IfsSnowHelper/blob/master/src/main/java/com/ruimin/helper/java/contributor/SnowLiteralCompletionConfidence.java
my Reference Contributor
com.ruimin.helper.java.contributor.JavaReferenceContributor

https://github.com/SvenShi/IfsSnowHelper/blob/master/src/main/java/com/ruimin/helper/java/contributor/JavaReferenceContributor.java
my ReferenceProvider
com.ruimin.helper.java.provider.JavaRqlxKeyReferenceProvider

https://github.com/SvenShi/IfsSnowHelper/blob/master/src/main/java/com/ruimin/helper/java/provider/JavaRqlxKeyReferenceProvider.java
my reference
com.ruimin.helper.java.reference.JavaRqlxKeyReference

https://github.com/SvenShi/IfsSnowHelper/blob/master/src/main/java/com/ruimin/helper/java/reference/JavaRqlxKeyReference.java

0

How to reproduce the error? I run the sample project, and it doesn't complete anything except Java packages.

Please describe step by step what to do and what is expected/actual result. Which elements do completions reference? And other details.

0

Hello,  thank you for your patience and for following up on my question.

Let me briefly introduce my sample project. In this project, there is a simple method call in the test method in TestService. The method call is dao.selectList("com.ruimin.sample.service.rqlx.test.selectTest",new HashMap<>()), where the first parameter references to an xml tag with the id "selectTest " in the test.rqlx file located under the "com.ruimin.sample.service.rqlx" package.

My expectation is that there should be a completion dialog that pops up after each character is entered, including when entering "." so that the completion dialog should continue to pop up to display all sub-packages. Please note that there is a plugin writing issue that causes the completion dialog to not get triggered when the parameter does not contain the "." symbol. Please disregard this issue.

Currently, I am facing a problem where the completion dialog of all sub-package names of the current package does not pop up when I enter "." after the package name.

 
 
0

Hi,

In the first screenshot, you showed completions with XML icons, and I cannot reproduce this case, so I assume I cannot reproduce the issue you struggle with. My behavior is exactly the opposite. If I have ., completions are provided:

I can’t spend so much time trying to reproduce the error if you are not providing me precise information on how to reproduce it step by step.

I suggest checking whether the PrefixMatcher doesn’t reject your completion variants. As I can see in com.intellij.codeInsight.completion.LegacyCompletionContributor#completeReference, CompletionResultsSet.prefixMatcher is CamelHumpMatcher with the prefix, e.g., com.ruimin.sample.service..

If this is the prefix matcher issue, I recommend moving the completion logic to CompletionContributor and adjusting the PrefixMatcher to one that passes your items. Example of completion provider:

@Override
protected void addCompletions(CompletionParameters parameters, ProcessingContext context, CompletionResultSet result) {
    ...
    CompletionResultSet adjustedResult = result.wothPrefixMatcher(PrefixMatcher.ALWAYS_TRUE);
    adjustedResult.addElement(...);
    ...
}

I’m sorry, but I can’t spend more time on it.

0

Thank you so much for your patience and helpfulness. I will attempt to troubleshoot the issue on my own.

0

Please sign in to leave a comment.