Converting completion code to Kotlin

This commit is contained in:
Valentin Kipyatkov
2014-07-30 14:46:51 +04:00
parent bc7b2307f6
commit 3f62a96028
22 changed files with 541 additions and 489 deletions
@@ -1,4 +1,24 @@
<root>
<item
name='com.intellij.codeInsight.completion.CompletionParameters com.intellij.codeInsight.completion.CompletionParameters delegateToClassName()'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.completion.CompletionParameters com.intellij.codeInsight.completion.CompletionParameters withInvocationCount(int)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.completion.CompletionParameters com.intellij.codeInsight.completion.CompletionParameters withType(com.intellij.codeInsight.completion.CompletionType)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.completion.CompletionProvider void addCompletionVariants(V, com.intellij.util.ProcessingContext, com.intellij.codeInsight.completion.CompletionResultSet) 1'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.completion.CompletionProvider void addCompletions(V, com.intellij.util.ProcessingContext, com.intellij.codeInsight.completion.CompletionResultSet) 1'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.codeInsight.completion.CompletionSorter com.intellij.codeInsight.completion.CompletionSorter weighAfter(java.lang.String, com.intellij.codeInsight.lookup.LookupElementWeigher...)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
@@ -2,4 +2,7 @@
<item name='com.intellij.openapi.diagnostic.Logger com.intellij.openapi.diagnostic.Logger getInstance(java.lang.Class)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.openapi.diagnostic.Logger com.intellij.openapi.diagnostic.Logger getInstance(java.lang.String)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
</root>
@@ -2,6 +2,9 @@
<item name='com.intellij.patterns.ObjectPattern Self and(com.intellij.patterns.ElementPattern)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.patterns.PlatformPatterns com.intellij.patterns.IElementTypePattern elementType()'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.patterns.PlatformPatterns com.intellij.patterns.PsiElementPattern.Capture&lt;T&gt; psiElement(java.lang.Class&lt;T&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
@@ -14,7 +17,100 @@
name='com.intellij.patterns.PlatformPatterns com.intellij.patterns.PsiElementPattern.Capture&lt;com.intellij.psi.PsiElement&gt; psiElement(com.intellij.psi.tree.IElementType)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.patterns.PsiElementPattern Self afterLeaf(java.lang.String...)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.patterns.PsiElementPattern Self afterLeafSkipping(com.intellij.patterns.ElementPattern, com.intellij.patterns.ElementPattern)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.patterns.PsiElementPattern Self withElementType(com.intellij.patterns.ElementPattern&lt;com.intellij.psi.tree.IElementType&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.patterns.PsiElementPattern Self withElementType(com.intellij.psi.tree.IElementType)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.patterns.PsiElementPattern Self withName(com.intellij.patterns.ElementPattern&lt;java.lang.String&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.patterns.PsiElementPattern Self withName(java.lang.String)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.patterns.PsiElementPattern Self withName(java.lang.String...)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.patterns.PsiElementPattern Self withText(java.lang.String)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.patterns.PsiElementPattern Self withoutText(java.lang.String)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.patterns.PsiJavaPatterns com.intellij.patterns.IElementTypePattern elementType()'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.patterns.PsiJavaPatterns com.intellij.patterns.PsiJavaElementPattern.Capture&lt;com.intellij.psi.PsiElement&gt; psiElement()'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.patterns.TreeElementPattern Self afterSibling(com.intellij.patterns.ElementPattern&lt;? extends ParentType&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.patterns.TreeElementPattern Self afterSiblingSkipping(com.intellij.patterns.ElementPattern, com.intellij.patterns.ElementPattern&lt;? extends ParentType&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.patterns.TreeElementPattern Self inside(boolean, com.intellij.patterns.ElementPattern&lt;? extends ParentType&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.patterns.TreeElementPattern Self inside(boolean, com.intellij.patterns.ElementPattern&lt;? extends ParentType&gt;, com.intellij.patterns.ElementPattern&lt;? extends ParentType&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.patterns.TreeElementPattern Self inside(com.intellij.patterns.ElementPattern&lt;? extends ParentType&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.patterns.TreeElementPattern Self inside(java.lang.Class&lt;? extends ParentType&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.patterns.TreeElementPattern Self insideSequence(boolean, com.intellij.patterns.ElementPattern&lt;? extends ParentType&gt;...)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.patterns.TreeElementPattern Self isFirstAcceptedChild(com.intellij.patterns.ElementPattern&lt;? super ParentType&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.patterns.TreeElementPattern Self withChild(com.intellij.patterns.ElementPattern&lt;? extends ParentType&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.patterns.TreeElementPattern Self withChildren(com.intellij.patterns.ElementPattern&lt;java.util.Collection&lt;ParentType&gt;&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.patterns.TreeElementPattern Self withFirstChild(com.intellij.patterns.ElementPattern&lt;? extends ParentType&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.patterns.TreeElementPattern Self withLastChild(com.intellij.patterns.ElementPattern&lt;? extends ParentType&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.patterns.TreeElementPattern Self withParent(com.intellij.patterns.ElementPattern&lt;? extends ParentType&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.patterns.TreeElementPattern Self withParent(java.lang.Class&lt;? extends ParentType&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.patterns.TreeElementPattern Self withParents(java.lang.Class&lt;? extends ParentType&gt;...)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.patterns.TreeElementPattern Self withSuperParent(int, com.intellij.patterns.ElementPattern&lt;? extends ParentType&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='com.intellij.patterns.TreeElementPattern Self withSuperParent(int, java.lang.Class&lt;? extends ParentType&gt;)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
</root>
@@ -0,0 +1,5 @@
<root>
<item name='com.intellij.psi.filters.ElementFilter boolean isClassAcceptable(java.lang.Class) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
</root>
@@ -19,11 +19,13 @@ package org.jetbrains.jet.plugin.completion;
import com.intellij.codeInsight.completion.CompletionProgressIndicator;
import com.intellij.codeInsight.completion.CompletionService;
import com.intellij.openapi.progress.ProcessCanceledException;
import org.jetbrains.annotations.NotNull;
public class CompletionProgressIndicatorUtil {
private CompletionProgressIndicatorUtil() {
}
@NotNull
public static ProcessCanceledException rethrowWithCancelIndicator(ProcessCanceledException exception) {
CompletionProgressIndicator indicator = (CompletionProgressIndicator) CompletionService.getCompletionService().getCurrentCompletion();
assert indicator != null;
@@ -291,10 +291,12 @@ class CompletionSession {
return parameters.getPosition();
}
@NotNull
public JetCompletionResultSet getJetResult() {
return jetResult;
}
@NotNull
public CompletionParameters getParameters() {
return parameters;
}
@@ -68,7 +68,7 @@ private object KindWeigher : LookupElementWeigher("kotlin.kind") {
override fun weigh(element: LookupElement): Weight {
val o = element.getObject()
return when (o) {
is DeclarationLookupObject -> when (o.getDescriptor()) {
is DeclarationLookupObject -> when (o.descriptor) {
is LocalVariableDescriptor, is ValueParameterDescriptor -> Weight.localOrParameter
is PropertyDescriptor -> Weight.property
is PackageViewDescriptor -> Weight.packages
@@ -86,7 +86,7 @@ private object DeprecatedWeigher : LookupElementWeigher("kotlin.deprecated") {
override fun weigh(element: LookupElement): Int {
val o = element.getObject()
if (o is DeclarationLookupObject) {
val descriptor = o.getDescriptor()
val descriptor = o.descriptor
if (descriptor != null && KotlinBuiltIns.getInstance().isDeprecated(descriptor)) return 1
}
@@ -115,12 +115,12 @@ private class JetDeclarationRemotenessWeigher(private val file: JetFile) : Looku
override fun weigh(element: LookupElement): Weight {
val o = element.getObject()
if (o is DeclarationLookupObject) {
val elementFile = o.getPsiElement()?.getContainingFile()
val elementFile = o.psiElement?.getContainingFile()
if (elementFile is JetFile && elementFile.getOriginalFile() == file) {
return Weight.thisFile
}
val descriptor = o.getDescriptor()
val descriptor = o.descriptor
if (descriptor != null) {
val fqName = DescriptorUtils.getFqName(descriptor).toString()
// Invalid name can be met for class object descriptor: Test.MyTest.A.<no name provided>.testOther
@@ -1,86 +0,0 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.completion;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer;
/**
* Stores information about resolved descriptor and position of that descriptor.
* Position will be used for sorting
*/
public final class DeclarationLookupObject {
private static final Logger LOG = Logger.getInstance("#" + DeclarationLookupObject.class.getName());
@Nullable
private final DeclarationDescriptor descriptor;
@NotNull
private final KotlinCodeAnalyzer analyzer;
@Nullable
private final PsiElement psiElement;
public DeclarationLookupObject(
@Nullable DeclarationDescriptor descriptor,
@NotNull KotlinCodeAnalyzer analyzer,
@Nullable PsiElement psiElement
) {
this.descriptor = descriptor;
this.analyzer = analyzer;
this.psiElement = psiElement;
}
@Nullable
public DeclarationDescriptor getDescriptor() {
return descriptor;
}
@Nullable
public PsiElement getPsiElement() {
return psiElement;
}
@Override
public String toString() {
return super.toString() + " " + descriptor;
}
@Override
public int hashCode() {
return descriptor != null ? descriptor.hashCode() : 0;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
DeclarationLookupObject lookupObject = (DeclarationLookupObject) obj;
if (!analyzer.equals(lookupObject.analyzer)) {
LOG.warn("Descriptors from different resolve sessions");
return false;
}
return descriptor != null ? descriptor.equals(lookupObject.descriptor) : lookupObject.descriptor == null;
}
}
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.completion
import com.intellij.openapi.diagnostic.Logger
import com.intellij.psi.PsiElement
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer
/**
* Stores information about resolved descriptor and position of that descriptor.
* Position will be used for sorting
*/
public class DeclarationLookupObject(public val descriptor: DeclarationDescriptor?, private val analyzer: KotlinCodeAnalyzer, public val psiElement: PsiElement?) {
override fun toString(): String {
return super.toString() + " " + descriptor
}
override fun hashCode(): Int {
return if (descriptor != null) descriptor!!.hashCode() else 0
}
override fun equals(other: Any?): Boolean {
if (this identityEquals other) return true
if (other == null || javaClass != other.javaClass) return false
val lookupObject = other as DeclarationLookupObject
if (analyzer != lookupObject.analyzer) {
LOG.warn("Descriptors from different resolve sessions")
return false
}
return lookupObject.descriptor == descriptor
}
class object {
private val LOG = Logger.getInstance("#" + javaClass<DeclarationLookupObject>().getName())
}
}
@@ -128,6 +128,7 @@ public final class DescriptorLookupConverter {
return createLookupElement(analyzer, descriptor, DescriptorToSourceUtils.descriptorToDeclaration(descriptor));
}
@NotNull
public static LookupElement[] collectLookupElements(
@NotNull KotlinCodeAnalyzer analyzer,
@NotNull Iterable<DeclarationDescriptor> descriptors
@@ -1,185 +0,0 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.completion;
import com.intellij.codeInsight.completion.*;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.patterns.ElementPattern;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.JetBlockExpression;
import org.jetbrains.jet.lang.psi.JetCallExpression;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.jet.plugin.completion.smart.SmartCompletion;
import org.jetbrains.jet.plugin.references.JetSimpleNameReference;
import static com.intellij.patterns.PsiJavaPatterns.elementType;
import static com.intellij.patterns.PsiJavaPatterns.psiElement;
public class JetCompletionContributor extends CompletionContributor {
private static final ElementPattern<PsiElement> AFTER_NUMBER_LITERAL = psiElement().afterLeafSkipping(
psiElement().withText(""),
psiElement().withElementType(elementType().oneOf(JetTokens.FLOAT_LITERAL, JetTokens.INTEGER_LITERAL)));
public JetCompletionContributor() {
CompletionProvider<CompletionParameters> provider = new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(
@NotNull CompletionParameters parameters,
ProcessingContext context,
@NotNull CompletionResultSet result
) {
doSimpleReferenceCompletion(parameters, result);
}
};
extend(CompletionType.BASIC, PlatformPatterns.psiElement(), provider);
extend(CompletionType.SMART, PlatformPatterns.psiElement(), provider);
}
public static void doSimpleReferenceCompletion(CompletionParameters parameters, CompletionResultSet result) {
PsiElement position = parameters.getPosition();
if (!(position.getContainingFile() instanceof JetFile)) return;
if (AFTER_NUMBER_LITERAL.accepts(parameters.getPosition())) {
// First Kotlin completion contributors - stop here will stop all completion
result.stopHere();
return;
}
JetSimpleNameReference jetReference = getJetReference(parameters);
if (jetReference != null) {
try {
result.restartCompletionWhenNothingMatches();
CompletionSession session = new CompletionSession(parameters, result, jetReference, position);
if (parameters.getCompletionType() == CompletionType.BASIC) {
session.completeForReference();
if (!session.getJetResult().isSomethingAdded()
&& session.getParameters().getInvocationCount() < 2) {
// Rerun completion if nothing was found
session = new CompletionSession(parameters.withInvocationCount(2), result, jetReference, position);
session.completeForReference();
}
}
else {
session.completeSmart();
}
}
catch (ProcessCanceledException e) {
throw CompletionProgressIndicatorUtil.rethrowWithCancelIndicator(e);
}
}
}
@Nullable
private static JetSimpleNameReference getJetReference(@NotNull CompletionParameters parameters) {
PsiElement element = parameters.getPosition();
if (element.getParent() != null) {
PsiElement parent = element.getParent();
PsiReference[] references = parent.getReferences();
if (references.length != 0) {
for (PsiReference reference : references) {
if (reference instanceof JetSimpleNameReference) {
return (JetSimpleNameReference)reference;
}
}
}
}
return null;
}
@Override
public void beforeCompletion(@NotNull CompletionInitializationContext context) {
if (context.getFile() instanceof JetFile) {
int offset = context.getStartOffset();
PsiElement tokenBefore = context.getFile().findElementAt(Math.max(0, offset - 1));
if (context.getCompletionType() == CompletionType.SMART) {
context.setDummyIdentifier(CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED + "$"); // add '$' to ignore context after the caret
}
else {
if (JetPackagesContributor.ACTIVATION_PATTERN.accepts(tokenBefore)) {
context.setDummyIdentifier(JetPackagesContributor.DUMMY_IDENTIFIER);
}
else if (JetExtensionReceiverTypeContributor.ACTIVATION_PATTERN.accepts(tokenBefore)) {
context.setDummyIdentifier(JetExtensionReceiverTypeContributor.DUMMY_IDENTIFIER);
}
else{
context.setDummyIdentifier(CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED);
}
}
// this code will make replacement offset "modified" and prevents altering it by the code in CompletionProgressIndicator
context.setReplacementOffset(context.getReplacementOffset());
if (context.getCompletionType() == CompletionType.SMART
&& !isAtEndOfLine(offset, context.getEditor().getDocument()) /* do not use parent expression if we are at the end of line - it's probably parsed incorrectly */) {
PsiElement tokenAt = context.getFile().findElementAt(Math.max(0, offset));
if (tokenAt != null) {
PsiElement parent = tokenAt.getParent();
if (parent instanceof JetExpression && !(parent instanceof JetBlockExpression)) {
// search expression to be replaced - go up while we are the first child of parent expression
JetExpression expression = (JetExpression) parent;
parent = expression.getParent();
while (parent instanceof JetExpression && parent.getFirstChild() == expression) {
expression = (JetExpression) parent;
parent = expression.getParent();
}
int expressionEnd = expression.getTextRange().getEndOffset();
int suggestedReplacementOffset;
if (expression instanceof JetCallExpression) {
JetExpression calleeExpression = ((JetCallExpression) expression).getCalleeExpression();
suggestedReplacementOffset = calleeExpression != null ? calleeExpression.getTextRange().getEndOffset() : expressionEnd;
}
else {
suggestedReplacementOffset = expressionEnd;
}
if (suggestedReplacementOffset > context.getReplacementOffset()) {
context.setReplacementOffset(suggestedReplacementOffset);
}
context.getOffsetMap().addOffset(SmartCompletion.OLD_ARGUMENTS_REPLACEMENT_OFFSET, expressionEnd);
}
}
}
}
}
private static boolean isAtEndOfLine(int offset, Document document) {
int i = offset;
CharSequence chars = document.getCharsSequence();
while (i < chars.length()) {
char c = chars.charAt(i);
if (c == '\n' || c == 'r') return true;
if (!Character.isWhitespace(c)) return false;
i++;
}
return true;
}
}
@@ -0,0 +1,171 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.completion
import com.intellij.codeInsight.completion.*
import com.intellij.openapi.editor.Document
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.patterns.PlatformPatterns
import com.intellij.util.ProcessingContext
import org.jetbrains.jet.lang.psi.JetBlockExpression
import org.jetbrains.jet.lang.psi.JetCallExpression
import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.lang.psi.JetFile
import org.jetbrains.jet.lexer.JetTokens
import org.jetbrains.jet.plugin.completion.smart.SmartCompletion
import org.jetbrains.jet.plugin.references.JetSimpleNameReference
import com.intellij.patterns.PsiJavaPatterns.elementType
import com.intellij.patterns.PsiJavaPatterns.psiElement
public class JetCompletionContributor : CompletionContributor() {
{
val provider = object : CompletionProvider<CompletionParameters>() {
override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) {
doSimpleReferenceCompletion(parameters, result)
}
}
extend(CompletionType.BASIC, PlatformPatterns.psiElement(), provider)
extend(CompletionType.SMART, PlatformPatterns.psiElement(), provider)
}
override fun beforeCompletion(context: CompletionInitializationContext) {
val psiFile = context.getFile()
if (psiFile !is JetFile) return
val offset = context.getStartOffset()
val tokenBefore = psiFile.findElementAt(Math.max(0, offset - 1))
if (context.getCompletionType() == CompletionType.SMART) {
context.setDummyIdentifier(CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED + "$") // add '$' to ignore context after the caret
}
else {
if (JetPackagesContributor.ACTIVATION_PATTERN.accepts(tokenBefore)) {
context.setDummyIdentifier(JetPackagesContributor.DUMMY_IDENTIFIER)
}
else if (JetExtensionReceiverTypeContributor.ACTIVATION_PATTERN.accepts(tokenBefore)) {
context.setDummyIdentifier(JetExtensionReceiverTypeContributor.DUMMY_IDENTIFIER)
}
else {
context.setDummyIdentifier(CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED)
}
}
// this code will make replacement offset "modified" and prevents altering it by the code in CompletionProgressIndicator
context.setReplacementOffset(context.getReplacementOffset())
if (context.getCompletionType() == CompletionType.SMART && !isAtEndOfLine(offset, context.getEditor().getDocument()) /* do not use parent expression if we are at the end of line - it's probably parsed incorrectly */) {
val tokenAt = psiFile.findElementAt(Math.max(0, offset))
if (tokenAt != null) {
var parent = tokenAt.getParent()
if (parent is JetExpression && parent !is JetBlockExpression) {
// search expression to be replaced - go up while we are the first child of parent expression
var expression = parent as JetExpression
parent = expression.getParent()
while (parent is JetExpression && parent!!.getFirstChild() == expression) {
expression = parent as JetExpression
parent = expression.getParent()
}
val expressionEnd = expression.getTextRange()!!.getEndOffset()
val suggestedReplacementOffset: Int
if (expression is JetCallExpression) {
val calleeExpression = (expression as JetCallExpression).getCalleeExpression()
suggestedReplacementOffset = if (calleeExpression != null) calleeExpression.getTextRange()!!.getEndOffset() else expressionEnd
}
else {
suggestedReplacementOffset = expressionEnd
}
if (suggestedReplacementOffset > context.getReplacementOffset()) {
context.setReplacementOffset(suggestedReplacementOffset)
}
context.getOffsetMap().addOffset(SmartCompletion.OLD_ARGUMENTS_REPLACEMENT_OFFSET, expressionEnd)
}
}
}
}
class object {
private val AFTER_NUMBER_LITERAL = psiElement().afterLeafSkipping(psiElement().withText(""), psiElement().withElementType(elementType().oneOf(JetTokens.FLOAT_LITERAL, JetTokens.INTEGER_LITERAL)))
public fun doSimpleReferenceCompletion(parameters: CompletionParameters, result: CompletionResultSet) {
val position = parameters.getPosition()
if (position.getContainingFile() !is JetFile) return
if (AFTER_NUMBER_LITERAL.accepts(parameters.getPosition())) {
// First Kotlin completion contributors - stop here will stop all completion
result.stopHere()
return
}
val jetReference = getJetReference(parameters)
if (jetReference != null) {
try {
result.restartCompletionWhenNothingMatches()
var session = CompletionSession(parameters, result, jetReference, position)
if (parameters.getCompletionType() == CompletionType.BASIC) {
session.completeForReference()
if (!session.getJetResult().isSomethingAdded() && session.getParameters().getInvocationCount() < 2) {
// Rerun completion if nothing was found
session = CompletionSession(parameters.withInvocationCount(2), result, jetReference, position)
session.completeForReference()
}
}
else {
session.completeSmart()
}
}
catch (e: ProcessCanceledException) {
throw CompletionProgressIndicatorUtil.rethrowWithCancelIndicator(e)
}
}
}
private fun getJetReference(parameters: CompletionParameters): JetSimpleNameReference? {
val element = parameters.getPosition()
val parent = element.getParent()
if (parent != null) {
val references = parent.getReferences()
if (references.size != 0) {
for (reference in references) {
if (reference is JetSimpleNameReference) {
return reference as JetSimpleNameReference
}
}
}
}
return null
}
private fun isAtEndOfLine(offset: Int, document: Document): Boolean {
var i = offset
val chars = document.getCharsSequence()
while (i < chars.length()) {
val c = chars.charAt(i)
if (c == '\n' || c == 'r') return true
if (!Character.isWhitespace(c)) return false
i++
}
return true
}
}
}
@@ -1,54 +0,0 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.completion;
import com.intellij.codeInsight.completion.*;
import com.intellij.patterns.ElementPattern;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lexer.JetTokens;
/**
* Special contributor for getting completion of type for extensions receiver.
*/
public class JetExtensionReceiverTypeContributor extends CompletionContributor {
// A way to add reference into file at completion place
public static final String DUMMY_IDENTIFIER = "KotlinExtensionDummy.fake() {}";
public static final ElementPattern<? extends PsiElement> ACTIVATION_PATTERN =
PlatformPatterns.psiElement().afterLeaf(
JetTokens.FUN_KEYWORD.toString(),
JetTokens.VAL_KEYWORD.toString(),
JetTokens.VAR_KEYWORD.toString());
public JetExtensionReceiverTypeContributor() {
extend(CompletionType.BASIC, ACTIVATION_PATTERN, new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(
@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result
) {
if (parameters.getInvocationCount() > 0) {
JetCompletionContributor.doSimpleReferenceCompletion(parameters, result);
}
result.stopHere();
}
});
}
}
@@ -0,0 +1,48 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.completion
import com.intellij.codeInsight.completion.*
import com.intellij.patterns.PlatformPatterns
import com.intellij.util.ProcessingContext
import org.jetbrains.jet.lexer.JetTokens
import com.intellij.psi.PsiElement
import com.intellij.patterns.PsiElementPattern
/**
* Special contributor for getting completion of type for extensions receiver.
*/
public class JetExtensionReceiverTypeContributor : CompletionContributor() {
class object {
// A way to add reference into file at completion place
public val DUMMY_IDENTIFIER: String = "KotlinExtensionDummy.fake() {}"
public val ACTIVATION_PATTERN: PsiElementPattern.Capture<PsiElement> = PlatformPatterns.psiElement().afterLeaf(JetTokens.FUN_KEYWORD.toString(), JetTokens.VAL_KEYWORD.toString(), JetTokens.VAR_KEYWORD.toString())
}
{
extend(CompletionType.BASIC, ACTIVATION_PATTERN, object : CompletionProvider<CompletionParameters>() {
override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) {
if (parameters.getInvocationCount() > 0) {
JetCompletionContributor.doSimpleReferenceCompletion(parameters, result)
}
result.stopHere()
}
})
}
}
@@ -42,7 +42,7 @@ import com.intellij.psi.filters.position.PatternFilter
class KeywordLookupObject(val keyword: String)
public open class JetKeywordCompletionContributor() : CompletionContributor() {
public class JetKeywordCompletionContributor : CompletionContributor() {
{
val inTopLevel = notIdentifier(InTopFilter())
val inTypeParameterFirstChildFilter = InTypeParameterFirstChildFilter()
@@ -160,11 +160,11 @@ public open class JetKeywordCompletionContributor() : CompletionContributor() {
PlatformPatterns.psiElement().and(FilterPattern(AndFilter(GENERAL_FILTER, placeFilter)))
private open class CommentFilter() : ElementFilter {
override fun isAcceptable(element : Any?, context : PsiElement?) : Boolean {
return (element is PsiElement) && JetPsiUtil.isInComment(element as PsiElement)
}
override fun isAcceptable(element : Any?, context : PsiElement?)
= (element is PsiElement) && JetPsiUtil.isInComment(element as PsiElement)
override fun isClassAcceptable(hintClass: Class<out Any?>?): Boolean = true
override fun isClassAcceptable(hintClass: Class<out Any?>)
= true
}
private open class ParentFilter(filter : ElementFilter) : PositionElementFilter() {
@@ -192,9 +192,8 @@ public open class JetKeywordCompletionContributor() : CompletionContributor() {
}
private open class InNonClassBlockFilter() : PositionElementFilter() {
override fun isAcceptable(element : Any?, context : PsiElement?) : Boolean {
return PsiTreeUtil.getParentOfType(context, javaClass<JetBlockExpression>(), true, javaClass<JetClassBody>()) != null
}
override fun isAcceptable(element : Any?, context : PsiElement?)
= PsiTreeUtil.getParentOfType(context, javaClass<JetBlockExpression>(), true, javaClass<JetClassBody>()) != null
}
private open class InTypeParameterFirstChildFilter() : PositionElementFilter() {
@@ -280,7 +279,7 @@ public open class JetKeywordCompletionContributor() : CompletionContributor() {
}
}
override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext?, result: CompletionResultSet) {
override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) {
result.addKotlinSorting(parameters).withPrefixMatcher(SimplePrefixMatcher(result.getPrefixMatcher().getPrefix())).addAllElements(elements)
}
@@ -1,98 +0,0 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.completion;
import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.patterns.ElementPattern;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetPackageDirective;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage;
import org.jetbrains.jet.plugin.codeInsight.TipsManager;
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies;
import org.jetbrains.jet.plugin.references.JetSimpleNameReference;
import java.util.Collection;
/**
* Performs completion in package directive. Should suggest only packages and avoid showing fake package produced by
* DUMMY_IDENTIFIER.
*/
public class JetPackagesContributor extends CompletionContributor {
static final String DUMMY_IDENTIFIER = "___package___";
static final ElementPattern<? extends PsiElement> ACTIVATION_PATTERN =
PlatformPatterns.psiElement().inside(JetPackageDirective.class);
public JetPackagesContributor() {
extend(CompletionType.BASIC, ACTIVATION_PATTERN,
new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context,
@NotNull CompletionResultSet result) {
PsiElement position = parameters.getPosition();
if (!(position.getContainingFile() instanceof JetFile)) {
return;
}
PsiReference ref = parameters.getPosition().getContainingFile().findReferenceAt(parameters.getOffset());
if (ref instanceof JetSimpleNameReference) {
JetSimpleNameReference simpleNameReference = (JetSimpleNameReference)ref;
String name = simpleNameReference.getExpression().getText();
if (name == null) {
return;
}
try {
int prefixLength = parameters.getOffset() - simpleNameReference.getExpression().getTextOffset();
result = result.withPrefixMatcher(new PlainPrefixMatcher(name.substring(0, prefixLength)));
ResolveSessionForBodies resolveSession =
ResolvePackage.getLazyResolveSession(simpleNameReference.getExpression());
BindingContext bindingContext = resolveSession.resolveToElement(simpleNameReference.getExpression());
Collection<DeclarationDescriptor> variants =
TipsManager.getPackageReferenceVariants(simpleNameReference.getExpression(), bindingContext);
for (LookupElement variant : DescriptorLookupConverter.collectLookupElements(resolveSession, variants)) {
if (!variant.getLookupString().contains(DUMMY_IDENTIFIER)) {
result.addElement(variant);
}
}
result.stopHere();
}
catch (ProcessCanceledException e) {
throw CompletionProgressIndicatorUtil.rethrowWithCancelIndicator(e);
}
}
}
});
}
}
@@ -0,0 +1,81 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.completion
import com.intellij.codeInsight.completion.*
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.patterns.ElementPattern
import com.intellij.patterns.PlatformPatterns
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.util.ProcessingContext
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.jet.lang.psi.JetFile
import org.jetbrains.jet.lang.psi.JetPackageDirective
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.plugin.caches.resolve.*
import org.jetbrains.jet.plugin.codeInsight.TipsManager
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies
import org.jetbrains.jet.plugin.references.JetSimpleNameReference
/**
* Performs completion in package directive. Should suggest only packages and avoid showing fake package produced by
* DUMMY_IDENTIFIER.
*/
public class JetPackagesContributor : CompletionContributor() {
class object {
val DUMMY_IDENTIFIER = "___package___"
val ACTIVATION_PATTERN: ElementPattern<out PsiElement> = PlatformPatterns.psiElement().inside(javaClass<JetPackageDirective>())
}
{
extend(CompletionType.BASIC, ACTIVATION_PATTERN, object : CompletionProvider<CompletionParameters>() {
override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) {
val position = parameters.getPosition()
if (position.getContainingFile() !is JetFile) return
val ref = parameters.getPosition().getContainingFile()!!.findReferenceAt(parameters.getOffset())
if (ref is JetSimpleNameReference) {
val name = ref.expression.getText() ?: return
try {
val prefixLength = parameters.getOffset() - ref.expression.getTextOffset()
var result = result.withPrefixMatcher(PlainPrefixMatcher(name.substring(0, prefixLength)))
val resolveSession = ref.expression.getLazyResolveSession()
val bindingContext = resolveSession.resolveToElement(ref.expression)
val variants = TipsManager.getPackageReferenceVariants(ref.expression, bindingContext)
for (variant in DescriptorLookupConverter.collectLookupElements(resolveSession, variants)) {
if (!variant.getLookupString().contains(DUMMY_IDENTIFIER)) {
result.addElement(variant)
}
}
result.stopHere()
}
catch (e: ProcessCanceledException) {
throw CompletionProgressIndicatorUtil.rethrowWithCancelIndicator(e)
}
}
}
})
}
}
@@ -62,7 +62,7 @@ public class KotlinNamedParametersContributor : CompletionContributor() {
extend(CompletionType.BASIC,
PlatformPatterns.psiElement().and(FilterPattern(InNamedParameterFilter)),
object : CompletionProvider<CompletionParameters>() {
override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext?, result: CompletionResultSet) {
override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) {
doParamsCompletion(parameters, result)
}
})
@@ -1,43 +0,0 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.completion;
import com.intellij.psi.PsiElement;
import com.intellij.psi.filters.ClassFilter;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import com.intellij.psi.tree.IElementType;
public class LeafElementFilter implements ElementFilter {
private static final ClassFilter LEAF_CLASS_FILTER = new ClassFilter(LeafPsiElement.class);
private final IElementType myElementType;
LeafElementFilter(IElementType elementType) {
myElementType = elementType;
}
@Override
public boolean isAcceptable(Object element, PsiElement context) {
return element instanceof LeafPsiElement && ((LeafPsiElement) element).getElementType() == myElementType;
}
@Override
public boolean isClassAcceptable(Class hintClass) {
return LEAF_CLASS_FILTER.isClassAcceptable(hintClass);
}
}
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.completion
import com.intellij.psi.PsiElement
import com.intellij.psi.filters.ClassFilter
import com.intellij.psi.filters.ElementFilter
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.tree.IElementType
public class LeafElementFilter(private val elementType: IElementType) : ElementFilter {
override fun isAcceptable(element: Any?, context: PsiElement?)
= element is LeafPsiElement && element.getElementType() == elementType
override fun isClassAcceptable(hintClass: Class<*>)
= LEAF_CLASS_FILTER.isClassAcceptable(hintClass)
class object {
private val LEAF_CLASS_FILTER = ClassFilter(javaClass<LeafPsiElement>())
}
}
@@ -33,7 +33,7 @@ public object JetClassInsertHandler : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, item: LookupElement) {
val file = context.getFile()
if (file is JetFile) {
val descriptor = (item.getObject() as? org.jetbrains.jet.plugin.completion.DeclarationLookupObject)?.getDescriptor() as? ClassDescriptor
val descriptor = (item.getObject() as? DeclarationLookupObject)?.descriptor as? ClassDescriptor
if (descriptor != null) {
val startOffset = context.getStartOffset()
val document = context.getDocument()
@@ -36,6 +36,7 @@ import org.jetbrains.jet.plugin.quickfix.ImportInsertHelper
import com.intellij.openapi.editor.Document
import org.jetbrains.jet.lang.types.JetType
import com.intellij.openapi.util.TextRange
import org.jetbrains.jet.plugin.completion.DeclarationLookupObject
public enum class CaretPosition {
IN_BRACKETS
@@ -155,20 +156,19 @@ public class JetFunctionInsertHandler(val caretPosition : CaretPosition, val lam
if (element == null) return@runReadAction
val file = context.getFile()
if (file is JetFile && item.getObject() is org.jetbrains.jet.plugin.completion.DeclarationLookupObject) {
val descriptor = (item.getObject() as org.jetbrains.jet.plugin.completion.DeclarationLookupObject).getDescriptor()
if (descriptor is SimpleFunctionDescriptor) {
val functionDescriptor = descriptor as SimpleFunctionDescriptor
val o = item.getObject()
if (file is JetFile && o is DeclarationLookupObject) {
val descriptor = o.descriptor as? SimpleFunctionDescriptor
if (descriptor != null) {
if (PsiTreeUtil.getParentOfType(element, javaClass<JetQualifiedExpression>()) != null &&
functionDescriptor.getReceiverParameter() == null) {
descriptor.getReceiverParameter() == null) {
return@runReadAction
}
if (DescriptorUtils.isTopLevelDeclaration(functionDescriptor)) {
if (DescriptorUtils.isTopLevelDeclaration(descriptor)) {
ApplicationManager.getApplication()?.runWriteAction {
val fqn = DescriptorUtils.getFqNameSafe(functionDescriptor)
val fqn = DescriptorUtils.getFqNameSafe(descriptor)
ImportInsertHelper.addImportDirectiveIfNeeded(fqn, file)
}
}