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 Files
|
||||||
scratch.run.button=Run Scratch File
|
scratch.run.button=Run Scratch File
|
||||||
|
scratch.stop.button=Stop scratch execution
|
||||||
scratch.clear.button=Clear results
|
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 class ScratchExecutor(protected val file: ScratchFile) {
|
||||||
abstract fun execute()
|
abstract fun execute()
|
||||||
|
abstract fun stop()
|
||||||
|
|
||||||
protected val handlers = mutableListOf<ScratchOutputHandler>()
|
protected val handlers = mutableListOf<ScratchOutputHandler>()
|
||||||
|
|
||||||
|
|||||||
@@ -23,11 +23,8 @@ import com.intellij.openapi.keymap.KeymapUtil
|
|||||||
import com.intellij.openapi.project.DumbService
|
import com.intellij.openapi.project.DumbService
|
||||||
import com.intellij.task.ProjectTaskManager
|
import com.intellij.task.ProjectTaskManager
|
||||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
import org.jetbrains.kotlin.idea.scratch.ScratchFile
|
import org.jetbrains.kotlin.idea.scratch.*
|
||||||
import org.jetbrains.kotlin.idea.scratch.ScratchFileLanguageProvider
|
|
||||||
import org.jetbrains.kotlin.idea.scratch.getScratchPanelFromSelectedEditor
|
|
||||||
import org.jetbrains.kotlin.idea.scratch.output.ScratchOutputHandlerAdapter
|
import org.jetbrains.kotlin.idea.scratch.output.ScratchOutputHandlerAdapter
|
||||||
import org.jetbrains.kotlin.idea.scratch.printDebugMessage
|
|
||||||
import org.jetbrains.kotlin.idea.scratch.LOG as log
|
import org.jetbrains.kotlin.idea.scratch.LOG as log
|
||||||
|
|
||||||
class RunScratchAction : ScratchAction(
|
class RunScratchAction : ScratchAction(
|
||||||
@@ -75,11 +72,13 @@ class RunScratchAction : ScratchAction(
|
|||||||
|
|
||||||
executor.addOutputHandler(object : ScratchOutputHandlerAdapter() {
|
executor.addOutputHandler(object : ScratchOutputHandlerAdapter() {
|
||||||
override fun onStart(file: ScratchFile) {
|
override fun onStart(file: ScratchFile) {
|
||||||
e.presentation.isEnabled = false
|
ScratchCompilationSupport.start(file, executor)
|
||||||
|
scratchPanel.updateToolbar()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFinish(file: ScratchFile) {
|
override fun onFinish(file: ScratchFile) {
|
||||||
e.presentation.isEnabled = true
|
ScratchCompilationSupport.stop()
|
||||||
|
scratchPanel.updateToolbar()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -109,4 +108,21 @@ class RunScratchAction : ScratchAction(
|
|||||||
executeScratch()
|
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 val TIMEOUT_MS = 30000
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var backgroundProcessIndicator: ProgressIndicator? = null
|
||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
handlers.forEach { it.onStart(file) }
|
handlers.forEach { it.onStart(file) }
|
||||||
|
|
||||||
@@ -76,6 +78,8 @@ class KtCompilingExecutor(file: ScratchFile) : ScratchExecutor(file) {
|
|||||||
|
|
||||||
object : Task.Backgroundable(psiFile.project, "Running Kotlin Scratch...", true) {
|
object : Task.Backgroundable(psiFile.project, "Running Kotlin Scratch...", true) {
|
||||||
override fun run(indicator: ProgressIndicator) {
|
override fun run(indicator: ProgressIndicator) {
|
||||||
|
backgroundProcessIndicator = indicator
|
||||||
|
|
||||||
val modifiedScratchSourceFile = runReadAction {
|
val modifiedScratchSourceFile = runReadAction {
|
||||||
KtPsiFactory(psiFile.project).createFileWithLightClassSupport("tmp.kt", result.code, psiFile)
|
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? {
|
private fun compileFileToTempDir(psiFile: KtFile): File? {
|
||||||
if (!checkForErrors(psiFile)) return null
|
if (!checkForErrors(psiFile)) return null
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,14 @@ class KtScratchReplExecutor(file: ScratchFile) : ScratchExecutor(file) {
|
|||||||
sendCommandToProcess(":quit")
|
sendCommandToProcess(":quit")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun stop() {
|
||||||
|
try {
|
||||||
|
osProcessHandler.process.destroy()
|
||||||
|
} finally {
|
||||||
|
handlers.forEach { it.onFinish(file) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun sendCommandToProcess(command: String) {
|
private fun sendCommandToProcess(command: String) {
|
||||||
LOG.printDebugMessage("Send to REPL: ${command}")
|
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.Disposable
|
||||||
import com.intellij.openapi.actionSystem.ActionManager
|
import com.intellij.openapi.actionSystem.ActionManager
|
||||||
import com.intellij.openapi.actionSystem.ActionPlaces
|
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||||
|
import com.intellij.openapi.actionSystem.ActionToolbar
|
||||||
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.fileEditor.TextEditor
|
import com.intellij.openapi.fileEditor.TextEditor
|
||||||
import com.intellij.openapi.module.Module
|
import com.intellij.openapi.module.Module
|
||||||
import com.intellij.openapi.module.ModuleManager
|
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.ScratchFileLanguageProvider
|
||||||
import org.jetbrains.kotlin.idea.scratch.actions.ClearScratchAction
|
import org.jetbrains.kotlin.idea.scratch.actions.ClearScratchAction
|
||||||
import org.jetbrains.kotlin.idea.scratch.actions.RunScratchAction
|
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.addScratchPanel
|
||||||
import org.jetbrains.kotlin.idea.scratch.removeScratchPanel
|
import org.jetbrains.kotlin.idea.scratch.removeScratchPanel
|
||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
@@ -59,8 +62,11 @@ class ScratchTopPanel private constructor(val scratchFile: ScratchFile) : JPanel
|
|||||||
private val isReplCheckbox: JCheckBox
|
private val isReplCheckbox: JCheckBox
|
||||||
private val isMakeBeforeRunCheckbox: JCheckBox
|
private val isMakeBeforeRunCheckbox: JCheckBox
|
||||||
|
|
||||||
|
private val actionsToolbar: ActionToolbar
|
||||||
|
|
||||||
init {
|
init {
|
||||||
add(createActionsToolbar())
|
actionsToolbar = createActionsToolbar()
|
||||||
|
add(actionsToolbar.component)
|
||||||
|
|
||||||
moduleChooser = createModuleChooser(scratchFile.project)
|
moduleChooser = createModuleChooser(scratchFile.project)
|
||||||
add(JLabel("Use classpath of module"))
|
add(JLabel("Use classpath of module"))
|
||||||
@@ -123,14 +129,21 @@ class ScratchTopPanel private constructor(val scratchFile: ScratchFile) : JPanel
|
|||||||
isMakeBeforeRunCheckbox.isSelected = isSelected
|
isMakeBeforeRunCheckbox.isSelected = isSelected
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createActionsToolbar(): JComponent {
|
fun updateToolbar() {
|
||||||
|
ApplicationManager.getApplication().invokeLater {
|
||||||
|
actionsToolbar.updateActionsImmediately()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createActionsToolbar(): ActionToolbar {
|
||||||
val toolbarGroup = DefaultActionGroup().apply {
|
val toolbarGroup = DefaultActionGroup().apply {
|
||||||
add(RunScratchAction())
|
add(RunScratchAction())
|
||||||
|
add(StopScratchAction())
|
||||||
addSeparator()
|
addSeparator()
|
||||||
add(ClearScratchAction())
|
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 {
|
private fun createModuleChooser(project: Project): ModulesComboBox {
|
||||||
|
|||||||
@@ -198,6 +198,9 @@
|
|||||||
<action id="Kotlin.ClearScratch" class="org.jetbrains.kotlin.idea.scratch.actions.ClearScratchAction"
|
<action id="Kotlin.ClearScratch" class="org.jetbrains.kotlin.idea.scratch.actions.ClearScratchAction"
|
||||||
text="Clear Kotlin Scratch" description="Clear Kotlin Scratch">
|
text="Clear Kotlin Scratch" description="Clear Kotlin Scratch">
|
||||||
</action>
|
</action>
|
||||||
|
<action id="Kotlin.StopScratch" class="org.jetbrains.kotlin.idea.scratch.actions.StopScratchAction"
|
||||||
|
text="Stop scratch execution" description="Stop scratch execution">
|
||||||
|
</action>
|
||||||
</actions>
|
</actions>
|
||||||
|
|
||||||
<extensions defaultExtensionNs="com.intellij">
|
<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.core.script.ScriptDependenciesManager
|
||||||
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingUtil
|
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingUtil
|
||||||
import org.jetbrains.kotlin.idea.scratch.actions.RunScratchAction
|
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.scratch.output.InlayScratchFileRenderer
|
||||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
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")
|
val (editor, scratchPanel) = getEditorWithScratchPanel(myManager, scratchFile)?: error("Couldn't find scratch panel")
|
||||||
scratchPanel.setReplMode(isRepl)
|
scratchPanel.setReplMode(isRepl)
|
||||||
|
|
||||||
val action = RunScratchAction()
|
launchScratch(scratchFile)
|
||||||
val event = getActionEvent(scratchFile, action)
|
|
||||||
launchAction(event, action)
|
|
||||||
|
|
||||||
UIUtil.dispatchAllInvocationEvents()
|
UIUtil.dispatchAllInvocationEvents()
|
||||||
|
|
||||||
val start = System.currentTimeMillis()
|
val start = System.currentTimeMillis()
|
||||||
// wait until output is displayed in editor or for 1 minute
|
// 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)
|
Thread.sleep(100)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +150,10 @@ abstract class AbstractScratchRunActionTest : FileEditorManagerTestCase() {
|
|||||||
KotlinTestUtils.assertEqualsToFile(expectedFile, actualOutput.toString())
|
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)
|
action.beforeActionPerformedUpdate(e)
|
||||||
Assert.assertTrue(e.presentation.isEnabled && e.presentation.isVisible)
|
Assert.assertTrue(e.presentation.isEnabled && e.presentation.isVisible)
|
||||||
action.actionPerformed(e)
|
action.actionPerformed(e)
|
||||||
|
|||||||
Reference in New Issue
Block a user