From ecdb57585d5a7d300bc13d455e7a528e4b627437 Mon Sep 17 00:00:00 2001 From: Uladzislau Trybukhouski Date: Thu, 2 Jul 2026 18:00:32 +0200 Subject: [PATCH] [terminal] IJPL-245227 Fix miscalculating cursor position on mouse click when prompt contains wide characters (cherry picked from commit ea2eb6143170bd1a8b87a4b3ef50cbabae72ae3e) IJ-CR-212254 GitOrigin-RevId: c99add843506d9576c8f4cc5ef1d5c69320437fd --- .../view/impl/TerminalEventDispatcher.kt | 35 +++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalEventDispatcher.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalEventDispatcher.kt index 67e63a5becb1..074929799464 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalEventDispatcher.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/TerminalEventDispatcher.kt @@ -11,11 +11,13 @@ import com.intellij.openapi.application.WriteIntentReadAction import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.trace import com.intellij.openapi.editor.Editor +import com.intellij.openapi.editor.VisualPosition import com.intellij.openapi.editor.event.EditorMouseEvent import com.intellij.openapi.editor.event.EditorMouseListener import com.intellij.openapi.editor.event.EditorMouseMotionListener import com.intellij.openapi.editor.ex.EditorEx import com.intellij.openapi.editor.ex.FocusChangeListener +import com.intellij.openapi.editor.impl.EditorImpl import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.registry.Registry import com.intellij.terminal.JBTerminalSystemSettingsProviderBase @@ -28,6 +30,7 @@ import org.jetbrains.annotations.NonNls import org.jetbrains.plugins.terminal.TerminalEscapeBehaviorChangeNotification import org.jetbrains.plugins.terminal.block.reworked.TerminalSessionModel import java.awt.AWTEvent +import java.awt.Point import java.awt.event.InputEvent import java.awt.event.KeyAdapter import java.awt.event.KeyEvent @@ -325,15 +328,15 @@ internal fun setupMouseEventsHandling( editor.addEditorMouseListener(object : EditorMouseListener { override fun mousePressed(event: EditorMouseEvent) { if (settings.enableMouseReporting() && isRemoteMouseAction(event.mouseEvent)) { - val p = event.visualPosition - eventsHandler.mousePressed(p.column, p.line, event.mouseEvent) + val cell = editor.mousePointToGridCell(event.mouseEvent.point, event.visualPosition.line) + eventsHandler.mousePressed(cell.column, cell.line, event.mouseEvent) } } override fun mouseReleased(event: EditorMouseEvent) { if (settings.enableMouseReporting() && isRemoteMouseAction(event.mouseEvent)) { - val p = event.visualPosition - eventsHandler.mouseReleased(p.column, p.line, event.mouseEvent) + val cell = editor.mousePointToGridCell(event.mouseEvent.point, event.visualPosition.line) + eventsHandler.mouseReleased(cell.column, cell.line, event.mouseEvent) } } }, disposable) @@ -341,25 +344,37 @@ internal fun setupMouseEventsHandling( editor.addEditorMouseMotionListener(object : EditorMouseMotionListener { override fun mouseMoved(event: EditorMouseEvent) { if (settings.enableMouseReporting() && isRemoteMouseAction(event.mouseEvent)) { - val p = event.visualPosition - eventsHandler.mouseMoved(p.column, p.line, event.mouseEvent) + val cell = editor.mousePointToGridCell(event.mouseEvent.point, event.visualPosition.line) + eventsHandler.mouseMoved(cell.column, cell.line, event.mouseEvent) } } override fun mouseDragged(event: EditorMouseEvent) { if (settings.enableMouseReporting() && isRemoteMouseAction(event.mouseEvent)) { - val p = event.visualPosition - eventsHandler.mouseDragged(p.column, p.line, event.mouseEvent) + val cell = editor.mousePointToGridCell(event.mouseEvent.point, event.visualPosition.line) + eventsHandler.mouseDragged(cell.column, cell.line, event.mouseEvent) } } }, disposable) val mouseWheelListener = MouseWheelListener { event -> - val p = editor.xyToVisualPosition(event.point) - eventsHandler.mouseWheelMoved(p.column, p.line, event) + val cell = editor.mousePointToGridCell(event.point, editor.xyToVisualPosition(event.point).line) + eventsHandler.mouseWheelMoved(cell.column, cell.line, event) } editor.scrollPane.addMouseWheelListener(mouseWheelListener) Disposer.register(disposable, Disposable { editor.scrollPane.removeMouseWheelListener(mouseWheelListener) }) +} + +/** + * Maps a mouse point to the terminal grid cell physically under the pointer (floor by the fixed cell width), + * so a double-width (CJK) character correctly spans two cells. Unlike [Editor.xyToVisualPosition], which rounds + * to the nearest character boundary and counts such a character as a single column. + */ +private fun EditorEx.mousePointToGridCell(point: Point, line: Int): VisualPosition { + val grid = checkNotNull((this as? EditorImpl)?.characterGrid) { "The editor is not in the character grid mode" } + val lineStartX = visualPositionToXY(VisualPosition(line, 0)).x + val column = ((point.x - lineStartX) / grid.charWidth).toInt().coerceIn(0, (grid.columns - 1).coerceAtLeast(0)) + return VisualPosition(line, column) } \ No newline at end of file