MCPcopy Create free account
hub / github.com/MoonshotAI/kimi-code / moveToVisualLine

Method moveToVisualLine

packages/pi-tui/src/components/editor.ts:1441–1521  ·  view source on GitHub ↗

* Move cursor to a target visual line, applying sticky column logic. * Shared by moveCursor() and pageScroll().

(
		visualLines: Array<{ logicalLine: number; startCol: number; length: number }>,
		currentVisualLine: number,
		targetVisualLine: number,
	)

Source from the content-addressed store, hash-verified

1439 * Shared by moveCursor() and pageScroll().
1440 */
1441 private moveToVisualLine(
1442 visualLines: Array<{ logicalLine: number; startCol: number; length: number }>,
1443 currentVisualLine: number,
1444 targetVisualLine: number,
1445 ): void {
1446 const currentVL = visualLines[currentVisualLine];
1447 const targetVL = visualLines[targetVisualLine];
1448 if (!(currentVL && targetVL)) return;
1449
1450 // When the cursor was snapped to a segment start, resolve the pre-snap
1451 // position against the VL it belongs to. This gives the correct visual
1452 // column even after a resize reshuffles VLs.
1453 let currentVisualCol: number;
1454 if (this.snappedFromCursorCol !== null) {
1455 const vlIndex = this.findVisualLineAt(visualLines, currentVL.logicalLine, this.snappedFromCursorCol);
1456 currentVisualCol = this.snappedFromCursorCol - visualLines[vlIndex]!.startCol;
1457 } else {
1458 currentVisualCol = this.state.cursorCol - currentVL.startCol;
1459 }
1460
1461 // For non-last segments, clamp to length-1 to stay within the segment
1462 const isLastSourceSegment =
1463 currentVisualLine === visualLines.length - 1 ||
1464 visualLines[currentVisualLine + 1]?.logicalLine !== currentVL.logicalLine;
1465 const sourceMaxVisualCol = isLastSourceSegment ? currentVL.length : Math.max(0, currentVL.length - 1);
1466
1467 const isLastTargetSegment =
1468 targetVisualLine === visualLines.length - 1 ||
1469 visualLines[targetVisualLine + 1]?.logicalLine !== targetVL.logicalLine;
1470 const targetMaxVisualCol = isLastTargetSegment ? targetVL.length : Math.max(0, targetVL.length - 1);
1471
1472 const moveToVisualCol = this.computeVerticalMoveColumn(currentVisualCol, sourceMaxVisualCol, targetMaxVisualCol);
1473
1474 // Set cursor position
1475 this.state.cursorLine = targetVL.logicalLine;
1476 const targetCol = targetVL.startCol + moveToVisualCol;
1477 const logicalLine = this.state.lines[targetVL.logicalLine] || "";
1478 this.state.cursorCol = Math.min(targetCol, logicalLine.length);
1479
1480 // Snap cursor to atomic segment boundary (e.g. paste markers)
1481 // so the cursor never lands in the middle of a multi-grapheme unit.
1482 // Single-grapheme segments don't need snapping.
1483 const segments = [...this.segment(logicalLine, "grapheme")];
1484 for (const seg of segments) {
1485 if (seg.index > this.state.cursorCol) break;
1486 if (seg.segment.length <= 1) continue;
1487 if (this.state.cursorCol < seg.index + seg.segment.length) {
1488 const isContinuation = seg.index < targetVL.startCol;
1489 const isMovingDown = targetVisualLine > currentVisualLine;
1490
1491 if (isContinuation && isMovingDown) {
1492 // The segment started on a previous visual line, and we
1493 // already visited it on the way down. Skip all remaining
1494 // continuation VLs and land on the first VL past it.
1495 const segEnd = seg.index + seg.segment.length;
1496 let next = targetVisualLine + 1;
1497 while (
1498 next < visualLines.length &&

Callers 2

moveCursorMethod · 0.95
pageScrollMethod · 0.95

Calls 3

findVisualLineAtMethod · 0.95
segmentMethod · 0.95

Tested by

no test coverage detected