Escaping of keyword names in completion (more tests to be added)

This commit is contained in:
Valentin Kipyatkov
2014-08-13 22:39:09 +04:00
parent f5419cb04e
commit 5fb9aae68c
20 changed files with 151 additions and 67 deletions
@@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.renderer.DescriptorRenderer;
public final class ImportPath {
private final @NotNull FqName fqName;
@@ -50,7 +51,7 @@ public final class ImportPath {
}
public String getPathStr() {
return fqName.asString() + (isAllUnder ? ".*" : "");
return DescriptorRenderer.SOURCE_CODE.renderFqName(fqName) + (isAllUnder ? ".*" : "");
}
@Override
@@ -21,6 +21,9 @@ import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.FqNameBase;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeProjection;
@@ -110,6 +113,12 @@ public interface DescriptorRenderer extends Renderer<DeclarationDescriptor> {
@NotNull
String renderFunctionParameters(@NotNull FunctionDescriptor functionDescriptor);
@NotNull
String renderName(@NotNull Name name);
@NotNull
String renderFqName(@NotNull FqNameBase fqName);
enum TextFormat {
PLAIN, HTML
}
@@ -198,8 +198,9 @@ public class DescriptorRendererImpl implements DescriptorRenderer {
}
/* NAMES RENDERING */
@Override
@NotNull
private String renderName(@NotNull Name identifier) {
public String renderName(@NotNull Name identifier) {
String asString = identifier.toString();
return escape(KeywordStringsGenerated.KEYWORDS.contains(asString) ? '`' + asString + '`' : asString);
}
@@ -219,8 +220,9 @@ public class DescriptorRendererImpl implements DescriptorRenderer {
}
}
@Override
@NotNull
private String renderFqName(@NotNull FqNameBase fqName) {
public String renderFqName(@NotNull FqNameBase fqName) {
return renderFqName(fqName.pathSegments());
}
@@ -61,6 +61,7 @@ import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils
import org.jetbrains.jet.plugin.imports.*
import org.jetbrains.jet.lang.psi.psiUtil.getReceiverExpression
import org.jetbrains.jet.utils.*
import org.jetbrains.jet.renderer.DescriptorRenderer
//NOTE: this class is based on CopyPasteReferenceProcessor and JavaCopyPasteReferenceProcessor
public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<ReferenceTransferableData>() {
@@ -322,9 +323,8 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Refere
fun lengthenReference(expression: JetElement, fqName: FqName) {
assert(canLengthenReferenceExpression(expression, fqName))
val project = expression.getProject()
val parent = expression.getParent()
val prefixToInsert = fqName.parent().asString()
val prefixToInsert = DescriptorRenderer.SOURCE_CODE.renderFqName(fqName.parent())
val psiFactory = JetPsiFactory(expression)
if (parent is JetCallExpression) {
val text = "$prefixToInsert.${parent.getText()}"
@@ -337,7 +337,7 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Refere
typeReference!!.replace(psiFactory.createType("$prefixToInsert.${typeReference.getText()}"))
}
else {
expression.replace(createQualifiedExpression(psiFactory, fqName.asString()))
expression.replace(createQualifiedExpression(psiFactory, DescriptorRenderer.SOURCE_CODE.renderFqName(fqName)))
}
}
@@ -22,6 +22,10 @@ import com.intellij.codeInsight.lookup.AutoCompletionPolicy
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.codeInsight.completion.CompletionService
import com.intellij.codeInsight.completion.CompletionProgressIndicator
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
import org.jetbrains.jet.renderer.DescriptorRenderer
import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor
import org.jetbrains.jet.lang.descriptors.PackageFragmentDescriptor
enum class ItemPriority {
DEFAULT
@@ -54,3 +58,18 @@ fun rethrowWithCancelIndicator(exception: ProcessCanceledException): ProcessCanc
return exception
}
fun qualifiedNameForSourceCode(descriptor: ClassDescriptor): String? {
val name = descriptor.getName()
if (name.isSpecial()) return null
val nameString = DescriptorRenderer.SOURCE_CODE.renderName(name)
val parent = descriptor.getContainingDeclaration()
val qualifier = when (parent) {
is ClassDescriptor -> qualifiedNameForSourceCode(parent)
is PackageViewDescriptor -> DescriptorRenderer.SOURCE_CODE.renderFqName(parent.getFqName())
is PackageFragmentDescriptor -> DescriptorRenderer.SOURCE_CODE.renderFqName(parent.fqName)
else -> null
}
return if (qualifier != null && qualifier != "") qualifier + "." + nameString else nameString
}
@@ -32,12 +32,14 @@ import org.jetbrains.jet.plugin.completion.handlers.GenerateLambdaInfo
import org.jetbrains.jet.plugin.completion.handlers.JetClassInsertHandler
import org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler
import org.jetbrains.jet.renderer.DescriptorRenderer
import org.jetbrains.jet.plugin.completion.handlers.BaseDeclarationInsertHandler
public object DescriptorLookupConverter {
public fun createLookupElement(analyzer: KotlinCodeAnalyzer, descriptor: DeclarationDescriptor, declaration: PsiElement?): LookupElement {
var element = LookupElementBuilder.create(DeclarationLookupObject(descriptor, analyzer, declaration), descriptor.getName().asString())
val name = descriptor.getName().asString()
var element = LookupElementBuilder.create(DeclarationLookupObject(descriptor, analyzer, declaration), name)
var presentableText = descriptor.getName().asString()
var presentableText = name
var typeText = ""
var tailText = ""
@@ -75,7 +77,7 @@ public object DescriptorLookupConverter {
return element
}
public fun getDefaultInsertHandler(descriptor: DeclarationDescriptor): InsertHandler<LookupElement>? {
public fun getDefaultInsertHandler(descriptor: DeclarationDescriptor): InsertHandler<LookupElement> {
return when (descriptor) {
is FunctionDescriptor -> {
val parameters = descriptor.getValueParameters()
@@ -100,7 +102,7 @@ public object DescriptorLookupConverter {
is ClassDescriptor -> JetClassInsertHandler
else -> null
else -> BaseDeclarationInsertHandler()
}
}
@@ -17,7 +17,6 @@
package org.jetbrains.jet.plugin.completion
import org.jetbrains.jet.lang.psi.JetValueArgument
import com.intellij.codeInsight.completion.CompletionParameters
import org.jetbrains.jet.lexer.JetTokens
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.jet.lang.psi.JetCallElement
@@ -27,7 +26,6 @@ import com.intellij.codeInsight.lookup.LookupElementBuilder
import org.jetbrains.jet.plugin.JetIcons
import org.jetbrains.jet.plugin.quickfix.QuickFixUtil
import org.jetbrains.jet.renderer.DescriptorRenderer
import com.intellij.codeInsight.completion.InsertHandler
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.psi.filters.AndFilter
@@ -38,6 +36,7 @@ import com.intellij.psi.filters.ClassFilter
import org.jetbrains.jet.plugin.util.FirstChildInParentFilter
import org.jetbrains.jet.lang.psi.psiUtil.getCallNameExpression
import com.intellij.psi.PsiElement
import org.jetbrains.jet.plugin.completion.handlers.BaseDeclarationInsertHandler
object NamedParametersCompletion {
private val positionFilter = AndFilter(
@@ -86,7 +85,7 @@ object NamedParametersCompletion {
for (parameter in funDescriptor.getValueParameters()) {
val name = parameter.getName().asString()
if (name !in usedArguments) {
val lookupElement = LookupElementBuilder.create("$name")
val lookupElement = LookupElementBuilder.create(parameter, name)
.withPresentableText("$name = ")
.withTailText("${DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(parameter.getType())}")
.withIcon(JetIcons.PARAMETER)
@@ -100,7 +99,7 @@ object NamedParametersCompletion {
}
}
private object NamedParameterInsertHandler : InsertHandler<LookupElement> {
private object NamedParameterInsertHandler : BaseDeclarationInsertHandler() {
override fun handleInsert(context: InsertionContext, item: LookupElement) {
val ch = context.getCompletionChar()
if (ch == '=' || ch == ' ') {
@@ -0,0 +1,38 @@
/*
* 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.handlers
import com.intellij.codeInsight.completion.InsertHandler
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.completion.InsertionContext
import org.jetbrains.jet.plugin.completion.DeclarationLookupObject
import org.jetbrains.jet.renderer.DescriptorRenderer
import com.intellij.openapi.util.TextRange
open class BaseDeclarationInsertHandler : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, item: LookupElement) {
val descriptor = (item.getObject() as? DeclarationLookupObject)?.descriptor
if (descriptor != null) {
val name = descriptor.getName()
val nameInCode = DescriptorRenderer.SOURCE_CODE.renderName(name)
val document = context.getDocument()
if (nameInCode != name.asString() && document.getText(TextRange(context.getStartOffset(), context.getTailOffset())) == name.asString()) {
document.replaceString(context.getStartOffset(), context.getTailOffset(), nameInCode)
}
}
}
}
@@ -16,7 +16,6 @@
package org.jetbrains.jet.plugin.completion.handlers
import com.intellij.codeInsight.completion.InsertHandler
import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.openapi.editor.Document
@@ -25,36 +24,34 @@ import org.jetbrains.jet.lang.psi.JetFile
import org.jetbrains.jet.plugin.codeInsight.ShortenReferences
import org.jetbrains.jet.plugin.completion.DeclarationLookupObject
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor
import org.jetbrains.jet.lang.descriptors.PackageFragmentDescriptor
public object JetClassInsertHandler : InsertHandler<LookupElement> {
import org.jetbrains.jet.plugin.completion.qualifiedNameForSourceCode
public object JetClassInsertHandler : BaseDeclarationInsertHandler() {
override fun handleInsert(context: InsertionContext, item: LookupElement) {
super.handleInsert(context, item)
val file = context.getFile()
if (file is JetFile) {
val descriptor = (item.getObject() as? DeclarationLookupObject)?.descriptor as? ClassDescriptor
if (descriptor != null) {
val startOffset = context.getStartOffset()
val document = context.getDocument()
if (!isAfterDot(document, startOffset)) {
val qualifiedName = getQualifiedName(descriptor)!!
// insert dot after because otherwise parser can sometimes produce no suitable reference here
val tempSuffix = ".xxx" // we add "xxx" after dot because of some bugs in resolve (see KT-5145)
document.replaceString(startOffset, context.getTailOffset(), qualifiedName + tempSuffix)
val classNameEnd = startOffset + qualifiedName.length()
val descriptor = (item.getObject() as DeclarationLookupObject).descriptor as ClassDescriptor
val startOffset = context.getStartOffset()
val document = context.getDocument()
if (!isAfterDot(document, startOffset)) {
val qualifiedName = qualifiedNameForSourceCode(descriptor)!!
// insert dot after because otherwise parser can sometimes produce no suitable reference here
val tempSuffix = ".xxx" // we add "xxx" after dot because of some bugs in resolve (see KT-5145)
document.replaceString(startOffset, context.getTailOffset(), qualifiedName + tempSuffix)
val classNameEnd = startOffset + qualifiedName.length()
val psiDocumentManager = PsiDocumentManager.getInstance(context.getProject())
psiDocumentManager.commitAllDocuments()
val rangeMarker = document.createRangeMarker(classNameEnd, classNameEnd + tempSuffix.length())
val psiDocumentManager = PsiDocumentManager.getInstance(context.getProject())
psiDocumentManager.commitAllDocuments()
val rangeMarker = document.createRangeMarker(classNameEnd, classNameEnd + tempSuffix.length())
ShortenReferences.process(file, startOffset, classNameEnd)
psiDocumentManager.commitAllDocuments()
psiDocumentManager.doPostponedOperationsAndUnblockDocument(document)
ShortenReferences.process(file, startOffset, classNameEnd)
psiDocumentManager.commitAllDocuments()
psiDocumentManager.doPostponedOperationsAndUnblockDocument(document)
if (rangeMarker.isValid()) {
document.deleteString(rangeMarker.getStartOffset(), rangeMarker.getEndOffset())
}
if (rangeMarker.isValid()) {
document.deleteString(rangeMarker.getStartOffset(), rangeMarker.getEndOffset())
}
}
}
@@ -72,18 +69,4 @@ public object JetClassInsertHandler : InsertHandler<LookupElement> {
}
return false
}
private fun getQualifiedName(descriptor: ClassDescriptor): String? {
val name = descriptor.getName()
if (name.isSpecial()) return null
val nameString = name.asString()
val parent = descriptor.getContainingDeclaration()
val qualifier = when (parent) {
is ClassDescriptor -> getQualifiedName(parent)
is PackageViewDescriptor -> parent.getFqName().asString()
is PackageFragmentDescriptor -> parent.fqName.asString()
else -> null
}
return if (qualifier != null && qualifier != "") qualifier + "." + nameString else nameString
}
}
@@ -16,7 +16,6 @@
package org.jetbrains.jet.plugin.completion.handlers
import com.intellij.codeInsight.completion.InsertHandler
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.psi.PsiDocumentManager
@@ -45,7 +44,7 @@ public enum class CaretPosition {
public data class GenerateLambdaInfo(val lambdaType: JetType, val explicitParameters: Boolean)
public class JetFunctionInsertHandler(val caretPosition : CaretPosition, val lambdaInfo: GenerateLambdaInfo?) : InsertHandler<LookupElement> {
public class JetFunctionInsertHandler(val caretPosition : CaretPosition, val lambdaInfo: GenerateLambdaInfo?) : BaseDeclarationInsertHandler() {
{
if (caretPosition == CaretPosition.AFTER_BRACKETS && lambdaInfo != null) {
throw IllegalArgumentException("CaretPosition.AFTER_BRACKETS with lambdaInfo != null combination is not supported")
@@ -53,6 +52,8 @@ public class JetFunctionInsertHandler(val caretPosition : CaretPosition, val lam
}
public override fun handleInsert(context: InsertionContext, item: LookupElement) {
super.handleInsert(context, item)
PsiDocumentManager.getInstance(context.getProject()).commitAllDocuments()
if (context.getCompletionChar() == '(') {
context.setAddCompletionChar(false)
@@ -37,6 +37,7 @@ import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.plugin.completion.ExpectedInfo
import org.jetbrains.jet.plugin.util.makeNotNullable
import org.jetbrains.jet.plugin.completion.qualifiedNameForSourceCode
// adds java static members, enum members and members from class object
class StaticMembers(val bindingContext: BindingContext, val resolveSession: ResolveSessionForBodies) {
@@ -117,7 +118,7 @@ class StaticMembers(val bindingContext: BindingContext, val resolveSession: Reso
val lookupElement = createLookupElement(memberDescriptor, resolveSession)
val qualifierPresentation = classDescriptor.getName().asString()
val lookupString = qualifierPresentation + "." + lookupElement.getLookupString()
val qualifierText = DescriptorUtils.getFqName(classDescriptor).asString() //TODO: escape keywords
val qualifierText = qualifiedNameForSourceCode(classDescriptor)
return object: LookupElementDecorator<LookupElement>(lookupElement) {
override fun getLookupString() = lookupString
@@ -141,7 +142,7 @@ class StaticMembers(val bindingContext: BindingContext, val resolveSession: Reso
}
override fun handleInsert(context: InsertionContext) {
var text = qualifierText + "." + memberDescriptor.getName().asString() //TODO: escape
var text = qualifierText + "." + DescriptorRenderer.SOURCE_CODE.renderName(memberDescriptor.getName())
context.getDocument().replaceString(context.getStartOffset(), context.getTailOffset(), text)
context.setTailOffset(context.getStartOffset() + text.length)
@@ -21,9 +21,6 @@ import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor
import com.intellij.codeInsight.lookup.LookupElementBuilder
import org.jetbrains.jet.renderer.DescriptorRenderer
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.jet.lang.resolve.name.Name
import org.jetbrains.jet.lang.resolve.BindingContextUtils
import org.jetbrains.jet.lang.psi.JetFunctionLiteral
import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression
import org.jetbrains.jet.lang.psi.JetObjectDeclaration
@@ -65,12 +62,14 @@ class ThisItems(val bindingContext: BindingContext) {
}
private fun thisQualifierName(receiver: ReceiverParameterDescriptor): String? {
val descriptor: DeclarationDescriptor = receiver.getContainingDeclaration()
val name: Name = descriptor.getName()
if (!name.isSpecial()) return name.asString()
val descriptor = receiver.getContainingDeclaration()
val name = descriptor.getName()
if (!name.isSpecial()) {
return DescriptorRenderer.SOURCE_CODE.renderName(name)
}
val psiElement = DescriptorToSourceUtils.descriptorToDeclaration(descriptor)
val expression: JetExpression? = when (psiElement) {
val expression = when (psiElement) {
is JetFunctionLiteral -> psiElement.getParent() as? JetFunctionLiteralExpression
is JetObjectDeclaration -> psiElement.getParent() as? JetObjectLiteralExpression
else -> null
@@ -22,14 +22,12 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
import org.jetbrains.jet.renderer.DescriptorRenderer
import com.intellij.codeInsight.completion.InsertHandler
import org.jetbrains.jet.lang.resolve.DescriptorUtils
import org.jetbrains.jet.lang.descriptors.Modality
import org.jetbrains.jet.lang.descriptors.ClassKind
import org.jetbrains.jet.plugin.codeInsight.ImplementMethodsHandler
import com.intellij.codeInsight.lookup.LookupElementDecorator
import com.intellij.codeInsight.lookup.LookupElementPresentation
import com.intellij.codeInsight.completion.InsertionContext
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies
import org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler
import org.jetbrains.jet.plugin.completion.*
@@ -37,6 +35,7 @@ import org.jetbrains.jet.plugin.completion.handlers.CaretPosition
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.jet.lang.descriptors.Visibilities
import org.jetbrains.jet.plugin.util.makeNotNullable
import org.jetbrains.jet.lang.resolve.DescriptorUtils
class TypeInstantiationItems(val resolveSession: ResolveSessionForBodies, val visibilityFilter: (DeclarationDescriptor) -> Boolean) {
public fun addToCollection(collection: MutableCollection<LookupElement>, expectedInfos: Collection<ExpectedInfo>) {
@@ -71,7 +70,7 @@ class TypeInstantiationItems(val resolveSession: ResolveSessionForBodies, val vi
var itemText = lookupString + DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderTypeArguments(typeArgs)
val insertHandler: InsertHandler<LookupElement>
val typeText = DescriptorUtils.getFqName(classifier).toString() + DescriptorRenderer.SOURCE_CODE.renderTypeArguments(typeArgs)
val typeText = qualifiedNameForSourceCode(classifier) + DescriptorRenderer.SOURCE_CODE.renderTypeArguments(typeArgs)
if (isAbstract) {
val constructorParenthesis = if (classifier.getKind() != ClassKind.TRAIT) "()" else ""
itemText += constructorParenthesis
@@ -97,7 +96,7 @@ class TypeInstantiationItems(val resolveSession: ResolveSessionForBodies, val vi
(if (visibleConstructors.size == 0)
JetFunctionInsertHandler.NO_PARAMETERS_HANDLER
else if (visibleConstructors.size == 1)
DescriptorLookupConverter.getDefaultInsertHandler(visibleConstructors.single())!!
DescriptorLookupConverter.getDefaultInsertHandler(visibleConstructors.single())
else
JetFunctionInsertHandler.WITH_PARAMETERS_HANDLER) as JetFunctionInsertHandler
insertHandler = object : InsertHandler<LookupElement> {
@@ -0,0 +1,5 @@
package test
class `class`
fun foo(p: <caret>)
@@ -0,0 +1,5 @@
package test
class `class`
fun foo(p: `class`<caret>)
@@ -0,0 +1,5 @@
package first
fun foo() {
"".pack<caret>
}
@@ -0,0 +1,3 @@
package second
fun String.`package`() { }
@@ -0,0 +1,7 @@
package first
import second.`package`
fun foo() {
"".`package`()
}
@@ -144,4 +144,6 @@ public class BasicCompletionHandlerTest : CompletionHandlerTestBase(){
fun testNestedLocalClassCompletion() = doTest(1, "Nested", null, '\n')
fun testTypeArgOfSuper() = doTest(1, "X", null, '\n')
fun testKeywordClassName() = doTest(1, "class", null, '\n')
}
@@ -46,6 +46,10 @@ public class CompletionMultifileHandlerTest extends KotlinCompletionTestCase {
doTest();
}
public void testKeywordExtensionFunctionName() throws Exception {
doTest();
}
public void doTest() throws Exception {
String fileName = getTestName(false);