diff --git a/idea/src/org/jetbrains/kotlin/idea/parameterInfo/ArgumentNameHints.kt b/idea/src/org/jetbrains/kotlin/idea/parameterInfo/ArgumentNameHints.kt new file mode 100644 index 00000000000..6c9e9466bd5 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/parameterInfo/ArgumentNameHints.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.parameterInfo + +import com.intellij.codeInsight.hints.InlayInfo +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade +import org.jetbrains.kotlin.idea.core.resolveCandidates +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.resolve.calls.callUtil.getCall +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode + +fun provideArgumentNameHints(element: KtCallExpression): List { + val ctx = element.analyze(BodyResolveMode.PARTIAL) + val call = element.getCall(ctx) ?: return emptyList() + val candidates = call.resolveCandidates(ctx, element.getResolutionFacade()) + if (candidates.isEmpty()) return emptyList() + candidates.singleOrNull()?.let { return getParameterInfoForCallCandidate(it) } + return candidates.map { getParameterInfoForCallCandidate(it) }.reduce { infos1, infos2 -> + for (index in infos1.indices) { + if (index >= infos2.size || infos1[index] != infos2[index]) { + return@reduce infos1.subList(0, index) + } + } + infos1 + } +} + +private fun getParameterInfoForCallCandidate(resolvedCall: ResolvedCall): List { + return resolvedCall.valueArguments.mapNotNull { (valueParam: ValueParameterDescriptor, resolvedArg) -> + resolvedArg.arguments.firstOrNull()?.let { arg -> + arg.getArgumentExpression()?.let { argExp -> + if (!arg.isNamed() && !valueParam.name.isSpecial && argExp.isUnclearExpression()) { + val prefix = if (valueParam.varargElementType != null) "..." else "" + return@mapNotNull InlayInfo(prefix + valueParam.name.identifier, argExp.startOffset) + } + } + } + null + } +} + +private fun KtExpression.isUnclearExpression() = when(this) { + is KtConstantExpression, is KtThisExpression, is KtBinaryExpression, is KtStringTemplateExpression -> true + is KtPrefixExpression -> baseExpression is KtConstantExpression && (operationToken == KtTokens.PLUS || operationToken == KtTokens.MINUS) + else -> false +} diff --git a/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinInlayParameterHintsProvider.kt b/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinInlayParameterHintsProvider.kt index 63680889c60..95d23bf34fd 100644 --- a/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinInlayParameterHintsProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinInlayParameterHintsProvider.kt @@ -23,40 +23,23 @@ import com.intellij.codeInsight.hints.Option import com.intellij.lang.Language import com.intellij.lang.java.JavaLanguage import com.intellij.psi.PsiElement -import com.intellij.psi.codeStyle.CodeStyleSettingsManager -import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade -import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings -import org.jetbrains.kotlin.idea.core.resolveCandidates -import org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getReturnTypeReference -import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.KtCallExpression +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.psiUtil.endOffset -import org.jetbrains.kotlin.psi.psiUtil.startOffset -import org.jetbrains.kotlin.renderer.DescriptorRenderer -import org.jetbrains.kotlin.renderer.RenderingFormat -import org.jetbrains.kotlin.resolve.calls.callUtil.getCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode -//hack to separate type presentation from param info presentation -private const val TYPE_INFO_PREFIX = "@TYPE@" -private val typeRenderer = DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.withOptions { - textFormat = RenderingFormat.PLAIN -} - - -private enum class HintType(desc: String, enabled: Boolean) { +enum class HintType(desc: String, enabled: Boolean) { PROPERTY_HINT("Show property type hints", false) { override fun provideHints(elem: PsiElement): List { @@ -102,7 +85,7 @@ private enum class HintType(desc: String, enabled: Boolean) { PARAMETER_HINT("Show argument name hints", true) { override fun provideHints(elem: PsiElement): List { (elem as? KtCallExpression)?.let { - return provideParameterInfo(it) + return provideArgumentNameHints(it) } return emptyList() } @@ -123,73 +106,6 @@ private enum class HintType(desc: String, enabled: Boolean) { null } } - - private fun provideParameterInfo(element: KtCallExpression): List { - val ctx = element.analyze(BodyResolveMode.PARTIAL) - val call = element.getCall(ctx) ?: return emptyList() - val candidates = call.resolveCandidates(ctx, element.getResolutionFacade()) - if (candidates.isEmpty()) return emptyList() - candidates.singleOrNull()?.let { return getParameterInfoForCallCandidate(it) } - return candidates.map { getParameterInfoForCallCandidate(it) }.reduce { infos1, infos2 -> - for (index in infos1.indices) { - if (index >= infos2.size || infos1[index] != infos2[index]) { - return@reduce infos1.subList(0, index) - } - } - infos1 - } - } - - private fun getParameterInfoForCallCandidate(resolvedCall: ResolvedCall): List { - return resolvedCall.valueArguments.mapNotNull { (valueParam: ValueParameterDescriptor, resolvedArg) -> - resolvedArg.arguments.firstOrNull()?.let { arg -> - arg.getArgumentExpression()?.let { argExp -> - if (!arg.isNamed() && !valueParam.name.isSpecial && argExp.isUnclearExpression()) { - val prefix = if (valueParam.varargElementType != null) "..." else "" - return@mapNotNull InlayInfo(prefix + valueParam.name.identifier, argExp.startOffset) - } - } - } - null - } - } - - private fun KtExpression.isUnclearExpression() = when(this) { - is KtConstantExpression, is KtThisExpression, is KtBinaryExpression, is KtStringTemplateExpression -> true - is KtPrefixExpression -> baseExpression is KtConstantExpression && (operationToken == KtTokens.PLUS || operationToken == KtTokens.MINUS) - else -> false - } - - private fun providePropertyTypeHint(elem: PsiElement): List { - (elem as? KtCallableDeclaration)?.let { property -> - property.nameIdentifier?.let { ident -> - return HintType.provideTypeHint(property, ident.endOffset) - } - } - return emptyList() - } - - private fun provideTypeHint(element: KtCallableDeclaration, offset: Int): List { - val type = SpecifyTypeExplicitlyIntention.getTypeForDeclaration(element) - return if (!type.isError) { - val settings = CodeStyleSettingsManager.getInstance(element.project).currentSettings - .getCustomSettings(KotlinCodeStyleSettings::class.java) - - val declString = buildString { - append(TYPE_INFO_PREFIX) - if (settings.SPACE_BEFORE_TYPE_COLON) - append(" ") - append(":") - if (settings.SPACE_AFTER_TYPE_COLON) - append(" ") - append(typeRenderer.renderType(type)) - } - listOf(InlayInfo(declString, offset)) - } - else { - emptyList() - } - } } abstract fun isApplicable(elem: PsiElement): Boolean diff --git a/idea/src/org/jetbrains/kotlin/idea/parameterInfo/TypeHints.kt b/idea/src/org/jetbrains/kotlin/idea/parameterInfo/TypeHints.kt new file mode 100644 index 00000000000..c4a076ddffd --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/parameterInfo/TypeHints.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.parameterInfo + +import com.intellij.codeInsight.hints.InlayInfo +import com.intellij.psi.PsiElement +import com.intellij.psi.codeStyle.CodeStyleSettingsManager +import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings +import org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention +import org.jetbrains.kotlin.psi.KtCallableDeclaration +import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.renderer.DescriptorRenderer +import org.jetbrains.kotlin.renderer.RenderingFormat +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.isError + +//hack to separate type presentation from param info presentation +const val TYPE_INFO_PREFIX = "@TYPE@" +private val typeRenderer = DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.withOptions { + textFormat = RenderingFormat.PLAIN +} + +fun providePropertyTypeHint(elem: PsiElement): List { + (elem as? KtCallableDeclaration)?.let { property -> + property.nameIdentifier?.let { ident -> + return provideTypeHint(property, ident.endOffset) + } + } + return emptyList() +} + +fun provideTypeHint(element: KtCallableDeclaration, offset: Int): List { + val type = SpecifyTypeExplicitlyIntention.getTypeForDeclaration(element) + return if (!type.isError) { + val settings = CodeStyleSettingsManager.getInstance(element.project).currentSettings + .getCustomSettings(KotlinCodeStyleSettings::class.java) + + val declString = buildString { + append(TYPE_INFO_PREFIX) + if (settings.SPACE_BEFORE_TYPE_COLON) + append(" ") + append(":") + if (settings.SPACE_AFTER_TYPE_COLON) + append(" ") + append(typeRenderer.renderType(type)) + } + listOf(InlayInfo(declString, offset)) + } + else { + emptyList() + } +} +