✨ feat(vscode): 代码阅读预览标出新增 / 修改 / 删除的卡片(左侧改动色条、新增淡绿底挂小标、删掉的块原位收成一行可展开),筛选栏第二行写对比基准和改动统计,新文件顶部横幅

This commit is contained in:
cruldra committed 2026-09-30 08:41:55 +08:00
1 parent 2a5806c6b5
commit 3b7ce69842
10 files changed
+491 -20

No files matched your search

@@ -0,0 +1,70 @@
import type { BlockView, ChangedBlock, DiffStats, DocumentDiff, RemovedView } from './types'
import { inlineText } from './segments'
/** 页面上怎么标差异:按块 id 取改动,按层取删掉的块。基准里没有这个文件(整份新增)时不逐块标,没有它。 */
export interface DiffTree {
/** 按块 id:新增、修改的块 */
changes: ReadonlyMap<string, ChangedBlock>
/** 按父块 id(顶层 ''):这一层删掉的块,照差异里的先后 */
removed: ReadonlyMap<string, PlacedRemoved[]>
/** 页面上正是差异算的那一版:卡片里的标记按下标认,只在这时画 */
current: boolean
}
/** 放在页面上的一块删掉的块:id 是页面上认它用的(`removed-<序号>`,不会和块 id、属性行 id 撞上),after 是排在哪块(id)后面,最前面时没有。 */
export interface PlacedRemoved {
id: string
after?: string
removed: RemovedView
}
function* allBlocks(blocks: readonly BlockView[]): Generator<BlockView> {
for (const block of blocks) {
yield block
yield* allBlocks(block.children)
}
}
/**
* 差异(块按记号认)→ 页面上的块 id:记号在页面上找不到的(文档刚改、差异还没重算)不标;删掉的块挂的父块、排在后面的那块找不到的也不画。
* shown 挑删掉的块画不画(被筛选藏掉的种类不画)。
*/
export function diffTree(blocks: readonly BlockView[], diff: DocumentDiff | null, version: number, shown: (removed: RemovedView) => boolean): DiffTree | undefined {
if (!diff || diff.newFile !== undefined)
return undefined
const ids = new Map([...allBlocks(blocks)].map(block => [block.anchor, block.id]))
const changes = new Map<string, ChangedBlock>()
for (const [anchor, change] of Object.entries(diff.blocks)) {
const id = ids.get(anchor)
if (id !== undefined)
changes.set(id, change)
}
const removed = new Map<string, PlacedRemoved[]>()
diff.removed.forEach((item, index) => {
const parent = item.parent === '' ? '' : ids.get(item.parent)
const after = item.after === undefined ? undefined : ids.get(item.after)
if (parent === undefined || (item.after !== undefined && after === undefined) || !shown(item))
return
removed.set(parent, [...removed.get(parent) ?? [], { id: `removed-${index}`, ...(after !== undefined && { after }), removed: item }])
})
return { changes, removed, current: diff.version === version }
}
/** 标了改动的块默认展开(函数、类本来默认收起子块):改了什么一眼能看到。 */
export function openedByDiff(tree: DiffTree | undefined): ReadonlySet<string> {
return new Set(tree?.changes.keys())
}
/** 「新增 2 · 修改 3 · 删除 1」的各段:没有的那种不写。 */
export function statsParts(stats: DiffStats): { change: keyof DiffStats, text: string }[] {
const labels: Record<keyof DiffStats, string> = { add: '新增', mod: '修改', del: '删除' }
return (['add', 'mod', 'del'] as const).filter(change => stats[change] > 0).map(change => ({ change, text: `${labels[change]} ${stats[change]}` }))
}
/** 删掉的块收起时写的名字:属性表是第一行的值(函数名、变量名、导入的来源),一行译文是译文;只取第一行。 */
export function removedTitle(block: BlockView): string {
const text = 'properties' in block
? block.properties[0] && ('markdown' in block.properties[0].value ? block.properties[0].value.markdown : inlineText(block.properties[0].value))
: 'markdown' in block.translation ? block.translation.markdown : inlineText(block.translation)
return (text ?? '').split('\n', 1)[0].trim()
}