Scratch: implement stop action
^KT-28643 Fixed
This commit is contained in:
@@ -250,4 +250,5 @@ android.klint.inspections.group.name=Android Lint for Kotlin
|
||||
|
||||
# Scratch Files
|
||||
scratch.run.button=Run Scratch File
|
||||
scratch.stop.button=Stop scratch execution
|
||||
scratch.clear.button=Clear results
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.idea.scratch.output.ScratchOutputHandler
|
||||
|
||||
abstract class ScratchExecutor(protected val file: ScratchFile) {
|
||||
abstract fun execute()
|
||||
abstract fun stop()
|
||||
|
||||
protected val handlers = mutableListOf<ScratchOutputHandler>()
|
||||
|
||||
|
||||
@@ -23,11 +23,8 @@ import com.intellij.openapi.keymap.KeymapUtil
|
||||
import com.intellij.openapi.project.DumbService
|
||||
import com.intellij.task.ProjectTaskManager
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.scratch.ScratchFile
|
||||
import org.jetbrains.kotlin.idea.scratch.ScratchFileLanguageProvider
|
||||
import org.jetbrains.kotlin.idea.scratch.getScratchPanelFromSelectedEditor
|
||||
import org.jetbrains.kotlin.idea.scratch.*
|
||||
import org.jetbrains.kotlin.idea.scratch.output.ScratchOutputHandlerAdapter
|
||||
import org.jetbrains.kotlin.idea.scratch.printDebugMessage
|
||||
import org.jetbrains.kotlin.idea.scratch.LOG as log
|
||||
|
||||
class RunScratchAction : ScratchAction(
|
||||
@@ -75,11 +72,13 @@ class RunScratchAction : ScratchAction(
|
||||
|
||||
executor.addOutputHandler(object : ScratchOutputHandlerAdapter() {
|
||||
override fun onStart(file: ScratchFile) {
|
||||
e.presentation.isEnabled = false
|
||||
ScratchCompilationSupport.start(file, executor)
|
||||
scratchPanel.updateToolbar()
|
||||
}
|
||||
|
||||
override fun onFinish(file: ScratchFile) {
|
||||
e.presentation.isEnabled = true
|
||||
ScratchCompilationSupport.stop()
|
||||
scratchPanel.updateToolbar()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -109,4 +108,21 @@ class RunScratchAction : ScratchAction(
|
||||
executeScratch()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
super.update(e)
|
||||
|
||||
e.presentation.isEnabled = !ScratchCompilationSupport.isAnyInProgress()
|
||||
|
||||
if (e.presentation.isEnabled) {
|
||||
e.presentation.text = templatePresentation.text
|
||||
} else {
|
||||
e.presentation.text = "Other Scratch file execution is in progress"
|
||||
}
|
||||
|
||||
val project = e.project ?: return
|
||||
val panel = getScratchPanelFromSelectedEditor(project) ?: return
|
||||
|
||||
e.presentation.isVisible = !ScratchCompilationSupport.isInProgress(panel.scratchFile)
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.actions
|
||||
|
||||
import org.jetbrains.kotlin.idea.scratch.ScratchExecutor
|
||||
import org.jetbrains.kotlin.idea.scratch.ScratchFile
|
||||
|
||||
object ScratchCompilationSupport {
|
||||
private var inProgress = false
|
||||
|
||||
private var file: ScratchFile? = null
|
||||
private var executor: ScratchExecutor? = null
|
||||
|
||||
fun isInProgress(file: ScratchFile): Boolean = inProgress && this.file == file
|
||||
fun isAnyInProgress(): Boolean = inProgress
|
||||
|
||||
fun start(file: ScratchFile, executor: ScratchExecutor) {
|
||||
this.inProgress = true
|
||||
|
||||
this.file = file
|
||||
this.executor = executor
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
this.inProgress = false
|
||||
|
||||
this.file = null
|
||||
this.executor = null
|
||||
}
|
||||
|
||||
fun forceStop() {
|
||||
this.executor?.stop()
|
||||
|
||||
stop()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.actions
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.scratch.getScratchPanelFromSelectedEditor
|
||||
import org.jetbrains.kotlin.idea.scratch.LOG as log
|
||||
|
||||
class StopScratchAction : ScratchAction(
|
||||
KotlinBundle.message("scratch.stop.button"),
|
||||
AllIcons.Actions.Suspend
|
||||
) {
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
ScratchCompilationSupport.forceStop()
|
||||
}
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
super.update(e)
|
||||
|
||||
val project = e.project ?: return
|
||||
val panel = getScratchPanelFromSelectedEditor(project) ?: return
|
||||
|
||||
e.presentation.isEnabledAndVisible = ScratchCompilationSupport.isInProgress(panel.scratchFile)
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,8 @@ class KtCompilingExecutor(file: ScratchFile) : ScratchExecutor(file) {
|
||||
private val TIMEOUT_MS = 30000
|
||||
}
|
||||
|
||||
private var backgroundProcessIndicator: ProgressIndicator? = null
|
||||
|
||||
override fun execute() {
|
||||
handlers.forEach { it.onStart(file) }
|
||||
|
||||
@@ -76,6 +78,8 @@ class KtCompilingExecutor(file: ScratchFile) : ScratchExecutor(file) {
|
||||
|
||||
object : Task.Backgroundable(psiFile.project, "Running Kotlin Scratch...", true) {
|
||||
override fun run(indicator: ProgressIndicator) {
|
||||
backgroundProcessIndicator = indicator
|
||||
|
||||
val modifiedScratchSourceFile = runReadAction {
|
||||
KtPsiFactory(psiFile.project).createFileWithLightClassSupport("tmp.kt", result.code, psiFile)
|
||||
}
|
||||
@@ -114,6 +118,14 @@ class KtCompilingExecutor(file: ScratchFile) : ScratchExecutor(file) {
|
||||
}
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
try {
|
||||
backgroundProcessIndicator?.cancel()
|
||||
} finally {
|
||||
handlers.forEach { it.onFinish(file) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun compileFileToTempDir(psiFile: KtFile): File? {
|
||||
if (!checkForErrors(psiFile)) return null
|
||||
|
||||
|
||||
@@ -58,6 +58,14 @@ class KtScratchReplExecutor(file: ScratchFile) : ScratchExecutor(file) {
|
||||
sendCommandToProcess(":quit")
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
try {
|
||||
osProcessHandler.process.destroy()
|
||||
} finally {
|
||||
handlers.forEach { it.onFinish(file) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendCommandToProcess(command: String) {
|
||||
LOG.printDebugMessage("Send to REPL: ${command}")
|
||||
|
||||
|
||||
@@ -22,7 +22,9 @@ import com.intellij.execution.ui.ConfigurationModuleSelector
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||
import com.intellij.openapi.actionSystem.ActionToolbar
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.fileEditor.TextEditor
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
@@ -38,6 +40,7 @@ import org.jetbrains.kotlin.idea.scratch.ScratchFile
|
||||
import org.jetbrains.kotlin.idea.scratch.ScratchFileLanguageProvider
|
||||
import org.jetbrains.kotlin.idea.scratch.actions.ClearScratchAction
|
||||
import org.jetbrains.kotlin.idea.scratch.actions.RunScratchAction
|
||||
import org.jetbrains.kotlin.idea.scratch.actions.StopScratchAction
|
||||
import org.jetbrains.kotlin.idea.scratch.addScratchPanel
|
||||
import org.jetbrains.kotlin.idea.scratch.removeScratchPanel
|
||||
import javax.swing.*
|
||||
@@ -59,8 +62,11 @@ class ScratchTopPanel private constructor(val scratchFile: ScratchFile) : JPanel
|
||||
private val isReplCheckbox: JCheckBox
|
||||
private val isMakeBeforeRunCheckbox: JCheckBox
|
||||
|
||||
private val actionsToolbar: ActionToolbar
|
||||
|
||||
init {
|
||||
add(createActionsToolbar())
|
||||
actionsToolbar = createActionsToolbar()
|
||||
add(actionsToolbar.component)
|
||||
|
||||
moduleChooser = createModuleChooser(scratchFile.project)
|
||||
add(JLabel("Use classpath of module"))
|
||||
@@ -123,14 +129,21 @@ class ScratchTopPanel private constructor(val scratchFile: ScratchFile) : JPanel
|
||||
isMakeBeforeRunCheckbox.isSelected = isSelected
|
||||
}
|
||||
|
||||
private fun createActionsToolbar(): JComponent {
|
||||
fun updateToolbar() {
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
actionsToolbar.updateActionsImmediately()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createActionsToolbar(): ActionToolbar {
|
||||
val toolbarGroup = DefaultActionGroup().apply {
|
||||
add(RunScratchAction())
|
||||
add(StopScratchAction())
|
||||
addSeparator()
|
||||
add(ClearScratchAction())
|
||||
}
|
||||
|
||||
return ActionManager.getInstance().createActionToolbar(ActionPlaces.EDITOR_TOOLBAR, toolbarGroup, true).component
|
||||
return ActionManager.getInstance().createActionToolbar(ActionPlaces.EDITOR_TOOLBAR, toolbarGroup, true)
|
||||
}
|
||||
|
||||
private fun createModuleChooser(project: Project): ModulesComboBox {
|
||||
|
||||
@@ -198,6 +198,9 @@
|
||||
<action id="Kotlin.ClearScratch" class="org.jetbrains.kotlin.idea.scratch.actions.ClearScratchAction"
|
||||
text="Clear Kotlin Scratch" description="Clear Kotlin Scratch">
|
||||
</action>
|
||||
<action id="Kotlin.StopScratch" class="org.jetbrains.kotlin.idea.scratch.actions.StopScratchAction"
|
||||
text="Stop scratch execution" description="Stop scratch execution">
|
||||
</action>
|
||||
</actions>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager
|
||||
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingUtil
|
||||
import org.jetbrains.kotlin.idea.scratch.actions.RunScratchAction
|
||||
import org.jetbrains.kotlin.idea.scratch.actions.ScratchCompilationSupport
|
||||
import org.jetbrains.kotlin.idea.scratch.output.InlayScratchFileRenderer
|
||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
@@ -113,15 +114,13 @@ abstract class AbstractScratchRunActionTest : FileEditorManagerTestCase() {
|
||||
val (editor, scratchPanel) = getEditorWithScratchPanel(myManager, scratchFile)?: error("Couldn't find scratch panel")
|
||||
scratchPanel.setReplMode(isRepl)
|
||||
|
||||
val action = RunScratchAction()
|
||||
val event = getActionEvent(scratchFile, action)
|
||||
launchAction(event, action)
|
||||
launchScratch(scratchFile)
|
||||
|
||||
UIUtil.dispatchAllInvocationEvents()
|
||||
|
||||
val start = System.currentTimeMillis()
|
||||
// wait until output is displayed in editor or for 1 minute
|
||||
while (!event.presentation.isEnabled && (System.currentTimeMillis() - start) < 60000) {
|
||||
while (ScratchCompilationSupport.isAnyInProgress() && (System.currentTimeMillis() - start) < 60000) {
|
||||
Thread.sleep(100)
|
||||
}
|
||||
|
||||
@@ -151,7 +150,10 @@ abstract class AbstractScratchRunActionTest : FileEditorManagerTestCase() {
|
||||
KotlinTestUtils.assertEqualsToFile(expectedFile, actualOutput.toString())
|
||||
}
|
||||
|
||||
private fun launchAction(e: TestActionEvent, action: AnAction) {
|
||||
private fun launchScratch(scratchFile: VirtualFile) {
|
||||
val action = RunScratchAction()
|
||||
val e = getActionEvent(scratchFile, action)
|
||||
|
||||
action.beforeActionPerformedUpdate(e)
|
||||
Assert.assertTrue(e.presentation.isEnabled && e.presentation.isVisible)
|
||||
action.actionPerformed(e)
|
||||
|
||||
Reference in New Issue
Block a user