[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
This commit is contained in:
Uladzislau Trybukhouski
2026-07-16 14:55:08 +00:00
committed by intellij-monorepo-bot
parent 7c12d18ca7
commit ecdb57585d
@@ -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)
}