Files
superwork/vscode/src/codeReader/qa/anchor.test.ts
T

94 lines
4.8 KiB
TypeScript

import type { BlockView } from '../blockView'
import { describe, expect, it } from 'vitest'
import { toBlockViews } from '../blockView'
import { createPythonAdapter } from '../languages/python'
import { WASM_DIR, withBlocks } from '../testSupport'
import { BlockAnchors } from './anchor'
const py = createPythonAdapter(WASM_DIR)
function views(source: string): Promise<BlockView[]> {
return withBlocks(py, 'python', source, blocks => toBlockViews(blocks, { source, language: py, markers: [] }, undefined))
}
function flatten(list: BlockView[]): BlockView[] {
return list.flatMap(view => [view, ...flatten(view.children)])
}
/** 各块的记号,按源码先后,连同它这块的第一行源码 */
async function anchors(source: string): Promise<[string, string][]> {
return flatten(await views(source)).map(view => [view.anchor, view.code.split('\n')[0]])
}
const SOURCE = `class ReferralRelation(Base):
def display(self):
if self.active:
return self.name
return "none"
def other(self):
if self.active:
return 1
def helper(x):
print(x)
print(x)
`
describe('块的位置记号', () => {
it('有名字的块是外层各个有名字的块加自己的种类和名字;没名字的块是外层最近的有名字的块加种类和源码哈希;同一层重复的依次编号', async () => {
const list = await anchors(SOURCE)
const hashed = (anchor: string): string => anchor.replace(/@[0-9a-f]{8}/g, '@#')
expect(list.map(([anchor, code]) => [hashed(anchor), code])).toEqual([
['class ReferralRelation', 'class ReferralRelation(Base):'],
['class ReferralRelation > function display', 'def display(self):'],
['class ReferralRelation > function display > if @#', 'if self.active:'],
['class ReferralRelation > function display > return @#', 'return self.name'],
['class ReferralRelation > function display > return @#', 'return "none"'],
['class ReferralRelation > function other', 'def other(self):'],
['class ReferralRelation > function other > if @#', 'if self.active:'],
['class ReferralRelation > function other > return @#', 'return 1'],
['function helper', 'def helper(x):'],
['function helper > statement @#', 'print(x)'],
['function helper > statement @#[1]', 'print(x)'],
])
// 两个函数里一模一样的条件各在各的函数下面,互不影响;源码不同的两个返回哈希不同
expect(list[2][0].split(' > ').at(-1)).toBe(list[6][0].split(' > ').at(-1))
expect(list[3][0]).not.toBe(list[4][0])
expect(new Set(list.map(([anchor]) => anchor)).size).toBe(list.length)
})
it('上面插几行、改缩进和空白:记号都不变', async () => {
const before = await anchors(SOURCE)
const after = await anchors(`import os\n\n# 新加的注释\n${SOURCE.replace('print(x)\n print(x)', 'print( x )\n print(x)')}`)
const shifted = after.filter(([anchor]) => before.some(([old]) => old === anchor))
expect(shifted.map(([anchor]) => anchor)).toEqual(before.map(([anchor]) => anchor))
})
it('改别的函数的名字:别处的记号不变,改名的函数和它里面的块换了记号', async () => {
const before = new Set((await anchors(SOURCE)).map(([anchor]) => anchor))
const after = (await anchors(SOURCE.replace('def other(self)', 'def renamed(self)'))).map(([anchor]) => anchor)
const kept = after.filter(anchor => before.has(anchor))
expect(kept).toEqual([...before].filter(anchor => !anchor.includes('function other')))
expect(after.filter(anchor => !before.has(anchor)).every(anchor => anchor.includes('function renamed'))).toBe(true)
})
it('没名字的块自己的源码改了:记号变了,旧问答认不回来;有名字的块改了主体、签名照样认得', async () => {
const before = await anchors(SOURCE)
const after = await anchors(SOURCE.replace('if self.active:\n return self.name', 'if self.visible:\n return self.name').replace('def helper(x):', 'def helper(x, y=1):'))
const anchorOf = (list: [string, string][], code: string): string | undefined => list.find(([, line]) => line === code)?.[0]
expect(after.map(([anchor]) => anchor)).not.toContain(anchorOf(before, 'if self.active:'))
expect(anchorOf(after, 'return self.name')).toBe(anchorOf(before, 'return self.name'))
expect(anchorOf(after, 'def helper(x, y=1):')).toBe(anchorOf(before, 'def helper(x):'))
})
it('名字里的空白压成一个空格,空名字按没名字算', () => {
const anchors = new BlockAnchors()
expect(anchors.place('', 'variable', '{\n a,\n b\n}', '').anchor).toBe('variable { a, b }')
const unnamed = anchors.place('scope', 'statement', ' ', 'x = 1')
expect(unnamed.anchor).toMatch(/^scope > statement @[0-9a-f]{8}$/)
expect(unnamed.inner).toBe('scope')
})
})