50 lines
2.0 KiB
TypeScript
50 lines
2.0 KiB
TypeScript
import type { Inline, Segment } from './types'
|
|
|
|
/** 取 segments 拼起来的文字里 [from, to) 这一段,每个字保留原来的词性。 */
|
|
export function sliceSegments(segments: Segment[], from: number, to: number): Segment[] {
|
|
const out: Segment[] = []
|
|
let offset = 0
|
|
for (const segment of segments) {
|
|
const start = Math.max(from, offset)
|
|
const end = Math.min(to, offset + segment.text.length)
|
|
if (start < end)
|
|
out.push({ text: segment.text.slice(start - offset, end - offset), role: segment.role })
|
|
offset += segment.text.length
|
|
if (offset >= to)
|
|
break
|
|
}
|
|
return out
|
|
}
|
|
|
|
/**
|
|
* 给输入框里改过的文字上色:`tokens` 是 `code` 的着色,和 `code` 相同的开头、结尾沿用原来的词性,
|
|
* 中间改动的部分先不着色,等扩展端按新代码着色的文档到了再换成准确的。
|
|
*/
|
|
export function recolor(tokens: Segment[], code: string, text: string): Segment[] {
|
|
if (text === code)
|
|
return tokens
|
|
const shorter = Math.min(code.length, text.length)
|
|
let head = 0
|
|
while (head < shorter && code[head] === text[head])
|
|
head++
|
|
let tail = 0
|
|
while (tail < shorter - head && code[code.length - 1 - tail] === text[text.length - 1 - tail])
|
|
tail++
|
|
const changed = text.slice(head, text.length - tail)
|
|
return [
|
|
...sliceSegments(tokens, 0, head),
|
|
...(changed ? [{ text: changed, role: 'plain' as const }] : []),
|
|
...sliceSegments(tokens, code.length - tail, code.length),
|
|
]
|
|
}
|
|
|
|
/** 着色的词拼成的文字(给折叠按钮的说明用);框写成框头加框里的文字。 */
|
|
export function inlineText(pieces: Inline[]): string {
|
|
return pieces.map(piece => 'role' in piece ? piece.text : `${piece.label} ${inlineText(piece.segments)}`).join('')
|
|
}
|
|
|
|
/** 文字里有没有 offset 处的那个名字,框里的也算。 */
|
|
export function showsOffset(pieces: Inline[], offset: number): boolean {
|
|
return pieces.some(piece => 'role' in piece ? piece.offset === offset : showsOffset(piece.segments, offset))
|
|
}
|