♻️ refactor(vscode): 卡片里的子块只留挂在子行上、点了才展开的一种,测试假数据换掉旧 React 卡片的名字
This commit is contained in:
1 parent
f6bc217e91
commit
5e994cd90e
16 files changed
+121
-130
No files matched your search
@@ -41,15 +41,15 @@ interface BlockViewBase {
|
||||
commentEndLine?: number
|
||||
/** 库规则的卡片才有:筛选栏里这个种类的名字,卡片边框的颜色(RuleCard.badge)。图标在 concepts 里。预览同类块一样默认收起它的子块。 */
|
||||
badge?: KindBadge
|
||||
/** 库规则放进父卡片里的子块才有(见 RuleSlot):画在父卡片属性表第 row 行下面,不排在父卡片的子块里。 */
|
||||
/** 库规则挂在父卡片子行上的子块才有(见 RuleSlot):展开时画在父卡片属性表第 row 行下面,不排在父卡片的子块里。 */
|
||||
slot?: BlockSlot
|
||||
children: BlockView[]
|
||||
}
|
||||
|
||||
/** 子块画在父卡片的哪儿:属性表第 row 行下面;item 是讲它的那个子行,点那个子行的第一格才展开(见 RuleSlot.item)。 */
|
||||
/** 子块挂在父卡片的哪儿:属性表第 row 行的第 item 个子行,点那个子行的第一格才在这一行下面展开(见 RuleSlot)。 */
|
||||
export interface BlockSlot {
|
||||
row: number
|
||||
item?: number
|
||||
item: number
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,7 +378,7 @@ export function toBlockViews(
|
||||
return plain({ ...replacement.as, node: block.node, header: span, children: kept, ...(attributes && { attributes }) }, span, id, scope)
|
||||
}
|
||||
const { kind, badge, rows, defines, attributes } = replacement
|
||||
const slots = new Map((replacement.slots ?? []).map(({ block: child, row, item }): [CodeBlock, BlockSlot] => [child, { row, ...(item !== undefined && { item }) }]))
|
||||
const slots = new Map((replacement.slots ?? []).map(({ block: child, row, item }): [CodeBlock, BlockSlot] => [child, { row, item }]))
|
||||
const { anchor, inner } = anchors.place(scope, kind, defines && source.slice(defines.startIndex, defines.endIndex), rowsText(span))
|
||||
return {
|
||||
id,
|
||||
@@ -395,7 +395,7 @@ export function toBlockViews(
|
||||
properties: rows,
|
||||
}
|
||||
}
|
||||
/** 一串兄弟块转成视图数据;略去的块不占位置,id 按出了块的往下数。slots 是父卡片放进它属性表里的那几块(见 BlockSlot)。 */
|
||||
/** 一串兄弟块转成视图数据;略去的块不占位置,id 按出了块的往下数。slots 是父卡片挂在它属性表子行上的那几块(见 BlockSlot)。 */
|
||||
function list(siblings: readonly CodeBlock[], prefix: string, scope: string, from = 0, slots?: ReadonlyMap<CodeBlock, BlockSlot>): BlockView[] {
|
||||
const spans = blockSpans(siblings, source)
|
||||
const views: BlockView[] = []
|
||||
|
||||
@@ -889,19 +889,19 @@ export interface RuleCard {
|
||||
* 这一块的编辑范围同别的块,是原块整条语句(类就是整个类),盖住留下的子块:它编辑时预览不画这些子块,同一行不会同时在两处能改。
|
||||
*/
|
||||
absorbs: CodeBlock[]
|
||||
/** 画在卡片某一行下面的子块(见 RuleSlot);一块只能放一处。 */
|
||||
/** 挂在卡片某一行的子行上、点那个子行才展开的子块(见 RuleSlot);一块只能放一处。 */
|
||||
slots?: RuleSlot[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 卡片里的一处子块:block(原块 children 里的对象,没并进来的)不排在卡片后面,画在卡片第 row 行下面当子卡片,
|
||||
* 照常转成块(翻译、库规则、编辑、问 AI、差异都照常)。item 是这一行的子行里讲它的那一个(第几个,0 起):
|
||||
* 这时它默认收着,点那个子行的第一格才展开、一次只展开一个(Rust 实现块的方法表点方法名展开那张函数卡)。
|
||||
* 卡片里的一处子块:block(原块 children 里的对象,没并进来的)不排在卡片后面,挂在卡片第 row 行的第 item 个子行上(都从 0 起),
|
||||
* 照常转成块(翻译、库规则、编辑、问 AI、差异都照常)。它默认收着,点那个子行的第一格才在这一行下面展开当子卡片,
|
||||
* 同一行一次只展开一个(Rust 实现块的方法表点方法名展开那张函数卡)。
|
||||
*/
|
||||
export interface RuleSlot {
|
||||
block: CodeBlock
|
||||
row: number
|
||||
item?: number
|
||||
item: number
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -194,30 +194,33 @@ describe('库规则:认领', () => {
|
||||
expect(reports).toEqual(['库规则 omit 给第 3 行的 class 块的替身不合约定,这块照常显示:要略去的块还有子块'])
|
||||
})
|
||||
|
||||
it('放进卡片属性表里的子块:照常转成块,记下画在哪一行(哪个子行);不是留下的子块、没有那一行 / 子行、放了两处的不合约定', async () => {
|
||||
const rows = [{ label: '字段', value: [], items: [{ cells: [[{ text: 'name', role: 'variable' as const }]] }], columns: ['名称'] }, { label: '方法', value: [] }]
|
||||
it('挂在卡片子行上的子块:照常转成块,记下挂在哪一行的哪个子行;不是留下的子块、没有那一行 / 子行、放了两处的不合约定', async () => {
|
||||
const rows = [
|
||||
{ label: '字段', value: [], items: [{ cells: [[{ text: 'name', role: 'variable' as const }]] }], columns: ['名称'] },
|
||||
{ label: '方法', value: [], items: [{ cells: [[{ text: 'greet', role: 'function' as const }]] }], columns: ['名称'] },
|
||||
]
|
||||
const slotting = (slots: (block: CodeBlock) => RuleSlot[], id = 'slot'): LibraryRule => ({
|
||||
...cardRule(id),
|
||||
claim: block => block.kind === 'class'
|
||||
? { kind: `${id}.card`, badge: { label: '卡片', color: { dark: '#fff', light: '#000' }, icon: 'box' }, rows, absorbs: block.children.slice(0, 1), slots: slots(block) }
|
||||
: undefined,
|
||||
})
|
||||
const [, card] = await views(MODEL, [slotting(block => [{ block: block.children[2], row: 0, item: 0 }, { block: block.children[3], row: 1 }])])
|
||||
expect(card.children.map(child => [child.kind, child.slot])).toEqual([['comment', undefined], ['variable', { row: 0, item: 0 }], ['function', { row: 1 }]])
|
||||
const [, card] = await views(MODEL, [slotting(block => [{ block: block.children[2], row: 0, item: 0 }, { block: block.children[3], row: 1, item: 0 }])])
|
||||
expect(card.children.map(child => [child.kind, child.slot])).toEqual([['comment', undefined], ['variable', { row: 0, item: 0 }], ['function', { row: 1, item: 0 }]])
|
||||
|
||||
const reports: string[] = []
|
||||
const bad = await views(MODEL, [
|
||||
slotting(block => [{ block: block.children[0], row: 1 }], 'absorbed'),
|
||||
slotting(block => [{ block: block.children[3], row: 5 }], 'row'),
|
||||
slotting(block => [{ block: block.children[3], row: 1, item: 0 }], 'item'),
|
||||
slotting(block => [{ block: block.children[3], row: 1 }, { block: block.children[3], row: 0 }], 'twice'),
|
||||
slotting(block => [{ block: block.children[0], row: 1, item: 0 }], 'absorbed'),
|
||||
slotting(block => [{ block: block.children[3], row: 5, item: 0 }], 'row'),
|
||||
slotting(block => [{ block: block.children[3], row: 1, item: 1 }], 'item'),
|
||||
slotting(block => [{ block: block.children[3], row: 1, item: 0 }, { block: block.children[3], row: 0, item: 0 }], 'twice'),
|
||||
], reports)
|
||||
expect(bad[1].kind).toBe('class')
|
||||
expect(reports.map(report => report.replace(/^.*照常显示:/, ''))).toEqual([
|
||||
'放进卡片里的块不是留下的子块',
|
||||
'放进卡片第 5 行下面的块,卡片没有这一行',
|
||||
'放进卡片第 1 行第 0 个子行的块,这一行没有这个子行',
|
||||
'同一块放进了卡片里的几处',
|
||||
'挂在子行上的块不是留下的子块',
|
||||
'挂在卡片第 5 行的块,卡片没有这一行',
|
||||
'挂在卡片第 1 行第 1 个子行的块,这一行没有这个子行',
|
||||
'同一块挂在了卡片里的几处',
|
||||
])
|
||||
})
|
||||
|
||||
|
||||
@@ -50,14 +50,14 @@ function problemOf(rule: LibraryRule, block: CodeBlock, claimed: RuleBlock): str
|
||||
return `种类 id「${claimed.kind}」没以「${rule.id}.」开头`
|
||||
const slots = claimed.slots ?? []
|
||||
if (slots.some(slot => !block.children.includes(slot.block) || claimed.absorbs.includes(slot.block)))
|
||||
return '放进卡片里的块不是留下的子块'
|
||||
return '挂在子行上的块不是留下的子块'
|
||||
if (new Set(slots.map(slot => slot.block)).size < slots.length)
|
||||
return '同一块放进了卡片里的几处'
|
||||
return '同一块挂在了卡片里的几处'
|
||||
for (const { row, item } of slots) {
|
||||
if (!claimed.rows[row])
|
||||
return `放进卡片第 ${row} 行下面的块,卡片没有这一行`
|
||||
if (item !== undefined && !claimed.rows[row].items?.[item])
|
||||
return `放进卡片第 ${row} 行第 ${item} 个子行的块,这一行没有这个子行`
|
||||
return `挂在卡片第 ${row} 行的块,卡片没有这一行`
|
||||
if (!claimed.rows[row].items?.[item])
|
||||
return `挂在卡片第 ${row} 行第 ${item} 个子行的块,这一行没有这个子行`
|
||||
}
|
||||
for (const { reference, offset } of referencedSegments(claimed.rows)) {
|
||||
if (reference?.rule !== rule.id)
|
||||
|
||||
@@ -7,7 +7,7 @@ const BADGE: KindBadge = { label: '实现', color: COLOR, icon: 'square-function
|
||||
|
||||
/**
|
||||
* impl 的方法表:点方法名展开那张函数卡。
|
||||
* 子块放进属性表某一行(RuleSlot)。不看导入,Rust 文件都启用。
|
||||
* 方法挂在方法表的子行上(RuleSlot)。不看导入,Rust 文件都启用。
|
||||
*/
|
||||
export const rustImplRule: LibraryRule = {
|
||||
id: 'rust',
|
||||
|
||||
Reference in new issue
Block a user