⚡ perf(vscode): 代码阅读预览发给页面的块只记相对位置:块在源码里占的地方记成离前一块多远、自己多长,块里名字的下标从块的开头算,页面从文件开头累加回文件里的位置、和扩展端打交道的地方都在一处换算;靠前改了代码,后面内容没变的块发过去一模一样,扩展端自己的块和发给页面的块分成两种类型
This commit is contained in:
1 parent
3a2815c19c
commit
ad5f559e92
43 files changed
+980
-424
No files matched your search
@@ -7,6 +7,7 @@ import { renderToStaticMarkup } from 'react-dom/server'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { fileAttributes } from '../../../src/codeReader/attributes'
|
||||
import { readBlockViews, toBlockViews } from '../../../src/codeReader/blockView'
|
||||
import { pageBlocks } from '../../../src/codeReader/editor/pageBlocks'
|
||||
import { createPythonAdapter } from '../../../src/codeReader/languages/python'
|
||||
import { fastapiRule } from '../../../src/codeReader/rules/fastapi'
|
||||
import { runtime, WASM_DIR, withBlocks } from '../../../src/codeReader/testSupport'
|
||||
@@ -14,8 +15,10 @@ import { pythonZhTranslator } from '../../../src/codeReader/translators/pythonZh
|
||||
import { BlockTree, treeState } from './BlockTree'
|
||||
import { applyFilter } from './filter'
|
||||
import { FilterBar } from './FilterBar'
|
||||
import { NO_PLACEMENT, placementOf } from './placement'
|
||||
import { EMPTY_CHAT } from './qa'
|
||||
import { staticStore } from './store'
|
||||
import { NO_SPAN } from './testBlocks'
|
||||
import { collapsedIds } from './viewState'
|
||||
|
||||
// 编辑框连着和 VS Code 收发消息的那一套,渲染成 HTML 用不着
|
||||
@@ -29,8 +32,7 @@ function view(id: string, kind: string, text: string, extra: Partial<BlockView>
|
||||
id,
|
||||
anchor: id,
|
||||
kind,
|
||||
startLine: 0,
|
||||
endLine: 0,
|
||||
span: NO_SPAN,
|
||||
attributes: { kind },
|
||||
concepts: [{ id: kind, label: badge?.label ?? kind, icon: badge?.icon ?? 'box', ...(badge && { color: badge.color }) }],
|
||||
children: [],
|
||||
@@ -66,7 +68,7 @@ function rewriteTree(state: ChatState): RewriteTree {
|
||||
function tree(view: Partial<Omit<BlockTreeState, 'folds'>> = {}, rewrite: ChatState = { ...EMPTY_CHAT, available: true }): BlockTreeHandle {
|
||||
return {
|
||||
state: staticStore(treeState({
|
||||
edit: { version: 1, sourceText: { source: '', colors: { roles: [], runs: [] } }, editing: new Set(), focusId: null },
|
||||
edit: { version: 1, sourceText: { source: '', colors: { roles: [], runs: [] } }, placement: NO_PLACEMENT, editing: new Set(), focusId: null },
|
||||
collapsed: new Set(),
|
||||
hidden: new Set(),
|
||||
temporary: new Set(),
|
||||
@@ -103,16 +105,17 @@ describe('路由筛选后展开函数体', () => {
|
||||
' return "unrelated-body"',
|
||||
].join('\n')
|
||||
const language = createPythonAdapter(WASM_DIR)
|
||||
const blocks = await withBlocks(language, 'python', source, parsed => toBlockViews(
|
||||
const views = await withBlocks(language, 'python', source, parsed => toBlockViews(
|
||||
parsed,
|
||||
{ source, language, markers: [] },
|
||||
pythonZhTranslator,
|
||||
{ rules: [fastapiRule], report: (message) => { throw new Error(message) } },
|
||||
))
|
||||
const blocks = pageBlocks(views, source)
|
||||
const route = blocks.find(block => block.kind === 'fastapi.route')!
|
||||
expect(route.children.map(block => block.kind)).toEqual(['if', 'return'])
|
||||
expect(route.children[0].children.map(block => block.kind)).toEqual(['return'])
|
||||
const attributes = fileAttributes(blocks, pythonZhTranslator.attributeNames)
|
||||
const attributes = fileAttributes(views, 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 view = { collapsed: collapsedIds(blocks, {}), hidden: filtered.hidden, temporary: filtered.temporary }
|
||||
@@ -195,7 +198,7 @@ describe('「注释」一行和「重写注释」按钮', () => {
|
||||
it('属性表卡片:「注释」一行照属性表画,按钮在右上角那排,不在这一行上', () => {
|
||||
const card = view('1', 'function', '', {
|
||||
properties: [{ label: '名称', value: [{ text: 'find', role: 'function' }] }, comment, { label: '异步', value: [{ text: '否', role: 'storage' }] }],
|
||||
commentTarget: { kind: 'replace', startLine: 0, endLine: 0 },
|
||||
commentTarget: 'replace',
|
||||
})
|
||||
const markup = html(card)
|
||||
inToolbar(markup, '按注释规范重写')
|
||||
@@ -203,13 +206,13 @@ describe('「注释」一行和「重写注释」按钮', () => {
|
||||
})
|
||||
|
||||
it('一行译文的卡片:「注释」一行排在译文下面,按钮同样在右上角那排', () => {
|
||||
const markup = html(view('2', 'statement', 'save()', { rows: [comment], commentTarget: { kind: 'replace', startLine: 0, endLine: 0 } }))
|
||||
const markup = html(view('2', 'statement', 'save()', { rows: [comment], commentTarget: 'replace' }))
|
||||
expect(markup.indexOf('save()')).toBeLessThan(markup.indexOf('按名字找'))
|
||||
inToolbar(markup, '按注释规范重写')
|
||||
})
|
||||
|
||||
it('重写过的块:下面一行「重写过 N 次」带灰字历史;记录收着时按钮先展开它,展开着时再点才重写', () => {
|
||||
const block = view('5', 'statement', 'save()', { commentTarget: { kind: 'replace', startLine: 0, endLine: 0 } })
|
||||
const block = view('5', 'statement', 'save()', { commentTarget: 'replace' })
|
||||
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 = markup([block], tree({}, closed))
|
||||
@@ -222,7 +225,7 @@ describe('「注释」一行和「重写注释」按钮', () => {
|
||||
})
|
||||
|
||||
it('展开着又在写或排着队时按钮不能点;出错、停下的不挡', () => {
|
||||
const block = view('6', 'statement', 'save()', { commentTarget: { kind: 'replace', startLine: 0, endLine: 0 } })
|
||||
const block = view('6', 'statement', 'save()', { commentTarget: 'replace' })
|
||||
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 }] }
|
||||
return /<button[^>]*aria-label="按注释规范重写"[^>]*>/.exec(markup([block], tree({}, rewrite)))?.[0] ?? ''
|
||||
@@ -233,7 +236,7 @@ describe('「注释」一行和「重写注释」按钮', () => {
|
||||
})
|
||||
|
||||
it('还没有注释的块:按钮同样在右上角那排,叫「按注释规范写注释」;不能写注释的块没有按钮', () => {
|
||||
inToolbar(html(view('3', 'statement', 'save()', { commentTarget: { kind: 'insert', line: 0 } })), '按注释规范写注释')
|
||||
inToolbar(html(view('3', 'statement', 'save()', { commentTarget: 'insert' })), '按注释规范写注释')
|
||||
expect(html(view('4', 'statement', 'save()'))).not.toContain('按注释规范')
|
||||
})
|
||||
})
|
||||
@@ -243,7 +246,7 @@ describe('一行译文下面拆开的各项', () => {
|
||||
async function returned(source: string): Promise<BlockView> {
|
||||
const language = createPythonAdapter(WASM_DIR)
|
||||
const views = await withBlocks(language, 'python', source, blocks => toBlockViews(blocks, { source, language, markers: [], calleeKind: call => call.callee.text === 'Filter' ? 'class' : undefined }, pythonZhTranslator))
|
||||
return views[0].children[0]
|
||||
return pageBlocks(views, source)[0].children[0]
|
||||
}
|
||||
|
||||
const SOURCE = [
|
||||
@@ -323,9 +326,10 @@ describe('一行译文下面拆开的各项', () => {
|
||||
describe('编辑框', () => {
|
||||
it('编辑着的块:编辑框里是文档源码按它的编辑范围取的整行、去掉共同缩进,着色取自整份源码的着色', async () => {
|
||||
const source = 'def f(x):\n if x:\n return "a"\n'
|
||||
const { blocks, colors } = await readBlockViews(runtime, createPythonAdapter(WASM_DIR), pythonZhTranslator, 'python', source)
|
||||
const { blocks: views, colors } = await readBlockViews(runtime, createPythonAdapter(WASM_DIR), pythonZhTranslator, 'python', source)
|
||||
const blocks = pageBlocks(views, source)
|
||||
const html = markup(blocks, tree({
|
||||
edit: { version: 1, sourceText: { source, colors }, editing: new Set(['0.0']), focusId: null },
|
||||
edit: { version: 1, sourceText: { source, colors }, placement: placementOf(blocks), editing: new Set(['0.0']), focusId: null },
|
||||
collapsed: new Set(),
|
||||
}))
|
||||
expect(html).toContain('>if x:\n return "a"</textarea>')
|
||||
|
||||
Reference in new issue
Block a user