✨ feat(vscode): 代码阅读的注释挂到它讲的代码块上成「注释」一行,重写注释改用技能一句话加结构化结果

- 普通注释不再单独成卡片:同一层挂到后面最近的块(隔空行也算),后面没有挂到前面那块,整层没代码挂到父块;只有文件开头隔着空行的注释、模块 docstring、//!、@file 单独成「文件开头的说明」
- 各语言(Python、TypeScript、Rust、Shell、Dockerfile)建好树后统一调 attachComments;块的编辑范围连着挂上的注释,差异、跳转、钉住卡片都按它
- 宿主统一给每张卡片(语言卡片、翻译插件的属性表、库规则卡片、一行译文的语句卡片)插「注释」一行,排在名称、说明后面
- TypeScript 的 JSDoc 也能当常量、变量的说明;sqlx、React 不再单独处理前面的注释
- 「重写注释」每块都有:提示只发 /ecc:writing-code-comments 路径:起-止,模型只读、按 --json-schema 交 body,没交结构化正文就失败不写;扩展换掉块前面的注释或在块上面新写一段
This commit is contained in:
cruldra committed 2026-10-08 07:51:21 +08:00
1 parent 335cf9ca9f
commit 582ac4c987
49 files changed
+1730 -757

No files matched your search

+71 -2
View File
@@ -4,9 +4,12 @@ import { describe, expect, it } from 'vitest'
import { readBlockViews, readDefinitionKind, toBlockViews } from './blockView'
import { replaceLines } from './editText'
import { createBashAdapter } from './languages/bash'
import { createDockerfileAdapter } from './languages/dockerfile'
import { createPythonAdapter } from './languages/python'
import { createTypeScriptAdapter } from './languages/typescript'
import { previewText, propertyLines, readFixture, runtime, WASM_DIR, withBlocks } from './testSupport'
import { commentText, DOCKERFILE_WASM_DIR, previewText, propertyLines, readFixture, runtime, WASM_DIR, withBlocks } from './testSupport'
import { bashZhTranslator } from './translators/bashZh'
import { dockerfileZhTranslator } from './translators/dockerfileZh'
import { pythonZhTranslator } from './translators/pythonZh'
import { typescriptZhTranslator } from './translators/typescriptZh'
@@ -159,7 +162,8 @@ describe('blockView', () => {
' 管理员可查看任意用户,普通用户只能查看自己。',
' """',
])
expect(getUser?.children[0]).toMatchObject({ kind: 'comment', startLine: 38, endLine: 38 })
expect(getUser?.children[0]).toMatchObject({ kind: 'if', startLine: 38, endLine: 40, headerEndLine: 39 })
expect(commentText(getUser?.children[0])).toBe('非管理员只能查看自己')
})
it('类的说明同样算在类块的头部里:JSDoc 连着中间的装饰器,docstring 连着它前面的注释', async () => {
@@ -385,3 +389,68 @@ describe('blockView', () => {
expect(await kind('pass')).toBeUndefined()
})
})
describe('卡片上的「注释」一行', () => {
it('luffy 的 acquisition/repository.py:讲类的注释是类卡片的「注释」一行(排在名称后面),类的编辑范围、源码连着这几行', async () => {
const source = readFixture('acquisitionRepository.py')
const list = await views(py, 'python', pythonZhTranslator, source)
expect(list.some(view => view.kind === 'comment')).toBe(false)
const repo = list.find(view => view.kind === 'class')
expect(repo).toMatchObject({ startLine: 12, headerEndLine: 20, commentRow: 1, commentTarget: { kind: 'replace', startLine: 12, endLine: 15 } })
expect('properties' in repo! && repo.properties.map(row => row.label).slice(0, 3)).toEqual(['名称', '注释', '访问级别'])
expect(commentText(repo)).toBe([
'luffy_agent.user.models.User 在各方法体内按需局部导入 (非模块顶层):',
'user 包 __init__ 会级联加载到 acquisition.service, 而 acquisition 是整个',
'应用最早被 import 的域之一 (见 api_routers.py 的 include 顺序), 顶层导入',
'会与该级联互相等待, 构成循环导入。',
].join('\n'))
expect(repo?.code.split('\n').slice(0, 7)).toEqual([
'# luffy_agent.user.models.User 在各方法体内按需局部导入 (非模块顶层):',
'# user 包 __init__ 会级联加载到 acquisition.service, 而 acquisition 是整个',
'# 应用最早被 import 的域之一 (见 api_routers.py 的 include 顺序), 顶层导入',
'# 会与该级联互相等待, 构成循环导入。',
'',
'',
'class AcquisitionChannelRepository(',
])
expect(replaceLines(source, repo!.startLine, repo!.endLine, repo!.code)).toBe(source)
})
it('说明和注释都有时排成 名称、说明、注释;几段注释是几段 Markdown;一行译文的卡片「注释」一行排在译文下面', async () => {
const source = [
'import { readFile } from \'node:fs/promises\'',
'',
'// 讲 load',
'',
'// 第二段',
'/** 读出配置。 */',
'export async function load(path: string) {',
' // 先检查',
' if (!path) {',
' return null',
' }',
' return readFile(path)',
'}',
].join('\n')
const [, load] = await views(ts, 'typescript', typescriptZhTranslator, source)
expect('properties' in load && load.properties.map(row => row.label).slice(0, 4)).toEqual(['名称', '说明', '注释', '访问级别'])
expect(commentText(load)).toBe('讲 load\n\n第二段')
expect(load).toMatchObject({ startLine: 2, commentRow: 2, commentTarget: { kind: 'replace', startLine: 2, endLine: 4 } })
const [check, returned] = load.children
expect(check).toMatchObject({ kind: 'if', startLine: 7, commentRow: 0, rows: [{ label: '注释', value: { markdown: '先检查' } }] })
expect(returned).toMatchObject({ kind: 'return', commentTarget: { kind: 'insert', line: 11 } })
expect(returned).not.toHaveProperty('commentRow')
})
it('嵌着的代码里的块不能写注释:没有 commentTarget', async () => {
const shell = createBashAdapter(WASM_DIR)
const source = 'FROM node:22\nRUN npm ci && npm run build\n'
const { blocks } = await readBlockViews(runtime, createDockerfileAdapter(DOCKERFILE_WASM_DIR), dockerfileZhTranslator, 'dockerfile', source, {
embedded: languageId => languageId === 'shellscript' ? { adapter: shell, translator: bashZhTranslator } : undefined,
})
const run = flatten(blocks).find(view => view.kind === 'run')
expect(run?.commentTarget).toEqual({ kind: 'insert', line: 1 })
expect(run?.children.length).toBeGreaterThan(0)
expect(run?.children.every(step => step.commentTarget === undefined)).toBe(true)
})
})