206 lines
11 KiB
TypeScript
206 lines
11 KiB
TypeScript
import type { FindResult, FindUnit } from './find'
|
|
import type { WrittenBlock } from './testBlocks'
|
|
import type { BlockKind, Inline, Markdown, PropertyRow, Segment } from './types'
|
|
import { describe, expect, it } from 'vitest'
|
|
import { nameMap } from './displayNames'
|
|
import { findCount, findMatches, findUnits, PageFind, paintPlan, shownText, spansOf } from './find'
|
|
import { placementOf } from './placement'
|
|
import { placed } from './testBlocks'
|
|
|
|
function name(text: string, offset: number): Segment {
|
|
return { text, role: 'variable', offset }
|
|
}
|
|
|
|
function word(text: string): Segment {
|
|
return { text, role: 'keyword' }
|
|
}
|
|
|
|
function textBlock(id: string, kind: BlockKind, line: number, translation: Inline[], children: WrittenBlock[] = []): WrittenBlock {
|
|
return { id, anchor: id, kind, lines: [line, line], attributes: { kind }, concepts: [{ id: kind, label: kind, icon: 'box' }], children, translation }
|
|
}
|
|
|
|
function factBlock(id: string, kind: BlockKind, line: number, properties: PropertyRow[], children: WrittenBlock[] = []): WrittenBlock {
|
|
return { id, anchor: id, kind, lines: [line, line], attributes: { kind }, concepts: [{ id: kind, label: kind, icon: 'box' }], children, properties }
|
|
}
|
|
|
|
/** Markdown 显示成的字:测试里就是原文 */
|
|
const markdownText = (markdown: Markdown): string => markdown.markdown
|
|
|
|
/**
|
|
* 0 def load(path): 函数默认折叠
|
|
* 1 return get_db(path)
|
|
* 3 LIMIT = load(3)
|
|
*/
|
|
const LOAD_ROWS: PropertyRow[] = [
|
|
{ label: '名称', value: [name('load', 4)] },
|
|
{ label: '输入参数', value: [word('1 个')], items: [{ cells: [[name('path', 9)]] }] },
|
|
]
|
|
|
|
const BLOCKS: WrittenBlock[] = [
|
|
factBlock('0', 'function', 0, LOAD_ROWS, [
|
|
textBlock('0.0', 'return', 1, [word('返回'), name('get_db', 30), name('path', 37)]),
|
|
]),
|
|
textBlock('1', 'const', 3, [word('设'), name('LIMIT', 50), word('为'), name('load', 58)]),
|
|
]
|
|
|
|
function unitsOf(blocks: WrittenBlock[], names = nameMap([])): FindUnit[] {
|
|
const page = placed(blocks)
|
|
return findUnits(page, placementOf(page), names, markdownText)
|
|
}
|
|
|
|
const ANYWHERE = (): boolean => true
|
|
|
|
describe('查找栏:页面上能查的字', () => {
|
|
it('整份文件按页面先后排,折叠着的块里的也在;每段记着在哪块、画在哪、要展开什么、块在第几行', () => {
|
|
expect(unitsOf(BLOCKS).map(({ block, key, expand, line, text }) => ({ block, key, expand, line, text }))).toEqual([
|
|
{ block: '0', key: '0/p0#label', expand: [], line: 0, text: '名称' },
|
|
{ block: '0', key: '0/p0#value', expand: [], line: 0, text: 'load' },
|
|
{ block: '0', key: '0/p1#label', expand: [], line: 0, text: '输入参数' },
|
|
{ block: '0', key: '0/p1#value', expand: [], line: 0, text: '1 个' },
|
|
{ block: '0', key: '0/p1/0#0', expand: ['0/p1'], line: 0, text: 'path' },
|
|
{ block: '0.0', key: '0.0#text', expand: ['0'], line: 1, text: '返回get_dbpath' },
|
|
{ block: '1', key: '1#text', expand: [], line: 3, text: '设LIMIT为load' },
|
|
])
|
|
})
|
|
|
|
it('按页面上显示的字:起了中文名的名字是中文名(定义处后面还跟着原名),框是框头加框里的字,收起的类名只有小标', () => {
|
|
const names = nameMap([{ offset: 58, length: 4, name: '加载' }, { offset: 50, length: 5, name: '上限', definition: true }])
|
|
const shown = shownText([word('设'), name('LIMIT', 50), word('为'), name('load', 58)], 0, names)
|
|
expect(shown).toEqual({ text: '设上限LIMIT为加载', renamed: [{ start: 9, end: 11, original: 'load' }] })
|
|
const boxed: Inline[] = [
|
|
word('对'),
|
|
{ style: 'iteration', label: '每个', segments: [name('item', 3), { style: 'tag', label: '', segments: [word('x')] }] },
|
|
{ style: 'classes', label: '样式 3 个类', detail: 'a\nb\nc', segments: [word('a b c')] },
|
|
]
|
|
expect(shownText(boxed, 0, nameMap([])).text).toBe('对每个itemx样式 3 个类')
|
|
expect(unitsOf(BLOCKS, names).at(-1)?.text).toBe('设上限LIMIT为加载')
|
|
})
|
|
|
|
it('注释、说明的 Markdown 是渲染出来的字;属性名、表头照原样', () => {
|
|
const blocks = [factBlock('0', 'class', 0, [
|
|
{ label: '说明', value: { markdown: '**加载**配置' } },
|
|
{ label: '字段', value: [word('1 个')], columns: ['名称', '类型'], items: [{ cells: [[name('path', 9)], [word('str')]] }] },
|
|
])]
|
|
const page = placed(blocks)
|
|
const units = findUnits(page, placementOf(page), nameMap([]), markdown => markdown.markdown.replaceAll('**', ''))
|
|
expect(units.map(unit => [unit.key, unit.text])).toEqual([
|
|
['0/p0#label', '说明'],
|
|
['0/p0#value', '加载配置'],
|
|
['0/p1#label', '字段'],
|
|
['0/p1#value', '1 个'],
|
|
['0/p1#h0', '名称'],
|
|
['0/p1#h1', '类型'],
|
|
['0/p1/0#0', 'path'],
|
|
['0/p1/0#1', 'str'],
|
|
])
|
|
})
|
|
})
|
|
|
|
describe('查找栏:命中', () => {
|
|
it('纯文字、不分大小写,一段字里从前往后不重叠地找;特殊字符照字面', () => {
|
|
expect(spansOf('ab')('xAbaBab')).toEqual([{ start: 1, end: 3 }, { start: 3, end: 5 }, { start: 5, end: 7 }])
|
|
expect(spansOf('aa')('aaaa')).toEqual([{ start: 0, end: 2 }, { start: 2, end: 4 }])
|
|
expect(spansOf('a.(b')('xa.(b a-(b')).toEqual([{ start: 1, end: 5 }])
|
|
expect(spansOf('')('abc')).toEqual([])
|
|
})
|
|
|
|
it('各段字分开找,不跨段;按页面先后排', () => {
|
|
const units = unitsOf(BLOCKS)
|
|
expect(findMatches(units, 'LOAD')).toEqual([{ unit: 1, start: 0, end: 4 }, { unit: 6, start: 7, end: 11 }])
|
|
expect(findMatches(units, 'namepath')).toEqual([])
|
|
expect(findMatches(units, 'path').map(match => units[match.unit].key)).toEqual(['0/p1/0#0', '0.0#text'])
|
|
})
|
|
|
|
it('显示成中文名的名字,输入源码原名也算命中,命中的是整个中文名;中文名自己也命中时不重复算', () => {
|
|
const names = nameMap([{ offset: 58, length: 4, name: '加载' }, { offset: 4, length: 4, name: 'load加载' }])
|
|
const units = unitsOf(BLOCKS, names)
|
|
expect(findMatches(units, 'Load')).toEqual([{ unit: 1, start: 0, end: 4 }, { unit: 6, start: 7, end: 9 }])
|
|
expect(findMatches(units, '加载')).toEqual([{ unit: 1, start: 4, end: 6 }, { unit: 6, start: 7, end: 9 }])
|
|
})
|
|
|
|
it('查找栏上的数:没写字时不显示,没有命中写「无结果」,有当前那处写「第几处/共几处」,一处都跳不到时写「N 处」', () => {
|
|
const matches = [{ unit: 0, start: 0, end: 1 }, { unit: 1, start: 0, end: 1 }]
|
|
expect(findCount({ query: '', matches: [] })).toBeUndefined()
|
|
expect(findCount({ query: 'x', matches: [] })).toBe('无结果')
|
|
expect(findCount({ query: 'x', matches, current: 1 })).toBe('2/2')
|
|
expect(findCount({ query: 'x', matches })).toBe('2 处')
|
|
})
|
|
|
|
it('画高亮要的:按段字的记号分好各段的字和命中,当前那处是它那段里的第几个', () => {
|
|
const units = unitsOf(BLOCKS)
|
|
const result: FindResult = { query: 't', matches: findMatches(units, 't'), current: 2 }
|
|
const plan = paintPlan(units, result)
|
|
expect(plan.query).toBe('t')
|
|
expect(plan.texts.get('0/p1/0#0')).toEqual({ text: 'path', spans: [{ start: 2, end: 3 }] })
|
|
expect(plan.texts.get('0.0#text')).toEqual({ text: '返回get_dbpath', spans: [{ start: 4, end: 5 }, { start: 10, end: 11 }] })
|
|
expect(plan.texts.has('0/p0#value')).toBe(false)
|
|
expect(plan.current).toEqual({ key: '0.0#text', index: 1 })
|
|
expect(paintPlan(units, { ...result, current: 1 }).current).toEqual({ key: '0.0#text', index: 0 })
|
|
expect(paintPlan(units, { ...result, current: undefined }).current).toBeUndefined()
|
|
})
|
|
})
|
|
|
|
describe('查找栏:当前那处', () => {
|
|
function session(units: FindUnit[]): { find: PageFind, results: FindResult[] } {
|
|
const results: FindResult[] = []
|
|
const find = new PageFind(result => results.push(result))
|
|
find.setUnits(units, ANYWHERE)
|
|
return { find, results }
|
|
}
|
|
|
|
it('写了字:从给的那处(视口顶部那块的行、选中的字)往后第一处能跳到的是当前那处;被筛掉的块里的跳过,总数照旧', () => {
|
|
const units = unitsOf(BLOCKS)
|
|
const { find, results } = session(units)
|
|
expect(find.search('load', ANYWHERE, { line: 1, start: 0 })).toEqual({ unit: 6, start: 7, end: 11 })
|
|
expect(results.at(-1)).toEqual({ query: 'load', matches: [{ unit: 1, start: 0, end: 4 }, { unit: 6, start: 7, end: 11 }], current: 1 })
|
|
const other = session(units).find
|
|
expect(other.search('load', unit => unit.block !== '1', { line: 1, start: 0 })).toEqual({ unit: 1, start: 0, end: 4 })
|
|
expect(other.state.current).toBe(0)
|
|
expect(session(units).find.search('load', () => false, { line: 1, start: 0 })).toBeUndefined()
|
|
const selected = session(units).find
|
|
expect(selected.search('load', ANYWHERE, { key: '0/p0#value', line: 0, start: 1 })).toEqual({ unit: 6, start: 7, end: 11 })
|
|
expect(selected.search('load', ANYWHERE, { key: '0/p0#value', line: 0, start: 0 })).toEqual({ unit: 1, start: 0, end: 4 })
|
|
})
|
|
|
|
it('改了字:从原来那处往后接着找,原来那处还命中就停在原地', () => {
|
|
const units = unitsOf(BLOCKS)
|
|
const { find } = session(units)
|
|
find.search('pat', ANYWHERE)
|
|
expect(find.move(1, ANYWHERE)).toEqual({ unit: 5, start: 8, end: 11 })
|
|
expect(find.search('path', ANYWHERE)).toEqual({ unit: 5, start: 8, end: 12 })
|
|
expect(find.search('load', ANYWHERE)).toEqual({ unit: 6, start: 7, end: 11 })
|
|
expect(find.search('', ANYWHERE)).toBeUndefined()
|
|
expect(find.state).toEqual({ query: '', matches: [] })
|
|
})
|
|
|
|
it('下一处、上一处按页面先后,到头了从另一头接着;跳不到的跳过;没有命中时不动', () => {
|
|
const units = unitsOf(BLOCKS)
|
|
const { find } = session(units)
|
|
expect(find.move(1, ANYWHERE)).toBeUndefined()
|
|
find.search('a', ANYWHERE)
|
|
expect(find.state.current).toBe(0)
|
|
expect(find.state.matches).toHaveLength(4)
|
|
expect(find.move(-1, ANYWHERE)).toBe(find.state.matches[3])
|
|
expect(find.move(1, ANYWHERE)).toBe(find.state.matches[0])
|
|
const shown = (unit: FindUnit): boolean => unit.block !== '0.0'
|
|
expect(find.move(1, shown)).toBe(find.state.matches[1])
|
|
expect(find.move(1, shown)).toBe(find.state.matches[3])
|
|
expect(find.state.current).toBe(3)
|
|
})
|
|
|
|
it('换了一版文档:照旧按原来的字找,当前那处停在原来那段字里原来的位置往后;那段字没了,按原来那块的行往后找', () => {
|
|
const { find, results } = session(unitsOf(BLOCKS))
|
|
find.search('path', ANYWHERE)
|
|
find.move(1, ANYWHERE)
|
|
const before = results.length
|
|
const added = [textBlock('9', 'statement', 0, [word('path')]), ...BLOCKS]
|
|
find.setUnits(unitsOf(added), ANYWHERE)
|
|
expect(results).toHaveLength(before + 1)
|
|
expect(find.state.matches).toHaveLength(3)
|
|
expect(find.state.current).toBe(2)
|
|
const renumbered = [factBlock('0', 'function', 0, LOAD_ROWS), textBlock('1', 'return', 1, [name('path', 37)])]
|
|
find.setUnits(unitsOf(renumbered), ANYWHERE)
|
|
expect(find.state.current).toBe(1)
|
|
})
|
|
})
|