16e9d6916d
String.length counts code points, not display columns, so CJK characters and emoji that occupy two terminal cells caused misaligned cursors, broken mention triggers, and incorrect history restoration offsets. Use Bun.stringWidth for now, we need an alternative for this. Fix #26716 Close #26922
40 lines
1.2 KiB
TypeScript
40 lines
1.2 KiB
TypeScript
const graphemes = new Intl.Segmenter(undefined, { granularity: "grapheme" })
|
|
|
|
function displayOffsetIndex(value: string, offset: number) {
|
|
if (offset <= 0) return 0
|
|
|
|
let width = 0
|
|
for (const part of graphemes.segment(value)) {
|
|
const next = width + Bun.stringWidth(part.segment)
|
|
if (next > offset) return part.index
|
|
width = next
|
|
}
|
|
|
|
return value.length
|
|
}
|
|
|
|
export function displaySlice(value: string, start = 0, end = Bun.stringWidth(value)) {
|
|
return value.slice(displayOffsetIndex(value, start), displayOffsetIndex(value, end))
|
|
}
|
|
|
|
export function displayCharAt(value: string, offset: number) {
|
|
let width = 0
|
|
for (const part of graphemes.segment(value)) {
|
|
const next = width + Bun.stringWidth(part.segment)
|
|
if (offset === width || offset < next) return part.segment
|
|
width = next
|
|
}
|
|
}
|
|
|
|
export function mentionTriggerIndex(value: string, offset = Bun.stringWidth(value)) {
|
|
const text = displaySlice(value, 0, offset)
|
|
const index = text.lastIndexOf("@")
|
|
if (index === -1) return
|
|
|
|
const before = index === 0 ? undefined : text[index - 1]
|
|
const query = text.slice(index)
|
|
if ((before === undefined || /\s/.test(before)) && !/\s/.test(query)) {
|
|
return Bun.stringWidth(text.slice(0, index))
|
|
}
|
|
}
|