Cleanup 191 extension files (KTI-240)
This commit is contained in:
-245
@@ -1,245 +0,0 @@
|
||||
/*
|
||||
* 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.idea.injection
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil
|
||||
import com.intellij.lang.Language
|
||||
import com.intellij.openapi.module.ModuleUtilCore
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.psi.util.PsiTreeUtil.getDeepestLast
|
||||
import com.intellij.util.Processor
|
||||
import org.intellij.plugins.intelliLang.Configuration
|
||||
import org.intellij.plugins.intelliLang.inject.*
|
||||
import org.intellij.plugins.intelliLang.inject.config.BaseInjection
|
||||
import org.jetbrains.annotations.NonNls
|
||||
import org.jetbrains.kotlin.idea.KotlinJvmBundle
|
||||
import org.jetbrains.kotlin.idea.patterns.KotlinPatterns
|
||||
import org.jetbrains.kotlin.idea.util.addAnnotation
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.idea.util.findAnnotation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.siblings
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
|
||||
@NonNls
|
||||
val KOTLIN_SUPPORT_ID = "kotlin"
|
||||
|
||||
class KotlinLanguageInjectionSupport : AbstractLanguageInjectionSupport() {
|
||||
override fun getId(): String = KOTLIN_SUPPORT_ID
|
||||
|
||||
override fun getPatternClasses() = arrayOf(KotlinPatterns::class.java)
|
||||
|
||||
override fun isApplicableTo(host: PsiLanguageInjectionHost?) = host is KtElement
|
||||
|
||||
override fun useDefaultInjector(host: PsiLanguageInjectionHost?): Boolean = false
|
||||
|
||||
override fun addInjectionInPlace(language: Language?, host: PsiLanguageInjectionHost?): Boolean {
|
||||
if (language == null || host == null) return false
|
||||
|
||||
val configuration = Configuration.getProjectInstance(host.project).advancedConfiguration
|
||||
if (!configuration.isSourceModificationAllowed) {
|
||||
// It's not allowed to modify code without explicit permission. Postpone adding @Inject or comment till it granted.
|
||||
host.putUserData(InjectLanguageAction.FIX_KEY, Processor { fixHost: PsiLanguageInjectionHost? ->
|
||||
fixHost != null && addInjectionInstructionInCode(language, fixHost)
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
if (!addInjectionInstructionInCode(language, host)) {
|
||||
return false
|
||||
}
|
||||
|
||||
TemporaryPlacesRegistry.getInstance(host.project).addHostWithUndo(host, InjectedLanguage.create(language.id))
|
||||
return true
|
||||
}
|
||||
|
||||
override fun removeInjectionInPlace(psiElement: PsiLanguageInjectionHost?): Boolean {
|
||||
if (psiElement == null || psiElement !is KtElement) return false
|
||||
|
||||
val project = psiElement.getProject()
|
||||
|
||||
val injectInstructions = listOfNotNull(
|
||||
findAnnotationInjection(psiElement),
|
||||
findInjectionComment(psiElement)
|
||||
)
|
||||
|
||||
TemporaryPlacesRegistry.getInstance(project).removeHostWithUndo(project, psiElement)
|
||||
|
||||
project.executeWriteCommand(KotlinJvmBundle.message("command.action.remove.injection.in.code.instructions")) {
|
||||
injectInstructions.forEach(PsiElement::delete)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun findCommentInjection(host: PsiElement, commentRef: Ref<PsiElement>?): BaseInjection? {
|
||||
// Do not inject through CommentLanguageInjector, because it injects as simple injection.
|
||||
// We need to behave special for interpolated strings.
|
||||
return null
|
||||
}
|
||||
|
||||
fun findCommentInjection(host: KtElement): BaseInjection? {
|
||||
return InjectorUtils.findCommentInjection(host, "", null)
|
||||
}
|
||||
|
||||
private fun findInjectionComment(host: KtElement): PsiComment? {
|
||||
val commentRef = Ref.create<PsiElement>(null)
|
||||
InjectorUtils.findCommentInjection(host, "", commentRef) ?: return null
|
||||
|
||||
return commentRef.get() as? PsiComment
|
||||
}
|
||||
|
||||
internal fun findAnnotationInjectionLanguageId(host: KtElement): InjectionInfo? {
|
||||
val annotationEntry = findAnnotationInjection(host) ?: return null
|
||||
val extractLanguageFromInjectAnnotation = extractLanguageFromInjectAnnotation(annotationEntry) ?: return null
|
||||
val prefix = extractStringArgumentByName(annotationEntry, "prefix")
|
||||
val suffix = extractStringArgumentByName(annotationEntry, "suffix")
|
||||
return InjectionInfo(extractLanguageFromInjectAnnotation, prefix, suffix)
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractStringArgumentByName(annotationEntry: KtAnnotationEntry, name: String): String? {
|
||||
val namedArgument: ValueArgument = annotationEntry.valueArguments.firstOrNull { it.getArgumentName()?.asName?.asString() == name } ?: return null
|
||||
return extractStringValue(namedArgument)
|
||||
}
|
||||
|
||||
|
||||
private fun extractLanguageFromInjectAnnotation(annotationEntry: KtAnnotationEntry): String? {
|
||||
val firstArgument: ValueArgument = annotationEntry.valueArguments.firstOrNull() ?: return null
|
||||
return extractStringValue(firstArgument)
|
||||
}
|
||||
|
||||
private fun extractStringValue(valueArgument: ValueArgument): String? {
|
||||
val firstStringArgument = valueArgument.getArgumentExpression() as? KtStringTemplateExpression ?: return null
|
||||
val firstStringEntry = firstStringArgument.entries.singleOrNull() ?: return null
|
||||
|
||||
return firstStringEntry.text
|
||||
}
|
||||
|
||||
private fun findAnnotationInjection(host: KtElement): KtAnnotationEntry? {
|
||||
val modifierListOwner = findElementToInjectWithAnnotation(host) ?: return null
|
||||
|
||||
val modifierList = modifierListOwner.modifierList ?: return null
|
||||
|
||||
// Host can't be before annotation
|
||||
if (host.startOffset < modifierList.endOffset) return null
|
||||
|
||||
return modifierListOwner.findAnnotation(FqName(AnnotationUtil.LANGUAGE))
|
||||
}
|
||||
|
||||
private fun canInjectWithAnnotation(host: PsiElement): Boolean {
|
||||
val module = ModuleUtilCore.findModuleForPsiElement(host) ?: return false
|
||||
val javaPsiFacade = JavaPsiFacade.getInstance(module.project)
|
||||
|
||||
return javaPsiFacade.findClass(AnnotationUtil.LANGUAGE, GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module)) != null
|
||||
}
|
||||
|
||||
private fun findElementToInjectWithAnnotation(host: KtElement): KtModifierListOwner? {
|
||||
return PsiTreeUtil.getParentOfType(
|
||||
host,
|
||||
KtModifierListOwner::class.java,
|
||||
false, /* strict */
|
||||
KtBlockExpression::class.java, KtParameterList::class.java, KtTypeParameterList::class.java /* Stop at */
|
||||
)
|
||||
}
|
||||
|
||||
private fun findElementToInjectWithComment(host: KtElement): KtExpression? {
|
||||
val parentBlockExpression = PsiTreeUtil.getParentOfType(
|
||||
host,
|
||||
KtBlockExpression::class.java,
|
||||
true, /* strict */
|
||||
KtDeclaration::class.java /* Stop at */
|
||||
) ?: return null
|
||||
|
||||
return parentBlockExpression.statements.firstOrNull { statement ->
|
||||
PsiTreeUtil.isAncestor(statement, host, false) && checkIsValidPlaceForInjectionWithLineComment(statement, host)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addInjectionInstructionInCode(language: Language, host: PsiLanguageInjectionHost): Boolean {
|
||||
val ktHost = host as? KtElement ?: return false
|
||||
val project = ktHost.project
|
||||
|
||||
// Find the place where injection can be stated with annotation or comment
|
||||
val modifierListOwner = findElementToInjectWithAnnotation(ktHost)
|
||||
|
||||
if (modifierListOwner != null && canInjectWithAnnotation(ktHost)) {
|
||||
project.executeWriteCommand(KotlinJvmBundle.message("command.action.add.injection.annotation")) {
|
||||
modifierListOwner.addAnnotation(FqName(AnnotationUtil.LANGUAGE), "\"${language.id}\"")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Find the place where injection can be done with one-line comment
|
||||
val commentBeforeAnchor: PsiElement =
|
||||
modifierListOwner?.firstNonCommentChild() ?:
|
||||
findElementToInjectWithComment(ktHost) ?:
|
||||
return false
|
||||
|
||||
val psiFactory = KtPsiFactory(project)
|
||||
val injectComment = psiFactory.createComment("//language=" + language.id)
|
||||
|
||||
project.executeWriteCommand(KotlinJvmBundle.message("command.action.add.injection.comment")) {
|
||||
commentBeforeAnchor.parent.addBefore(injectComment, commentBeforeAnchor)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Inspired with InjectorUtils.findCommentInjection()
|
||||
private fun checkIsValidPlaceForInjectionWithLineComment(statement: KtExpression, host: KtElement): Boolean {
|
||||
// make sure comment is close enough and ...
|
||||
val statementStartOffset = statement.startOffset
|
||||
val hostStart = host.startOffset
|
||||
if (hostStart < statementStartOffset || hostStart - statementStartOffset > 120) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (hostStart - statementStartOffset > 2) {
|
||||
// ... there's no non-empty valid host in between comment and e2
|
||||
if (prevWalker(host, statement).asSequence().takeWhile { it != null }.any {
|
||||
it is PsiLanguageInjectionHost && it.isValidHost && !StringUtil.isEmptyOrSpaces(it.text)
|
||||
}) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun PsiElement.firstNonCommentChild(): PsiElement? {
|
||||
return firstChild.siblings().dropWhile { it is PsiComment || it is PsiWhiteSpace }.firstOrNull()
|
||||
}
|
||||
|
||||
// Based on InjectorUtils.prevWalker
|
||||
private fun prevWalker(element: PsiElement, scope: PsiElement): Iterator<PsiElement?> {
|
||||
return object : Iterator<PsiElement?> {
|
||||
private var e: PsiElement? = element
|
||||
|
||||
override fun hasNext(): Boolean = true
|
||||
override fun next(): PsiElement? {
|
||||
val current = e
|
||||
|
||||
if (current == null || current === scope) return null
|
||||
val prev = current.prevSibling
|
||||
e = if (prev != null) {
|
||||
getDeepestLast(prev)
|
||||
}
|
||||
else {
|
||||
val parent = current.parent
|
||||
if (parent === scope || parent is PsiFile) null else parent
|
||||
}
|
||||
return e
|
||||
}
|
||||
}
|
||||
}
|
||||
-181
@@ -1,181 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.idea.scratch.compile
|
||||
|
||||
import com.intellij.execution.configurations.GeneralCommandLine
|
||||
import com.intellij.execution.process.CapturingProcessHandler
|
||||
import com.intellij.openapi.diagnostic.ControlFlowException
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.progress.Task
|
||||
import com.intellij.openapi.project.DumbService
|
||||
import com.intellij.openapi.util.Computable
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
||||
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
||||
import org.jetbrains.kotlin.codegen.filterClassFiles
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.idea.KotlinJvmBundle
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
|
||||
import org.jetbrains.kotlin.idea.debugger.DebuggerUtils
|
||||
import org.jetbrains.kotlin.idea.scratch.LOG
|
||||
import org.jetbrains.kotlin.idea.scratch.ScratchExpression
|
||||
import org.jetbrains.kotlin.idea.scratch.ScratchFile
|
||||
import org.jetbrains.kotlin.idea.scratch.printDebugMessage
|
||||
import org.jetbrains.kotlin.idea.util.JavaParametersBuilder
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import java.io.File
|
||||
|
||||
class KtScratchExecutionSession(
|
||||
private val file: ScratchFile,
|
||||
private val executor: KtCompilingExecutor
|
||||
) {
|
||||
companion object {
|
||||
private const val TIMEOUT_MS = 30000
|
||||
}
|
||||
|
||||
private var backgroundProcessIndicator: ProgressIndicator? = null
|
||||
|
||||
fun execute(callback: () -> Unit) {
|
||||
val psiFile = file.getPsiFile() as? KtFile ?: return executor.errorOccurs(KotlinJvmBundle.message("couldn.t.find.ktfile.for.current.editor"), isFatal = true)
|
||||
|
||||
val expressions = file.getExpressions()
|
||||
if (!executor.checkForErrors(psiFile, expressions)) return
|
||||
|
||||
when (val result = runReadAction { KtScratchSourceFileProcessor().process(expressions) }) {
|
||||
is KtScratchSourceFileProcessor.Result.Error -> return executor.errorOccurs(result.message, isFatal = true)
|
||||
is KtScratchSourceFileProcessor.Result.OK -> {
|
||||
LOG.printDebugMessage("After processing by KtScratchSourceFileProcessor:\n ${result.code}")
|
||||
|
||||
object : Task.Backgroundable(psiFile.project, KotlinJvmBundle.message("running.kotlin.scratch"), true) {
|
||||
override fun run(indicator: ProgressIndicator) {
|
||||
backgroundProcessIndicator = indicator
|
||||
|
||||
val modifiedScratchSourceFile = runReadAction {
|
||||
KtPsiFactory(psiFile.project).createFileWithLightClassSupport("tmp.kt", result.code, psiFile)
|
||||
}
|
||||
|
||||
try {
|
||||
val tempDir = DumbService.getInstance(project).runReadActionInSmartMode(
|
||||
Computable {
|
||||
compileFileToTempDir(modifiedScratchSourceFile, expressions)
|
||||
}
|
||||
) ?: return
|
||||
|
||||
try {
|
||||
val commandLine = createCommandLine(psiFile, file.module, result.mainClassName, tempDir.path)
|
||||
|
||||
LOG.printDebugMessage(commandLine.commandLineString)
|
||||
|
||||
val processHandler = CapturingProcessHandler(commandLine)
|
||||
val executionResult = processHandler.runProcessWithProgressIndicator(indicator, TIMEOUT_MS)
|
||||
when {
|
||||
executionResult.isTimeout -> {
|
||||
executor.errorOccurs(
|
||||
KotlinJvmBundle.message(
|
||||
"couldn.t.get.scratch.execution.result.stopped.by.timeout.0.ms",
|
||||
TIMEOUT_MS
|
||||
)
|
||||
)
|
||||
}
|
||||
executionResult.isCancelled -> {
|
||||
// ignore
|
||||
}
|
||||
else -> {
|
||||
executor.parseOutput(executionResult, expressions)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
tempDir.delete()
|
||||
callback()
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
if (e is ControlFlowException) throw e
|
||||
|
||||
LOG.printDebugMessage(result.code)
|
||||
executor.errorOccurs(e.message ?: KotlinJvmBundle.message("couldn.t.compile.0", psiFile.name), e, isFatal = true)
|
||||
}
|
||||
}
|
||||
}.queue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
backgroundProcessIndicator?.cancel()
|
||||
}
|
||||
|
||||
private fun compileFileToTempDir(psiFile: KtFile, expressions: List<ScratchExpression>): File? {
|
||||
if (!executor.checkForErrors(psiFile, expressions)) return null
|
||||
|
||||
val resolutionFacade = psiFile.getResolutionFacade()
|
||||
val (bindingContext, files) = DebuggerUtils.analyzeInlinedFunctions(resolutionFacade, psiFile, false)
|
||||
|
||||
LOG.printDebugMessage("Analyzed files: \n${files.joinToString("\n") { it.virtualFilePath }}")
|
||||
|
||||
val generateClassFilter = object : GenerationState.GenerateClassFilter() {
|
||||
override fun shouldGeneratePackagePart(ktFile: KtFile) = ktFile == psiFile
|
||||
override fun shouldAnnotateClass(processingClassOrObject: KtClassOrObject) = true
|
||||
override fun shouldGenerateClass(processingClassOrObject: KtClassOrObject) = processingClassOrObject.containingKtFile == psiFile
|
||||
override fun shouldGenerateScript(script: KtScript) = false
|
||||
override fun shouldGenerateCodeFragment(script: KtCodeFragment) = false
|
||||
}
|
||||
|
||||
val state = GenerationState.Builder(
|
||||
file.project,
|
||||
ClassBuilderFactories.BINARIES,
|
||||
resolutionFacade.moduleDescriptor,
|
||||
bindingContext,
|
||||
files,
|
||||
CompilerConfiguration.EMPTY
|
||||
).generateDeclaredClassFilter(generateClassFilter).build()
|
||||
|
||||
KotlinCodegenFacade.compileCorrectFiles(state)
|
||||
|
||||
return writeClassFilesToTempDir(state)
|
||||
}
|
||||
|
||||
private fun writeClassFilesToTempDir(state: GenerationState): File {
|
||||
val classFiles = state.factory.asList().filterClassFiles()
|
||||
|
||||
val dir = FileUtil.createTempDirectory("compile", "scratch")
|
||||
|
||||
LOG.printDebugMessage("Temp output dir: ${dir.path}")
|
||||
|
||||
for (classFile in classFiles) {
|
||||
val tmpOutFile = File(dir, classFile.relativePath)
|
||||
tmpOutFile.parentFile.mkdirs()
|
||||
tmpOutFile.createNewFile()
|
||||
tmpOutFile.writeBytes(classFile.asByteArray())
|
||||
|
||||
LOG.printDebugMessage("Generated class file: ${classFile.relativePath}")
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
private fun createCommandLine(originalFile: KtFile, module: Module?, mainClassName: String, tempOutDir: String): GeneralCommandLine {
|
||||
val javaParameters = JavaParametersBuilder(originalFile.project)
|
||||
.withSdkFrom(module, true)
|
||||
.withMainClassName(mainClassName)
|
||||
.build()
|
||||
|
||||
javaParameters.classPath.add(tempOutDir)
|
||||
|
||||
if (module != null) {
|
||||
javaParameters.classPath.addAll(JavaParametersBuilder.getModuleDependencies(module))
|
||||
}
|
||||
|
||||
ScriptConfigurationManager.getInstance(originalFile.project)
|
||||
.getConfiguration(originalFile)?.let {
|
||||
javaParameters.classPath.addAll(it.dependenciesClassPath.map { f -> f.absolutePath })
|
||||
}
|
||||
|
||||
return javaParameters.toCommandLine()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user