diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java index 171e71619f66..e2f0299b1ed3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java @@ -1237,6 +1237,33 @@ final class EditorGutterComponentImpl extends EditorGutterComponentEx return list; } + @VisibleForTesting + public @Nullable Rectangle getActiveGutterRendererRectangle(int lineNum, String accessibleName) { + int firstVisibleOffset = myEditor.visualLineStartOffset(lineNum); + int lastVisibleOffset = EditorUtil.getVisualLineEndOffset(myEditor, lineNum); + List> rectangles = new ArrayList<>(); + Rectangle[] rectangle = {null}; + processRangeHighlighters(firstVisibleOffset, lastVisibleOffset, highlighter -> { + LineMarkerRenderer renderer = highlighter.getLineMarkerRenderer(); + if (renderer instanceof ActiveGutterRenderer activeRenderer) { + if (!activeRenderer.getAccessibleName().equals(accessibleName) || rectangle[0] != null) return; + Rectangle rect = getLineRendererRectangle(highlighter); + if (rect != null) { + Rectangle bounds = activeRenderer.calcBounds(myEditor, lineNum, rect); + if (bounds != null) { + int[] lineToYRange = myEditor.visualLineToYRange(lineNum); + boolean isAtLine = + lineToYRange[0] >= bounds.y && lineToYRange[1] <= (bounds.y + bounds.height); + if (isAtLine) { + rectangle[0] = bounds; + } + } + } + } + }); + return rectangle[0]; + } + private boolean isHighlighterVisible(RangeHighlighter highlighter) { return !FoldingUtil.isHighlighterFolded(myEditor, highlighter); } diff --git a/platform/remote-driver/test-sdk/src/com/intellij/driver/sdk/editors.kt b/platform/remote-driver/test-sdk/src/com/intellij/driver/sdk/editors.kt index 7a171523b79d..7d361158d4b9 100644 --- a/platform/remote-driver/test-sdk/src/com/intellij/driver/sdk/editors.kt +++ b/platform/remote-driver/test-sdk/src/com/intellij/driver/sdk/editors.kt @@ -26,6 +26,7 @@ interface Editor { fun logicalPositionToOffset(logicalPosition: LogicalPosition): Int fun getSelectionModel(): SelectionModel fun getSoftWrapModel(): SoftWrapModel + fun visualLineToY(visualLine: Int): Int } @Remote("com.intellij.openapi.editor.VisualPosition") diff --git a/platform/remote-driver/test-sdk/src/com/intellij/driver/sdk/ui/components/common/JEditorUi.kt b/platform/remote-driver/test-sdk/src/com/intellij/driver/sdk/ui/components/common/JEditorUi.kt index 6c002cc87985..1447956baa62 100644 --- a/platform/remote-driver/test-sdk/src/com/intellij/driver/sdk/ui/components/common/JEditorUi.kt +++ b/platform/remote-driver/test-sdk/src/com/intellij/driver/sdk/ui/components/common/JEditorUi.kt @@ -16,6 +16,7 @@ import com.intellij.driver.sdk.ui.components.UiComponent import com.intellij.driver.sdk.ui.remote.Component import org.intellij.lang.annotations.Language import java.awt.Point +import java.awt.Rectangle fun Finder.editor(@Language("xpath") xpath: String? = null): JEditorUiComponent { return x(xpath ?: "//div[@class='EditorComponentImpl']", @@ -232,7 +233,6 @@ class GutterUiComponent(data: ComponentData) : UiComponent(data) { val iconAreaOffset get() = gutter.getIconAreaOffset() - fun getGutterIcons(): List { waitFor { this.icons.isNotEmpty() } return this.icons @@ -249,6 +249,29 @@ class GutterUiComponent(data: ComponentData) : UiComponent(data) { rightClick(icons.firstOrNull { it.line == line - 1 }!!.location) } + fun clickLineMarkerAtLine(lineNum: Int, accessibleName: String, lineY: Int? = null) { + val lineIndex = lineNum - 1 + val rectangle = waitNotNull("No $accessibleName marker on line $lineNum") { + driver.withContext(OnDispatcher.EDT) { + gutter.getActiveGutterRendererRectangle(lineIndex, accessibleName) + } + } + val lineY = lineY ?: driver.withContext(OnDispatcher.EDT) { + val startY = gutter.getEditor().visualLineToY(lineIndex) + startY + (gutter.getEditor().getLineHeight() / 2) + } + click(Point(rectangle.centerX.toInt(), lineY)) + } + + fun clickVcsLineMarkerAtLine(line: Int) { + //to support a deleted block marker, click on the first third of the line + val lineY = driver.withContext(OnDispatcher.EDT) { + val startY = gutter.getEditor().visualLineToY(line - 1) + startY + (gutter.getEditor().getLineHeight() / 6) + } + clickLineMarkerAtLine(line, "VCS marker: changed line", lineY) + } + inner class GutterIcon(private val data: GutterIconWithLocation) { val line: Int get() = data.getLine() @@ -269,6 +292,7 @@ class GutterUiComponent(data: ComponentData) : UiComponent(data) { .findLast { it.trim().startsWith("path") }!!.split('=')[1] } } + } enum class GutterIcon(val path: String) { @@ -287,7 +311,6 @@ data class GutterState( val iconPath: String = "", ) - class InlayHint(val offset: Int, val text: String) fun List.getHint(offset: Int): InlayHint { @@ -298,12 +321,15 @@ fun List.getHint(offset: Int): InlayHint { return foundHint } - @Remote("com.intellij.openapi.editor.impl.EditorGutterComponentImpl") interface EditorGutterComponentImpl : Component { fun getLineGutterMarks(): List fun getIconAreaOffset(): Int + + fun getActiveGutterRendererRectangle(lineNum: Int, accessibleName: String): Rectangle? + + fun getEditor(): Editor } @Remote("com.intellij.openapi.editor.impl.GutterIconWithLocation")