KT-32366: Get replace editor with virtual file in ScratchFile

- remove `getOutputHandler` method from `KtScratchFileLanguageProvider`
- now `InlayScratchOutputHandler` is a class, it has reference to the target editor and is (temporarily) attached to the editor
This commit is contained in:
Roman Golyshev
2019-08-17 19:48:25 +03:00
committed by Roman Golyshev
parent 1fc88fc47f
commit 54dde777ac
9 changed files with 60 additions and 50 deletions
@@ -16,8 +16,8 @@
package org.jetbrains.kotlin.idea.scratch package org.jetbrains.kotlin.idea.scratch
import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiComment import com.intellij.psi.PsiComment
import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
@@ -33,7 +33,7 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.AnalyzingUtils import org.jetbrains.kotlin.resolve.AnalyzingUtils
class KtScratchFile(project: Project, editor: TextEditor) : ScratchFile(project, editor) { class KtScratchFile(project: Project, file: VirtualFile) : ScratchFile(project, file) {
override fun getExpressions(psiFile: PsiFile): List<ScratchExpression> { override fun getExpressions(psiFile: PsiFile): List<ScratchExpression> {
// todo multiple expressions at one line // todo multiple expressions at one line
val doc = PsiDocumentManager.getInstance(psiFile.project).getLastCommittedDocument(psiFile) ?: return emptyList() val doc = PsiDocumentManager.getInstance(psiFile.project).getLastCommittedDocument(psiFile) ?: return emptyList()
@@ -16,21 +16,16 @@
package org.jetbrains.kotlin.idea.scratch package org.jetbrains.kotlin.idea.scratch
import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.scratch.compile.KtCompilingExecutor import org.jetbrains.kotlin.idea.scratch.compile.KtCompilingExecutor
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
class KtScratchFileLanguageProvider : ScratchFileLanguageProvider() { class KtScratchFileLanguageProvider : ScratchFileLanguageProvider() {
override fun createFile(project: Project, editor: TextEditor): ScratchFile? { override fun createFile(project: Project, file: VirtualFile): ScratchFile? {
return KtScratchFile(project, editor) return KtScratchFile(project, file)
} }
override fun createReplExecutor(file: ScratchFile) = KtScratchReplExecutor(file) override fun createReplExecutor(file: ScratchFile) = KtScratchReplExecutor(file)
override fun createCompilingExecutor(file: ScratchFile) = KtCompilingExecutor(file) override fun createCompilingExecutor(file: ScratchFile) = KtCompilingExecutor(file)
override fun getOutputHandler() = InlayScratchOutputHandler
} }
@@ -17,16 +17,16 @@
package org.jetbrains.kotlin.idea.scratch package org.jetbrains.kotlin.idea.scratch
import com.intellij.openapi.application.runReadAction import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.module.Module import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiDocumentManager import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.util.messages.Topic import com.intellij.util.messages.Topic
import org.jetbrains.kotlin.idea.core.util.toPsiFile
import org.jetbrains.kotlin.idea.scratch.ui.scratchFileOptions import org.jetbrains.kotlin.idea.scratch.ui.scratchFileOptions
abstract class ScratchFile(val project: Project, val editor: TextEditor) { abstract class ScratchFile(val project: Project, private val file: VirtualFile) {
var replScratchExecutor: SequentialScratchExecutor? = null var replScratchExecutor: SequentialScratchExecutor? = null
var compilingScratchExecutor: ScratchExecutor? = null var compilingScratchExecutor: ScratchExecutor? = null
@@ -39,7 +39,7 @@ abstract class ScratchFile(val project: Project, val editor: TextEditor) {
} }
fun getPsiFile(): PsiFile? = runReadAction { fun getPsiFile(): PsiFile? = runReadAction {
PsiDocumentManager.getInstance(project).getPsiFile(editor.editor.document) file.toPsiFile(project)
} }
fun setModule(value: Module?) { fun setModule(value: Module?) {
@@ -18,18 +18,17 @@ package org.jetbrains.kotlin.idea.scratch
import com.intellij.lang.Language import com.intellij.lang.Language
import com.intellij.lang.LanguageExtension import com.intellij.lang.LanguageExtension
import com.intellij.openapi.fileEditor.TextEditor
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.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.scratch.actions.ScratchCompilationSupport import org.jetbrains.kotlin.idea.scratch.actions.ScratchCompilationSupport
import org.jetbrains.kotlin.idea.scratch.output.ScratchOutputHandler
import org.jetbrains.kotlin.idea.scratch.output.ScratchOutputHandlerAdapter import org.jetbrains.kotlin.idea.scratch.output.ScratchOutputHandlerAdapter
import org.jetbrains.kotlin.idea.syncPublisherWithDisposeCheck import org.jetbrains.kotlin.idea.syncPublisherWithDisposeCheck
abstract class ScratchFileLanguageProvider { abstract class ScratchFileLanguageProvider {
fun newScratchFile(project: Project, editor: TextEditor): ScratchFile? { fun newScratchFile(project: Project, file: VirtualFile): ScratchFile? {
val scratchFile = createFile(project, editor) ?: return null val scratchFile = createFile(project, file) ?: return null
scratchFile.replScratchExecutor = createReplExecutor(scratchFile) scratchFile.replScratchExecutor = createReplExecutor(scratchFile)
scratchFile.compilingScratchExecutor = createCompilingExecutor(scratchFile) scratchFile.compilingScratchExecutor = createCompilingExecutor(scratchFile)
@@ -43,7 +42,6 @@ abstract class ScratchFileLanguageProvider {
} }
private fun ScratchExecutor.addOutputHandlers() { private fun ScratchExecutor.addOutputHandlers() {
addOutputHandler(getOutputHandler())
addOutputHandler(object : ScratchOutputHandlerAdapter() { addOutputHandler(object : ScratchOutputHandlerAdapter() {
override fun onStart(file: ScratchFile) { override fun onStart(file: ScratchFile) {
ScratchCompilationSupport.start(file, this@addOutputHandlers) ScratchCompilationSupport.start(file, this@addOutputHandlers)
@@ -55,12 +53,10 @@ abstract class ScratchFileLanguageProvider {
}) })
} }
protected abstract fun createFile(project: Project, editor: TextEditor): ScratchFile? protected abstract fun createFile(project: Project, file: VirtualFile): ScratchFile?
protected abstract fun createReplExecutor(file: ScratchFile): SequentialScratchExecutor? protected abstract fun createReplExecutor(file: ScratchFile): SequentialScratchExecutor?
protected abstract fun createCompilingExecutor(file: ScratchFile): ScratchExecutor? protected abstract fun createCompilingExecutor(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")
@@ -18,9 +18,11 @@ package org.jetbrains.kotlin.idea.scratch.actions
import com.intellij.icons.AllIcons import com.intellij.icons.AllIcons
import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.scratch.ScratchFileLanguageProvider import org.jetbrains.kotlin.idea.scratch.attachedOutputHandler
import org.jetbrains.kotlin.idea.scratch.getScratchFileFromSelectedEditor import org.jetbrains.kotlin.idea.scratch.getScratchFile
class ClearScratchAction : ScratchAction( class ClearScratchAction : ScratchAction(
KotlinBundle.message("scratch.clear.button"), KotlinBundle.message("scratch.clear.button"),
@@ -28,9 +30,11 @@ class ClearScratchAction : ScratchAction(
) { ) {
override fun actionPerformed(e: AnActionEvent) { override fun actionPerformed(e: AnActionEvent) {
val project = e.project ?: return val project = e.project ?: return
val scratchFile = getScratchFileFromSelectedEditor(project) ?: return
val psiFile = scratchFile.getPsiFile() ?: return
ScratchFileLanguageProvider.get(psiFile.language)?.getOutputHandler()?.clear(scratchFile) val selectedEditor = FileEditorManager.getInstance(project).selectedTextEditor ?: return
val textEditor = TextEditorProvider.getInstance().getTextEditor(selectedEditor)
val scratchFile = textEditor.getScratchFile() ?: return
textEditor.attachedOutputHandler?.clear(scratchFile)
} }
} }
@@ -23,13 +23,15 @@ import com.intellij.openapi.util.text.StringUtil
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
object InlayScratchOutputHandler : ScratchOutputHandler { class InlayScratchOutputHandler(private val textEditor: TextEditor) : ScratchOutputHandler {
private const val maxLineLength = 120 private val maxLineLength = 120
private const val maxInsertOffset = 60 private val maxInsertOffset = 60
private const val minSpaceCount = 4 private val minSpaceCount = 4
private val toolwindowHandler = getToolwindowHandler(textEditor)
override fun onStart(file: ScratchFile) { override fun onStart(file: ScratchFile) {
getToolwindowHandler().onStart(file) toolwindowHandler.onStart(file)
} }
override fun handle(file: ScratchFile, expression: ScratchExpression, output: ScratchOutput) { override fun handle(file: ScratchFile, expression: ScratchExpression, output: ScratchOutput) {
@@ -38,26 +40,26 @@ object InlayScratchOutputHandler : ScratchOutputHandler {
createInlay(file, expression, output) createInlay(file, expression, output)
if (output.type == ScratchOutputType.ERROR) { if (output.type == ScratchOutputType.ERROR) {
getToolwindowHandler().handle(file, expression, output) toolwindowHandler.handle(file, expression, output)
} }
} }
override fun error(file: ScratchFile, message: String) { override fun error(file: ScratchFile, message: String) {
getToolwindowHandler().error(file, message) toolwindowHandler.error(file, message)
} }
override fun onFinish(file: ScratchFile) { override fun onFinish(file: ScratchFile) {
getToolwindowHandler().onFinish(file) toolwindowHandler.onFinish(file)
} }
override fun clear(file: ScratchFile) { override fun clear(file: ScratchFile) {
clearInlays(file.editor) clearInlays(textEditor)
getToolwindowHandler().clear(file) toolwindowHandler.clear(file)
} }
private fun createInlay(file: ScratchFile, expression: ScratchExpression, output: ScratchOutput) { private fun createInlay(file: ScratchFile, expression: ScratchExpression, output: ScratchOutput) {
TransactionGuard.submitTransaction(file.project, Runnable { TransactionGuard.submitTransaction(file.project, Runnable {
val editor = file.editor.editor val editor = textEditor.editor
val line = expression.lineStart val line = expression.lineStart
val lineStartOffset = editor.document.getLineStartOffset(line) val lineStartOffset = editor.document.getLineStartOffset(line)
@@ -94,12 +96,12 @@ object InlayScratchOutputHandler : ScratchOutputHandler {
private fun printToToolWindow(file: ScratchFile, expression: ScratchExpression, output: ScratchOutput) { private fun printToToolWindow(file: ScratchFile, expression: ScratchExpression, output: ScratchOutput) {
if (output.type != ScratchOutputType.ERROR) { if (output.type != ScratchOutputType.ERROR) {
getToolwindowHandler().handle(file, expression, output) toolwindowHandler.handle(file, expression, output)
} }
} }
private fun maxLineLength(file: ScratchFile): Int { private fun maxLineLength(file: ScratchFile): Int {
val doc = file.editor.editor.document val doc = textEditor.editor.document
return file.getExpressions() return file.getExpressions()
.flatMap { it.lineStart..it.lineEnd } .flatMap { it.lineStart..it.lineEnd }
.map { doc.getLineEndOffset(it) - doc.getLineStartOffset(it) } .map { doc.getLineEndOffset(it) - doc.getLineStartOffset(it) }
@@ -38,15 +38,15 @@ import org.jetbrains.kotlin.idea.scratch.ScratchFile
import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.KtPsiFactory
fun getToolwindowHandler(): ScratchOutputHandler { fun getToolwindowHandler(parentDisposable: Disposable): ScratchOutputHandler {
return if (ApplicationManager.getApplication().isUnitTestMode) { return if (ApplicationManager.getApplication().isUnitTestMode) {
TestOutputHandler TestOutputHandler
} else { } else {
ToolWindowScratchOutputHandler ToolWindowScratchOutputHandler(parentDisposable)
} }
} }
private object ToolWindowScratchOutputHandler : ScratchOutputHandlerAdapter() { private class ToolWindowScratchOutputHandler(private val parentDisposable: Disposable) : ScratchOutputHandlerAdapter() {
override fun handle(file: ScratchFile, expression: ScratchExpression, output: ScratchOutput) { override fun handle(file: ScratchFile, expression: ScratchExpression, output: ScratchOutput) {
printToConsole(file) { printToConsole(file) {
@@ -134,7 +134,7 @@ private object ToolWindowScratchOutputHandler : ScratchOutputHandlerAdapter() {
val window = toolWindowManager.getToolWindow(ScratchToolWindowFactory.ID) val window = toolWindowManager.getToolWindow(ScratchToolWindowFactory.ID)
ScratchToolWindowFactory().createToolWindowContent(project, window) ScratchToolWindowFactory().createToolWindowContent(project, window)
Disposer.register(file.editor, Disposable { Disposer.register(parentDisposable, Disposable {
window.setAvailable(false, null) window.setAvailable(false, null)
}) })
@@ -17,6 +17,7 @@ import com.intellij.openapi.util.Key
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.annotations.TestOnly import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.idea.actions.KOTLIN_WORKSHEET_EXTENSION import org.jetbrains.kotlin.idea.actions.KOTLIN_WORKSHEET_EXTENSION
import org.jetbrains.kotlin.idea.scratch.output.ScratchOutputHandler
import org.jetbrains.kotlin.idea.scratch.ui.ScratchTopPanel import org.jetbrains.kotlin.idea.scratch.ui.ScratchTopPanel
import org.jetbrains.kotlin.psi.UserDataProperty import org.jetbrains.kotlin.psi.UserDataProperty
@@ -69,4 +70,12 @@ fun TextEditor.addScratchPanel(panel: ScratchTopPanel) {
Disposer.register(this, panel) Disposer.register(this, panel)
} }
fun TextEditor.attachOutputHandler(handler: ScratchOutputHandler) {
outputHandler = handler
}
val TextEditor.attachedOutputHandler: ScratchOutputHandler? get() = outputHandler
private var TextEditor.scratchTopPanel: ScratchTopPanel? by UserDataProperty<TextEditor, ScratchTopPanel>(Key.create("scratch.panel")) private var TextEditor.scratchTopPanel: ScratchTopPanel? by UserDataProperty<TextEditor, ScratchTopPanel>(Key.create("scratch.panel"))
private var TextEditor.outputHandler: ScratchOutputHandler? by UserDataProperty<TextEditor, ScratchOutputHandler>(Key.create("scratch.output.handler"))
@@ -21,7 +21,6 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.ProjectComponent import com.intellij.openapi.components.ProjectComponent
import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.fileEditor.FileEditorManagerListener import com.intellij.openapi.fileEditor.FileEditorManagerListener
import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiManager import com.intellij.psi.PsiManager
@@ -41,9 +40,14 @@ class ScratchFileHook(val project: Project) : ProjectComponent {
val editor = getEditorWithoutScratchFile(source, file) ?: return val editor = getEditorWithoutScratchFile(source, file) ?: return
val scratchFile = createScratchFile(project, file, editor) ?: return val scratchFile = createScratchFile(project, file) ?: return
val panel = ScratchTopPanel(scratchFile)
editor.addScratchPanel(panel) val inlayOutputHandler = InlayScratchOutputHandler(editor)
scratchFile.compilingScratchExecutor?.addOutputHandler(inlayOutputHandler)
scratchFile.replScratchExecutor?.addOutputHandler(inlayOutputHandler)
editor.attachOutputHandler(inlayOutputHandler)
editor.addScratchPanel(ScratchTopPanel(scratchFile))
ScratchFileAutoRunner.addListener(project, editor) ScratchFileAutoRunner.addListener(project, editor)
} }
@@ -59,9 +63,9 @@ class ScratchFileHook(val project: Project) : ProjectComponent {
} }
} }
private fun createScratchFile(project: Project, file: VirtualFile, editor: TextEditor): ScratchFile? { private fun createScratchFile(project: Project, file: VirtualFile): ScratchFile? {
val psiFile = PsiManager.getInstance(project).findFile(file) ?: return null val psiFile = PsiManager.getInstance(project).findFile(file) ?: return null
val scratchFile = ScratchFileLanguageProvider.get(psiFile.language)?.newScratchFile(project, editor) ?: return null val scratchFile = ScratchFileLanguageProvider.get(psiFile.language)?.newScratchFile(project, file) ?: return null
setupReplRestartingOutputHandler(project, scratchFile) setupReplRestartingOutputHandler(project, scratchFile)
return scratchFile return scratchFile