⚡ perf(vscode): 代码阅读预览按卡片订阅状态:中文名、调用树棵数、单击高亮、问答一条消息只重画受影响的卡片和文字,新文档里内容没变的块沿用原来的对象不重画,没展开又没说过的块不画问答区
This commit is contained in:
1 parent
6e2c541558
commit
0ad4748cc6
27 files changed
+1359
-620
No files matched your search
@@ -1,6 +1,6 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import type { BlockTreeState, RewriteTree } from './BlockTree'
|
||||
import type { BlockTreeHandle, BlockTreeState, RewriteTree } from './BlockTree'
|
||||
import type { Ask, ChatState } from './qa'
|
||||
import type { BlockView, KindBadge } from './types'
|
||||
import { renderToStaticMarkup } from 'react-dom/server'
|
||||
@@ -11,17 +11,14 @@ import { createPythonAdapter } from '../../../src/codeReader/languages/python'
|
||||
import { fastapiRule } from '../../../src/codeReader/rules/fastapi'
|
||||
import { WASM_DIR, withBlocks } from '../../../src/codeReader/testSupport'
|
||||
import { pythonZhTranslator } from '../../../src/codeReader/translators/pythonZh'
|
||||
import { BlockLevel } from './BlockTree'
|
||||
import { BlockTree, treeState } from './BlockTree'
|
||||
import { applyFilter } from './filter'
|
||||
import { FilterBar } from './FilterBar'
|
||||
import { EMPTY_CHAT } from './qa'
|
||||
import { staticStore } from './store'
|
||||
import { collapsedIds } from './viewState'
|
||||
|
||||
// 高亮的上下文所在的模块、编辑框都连着和 VS Code 收发消息的那一套,渲染成 HTML 用不着
|
||||
vi.mock('./useOccurrences', async () => {
|
||||
const { createContext } = await import('react')
|
||||
return { HighlightContext: createContext(undefined) }
|
||||
})
|
||||
// 编辑框连着和 VS Code 收发消息的那一套,渲染成 HTML 用不着
|
||||
vi.mock('./vscode', () => ({ postMessage: () => {}, onMessage: () => () => {}, viewState: { get: () => ({ editing: [], folds: {} }), update: () => {} } }))
|
||||
|
||||
const BADGE: KindBadge = { label: '卡片', color: { dark: '#61dafb', light: '#0a7ea4' }, icon: 'box' }
|
||||
@@ -64,32 +61,34 @@ const CARD: BlockView = {
|
||||
|
||||
/** 重写注释的状态和动作:动作什么都不做 */
|
||||
function rewriteTree(state: ChatState): RewriteTree {
|
||||
return { state, onRewrite: () => {}, onAsk: () => {}, onStop: () => {}, onDelete: () => {}, onOpen: () => {}, onClose: () => {}, onSelectProfile: () => {} }
|
||||
return { store: staticStore(state), onRewrite: () => {}, onAsk: () => {}, onStop: () => {}, onDelete: () => {}, onOpen: () => {}, onClose: () => {}, onSelectProfile: () => {} }
|
||||
}
|
||||
|
||||
function state(collapsed: ReadonlySet<string>, rewrite: ChatState = { ...EMPTY_CHAT, available: true }): BlockTreeState {
|
||||
/** 能编辑、能重写注释的块树,动作什么都不做;view 盖在默认的状态上(什么都没折、没筛掉) */
|
||||
function tree(view: Partial<Omit<BlockTreeState, 'folds'>> = {}, rewrite: ChatState = { ...EMPTY_CHAT, available: true }): BlockTreeHandle {
|
||||
return {
|
||||
edit: {
|
||||
version: 1,
|
||||
editing: new Set(),
|
||||
focusId: null,
|
||||
onFocused: () => {},
|
||||
onToggleEdit: () => {},
|
||||
},
|
||||
rewrite: rewriteTree(rewrite),
|
||||
collapsed,
|
||||
hidden: new Set(),
|
||||
temporary: new Set(),
|
||||
covered: new Map(),
|
||||
kindName: kind => kind,
|
||||
state: staticStore(treeState({
|
||||
edit: { version: 1, editing: new Set(), focusId: null },
|
||||
collapsed: new Set(),
|
||||
hidden: new Set(),
|
||||
temporary: new Set(),
|
||||
covered: new Map(),
|
||||
...view,
|
||||
})),
|
||||
onToggleFold: () => {},
|
||||
onSetFolds: () => {},
|
||||
onOpenRun: () => {},
|
||||
edit: { onToggleEdit: () => {}, onFocused: () => {} },
|
||||
rewrite: rewriteTree(rewrite),
|
||||
}
|
||||
}
|
||||
|
||||
function markup(blocks: BlockView[], handle: BlockTreeHandle): string {
|
||||
return renderToStaticMarkup(<BlockTree blocks={blocks} tree={handle} />)
|
||||
}
|
||||
|
||||
function render(folds: Record<string, boolean>): string {
|
||||
return renderToStaticMarkup(<BlockLevel blocks={[CARD]} tree={state(collapsedIds([CARD], folds))} />)
|
||||
return markup([CARD], tree({ collapsed: collapsedIds([CARD], folds) }))
|
||||
}
|
||||
|
||||
describe('路由筛选后展开函数体', () => {
|
||||
@@ -118,11 +117,11 @@ describe('路由筛选后展开函数体', () => {
|
||||
const attributes = fileAttributes(blocks, pythonZhTranslator.attributeNames)
|
||||
const excluded = { kind: attributes.find(attribute => attribute.id === 'kind')!.values.map(value => value.id).filter(kind => kind !== 'fastapi.route') }
|
||||
const filtered = applyFilter(blocks, excluded, new Set())
|
||||
const tree = { ...state(collapsedIds(blocks, {})), ...filtered }
|
||||
const closed = renderToStaticMarkup(<BlockLevel blocks={blocks} tree={tree} />)
|
||||
const view = { collapsed: collapsedIds(blocks, {}), hidden: filtered.hidden, temporary: filtered.temporary }
|
||||
const closed = markup(blocks, tree(view))
|
||||
expect(closed).toContain('aria-label="展开子块"')
|
||||
expect(closed).not.toContain('enabled-body')
|
||||
const opened = renderToStaticMarkup(<BlockLevel blocks={blocks} tree={{ ...tree, collapsed: new Set() }} />)
|
||||
const opened = markup(blocks, tree({ ...view, collapsed: new Set() }))
|
||||
expect(opened).toContain('enabled-body')
|
||||
expect(opened).toContain('disabled-body')
|
||||
expect(opened).not.toContain('unrelated-body')
|
||||
@@ -182,14 +181,13 @@ describe('挂在卡片子行上的子块', () => {
|
||||
describe('卡片记着块记号', () => {
|
||||
it('每张卡片带着它那块的记号(data-anchor):说明里不带路径的引用靠它认自己写在哪块', () => {
|
||||
const method = view('0.1', 'function', '', { anchor: 'class AgentService > function get_default_agent', translation: { markdown: '先看 [get_agent_admitted]' } })
|
||||
const markup = renderToStaticMarkup(<BlockLevel blocks={[method]} tree={state(new Set())} />)
|
||||
expect(markup).toMatch(/<div class="cr-block [^"]*"[^>]*data-anchor="class AgentService > function get_default_agent"[\s\S]*href="symbol:get_agent_admitted"/)
|
||||
expect(markup([method], tree())).toMatch(/<div class="cr-block [^"]*"[^>]*data-anchor="class AgentService > function get_default_agent"[\s\S]*href="symbol:get_agent_admitted"/)
|
||||
})
|
||||
})
|
||||
|
||||
describe('「注释」一行和「重写注释」按钮', () => {
|
||||
const comment = { label: '注释', value: { markdown: '按名字找' } }
|
||||
const html = (block: BlockView): string => renderToStaticMarkup(<BlockLevel blocks={[block]} tree={state(new Set())} />)
|
||||
const html = (block: BlockView): string => markup([block], tree())
|
||||
/** 按钮只有一个,排在右上角那排按钮(块操作)的最前面 */
|
||||
const inToolbar = (markup: string, label: string): void => {
|
||||
expect(markup.match(new RegExp(`aria-label="${label}"`, 'g'))).toHaveLength(1)
|
||||
@@ -216,10 +214,10 @@ describe('「注释」一行和「重写注释」按钮', () => {
|
||||
const block = view('5', 'statement', 'save()', { commentTarget: { kind: 'replace', startLine: 0, endLine: 0 } })
|
||||
const entry = { id: 'r1', question: '/ecc:writing-code-comments a.py:1-1', answer: '保存。', createdAt: '2026-09-25T10:00:00.000Z' }
|
||||
const closed = { ...EMPTY_CHAT, available: true, threads: [{ anchor: block.anchor, entries: [entry] }] }
|
||||
const collapsedMarkup = renderToStaticMarkup(<BlockLevel blocks={[block]} tree={state(new Set(), closed)} />)
|
||||
const collapsedMarkup = markup([block], tree({}, closed))
|
||||
expect(collapsedMarkup.replace(/<[^>]*>/g, '')).toContain('重写过 1 次')
|
||||
inToolbar(collapsedMarkup, '展开重写记录')
|
||||
const opened = renderToStaticMarkup(<BlockLevel blocks={[block]} tree={state(new Set(), { ...closed, open: [block.anchor] })} />)
|
||||
const opened = markup([block], tree({}, { ...closed, open: [block.anchor] }))
|
||||
expect(opened).toMatch(/role="group" aria-label="块操作"><button[^>]*aria-label="按注释规范重写"/)
|
||||
expect(opened).toContain('<section class="cr-qa" aria-label="按注释规范重写">')
|
||||
expect(opened).toContain('aria-label="对这段注释的修改意见"')
|
||||
@@ -229,8 +227,7 @@ describe('「注释」一行和「重写注释」按钮', () => {
|
||||
const block = view('6', 'statement', 'save()', { commentTarget: { kind: 'replace', startLine: 0, endLine: 0 } })
|
||||
const button = (status: Ask['status']): string => {
|
||||
const rewrite = { ...EMPTY_CHAT, available: true, open: [block.anchor], asks: [{ askId: 'r1', anchor: block.anchor, question: 'q', text: '', status }] }
|
||||
const markup = renderToStaticMarkup(<BlockLevel blocks={[block]} tree={state(new Set(), rewrite)} />)
|
||||
return /<button[^>]*aria-label="按注释规范重写"[^>]*>/.exec(markup)?.[0] ?? ''
|
||||
return /<button[^>]*aria-label="按注释规范重写"[^>]*>/.exec(markup([block], tree({}, rewrite)))?.[0] ?? ''
|
||||
}
|
||||
expect(button('running')).toContain('disabled')
|
||||
expect(button('queued')).toContain('disabled')
|
||||
@@ -266,7 +263,7 @@ describe('一行译文下面拆开的各项', () => {
|
||||
/** 画成页面(只画这一块),collapsed 是折叠着的 id */
|
||||
function page(block: BlockView, collapsed: ReadonlySet<string> = new Set(), diff?: BlockTreeState['diff']): HTMLElement {
|
||||
const root = document.createElement('div')
|
||||
root.innerHTML = renderToStaticMarkup(<BlockLevel blocks={[block]} tree={{ ...state(collapsed), ...(diff && { diff }) }} />)
|
||||
root.innerHTML = markup([block], tree({ collapsed, ...(diff && { diff }) }))
|
||||
return root
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user