diff --git a/notebooks/notebook-ui/src/com/intellij/notebooks/ui/editor/actions/command/mode/NotebookEditorMode.kt b/notebooks/notebook-ui/src/com/intellij/notebooks/ui/editor/actions/command/mode/NotebookEditorMode.kt index 08c2c233613d..951340c979b7 100644 --- a/notebooks/notebook-ui/src/com/intellij/notebooks/ui/editor/actions/command/mode/NotebookEditorMode.kt +++ b/notebooks/notebook-ui/src/com/intellij/notebooks/ui/editor/actions/command/mode/NotebookEditorMode.kt @@ -13,10 +13,10 @@ import com.intellij.openapi.editor.ex.EditorEx import com.intellij.openapi.editor.ex.MarkupModelEx import com.intellij.openapi.editor.ex.RangeHighlighterEx import com.intellij.openapi.util.Key +import com.intellij.ui.Gray import com.intellij.util.concurrency.ThreadingAssertions import com.intellij.util.concurrency.annotations.RequiresEdt import com.intellij.util.messages.Topic -import java.awt.Color /** * The Jupyter Notebook has a modal user interface. @@ -37,7 +37,6 @@ val NOTEBOOK_EDITOR_MODE: Topic = Topic.create("Note @FunctionalInterface interface NotebookEditorModeListener { - fun onModeChange(editor: Editor, mode: NotebookEditorMode) } @@ -150,7 +149,6 @@ fun Editor.setMode(mode: NotebookEditorMode) { } } -private val INVISIBLE_CARET = CaretVisualAttributes( - Color(0, 0, 0, 0), - CaretVisualAttributes.Weight.NORMAL) +private val INVISIBLE_CARET = CaretVisualAttributes(Gray.TRANSPARENT, + CaretVisualAttributes.Weight.NORMAL) diff --git a/notebooks/visualization/src/com/intellij/notebooks/visualization/NotebookCellInlayManager.kt b/notebooks/visualization/src/com/intellij/notebooks/visualization/NotebookCellInlayManager.kt index fd0cc7193cf0..b0abd2915bb9 100644 --- a/notebooks/visualization/src/com/intellij/notebooks/visualization/NotebookCellInlayManager.kt +++ b/notebooks/visualization/src/com/intellij/notebooks/visualization/NotebookCellInlayManager.kt @@ -1,9 +1,6 @@ package com.intellij.notebooks.visualization import com.intellij.ide.ui.LafManagerListener -import com.intellij.notebooks.ui.editor.actions.command.mode.NOTEBOOK_EDITOR_MODE -import com.intellij.notebooks.ui.editor.actions.command.mode.NotebookEditorMode -import com.intellij.notebooks.ui.editor.actions.command.mode.NotebookEditorModeListener import com.intellij.notebooks.ui.isFoldingEnabledKey import com.intellij.notebooks.visualization.inlay.JupyterBoundsChangeHandler import com.intellij.notebooks.visualization.ui.* @@ -39,7 +36,7 @@ class NotebookCellInlayManager private constructor( private val shouldCheckInlayOffsets: Boolean, private val inputFactories: List, private val cellExtensionFactories: List, -) : Disposable, NotebookIntervalPointerFactory.ChangeListener, NotebookEditorModeListener { +) : Disposable, NotebookIntervalPointerFactory.ChangeListener { private val notebookCellLines = NotebookCellLines.get(editor) @@ -83,19 +80,22 @@ class NotebookCellInlayManager private constructor( val newCtx = UpdateContext(force) updateCtx = newCtx try { - JupyterBoundsChangeHandler.get(editor).postponeUpdates() + val jupyterBoundsChangeHandler = JupyterBoundsChangeHandler.get(editor) + jupyterBoundsChangeHandler.postponeUpdates() val r = keepScrollingPositionWhile(editor) { val r = block(newCtx) updateCtx = null if (editorIsProcessingDocument) { postponedUpdates.add(newCtx) - } else { + } + else { newCtx.applyUpdates(editor) } r } inlaysChanged() - JupyterBoundsChangeHandler.get(editor).performPostponed() + jupyterBoundsChangeHandler.boundsChanged() + jupyterBoundsChangeHandler.performPostponed() r } finally { @@ -184,8 +184,6 @@ class NotebookCellInlayManager private constructor( setupFoldingListener() setupSelectionUI() - ApplicationManager.getApplication().messageBus.connect(this).subscribe(NOTEBOOK_EDITOR_MODE, this) - cellEventListeners.addListener(object : EditorCellEventListener { override fun onEditorCellEvents(events: List) { updateUI(events) @@ -233,7 +231,6 @@ class NotebookCellInlayManager private constructor( } } - private fun createCellViewIfNecessary(cell: EditorCell, ctx: UpdateContext) { if (views[cell] == null) { createCellView(cell, ctx) @@ -502,28 +499,6 @@ class NotebookCellInlayManager private constructor( internal fun getInputFactories(): Sequence { return inputFactories.asSequence() } - - override fun onModeChange(editor: Editor, mode: NotebookEditorMode) { - if (editor == this.editor) { - when (mode) { - NotebookEditorMode.EDIT -> { - editor.caretModel.allCarets.forEach { caret -> - getCellByLine(editor.document.getLineNumber(caret.offset))?.switchToEditMode() - } - } - NotebookEditorMode.COMMAND -> { - _cells.forEach { - it.switchToCommandMode() - } - } - } - } - } - - fun getCellByLine(line: Int): EditorCell? { - return _cells.firstOrNull { it.interval.lines.contains(line) } - } - } class UpdateContext(val force: Boolean = false) { diff --git a/notebooks/visualization/src/com/intellij/notebooks/visualization/inlay/JupyterBoundsChangeHandler.kt b/notebooks/visualization/src/com/intellij/notebooks/visualization/inlay/JupyterBoundsChangeHandler.kt index 29b2161dfe4d..b1b944c7fba5 100644 --- a/notebooks/visualization/src/com/intellij/notebooks/visualization/inlay/JupyterBoundsChangeHandler.kt +++ b/notebooks/visualization/src/com/intellij/notebooks/visualization/inlay/JupyterBoundsChangeHandler.kt @@ -15,6 +15,18 @@ import com.intellij.openapi.util.Key import com.intellij.util.EventDispatcher import java.beans.PropertyChangeListener +/** + * Per-editor service on which can one subscribe. + * It sends the boundsChanged event to the subscribed JupyterBoundsChangeListener. + * + * boundsChanged event will be dispatched if + * - someone directly calls JupyterBoundsChangeHandler.get(editor).boundsChanged() + * - EditorImpl property was changed + * - On soft wrap recalculation ends + * - Folding model change + * - Inlay model change + */ +// Class name does not reflect the functionality of this class. class JupyterBoundsChangeHandler(val editor: EditorImpl) : Disposable { private var isDelayed = false private var isShouldBeRecalculated = false @@ -26,10 +38,8 @@ class JupyterBoundsChangeHandler(val editor: EditorImpl) : Disposable { boundsChanged() }, this) - editor.softWrapModel.addSoftWrapChangeListener(object : SoftWrapChangeListener { - override fun softWrapsChanged() { - } + override fun softWrapsChanged() = Unit override fun recalculationEnds() { if (editor.document.isInEventsHandling) @@ -81,7 +91,7 @@ class JupyterBoundsChangeHandler(val editor: EditorImpl) : Disposable { }, this) } - override fun dispose() {} + override fun dispose() = Unit fun subscribe(listener: JupyterBoundsChangeListener) { dispatcher.addListener(listener) @@ -91,13 +101,14 @@ class JupyterBoundsChangeHandler(val editor: EditorImpl) : Disposable { dispatcher.removeListener(listener) } - fun boundsChanged() { if (isDelayed) { isShouldBeRecalculated = true return } - dispatcher.multicaster.boundsChanged() + if (!editor.isDisposed) { + dispatcher.multicaster.boundsChanged() + } } fun postponeUpdates() { @@ -116,7 +127,8 @@ class JupyterBoundsChangeHandler(val editor: EditorImpl) : Disposable { private val INSTANCE_KEY = Key("INLAYS_CHANGE_HANDLER") fun install(editor: EditorImpl) { - val updater = JupyterBoundsChangeHandler(editor).also { Disposer.register(editor.disposable, it) } + val updater = JupyterBoundsChangeHandler(editor) + Disposer.register(editor.disposable, updater) editor.putUserData(INSTANCE_KEY, updater) } diff --git a/notebooks/visualization/src/com/intellij/notebooks/visualization/inlay/JupyterBoundsChangeListener.kt b/notebooks/visualization/src/com/intellij/notebooks/visualization/inlay/JupyterBoundsChangeListener.kt index 49c7cb53c469..85349100463e 100644 --- a/notebooks/visualization/src/com/intellij/notebooks/visualization/inlay/JupyterBoundsChangeListener.kt +++ b/notebooks/visualization/src/com/intellij/notebooks/visualization/inlay/JupyterBoundsChangeListener.kt @@ -2,6 +2,6 @@ package com.intellij.notebooks.visualization.inlay import java.util.* -interface JupyterBoundsChangeListener: EventListener { - fun boundsChanged() {} +interface JupyterBoundsChangeListener : EventListener { + fun boundsChanged() = Unit } \ No newline at end of file diff --git a/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/EditorCell.kt b/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/EditorCell.kt index 63b91456c7b3..03edff179188 100644 --- a/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/EditorCell.kt +++ b/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/EditorCell.kt @@ -1,6 +1,5 @@ package com.intellij.notebooks.visualization.ui -import com.intellij.notebooks.ui.editor.actions.command.mode.NotebookEditorMode import com.intellij.notebooks.visualization.NotebookCellInlayController import com.intellij.notebooks.visualization.NotebookCellInlayManager import com.intellij.notebooks.visualization.NotebookIntervalPointer @@ -44,15 +43,8 @@ class EditorCell( val executionStatus = AtomicProperty(ExecutionStatus()) - // ToDo we should remove or rework this. Mode does not really reflects the state of markdown cells. - val mode = AtomicProperty(NotebookEditorMode.COMMAND) - val outputs = AtomicProperty>(getOutputs()) - init { - CELL_EXTENSION_CONTAINER_KEY.set(this, mutableMapOf()) - } - private fun getSource(): String { val document = editor.document if (interval.lines.first + 1 >= document.lineCount) return "" @@ -151,29 +143,27 @@ class EditorCell( } } - fun switchToEditMode() = runInEdt { - mode.set(NotebookEditorMode.EDIT) - } - - fun switchToCommandMode() = runInEdt { - mode.set(NotebookEditorMode.COMMAND) - } - fun requestCaret() { view?.requestCaret() } - inline fun getExtension(): T { + inline fun getExtension(): T? { return getExtension(T::class) } @Suppress("UNCHECKED_CAST") - fun getExtension(cls: KClass): T { - return CELL_EXTENSION_CONTAINER_KEY.get(this)!![cls] as T + fun getExtension(cls: KClass): T? { + val extensions = CELL_EXTENSION_CONTAINER_KEY.get(this) ?: return null + return extensions[cls] as? T } fun addExtension(cls: KClass, extension: T) { - CELL_EXTENSION_CONTAINER_KEY.get(this)!![cls] = extension + var map = CELL_EXTENSION_CONTAINER_KEY.get(this) + if (map == null) { + map = mutableMapOf, EditorCellExtension>() + CELL_EXTENSION_CONTAINER_KEY.set(this, map) + } + map[cls] = extension } fun onBeforeRemove() { diff --git a/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/EditorCellExtension.kt b/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/EditorCellExtension.kt index 1001d8e67183..1e297e248751 100644 --- a/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/EditorCellExtension.kt +++ b/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/EditorCellExtension.kt @@ -1,7 +1,5 @@ package com.intellij.notebooks.visualization.ui interface EditorCellExtension { - - fun onBeforeRemove() {} - + fun onBeforeRemove() = Unit } \ No newline at end of file diff --git a/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/EditorCellFoldingBar.kt b/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/EditorCellFoldingBar.kt index 8d381d37b793..2af02f42dbea 100644 --- a/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/EditorCellFoldingBar.kt +++ b/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/EditorCellFoldingBar.kt @@ -1,15 +1,15 @@ package com.intellij.notebooks.visualization.ui -import com.intellij.openapi.editor.impl.EditorImpl -import com.intellij.ui.ExperimentalUI -import com.intellij.ui.paint.LinePainter2D -import com.intellij.ui.paint.RectanglePainter2D -import org.jetbrains.annotations.ApiStatus import com.intellij.notebooks.ui.visualization.NotebookEditorAppearanceUtils.isOrdinaryNotebookEditor import com.intellij.notebooks.ui.visualization.notebookAppearance import com.intellij.notebooks.visualization.inlay.JupyterBoundsChangeHandler import com.intellij.notebooks.visualization.inlay.JupyterBoundsChangeListener import com.intellij.notebooks.visualization.use +import com.intellij.openapi.editor.impl.EditorImpl +import com.intellij.ui.ExperimentalUI +import com.intellij.ui.paint.LinePainter2D +import com.intellij.ui.paint.RectanglePainter2D +import org.jetbrains.annotations.ApiStatus import java.awt.* import java.awt.event.MouseAdapter import java.awt.event.MouseEvent @@ -75,7 +75,9 @@ class EditorCellFoldingBar( panel.setBounds(editor.gutterComponentEx.extraLineMarkerFreePaintersAreaOffset + 1, yAndHeight.first, 6, yAndHeight.second) } - private fun createFoldingBar() = object : JComponent() { + private fun createFoldingBar() = EditorCellFoldingBarComponent() + + inner class EditorCellFoldingBarComponent : JComponent() { private var mouseOver = false init { diff --git a/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/EditorCellViewComponent.kt b/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/EditorCellViewComponent.kt index 979546e4fb3d..effb46243f81 100644 --- a/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/EditorCellViewComponent.kt +++ b/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/EditorCellViewComponent.kt @@ -1,12 +1,12 @@ package com.intellij.notebooks.visualization.ui import com.intellij.codeInsight.hints.presentation.InlayPresentation +import com.intellij.notebooks.visualization.UpdateContext import com.intellij.openapi.Disposable import com.intellij.openapi.editor.Inlay import com.intellij.openapi.util.Disposer -import com.intellij.notebooks.visualization.UpdateContext import java.awt.Rectangle -import java.util.Collections +import java.util.* abstract class EditorCellViewComponent : Disposable { protected var parent: EditorCellViewComponent? = null @@ -23,7 +23,7 @@ abstract class EditorCellViewComponent : Disposable { Disposer.register(this, child) } - /* Chile disposable will be automatically disposed. */ + /* Child will be automatically disposed. */ fun remove(child: EditorCellViewComponent) { Disposer.dispose(child) _children.remove(child) diff --git a/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/TextEditorCellViewComponent.kt b/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/TextEditorCellViewComponent.kt index b9c5df3fe820..1253ae41a227 100644 --- a/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/TextEditorCellViewComponent.kt +++ b/notebooks/visualization/src/com/intellij/notebooks/visualization/ui/TextEditorCellViewComponent.kt @@ -43,7 +43,6 @@ class TextEditorCellViewComponent( override fun mousePressed(e: MouseEvent) { if (editor.xyToLogicalPosition(e.point).line in cell.interval.lines) { editor.setMode(NotebookEditorMode.EDIT) - cell.switchToEditMode() } } } @@ -60,21 +59,21 @@ class TextEditorCellViewComponent( private fun updateGutterIcons(gutterAction: AnAction?) = runInEdt { disposeExistingHighlighter() - if (gutterAction != null) { - val markupModel = editor.markupModel - val interval = safeInterval ?: return@runInEdt - val startOffset = editor.document.getLineStartOffset(interval.lines.first) - val endOffset = editor.document.getLineEndOffset(interval.lines.last) - val highlighter = markupModel.addRangeHighlighter( - startOffset, - endOffset, - HighlighterLayer.FIRST - 100, - TextAttributes(), - HighlighterTargetArea.LINES_IN_RANGE - ) - highlighter.gutterIconRenderer = ActionToGutterRendererAdapter(gutterAction) - this.highlighters = listOf(highlighter) - } + if (gutterAction == null) return@runInEdt + + val markupModel = editor.markupModel + val interval = safeInterval ?: return@runInEdt + val startOffset = editor.document.getLineStartOffset(interval.lines.first) + val endOffset = editor.document.getLineEndOffset(interval.lines.last) + val highlighter = markupModel.addRangeHighlighter( + startOffset, + endOffset, + HighlighterLayer.FIRST - 100, + TextAttributes(), + HighlighterTargetArea.LINES_IN_RANGE + ) + highlighter.gutterIconRenderer = ActionToGutterRendererAdapter(gutterAction) + this.highlighters = listOf(highlighter) } override fun dispose() = cell.manager.update { ctx ->