Files
superwork/vscode/src/codeReader/blockView.test.ts
T

139 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { BlockView } from './blockView'
import type { LanguageAdapter, Translator } from './contract'
import { describe, expect, it } from 'vitest'
import { readBlockViews, toBlockViews } from './blockView'
import { createPythonAdapter } from './languages/python'
import { createTypeScriptAdapter } from './languages/typescript'
import { propertyLines, readFixture, runtime, segmentText, WASM_DIR, withBlocks } from './testSupport'
import { pythonZhTranslator } from './translators/pythonZh'
import { typescriptZhTranslator } from './translators/typescriptZh'
const ts = createTypeScriptAdapter(WASM_DIR)
const py = createPythonAdapter(WASM_DIR)
function views(adapter: LanguageAdapter, languageId: string, translator: Translator | undefined, source: string): Promise<BlockView[]> {
return withBlocks(adapter, languageId, source, blocks => toBlockViews(blocks, source, adapter, translator))
}
function flatten(list: BlockView[]): BlockView[] {
return list.flatMap(view => [view, ...flatten(view.children)])
}
function byId(list: BlockView[], id: string): BlockView {
const found = flatten(list).find(view => view.id === id)
if (!found)
throw new Error(`没有块 ${id}`)
return found
}
const SAMPLES: [LanguageAdapter, string, Translator, string][] = [
[ts, 'typescript', typescriptZhTranslator, 'envLock.ts.txt'],
[ts, 'typescript', typescriptZhTranslator, 'issueLock.ts.txt'],
[py, 'python', pythonZhTranslator, 'sample.py'],
[py, 'python', pythonZhTranslator, 'routes.py'],
]
const PROPERTY_KINDS = new Set(['function', 'import', 'const', 'variable'])
function translationText(view: BlockView): string {
if (!('translation' in view))
throw new Error(`块 ${view.id} 是属性表`)
return segmentText(view.translation)
}
describe('blockView', () => {
it.each(SAMPLES)('%#: codeTokens 拼起来等于 code,code 是 header 整行去共同缩进', async (adapter, languageId, translator, fixture) => {
const source = readFixture(fixture)
const lines = source.split('\n')
for (const view of flatten(await views(adapter, languageId, translator, source))) {
expect(view.codeTokens.map(token => token.text).join(''), view.id).toBe(view.code)
const rows = lines.slice(view.startLine, view.endLine + 1)
const indent = Math.min(...rows.filter(row => row.trim()).map(row => row.length - row.trimStart().length))
expect(view.code, view.id).toBe(rows.map(row => row.slice(indent)).join('\n'))
if ('properties' in view)
expect(view.properties.length, view.id).toBeGreaterThan(0)
else
expect(view.translation.length, view.id).toBeGreaterThan(0)
}
})
it.each(SAMPLES)('%#: 函数、导入、常量、变量块只带属性表,其余块只带一行译文', async (adapter, languageId, translator, fixture) => {
for (const view of flatten(await views(adapter, languageId, translator, readFixture(fixture)))) {
expect('properties' in view, view.id).toBe(PROPERTY_KINDS.has(view.kind))
expect('translation' in view, view.id).toBe(!PROPERTY_KINDS.has(view.kind))
}
})
it('id 是块在树里的位置,行号 0 起,多行块去掉共同缩进', async () => {
const env = await views(ts, 'typescript', typescriptZhTranslator, readFixture('envLock.ts.txt'))
expect(byId(env, '6.1.0')).toMatchObject({ kind: 'return', startLine: 25, endLine: 25, code: 'return []' })
expect(byId(env, '6.1')).toMatchObject({ kind: 'if', startLine: 24, code: 'if (!folder)' })
expect(byId(env, '8.2.1')).toMatchObject({ kind: 'catch', startLine: 45, code: 'catch (err) {' })
const issue = await views(ts, 'typescript', typescriptZhTranslator, readFixture('issueLock.ts.txt'))
expect(byId(issue, '3.4')).toMatchObject({
kind: 'return',
startLine: 19,
endLine: 23,
code: 'return {\n locked: true,\n prerequisiteNumber: prereq.number,\n prerequisiteColumn: prereq.column,\n}',
})
})
it('源码着色来自语法树:每个叶子一段,空白和标点归 plain', async () => {
const env = await views(ts, 'typescript', typescriptZhTranslator, readFixture('envLock.ts.txt'))
const tokens = byId(env, '6.3').codeTokens
expect(byId(env, '6.3').code).toBe('const uris = await workspace.findFiles(include, EXCLUDE_PATTERN)')
expect(tokens.slice(0, 7)).toEqual([
{ text: 'const', role: 'storage' },
{ text: ' ', role: 'plain' },
{ text: 'uris', role: 'variable' },
{ text: ' = ', role: 'plain' },
{ text: 'await', role: 'keyword' },
{ text: ' ', role: 'plain' },
{ text: 'workspace', role: 'variable' },
])
expect(tokens).toContainEqual({ text: 'findFiles', role: 'function' })
expect(byId(env, '0').codeTokens).toEqual([{ text: byId(env, '0').code, role: 'comment' }])
// 中文注释之后的行:语法树下标按字符串下标算,不按 UTF-8 字节
const issue = await views(ts, 'typescript', typescriptZhTranslator, readFixture('issueLock.ts.txt'))
expect(byId(issue, '3').codeTokens.slice(0, 5)).toEqual([
{ text: 'export', role: 'keyword' },
{ text: ' ', role: 'plain' },
{ text: 'function', role: 'storage' },
{ text: ' ', role: 'plain' },
{ text: 'evaluateLockedReason', role: 'function' },
])
expect(byId(issue, '2').codeTokens).toEqual([{ text: byId(issue, '2').code, role: 'comment' }])
const sample = await views(py, 'python', pythonZhTranslator, readFixture('sample.py'))
const signature = byId(sample, '5').codeTokens
expect(signature).toContainEqual({ text: 'def', role: 'storage' })
expect(signature).toContainEqual({ text: 'find_env_files', role: 'function' })
expect(signature.filter(token => token.text === 'str')).toEqual([{ text: 'str', role: 'type' }, { text: 'str', role: 'type' }])
})
it('\\r\\n 和制表符缩进:code 用 \\n 连接,去掉共同的制表符', async () => {
const source = 'function f() {\r\n\tif (a &&\r\n\t\tb) {\r\n\t\treturn 1\r\n\t}\r\n}\r\n'
const list = await views(ts, 'typescript', undefined, source)
const cond = byId(list, '0.0')
expect(cond).toMatchObject({ kind: 'if', startLine: 1, endLine: 2, code: 'if (a &&\n\tb) {', translation: [] })
expect(cond.codeTokens.map(token => token.text).join('')).toBe(cond.code)
expect(byId(list, '0')).toMatchObject({ kind: 'function', properties: [] })
})
it('readBlockViews:解析、转视图、释放语法树', async () => {
const list = await readBlockViews(runtime, py, pythonZhTranslator, 'python', 'def f(x: int) -> bool:\n return not x\n')
expect(list).toHaveLength(1)
expect('properties' in list[0] && propertyLines(list[0].properties)).toEqual([
'名称: f',
'访问级别: 公共',
'异步: 否',
'输入参数: 1 个',
' x | 整数 | ',
'输出参数: 布尔',
])
expect(translationText(list[0].children[0])).toBe('返回 非 x')
})
})