From 9cff5903666bbfdf56407f9c8c5a81756693f97d Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Thu, 7 Apr 2016 20:06:55 +0300 Subject: [PATCH] Use ReplState to implement completion in IDE REPL Consider sucessfully evaluated lines as previous lines when analyzing console file --- .../KotlinScriptDefinitionProvider.java | 8 +- .../kotlin/console/KotlinConsoleRunner.kt | 90 ++++++++++++++----- .../kotlin/console/ReplOutputHandler.kt | 4 +- 3 files changed, 77 insertions(+), 25 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionProvider.java b/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionProvider.java index fbb5fd5e3f2..f4ec3626f2a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionProvider.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2016 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. @@ -20,7 +20,6 @@ import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.TestOnly; import java.util.ArrayList; import java.util.List; @@ -50,11 +49,14 @@ public class KotlinScriptDefinitionProvider { return findScriptDefinition(psiFile) != null; } - @TestOnly public void addScriptDefinition(@NotNull KotlinScriptDefinition scriptDefinition) { definitions.add(0, scriptDefinition); } + public void removeScriptDefinition(@NotNull KotlinScriptDefinition scriptDefinition) { + definitions.remove(scriptDefinition); + } + public void setScriptDefinitions(@NotNull List definitions) { this.definitions.clear(); this.definitions.addAll(definitions); diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleRunner.kt b/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleRunner.kt index 029ac05ded2..27fb331987c 100644 --- a/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleRunner.kt +++ b/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleRunner.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2016 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. @@ -23,8 +23,6 @@ import com.intellij.execution.console.LanguageConsoleBuilder import com.intellij.execution.console.LanguageConsoleView import com.intellij.execution.console.ProcessBackedConsoleExecuteActionHandler import com.intellij.execution.process.OSProcessHandler -import com.intellij.execution.process.ProcessAdapter -import com.intellij.execution.process.ProcessEvent import com.intellij.execution.runners.AbstractConsoleRunnerWithHistory import com.intellij.execution.ui.RunContentDescriptor import com.intellij.openapi.actionSystem.ActionManager @@ -32,6 +30,7 @@ import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.CommonShortcuts import com.intellij.openapi.actionSystem.DefaultActionGroup import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.runReadAction import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.editor.colors.EditorColors import com.intellij.openapi.editor.ex.EditorEx @@ -43,7 +42,12 @@ import com.intellij.openapi.keymap.KeymapUtil import com.intellij.openapi.module.Module import com.intellij.openapi.project.Project import com.intellij.openapi.util.Disposer +import com.intellij.openapi.vfs.CharsetToolkit +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiFileFactory import com.intellij.psi.PsiManager +import com.intellij.psi.impl.PsiFileFactoryImpl +import com.intellij.testFramework.LightVirtualFile import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.console.actions.BuildAndRestartConsoleAction import org.jetbrains.kotlin.console.actions.KtExecuteCommandAction @@ -51,9 +55,21 @@ import org.jetbrains.kotlin.console.gutter.ConsoleGutterContentProvider import org.jetbrains.kotlin.console.gutter.ConsoleIndicatorRenderer import org.jetbrains.kotlin.console.gutter.IconWithTooltip import org.jetbrains.kotlin.console.gutter.ReplIcons +import org.jetbrains.kotlin.descriptors.ScriptDescriptor import org.jetbrains.kotlin.idea.KotlinLanguage -import org.jetbrains.kotlin.idea.completion.doNotComplete +import org.jetbrains.kotlin.idea.caches.resolve.ModuleTestSourceInfo +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.parsing.KotlinParserDefinition import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtScript +import org.jetbrains.kotlin.psi.moduleInfo +import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil +import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyScriptDescriptor +import org.jetbrains.kotlin.resolve.repl.ReplState +import org.jetbrains.kotlin.script.KotlinScriptDefinition +import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider +import org.jetbrains.kotlin.script.ScriptParameter import java.awt.Color import java.awt.Font import kotlin.properties.Delegates @@ -68,7 +84,13 @@ class KotlinConsoleRunner( title: String, path: String? ) : AbstractConsoleRunnerWithHistory(myProject, title, path) { + + private val replState = ReplState() + override fun finishConsole() { + KotlinConsoleKeeper.getInstance(project).removeConsole(consoleView.virtualFile) + KotlinScriptDefinitionProvider.getInstance(project).removeScriptDefinition(consoleScriptDefinition) + if (ApplicationManager.getApplication().isUnitTestMode) { // Ignore super with myConsoleView.setEditable(false) return @@ -99,17 +121,22 @@ class KotlinConsoleRunner( val executor = CommandExecutor(this) var compilerHelper: ConsoleCompilerHelper by Delegates.notNull() + private val consoleScriptDefinition = object : KotlinScriptDefinition { + override fun isScript(file: PsiFile) = file.originalFile.virtualFile == consoleView.virtualFile + override fun getScriptParameters(scriptDescriptor: ScriptDescriptor) = emptyList() + override fun getScriptName(script: KtScript) = Name.identifier("REPL") + } + override fun createProcess() = cmdLine.createProcess() override fun createConsoleView(): LanguageConsoleView? { - val consoleView = LanguageConsoleBuilder() - .gutterContentProvider(ConsoleGutterContentProvider()) - .build(project, KotlinLanguage.INSTANCE) + val builder = LanguageConsoleBuilder() + + val consoleView = builder.gutterContentProvider(ConsoleGutterContentProvider()).build(project, KotlinLanguage.INSTANCE) + consoleView.prompt = null - disableCompletion(consoleView) - val consoleEditor = consoleView.consoleEditor setupPlaceholder(consoleEditor) @@ -120,9 +147,17 @@ class KotlinConsoleRunner( val executeAction = KtExecuteCommandAction(consoleView.virtualFile) executeAction.registerCustomShortcutSet(CommonShortcuts.CTRL_ENTER, consoleView.consoleEditor.component) + KotlinScriptDefinitionProvider.getInstance(project).addScriptDefinition(consoleScriptDefinition) + enableCompletion(consoleView) + return consoleView } + private fun enableCompletion(consoleView: LanguageConsoleView) { + val consoleKtFile = PsiManager.getInstance(project).findFile(consoleView.virtualFile) as? KtFile ?: return + consoleKtFile.moduleInfo = ModuleTestSourceInfo(module) + } + override fun createProcessHandler(process: Process): OSProcessHandler { val processHandler = ReplOutputHandler( this, @@ -133,11 +168,6 @@ class KotlinConsoleRunner( val keeper = KotlinConsoleKeeper.getInstance(project) keeper.putVirtualFileToConsole(consoleFile, this) - processHandler.addProcessListener(object : ProcessAdapter() { - override fun processTerminated(event: ProcessEvent) { - keeper.removeConsole(consoleFile) - } - }) return processHandler } @@ -180,12 +210,6 @@ class KotlinConsoleRunner( editor.setPlaceholderAttributes(placeholderAttrs) } - private fun disableCompletion(consoleView: LanguageConsoleView) { - val consoleFile = consoleView.virtualFile - val jetFile = PsiManager.getInstance(project).findFile(consoleFile) as? KtFile ?: return - jetFile.doNotComplete = true - } - fun setupGutters() { fun configureEditorGutter(editor: EditorEx, color: Color, iconWithTooltip: IconWithTooltip): RangeHighlighter { editor.settings.isLineMarkerAreaShown = true // hack to show gutter @@ -225,4 +249,30 @@ class KotlinConsoleRunner( processHandler.destroyProcess() Disposer.dispose(disposableDescriptor) } + + fun successfulLine(text: String) { + runReadAction { + val lineNumber = replState.successfulLinesCount + 1 + val virtualFile = + LightVirtualFile("line$lineNumber${KotlinParserDefinition.STD_SCRIPT_EXT}", KotlinLanguage.INSTANCE, text).apply { + charset = CharsetToolkit.UTF8_CHARSET + } + val psiFile = (PsiFileFactory.getInstance(project) as PsiFileFactoryImpl).trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile? + ?: error("Failed to setup PSI for file:\n$text") + + replState.submitLine(psiFile) + psiFile.moduleInfo = ModuleTestSourceInfo(module) + val scriptDescriptor = psiFile.script!!.resolveToDescriptor() as? LazyScriptDescriptor ?: error("Failed to analyze line:\n$text") + ForceResolveUtil.forceResolveAllContents(scriptDescriptor) + replState.lineSuccess(psiFile, scriptDescriptor) + + replState.submitLine(consoleFile) // reset file scope customizer + } + } + + val consoleFile: KtFile + get() { + val consoleFile = consoleView.virtualFile + return PsiManager.getInstance(project).findFile(consoleFile) as KtFile + } } \ No newline at end of file diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/console/ReplOutputHandler.kt b/idea/idea-repl/src/org/jetbrains/kotlin/console/ReplOutputHandler.kt index a5a2c07a190..8f5963bf4f6 100644 --- a/idea/idea-repl/src/org/jetbrains/kotlin/console/ReplOutputHandler.kt +++ b/idea/idea-repl/src/org/jetbrains/kotlin/console/ReplOutputHandler.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2016 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. @@ -89,7 +89,7 @@ class ReplOutputHandler( "COMPILE_ERROR" -> outputProcessor.highlightCompilerErrors(createCompilerMessages(content)) "RUNTIME_ERROR" -> outputProcessor.printRuntimeError("${content.trim()}\n") "INTERNAL_ERROR" -> outputProcessor.printInternalErrorMessage(content) - "SUCCESS" -> Unit + "SUCCESS" -> runner.commandHistory.lastUnprocessedEntry()?.entryText?.let { runner.successfulLine(it) } else -> logError(ReplOutputHandler::class.java, "Unexpected output type:\n$outputType") }