From 1d6de45db68e6a4c375f1e14090014b83e3241e8 Mon Sep 17 00:00:00 2001 From: Roman Golyshev Date: Mon, 19 Aug 2019 12:05:17 +0300 Subject: [PATCH] KT-32366: Use `TextEditorWithPreview` as editor for scratch files - get rid of `ScratchFileHook` completely - use `KtsScratchFileEditorProvider` to encapsulate details about scratch file presentation - remove obsolete functions from `scratchUtils` --- .../scratch/actions/ClearScratchAction.kt | 6 +- .../kotlin/idea/scratch/scratchUtils.kt | 32 +--- .../scratch/ui/KtScratchFileEditorProvider.kt | 140 ++++++++++++++++++ .../kotlin/idea/scratch/ui/ScratchFileHook.kt | 87 ----------- .../kotlin/idea/scratch/ui/ScratchTopPanel.kt | 14 +- idea/resources/META-INF/extensions/ide.xml | 1 + .../resources/META-INF/extensions/ide.xml.183 | 1 + .../META-INF/extensions/ide.xml.as33 | 1 + .../META-INF/extensions/ide.xml.as34 | 1 + idea/resources/META-INF/jvm.xml | 3 - idea/resources/META-INF/jvm.xml.182 | 3 - idea/resources/META-INF/jvm.xml.183 | 3 - 12 files changed, 152 insertions(+), 140 deletions(-) create mode 100644 idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/ui/KtScratchFileEditorProvider.kt delete mode 100644 idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/ui/ScratchFileHook.kt diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/actions/ClearScratchAction.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/actions/ClearScratchAction.kt index aecdc3fda78..ecf0b69172c 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/actions/ClearScratchAction.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/actions/ClearScratchAction.kt @@ -21,8 +21,7 @@ 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.scratch.attachedOutputHandler -import org.jetbrains.kotlin.idea.scratch.getScratchFile +import org.jetbrains.kotlin.idea.scratch.ui.findScratchFileEditorWithPreview class ClearScratchAction : ScratchAction( KotlinBundle.message("scratch.clear.button"), @@ -33,8 +32,7 @@ class ClearScratchAction : ScratchAction( val selectedEditor = FileEditorManager.getInstance(project).selectedTextEditor ?: return val textEditor = TextEditorProvider.getInstance().getTextEditor(selectedEditor) - val scratchFile = textEditor.getScratchFile() ?: return - textEditor.attachedOutputHandler?.clear(scratchFile) + textEditor.findScratchFileEditorWithPreview()?.clearOutputHandlers() } } \ No newline at end of file diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/scratchUtils.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/scratchUtils.kt index 201740fa48c..78b51e8b1af 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/scratchUtils.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/scratchUtils.kt @@ -12,14 +12,9 @@ import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.fileEditor.TextEditor import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider import com.intellij.openapi.project.Project -import com.intellij.openapi.util.Disposer -import com.intellij.openapi.util.Key import com.intellij.openapi.vfs.VirtualFile -import org.jetbrains.annotations.TestOnly 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.psi.UserDataProperty +import org.jetbrains.kotlin.idea.scratch.ui.findScratchFileEditorWithPreview internal val LOG = Logger.getInstance("#org.jetbrains.kotlin.idea.scratch") internal fun Logger.printDebugMessage(str: String) { @@ -32,12 +27,6 @@ val VirtualFile.isKotlinWorksheet: Boolean val VirtualFile.isKotlinScratch: Boolean get() = ScratchFileService.getInstance().getRootType(this) is ScratchRootType -fun getEditorWithoutScratchFile(fileManager: FileEditorManager, virtualFile: VirtualFile): TextEditor? { - val editor = fileManager.getSelectedEditor(virtualFile) as? TextEditor - if (editor?.getScratchFile() != null) return null - return editor -} - fun getScratchFileFromEditorSelectedForFile(fileManager: FileEditorManager, virtualFile: VirtualFile): ScratchFile? { val editor = fileManager.getSelectedEditor(virtualFile) as? TextEditor ?: return null return editor.getScratchFile() @@ -49,22 +38,5 @@ fun getScratchFileFromSelectedEditor(project: Project): ScratchFile? { } fun TextEditor.getScratchFile(): ScratchFile? { - return scratchTopPanel?.scratchFile + return findScratchFileEditorWithPreview()?.scratchFile } - -fun TextEditor.addScratchPanel(panel: ScratchTopPanel) { - scratchTopPanel = panel - FileEditorManager.getInstance(panel.scratchFile.project).addTopComponent(this, panel.component) - - Disposer.register(this, panel) -} - -fun TextEditor.attachOutputHandler(handler: ScratchOutputHandler) { - outputHandler = handler -} - -val TextEditor.attachedOutputHandler: ScratchOutputHandler? get() = outputHandler - -private var TextEditor.scratchTopPanel: ScratchTopPanel? by UserDataProperty(Key.create("scratch.panel")) - -private var TextEditor.outputHandler: ScratchOutputHandler? by UserDataProperty(Key.create("scratch.output.handler")) \ No newline at end of file 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 new file mode 100644 index 00000000000..e4ec3f997f5 --- /dev/null +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/ui/KtScratchFileEditorProvider.kt @@ -0,0 +1,140 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.ui + +import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer +import com.intellij.openapi.Disposable +import com.intellij.openapi.actionSystem.ActionToolbar +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.editor.EditorFactory +import com.intellij.openapi.fileEditor.FileEditor +import com.intellij.openapi.fileEditor.FileEditorPolicy +import com.intellij.openapi.fileEditor.FileEditorProvider +import com.intellij.openapi.fileEditor.TextEditor +import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider +import com.intellij.openapi.project.DumbAware +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.Key +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.pom.Navigatable +import com.intellij.psi.PsiManager +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.psi.UserDataProperty + +private const val KTS_SCRATCH_EDITOR_PROVIDER: String = "KtsScratchFileEditorProvider" + +class KtScratchFileEditorProvider : FileEditorProvider, DumbAware { + override fun getEditorTypeId(): String = KTS_SCRATCH_EDITOR_PROVIDER + + override fun accept(project: Project, file: VirtualFile): Boolean { + if (!file.isValid) return false + if (!(file.isKotlinScratch || file.isKotlinWorksheet)) return false + val psiFile = PsiManager.getInstance(project).findFile(file) ?: return false + return ScratchFileLanguageProvider.get(psiFile.fileType) != null + } + + override fun createEditor(project: Project, file: VirtualFile): FileEditor { + val scratchFile = createScratchFile(project, file) ?: return TextEditorProvider.getInstance().createEditor(project, file) + + return KtScratchFileEditorWithPreview.create(scratchFile) + } + + override fun getPolicy(): FileEditorPolicy = FileEditorPolicy.HIDE_DEFAULT_EDITOR +} + +class KtScratchFileEditorWithPreview private constructor( + val scratchFile: ScratchFile, + editor: TextEditor, + preview: TextEditor +) : TextEditorWithPreview(editor, preview), TextEditor { + + private val inlayOutputHandler = InlayScratchOutputHandler(editor) + private val scratchTopPanel = ScratchTopPanel(scratchFile) + + init { + editor.parentScratchEditorWithPreview = this + + scratchFile.compilingScratchExecutor?.addOutputHandler(inlayOutputHandler) + scratchFile.replScratchExecutor?.addOutputHandler(inlayOutputHandler) + + ScratchFileAutoRunner.addListener(scratchFile.project, editor) + } + + override fun dispose() { + scratchFile.replScratchExecutor?.stop() + scratchFile.compilingScratchExecutor?.stop() + super.dispose() + } + + override fun navigateTo(navigatable: Navigatable) { + myEditor.navigateTo(navigatable) + } + + override fun canNavigateTo(navigatable: Navigatable): Boolean { + return myEditor.canNavigateTo(navigatable) + } + + override fun getEditor(): Editor { + return myEditor.editor + } + + override fun createToolbar(): ActionToolbar? { + return scratchTopPanel.actionsToolbar + } + + fun clearOutputHandlers() { + inlayOutputHandler.clear(scratchFile) + } + + companion object { + fun create(scratchFile: ScratchFile): KtScratchFileEditorWithPreview { + val textEditorProvider = TextEditorProvider.getInstance() + + val mainEditor = textEditorProvider.createEditor(scratchFile.project, scratchFile.file) as TextEditor + val editorFactory = EditorFactory.getInstance() + + val viewer = editorFactory.createViewer(editorFactory.createDocument("")) + Disposer.register(mainEditor, Disposable { editorFactory.releaseEditor(viewer) }) + + val previewEditor = textEditorProvider.getTextEditor(viewer) + + return KtScratchFileEditorWithPreview(scratchFile, mainEditor, previewEditor) + } + } +} + +fun TextEditor.findScratchFileEditorWithPreview(): KtScratchFileEditorWithPreview? = + if (this is KtScratchFileEditorWithPreview) this else parentScratchEditorWithPreview + +private var TextEditor.parentScratchEditorWithPreview: KtScratchFileEditorWithPreview? + by UserDataProperty(Key.create("parent.preview.editor")) + +fun createScratchFile(project: Project, file: VirtualFile): ScratchFile? { + val psiFile = PsiManager.getInstance(project).findFile(file) ?: return null + val scratchFile = ScratchFileLanguageProvider.get(psiFile.language)?.newScratchFile(project, file) ?: return null + setupCodeAnalyzerRestarterOutputHandler(project, scratchFile) + + return scratchFile +} + +private fun setupCodeAnalyzerRestarterOutputHandler(project: Project, scratchFile: ScratchFile) { + scratchFile.replScratchExecutor?.addOutputHandler(object : ScratchOutputHandlerAdapter() { + override fun onFinish(file: ScratchFile) { + ApplicationManager.getApplication().invokeLater { + if (!file.project.isDisposed) { + val scratch = file.getPsiFile() + if (scratch?.isValid == true) { + DaemonCodeAnalyzer.getInstance(project).restart(scratch) + } + } + } + } + }) +} \ No newline at end of file diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/ui/ScratchFileHook.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/ui/ScratchFileHook.kt deleted file mode 100644 index 4e24eb33938..00000000000 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/ui/ScratchFileHook.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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.codeInsight.daemon.DaemonCodeAnalyzer -import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.components.ProjectComponent -import com.intellij.openapi.fileEditor.FileEditorManager -import com.intellij.openapi.fileEditor.FileEditorManagerListener -import com.intellij.openapi.project.Project -import com.intellij.openapi.vfs.VirtualFile -import com.intellij.psi.PsiManager -import org.jetbrains.kotlin.idea.scratch.* -import org.jetbrains.kotlin.idea.scratch.output.InlayScratchOutputHandler -import org.jetbrains.kotlin.idea.scratch.output.ScratchOutputHandlerAdapter - -class ScratchFileHook(val project: Project) : ProjectComponent { - - override fun projectOpened() { - project.messageBus.connect(project).subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, ScratchEditorListener()) - } - - private inner class ScratchEditorListener : FileEditorManagerListener { - override fun fileOpened(source: FileEditorManager, file: VirtualFile) { - if (!isPluggable(file)) return - - val editor = getEditorWithoutScratchFile(source, file) ?: return - - val scratchFile = createScratchFile(project, file) ?: return - - val inlayOutputHandler = InlayScratchOutputHandler(editor) - scratchFile.compilingScratchExecutor?.addOutputHandler(inlayOutputHandler) - scratchFile.replScratchExecutor?.addOutputHandler(inlayOutputHandler) - editor.attachOutputHandler(inlayOutputHandler) - - editor.addScratchPanel(ScratchTopPanel(scratchFile)) - - ScratchFileAutoRunner.addListener(project, editor) - } - - override fun fileClosed(source: FileEditorManager, file: VirtualFile) {} - } - - private fun isPluggable(file: VirtualFile): Boolean { - if (!file.isValid) return false - if (!(file.isKotlinScratch || file.isKotlinWorksheet)) return false - val psiFile = PsiManager.getInstance(project).findFile(file) ?: return false - return ScratchFileLanguageProvider.get(psiFile.fileType) != null - } -} - -private fun createScratchFile(project: Project, file: VirtualFile): ScratchFile? { - val psiFile = PsiManager.getInstance(project).findFile(file) ?: return null - val scratchFile = ScratchFileLanguageProvider.get(psiFile.language)?.newScratchFile(project, file) ?: return null - setupReplRestartingOutputHandler(project, scratchFile) - - return scratchFile -} - -private fun setupReplRestartingOutputHandler(project: Project, scratchFile: ScratchFile) { - scratchFile.replScratchExecutor?.addOutputHandler(object : ScratchOutputHandlerAdapter() { - override fun onFinish(file: ScratchFile) { - ApplicationManager.getApplication().invokeLater { - if (!file.project.isDisposed) { - val scratch = file.getPsiFile() - if (scratch?.isValid == true) { - DaemonCodeAnalyzer.getInstance(project).restart(scratch) - } - } - } - } - }) -} diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/ui/ScratchTopPanel.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/ui/ScratchTopPanel.kt index ae2dd14d2d6..5c2b6b7182e 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/ui/ScratchTopPanel.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/ui/ScratchTopPanel.kt @@ -29,17 +29,11 @@ import org.jetbrains.kotlin.idea.scratch.actions.StopScratchAction import org.jetbrains.kotlin.idea.scratch.output.ScratchOutputHandlerAdapter import javax.swing.JComponent -class ScratchTopPanel(val scratchFile: ScratchFile) : Disposable { - override fun dispose() { - scratchFile.replScratchExecutor?.stop() - scratchFile.compilingScratchExecutor?.stop() - } - +class ScratchTopPanel(val scratchFile: ScratchFile) { private val moduleChooserAction: ModulesComboBoxAction = ModulesComboBoxAction(scratchFile) - private val actionsToolbar: ActionToolbar + val actionsToolbar: ActionToolbar init { - scratchFile.addModuleListener { _, _ -> updateToolbar() } setupTopPanelUpdateHandlers() val toolbarGroup = DefaultActionGroup().apply { @@ -59,9 +53,9 @@ class ScratchTopPanel(val scratchFile: ScratchFile) : Disposable { actionsToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.EDITOR_TOOLBAR, toolbarGroup, true) } - val component: JComponent = actionsToolbar.component - private fun setupTopPanelUpdateHandlers() { + scratchFile.addModuleListener { _, _ -> updateToolbar() } + val toolbarHandler = createUpdateToolbarHandler() scratchFile.replScratchExecutor?.addOutputHandler(toolbarHandler) scratchFile.compilingScratchExecutor?.addOutputHandler(toolbarHandler) diff --git a/idea/resources/META-INF/extensions/ide.xml b/idea/resources/META-INF/extensions/ide.xml index d2eb2279f02..09a01395241 100644 --- a/idea/resources/META-INF/extensions/ide.xml +++ b/idea/resources/META-INF/extensions/ide.xml @@ -100,6 +100,7 @@ description="Add all transitive dependencies on MPP modules when Android project is imported" defaultValue="true" restartRequired="false"/> + diff --git a/idea/resources/META-INF/extensions/ide.xml.183 b/idea/resources/META-INF/extensions/ide.xml.183 index dfefcd664a8..fb27af5c1ad 100644 --- a/idea/resources/META-INF/extensions/ide.xml.183 +++ b/idea/resources/META-INF/extensions/ide.xml.183 @@ -104,6 +104,7 @@ description="Add all transitive dependencies on MPP modules when Android project is imported" defaultValue="true" restartRequired="false"/> + diff --git a/idea/resources/META-INF/extensions/ide.xml.as33 b/idea/resources/META-INF/extensions/ide.xml.as33 index 42bd3e4acaf..f4be5162a3e 100644 --- a/idea/resources/META-INF/extensions/ide.xml.as33 +++ b/idea/resources/META-INF/extensions/ide.xml.as33 @@ -96,6 +96,7 @@ description="Add all transitive dependencies on MPP modules when Android project is imported" defaultValue="true" restartRequired="false"/> + diff --git a/idea/resources/META-INF/extensions/ide.xml.as34 b/idea/resources/META-INF/extensions/ide.xml.as34 index 01bfd42483e..ff8c8a08424 100644 --- a/idea/resources/META-INF/extensions/ide.xml.as34 +++ b/idea/resources/META-INF/extensions/ide.xml.as34 @@ -95,6 +95,7 @@ description="Add all transitive dependencies on MPP modules when Android project is imported" defaultValue="true" restartRequired="false"/> + diff --git a/idea/resources/META-INF/jvm.xml b/idea/resources/META-INF/jvm.xml index 68a385df6e8..4cec9cb2ade 100644 --- a/idea/resources/META-INF/jvm.xml +++ b/idea/resources/META-INF/jvm.xml @@ -17,9 +17,6 @@ org.jetbrains.kotlin.idea.configuration.ui.KotlinConfigurationCheckerComponent - - org.jetbrains.kotlin.idea.scratch.ui.ScratchFileHook - org.jetbrains.kotlin.idea.scratch.ScratchFileModuleInfoProvider diff --git a/idea/resources/META-INF/jvm.xml.182 b/idea/resources/META-INF/jvm.xml.182 index be9fa9a801d..0e10bf7bd36 100644 --- a/idea/resources/META-INF/jvm.xml.182 +++ b/idea/resources/META-INF/jvm.xml.182 @@ -17,9 +17,6 @@ org.jetbrains.kotlin.idea.configuration.ui.KotlinConfigurationCheckerComponent - - org.jetbrains.kotlin.idea.scratch.ui.ScratchFileHook - org.jetbrains.kotlin.idea.scratch.ScratchFileModuleInfoProvider diff --git a/idea/resources/META-INF/jvm.xml.183 b/idea/resources/META-INF/jvm.xml.183 index 797877707df..9dbb3323d2f 100644 --- a/idea/resources/META-INF/jvm.xml.183 +++ b/idea/resources/META-INF/jvm.xml.183 @@ -17,9 +17,6 @@ org.jetbrains.kotlin.idea.configuration.ui.KotlinConfigurationCheckerComponent - - org.jetbrains.kotlin.idea.scratch.ui.ScratchFileHook - org.jetbrains.kotlin.idea.scratch.ScratchFileModuleInfoProvider