diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/output/InlayScratchOutputHandler.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/output/InlayScratchOutputHandler.kt index 5165b1f6123..eaa3e2888ae 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/output/InlayScratchOutputHandler.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/output/InlayScratchOutputHandler.kt @@ -23,15 +23,16 @@ import com.intellij.openapi.util.text.StringUtil import org.jetbrains.kotlin.idea.scratch.ScratchExpression import org.jetbrains.kotlin.idea.scratch.ScratchFile -class InlayScratchOutputHandler(private val textEditor: TextEditor) : ScratchOutputHandler { +class InlayScratchOutputHandler( + private val textEditor: TextEditor, + private val toolWindowHandler: ScratchOutputHandler +) : ScratchOutputHandler { private val maxLineLength = 120 private val maxInsertOffset = 60 private val minSpaceCount = 4 - private val toolwindowHandler = getToolwindowHandler(textEditor) - override fun onStart(file: ScratchFile) { - toolwindowHandler.onStart(file) + toolWindowHandler.onStart(file) } override fun handle(file: ScratchFile, expression: ScratchExpression, output: ScratchOutput) { @@ -40,21 +41,21 @@ class InlayScratchOutputHandler(private val textEditor: TextEditor) : ScratchOut createInlay(file, expression, output) if (output.type == ScratchOutputType.ERROR) { - toolwindowHandler.handle(file, expression, output) + toolWindowHandler.handle(file, expression, output) } } override fun error(file: ScratchFile, message: String) { - toolwindowHandler.error(file, message) + toolWindowHandler.error(file, message) } override fun onFinish(file: ScratchFile) { - toolwindowHandler.onFinish(file) + toolWindowHandler.onFinish(file) } override fun clear(file: ScratchFile) { clearInlays(textEditor) - toolwindowHandler.clear(file) + toolWindowHandler.clear(file) } private fun createInlay(file: ScratchFile, expression: ScratchExpression, output: ScratchOutput) { @@ -96,7 +97,7 @@ class InlayScratchOutputHandler(private val textEditor: TextEditor) : ScratchOut private fun printToToolWindow(file: ScratchFile, expression: ScratchExpression, output: ScratchOutput) { if (output.type != ScratchOutputType.ERROR) { - toolwindowHandler.handle(file, expression, output) + toolWindowHandler.handle(file, expression, output) } } diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/output/ToolWindowScratchOutputHandler.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/output/ToolWindowScratchOutputHandler.kt index ce569c490aa..eb889c3e2ab 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/output/ToolWindowScratchOutputHandler.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/output/ToolWindowScratchOutputHandler.kt @@ -37,12 +37,68 @@ import org.jetbrains.kotlin.idea.scratch.ScratchExpression import org.jetbrains.kotlin.idea.scratch.ScratchFile import org.jetbrains.kotlin.psi.KtPsiFactory - -fun getToolwindowHandler(parentDisposable: Disposable): ScratchOutputHandler { +/** + * Method to retrieve shared instance of scratches ToolWindow output handler. + * + * [releaseToolWindowHandler] must be called for every output handler received from this method. + * + * Can be called from EDT only. + * + * @return new toolWindow output handler if one does not exist, otherwise returns the existing one. When application in test mode, + * returns [TestOutputHandler]. + */ +fun requestToolWindowHandler(): ScratchOutputHandler { return if (ApplicationManager.getApplication().isUnitTestMode) { TestOutputHandler } else { - ToolWindowScratchOutputHandler(parentDisposable) + ScratchToolWindowHandlerKeeper.requestOutputHandler() + } +} + +/** + * Should be called once with the output handler received from the [requestToolWindowHandler] call. + * + * When release is called for every request, the output handler is actually disposed. + * + * When application in test mode, does nothing. + * + * Can be called from EDT only. + */ +fun releaseToolWindowHandler(scratchOutputHandler: ScratchOutputHandler) { + if (!ApplicationManager.getApplication().isUnitTestMode) { + ScratchToolWindowHandlerKeeper.releaseOutputHandler(scratchOutputHandler) + } +} + +/** + * Implements logic of shared pointer for the toolWindow output handler. + * + * Not thread safe! Can be used only from the EDT. + */ +private object ScratchToolWindowHandlerKeeper { + private var toolWindowHandler: ScratchOutputHandler? = null + private var toolWindowDisposable = Disposer.newDisposable() + private var counter = 0 + + fun requestOutputHandler(): ScratchOutputHandler { + if (counter == 0) { + toolWindowHandler = ToolWindowScratchOutputHandler(toolWindowDisposable) + } + + counter += 1 + return toolWindowHandler!! + } + + fun releaseOutputHandler(scratchOutputHandler: ScratchOutputHandler) { + require(counter > 0) { "Counter is $counter, nothing to release!" } + require(toolWindowHandler === scratchOutputHandler) { "$scratchOutputHandler differs from stored $toolWindowHandler" } + + counter -= 1 + if (counter == 0) { + Disposer.dispose(toolWindowDisposable) + toolWindowDisposable = Disposer.newDisposable() + toolWindowHandler = null + } } } @@ -130,12 +186,12 @@ private class ToolWindowScratchOutputHandler(private val parentDisposable: Dispo private fun createToolWindow(file: ScratchFile): ToolWindow { val project = file.project val toolWindowManager = ToolWindowManager.getInstance(project) - toolWindowManager.registerToolWindow(ScratchToolWindowFactory.ID, false, ToolWindowAnchor.BOTTOM) + toolWindowManager.registerToolWindow(ScratchToolWindowFactory.ID, true, ToolWindowAnchor.BOTTOM) val window = toolWindowManager.getToolWindow(ScratchToolWindowFactory.ID) ScratchToolWindowFactory().createToolWindowContent(project, window) Disposer.register(parentDisposable, Disposable { - window.setAvailable(false, null) + toolWindowManager.unregisterToolWindow(ScratchToolWindowFactory.ID) }) return window diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/ui/KtScratchFileEditorProvider.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/ui/KtScratchFileEditorProvider.kt index 3cfc9c05170..4beb1558d81 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/ui/KtScratchFileEditorProvider.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/ui/KtScratchFileEditorProvider.kt @@ -25,8 +25,7 @@ import com.intellij.pom.Navigatable import com.intellij.psi.PsiManager import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.idea.scratch.* -import org.jetbrains.kotlin.idea.scratch.output.InlayScratchOutputHandler -import org.jetbrains.kotlin.idea.scratch.output.ScratchOutputHandlerAdapter +import org.jetbrains.kotlin.idea.scratch.output.* import org.jetbrains.kotlin.psi.UserDataProperty private const val KTS_SCRATCH_EDITOR_PROVIDER: String = "KtsScratchFileEditorProvider" @@ -56,7 +55,8 @@ class KtScratchFileEditorWithPreview private constructor( preview: TextEditor ) : TextEditorWithPreview(editor, preview), TextEditor { - private val inlayOutputHandler = InlayScratchOutputHandler(editor) + private val toolWindowHandler: ScratchOutputHandler = requestToolWindowHandler() + private val inlayOutputHandler = InlayScratchOutputHandler(editor, toolWindowHandler) private val scratchTopPanel = ScratchTopPanel(scratchFile) init { @@ -71,6 +71,7 @@ class KtScratchFileEditorWithPreview private constructor( override fun dispose() { scratchFile.replScratchExecutor?.stop() scratchFile.compilingScratchExecutor?.stop() + releaseToolWindowHandler(toolWindowHandler) super.dispose() }