Code completion: template item for function taking lambda with 2 or more parameters (KT-4855) + better handling of type instantiation in smart completion
This commit is contained in:
@@ -54,6 +54,11 @@ public class JetCallExpression extends JetReferenceExpression implements JetCall
|
||||
return (JetTypeArgumentList) findChildByType(JetNodeTypes.TYPE_ARGUMENT_LIST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normally there should be only one (or zero) function literal arguments.
|
||||
* The returned value is a list for better handling of commonly made mistake of a function taking a lambda and returning another function.
|
||||
* Most of users can simply ignore lists of more than one element.
|
||||
*/
|
||||
@Override
|
||||
@NotNull
|
||||
public List<JetExpression> getFunctionLiteralArguments() {
|
||||
|
||||
@@ -37,14 +37,11 @@ import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
||||
import org.jetbrains.jet.plugin.JetDescriptorIconProvider;
|
||||
import org.jetbrains.jet.plugin.completion.handlers.JetClassInsertHandler;
|
||||
import org.jetbrains.jet.plugin.completion.handlers.JetJavaClassInsertHandler;
|
||||
import org.jetbrains.jet.plugin.completion.handlers.*;
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler.*;
|
||||
|
||||
public final class DescriptorLookupConverter {
|
||||
private DescriptorLookupConverter() {}
|
||||
|
||||
@@ -91,7 +88,11 @@ public final class DescriptorLookupConverter {
|
||||
typeText = DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(descriptor);
|
||||
}
|
||||
|
||||
element = element.withInsertHandler(getInsertHandler(descriptor));
|
||||
InsertHandler<LookupElement> insertHandler = getDefaultInsertHandler(descriptor);
|
||||
element = element.withInsertHandler(insertHandler);
|
||||
if (insertHandler instanceof JetFunctionInsertHandler && ((JetFunctionInsertHandler)insertHandler).getLambdaInfo() != null) {
|
||||
element.putUserData(JetCompletionCharFilter.ACCEPT_OPENING_BRACE, true);
|
||||
}
|
||||
element = element.withTailText(tailText, true).withTypeText(typeText).withPresentableText(presentableText);
|
||||
element = element.withIcon(JetDescriptorIconProvider.getIcon(descriptor, Iconable.ICON_FLAG_VISIBILITY));
|
||||
element = element.withStrikeoutness(KotlinBuiltIns.getInstance().isDeprecated(descriptor));
|
||||
@@ -100,20 +101,26 @@ public final class DescriptorLookupConverter {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static InsertHandler<LookupElement> getInsertHandler(@NotNull DeclarationDescriptor descriptor) {
|
||||
public static InsertHandler<LookupElement> getDefaultInsertHandler(@NotNull DeclarationDescriptor descriptor) {
|
||||
if (descriptor instanceof FunctionDescriptor) {
|
||||
FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
|
||||
List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
|
||||
|
||||
if (functionDescriptor.getValueParameters().isEmpty()) {
|
||||
return EMPTY_FUNCTION_HANDLER;
|
||||
if (parameters.isEmpty()) {
|
||||
return JetFunctionInsertHandler.NO_PARAMETERS_HANDLER;
|
||||
}
|
||||
|
||||
if (functionDescriptor.getValueParameters().size() == 1 &&
|
||||
KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType(functionDescriptor.getValueParameters().get(0).getType())) {
|
||||
return PARAMS_BRACES_FUNCTION_HANDLER;
|
||||
if (parameters.size() == 1) {
|
||||
JetType parameterType = parameters.get(0).getType();
|
||||
if (KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType(parameterType)) {
|
||||
int parameterCount = KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(parameterType).size();
|
||||
if (parameterCount <= 1) { // otherwise additional item with lambda template is to be added
|
||||
return new JetFunctionInsertHandler(CaretPosition.IN_BRACKETS, new GenerateLambdaInfo(parameterType, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return PARAMS_PARENTHESIS_FUNCTION_HANDLER;
|
||||
return JetFunctionInsertHandler.WITH_PARAMETERS_HANDLER;
|
||||
}
|
||||
|
||||
if (descriptor instanceof ClassDescriptor) {
|
||||
@@ -136,7 +143,7 @@ public final class DescriptorLookupConverter {
|
||||
PsiClass containingClass = ((PsiMember) declaration).getContainingClass();
|
||||
if (containingClass != null && !JavaResolverPsiUtils.isCompiledKotlinClassOrPackageClass(containingClass)) {
|
||||
if (declaration instanceof PsiMethod) {
|
||||
InsertHandler<LookupElement> handler = getInsertHandler(descriptor);
|
||||
InsertHandler<LookupElement> handler = getDefaultInsertHandler(descriptor);
|
||||
assert handler != null:
|
||||
"Special kotlin handler is expected for function: " + declaration.getText() +
|
||||
" and descriptor: " + DescriptorRenderer.FQNAMES_IN_TYPES.render(descriptor);
|
||||
|
||||
+41
-13
@@ -1,4 +1,4 @@
|
||||
package org.jetbrains.jet.plugin.completion.smart
|
||||
package org.jetbrains.jet.plugin.completion
|
||||
|
||||
import org.jetbrains.jet.lang.psi.JetExpression
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||
@@ -23,17 +23,25 @@ import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
|
||||
import java.util.HashSet
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.jet.lang.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.jet.lang.types.JetType
|
||||
|
||||
enum class Tail {
|
||||
COMMA
|
||||
PARENTHESIS
|
||||
}
|
||||
|
||||
data class ExpectedInfo(val `type`: JetType, val tail: Tail?)
|
||||
|
||||
class ExpectedInfos(val bindingContext: BindingContext, val moduleDescriptor: ModuleDescriptor) {
|
||||
public fun calculate(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
|
||||
val expectedInfos = calculateForArgument(expressionWithType)
|
||||
if (expectedInfos != null) {
|
||||
return expectedInfos
|
||||
}
|
||||
else {
|
||||
val expectedType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, expressionWithType] ?: return null
|
||||
return listOf(ExpectedInfo(expectedType, null))
|
||||
}
|
||||
val expectedInfos1 = calculateForArgument(expressionWithType)
|
||||
if (expectedInfos1 != null) return expectedInfos1
|
||||
|
||||
val expectedInfos2 = calculateForFunctionLiteralArgument(expressionWithType)
|
||||
if (expectedInfos2 != null) return expectedInfos2
|
||||
|
||||
val expectedType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, expressionWithType] ?: return null
|
||||
return listOf(ExpectedInfo(expectedType, null))
|
||||
}
|
||||
|
||||
private fun calculateForArgument(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
|
||||
@@ -42,6 +50,21 @@ class ExpectedInfos(val bindingContext: BindingContext, val moduleDescriptor: Mo
|
||||
val argumentList = argument.getParent() as JetValueArgumentList
|
||||
val argumentIndex = argumentList.getArguments().indexOf(argument)
|
||||
val callExpression = argumentList.getParent() as? JetCallExpression ?: return null
|
||||
return calculateForArgument(callExpression, argumentIndex, false)
|
||||
}
|
||||
|
||||
private fun calculateForFunctionLiteralArgument(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
|
||||
val callExpression = expressionWithType.getParent() as? JetCallExpression
|
||||
if (callExpression != null) {
|
||||
val arguments = callExpression.getFunctionLiteralArguments()
|
||||
if (arguments.size > 0 && arguments[0] == expressionWithType) {
|
||||
return calculateForArgument(callExpression, callExpression.getValueArguments().size, true)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun calculateForArgument(callExpression: JetCallExpression, argumentIndex: Int, isFunctionLiteralArgument: Boolean): Collection<ExpectedInfo>? {
|
||||
val calleeExpression = callExpression.getCalleeExpression()
|
||||
|
||||
val parent = callExpression.getParent()
|
||||
@@ -62,7 +85,7 @@ class ExpectedInfos(val bindingContext: BindingContext, val moduleDescriptor: Mo
|
||||
val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, calleeExpression] ?: return null //TODO: discuss it
|
||||
|
||||
val callResolutionContext = BasicCallResolutionContext.create(
|
||||
DelegatingBindingTrace(bindingContext, "Temporary trace for smart completion"),
|
||||
DelegatingBindingTrace(bindingContext, "Temporary trace for completion"),
|
||||
resolutionScope,
|
||||
call,
|
||||
bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, callExpression] ?: TypeUtils.NO_EXPECTED_TYPE,
|
||||
@@ -71,15 +94,20 @@ class ExpectedInfos(val bindingContext: BindingContext, val moduleDescriptor: Mo
|
||||
CheckValueArgumentsMode.ENABLED,
|
||||
CompositeExtension(listOf()),
|
||||
false).replaceCollectAllCandidates(true)
|
||||
val callResolver = InjectorForMacros(expressionWithType.getProject(), moduleDescriptor).getCallResolver()!!
|
||||
val callResolver = InjectorForMacros(callExpression.getProject(), moduleDescriptor).getCallResolver()!!
|
||||
val results: OverloadResolutionResults<FunctionDescriptor> = callResolver.resolveFunctionCall(callResolutionContext)
|
||||
|
||||
val expectedInfos = HashSet<ExpectedInfo>()
|
||||
for (candidate: ResolvedCall<FunctionDescriptor> in results.getAllCandidates()!!) {
|
||||
val parameters = candidate.getResultingDescriptor().getValueParameters()
|
||||
if (parameters.size <= argumentIndex) continue
|
||||
if (isFunctionLiteralArgument) {
|
||||
if (argumentIndex != parameters.size - 1) continue
|
||||
}
|
||||
else{
|
||||
if (parameters.size <= argumentIndex) continue
|
||||
}
|
||||
val parameterDescriptor = parameters[argumentIndex]
|
||||
val tail = if (argumentIndex == parameters.size - 1) Tail.PARENTHESIS else Tail.COMMA
|
||||
val tail = if (isFunctionLiteralArgument) null else if (argumentIndex == parameters.size - 1) Tail.PARENTHESIS else Tail.COMMA
|
||||
expectedInfos.add(ExpectedInfo(parameterDescriptor.getType(), tail))
|
||||
}
|
||||
return expectedInfos
|
||||
@@ -21,55 +21,37 @@ import com.intellij.codeInsight.lookup.Lookup
|
||||
import com.intellij.codeInsight.lookup.CharFilter.Result
|
||||
import org.jetbrains.jet.lang.psi.JetFile
|
||||
import org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler
|
||||
import com.intellij.openapi.util.Key
|
||||
|
||||
public open class JetCompletionCharFilter() : CharFilter() {
|
||||
class object {
|
||||
public val ACCEPT_OPENING_BRACE: Key<Boolean> = Key<Boolean>("JetCompletionCharFilter.ACCEPT_OPENNING_BRACE")
|
||||
}
|
||||
|
||||
public override fun acceptChar(c : Char, prefixLength : Int, lookup : Lookup) : Result? {
|
||||
if (lookup.getPsiFile() !is JetFile) return null
|
||||
|
||||
fun checkHideLookupForRangeOperator(): Result? {
|
||||
if (c == '.' && prefixLength == 0 && !lookup.isSelectionTouched()) {
|
||||
val caret = lookup.getEditor().getCaretModel().getOffset()
|
||||
if (caret > 0 && (lookup.getEditor().getDocument().getCharsSequence().charAt(caret - 1)) == '.') {
|
||||
return Result.HIDE_LOOKUP
|
||||
}
|
||||
if (c == '.' && prefixLength == 0 && !lookup.isSelectionTouched()) {
|
||||
val caret = lookup.getEditor().getCaretModel().getOffset()
|
||||
if (caret > 0 && (lookup.getEditor().getDocument().getCharsSequence().charAt(caret - 1)) == '.') {
|
||||
return Result.HIDE_LOOKUP
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun checkFinishCompletionForOpenBrace(): Result? {
|
||||
if (c == '{') {
|
||||
val currentItem = lookup.getCurrentItem()
|
||||
if (currentItem != null && (currentItem.getObject() is JetLookupObject)) {
|
||||
val lookupObject = (currentItem.getObject() as JetLookupObject)
|
||||
val descriptor = lookupObject.getDescriptor()
|
||||
|
||||
if (descriptor != null) {
|
||||
val handler = DescriptorLookupConverter.getInsertHandler(descriptor)
|
||||
if (handler == JetFunctionInsertHandler.PARAMS_BRACES_FUNCTION_HANDLER) {
|
||||
return Result.SELECT_ITEM_AND_FINISH_LOOKUP
|
||||
}
|
||||
}
|
||||
}
|
||||
if (c == '{') {
|
||||
val currentItem = lookup.getCurrentItem()
|
||||
if (currentItem != null && currentItem.getUserData(ACCEPT_OPENING_BRACE) ?: false) {
|
||||
return Result.SELECT_ITEM_AND_FINISH_LOOKUP
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun checkFinishCompletionForEqual(): Result? {
|
||||
if (c == '=') {
|
||||
val currentItem = lookup.getCurrentItem()
|
||||
if (currentItem != null && (currentItem.getObject() is KotlinNamedParametersContributor.NamedParameterLookupObject)) {
|
||||
return Result.SELECT_ITEM_AND_FINISH_LOOKUP
|
||||
}
|
||||
if (c == '=') {
|
||||
val currentItem = lookup.getCurrentItem()
|
||||
if (currentItem != null && (currentItem.getObject() is KotlinNamedParametersContributor.NamedParameterLookupObject)) {
|
||||
return Result.SELECT_ITEM_AND_FINISH_LOOKUP
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
return checkHideLookupForRangeOperator() ?:
|
||||
checkFinishCompletionForOpenBrace() ?:
|
||||
checkFinishCompletionForEqual() ?:
|
||||
null
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,14 +17,27 @@
|
||||
package org.jetbrains.jet.plugin.completion;
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionResultSet;
|
||||
import com.intellij.codeInsight.completion.InsertionContext;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator;
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Conditions;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
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.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
||||
import org.jetbrains.jet.plugin.completion.handlers.CaretPosition;
|
||||
import org.jetbrains.jet.plugin.completion.handlers.GenerateLambdaInfo;
|
||||
import org.jetbrains.jet.plugin.completion.handlers.HandlersPackage;
|
||||
import org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler;
|
||||
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class JetCompletionResultSet {
|
||||
private final ResolveSessionForBodies resolveSession;
|
||||
private final BindingContext bindingContext;
|
||||
@@ -74,12 +87,38 @@ public class JetCompletionResultSet {
|
||||
}
|
||||
|
||||
addElement(DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, descriptor));
|
||||
|
||||
// add special item for function with one argument of function type with more than one parameter
|
||||
if (descriptor instanceof FunctionDescriptor) {
|
||||
FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
|
||||
List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
|
||||
if (parameters.size() == 1) {
|
||||
final JetType parameterType = parameters.get(0).getType();
|
||||
if (KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType(parameterType)) {
|
||||
int parameterCount = KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(parameterType).size();
|
||||
if (parameterCount > 1) {
|
||||
LookupElement lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, descriptor);
|
||||
addElement(new LookupElementDecorator<LookupElement>(lookupElement) {
|
||||
@Override
|
||||
public void renderElement(LookupElementPresentation presentation) {
|
||||
super.renderElement(presentation);
|
||||
presentation.setItemText(getLookupString() + " " + HandlersPackage.buildLambdaPresentation(parameterType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleInsert(InsertionContext context) {
|
||||
new JetFunctionInsertHandler(CaretPosition.IN_BRACKETS, new GenerateLambdaInfo(parameterType, true))
|
||||
.handleInsert(context, this);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addElement(@NotNull LookupElement element) {
|
||||
if (!result.getPrefixMatcher().prefixMatches(element)) {
|
||||
return;
|
||||
}
|
||||
if (!result.getPrefixMatcher().prefixMatches(element)) return;
|
||||
|
||||
result.addElement(element);
|
||||
isSomethingAdded = true;
|
||||
|
||||
@@ -279,7 +279,7 @@ public open class JetKeywordCompletionContributor() : CompletionContributor() {
|
||||
lookupElementBuilder.withInsertHandler(JetKeywordCompletionContributor.KEYWORDS_INSERT_HANDLER)
|
||||
}
|
||||
else {
|
||||
lookupElementBuilder.withInsertHandler(JetFunctionInsertHandler.EMPTY_FUNCTION_HANDLER)
|
||||
lookupElementBuilder.withInsertHandler(JetFunctionInsertHandler.NO_PARAMETERS_HANDLER)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
package org.jetbrains.jet.plugin.completion.handlers
|
||||
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.util.Computable
|
||||
import com.intellij.codeInsight.template.TemplateManager
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.codeInsight.template.Template
|
||||
import org.jetbrains.jet.plugin.refactoring.JetNameValidator
|
||||
import org.jetbrains.jet.plugin.refactoring.JetNameSuggester
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||
import com.intellij.codeInsight.template.Expression
|
||||
import com.intellij.codeInsight.template.ExpressionContext
|
||||
import com.intellij.codeInsight.template.TextResult
|
||||
import com.intellij.codeInsight.template.Result
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import org.jetbrains.jet.lang.types.JetType
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.jet.lang.psi.JetExpression
|
||||
import org.jetbrains.jet.plugin.completion.ExpectedInfos
|
||||
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
|
||||
import org.jetbrains.jet.lang.psi.JetFile
|
||||
|
||||
fun insertLambdaTemplate(context: InsertionContext, placeholderRange: TextRange, lambdaType: JetType) {
|
||||
val explicitParameterTypes = needExplicitParameterTypes(context, placeholderRange, lambdaType)
|
||||
|
||||
// we start template later to not interfere with insertion of tail type
|
||||
val commandProcessor = CommandProcessor.getInstance()
|
||||
val commandName = commandProcessor.getCurrentCommandName()
|
||||
val commandGroupId = commandProcessor.getCurrentCommandGroupId()
|
||||
|
||||
val rangeMarker = context.getDocument().createRangeMarker(placeholderRange)
|
||||
|
||||
context.setLaterRunnable {
|
||||
commandProcessor.executeCommand(context.getProject(), {
|
||||
ApplicationManager.getApplication()!!.runWriteAction(Computable<Unit> {
|
||||
try {
|
||||
if (rangeMarker.isValid()) {
|
||||
context.getDocument().deleteString(rangeMarker.getStartOffset(), rangeMarker.getEndOffset())
|
||||
context.getEditor().getCaretModel().moveToOffset(rangeMarker.getStartOffset())
|
||||
val template = buildTemplate(lambdaType, explicitParameterTypes, context.getProject())
|
||||
TemplateManager.getInstance(context.getProject()).startTemplate(context.getEditor(), template)
|
||||
}
|
||||
}
|
||||
finally {
|
||||
rangeMarker.dispose()
|
||||
}
|
||||
})
|
||||
}, commandName, commandGroupId)
|
||||
}
|
||||
}
|
||||
|
||||
fun buildLambdaPresentation(lambdaType: JetType): String {
|
||||
val parameterTypes = functionParameterTypes(lambdaType)
|
||||
val parametersPresentation = parameterTypes.map { DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(it) }.makeString(", ")
|
||||
fun wrap(s: String) = if (parameterTypes.size != 1) "($s)" else s
|
||||
return "{ ${wrap(parametersPresentation)} -> ... }"
|
||||
}
|
||||
|
||||
private fun needExplicitParameterTypes(context: InsertionContext, placeholderRange: TextRange, lambdaType: JetType): Boolean {
|
||||
PsiDocumentManager.getInstance(context.getProject()).commitAllDocuments()
|
||||
val file = context.getFile() as JetFile
|
||||
val expression = PsiTreeUtil.findElementOfClassAtRange(file, placeholderRange.getStartOffset(), placeholderRange.getEndOffset(), javaClass<JetExpression>())
|
||||
if (expression == null) return false
|
||||
|
||||
val resolveSession = AnalyzerFacadeWithCache.getLazyResolveSessionForFile(file)
|
||||
val bindingContext = resolveSession.resolveToElement(expression)
|
||||
val expectedInfos = ExpectedInfos(bindingContext, resolveSession.getModuleDescriptor()).calculate(expression) ?: return false
|
||||
val functionTypes = expectedInfos.map { it.`type` }.filter { KotlinBuiltIns.getInstance().isExactFunctionOrExtensionFunctionType(it) }.toSet()
|
||||
if (functionTypes.size <= 1) return false
|
||||
|
||||
val lambdaParameterCount = KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(lambdaType).size
|
||||
return functionTypes.filter { KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(it).size == lambdaParameterCount}.size > 1
|
||||
}
|
||||
|
||||
private fun buildTemplate(lambdaType: JetType, explicitParameterTypes: Boolean, project: Project): Template {
|
||||
val parameterTypes = functionParameterTypes(lambdaType)
|
||||
|
||||
val nameValidator = JetNameValidator.getEmptyValidator(project) //TODO: check for names in scope
|
||||
|
||||
val useParenthesis = explicitParameterTypes || parameterTypes.size != 1
|
||||
|
||||
val manager = TemplateManager.getInstance(project)
|
||||
|
||||
val template = manager.createTemplate("", "")
|
||||
template.setToShortenLongNames(true)
|
||||
//template.setToReformat(true) //TODO
|
||||
template.addTextSegment("{ ")
|
||||
if (useParenthesis) {
|
||||
template.addTextSegment("(")
|
||||
}
|
||||
|
||||
for (i in parameterTypes.indices) {
|
||||
val parameterType = parameterTypes[i]
|
||||
if (i > 0) {
|
||||
template.addTextSegment(", ")
|
||||
}
|
||||
template.addVariable(ParameterNameExpression(JetNameSuggester.suggestNames(parameterType, nameValidator, "p")), true)
|
||||
if (explicitParameterTypes) {
|
||||
template.addTextSegment(": " + DescriptorRenderer.SOURCE_CODE.renderType(parameterType))
|
||||
}
|
||||
}
|
||||
|
||||
if (useParenthesis) {
|
||||
template.addTextSegment(")")
|
||||
}
|
||||
template.addTextSegment(" -> ")
|
||||
template.addEndVariable()
|
||||
template.addTextSegment(" }")
|
||||
return template
|
||||
}
|
||||
|
||||
private class ParameterNameExpression(val nameSuggestions: Array<String>) : Expression() {
|
||||
override fun calculateResult(context: ExpressionContext?) = TextResult(nameSuggestions[0])
|
||||
|
||||
override fun calculateQuickResult(context: ExpressionContext?): Result? = null
|
||||
|
||||
override fun calculateLookupItems(context: ExpressionContext?)
|
||||
= Array<LookupElement>(nameSuggestions.size, { LookupElementBuilder.create(nameSuggestions[it]) })
|
||||
}
|
||||
|
||||
fun functionParameterTypes(functionType: JetType): List<JetType>
|
||||
= KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(functionType).map { it.getType() }
|
||||
@@ -33,22 +33,22 @@ import org.jetbrains.jet.plugin.completion.JetLookupObject
|
||||
import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.jet.lang.psi.JetQualifiedExpression
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils
|
||||
import org.jetbrains.jet.lang.resolve.name.FqName
|
||||
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
|
||||
|
||||
public enum class CaretPosition {
|
||||
IN_BRACKETS
|
||||
AFTER_BRACKETS
|
||||
}
|
||||
public enum class BracketType {
|
||||
PARENTHESIS
|
||||
BRACES
|
||||
}
|
||||
|
||||
public class JetFunctionInsertHandler(val caretPosition : CaretPosition, val bracketType : BracketType) : InsertHandler<LookupElement> {
|
||||
public data class GenerateLambdaInfo(val lambdaType: JetType, val explicitParameters: Boolean)
|
||||
|
||||
public class JetFunctionInsertHandler(val caretPosition : CaretPosition, val lambdaInfo: GenerateLambdaInfo?) : InsertHandler<LookupElement> {
|
||||
{
|
||||
if (caretPosition == CaretPosition.AFTER_BRACKETS && bracketType == BracketType.BRACES) {
|
||||
throw IllegalArgumentException("CaretPosition.AFTER_BRACKETS with bracketType == BracketType.BRACES combination is not supported")
|
||||
if (caretPosition == CaretPosition.AFTER_BRACKETS && lambdaInfo != null) {
|
||||
throw IllegalArgumentException("CaretPosition.AFTER_BRACKETS with lambdaInfo != null combination is not supported")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,32 +67,29 @@ public class JetFunctionInsertHandler(val caretPosition : CaretPosition, val bra
|
||||
addBrackets(context, element)
|
||||
}
|
||||
|
||||
addImport(context, item)
|
||||
addImports(context, item)
|
||||
}
|
||||
|
||||
private fun addBrackets(context : InsertionContext, offsetElement : PsiElement) {
|
||||
val offset = context.getSelectionEndOffset()
|
||||
val offset = context.getTailOffset()
|
||||
val document = context.getDocument()
|
||||
val completionChar = context.getCompletionChar()
|
||||
|
||||
var documentText = document.getText()
|
||||
|
||||
val forceParenthesis = bracketType == BracketType.BRACES && completionChar == '\t' && documentText.charAt(offset) == '('
|
||||
|
||||
val braces = bracketType == BracketType.BRACES && completionChar != '(' && !forceParenthesis
|
||||
val forceParenthesis = lambdaInfo != null && completionChar == '\t' && document.getCharsSequence().charAt(offset) == '('
|
||||
val braces = lambdaInfo != null && completionChar != '(' && !forceParenthesis
|
||||
|
||||
val openingBracket = if (braces) '{' else '('
|
||||
val closingBracket = if (braces) '}' else ')'
|
||||
|
||||
var openingBracketIndex = indexOfSkippingSpace(documentText, openingBracket, offset)
|
||||
var openingBracketOffset = indexOfSkippingSpace(document, openingBracket, offset)
|
||||
var inBracketsShift = 0
|
||||
if (openingBracketIndex == -1) {
|
||||
if (openingBracketOffset == -1) {
|
||||
if (braces) {
|
||||
if (completionChar == ' ' || completionChar == '{') {
|
||||
context.setAddCompletionChar(false)
|
||||
}
|
||||
|
||||
if (isInsertSpacesInOneLineFunctionEnabled(offsetElement.getProject())) {
|
||||
if (isInsertSpacesInOneLineFunctionEnabled(context.getProject())) {
|
||||
document.insertString(offset, " { }")
|
||||
inBracketsShift = 1
|
||||
}
|
||||
@@ -104,74 +101,69 @@ public class JetFunctionInsertHandler(val caretPosition : CaretPosition, val bra
|
||||
document.insertString(offset, "()")
|
||||
}
|
||||
PsiDocumentManager.getInstance(context.getProject()).commitDocument(document)
|
||||
documentText = document.getText()
|
||||
}
|
||||
|
||||
openingBracketIndex = indexOfSkippingSpace(documentText, openingBracket, offset)
|
||||
assert (openingBracketIndex != -1) { "If there wasn't open bracket it should already have been inserted" }
|
||||
openingBracketOffset = indexOfSkippingSpace(document, openingBracket, offset)
|
||||
assert (openingBracketOffset != -1) { "If there wasn't open bracket it should already have been inserted" }
|
||||
|
||||
val closeBracketIndex = indexOfSkippingSpace(documentText, closingBracket, openingBracketIndex + 1)
|
||||
val closeBracketOffset = indexOfSkippingSpace(document, closingBracket, openingBracketOffset + 1)
|
||||
val editor = context.getEditor()
|
||||
|
||||
var forcePlaceCaretIntoParentheses : Boolean = completionChar == '('
|
||||
|
||||
if (caretPosition == CaretPosition.IN_BRACKETS || forcePlaceCaretIntoParentheses || closeBracketIndex == -1) {
|
||||
editor.getCaretModel().moveToOffset(openingBracketIndex + 1 + inBracketsShift)
|
||||
if (caretPosition == CaretPosition.IN_BRACKETS || forcePlaceCaretIntoParentheses || closeBracketOffset == -1) {
|
||||
editor.getCaretModel().moveToOffset(openingBracketOffset + 1 + inBracketsShift)
|
||||
AutoPopupController.getInstance(context.getProject())?.autoPopupParameterInfo(editor, offsetElement)
|
||||
}
|
||||
else {
|
||||
editor.getCaretModel().moveToOffset(closeBracketIndex + 1)
|
||||
editor.getCaretModel().moveToOffset(closeBracketOffset + 1)
|
||||
}
|
||||
|
||||
PsiDocumentManager.getInstance(context.getProject()).commitDocument(context.getDocument())
|
||||
|
||||
if (lambdaInfo != null && lambdaInfo.explicitParameters) {
|
||||
insertLambdaTemplate(context, TextRange(openingBracketOffset, closeBracketOffset + 1), lambdaInfo.lambdaType)
|
||||
}
|
||||
}
|
||||
|
||||
class object {
|
||||
public val EMPTY_FUNCTION_HANDLER: JetFunctionInsertHandler = JetFunctionInsertHandler(CaretPosition.AFTER_BRACKETS, BracketType.PARENTHESIS)
|
||||
public val PARAMS_PARENTHESIS_FUNCTION_HANDLER: JetFunctionInsertHandler = JetFunctionInsertHandler(CaretPosition.IN_BRACKETS, BracketType.PARENTHESIS)
|
||||
public val PARAMS_BRACES_FUNCTION_HANDLER: JetFunctionInsertHandler = JetFunctionInsertHandler(CaretPosition.IN_BRACKETS, BracketType.BRACES)
|
||||
public val NO_PARAMETERS_HANDLER: JetFunctionInsertHandler = JetFunctionInsertHandler(CaretPosition.AFTER_BRACKETS, null)
|
||||
public val WITH_PARAMETERS_HANDLER: JetFunctionInsertHandler = JetFunctionInsertHandler(CaretPosition.IN_BRACKETS, null)
|
||||
|
||||
private fun shouldAddBrackets(element : PsiElement) : Boolean {
|
||||
return PsiTreeUtil.getParentOfType(element, javaClass<JetImportDirective>()) == null
|
||||
}
|
||||
|
||||
private fun indexOfSkippingSpace(str : String, ch : Char, startIndex : Int) : Int {
|
||||
for (i in startIndex..str.length() - 1) {
|
||||
val currentChar = str.charAt(i)
|
||||
if (ch == currentChar) {
|
||||
return i
|
||||
}
|
||||
|
||||
if (!Character.isWhitespace(currentChar)) {
|
||||
return -1
|
||||
}
|
||||
private fun indexOfSkippingSpace(document: Document, ch : Char, startIndex : Int) : Int {
|
||||
val text = document.getCharsSequence()
|
||||
for (i in startIndex..text.length() - 1) {
|
||||
val currentChar = text.charAt(i)
|
||||
if (ch == currentChar) return i
|
||||
if (!Character.isWhitespace(currentChar)) return -1
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
private open fun isInsertSpacesInOneLineFunctionEnabled(project : Project) : Boolean {
|
||||
val settings = CodeStyleSettingsManager.getSettings(project)
|
||||
val jetSettings = settings.getCustomSettings(javaClass<JetCodeStyleSettings>())!!
|
||||
return jetSettings.INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD
|
||||
}
|
||||
private open fun isInsertSpacesInOneLineFunctionEnabled(project : Project)
|
||||
= CodeStyleSettingsManager.getSettings(project)
|
||||
.getCustomSettings(javaClass<JetCodeStyleSettings>())!!.INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD
|
||||
|
||||
private open fun addImport(context : InsertionContext, item : LookupElement) {
|
||||
private open fun addImports(context : InsertionContext, item : LookupElement) {
|
||||
ApplicationManager.getApplication()?.runReadAction { () : Unit ->
|
||||
val startOffset = context.getStartOffset()
|
||||
val element = context.getFile().findElementAt(startOffset)
|
||||
|
||||
if (element == null) return@runReadAction
|
||||
|
||||
if (context.getFile() is JetFile && item.getObject() is JetLookupObject) {
|
||||
val file = context.getFile()
|
||||
if (file is JetFile && item.getObject() is JetLookupObject) {
|
||||
val descriptor = (item.getObject() as JetLookupObject).getDescriptor()
|
||||
|
||||
if (descriptor is SimpleFunctionDescriptor) {
|
||||
val file = context.getFile() as JetFile
|
||||
val functionDescriptor = descriptor as SimpleFunctionDescriptor
|
||||
|
||||
if (PsiTreeUtil.getParentOfType(element, javaClass<JetQualifiedExpression>()) != null &&
|
||||
functionDescriptor.getReceiverParameter() == null) {
|
||||
functionDescriptor.getReceiverParameter() == null) {
|
||||
return@runReadAction
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.jetbrains.jet.plugin.completion.handlers
|
||||
import com.intellij.codeInsight.completion.*
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.openapi.editor.event.*
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.codeInsight.AutoPopupController
|
||||
|
||||
@@ -46,7 +45,7 @@ abstract class WithTailInsertHandlerBase : InsertHandler<LookupElement> {
|
||||
class WithTailCharInsertHandler(val tailChar: Char, val spaceAfter: Boolean) : WithTailInsertHandlerBase() {
|
||||
override fun insertTail(context: InsertionContext, offset: Int, moveCaret: Boolean) {
|
||||
val document = context.getDocument()
|
||||
fun isCharAt(offset: Int, c: Char) = offset < document.getTextLength() && document.getText(TextRange(offset, offset + 1))[0] == c
|
||||
fun isCharAt(offset: Int, c: Char) = offset < document.getTextLength() && document.getCharsSequence().charAt(offset) == c
|
||||
|
||||
if (isCharAt(offset, tailChar)) {
|
||||
document.deleteString(offset, offset + 1)
|
||||
|
||||
@@ -3,133 +3,45 @@ package org.jetbrains.jet.plugin.completion.smart
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||
import org.jetbrains.jet.lang.types.JetType
|
||||
import org.jetbrains.jet.plugin.completion.handlers.functionParameterTypes
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||
import org.jetbrains.jet.plugin.refactoring.JetNameValidator
|
||||
import org.jetbrains.jet.plugin.refactoring.JetNameSuggester
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.codeInsight.completion.InsertHandler
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.template.*
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.util.Computable
|
||||
import org.jetbrains.jet.plugin.completion.handlers.insertLambdaTemplate
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import org.jetbrains.jet.plugin.completion.JetCompletionCharFilter
|
||||
import org.jetbrains.jet.plugin.completion.ExpectedInfo
|
||||
import org.jetbrains.jet.plugin.completion.handlers.buildLambdaPresentation
|
||||
|
||||
class LambdaItems(val project: Project) {
|
||||
object LambdaItems {
|
||||
public fun addToCollection(collection: MutableCollection<LookupElement>, functionExpectedInfos: Collection<ExpectedInfo>) {
|
||||
val distinctTypes = functionExpectedInfos.map { it.`type` }.toSet()
|
||||
|
||||
fun createLookupElement(lookupString: String, textBeforeCaret: String, textAfterCaret: String, shortenRefs: Boolean)
|
||||
= LookupElementBuilder.create(lookupString)
|
||||
.withInsertHandler(ArtificialElementInsertHandler(textBeforeCaret, textAfterCaret, shortenRefs))
|
||||
.suppressAutoInsertion()
|
||||
|
||||
val singleType = if (distinctTypes.size == 1) distinctTypes.single() else null
|
||||
val singleSignatureLength = singleType?.let { KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(it).size }
|
||||
val offerNoParametersLambda = singleSignatureLength == 0 || singleSignatureLength == 1
|
||||
if (offerNoParametersLambda) {
|
||||
val lookupElement = createLookupElement("{...}", "{ ", " }", shortenRefs = false)
|
||||
collection.add(lookupElement.addTail(functionExpectedInfos))
|
||||
val lookupElement = LookupElementBuilder.create("{...}")
|
||||
.withInsertHandler(ArtificialElementInsertHandler("{ ", " }", false))
|
||||
.suppressAutoInsertion()
|
||||
.addTail(functionExpectedInfos)
|
||||
lookupElement.putUserData(JetCompletionCharFilter.ACCEPT_OPENING_BRACE, true)
|
||||
collection.add(lookupElement)
|
||||
}
|
||||
|
||||
if (singleSignatureLength != 0) {
|
||||
for (functionType in distinctTypes) {
|
||||
val parameterTypes = functionParameterTypes(functionType)
|
||||
val parametersPresentation = parameterTypes.map { DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(it) }.makeString(", ")
|
||||
val useExplicitTypes = distinctTypes.stream().any { it != functionType && functionParameterTypes(it).size == parameterTypes.size }
|
||||
|
||||
fun wrap(s: String) = if (useParenthesis(useExplicitTypes, parameterTypes)) "($s)" else s
|
||||
val lookupString = "{ ${wrap(parametersPresentation)} -> ... }"
|
||||
|
||||
val lookupString = buildLambdaPresentation(functionType)
|
||||
val lookupElement = LookupElementBuilder.create(lookupString)
|
||||
.withInsertHandler(LambdaInsertHandler(functionType, useExplicitTypes))
|
||||
.withInsertHandler({ (context, lookupElement) ->
|
||||
val offset = context.getStartOffset()
|
||||
val placeholder = "{}"
|
||||
context.getDocument().replaceString(offset, context.getTailOffset(), placeholder)
|
||||
insertLambdaTemplate(context, TextRange(offset, offset + placeholder.length), functionType)
|
||||
})
|
||||
.suppressAutoInsertion()
|
||||
collection.add(lookupElement.addTail(functionExpectedInfos.filter { it.`type` == functionType }))
|
||||
.addTail(functionExpectedInfos.filter { it.`type` == functionType })
|
||||
lookupElement.putUserData(JetCompletionCharFilter.ACCEPT_OPENING_BRACE, true)
|
||||
collection.add(lookupElement)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun useParenthesis(useExplicitTypes: Boolean, parameterTypes: List<JetType>) = useExplicitTypes || parameterTypes.size != 1
|
||||
|
||||
private inner class LambdaInsertHandler(val functionType: JetType, val useExplicitTypes: Boolean) : InsertHandler<LookupElement> {
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
val document = context.getDocument()
|
||||
val editor = context.getEditor()
|
||||
val offset = context.getStartOffset()
|
||||
val placeholder = "{}"
|
||||
document.replaceString(offset, context.getTailOffset(), placeholder)
|
||||
val rangeMarker = document.createRangeMarker(offset, offset + placeholder.length)
|
||||
|
||||
// we start template later to not interfere with insertion of tail type
|
||||
val commandProcessor = CommandProcessor.getInstance()
|
||||
val commandName = commandProcessor.getCurrentCommandName()
|
||||
val commandGroupId = commandProcessor.getCurrentCommandGroupId()
|
||||
context.setLaterRunnable {
|
||||
commandProcessor.executeCommand(project, {
|
||||
ApplicationManager.getApplication()!!.runWriteAction(Computable<Unit> {
|
||||
try {
|
||||
if (rangeMarker.isValid()) {
|
||||
document.deleteString(rangeMarker.getStartOffset(), rangeMarker.getEndOffset())
|
||||
editor.getCaretModel().moveToOffset(rangeMarker.getStartOffset())
|
||||
TemplateManager.getInstance(project).startTemplate(editor, buildTemplate())
|
||||
}
|
||||
}
|
||||
finally {
|
||||
rangeMarker.dispose()
|
||||
}
|
||||
})
|
||||
}, commandName, commandGroupId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildTemplate(): Template {
|
||||
val parameterTypes = functionParameterTypes(functionType)
|
||||
|
||||
val nameValidator = JetNameValidator.getEmptyValidator(project) //TODO: check for names in scope
|
||||
|
||||
val useParenthesis = useParenthesis(useExplicitTypes, parameterTypes)
|
||||
|
||||
val manager = TemplateManager.getInstance(project)
|
||||
|
||||
val template = manager.createTemplate("", "")
|
||||
template.setToShortenLongNames(true)
|
||||
//template.setToReformat(true) //TODO
|
||||
template.addTextSegment("{ ")
|
||||
if (useParenthesis) {
|
||||
template.addTextSegment("(")
|
||||
}
|
||||
|
||||
for (i in parameterTypes.indices) {
|
||||
val parameterType = parameterTypes[i]
|
||||
if (i > 0) {
|
||||
template.addTextSegment(", ")
|
||||
}
|
||||
template.addVariable(ParameterNameExpression(JetNameSuggester.suggestNames(parameterType, nameValidator, "p")), true)
|
||||
if (useExplicitTypes) {
|
||||
template.addTextSegment(": " + DescriptorRenderer.SOURCE_CODE.renderType(parameterType))
|
||||
}
|
||||
}
|
||||
|
||||
if (useParenthesis) {
|
||||
template.addTextSegment(")")
|
||||
}
|
||||
template.addTextSegment(" -> ")
|
||||
template.addEndVariable()
|
||||
template.addTextSegment(" }")
|
||||
return template
|
||||
}
|
||||
}
|
||||
|
||||
private fun functionParameterTypes(functionType: JetType): List<JetType>
|
||||
= KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(functionType).map { it.getType() }
|
||||
|
||||
private class ParameterNameExpression(val nameSuggestions: Array<String>) : Expression() {
|
||||
override fun calculateResult(context: ExpressionContext?) = TextResult(nameSuggestions[0])
|
||||
|
||||
override fun calculateQuickResult(context: ExpressionContext?): Result? = null
|
||||
|
||||
override fun calculateLookupItems(context: ExpressionContext?)
|
||||
= Array<LookupElement>(nameSuggestions.size, { LookupElementBuilder.create(nameSuggestions[it]) })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,16 +9,9 @@ import org.jetbrains.jet.lang.types.*
|
||||
import com.intellij.codeInsight.lookup.*
|
||||
import com.intellij.codeInsight.completion.*
|
||||
import java.util.*
|
||||
import org.jetbrains.jet.plugin.completion.DescriptorLookupConverter
|
||||
import org.jetbrains.jet.plugin.completion.*
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||
|
||||
enum class Tail {
|
||||
COMMA
|
||||
PARENTHESIS
|
||||
}
|
||||
|
||||
data class ExpectedInfo(val `type`: JetType, val tail: Tail?)
|
||||
|
||||
class SmartCompletion(val expression: JetSimpleNameExpression,
|
||||
val resolveSession: ResolveSessionForBodies,
|
||||
val visibilityFilter: (DeclarationDescriptor) -> Boolean) {
|
||||
@@ -78,7 +71,7 @@ class SmartCompletion(val expression: JetSimpleNameExpression,
|
||||
|
||||
ThisItems(bindingContext).addToCollection(result, expressionWithType, expectedInfos)
|
||||
|
||||
LambdaItems(project).addToCollection(result, functionExpectedInfos)
|
||||
LambdaItems.addToCollection(result, functionExpectedInfos)
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.intellij.codeInsight.completion.InsertionContext
|
||||
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||
import org.jetbrains.jet.lang.psi.JetExpression
|
||||
import org.jetbrains.jet.plugin.completion.ExpectedInfo
|
||||
import java.util.ArrayList
|
||||
|
||||
// adds java static members, enum members and members from class object
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.jetbrains.jet.lang.psi.JetCallExpression
|
||||
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||
import org.jetbrains.jet.lang.types.TypeUtils
|
||||
import org.jetbrains.jet.plugin.completion.ExpectedInfo
|
||||
|
||||
class ThisItems(val bindingContext: BindingContext) {
|
||||
public fun addToCollection(collection: MutableCollection<LookupElement>, context: JetExpression, expectedInfos: Collection<ExpectedInfo>) {
|
||||
|
||||
@@ -13,13 +13,13 @@ import org.jetbrains.jet.lang.descriptors.Modality
|
||||
import org.jetbrains.jet.lang.descriptors.ClassKind
|
||||
import org.jetbrains.jet.plugin.codeInsight.ImplementMethodsHandler
|
||||
import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.jet.plugin.completion.handlers.CaretPosition
|
||||
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 com.intellij.codeInsight.AutoPopupController
|
||||
import org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler
|
||||
import org.jetbrains.jet.plugin.completion.*
|
||||
|
||||
class TypeInstantiationItems(val bindingContext: BindingContext, val resolveSession: ResolveSessionForBodies) {
|
||||
public fun addToCollection(collection: MutableCollection<LookupElement>, expectedInfos: Collection<ExpectedInfo>) {
|
||||
@@ -65,26 +65,28 @@ class TypeInstantiationItems(val bindingContext: BindingContext, val resolveSess
|
||||
lookupElement = lookupElement.suppressAutoInsertion()
|
||||
}
|
||||
else {
|
||||
//TODO: when constructor has one parameter of lambda type with more than one parameter, generate special additional item
|
||||
itemText += "()"
|
||||
val constructors: Collection<ConstructorDescriptor> = classifier.getConstructors()
|
||||
val caretPosition =
|
||||
val constructors = classifier.getConstructors()
|
||||
val baseInsertHandler =
|
||||
if (constructors.size == 0)
|
||||
CaretPosition.AFTER_BRACKETS
|
||||
JetFunctionInsertHandler.NO_PARAMETERS_HANDLER
|
||||
else if (constructors.size == 1)
|
||||
if (constructors.first().getValueParameters().isEmpty()) CaretPosition.AFTER_BRACKETS else CaretPosition.IN_BRACKETS
|
||||
DescriptorLookupConverter.getDefaultInsertHandler(constructors.first())!!
|
||||
else
|
||||
CaretPosition.IN_BRACKETS
|
||||
insertHandler = InsertHandler<LookupElement> {(context, item) ->
|
||||
val editor = context.getEditor()
|
||||
val startOffset = context.getStartOffset()
|
||||
val text = typeText + "()"
|
||||
editor.getDocument().replaceString(startOffset, context.getTailOffset(), text)
|
||||
val endOffset = startOffset + text.length
|
||||
editor.getCaretModel().moveToOffset(if (caretPosition == CaretPosition.IN_BRACKETS) endOffset - 1 else endOffset)
|
||||
JetFunctionInsertHandler.WITH_PARAMETERS_HANDLER
|
||||
insertHandler = object : InsertHandler<LookupElement> {
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
context.getDocument().replaceString(context.getStartOffset(), context.getTailOffset(), typeText)
|
||||
context.setTailOffset(context.getStartOffset() + typeText.length)
|
||||
|
||||
shortenReferences(context, startOffset, endOffset)
|
||||
baseInsertHandler.handleInsert(context, item)
|
||||
|
||||
AutoPopupController.getInstance(context.getProject())?.autoPopupParameterInfo(editor, null)
|
||||
shortenReferences(context, context.getStartOffset(), context.getTailOffset())
|
||||
}
|
||||
}
|
||||
if ((baseInsertHandler as JetFunctionInsertHandler).lambdaInfo != null) {
|
||||
lookupElement.putUserData(JetCompletionCharFilter.ACCEPT_OPENING_BRACE, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.jetbrains.jet.lang.types.checker.JetTypeChecker
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||
import org.jetbrains.jet.plugin.completion.handlers.WithTailStringInsertHandler
|
||||
import java.util.ArrayList
|
||||
import org.jetbrains.jet.plugin.completion.*
|
||||
|
||||
class ArtificialElementInsertHandler(
|
||||
val textBeforeCaret: String, val textAfterCaret: String, val shortenRefs: Boolean) : InsertHandler<LookupElement>{
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fun foo(p: (String, Char) -> Unit){}
|
||||
|
||||
fun test() {
|
||||
fo<caret>
|
||||
}
|
||||
|
||||
// EXIST: { lookupString:"foo", itemText: "foo(p: (String, Char) -> Unit)", typeText:"Unit" }
|
||||
// EXIST: { lookupString:"foo", itemText: "foo { (String, Char) -> ... }", typeText:"Unit" }
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(p : (String, Char) -> Boolean){}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
fo<caret>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(p : (String, Char) -> Boolean){}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
foo { (s, c) -> <caret> }
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(p : (String, Char) -> Boolean){}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
fo<caret>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(p : (String, Char) -> Boolean){}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
foo(<caret>)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(p : (String, Char) -> Boolean){}
|
||||
fun foo(p : (String, Boolean) -> Boolean){}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
fo<caret>
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(p : (String, Char) -> Boolean){}
|
||||
fun foo(p : (String, Boolean) -> Boolean){}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
foo { (s: String, c: Char) -> <caret> }
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
class Foo(p : () -> Unit)
|
||||
|
||||
var a : Foo = <caret>
|
||||
@@ -0,0 +1,3 @@
|
||||
class Foo(p : () -> Unit)
|
||||
|
||||
var a : Foo = Foo { <caret> }
|
||||
@@ -0,0 +1,3 @@
|
||||
class Foo(p : (String) -> Unit)
|
||||
|
||||
var a : Foo = <caret>
|
||||
@@ -0,0 +1,3 @@
|
||||
class Foo(p : (String) -> Unit)
|
||||
|
||||
var a : Foo = Foo { <caret> }
|
||||
@@ -5,4 +5,4 @@ fun bar() {
|
||||
foo(<caret>)
|
||||
}
|
||||
|
||||
// ELEMENT: "{ (Date) -> ... }"
|
||||
// ELEMENT: "{ Date -> ... }"
|
||||
|
||||
@@ -7,4 +7,4 @@ fun bar() {
|
||||
foo({ (date: Date) -> <caret> })
|
||||
}
|
||||
|
||||
// ELEMENT: "{ (Date) -> ... }"
|
||||
// ELEMENT: "{ Date -> ... }"
|
||||
|
||||
@@ -6,5 +6,5 @@ fun bar() {
|
||||
}
|
||||
|
||||
// ABSENT: "{...}"
|
||||
// EXIST: "{ (String) -> ... }"
|
||||
// EXIST: "{ (Int) -> ... }"
|
||||
// EXIST: "{ String -> ... }"
|
||||
// EXIST: "{ Int -> ... }"
|
||||
|
||||
@@ -189,6 +189,11 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
|
||||
doTest("idea/testData/completion/basic/common/FunctionCompletionFormatting.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("HigherOrderFunction1.kt")
|
||||
public void testHigherOrderFunction1() throws Exception {
|
||||
doTest("idea/testData/completion/basic/common/HigherOrderFunction1.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("InCallExpression.kt")
|
||||
public void testInCallExpression() throws Exception {
|
||||
doTest("idea/testData/completion/basic/common/InCallExpression.kt");
|
||||
|
||||
@@ -16,17 +16,14 @@
|
||||
|
||||
package org.jetbrains.jet.completion;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
import org.jetbrains.jet.JetTestUtils;
|
||||
import org.jetbrains.jet.test.InnerTestClasses;
|
||||
import org.jetbrains.jet.test.TestMetadata;
|
||||
|
||||
import org.jetbrains.jet.completion.AbstractJvmBasicCompletionTest;
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@@ -189,6 +186,11 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
||||
doTest("idea/testData/completion/basic/common/FunctionCompletionFormatting.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("HigherOrderFunction1.kt")
|
||||
public void testHigherOrderFunction1() throws Exception {
|
||||
doTest("idea/testData/completion/basic/common/HigherOrderFunction1.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("InCallExpression.kt")
|
||||
public void testInCallExpression() throws Exception {
|
||||
doTest("idea/testData/completion/basic/common/InCallExpression.kt");
|
||||
|
||||
@@ -46,6 +46,12 @@ public class BasicCompletionHandlerTest : CompletionHandlerTestBase(){
|
||||
|
||||
fun testHigherOrderFunctionWithArg() = doTest(2, "filterNot", null, '\n')
|
||||
|
||||
fun testHigherOrderFunctionWithArgs1() = doTest(1, "foo", "foo { (String, Char) -> ... }", null, '\n')
|
||||
|
||||
fun testHigherOrderFunctionWithArgs2() = doTest(1, "foo", "foo(p: (String, Char) -> Boolean)", null, '\n')
|
||||
|
||||
fun testHigherOrderFunctionWithArgs3() = doTest(1, "foo", "foo { (String, Char) -> ... }", null, '\n')
|
||||
|
||||
fun testForceParenthesisForTabChar() = doTest(0, "some", null, '\t')
|
||||
|
||||
fun testTabInsertAtTheFileEnd() = doTest(0, "vvvvv", null, '\t')
|
||||
|
||||
+12
-8
@@ -16,17 +16,11 @@
|
||||
|
||||
package org.jetbrains.jet.completion.handlers;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestSuite;
|
||||
import org.jetbrains.jet.JetTestUtils;
|
||||
import org.jetbrains.jet.test.TestMetadata;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
import org.jetbrains.jet.JetTestUtils;
|
||||
import org.jetbrains.jet.test.InnerTestClasses;
|
||||
import org.jetbrains.jet.test.TestMetadata;
|
||||
|
||||
import org.jetbrains.jet.completion.handlers.AbstractSmartCompletionHandlerTest;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@@ -151,6 +145,16 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
|
||||
doTest("idea/testData/completion/handlers/smart/ConstructorInsertsImport2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ConstructorWithLambdaParameter1.kt")
|
||||
public void testConstructorWithLambdaParameter1() throws Exception {
|
||||
doTest("idea/testData/completion/handlers/smart/ConstructorWithLambdaParameter1.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ConstructorWithLambdaParameter2.kt")
|
||||
public void testConstructorWithLambdaParameter2() throws Exception {
|
||||
doTest("idea/testData/completion/handlers/smart/ConstructorWithLambdaParameter2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ConstructorWithParameters.kt")
|
||||
public void testConstructorWithParameters() throws Exception {
|
||||
doTest("idea/testData/completion/handlers/smart/ConstructorWithParameters.kt");
|
||||
|
||||
Reference in New Issue
Block a user