Custom language - code completion assertion
Hi,
I'm developing the code complition for our custom language, and down below there is a copy of a light version of our CompletionContributor class.
The problem is that if I'm editing my file at the very end (the last character of the file) I get the following assertion:
startOffset outside the host file: 69; Cogito File
java.lang.AssertionError: startOffset outside the host file: 69; Cogito File
at com.intellij.codeInsight.completion.CompletionAssertions.assertHostInfo(CompletionAssertions.java:124)
at com.intellij.codeInsight.completion.CodeCompletionHandlerBase.toInjectedIfAny(CodeCompletionHandlerBase.java:486)
at com.intellij.codeInsight.completion.CodeCompletionHandlerBase.prepareCompletionParameters(CodeCompletionHandlerBase.java:333)
at com.intellij.codeInsight.completion.CodeCompletionHandlerBase.lambda$null$2(CodeCompletionHandlerBase.java:291)
at com.intellij.openapi.application.impl.ApplicationImpl.tryRunReadAction(ApplicationImpl.java:1134)
at com.intellij.codeInsight.completion.AsyncCompletion.tryReadOrCancel(CompletionThreading.java:181)
at com.intellij.codeInsight.completion.CodeCompletionHandlerBase.lambda$doComplete$3(CodeCompletionHandlerBase.java:290)
at com.intellij.codeInsight.completion.AsyncCompletion.lambda$null$0(CompletionThreading.java:108)
at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$runProcess$1(CoreProgressManager.java:157)
at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:580)
at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:525)
at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:85)
at com.intellij.openapi.progress.impl.CoreProgressManager.runProcess(CoreProgressManager.java:144)
at com.intellij.codeInsight.completion.AsyncCompletion.lambda$startThread$1(CompletionThreading.java:104)
at com.intellij.openapi.application.impl.ApplicationImpl$1.run(ApplicationImpl.java:305)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
while if I'm editing the file in any other position the code complition works because no assertion is fired.
Since the assertion is in IntelliJ code I need to know how to solve the problem, I'm probably missing something. From what I understand in this line of code
at com.intellij.codeInsight.completion.CompletionAssertions.assertHostInfo(CompletionAssertions.java:124)
it checks if I'm beyond the end of file. It looks like it doesn't understand that I'm "extending" the file because I'm adding characters.
I need to know if I'm doing something wrong or if it could be an intellij bug.
How can I avoid this assertion to fire?
Here is the CompletionContributor code:
package com.expertsystem.cogitosum.csplugin.completition;
import com.expertsystem.cogitosum.csplugin.core.CogitoModuleComponentImpl;
import com.expertsystem.cogitosum.csplugin.psi.*;
import com.expertsystem.cogitosum.csplugin.psi.impl.CogitoAncestorOptionsImpl;
import com.expertsystem.cogitosum.csplugin.settings.CogitoSettings;
import com.expertsystem.cogitosum.csplugin.utils.CSUtility;
import com.expertsystem.cogitosum.csplugin.utils.CogitoPsiTreeUtil;
import com.expertsystem.cogitosum.csplugin.sdk.SenseiFiles;
import com.expertsystem.cogitosum.csplugin.sdk.SenseiWrapper;
import com.expertsystem.cogitosum.sdk.jsensei.core.loaders.SynconsFinder;
import com.expertsystem.cogitosum.sdk.jsensei.core.loaders.SynconsLoader;
import com.expertsystem.cogitosum.sdk.jsensei.core.sensei.Sensei;
import com.expertsystem.cogitosum.sdk.jsensei.model.exceptions.SenseiException;
import com.expertsystem.cogitosum.sdk.jsensei.model.find.*;
import com.expertsystem.cogitosum.sdk.jsensei.model.rules.taxonomy.Taxonomy;
import com.expertsystem.cogitosum.sdk.jsensei.model.rules.taxonomy.TaxonomyDomain;
import com.expertsystem.cogitosum.sdk.jsensei.model.syncons.Syncon;
import com.expertsystem.cogitosum.sdk.jsensei.model.syncons.SynconExternalIdType;
import com.google.gson.JsonObject;
import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.jarRepository.services.artifactory.Endpoint;
import com.intellij.lang.parser.GeneratedParserUtilBase;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.patterns.ElementPattern;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.patterns.PsiElementPattern;
import com.intellij.patterns.StandardPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiErrorElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.impl.source.tree.CompositePsiElement;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ProcessingContext;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.xml.sax.SAXException;
import javax.xml.bind.Element;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import static com.intellij.patterns.PlatformPatterns.psiElement;
import static com.intellij.patterns.StandardPatterns.instanceOf;
import static jdk.nashorn.internal.objects.Global.print;
public class CogitoCompletionContributor extends CompletionContributor {
Collection<String> basiclist_suggestions = null;
PropertiesComponent projectPropertiesComponent;
PropertiesComponent globalPropertiesComponent;
/**
* Invoked before completion is started. Is used mainly for determining custom offsets in editor, and to change default dummy identifier.
*/
@Override
public void beforeCompletion(@NotNull CompletionInitializationContext context) {
super.beforeCompletion(context);
Project project = context.getProject();
VirtualFile currentFile = context.getFile().getVirtualFile();
CogitoModuleComponentImpl moduleComp = CSUtility.getCurrentCogitoModule(project, currentFile);
context.setDummyIdentifier("");
if(basiclist_suggestions == null){
basiclist_suggestions = new ArrayList<String>();
basiclist_suggestions.add("SCOPE");
basiclist_suggestions.add("DOMAIN");
basiclist_suggestions.add("KEYWORD");
basiclist_suggestions.add("SYNCON");
basiclist_suggestions.add("ANCESTOR");
basiclist_suggestions.add("LEMMA");
basiclist_suggestions.add("POSITION");
basiclist_suggestions.add("IDENTIFY");
basiclist_suggestions.add("TYPE");
basiclist_suggestions.add("AND");
basiclist_suggestions.add("OR");
}
}
/**
* The main contributor method that is supposed to provide completion variants to result, basing on completion parameters.
* The default implementation looks for {@link com.intellij.codeInsight.completion.CompletionProvider}s you could register by
* invoking {@link #extend(CompletionType, ElementPattern, CompletionProvider)} from you contributor constructor,
* matches the desired completion type and {@link ElementPattern} with actual ones, and, depending on it, invokes those
* completion providers.<p>
* <p>
* If you want to implement this functionality directly by overriding this method, the following is for you.
* Always check that parameters match your situation, and that completion type ({@link CompletionParameters#getCompletionType()}
* is of your favourite kind. This method is run inside a read action. If you do any long activity non-related to PSI in it, please
* ensure you call {@link com.intellij.openapi.progress.ProgressManager#checkCanceled()} often enough so that the completion process
* can be cancelled smoothly when the user begins to type in the editor.
*/
@Override
public void fillCompletionVariants(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet result) {
super.fillCompletionVariants(parameters, result);
}
private String dumpText(String text){
String ret = text;
if(text.length() > 10)
ret = text.substring(0, 10) + "...";
StringUtils su = new StringUtils();
ret = su.replace(ret, "\n", "_");
ret = su.replace(ret, "\r", "_");
ret = su.replace(ret, "\t", "_");
return ret;
}
public CogitoCompletionContributor() {
PsiElementPattern.Capture<PsiElement> suggeneric99 = psiElement().whitespace();
extend(CompletionType.BASIC, suggeneric99, new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters,
ProcessingContext context,
@NotNull CompletionResultSet result) {
if(!result.isStopped()){
if(parameters.getPosition() != null) {
PsiElement ws = parameters.getPosition();
PsiElement ident_value = ws.getPrevSibling();
if(ident_value != null) {
System.out.println("generic contributor VALUE99: " + dumpText(ident_value.getText()));
CompletionGeneric(ident_value.getText(), result);
result.stopHere();
}
}
else
System.out.println("generic contributor");
}
};
});
}
private void CompletionGeneric(String value, @NotNull CompletionResultSet result){
// negli altri casi suggerisco parole chiave del linguaggio
for (String sugg : suggestReservedKeys(value)) {
result.addElement(LookupElementBuilder.create(sugg).withInsertHandler(new CogitoReplaceInsertHandler(value)));
}
}
@NotNull
private Collection<String> suggestReservedKeys(String suggestionRadix) {
Collection<String> suggestions = new ArrayList<String>();
if(!suggestionRadix.isEmpty()){
StringUtils su = new StringUtils();
basiclist_suggestions.forEach((k) -> {
if(su.startsWith(k, suggestionRadix)) {
suggestions.add(k);
}
});
}
return suggestions;
}
private static class CogitoReplaceInsertHandler<T extends LookupElement> implements InsertHandler<T> {
String suggestionRadix = null;
public CogitoReplaceInsertHandler(String _suggestionRadix) {
this.suggestionRadix = _suggestionRadix;
}
@Override
public void handleInsert(@NotNull InsertionContext context, LookupElement item) {
Editor editor = context.getEditor();
Document document = editor.getDocument();
int offset = editor.getCaretModel().getOffset();
document.replaceString(context.getStartOffset() - suggestionRadix.length(), offset, item.getLookupString());
}
}
}
Please sign in to leave a comment.
You can avoid the assertion by setting the dummy identifier to some non-empty string. The exception will be fixed in 2018.2 (https://youtrack.jetbrains.com/issue/IDEA-188584).
Thanks Peter, this solved the problem.