mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
avoid expensive PSI copying in completion when the dummy identifier is empty, use this to speed up word completion
This commit is contained in:
+2
-10
@@ -42,14 +42,6 @@ import java.util.List;
|
||||
* A more generic way is to override default {@link #fillCompletionVariants(CompletionParameters, CompletionResultSet)} implementation
|
||||
* and provide your own. It's easier to debug, but harder to write.<p>
|
||||
*
|
||||
* Q: What does the {@link CompletionParameters#getPosition()} return?<br>
|
||||
* A: When completion is invoked, the file being edited is first copied (the original file can be accessed from {@link com.intellij.psi.PsiFile#getOriginalFile()}
|
||||
* and {@link CompletionParameters#getOriginalFile()}. Then a special 'dummy identifier' string is inserted to the copied file at caret offset (removing the selection).
|
||||
* Most often this string is an identifier (see {@link CompletionInitializationContext#DUMMY_IDENTIFIER}).
|
||||
* This is usually done to guarantee that there'll always be some non-empty element there, which will be easy to describe via {@link ElementPattern}s.
|
||||
* Also a reference can suddenly appear in that position, which will certainly help invoking its {@link PsiReference#getVariants()}.
|
||||
* Dummy identifier string can be easily changed in {@link #beforeCompletion(CompletionInitializationContext)} method.<p>
|
||||
*
|
||||
* Q: How do I get automatic lookup element filtering by prefix?<br>
|
||||
* A: When you return variants from reference ({@link PsiReference#getVariants()}), the filtering will be done
|
||||
* automatically, with prefix taken as the reference text from its start ({@link PsiReference#getRangeInElement()}) to
|
||||
@@ -150,7 +142,7 @@ public abstract class CompletionContributor {
|
||||
* 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
|
||||
* ensure you call {@link ProgressManager#checkCanceled()} often enough so that the completion process
|
||||
* can be cancelled smoothly when the user begins to type in the editor.
|
||||
*/
|
||||
public void fillCompletionVariants(@NotNull final CompletionParameters parameters, @NotNull CompletionResultSet result) {
|
||||
@@ -201,7 +193,7 @@ public abstract class CompletionContributor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the completion is finished quickly, lookup hasn't been shown and gives possibility to autoinsert some item (typically - the only one).
|
||||
* Called when the completion is finished quickly, lookup hasn't been shown and gives possibility to auto-insert some item (typically - the only one).
|
||||
*/
|
||||
@Nullable
|
||||
public AutoCompletionDecision handleAutoCompletionPossibility(@NotNull AutoCompletionContext context) {
|
||||
|
||||
+25
-2
@@ -50,6 +50,23 @@ public final class CompletionParameters {
|
||||
return new CompletionParameters(myPosition, myOriginalFile, myCompletionType, myOffset, newCount, myEditor, myProcess);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the leaf PSI element in the "completion file" at offset {@link #getOffset()}.<p></p>
|
||||
*
|
||||
* "Completion file" is a PSI file used for completion purposes. Most often it's a non-physical copy of the file being edited
|
||||
* (the original file can be accessed from {@link PsiFile#getOriginalFile()} or {@link #getOriginalFile()}).<p></p>
|
||||
*
|
||||
* A special 'dummy identifier' string is inserted to the copied file at caret offset (removing the selection).
|
||||
* Most often this string is an identifier (see {@link CompletionInitializationContext#DUMMY_IDENTIFIER}).
|
||||
* It can be changed via {@link CompletionContributor#beforeCompletion(CompletionInitializationContext)} method.<p></p>
|
||||
*
|
||||
* Why? This way there'll always be some non-empty element there, which usually reduces the number of
|
||||
* possible cases to be considered inside a {@link CompletionContributor}.
|
||||
* Also, even if completion was invoked in the middle of a white space, a reference might appear there after dummy identifier is inserted,
|
||||
* and its {@link com.intellij.psi.PsiReference#getVariants()} can then be suggested.<p></p>
|
||||
*
|
||||
* If the dummy identifier is empty, then the file isn't copied and this method returns whatever is at caret in the original file.
|
||||
*/
|
||||
@NotNull
|
||||
public PsiElement getPosition() {
|
||||
return myPosition;
|
||||
@@ -60,6 +77,9 @@ public final class CompletionParameters {
|
||||
return myOriginalFile.findElementAt(myPosition.getTextRange().getStartOffset());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the file being edited, possibly injected, where code completion was invoked.
|
||||
*/
|
||||
@NotNull
|
||||
public PsiFile getOriginalFile() {
|
||||
return myOriginalFile;
|
||||
@@ -70,14 +90,17 @@ public final class CompletionParameters {
|
||||
return myCompletionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the offset (relative to the file) where code completion was invoked.
|
||||
*/
|
||||
public int getOffset() {
|
||||
return myOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* 0 for autopopup
|
||||
* 1 for explicitly invoked completion
|
||||
* 0 for autopopup<br>
|
||||
* 1 for explicitly invoked completion<br>
|
||||
* >1 for next completion invocations when one lookup is already active
|
||||
*/
|
||||
public int getInvocationCount() {
|
||||
|
||||
+7
-2
@@ -116,6 +116,9 @@ public class CompletionInitializationUtil {
|
||||
CompletionProcessEx indicator,
|
||||
OffsetsInFile topLevelOffsets) {
|
||||
CompletionAssertions.checkEditorValid(initContext.getEditor());
|
||||
if (initContext.getDummyIdentifier().isEmpty()) {
|
||||
return topLevelOffsets;
|
||||
}
|
||||
|
||||
Editor hostEditor = InjectedLanguageUtil.getTopLevelEditor(initContext.getEditor());
|
||||
OffsetMap hostMap = topLevelOffsets.getOffsets();
|
||||
@@ -141,7 +144,9 @@ public class CompletionInitializationUtil {
|
||||
OffsetsInFile translatedOffsets = hostCopyOffsets.toInjectedIfAny(hostStartOffset);
|
||||
if (translatedOffsets != hostCopyOffsets) {
|
||||
PsiFile injected = translatedOffsets.getFile();
|
||||
if (injected instanceof PsiFileImpl && InjectedLanguageManager.getInstance(originalFile.getProject()).isInjectedFragment(originalFile)) {
|
||||
if (originalFile != injected &&
|
||||
injected instanceof PsiFileImpl &&
|
||||
InjectedLanguageManager.getInstance(originalFile.getProject()).isInjectedFragment(originalFile)) {
|
||||
((PsiFileImpl)injected).setOriginalFile(originalFile);
|
||||
}
|
||||
DocumentWindow documentWindow = InjectedLanguageUtil.getDocumentWindow(injected);
|
||||
@@ -158,7 +163,7 @@ public class CompletionInitializationUtil {
|
||||
@NotNull
|
||||
private static PsiElement findCompletionPositionLeaf(OffsetsInFile offsets, int offset, PsiFile originalFile) {
|
||||
PsiElement insertedElement = offsets.getFile().findElementAt(offset);
|
||||
if (insertedElement == null && offsets.getFile().getTextLength() == 0) {
|
||||
if (insertedElement == null && offsets.getFile().getTextLength() == offset) {
|
||||
insertedElement = PsiTreeUtil.getDeepestLast(offsets.getFile());
|
||||
}
|
||||
CompletionAssertions.assertCompletionPositionPsiConsistent(offsets, offset, originalFile, insertedElement);
|
||||
|
||||
+22
-20
@@ -27,10 +27,8 @@ import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.patterns.ElementPattern;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.cache.impl.id.IdTableBuilding;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@@ -44,6 +42,17 @@ import static com.intellij.patterns.PlatformPatterns.psiElement;
|
||||
*/
|
||||
public class WordCompletionContributor extends CompletionContributor implements DumbAware {
|
||||
|
||||
@Override
|
||||
public void beforeCompletion(@NotNull CompletionInitializationContext context) {
|
||||
if (context.getCompletionType() == CompletionType.BASIC && isWordCompletionDefinitelyEnabled(context.getFile())) {
|
||||
context.setDummyIdentifier("");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isWordCompletionDefinitelyEnabled(@NotNull PsiFile file) {
|
||||
return DumbService.isDumb(file.getProject()) || file instanceof PsiPlainTextFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillCompletionVariants(@NotNull final CompletionParameters parameters, @NotNull final CompletionResultSet result) {
|
||||
if (parameters.getCompletionType() == CompletionType.BASIC && shouldPerformWordCompletion(parameters)) {
|
||||
@@ -54,7 +63,7 @@ public class WordCompletionContributor extends CompletionContributor implements
|
||||
public static void addWordCompletionVariants(CompletionResultSet result, final CompletionParameters parameters, Set<String> excludes) {
|
||||
final Set<String> realExcludes = new HashSet<>(excludes);
|
||||
for (String exclude : excludes) {
|
||||
String[] words = exclude.split("[ \\.-]");
|
||||
String[] words = exclude.split("[ .-]");
|
||||
if (words.length > 0 && StringUtil.isNotEmpty(words[0])) {
|
||||
realExcludes.add(words[0]);
|
||||
}
|
||||
@@ -120,19 +129,16 @@ public class WordCompletionContributor extends CompletionContributor implements
|
||||
}
|
||||
|
||||
private static boolean shouldPerformWordCompletion(CompletionParameters parameters) {
|
||||
final PsiElement insertedElement = parameters.getPosition();
|
||||
final boolean dumb = DumbService.getInstance(insertedElement.getProject()).isDumb();
|
||||
if (dumb) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (parameters.getInvocationCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PsiElement insertedElement = parameters.getPosition();
|
||||
PsiFile file = insertedElement.getContainingFile();
|
||||
if (isWordCompletionDefinitelyEnabled(file)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
final PsiFile file = insertedElement.getContainingFile();
|
||||
final CompletionData data = CompletionUtil.getCompletionDataByElement(insertedElement, file);
|
||||
if (data != null) {
|
||||
Set<CompletionVariant> toAdd = new HashSet<>();
|
||||
@@ -155,8 +161,7 @@ public class WordCompletionContributor extends CompletionContributor implements
|
||||
|
||||
ASTNode textContainer = element != null ? element.getNode() : null;
|
||||
while (textContainer != null) {
|
||||
final IElementType elementType = textContainer.getElementType();
|
||||
if (LanguageWordCompletion.INSTANCE.isEnabledIn(elementType) || elementType == PlainTextTokenTypes.PLAIN_TEXT) {
|
||||
if (LanguageWordCompletion.INSTANCE.isEnabledIn(textContainer.getElementType())) {
|
||||
return true;
|
||||
}
|
||||
textContainer = textContainer.getTreeParent();
|
||||
@@ -164,19 +169,16 @@ public class WordCompletionContributor extends CompletionContributor implements
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Set<String> getAllWords(final PsiElement context, final int offset) {
|
||||
private static Set<String> getAllWords(PsiElement context, int offset) {
|
||||
final Set<String> words = new LinkedHashSet<>();
|
||||
if (StringUtil.isEmpty(CompletionUtil.findJavaIdentifierPrefix(context, offset))) {
|
||||
return words;
|
||||
}
|
||||
|
||||
final CharSequence chars = context.getContainingFile().getViewProvider().getContents(); // ??
|
||||
IdTableBuilding.scanWords(new IdTableBuilding.ScanWordProcessor() {
|
||||
@Override
|
||||
public void run(final CharSequence chars, @Nullable char[] charsArray, final int start, final int end) {
|
||||
if (start > offset || offset > end) {
|
||||
words.add(chars.subSequence(start, end).toString());
|
||||
}
|
||||
IdTableBuilding.scanWords((chars1, charsArray, start, end) -> {
|
||||
if (start > offset || offset > end) {
|
||||
words.add(chars1.subSequence(start, end).toString());
|
||||
}
|
||||
}, chars, 0, chars.length());
|
||||
return words;
|
||||
|
||||
Reference in New Issue
Block a user