Add new REPL API JVM implementation
This commit is contained in:
committed by
Ilya Chernikov
parent
4c2c44b106
commit
d2fec96f38
@@ -0,0 +1,41 @@
|
||||
|
||||
description = "Kotlin Scripting Compiler extension providing code completion and static analysis"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
jvmTarget = "1.8"
|
||||
|
||||
publish()
|
||||
|
||||
dependencies {
|
||||
compile(project(":kotlin-script-runtime"))
|
||||
compile(kotlinStdlib())
|
||||
compileOnly(project(":idea:ide-common"))
|
||||
compile(project(":kotlin-scripting-common"))
|
||||
compile(project(":kotlin-scripting-jvm"))
|
||||
compileOnly(project(":kotlin-scripting-compiler"))
|
||||
compileOnly(project(":compiler:cli"))
|
||||
compileOnly(project(":kotlin-reflect-api"))
|
||||
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
||||
publishedRuntime(project(":kotlin-compiler"))
|
||||
publishedRuntime(project(":kotlin-scripting-compiler"))
|
||||
publishedRuntime(project(":kotlin-reflect"))
|
||||
publishedRuntime(commonDep("org.jetbrains.intellij.deps", "trove4j"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { }
|
||||
}
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs += "-Xskip-metadata-version-check"
|
||||
freeCompilerArgs += "-Xallow-kotlin-package"
|
||||
}
|
||||
}
|
||||
|
||||
standardPublicJars()
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.ide_services.compiler
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmReplCompilerBase
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.failure
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.withMessageCollector
|
||||
import org.jetbrains.kotlin.scripting.ide_services.compiler.impl.IdeLikeReplCodeAnalyzer
|
||||
import org.jetbrains.kotlin.scripting.ide_services.compiler.impl.getKJvmCompletion
|
||||
import org.jetbrains.kotlin.scripting.ide_services.compiler.impl.prepareCodeForCompletion
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
||||
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
|
||||
import kotlin.script.experimental.jvm.util.calcAbsolute
|
||||
|
||||
class KJvmReplCompilerWithIdeServices(hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration) :
|
||||
KJvmReplCompilerBase<IdeLikeReplCodeAnalyzer>(hostConfiguration, {
|
||||
IdeLikeReplCodeAnalyzer(it.environment)
|
||||
}),
|
||||
ReplCompleter, ReplCodeAnalyzer {
|
||||
|
||||
override suspend fun complete(
|
||||
snippet: SourceCode,
|
||||
cursor: SourceCode.Position,
|
||||
configuration: ScriptCompilationConfiguration
|
||||
): ResultWithDiagnostics<ReplCompletionResult> =
|
||||
withMessageCollector(snippet) { messageCollector ->
|
||||
val initialConfiguration = configuration.refineBeforeParsing(snippet).valueOr {
|
||||
return it
|
||||
}
|
||||
|
||||
val cursorAbs = cursor.calcAbsolute(snippet)
|
||||
|
||||
val newText =
|
||||
prepareCodeForCompletion(snippet.text, cursorAbs)
|
||||
val newSnippet = object : SourceCode {
|
||||
override val text: String
|
||||
get() = newText
|
||||
override val name: String?
|
||||
get() = snippet.name
|
||||
override val locationId: String?
|
||||
get() = snippet.locationId
|
||||
|
||||
}
|
||||
|
||||
val compilationState = state.getCompilationState(initialConfiguration)
|
||||
|
||||
val (_, errorHolder, snippetKtFile) = prepareForAnalyze(
|
||||
newSnippet,
|
||||
messageCollector,
|
||||
compilationState,
|
||||
checkSyntaxErrors = false
|
||||
).valueOr { return@withMessageCollector it }
|
||||
|
||||
val analysisResult =
|
||||
compilationState.analyzerEngine.statelessAnalyzeWithImportedScripts(snippetKtFile, emptyList(), scriptPriority.get() + 1)
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
||||
|
||||
val (_, bindingContext, resolutionFacade, moduleDescriptor) = when (analysisResult) {
|
||||
is IdeLikeReplCodeAnalyzer.ReplLineAnalysisResultWithStateless.Stateless -> {
|
||||
analysisResult
|
||||
}
|
||||
else -> return failure(
|
||||
newSnippet,
|
||||
messageCollector,
|
||||
"Unexpected result ${analysisResult::class.java}"
|
||||
)
|
||||
}
|
||||
|
||||
return getKJvmCompletion(
|
||||
snippetKtFile,
|
||||
bindingContext,
|
||||
resolutionFacade,
|
||||
moduleDescriptor,
|
||||
cursorAbs
|
||||
).asSuccess(messageCollector.diagnostics)
|
||||
}
|
||||
|
||||
private fun List<ScriptDiagnostic>.toAnalyzeResult() = (filter {
|
||||
when (it.severity) {
|
||||
ScriptDiagnostic.Severity.FATAL,
|
||||
ScriptDiagnostic.Severity.ERROR,
|
||||
ScriptDiagnostic.Severity.WARNING
|
||||
-> true
|
||||
else -> false
|
||||
}
|
||||
}).asSequence()
|
||||
|
||||
override suspend fun analyze(
|
||||
snippet: SourceCode,
|
||||
cursor: SourceCode.Position,
|
||||
configuration: ScriptCompilationConfiguration
|
||||
): ResultWithDiagnostics<ReplAnalyzerResult> {
|
||||
return withMessageCollector(snippet) { messageCollector ->
|
||||
val initialConfiguration = configuration.refineBeforeParsing(snippet).valueOr {
|
||||
return it
|
||||
}
|
||||
|
||||
val compilationState = state.getCompilationState(initialConfiguration)
|
||||
|
||||
val (_, errorHolder, snippetKtFile) = prepareForAnalyze(
|
||||
snippet,
|
||||
messageCollector,
|
||||
compilationState,
|
||||
checkSyntaxErrors = true
|
||||
).valueOr { return@withMessageCollector messageCollector.diagnostics.toAnalyzeResult().asSuccess() }
|
||||
|
||||
val analysisResult =
|
||||
compilationState.analyzerEngine.statelessAnalyzeWithImportedScripts(snippetKtFile, emptyList(), scriptPriority.get() + 1)
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
||||
|
||||
messageCollector.diagnostics.toAnalyzeResult().asSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.ide_services.compiler.impl
|
||||
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.CliBindingTrace
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.container.getService
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
|
||||
import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptPriorities
|
||||
|
||||
class IdeLikeReplCodeAnalyzer(private val environment: KotlinCoreEnvironment) : ReplCodeAnalyzerBase(environment, CliBindingTrace()) {
|
||||
interface ReplLineAnalysisResultWithStateless : ReplLineAnalysisResult {
|
||||
// Result of stateless analyse, which may be used for reporting errors
|
||||
// without code generation
|
||||
data class Stateless(
|
||||
override val diagnostics: Diagnostics,
|
||||
val bindingContext: BindingContext,
|
||||
val resolutionFacade: KotlinResolutionFacadeForRepl,
|
||||
val moduleDescriptor: ModuleDescriptor
|
||||
) :
|
||||
ReplLineAnalysisResultWithStateless {
|
||||
override val scriptDescriptor: ClassDescriptorWithResolutionScopes? get() = null
|
||||
}
|
||||
}
|
||||
|
||||
fun statelessAnalyzeWithImportedScripts(
|
||||
psiFile: KtFile,
|
||||
importedScripts: List<KtFile>,
|
||||
priority: Int
|
||||
): ReplLineAnalysisResultWithStateless {
|
||||
topDownAnalysisContext.scripts.clear()
|
||||
trace.clearDiagnostics()
|
||||
|
||||
psiFile.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, priority)
|
||||
|
||||
return doStatelessAnalyze(psiFile, importedScripts)
|
||||
}
|
||||
|
||||
private fun doStatelessAnalyze(linePsi: KtFile, importedScripts: List<KtFile>): ReplLineAnalysisResultWithStateless {
|
||||
scriptDeclarationFactory.setDelegateFactory(
|
||||
FileBasedDeclarationProviderFactory(resolveSession.storageManager, listOf(linePsi) + importedScripts)
|
||||
)
|
||||
replState.submitLine(linePsi)
|
||||
|
||||
topDownAnalyzer.analyzeDeclarations(topDownAnalysisContext.topDownAnalysisMode, listOf(linePsi) + importedScripts)
|
||||
|
||||
val moduleDescriptor = container.getService(ModuleDescriptor::class.java)
|
||||
val resolutionFacade =
|
||||
KotlinResolutionFacadeForRepl(environment, container)
|
||||
val diagnostics = trace.bindingContext.diagnostics
|
||||
return ReplLineAnalysisResultWithStateless.Stateless(
|
||||
diagnostics,
|
||||
trace.bindingContext,
|
||||
resolutionFacade,
|
||||
moduleDescriptor
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
+406
@@ -0,0 +1,406 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.ide_services.compiler.impl
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.tree.TokenSet
|
||||
import org.jetbrains.kotlin.backend.common.onlyIf
|
||||
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper
|
||||
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
import org.jetbrains.kotlin.lexer.KtKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
import org.jetbrains.kotlin.renderer.ClassifierNamePolicy
|
||||
import org.jetbrains.kotlin.renderer.ParameterNameRenderingPolicy
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope.Companion.ALL_NAME_FILTER
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.asFlexibleType
|
||||
import org.jetbrains.kotlin.types.isFlexible
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import kotlin.script.experimental.api.SourceCodeCompletionVariant
|
||||
|
||||
fun getKJvmCompletion(
|
||||
ktScript: KtFile,
|
||||
bindingContext: BindingContext,
|
||||
resolutionFacade: KotlinResolutionFacadeForRepl,
|
||||
moduleDescriptor: ModuleDescriptor,
|
||||
cursor: Int
|
||||
) = KJvmReplCompleter(ktScript, bindingContext, resolutionFacade, moduleDescriptor, cursor).getCompletion()
|
||||
|
||||
// Insert a constant string right after a cursor position to make this identifiable as a simple reference
|
||||
// For example, code line
|
||||
// import java.
|
||||
// ^
|
||||
// is converted to
|
||||
// import java.ABCDEF
|
||||
// and it makes token after dot (for which reference variants are looked) discoverable in PSI
|
||||
fun prepareCodeForCompletion(code: String, cursor: Int) =
|
||||
code.substring(0, cursor) + KJvmReplCompleter.INSERTED_STRING + code.substring(cursor)
|
||||
|
||||
private class KJvmReplCompleter(
|
||||
private val ktScript: KtFile,
|
||||
private val bindingContext: BindingContext,
|
||||
private val resolutionFacade: KotlinResolutionFacadeForRepl,
|
||||
private val moduleDescriptor: ModuleDescriptor,
|
||||
private val cursor: Int
|
||||
) {
|
||||
|
||||
private fun getElementAt(cursorPos: Int): PsiElement? {
|
||||
var element: PsiElement? = ktScript.findElementAt(cursorPos)
|
||||
while (element !is KtExpression && element != null) {
|
||||
element = element.parent
|
||||
}
|
||||
return element
|
||||
}
|
||||
|
||||
fun getCompletion() = sequence<SourceCodeCompletionVariant> gen@{
|
||||
val element = getElementAt(cursor)
|
||||
|
||||
var descriptors: Collection<DeclarationDescriptor>? = null
|
||||
var isTipsManagerCompletion = true
|
||||
var isSortNeeded = true
|
||||
|
||||
if (element == null)
|
||||
return@gen
|
||||
|
||||
val simpleExpression = when {
|
||||
element is KtSimpleNameExpression -> element
|
||||
element.parent is KtSimpleNameExpression -> element.parent as KtSimpleNameExpression
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (simpleExpression != null) {
|
||||
val inDescriptor: DeclarationDescriptor = simpleExpression.getResolutionScope(bindingContext, resolutionFacade).ownerDescriptor
|
||||
val prefix = element.text.substring(0, cursor - element.startOffset)
|
||||
isSortNeeded = false
|
||||
descriptors = ReferenceVariantsHelper(
|
||||
bindingContext,
|
||||
resolutionFacade,
|
||||
moduleDescriptor,
|
||||
VisibilityFilter(inDescriptor)
|
||||
).getReferenceVariants(
|
||||
simpleExpression,
|
||||
DescriptorKindFilter.ALL,
|
||||
{ name: Name -> !name.isSpecial && name.identifier.startsWith(prefix) },
|
||||
filterOutJavaGettersAndSetters = true,
|
||||
filterOutShadowed = false, // setting to true makes it slower up to 4 times
|
||||
excludeNonInitializedVariable = true,
|
||||
useReceiverType = null
|
||||
)
|
||||
|
||||
} else if (element is KtStringTemplateExpression) {
|
||||
if (element.hasInterpolation()) {
|
||||
return@gen
|
||||
}
|
||||
|
||||
val stringVal = element.entries.joinToString("") {
|
||||
val t = it.text
|
||||
if (it.startOffset <= cursor && cursor <= it.endOffset) {
|
||||
val s = cursor - it.startOffset
|
||||
val e = s + INSERTED_STRING.length
|
||||
t.substring(0, s) + t.substring(e)
|
||||
} else t
|
||||
}
|
||||
|
||||
val separatorIndex = stringVal.lastIndexOfAny(charArrayOf('/', '\\'))
|
||||
val dir = if (separatorIndex != -1) {
|
||||
stringVal.substring(0, separatorIndex + 1)
|
||||
} else {
|
||||
"."
|
||||
}
|
||||
val namePrefix = stringVal.substring(separatorIndex + 1)
|
||||
|
||||
val file = File(dir)
|
||||
|
||||
file.listFiles { p, f -> p == file && f.startsWith(namePrefix, true) }?.forEach {
|
||||
yield(SourceCodeCompletionVariant(it.name, it.name, "file", "file"))
|
||||
}
|
||||
|
||||
return@gen
|
||||
|
||||
} else {
|
||||
isTipsManagerCompletion = false
|
||||
val resolutionScope: LexicalScope?
|
||||
val parent = element.parent
|
||||
|
||||
val qualifiedExpression = when {
|
||||
element is KtQualifiedExpression -> {
|
||||
isTipsManagerCompletion = true
|
||||
element
|
||||
}
|
||||
parent is KtQualifiedExpression -> parent
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (qualifiedExpression != null) {
|
||||
val receiverExpression = qualifiedExpression.receiverExpression
|
||||
val expressionType = bindingContext.get(
|
||||
BindingContext.EXPRESSION_TYPE_INFO,
|
||||
receiverExpression
|
||||
)?.type
|
||||
if (expressionType != null) {
|
||||
isSortNeeded = false
|
||||
descriptors = ReferenceVariantsHelper(
|
||||
bindingContext,
|
||||
resolutionFacade,
|
||||
moduleDescriptor,
|
||||
{ true }
|
||||
).getReferenceVariants(
|
||||
receiverExpression,
|
||||
CallTypeAndReceiver.DOT(receiverExpression),
|
||||
DescriptorKindFilter.ALL,
|
||||
ALL_NAME_FILTER
|
||||
)
|
||||
}
|
||||
} else {
|
||||
resolutionScope = bindingContext.get(
|
||||
BindingContext.LEXICAL_SCOPE,
|
||||
element as KtExpression?
|
||||
)
|
||||
descriptors = (resolutionScope?.getContributedDescriptors(
|
||||
DescriptorKindFilter.ALL,
|
||||
ALL_NAME_FILTER
|
||||
)
|
||||
?: return@gen)
|
||||
}
|
||||
}
|
||||
|
||||
if (descriptors != null) {
|
||||
val targetElement = if (isTipsManagerCompletion) element else element.parent
|
||||
val prefixEnd = cursor - targetElement.startOffset
|
||||
var prefix = targetElement.text.substring(0, prefixEnd)
|
||||
|
||||
val cursorWithinElement = cursor - element.startOffset
|
||||
val dotIndex = prefix.lastIndexOf('.', cursorWithinElement)
|
||||
|
||||
prefix = if (dotIndex >= 0) {
|
||||
prefix.substring(dotIndex + 1, cursorWithinElement)
|
||||
} else {
|
||||
prefix.substring(0, cursorWithinElement)
|
||||
}
|
||||
|
||||
if (descriptors !is ArrayList<*>) {
|
||||
descriptors = ArrayList(descriptors)
|
||||
}
|
||||
|
||||
(descriptors as ArrayList<DeclarationDescriptor>)
|
||||
.map {
|
||||
val presentation =
|
||||
getPresentation(
|
||||
it
|
||||
)
|
||||
Triple(it, presentation, (presentation.presentableText + presentation.tailText).toLowerCase())
|
||||
}
|
||||
.onlyIf({ isSortNeeded }) { it.sortedBy { descTriple -> descTriple.third } }
|
||||
.forEach {
|
||||
val descriptor = it.first
|
||||
val (rawName, presentableText, tailText, completionText) = it.second
|
||||
if (rawName.startsWith(prefix)) {
|
||||
val fullName: String =
|
||||
formatName(
|
||||
presentableText
|
||||
)
|
||||
yield(
|
||||
SourceCodeCompletionVariant(
|
||||
completionText,
|
||||
fullName,
|
||||
tailText,
|
||||
getIconFromDescriptor(
|
||||
descriptor
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
yieldAll(
|
||||
keywordsCompletionVariants(
|
||||
KtTokens.KEYWORDS,
|
||||
prefix
|
||||
)
|
||||
)
|
||||
yieldAll(
|
||||
keywordsCompletionVariants(
|
||||
KtTokens.SOFT_KEYWORDS,
|
||||
prefix
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private inner class VisibilityFilter(
|
||||
private val inDescriptor: DeclarationDescriptor
|
||||
) : (DeclarationDescriptor) -> Boolean {
|
||||
override fun invoke(descriptor: DeclarationDescriptor): Boolean {
|
||||
if (descriptor is TypeParameterDescriptor && !isTypeParameterVisible(descriptor)) return false
|
||||
|
||||
if (descriptor is DeclarationDescriptorWithVisibility) {
|
||||
return try {
|
||||
descriptor.visibility.isVisible(null, descriptor, inDescriptor)
|
||||
} catch (e: IllegalStateException) {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun isTypeParameterVisible(typeParameter: TypeParameterDescriptor): Boolean {
|
||||
val owner = typeParameter.containingDeclaration
|
||||
var parent: DeclarationDescriptor? = inDescriptor
|
||||
while (parent != null) {
|
||||
if (parent == owner) return true
|
||||
if (parent is ClassDescriptor && !parent.isInner) return false
|
||||
parent = parent.containingDeclaration
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val INSERTED_STRING = "ABCDEF"
|
||||
private const val NUMBER_OF_CHAR_IN_COMPLETION_NAME = 40
|
||||
|
||||
private fun keywordsCompletionVariants(
|
||||
keywords: TokenSet,
|
||||
prefix: String
|
||||
) = sequence {
|
||||
keywords.types.forEach {
|
||||
val token = (it as KtKeywordToken).value
|
||||
if (token.startsWith(prefix)) yield(
|
||||
SourceCodeCompletionVariant(
|
||||
token,
|
||||
token,
|
||||
"keyword",
|
||||
"keyword"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val RENDERER =
|
||||
IdeDescriptorRenderers.SOURCE_CODE.withOptions {
|
||||
this.classifierNamePolicy =
|
||||
ClassifierNamePolicy.SHORT
|
||||
this.typeNormalizer =
|
||||
IdeDescriptorRenderers.APPROXIMATE_FLEXIBLE_TYPES
|
||||
this.parameterNameRenderingPolicy =
|
||||
ParameterNameRenderingPolicy.NONE
|
||||
this.renderDefaultAnnotationArguments = false
|
||||
this.typeNormalizer = lambda@{ kotlinType: KotlinType ->
|
||||
if (kotlinType.isFlexible()) {
|
||||
return@lambda kotlinType.asFlexibleType().upperBound
|
||||
}
|
||||
kotlinType
|
||||
}
|
||||
}
|
||||
|
||||
private fun getIconFromDescriptor(descriptor: DeclarationDescriptor): String = when (descriptor) {
|
||||
is FunctionDescriptor -> "method"
|
||||
is PropertyDescriptor -> "property"
|
||||
is LocalVariableDescriptor -> "property"
|
||||
is ClassDescriptor -> "class"
|
||||
is PackageFragmentDescriptor -> "package"
|
||||
is PackageViewDescriptor -> "package"
|
||||
is ValueParameterDescriptor -> "genericValue"
|
||||
is TypeParameterDescriptorImpl -> "class"
|
||||
else -> ""
|
||||
}
|
||||
|
||||
private fun formatName(builder: String, symbols: Int = NUMBER_OF_CHAR_IN_COMPLETION_NAME): String {
|
||||
return if (builder.length > symbols) {
|
||||
builder.substring(0, symbols) + "..."
|
||||
} else builder
|
||||
}
|
||||
|
||||
data class DescriptorPresentation(
|
||||
val rawName: String,
|
||||
val presentableText: String,
|
||||
val tailText: String,
|
||||
val completionText: String
|
||||
)
|
||||
|
||||
fun getPresentation(descriptor: DeclarationDescriptor): DescriptorPresentation {
|
||||
val rawDescriptorName = descriptor.name.asString()
|
||||
val descriptorName = rawDescriptorName.quoteIfNeeded()
|
||||
var presentableText = descriptorName
|
||||
var typeText = ""
|
||||
var tailText = ""
|
||||
var completionText = ""
|
||||
if (descriptor is FunctionDescriptor) {
|
||||
val returnType = descriptor.returnType
|
||||
typeText =
|
||||
if (returnType != null) RENDERER.renderType(returnType) else ""
|
||||
presentableText += RENDERER.renderFunctionParameters(
|
||||
descriptor
|
||||
)
|
||||
val parameters = descriptor.valueParameters
|
||||
if (parameters.size == 1 && parameters.first().type.isFunctionType)
|
||||
completionText = "$descriptorName { "
|
||||
|
||||
val extensionFunction = descriptor.extensionReceiverParameter != null
|
||||
val containingDeclaration = descriptor.containingDeclaration
|
||||
if (extensionFunction) {
|
||||
tailText += " for " + RENDERER.renderType(
|
||||
descriptor.extensionReceiverParameter!!.type
|
||||
)
|
||||
tailText += " in " + DescriptorUtils.getFqName(containingDeclaration)
|
||||
}
|
||||
} else if (descriptor is VariableDescriptor) {
|
||||
val outType =
|
||||
descriptor.type
|
||||
typeText = RENDERER.renderType(outType)
|
||||
} else if (descriptor is ClassDescriptor) {
|
||||
val declaredIn = descriptor.containingDeclaration
|
||||
tailText = " (" + DescriptorUtils.getFqName(declaredIn) + ")"
|
||||
} else {
|
||||
typeText = RENDERER.render(descriptor)
|
||||
}
|
||||
tailText = if (typeText.isEmpty()) tailText else typeText
|
||||
|
||||
if (completionText.isEmpty()) {
|
||||
completionText = presentableText
|
||||
var position = completionText.indexOf('(')
|
||||
if (position != -1) { //If this is a string with a package after
|
||||
if (completionText[position - 1] == ' ') {
|
||||
position -= 2
|
||||
}
|
||||
//if this is a method without args
|
||||
if (completionText[position + 1] == ')') {
|
||||
position++
|
||||
}
|
||||
completionText = completionText.substring(0, position + 1)
|
||||
}
|
||||
position = completionText.indexOf(":")
|
||||
if (position != -1) {
|
||||
completionText = completionText.substring(0, position - 1)
|
||||
}
|
||||
}
|
||||
|
||||
return DescriptorPresentation(
|
||||
rawDescriptorName,
|
||||
presentableText,
|
||||
tailText,
|
||||
completionText
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.ide_services.compiler.impl
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.container.ComponentProvider
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
|
||||
class KotlinResolutionFacadeForRepl(
|
||||
private val environment: KotlinCoreEnvironment,
|
||||
private val provider: ComponentProvider
|
||||
) :
|
||||
ResolutionFacade {
|
||||
override val project: Project
|
||||
get() = environment.project
|
||||
|
||||
override fun analyze(
|
||||
element: KtElement,
|
||||
bodyResolveMode: BodyResolveMode
|
||||
): BindingContext {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override val moduleDescriptor: ModuleDescriptor
|
||||
get() {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun <T : Any> getFrontendService(serviceClass: Class<T>): T {
|
||||
return provider.resolve(serviceClass)!!.getValue() as T
|
||||
}
|
||||
|
||||
override fun <T : Any> getIdeService(serviceClass: Class<T>): T {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun <T : Any> tryGetFrontendService(element: PsiElement, serviceClass: Class<T>): T? {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun <T : Any> getFrontendService(element: PsiElement, serviceClass: Class<T>): T {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun <T : Any> getFrontendService(moduleDescriptor: ModuleDescriptor, serviceClass: Class<T>): T {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun analyze(elements: Collection<KtElement>, bodyResolveMode: BodyResolveMode): BindingContext {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun analyzeWithAllCompilerChecks(elements: Collection<KtElement>): AnalysisResult {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun resolveToDescriptor(declaration: KtDeclaration, bodyResolveMode: BodyResolveMode): DeclarationDescriptor {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user