Skip to content

Commit 0f31fd6

Browse files
authored
Fix multiline mentions (anomalyco#27649)
1 parent aa07e21 commit 0f31fd6

2 files changed

Lines changed: 20 additions & 5 deletions

File tree

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,48 @@
11
const graphemes = new Intl.Segmenter(undefined, { granularity: "grapheme" })
22

3+
function promptOffsetWidth(value: string) {
4+
let width = 0
5+
for (const part of graphemes.segment(value)) {
6+
// Textarea offsets count newlines as one position; Bun.stringWidth counts them as zero.
7+
width += part.segment === "\n" ? 1 : Bun.stringWidth(part.segment)
8+
}
9+
return width
10+
}
11+
312
function displayOffsetIndex(value: string, offset: number) {
413
if (offset <= 0) return 0
514

615
let width = 0
716
for (const part of graphemes.segment(value)) {
8-
const next = width + Bun.stringWidth(part.segment)
17+
const next = width + promptOffsetWidth(part.segment)
918
if (next > offset) return part.index
1019
width = next
1120
}
1221

1322
return value.length
1423
}
1524

16-
export function displaySlice(value: string, start = 0, end = Bun.stringWidth(value)) {
25+
export function displaySlice(value: string, start = 0, end = promptOffsetWidth(value)) {
1726
return value.slice(displayOffsetIndex(value, start), displayOffsetIndex(value, end))
1827
}
1928

2029
export function displayCharAt(value: string, offset: number) {
2130
let width = 0
2231
for (const part of graphemes.segment(value)) {
23-
const next = width + Bun.stringWidth(part.segment)
32+
const next = width + promptOffsetWidth(part.segment)
2433
if (offset === width || offset < next) return part.segment
2534
width = next
2635
}
2736
}
2837

29-
export function mentionTriggerIndex(value: string, offset = Bun.stringWidth(value)) {
38+
export function mentionTriggerIndex(value: string, offset = promptOffsetWidth(value)) {
3039
const text = displaySlice(value, 0, offset)
3140
const index = text.lastIndexOf("@")
3241
if (index === -1) return
3342

3443
const before = index === 0 ? undefined : text[index - 1]
3544
const query = text.slice(index)
3645
if ((before === undefined || /\s/.test(before)) && !/\s/.test(query)) {
37-
return Bun.stringWidth(text.slice(0, index))
46+
return promptOffsetWidth(text.slice(0, index))
3847
}
3948
}

packages/opencode/test/cli/run/prompt.shared.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ describe("run prompt shared", () => {
126126
expect(mentionTriggerIndex("👨‍👩‍👧‍👦 @src", Bun.stringWidth("👨‍👩‍👧‍👦 @src"))).toBe(3)
127127
expect(displayCharAt("👨‍👩‍👧‍👦 @src", Bun.stringWidth("👨‍👩‍👧‍👦 @"))).toBe("s")
128128
expect(displaySlice("👨‍👩‍👧‍👦 @src", 3, Bun.stringWidth("👨‍👩‍👧‍👦 @src"))).toBe("@src")
129+
expect(mentionTriggerIndex("@file1\n@file2", 13)).toBe(7)
130+
expect(displayCharAt("@file1\n@file2", 6)).toBe("\n")
131+
expect(displaySlice("@file1\n@file2", 8, 13)).toBe("file2")
132+
expect(mentionTriggerIndex("@file1\nfoo @file2", 17)).toBe(11)
133+
expect(mentionTriggerIndex("中文 @one\n@two", 14)).toBe(10)
134+
expect(displaySlice("中文 @one\n@two", 11, 14)).toBe("two")
129135
expect(mentionTriggerIndex("中文@")).toBeUndefined()
130136
expect(mentionTriggerIndex("こんにちは@")).toBeUndefined()
131137
expect(mentionTriggerIndex("한국어@")).toBeUndefined()

0 commit comments

Comments
 (0)