diff --git a/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionContributor.java b/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionContributor.java
index a07f068710d6..18ea2205d2a9 100644
--- a/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionContributor.java
+++ b/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionContributor.java
@@ -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.
*
- * Q: What does the {@link CompletionParameters#getPosition()} return?
- * 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.
- *
* Q: How do I get automatic lookup element filtering by prefix?
* 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) {
diff --git a/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionParameters.java b/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionParameters.java
index 82630859cfb7..3078ee32efd3 100644
--- a/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionParameters.java
+++ b/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionParameters.java
@@ -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()}.
+ *
+ * "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()}).
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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
+ * 1 for explicitly invoked completion
* >1 for next completion invocations when one lookup is already active
*/
public int getInvocationCount() {
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionInitializationUtil.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionInitializationUtil.java
index 169b2460e04e..5c01664f68a4 100644
--- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionInitializationUtil.java
+++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionInitializationUtil.java
@@ -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);
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/WordCompletionContributor.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/WordCompletionContributor.java
index c29c1e1a1978..6ceaa27bf8aa 100644
--- a/platform/lang-impl/src/com/intellij/codeInsight/completion/WordCompletionContributor.java
+++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/WordCompletionContributor.java
@@ -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 excludes) {
final Set 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 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 getAllWords(final PsiElement context, final int offset) {
+ private static Set getAllWords(PsiElement context, int offset) {
final Set 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;