Add basic completion for operator fun names

#KT-11250 fixed
This commit is contained in:
Simon Ogorodnik
2017-04-27 15:59:30 +03:00
parent 0e7753df3a
commit 9a969b0449
8 changed files with 225 additions and 0 deletions
@@ -142,6 +142,20 @@ public class OperatorConventions {
return null;
}
@Nullable
public static KtToken getOperationSymbolForName(@NotNull Name name) {
if (!isConventionName(name)) return null;
KtToken token;
token = BINARY_OPERATION_NAMES.inverse().get(name);
if (token != null) return token;
token = UNARY_OPERATION_NAMES.inverse().get(name);
if (token != null) return token;
token = ASSIGNMENT_OPERATIONS.inverse().get(name);
if (token != null) return token;
return null;
}
public static boolean isConventionName(@NotNull Name name) {
return CONVENTION_NAMES.contains(name) || COMPONENT_REGEX.matches(name.asString());
}
@@ -25,6 +25,7 @@ import com.intellij.codeInsight.template.TemplateManager
import com.intellij.patterns.PatternCondition
import com.intellij.patterns.StandardPatterns
import com.intellij.psi.*
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.ProcessingContext
import org.jetbrains.kotlin.descriptors.*
@@ -96,6 +97,9 @@ class BasicCompletionSession(
}
}
if (OPERATOR_NAME.isApplicable())
return OPERATOR_NAME
if (NamedArgumentCompletion.isOnlyNamedArgumentExpected(nameExpression)) {
return NAMED_ARGUMENTS_ONLY
}
@@ -453,6 +457,26 @@ class BasicCompletionSession(
}
}
private val OPERATOR_NAME = object : CompletionKind {
override val descriptorKindFilter: DescriptorKindFilter?
get() = null
fun isApplicable(): Boolean {
if (nameExpression == null || nameExpression != expression) return false
val func = position.getParentOfType<KtNamedFunction>(strict = false) ?: return false
val funcNameIdentifier = func.nameIdentifier ?: return false
val identifierInNameExpression = nameExpression.nextLeaf { it is LeafPsiElement && it.elementType == KtTokens.IDENTIFIER } ?: return false
if (!func.hasModifier(KtTokens.OPERATOR_KEYWORD) || identifierInNameExpression != funcNameIdentifier) return false
val originalFunc = toFromOriginalFileMapper.toOriginalFile(func) ?: return false
return !originalFunc.isTopLevel || (originalFunc.isExtensionDeclaration())
}
override fun doComplete() {
OperatorNameCompletion.doComplete(collector, descriptorNameFilter)
flushToResultSet()
}
}
private val DECLARATION_NAME = object : CompletionKind {
override val descriptorKindFilter: DescriptorKindFilter?
get() = null
@@ -0,0 +1,58 @@
/*
* Copyright 2010-2017 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.kotlin.idea.completion
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder
import org.jetbrains.kotlin.lexer.KtSingleValueToken
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.util.OperatorNameConventions.COMPARE_TO
import org.jetbrains.kotlin.util.OperatorNameConventions.CONTAINS
import org.jetbrains.kotlin.util.OperatorNameConventions.EQUALS
import org.jetbrains.kotlin.util.OperatorNameConventions.GET
import org.jetbrains.kotlin.util.OperatorNameConventions.INVOKE
import org.jetbrains.kotlin.util.OperatorNameConventions.SET
object OperatorNameCompletion {
val additionalOperatorPresentation = mapOf(
SET to "[...] = ...",
GET to "[...]",
CONTAINS to "in !in",
COMPARE_TO to "< > <= >=",
EQUALS to "== !=",
INVOKE to "(...)"
)
private fun buildLookupElement(opName: Name): LookupElement {
val element = LookupElementBuilder.create(opName)
val symbol =
(OperatorConventions.getOperationSymbolForName(opName) as? KtSingleValueToken)?.value ?:
additionalOperatorPresentation[opName]
if (symbol != null) return element.withTypeText(symbol)
return element
}
fun doComplete(collector: LookupElementsCollector, descriptorNameFilter: (String) -> Boolean) {
collector.addElements(OperatorConventions.CONVENTION_NAMES
.filter { descriptorNameFilter(it.asString()) }
.map(this::buildLookupElement))
}
}
@@ -0,0 +1,5 @@
operator fun <caret>() {
}
// NOTHING_ELSE
@@ -0,0 +1,36 @@
class Some {
}
operator fun Some.<caret>() {
}
// EXIST: {"lookupString":"compareTo","typeText":"< > <= >="}
// EXIST: {"lookupString":"contains","typeText":"in !in"}
// EXIST: {"lookupString":"dec","typeText":"--"}
// EXIST: {"lookupString":"div","typeText":"/"}
// EXIST: {"lookupString":"divAssign","typeText":"/="}
// EXIST: {"lookupString":"equals","typeText":"== !="}
// EXIST: {"lookupString":"get","typeText":"[...]"}
// EXIST: {"lookupString":"getValue"}
// EXIST: {"lookupString":"hasNext"}
// EXIST: {"lookupString":"inc","typeText":"++"}
// EXIST: {"lookupString":"invoke","typeText":"(...)"}
// EXIST: {"lookupString":"iterator"}
// EXIST: {"lookupString":"minus","typeText":"-"}
// EXIST: {"lookupString":"minusAssign","typeText":"-="}
// EXIST: {"lookupString":"next"}
// EXIST: {"lookupString":"not","typeText":"!"}
// EXIST: {"lookupString":"plus","typeText":"+"}
// EXIST: {"lookupString":"plusAssign","typeText":"+="}
// EXIST: {"lookupString":"rangeTo","typeText":".."}
// EXIST: {"lookupString":"rem","typeText":"%"}
// EXIST: {"lookupString":"remAssign","typeText":"%="}
// EXIST: {"lookupString":"set","typeText":"[...] = ..."}
// EXIST: {"lookupString":"setValue"}
// EXIST: {"lookupString":"times","typeText":"*"}
// EXIST: {"lookupString":"timesAssign","typeText":"*="}
// EXIST: {"lookupString":"unaryMinus","typeText":"-"}
// EXIST: {"lookupString":"unaryPlus","typeText":"+"}
// NOTHING_ELSE
@@ -0,0 +1,34 @@
class Some {
operator fun <caret>() {
}
}
// EXIST: {"lookupString":"compareTo","typeText":"< > <= >="}
// EXIST: {"lookupString":"contains","typeText":"in !in"}
// EXIST: {"lookupString":"dec","typeText":"--"}
// EXIST: {"lookupString":"div","typeText":"/"}
// EXIST: {"lookupString":"divAssign","typeText":"/="}
// EXIST: {"lookupString":"equals","typeText":"== !="}
// EXIST: {"lookupString":"get","typeText":"[...]"}
// EXIST: {"lookupString":"getValue"}
// EXIST: {"lookupString":"hasNext"}
// EXIST: {"lookupString":"inc","typeText":"++"}
// EXIST: {"lookupString":"invoke","typeText":"(...)"}
// EXIST: {"lookupString":"iterator"}
// EXIST: {"lookupString":"minus","typeText":"-"}
// EXIST: {"lookupString":"minusAssign","typeText":"-="}
// EXIST: {"lookupString":"next"}
// EXIST: {"lookupString":"not","typeText":"!"}
// EXIST: {"lookupString":"plus","typeText":"+"}
// EXIST: {"lookupString":"plusAssign","typeText":"+="}
// EXIST: {"lookupString":"rangeTo","typeText":".."}
// EXIST: {"lookupString":"rem","typeText":"%"}
// EXIST: {"lookupString":"remAssign","typeText":"%="}
// EXIST: {"lookupString":"set","typeText":"[...] = ..."}
// EXIST: {"lookupString":"setValue"}
// EXIST: {"lookupString":"times","typeText":"*"}
// EXIST: {"lookupString":"timesAssign","typeText":"*="}
// EXIST: {"lookupString":"unaryMinus","typeText":"-"}
// EXIST: {"lookupString":"unaryPlus","typeText":"+"}
// NOTHING_ELSE
@@ -2021,6 +2021,33 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/operatorNames")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class OperatorNames extends AbstractJSBasicCompletionTest {
public void testAllFilesPresentInOperatorNames() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/operatorNames"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("NoOperatorNameForTopLevel.kt")
public void testNoOperatorNameForTopLevel() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/operatorNames/NoOperatorNameForTopLevel.kt");
doTest(fileName);
}
@TestMetadata("OperatorNameForExtension.kt")
public void testOperatorNameForExtension() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/operatorNames/OperatorNameForExtension.kt");
doTest(fileName);
}
@TestMetadata("OperatorNameForMember.kt")
public void testOperatorNameForMember() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/operatorNames/OperatorNameForMember.kt");
doTest(fileName);
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/override")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -2021,6 +2021,33 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/operatorNames")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class OperatorNames extends AbstractJvmBasicCompletionTest {
public void testAllFilesPresentInOperatorNames() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/operatorNames"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("NoOperatorNameForTopLevel.kt")
public void testNoOperatorNameForTopLevel() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/operatorNames/NoOperatorNameForTopLevel.kt");
doTest(fileName);
}
@TestMetadata("OperatorNameForExtension.kt")
public void testOperatorNameForExtension() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/operatorNames/OperatorNameForExtension.kt");
doTest(fileName);
}
@TestMetadata("OperatorNameForMember.kt")
public void testOperatorNameForMember() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/operatorNames/OperatorNameForMember.kt");
doTest(fileName);
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/override")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)