Converted JetFunctionParameterInfoHandler to Kotlin
This commit is contained in:
-466
@@ -1,466 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.parameterInfo;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.intellij.codeInsight.CodeInsightBundle;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.parameterInfo.*;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.ui.Gray;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
|
||||
import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper;
|
||||
import org.jetbrains.kotlin.idea.core.CorePackage;
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade;
|
||||
import org.jetbrains.kotlin.lexer.JetTokens;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage;
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage;
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode;
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude;
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter;
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScope;
|
||||
import org.jetbrains.kotlin.types.JetType;
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
public class JetFunctionParameterInfoHandler implements ParameterInfoHandlerWithTabActionSupport<
|
||||
JetValueArgumentList,
|
||||
Pair<? extends FunctionDescriptor, ResolutionFacade>,
|
||||
JetValueArgument>
|
||||
{
|
||||
public final static Color GREEN_BACKGROUND = new JBColor(new Color(231, 254, 234), Gray._100);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JetValueArgument[] getActualParameters(@NotNull JetValueArgumentList arguments) {
|
||||
List<JetValueArgument> argumentList = arguments.getArguments();
|
||||
return argumentList.toArray(new JetValueArgument[argumentList.size()]);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public IElementType getActualParameterDelimiterType() {
|
||||
return JetTokens.COMMA;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public IElementType getActualParametersRBraceType() {
|
||||
return JetTokens.RBRACE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Class> getArgumentListAllowedParentClasses() {
|
||||
return Collections.singleton((Class) JetCallElement.class);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<? extends Class> getArgListStopSearchClasses() {
|
||||
return Collections.singleton(JetFunction.class);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Class<JetValueArgumentList> getArgumentListClass() {
|
||||
return JetValueArgumentList.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldShowInLookup() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getParametersForLookup(LookupElement item, ParameterInfoContext context) {
|
||||
return ArrayUtil.EMPTY_OBJECT_ARRAY; //todo: ?
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object[] getParametersForDocumentation(Pair<? extends FunctionDescriptor, ResolutionFacade> p, ParameterInfoContext context) {
|
||||
return ArrayUtil.EMPTY_OBJECT_ARRAY; //todo: ?
|
||||
}
|
||||
|
||||
@Override
|
||||
public JetValueArgumentList findElementForParameterInfo(@NotNull CreateParameterInfoContext context) {
|
||||
return findCall(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showParameterInfo(@NotNull JetValueArgumentList element, @NotNull CreateParameterInfoContext context) {
|
||||
context.showHint(element, element.getTextRange().getStartOffset(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JetValueArgumentList findElementForUpdatingParameterInfo(@NotNull UpdateParameterInfoContext context) {
|
||||
return findCallAndUpdateContext(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateParameterInfo(@NotNull JetValueArgumentList argumentList, @NotNull UpdateParameterInfoContext context) {
|
||||
if (context.getParameterOwner() != argumentList) context.removeHint();
|
||||
int offset = context.getOffset();
|
||||
ASTNode child = argumentList.getNode().getFirstChildNode();
|
||||
int i = 0;
|
||||
while (child != null && child.getStartOffset() < offset) {
|
||||
if (child.getElementType() == JetTokens.COMMA) ++i;
|
||||
child = child.getTreeNext();
|
||||
}
|
||||
context.setCurrentParameter(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getParameterCloseChars() {
|
||||
return ParameterInfoUtils.DEFAULT_PARAMETER_CLOSE_CHARS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tracksParameterIndex() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String renderParameter(ValueParameterDescriptor parameter, boolean named) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (named) builder.append("[");
|
||||
if (parameter.getVarargElementType() != null) {
|
||||
builder.append("vararg ");
|
||||
}
|
||||
builder.append(parameter.getName())
|
||||
.append(": ")
|
||||
.append(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(getActualParameterType(parameter)));
|
||||
if (DescriptorUtilPackage.hasDefaultValue(parameter)) {
|
||||
PsiElement parameterDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(parameter);
|
||||
builder.append(" = ").append(getDefaultExpressionString(parameterDeclaration));
|
||||
}
|
||||
if (named) builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static String getDefaultExpressionString(@Nullable PsiElement parameterDeclaration) {
|
||||
if (parameterDeclaration instanceof JetParameter) {
|
||||
JetExpression defaultValue = ((JetParameter) parameterDeclaration).getDefaultValue();
|
||||
if (defaultValue != null) {
|
||||
String defaultExpression = defaultValue.getText();
|
||||
if (defaultExpression.length() <= 32) {
|
||||
return defaultExpression;
|
||||
}
|
||||
|
||||
if (defaultValue instanceof JetConstantExpression || defaultValue instanceof JetStringTemplateExpression) {
|
||||
if (defaultExpression.startsWith("\"")) {
|
||||
return "\"...\"";
|
||||
}
|
||||
else if (defaultExpression.startsWith("\'")) {
|
||||
return "\'...\'";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "...";
|
||||
}
|
||||
|
||||
private static JetType getActualParameterType(ValueParameterDescriptor descriptor) {
|
||||
JetType paramType = descriptor.getType();
|
||||
if (descriptor.getVarargElementType() != null) paramType = descriptor.getVarargElementType();
|
||||
return paramType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUI(Pair<? extends FunctionDescriptor, ResolutionFacade> itemToShow, @NotNull ParameterInfoUIContext context) {
|
||||
//todo: when we will have ability to pass Array as vararg, implement such feature here too?
|
||||
if (context == null || context.getParameterOwner() == null || !context.getParameterOwner().isValid()) {
|
||||
context.setUIComponentEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
PsiElement parameterOwner = context.getParameterOwner();
|
||||
if (!(parameterOwner instanceof JetValueArgumentList)) {
|
||||
context.setUIComponentEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
JetValueArgumentList argumentList = (JetValueArgumentList) parameterOwner;
|
||||
|
||||
FunctionDescriptor functionDescriptor = itemToShow.first;
|
||||
ResolutionFacade resolutionFacade = itemToShow.second;
|
||||
|
||||
List<ValueParameterDescriptor> valueParameters = functionDescriptor.getValueParameters();
|
||||
List<JetValueArgument> valueArguments = argumentList.getArguments();
|
||||
|
||||
int currentParameterIndex = context.getCurrentParameterIndex();
|
||||
int boldStartOffset = -1;
|
||||
int boldEndOffset = -1;
|
||||
boolean isGrey = false;
|
||||
boolean isDeprecated = KotlinBuiltIns.isDeprecated(functionDescriptor);
|
||||
|
||||
boolean[] usedIndexes = new boolean[valueParameters.size()];
|
||||
Arrays.fill(usedIndexes, false);
|
||||
|
||||
boolean namedMode = false;
|
||||
|
||||
if (!isIndexValid(valueParameters, currentParameterIndex)) {
|
||||
isGrey = true;
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
PsiElement owner = context.getParameterOwner();
|
||||
BindingContext bindingContext = resolutionFacade.analyze((JetElement) owner, BodyResolveMode.FULL);
|
||||
|
||||
for (int i = 0; i < valueParameters.size(); ++i) {
|
||||
if (i != 0) {
|
||||
builder.append(", ");
|
||||
}
|
||||
|
||||
boolean highlightParameter = i == currentParameterIndex ||
|
||||
(!namedMode && i < currentParameterIndex && Iterables.getLast(valueParameters).getVarargElementType() != null);
|
||||
|
||||
if (highlightParameter) {
|
||||
boldStartOffset = builder.length();
|
||||
}
|
||||
|
||||
if (!namedMode) {
|
||||
if (valueArguments.size() > i) {
|
||||
JetValueArgument argument = valueArguments.get(i);
|
||||
if (argument.isNamed()) {
|
||||
namedMode = true;
|
||||
}
|
||||
else {
|
||||
ValueParameterDescriptor param = valueParameters.get(i);
|
||||
builder.append(renderParameter(param, false));
|
||||
if (i <= currentParameterIndex && !isArgumentTypeValid(bindingContext, argument, param)) {
|
||||
isGrey = true;
|
||||
}
|
||||
usedIndexes[i] = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ValueParameterDescriptor param = valueParameters.get(i);
|
||||
builder.append(renderParameter(param, false));
|
||||
}
|
||||
}
|
||||
|
||||
if (namedMode) {
|
||||
boolean takeAnyArgument = true;
|
||||
if (valueArguments.size() > i) {
|
||||
JetValueArgument argument = valueArguments.get(i);
|
||||
if (argument.isNamed()) {
|
||||
for (int j = 0; j < valueParameters.size(); ++j) {
|
||||
JetSimpleNameExpression referenceExpression = argument.getArgumentName().getReferenceExpression();
|
||||
ValueParameterDescriptor param = valueParameters.get(j);
|
||||
if (referenceExpression != null && !usedIndexes[j] && param.getName().equals(referenceExpression.getReferencedNameAsName())) {
|
||||
takeAnyArgument = false;
|
||||
usedIndexes[j] = true;
|
||||
builder.append(renderParameter(param, true));
|
||||
if (i < currentParameterIndex && !isArgumentTypeValid(bindingContext, argument, param)) {
|
||||
isGrey = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (takeAnyArgument) {
|
||||
if (i < currentParameterIndex) {
|
||||
isGrey = true;
|
||||
}
|
||||
|
||||
for (int j = 0; j < valueParameters.size(); ++j) {
|
||||
ValueParameterDescriptor param = valueParameters.get(j);
|
||||
if (!usedIndexes[j]) {
|
||||
usedIndexes[j] = true;
|
||||
builder.append(renderParameter(param, true));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (highlightParameter) {
|
||||
boldEndOffset = builder.length();
|
||||
}
|
||||
}
|
||||
|
||||
if (valueParameters.size() == 0) {
|
||||
builder.append(CodeInsightBundle.message("parameter.info.no.parameters"));
|
||||
}
|
||||
|
||||
assert !builder.toString().isEmpty() : "A message about 'no parameters' or some parameters should be present: " + functionDescriptor;
|
||||
|
||||
Color color = isResolvedToDescriptor(argumentList, functionDescriptor, bindingContext) ? GREEN_BACKGROUND : context.getDefaultParameterColor();
|
||||
context.setupUIComponentPresentation(builder.toString(), boldStartOffset, boldEndOffset, isGrey, isDeprecated, false, color);
|
||||
}
|
||||
|
||||
private static boolean isArgumentTypeValid(BindingContext bindingContext, JetValueArgument argument, ValueParameterDescriptor param) {
|
||||
if (argument.getArgumentExpression() != null) {
|
||||
JetType paramType = getActualParameterType(param);
|
||||
JetType exprType = bindingContext.getType(argument.getArgumentExpression());
|
||||
return exprType == null || JetTypeChecker.DEFAULT.isSubtypeOf(exprType, paramType);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isIndexValid(List<ValueParameterDescriptor> valueParameters, int index) {
|
||||
// Index is within range of parameters or last parameter is vararg
|
||||
return index < valueParameters.size() ||
|
||||
(valueParameters.size() > 0 && Iterables.getLast(valueParameters).getVarargElementType() != null);
|
||||
}
|
||||
|
||||
private static Boolean isResolvedToDescriptor(
|
||||
JetValueArgumentList argumentList,
|
||||
FunctionDescriptor functionDescriptor,
|
||||
BindingContext bindingContext
|
||||
) {
|
||||
JetSimpleNameExpression callNameExpression = getCallSimpleNameExpression(argumentList);
|
||||
if (callNameExpression != null) {
|
||||
DeclarationDescriptor declarationDescriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, callNameExpression);
|
||||
if (declarationDescriptor != null) {
|
||||
if (declarationDescriptor == functionDescriptor) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static JetValueArgumentList findCall(CreateParameterInfoContext context) {
|
||||
//todo: calls to this constructors, when we will have auxiliary constructors
|
||||
PsiFile file = context.getFile();
|
||||
if (!(file instanceof JetFile)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JetValueArgumentList argumentList = PsiTreeUtil.getParentOfType(file.findElementAt(context.getOffset()), JetValueArgumentList.class);
|
||||
if (argumentList == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final JetSimpleNameExpression callNameExpression = getCallSimpleNameExpression(argumentList);
|
||||
if (callNameExpression == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiReference[] references = callNameExpression.getReferences();
|
||||
if (references.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ResolutionFacade resolutionFacade = ResolutionUtils.getResolutionFacade(callNameExpression.getContainingJetFile());
|
||||
final BindingContext bindingContext = resolutionFacade.analyze(callNameExpression, BodyResolveMode.FULL);
|
||||
|
||||
JetScope scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, callNameExpression);
|
||||
final DeclarationDescriptor placeDescriptor;
|
||||
if (scope != null) {
|
||||
placeDescriptor = scope.getContainingDeclaration();
|
||||
}
|
||||
else {
|
||||
placeDescriptor = null;
|
||||
}
|
||||
Function1<DeclarationDescriptor, Boolean> visibilityFilter = new Function1<DeclarationDescriptor, Boolean>() {
|
||||
@Override
|
||||
public Boolean invoke(DeclarationDescriptor descriptor) {
|
||||
if (placeDescriptor == null) return true;
|
||||
if (!(descriptor instanceof DeclarationDescriptorWithVisibility)) return true;
|
||||
return CorePackage.isVisible((DeclarationDescriptorWithVisibility) descriptor, placeDescriptor, bindingContext, callNameExpression);
|
||||
}
|
||||
};
|
||||
|
||||
final Name refName = callNameExpression.getReferencedNameAsName();
|
||||
|
||||
Function1<Name, Boolean> nameFilter = new Function1<Name, Boolean>() {
|
||||
@Override
|
||||
public Boolean invoke(Name name) {
|
||||
return name.equals(refName);
|
||||
}
|
||||
};
|
||||
Collection<DeclarationDescriptor> variants = new ReferenceVariantsHelper(bindingContext, resolutionFacade, visibilityFilter).getReferenceVariants(
|
||||
callNameExpression,
|
||||
new DescriptorKindFilter(DescriptorKindFilter.FUNCTIONS_MASK | DescriptorKindFilter.CLASSIFIERS_MASK, Collections.<DescriptorKindExclude>emptyList()),
|
||||
nameFilter
|
||||
);
|
||||
|
||||
Collection<Pair<? extends DeclarationDescriptor, ResolutionFacade>> itemsToShow = new ArrayList<Pair<? extends DeclarationDescriptor, ResolutionFacade>>();
|
||||
for (DeclarationDescriptor variant : variants) {
|
||||
if (variant instanceof FunctionDescriptor) {
|
||||
//todo: renamed functions?
|
||||
itemsToShow.add(Pair.create((FunctionDescriptor) variant, resolutionFacade));
|
||||
}
|
||||
else if (variant instanceof ClassDescriptor) {
|
||||
//todo: renamed classes?
|
||||
for (ConstructorDescriptor constructorDescriptor : ((ClassDescriptor) variant).getConstructors()) {
|
||||
itemsToShow.add(Pair.create(constructorDescriptor, resolutionFacade));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.setItemsToShow(ArrayUtil.toObjectArray(itemsToShow));
|
||||
return argumentList;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static JetSimpleNameExpression getCallSimpleNameExpression(JetValueArgumentList argumentList) {
|
||||
PsiElement argumentListParent = argumentList.getParent();
|
||||
return (argumentListParent instanceof JetCallElement) ?
|
||||
PsiUtilPackage.getCallNameExpression((JetCallElement) argumentListParent) :
|
||||
null;
|
||||
}
|
||||
|
||||
private static JetValueArgumentList findCallAndUpdateContext(UpdateParameterInfoContext context) {
|
||||
PsiFile file = context.getFile();
|
||||
PsiElement element = file.findElementAt(context.getOffset());
|
||||
if (element == null) return null;
|
||||
PsiElement parent = element.getParent();
|
||||
while (parent != null && !(parent instanceof JetValueArgumentList)) {
|
||||
element = element.getParent();
|
||||
parent = parent.getParent();
|
||||
}
|
||||
if (parent == null) return null;
|
||||
JetValueArgumentList argumentList = (JetValueArgumentList) parent;
|
||||
if (element instanceof JetValueArgument) {
|
||||
JetValueArgument arg = (JetValueArgument) element;
|
||||
int i = argumentList.getArguments().indexOf(arg);
|
||||
context.setCurrentParameter(i);
|
||||
context.setHighlightedParameter(arg);
|
||||
}
|
||||
return argumentList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.parameterInfo
|
||||
|
||||
import com.google.common.collect.Iterables
|
||||
import com.intellij.codeInsight.CodeInsightBundle
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.lang.ASTNode
|
||||
import com.intellij.lang.parameterInfo.*
|
||||
import com.intellij.openapi.util.Pair
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.ui.Gray
|
||||
import com.intellij.ui.JBColor
|
||||
import com.intellij.util.ArrayUtil
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper
|
||||
import org.jetbrains.kotlin.idea.core.isVisible
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||
import java.awt.Color
|
||||
import java.util.*
|
||||
|
||||
class JetFunctionParameterInfoHandler : ParameterInfoHandlerWithTabActionSupport<JetValueArgumentList, Pair<out FunctionDescriptor, ResolutionFacade>, JetValueArgument> {
|
||||
|
||||
override fun getActualParameters(arguments: JetValueArgumentList) = arguments.arguments.toTypedArray()
|
||||
|
||||
override fun getActualParameterDelimiterType() = JetTokens.COMMA
|
||||
|
||||
override fun getActualParametersRBraceType() = JetTokens.RBRACE
|
||||
|
||||
override fun getArgumentListAllowedParentClasses() = setOf(JetCallElement::class.java)
|
||||
|
||||
override fun getArgListStopSearchClasses() = setOf(JetFunction::class.java)
|
||||
|
||||
override fun getArgumentListClass() = JetValueArgumentList::class.java
|
||||
|
||||
override fun couldShowInLookup() = true
|
||||
|
||||
override fun getParametersForLookup(item: LookupElement, context: ParameterInfoContext) = emptyArray<Any>() //todo: ?
|
||||
|
||||
override fun getParametersForDocumentation(p: Pair<out FunctionDescriptor, ResolutionFacade>, context: ParameterInfoContext) = emptyArray<Any>() //todo: ?
|
||||
|
||||
override fun findElementForParameterInfo(context: CreateParameterInfoContext): JetValueArgumentList? {
|
||||
return findCall(context)
|
||||
}
|
||||
|
||||
override fun showParameterInfo(element: JetValueArgumentList, context: CreateParameterInfoContext) {
|
||||
context.showHint(element, element.textRange.startOffset, this)
|
||||
}
|
||||
|
||||
override fun findElementForUpdatingParameterInfo(context: UpdateParameterInfoContext): JetValueArgumentList? {
|
||||
return findCallAndUpdateContext(context)
|
||||
}
|
||||
|
||||
override fun updateParameterInfo(argumentList: JetValueArgumentList, context: UpdateParameterInfoContext) {
|
||||
if (context.parameterOwner !== argumentList) context.removeHint()
|
||||
val offset = context.offset
|
||||
var child: ASTNode? = argumentList.node.firstChildNode
|
||||
var i = 0
|
||||
while (child != null && child.startOffset < offset) {
|
||||
if (child.elementType === JetTokens.COMMA) ++i
|
||||
child = child.treeNext
|
||||
}
|
||||
context.setCurrentParameter(i)
|
||||
}
|
||||
|
||||
override fun getParameterCloseChars() = ParameterInfoUtils.DEFAULT_PARAMETER_CLOSE_CHARS
|
||||
|
||||
override fun tracksParameterIndex() = true
|
||||
|
||||
override fun updateUI(itemToShow: Pair<out FunctionDescriptor, ResolutionFacade>, context: ParameterInfoUIContext) {
|
||||
//todo: when we will have ability to pass Array as vararg, implement such feature here too?
|
||||
if (context.parameterOwner == null || !context.parameterOwner.isValid) {
|
||||
context.isUIComponentEnabled = false
|
||||
return
|
||||
}
|
||||
|
||||
val parameterOwner = context.parameterOwner
|
||||
if (parameterOwner !is JetValueArgumentList) {
|
||||
context.isUIComponentEnabled = false
|
||||
return
|
||||
}
|
||||
|
||||
val functionDescriptor = itemToShow.first
|
||||
val resolutionFacade = itemToShow.second
|
||||
|
||||
val valueParameters = functionDescriptor.valueParameters
|
||||
val valueArguments = parameterOwner.arguments
|
||||
|
||||
val currentParameterIndex = context.currentParameterIndex
|
||||
var boldStartOffset = -1
|
||||
var boldEndOffset = -1
|
||||
var isGrey = false
|
||||
val isDeprecated = KotlinBuiltIns.isDeprecated(functionDescriptor)
|
||||
|
||||
val usedIndexes = BooleanArray(valueParameters.size())
|
||||
Arrays.fill(usedIndexes, false)
|
||||
|
||||
var namedMode = false
|
||||
|
||||
if (!isIndexValid(valueParameters, currentParameterIndex)) {
|
||||
isGrey = true
|
||||
}
|
||||
|
||||
val builder = StringBuilder()
|
||||
|
||||
val owner = context.parameterOwner
|
||||
val bindingContext = resolutionFacade.analyze(owner as JetElement, BodyResolveMode.FULL)
|
||||
|
||||
for (i in valueParameters.indices) {
|
||||
if (i != 0) {
|
||||
builder.append(", ")
|
||||
}
|
||||
|
||||
val highlightParameter = i == currentParameterIndex || (!namedMode && i < currentParameterIndex && Iterables.getLast(valueParameters).varargElementType != null)
|
||||
|
||||
if (highlightParameter) {
|
||||
boldStartOffset = builder.length()
|
||||
}
|
||||
|
||||
if (!namedMode) {
|
||||
if (valueArguments.size() > i) {
|
||||
val argument = valueArguments.get(i)
|
||||
if (argument.isNamed()) {
|
||||
namedMode = true
|
||||
}
|
||||
else {
|
||||
val param = valueParameters.get(i)
|
||||
builder.append(renderParameter(param, false))
|
||||
if (i <= currentParameterIndex && !isArgumentTypeValid(bindingContext, argument, param)) {
|
||||
isGrey = true
|
||||
}
|
||||
usedIndexes[i] = true
|
||||
}
|
||||
}
|
||||
else {
|
||||
val param = valueParameters.get(i)
|
||||
builder.append(renderParameter(param, false))
|
||||
}
|
||||
}
|
||||
|
||||
if (namedMode) {
|
||||
var takeAnyArgument = true
|
||||
if (valueArguments.size() > i) {
|
||||
val argument = valueArguments.get(i)
|
||||
if (argument.isNamed()) {
|
||||
for (j in valueParameters.indices) {
|
||||
val referenceExpression = argument.getArgumentName()!!.getReferenceExpression()
|
||||
val param = valueParameters[j]
|
||||
if (!usedIndexes[j] && param.name == referenceExpression.getReferencedNameAsName()) {
|
||||
takeAnyArgument = false
|
||||
usedIndexes[j] = true
|
||||
builder.append(renderParameter(param, true))
|
||||
if (i < currentParameterIndex && !isArgumentTypeValid(bindingContext, argument, param)) {
|
||||
isGrey = true
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (takeAnyArgument) {
|
||||
if (i < currentParameterIndex) {
|
||||
isGrey = true
|
||||
}
|
||||
|
||||
for (j in valueParameters.indices) {
|
||||
val param = valueParameters.get(j)
|
||||
if (!usedIndexes[j]) {
|
||||
usedIndexes[j] = true
|
||||
builder.append(renderParameter(param, true))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (highlightParameter) {
|
||||
boldEndOffset = builder.length()
|
||||
}
|
||||
}
|
||||
|
||||
if (valueParameters.size() == 0) {
|
||||
builder.append(CodeInsightBundle.message("parameter.info.no.parameters"))
|
||||
}
|
||||
|
||||
assert(!builder.toString().isEmpty()) { "A message about 'no parameters' or some parameters should be present: " + functionDescriptor }
|
||||
|
||||
val color = if (isResolvedToDescriptor(parameterOwner, functionDescriptor, bindingContext)) GREEN_BACKGROUND else context.defaultParameterColor
|
||||
context.setupUIComponentPresentation(builder.toString(), boldStartOffset, boldEndOffset, isGrey, isDeprecated, false, color)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val GREEN_BACKGROUND: Color = JBColor(Color(231, 254, 234), Gray._100)
|
||||
|
||||
private fun renderParameter(parameter: ValueParameterDescriptor, named: Boolean): String {
|
||||
val builder = StringBuilder()
|
||||
if (named) builder.append("[")
|
||||
if (parameter.varargElementType != null) {
|
||||
builder.append("vararg ")
|
||||
}
|
||||
builder.append(parameter.name).append(": ").append(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(getActualParameterType(parameter)))
|
||||
if (parameter.hasDefaultValue()) {
|
||||
val parameterDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(parameter)
|
||||
builder.append(" = ").append(getDefaultExpressionString(parameterDeclaration))
|
||||
}
|
||||
if (named) builder.append("]")
|
||||
return builder.toString()
|
||||
}
|
||||
|
||||
private fun getDefaultExpressionString(parameterDeclaration: PsiElement?): String {
|
||||
if (parameterDeclaration is JetParameter) {
|
||||
val defaultValue = parameterDeclaration.defaultValue
|
||||
if (defaultValue != null) {
|
||||
val defaultExpression = defaultValue.text
|
||||
if (defaultExpression.length() <= 32) {
|
||||
return defaultExpression
|
||||
}
|
||||
|
||||
if (defaultValue is JetConstantExpression || defaultValue is JetStringTemplateExpression) {
|
||||
if (defaultExpression.startsWith("\"")) {
|
||||
return "\"...\""
|
||||
}
|
||||
else if (defaultExpression.startsWith("\'")) {
|
||||
return "\'...\'"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "..."
|
||||
}
|
||||
|
||||
private fun getActualParameterType(descriptor: ValueParameterDescriptor)
|
||||
= descriptor.varargElementType ?: descriptor.type
|
||||
|
||||
private fun isArgumentTypeValid(bindingContext: BindingContext, argument: JetValueArgument, param: ValueParameterDescriptor): Boolean {
|
||||
val expression = argument.getArgumentExpression() ?: return false
|
||||
val paramType = getActualParameterType(param)
|
||||
val exprType = bindingContext.getType(expression)
|
||||
return exprType == null || JetTypeChecker.DEFAULT.isSubtypeOf(exprType, paramType)
|
||||
}
|
||||
|
||||
private fun isIndexValid(valueParameters: List<ValueParameterDescriptor>, index: Int): Boolean {
|
||||
// Index is within range of parameters or last parameter is vararg
|
||||
return index < valueParameters.size() || (valueParameters.size() > 0 && Iterables.getLast(valueParameters).varargElementType != null)
|
||||
}
|
||||
|
||||
private fun isResolvedToDescriptor(
|
||||
argumentList: JetValueArgumentList,
|
||||
functionDescriptor: FunctionDescriptor,
|
||||
bindingContext: BindingContext): Boolean {
|
||||
val callNameExpression = getCallSimpleNameExpression(argumentList)
|
||||
if (callNameExpression != null) {
|
||||
val declarationDescriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, callNameExpression)
|
||||
if (declarationDescriptor != null) {
|
||||
if (declarationDescriptor === functionDescriptor) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun findCall(context: CreateParameterInfoContext): JetValueArgumentList? {
|
||||
//todo: calls to this constructors, when we will have auxiliary constructors
|
||||
val file = context.file
|
||||
if (file !is JetFile) {
|
||||
return null
|
||||
}
|
||||
|
||||
val argumentList = PsiTreeUtil.getParentOfType(file.findElementAt(context.offset), JetValueArgumentList::class.java) ?: return null
|
||||
|
||||
val callNameExpression = getCallSimpleNameExpression(argumentList) ?: return null
|
||||
|
||||
val references = callNameExpression.references
|
||||
if (references.size() == 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
val resolutionFacade = callNameExpression.getContainingJetFile().getResolutionFacade()
|
||||
val bindingContext = resolutionFacade.analyze(callNameExpression, BodyResolveMode.FULL)
|
||||
|
||||
val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, callNameExpression)
|
||||
val placeDescriptor = scope?.getContainingDeclaration()
|
||||
|
||||
val visibilityFilter = { descriptor: DeclarationDescriptor ->
|
||||
placeDescriptor == null
|
||||
|| descriptor !is DeclarationDescriptorWithVisibility
|
||||
|| descriptor.isVisible(placeDescriptor, bindingContext, callNameExpression)
|
||||
}
|
||||
|
||||
val refName = callNameExpression.getReferencedNameAsName()
|
||||
|
||||
val descriptorKindFilter = DescriptorKindFilter(DescriptorKindFilter.FUNCTIONS_MASK or DescriptorKindFilter.CLASSIFIERS_MASK, emptyList<DescriptorKindExclude>())
|
||||
|
||||
val variants = ReferenceVariantsHelper(bindingContext, resolutionFacade, visibilityFilter)
|
||||
.getReferenceVariants(callNameExpression, descriptorKindFilter, { it == refName })
|
||||
|
||||
val itemsToShow = ArrayList<Pair<out DeclarationDescriptor, ResolutionFacade>>()
|
||||
for (variant in variants) {
|
||||
if (variant is FunctionDescriptor) {
|
||||
//todo: renamed functions?
|
||||
itemsToShow.add(Pair.create(variant, resolutionFacade))
|
||||
}
|
||||
else if (variant is ClassDescriptor) {
|
||||
//todo: renamed classes?
|
||||
for (constructorDescriptor in variant.constructors) {
|
||||
itemsToShow.add(Pair.create(constructorDescriptor, resolutionFacade))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.itemsToShow = ArrayUtil.toObjectArray(itemsToShow)
|
||||
return argumentList
|
||||
}
|
||||
|
||||
private fun getCallSimpleNameExpression(argumentList: JetValueArgumentList): JetSimpleNameExpression? {
|
||||
val argumentListParent = argumentList.parent
|
||||
return if ((argumentListParent is JetCallElement))
|
||||
argumentListParent.getCallNameExpression()
|
||||
else
|
||||
null
|
||||
}
|
||||
|
||||
private fun findCallAndUpdateContext(context: UpdateParameterInfoContext): JetValueArgumentList? {
|
||||
val file = context.file
|
||||
var element = file.findElementAt(context.offset) ?: return null
|
||||
var parent = element.parent
|
||||
while (parent != null && parent !is JetValueArgumentList) {
|
||||
element = element!!.parent
|
||||
parent = parent.parent
|
||||
}
|
||||
if (parent == null) return null
|
||||
val argumentList = parent as JetValueArgumentList
|
||||
if (element is JetValueArgument) {
|
||||
val i = argumentList.arguments.indexOf(element)
|
||||
context.setCurrentParameter(i)
|
||||
context.setHighlightedParameter(element)
|
||||
}
|
||||
return argumentList
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user