KT-32366: Inject toolWindowHandler from InlayScratchOutputHandler

- `toolWindowHandler` would be shared between different scratch editors, so we need `ScratchToolWindowHandlerKeeper` to provide proper mechanism for its acquiring and releasing
- fix problem when scratch output panel is never released after first scratch opening by properly unregistering `toolWindow` with `ToolWindowManager`
This commit is contained in:
Roman Golyshev
2019-08-21 16:52:08 +03:00
committed by Roman Golyshev
parent d0cd4967a7
commit e321ee1396
3 changed files with 75 additions and 17 deletions
@@ -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)
}
}
@@ -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
@@ -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()
}