Use ReplState to implement completion in IDE REPL

Consider sucessfully evaluated lines as previous lines when analyzing console file
This commit is contained in:
Pavel V. Talanov
2016-04-07 20:06:55 +03:00
parent 08ef1749b6
commit 9cff590366
3 changed files with 77 additions and 25 deletions
@@ -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<KotlinScriptDefinition> definitions) {
this.definitions.clear();
this.definitions.addAll(definitions);
@@ -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<LanguageConsoleView>(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<ScriptParameter>()
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
}
}
@@ -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")
}