(text: string, cursor: number, options?: WordNavigationOptions)
| 76 | * Pure function - does not mutate any state. |
| 77 | */ |
| 78 | export function findWordForward(text: string, cursor: number, options?: WordNavigationOptions): number { |
| 79 | if (cursor >= text.length) return text.length; |
| 80 | |
| 81 | const textAfterCursor = text.slice(cursor); |
| 82 | const segmentFn = options?.segment; |
| 83 | const isAtomic = options?.isAtomicSegment; |
| 84 | const segments = segmentFn ? segmentFn(textAfterCursor) : wordSegmenter.segment(textAfterCursor); |
| 85 | const iterator = segments[Symbol.iterator](); |
| 86 | let next = iterator.next(); |
| 87 | let newCursor = cursor; |
| 88 | |
| 89 | // Skip leading whitespace |
| 90 | while (!next.done && !isAtomic?.(next.value.segment) && isWhitespaceChar(next.value.segment)) { |
| 91 | newCursor += next.value.segment.length; |
| 92 | next = iterator.next(); |
| 93 | } |
| 94 | |
| 95 | if (next.done) return newCursor; |
| 96 | |
| 97 | if (isAtomic?.(next.value.segment)) { |
| 98 | // Skip one atomic segment. |
| 99 | newCursor += next.value.segment.length; |
| 100 | } else if (next.value.isWordLike) { |
| 101 | // Skip inside one word-like segment, preserving ASCII punctuation boundaries. |
| 102 | newCursor += PUNCTUATION_REGEX.exec(next.value.segment)?.index ?? next.value.segment.length; |
| 103 | } else { |
| 104 | // Skip non-word non-whitespace run (punctuation) |
| 105 | while ( |
| 106 | !next.done && |
| 107 | !isAtomic?.(next.value.segment) && |
| 108 | !next.value.isWordLike && |
| 109 | !isWhitespaceChar(next.value.segment) |
| 110 | ) { |
| 111 | newCursor += next.value.segment.length; |
| 112 | next = iterator.next(); |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | return newCursor; |
| 117 | } |
no test coverage detected