Scratch: implement output handlers for displaying execution results
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.idea.scratch
|
package org.jetbrains.kotlin.idea.scratch
|
||||||
|
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.output.InlayScratchOutputHandler
|
||||||
import org.jetbrains.kotlin.idea.scratch.repl.KtScratchReplExecutor
|
import org.jetbrains.kotlin.idea.scratch.repl.KtScratchReplExecutor
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
|
||||||
@@ -26,4 +27,5 @@ class KtScratchFileLanguageProvider : ScratchFileLanguageProvider() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun createReplExecutor(file: ScratchFile) = KtScratchReplExecutor(file)
|
override fun createReplExecutor(file: ScratchFile) = KtScratchReplExecutor(file)
|
||||||
|
override fun getOutputHandler() = InlayScratchOutputHandler
|
||||||
}
|
}
|
||||||
@@ -16,6 +16,14 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.scratch
|
package org.jetbrains.kotlin.idea.scratch
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
protected val handlers = mutableListOf<ScratchOutputHandler>()
|
||||||
|
|
||||||
|
fun addOutputHandler(outputHandler: ScratchOutputHandler) {
|
||||||
|
handlers.add(outputHandler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -21,11 +21,14 @@ import com.intellij.lang.LanguageExtension
|
|||||||
import com.intellij.openapi.fileTypes.FileType
|
import com.intellij.openapi.fileTypes.FileType
|
||||||
import com.intellij.openapi.fileTypes.LanguageFileType
|
import com.intellij.openapi.fileTypes.LanguageFileType
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.output.ScratchOutputHandler
|
||||||
|
|
||||||
abstract class ScratchFileLanguageProvider {
|
abstract class ScratchFileLanguageProvider {
|
||||||
abstract fun createFile(psiFile: PsiFile): ScratchFile?
|
abstract fun createFile(psiFile: PsiFile): ScratchFile?
|
||||||
abstract fun createReplExecutor(file: ScratchFile): ScratchExecutor?
|
abstract fun createReplExecutor(file: ScratchFile): ScratchExecutor?
|
||||||
|
|
||||||
|
abstract fun getOutputHandler(): ScratchOutputHandler
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val EXTENSION = LanguageExtension<ScratchFileLanguageProvider>("org.jetbrains.kotlin.scratchFileLanguageProvider")
|
private val EXTENSION = LanguageExtension<ScratchFileLanguageProvider>("org.jetbrains.kotlin.scratchFileLanguageProvider")
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,10 @@ import com.intellij.openapi.actionSystem.AnAction
|
|||||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||||
import com.intellij.openapi.compiler.CompilerManager
|
import com.intellij.openapi.compiler.CompilerManager
|
||||||
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.ScratchFileLanguageProvider
|
import org.jetbrains.kotlin.idea.scratch.ScratchFileLanguageProvider
|
||||||
import org.jetbrains.kotlin.idea.scratch.getScratchPanelFromSelectedEditor
|
import org.jetbrains.kotlin.idea.scratch.getScratchPanelFromSelectedEditor
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.output.ScratchOutputHandlerAdapter
|
||||||
|
|
||||||
class RunScratchAction : AnAction(
|
class RunScratchAction : AnAction(
|
||||||
KotlinBundle.message("scratch.run.button"),
|
KotlinBundle.message("scratch.run.button"),
|
||||||
@@ -40,19 +42,33 @@ class RunScratchAction : AnAction(
|
|||||||
|
|
||||||
val provider = ScratchFileLanguageProvider.get(scratchFile.psiFile.language) ?: return
|
val provider = ScratchFileLanguageProvider.get(scratchFile.psiFile.language) ?: return
|
||||||
|
|
||||||
|
val handler = provider.getOutputHandler()
|
||||||
|
|
||||||
val module = scratchTopPanel.getModule()
|
val module = scratchTopPanel.getModule()
|
||||||
if (module == null) {
|
if (module == null) {
|
||||||
|
handler.error(scratchFile, "Module should be selected")
|
||||||
|
handler.onFinish(scratchFile)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val runnable = r@ {
|
val runnable = r@ {
|
||||||
val executor = provider.createReplExecutor(scratchFile)
|
val executor = provider.createReplExecutor(scratchFile)
|
||||||
if (executor == null) {
|
if (executor == null) {
|
||||||
|
handler.error(scratchFile, "Couldn't run file using REPL")
|
||||||
|
handler.onFinish(scratchFile)
|
||||||
return@r
|
return@r
|
||||||
}
|
}
|
||||||
|
|
||||||
e.presentation.isEnabled = false
|
e.presentation.isEnabled = false
|
||||||
|
|
||||||
|
executor.addOutputHandler(handler)
|
||||||
|
|
||||||
|
executor.addOutputHandler(object : ScratchOutputHandlerAdapter() {
|
||||||
|
override fun onFinish(file: ScratchFile) {
|
||||||
|
e.presentation.isEnabled = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
executor.execute()
|
executor.execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+162
@@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.scratch.output
|
||||||
|
|
||||||
|
import com.intellij.execution.ui.ConsoleViewContentType
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.editor.EditorCustomElementRenderer
|
||||||
|
import com.intellij.openapi.editor.impl.ComplementaryFontsRegistry
|
||||||
|
import com.intellij.openapi.editor.impl.FontInfo
|
||||||
|
import com.intellij.openapi.editor.markup.TextAttributes
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
|
import com.intellij.openapi.fileEditor.TextEditor
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.Disposer
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.psi.PsiDocumentManager
|
||||||
|
import com.intellij.ui.Gray
|
||||||
|
import com.intellij.ui.JBColor
|
||||||
|
import com.intellij.util.ui.UIUtil
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.ScratchExpression
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.ScratchFile
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.ui.showToolWindow
|
||||||
|
import java.awt.Color
|
||||||
|
import java.awt.Font
|
||||||
|
import java.awt.Graphics
|
||||||
|
import java.awt.Rectangle
|
||||||
|
|
||||||
|
object InlayScratchOutputHandler : ScratchOutputHandler {
|
||||||
|
override fun onStart(file: ScratchFile) {
|
||||||
|
clearInlays(file.psiFile.project, file.psiFile.virtualFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handle(file: ScratchFile, expression: ScratchExpression, output: ScratchOutput) {
|
||||||
|
val document = PsiDocumentManager.getInstance(file.psiFile.project).getDocument(file.psiFile) ?: return
|
||||||
|
val lineEndOffset = document.getLineEndOffset(expression.lineStart)
|
||||||
|
createInlay(file.psiFile.project, file.psiFile.virtualFile, lineEndOffset, output.text.substringBefore("\n"), output.type)
|
||||||
|
|
||||||
|
if (output.type == ScratchOutputType.ERROR) {
|
||||||
|
error(file, output.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun error(file: ScratchFile, message: String) {
|
||||||
|
// todo multiple errors, clear, close
|
||||||
|
showToolWindow(file.psiFile.project, message, ConsoleViewContentType.ERROR_OUTPUT)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFinish(file: ScratchFile) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clear(file: ScratchFile) {
|
||||||
|
clearInlays(file.psiFile.project, file.psiFile.virtualFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createInlay(project: Project, file: VirtualFile, offset: Int, inlayText: String, outputType: ScratchOutputType) {
|
||||||
|
UIUtil.invokeLaterIfNeeded {
|
||||||
|
val textEditor = FileEditorManager.getInstance(project).getSelectedEditor(file) as? TextEditor ?: return@invokeLaterIfNeeded
|
||||||
|
val editor = textEditor.editor
|
||||||
|
|
||||||
|
val text = editor.document.immutableCharSequence
|
||||||
|
var insertOffset = offset
|
||||||
|
while (insertOffset < text.length && Character.isJavaIdentifierPart(text[insertOffset])) insertOffset++
|
||||||
|
|
||||||
|
val existing = editor.inlayModel
|
||||||
|
.getInlineElementsInRange(insertOffset, insertOffset)
|
||||||
|
.singleOrNull { it.renderer is ScratchFileRenderer }
|
||||||
|
if (existing != null) {
|
||||||
|
existing.dispose()
|
||||||
|
editor.inlayModel.addInlineElement(
|
||||||
|
insertOffset,
|
||||||
|
ScratchFileRenderer((existing.renderer as ScratchFileRenderer).text + "; " + inlayText, outputType)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
editor.inlayModel.addInlineElement(insertOffset, ScratchFileRenderer(" $inlayText", outputType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearInlays(project: Project, file: VirtualFile) {
|
||||||
|
UIUtil.invokeLaterIfNeeded {
|
||||||
|
val editors = FileEditorManager.getInstance(project).getEditors(file)
|
||||||
|
editors.filterIsInstance<TextEditor>()
|
||||||
|
.flatMap { it.editor.inlayModel.getInlineElementsInRange(0, it.editor.document.textLength) }
|
||||||
|
.filter { it.renderer is ScratchFileRenderer }
|
||||||
|
.forEach { Disposer.dispose(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScratchFileRenderer(val text: String, val outputType: ScratchOutputType) : EditorCustomElementRenderer {
|
||||||
|
private fun getFontInfo(editor: Editor): FontInfo {
|
||||||
|
val colorsScheme = editor.colorsScheme
|
||||||
|
val fontPreferences = colorsScheme.fontPreferences
|
||||||
|
val attributes = getAttributes()
|
||||||
|
val fontStyle = attributes.fontType
|
||||||
|
return ComplementaryFontsRegistry.getFontAbleToDisplay(
|
||||||
|
'a'.toInt(), fontStyle, fontPreferences, FontInfo.getFontRenderContext(editor.contentComponent)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun calcWidthInPixels(editor: Editor): Int {
|
||||||
|
val fontInfo = getFontInfo(editor)
|
||||||
|
return fontInfo.fontMetrics().stringWidth(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun paint(editor: Editor, g: Graphics, r: Rectangle, textAttributes: TextAttributes) {
|
||||||
|
val attributes = getAttributes()
|
||||||
|
val fgColor = attributes.foregroundColor ?: return
|
||||||
|
g.color = fgColor
|
||||||
|
val fontInfo = getFontInfo(editor)
|
||||||
|
g.font = fontInfo.font
|
||||||
|
val metrics = fontInfo.fontMetrics()
|
||||||
|
g.drawString(text, r.x, r.y + metrics.ascent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAttributes(): TextAttributes {
|
||||||
|
return when (outputType) {
|
||||||
|
ScratchOutputType.OUTPUT -> userOutputAttributes
|
||||||
|
ScratchOutputType.RESULT -> normalAttributes
|
||||||
|
ScratchOutputType.ERROR -> errorAttributes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "${outputType.name}: ${text.trim()}"
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val normalAttributes = TextAttributes(
|
||||||
|
JBColor(Gray._135, Color(0x3d8065)),
|
||||||
|
null, null, null,
|
||||||
|
Font.ITALIC
|
||||||
|
)
|
||||||
|
|
||||||
|
private val errorAttributes = TextAttributes(
|
||||||
|
JBColor(Color.RED, Color.RED),
|
||||||
|
null, null, null,
|
||||||
|
Font.ITALIC
|
||||||
|
)
|
||||||
|
|
||||||
|
private val userOutputAttributes = TextAttributes(
|
||||||
|
JBColor(Color.GREEN, Color.GREEN),
|
||||||
|
null, null, null,
|
||||||
|
Font.ITALIC
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.scratch.output
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.ScratchExpression
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.ScratchFile
|
||||||
|
|
||||||
|
interface ScratchOutputHandler {
|
||||||
|
fun onStart(file: ScratchFile)
|
||||||
|
fun handle(file: ScratchFile, expression: ScratchExpression, output: ScratchOutput)
|
||||||
|
fun error(file: ScratchFile, message: String)
|
||||||
|
fun onFinish(file: ScratchFile)
|
||||||
|
fun clear(file: ScratchFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ScratchOutput(val text: String, val type: ScratchOutputType)
|
||||||
|
|
||||||
|
enum class ScratchOutputType {
|
||||||
|
RESULT,
|
||||||
|
OUTPUT,
|
||||||
|
ERROR
|
||||||
|
}
|
||||||
|
|
||||||
|
open class ScratchOutputHandlerAdapter: ScratchOutputHandler {
|
||||||
|
override fun onStart(file: ScratchFile) {}
|
||||||
|
override fun handle(file: ScratchFile, expression: ScratchExpression, output: ScratchOutput) {}
|
||||||
|
override fun error(file: ScratchFile, message: String) {}
|
||||||
|
override fun onFinish(file: ScratchFile) {}
|
||||||
|
override fun clear(file: ScratchFile) {}
|
||||||
|
}
|
||||||
@@ -28,6 +28,8 @@ import org.jetbrains.kotlin.console.actions.logError
|
|||||||
import org.jetbrains.kotlin.idea.scratch.ScratchExecutor
|
import org.jetbrains.kotlin.idea.scratch.ScratchExecutor
|
||||||
import org.jetbrains.kotlin.idea.scratch.ScratchExpression
|
import org.jetbrains.kotlin.idea.scratch.ScratchExpression
|
||||||
import org.jetbrains.kotlin.idea.scratch.ScratchFile
|
import org.jetbrains.kotlin.idea.scratch.ScratchFile
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.output.ScratchOutput
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.output.ScratchOutputType
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import org.xml.sax.InputSource
|
import org.xml.sax.InputSource
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
@@ -41,7 +43,9 @@ class KtScratchReplExecutor(file: ScratchFile) : ScratchExecutor(file) {
|
|||||||
private lateinit var osProcessHandler: OSProcessHandler
|
private lateinit var osProcessHandler: OSProcessHandler
|
||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
val module = file.module ?: return
|
handlers.forEach { it.onStart(file) }
|
||||||
|
|
||||||
|
val module = file.module ?: return error(file, "Module should be selected")
|
||||||
val cmdLine = KotlinConsoleKeeper.createCommandLine(module)
|
val cmdLine = KotlinConsoleKeeper.createCommandLine(module)
|
||||||
|
|
||||||
osProcessHandler = ReplOSProcessHandler(cmdLine)
|
osProcessHandler = ReplOSProcessHandler(cmdLine)
|
||||||
@@ -71,6 +75,11 @@ class KtScratchReplExecutor(file: ScratchFile) : ScratchExecutor(file) {
|
|||||||
processInputOS.flush()
|
processInputOS.flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun error(file: ScratchFile, message: String) {
|
||||||
|
handlers.forEach { it.error(file, message) }
|
||||||
|
handlers.forEach { it.onFinish(file) }
|
||||||
|
}
|
||||||
|
|
||||||
private class ReplHistory {
|
private class ReplHistory {
|
||||||
private var entries = arrayListOf<ScratchExpression>()
|
private var entries = arrayListOf<ScratchExpression>()
|
||||||
private var processedEntriesCount: Int = 0
|
private var processedEntriesCount: Int = 0
|
||||||
@@ -104,6 +113,10 @@ class KtScratchReplExecutor(file: ScratchFile) : ScratchExecutor(file) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun notifyProcessTerminated(exitCode: Int) {
|
||||||
|
handlers.forEach { it.onFinish(file) }
|
||||||
|
}
|
||||||
|
|
||||||
private fun strToSource(s: String, encoding: Charset = Charsets.UTF_8) = InputSource(ByteArrayInputStream(s.toByteArray(encoding)))
|
private fun strToSource(s: String, encoding: Charset = Charsets.UTF_8) = InputSource(ByteArrayInputStream(s.toByteArray(encoding)))
|
||||||
|
|
||||||
private fun handleReplMessage(text: String) {
|
private fun handleReplMessage(text: String) {
|
||||||
@@ -111,6 +124,7 @@ class KtScratchReplExecutor(file: ScratchFile) : ScratchExecutor(file) {
|
|||||||
val output = try {
|
val output = try {
|
||||||
factory.newDocumentBuilder().parse(strToSource(text))
|
factory.newDocumentBuilder().parse(strToSource(text))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
handlers.forEach { it.error(file, "Couldn't parse REPL output: $text") }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +136,7 @@ class KtScratchReplExecutor(file: ScratchFile) : ScratchExecutor(file) {
|
|||||||
history.entryProcessed()
|
history.entryProcessed()
|
||||||
}
|
}
|
||||||
|
|
||||||
val result = content
|
val result = parseReplOutput(content, outputType)
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
val lastExpression = if (outputType == "USER_OUTPUT") {
|
val lastExpression = if (outputType == "USER_OUTPUT") {
|
||||||
// success command is printed after user output
|
// success command is printed after user output
|
||||||
@@ -132,9 +146,21 @@ class KtScratchReplExecutor(file: ScratchFile) : ScratchExecutor(file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (lastExpression != null) {
|
if (lastExpression != null) {
|
||||||
|
handlers.forEach { it.handle(file, lastExpression, result) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseReplOutput(text: String, outputType: String): ScratchOutput? {
|
||||||
|
return when (outputType) {
|
||||||
|
"USER_OUTPUT" -> ScratchOutput(text, ScratchOutputType.OUTPUT)
|
||||||
|
"REPL_RESULT" -> ScratchOutput(text, ScratchOutputType.RESULT)
|
||||||
|
"REPL_INCOMPLETE",
|
||||||
|
"INTERNAL_ERROR",
|
||||||
|
"COMPILE_ERROR",
|
||||||
|
"RUNTIME_ERROR" -> ScratchOutput(text, ScratchOutputType.ERROR)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.scratch.ui
|
||||||
|
|
||||||
|
import com.intellij.execution.impl.ConsoleViewImpl
|
||||||
|
import com.intellij.execution.ui.ConsoleViewContentType
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.editor.ex.EditorEx
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManagerListener
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.Disposer
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.openapi.wm.ToolWindow
|
||||||
|
import com.intellij.openapi.wm.ToolWindowAnchor
|
||||||
|
import com.intellij.openapi.wm.ToolWindowFactory
|
||||||
|
import com.intellij.openapi.wm.ToolWindowManager
|
||||||
|
|
||||||
|
class ScratchToolWindowFactory : ToolWindowFactory {
|
||||||
|
companion object {
|
||||||
|
val ID = "Scratch error output"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
|
||||||
|
val consoleView = ConsoleViewImpl(project, true)
|
||||||
|
toolWindow.isToHideOnEmptyContent = true
|
||||||
|
|
||||||
|
val contentManager = toolWindow.contentManager
|
||||||
|
val content = contentManager.factory.createContent(consoleView.component, null, false)
|
||||||
|
contentManager.addContent(content)
|
||||||
|
val editor = consoleView.editor
|
||||||
|
if (editor is EditorEx) {
|
||||||
|
editor.isRendererMode = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Disposer.register(project, consoleView)
|
||||||
|
project.messageBus.connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, getFileEditorManagerListener(toolWindow))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFileEditorManagerListener(toolWindow: ToolWindow): FileEditorManagerListener {
|
||||||
|
return object : FileEditorManagerListener {
|
||||||
|
override fun fileClosed(source: FileEditorManager, file: VirtualFile) {
|
||||||
|
toolWindow.setAvailable(false, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun selectionChanged(event: FileEditorManagerEvent) {
|
||||||
|
toolWindow.setAvailable(false, {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showToolWindow(
|
||||||
|
project: Project,
|
||||||
|
message: String,
|
||||||
|
type: ConsoleViewContentType
|
||||||
|
) {
|
||||||
|
if (ApplicationManager.getApplication().isUnitTestMode) return
|
||||||
|
|
||||||
|
ApplicationManager.getApplication().invokeLater {
|
||||||
|
val toolWindowManager = ToolWindowManager.getInstance(project)
|
||||||
|
var window: ToolWindow? = toolWindowManager.getToolWindow(ScratchToolWindowFactory.ID)
|
||||||
|
if (window == null) {
|
||||||
|
toolWindowManager.registerToolWindow(ScratchToolWindowFactory.ID, true, ToolWindowAnchor.BOTTOM)
|
||||||
|
window = toolWindowManager.getToolWindow(ScratchToolWindowFactory.ID)
|
||||||
|
ScratchToolWindowFactory().createToolWindowContent(project, window!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
val contents = window.contentManager.contents
|
||||||
|
for (content in contents) {
|
||||||
|
val component = content.component
|
||||||
|
if (component is ConsoleViewImpl) {
|
||||||
|
component.clear()
|
||||||
|
component.print(message, type)
|
||||||
|
window.setAvailable(true, null)
|
||||||
|
window.show(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user