* Insert text at a character offset within the concatenated JSXText of a subtree. * Walks through JSXText nodes, tracking cumulative offset, and inserts when found.
(node: any, offset: number, insertion: string, source?: string)
| 1499 | * Walks through JSXText nodes, tracking cumulative offset, and inserts when found. |
| 1500 | */ |
| 1501 | function insertInJSXTextAtOffset(node: any, offset: number, insertion: string, source?: string): boolean { |
| 1502 | const segments = buildImmediateTextSegments(node, source); |
| 1503 | if (segments.length === 0) return false; |
| 1504 | |
| 1505 | let cursor = 0; |
| 1506 | for (let i = 0; i < segments.length; i++) { |
| 1507 | const segment = segments[i]; |
| 1508 | const segmentStart = cursor; |
| 1509 | const segmentEnd = cursor + segment.text.length; |
| 1510 | cursor = segmentEnd; |
| 1511 | |
| 1512 | if (offset < segmentStart || offset > segmentEnd) continue; |
| 1513 | |
| 1514 | if (segment.type === "boundary") { |
| 1515 | return insertBetweenImmediateChildren(node, segment.leftChildIndex, segment.rightChildIndex, insertion, segment.text); |
| 1516 | } |
| 1517 | |
| 1518 | const child = node.children?.[segment.childIndex]; |
| 1519 | if (!child) return false; |
| 1520 | |
| 1521 | const nextSegment = segments[i + 1]; |
| 1522 | if ( |
| 1523 | offset === segmentEnd && |
| 1524 | nextSegment?.type === "child" && |
| 1525 | nextSegment.childIndex !== segment.childIndex |
| 1526 | ) { |
| 1527 | return insertBetweenImmediateChildren(node, segment.childIndex, nextSegment.childIndex, insertion); |
| 1528 | } |
| 1529 | |
| 1530 | if (child.type === "JSXText") { |
| 1531 | const rawOffset = mapRenderedOffsetToRawIndex(child.value, Math.max(0, offset - segmentStart), "end"); |
| 1532 | const localOffset = Math.max(0, Math.min(child.value.length, rawOffset)); |
| 1533 | child.value = child.value.slice(0, localOffset) + insertion + child.value.slice(localOffset); |
| 1534 | return true; |
| 1535 | } |
| 1536 | |
| 1537 | if (child.type === "JSXElement") { |
| 1538 | return insertInJSXTextAtOffset(child, offset - segmentStart, insertion, source); |
| 1539 | } |
| 1540 | |
| 1541 | if (child.type === "JSXExpressionContainer" && child.expression?.type === "StringLiteral") { |
| 1542 | const value = String(child.expression.value ?? ""); |
| 1543 | const rawOffset = mapRenderedOffsetToRawIndex(value, Math.max(0, offset - segmentStart), "end"); |
| 1544 | const localOffset = Math.max(0, Math.min(value.length, rawOffset)); |
| 1545 | child.expression.value = value.slice(0, localOffset) + insertion + value.slice(localOffset); |
| 1546 | return true; |
| 1547 | } |
| 1548 | |
| 1549 | return false; |
| 1550 | } |
| 1551 | |
| 1552 | const lastSegment = segments[segments.length - 1]; |
| 1553 | if (lastSegment.type === "child") { |
| 1554 | const child = node.children?.[lastSegment.childIndex]; |
| 1555 | if (child?.type === "JSXText") { |
| 1556 | child.value += insertion; |
| 1557 | return true; |
| 1558 | } |
no test coverage detected