✨ feat(vscode): 注释卡片可按规范重写注释
This commit is contained in:
1 parent
724c81188c
commit
299acb979f
11 files changed
+717
-5
No files matched your search
@@ -12,15 +12,18 @@ import type { IndexedEntry } from '../workspaceIndex'
|
||||
import type { BlockFilter } from './blockFilter'
|
||||
import type { Definition, FoundLocation } from './definitions'
|
||||
import type { ExtensionToWebview, Occurrence, Reveal, WebviewToExtension } from './messages'
|
||||
import { dirname } from 'node:path'
|
||||
import { commands, Range, window, workspace, WorkspaceEdit } from 'vscode'
|
||||
import { logger } from '../../logging/logger'
|
||||
import { webviewHtml, webviewUiRoot } from '../../webview/html'
|
||||
import { DocumentCallees } from '../calleeKinds'
|
||||
import { commentRewritePrompt, commentSource, findPlaced, loadCommentSkill, modelCommentBody, refillComment } from '../commentRewrite'
|
||||
import { PreviewDiff } from '../diff/previewDiff'
|
||||
import { blockReplacement } from '../editText'
|
||||
import { locate } from '../folderStore'
|
||||
import { definitionFor, symbolAt, symbolOccurrences } from '../localSymbols'
|
||||
import { DocumentNames, resolveNames } from '../nameResolution'
|
||||
import { askClaude, askFailure } from '../qa/claudeAsk'
|
||||
import { askPrompt, followUpPrompt } from '../qa/prompt'
|
||||
import { DocumentLookups } from '../ruleLookups'
|
||||
import { definitionAction } from './definitionAction'
|
||||
@@ -137,6 +140,10 @@ class Preview {
|
||||
private readonly definitions: DefinitionSources<FoundLocation>
|
||||
/** webview 报来的视口顶部那块的起始行 */
|
||||
visibleLine: number | undefined
|
||||
/** 正在重写的注释(块的记号)。同一块没写完不再开一次 */
|
||||
private readonly rewriting = new Set<string>()
|
||||
/** 预览关掉时停掉还在跑的重写 */
|
||||
private readonly rewriteAbort = new AbortController()
|
||||
/** 最近一次发出去的文档(版本、块树、源码):问 AI 时按块的记号在里面找那块代码,对比基准变了时拿它重新比 */
|
||||
private sent: SentDocument | undefined
|
||||
/** 最近一次发出去的文档里语言插件自己认得出的名字(LanguageAdapter.symbols):语言扩展查不到定义、各处出现时用 */
|
||||
@@ -374,6 +381,7 @@ class Preview {
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.rewriteAbort.abort()
|
||||
clearTimeout(this.timer)
|
||||
this.names.dispose()
|
||||
this.lookups.dispose()
|
||||
@@ -453,6 +461,9 @@ class Preview {
|
||||
case 'edit':
|
||||
await this.applyEdit(msg.startLine, msg.endLine, msg.text)
|
||||
return
|
||||
case 'rewriteComment':
|
||||
await this.rewriteComment(msg.anchor)
|
||||
return
|
||||
case 'selectTranslator':
|
||||
await this.registry.selectTranslator(this.languageId, msg.id)
|
||||
this.owner.refresh(this.languageId)
|
||||
@@ -539,6 +550,111 @@ class Preview {
|
||||
await this.owner.openAt(uri, reveal, () => commands.executeCommand('vscode.openWith', uri, PREVIEW_VIEW_TYPE, options))
|
||||
}
|
||||
|
||||
/** 用写注释的技能重写这一段,只把正文套回原来的定界符,再走块编辑那条写回。 */
|
||||
private async rewriteComment(anchor: string): Promise<void> {
|
||||
if (this.rewriting.has(anchor) || this.rewriteAbort.signal.aborted)
|
||||
return
|
||||
this.rewriting.add(anchor)
|
||||
try {
|
||||
await this.rewriteCommentNow(anchor)
|
||||
}
|
||||
finally {
|
||||
this.rewriting.delete(anchor)
|
||||
this.post({ type: 'commentRewritten', anchor })
|
||||
}
|
||||
}
|
||||
|
||||
private failRewrite(message: string): void {
|
||||
void window.showErrorMessage(`代码阅读:${message}`)
|
||||
}
|
||||
|
||||
private async rewriteCommentNow(anchor: string): Promise<void> {
|
||||
const found = this.sent && findPlaced(this.sent.blocks, anchor)
|
||||
if (!found || found.block.kind !== 'comment') {
|
||||
this.failRewrite('这块注释刚改过,找不到了。等预览刷新后再重写。')
|
||||
return
|
||||
}
|
||||
const { block, siblings, index } = found
|
||||
const { document } = this
|
||||
if (block.startLine < 0 || block.endLine < block.startLine || block.endLine >= document.lineCount) {
|
||||
this.failRewrite('这块注释的位置已经变了,没有写回。')
|
||||
return
|
||||
}
|
||||
const raw = Array.from({ length: block.endLine - block.startLine + 1 }, (_, i) => document.lineAt(block.startLine + i).text)
|
||||
const snapshot = raw.join('\n')
|
||||
const comment = commentSource(raw)
|
||||
const source = document.getText()
|
||||
const next = siblings[index + 1]
|
||||
const following = next && next.startLine >= 0 && next.endLine >= next.startLine && next.endLine < document.lineCount
|
||||
? Array.from({ length: next.endLine - next.startLine + 1 }, (_, i) => document.lineAt(next.startLine + i).text).join('\n')
|
||||
: undefined
|
||||
let skill: string | undefined
|
||||
try {
|
||||
skill = await loadCommentSkill()
|
||||
}
|
||||
catch (err) {
|
||||
if (!this.rewriteAbort.signal.aborted)
|
||||
this.failRewrite(`读注释技能失败:${err instanceof Error ? err.message : String(err)}`)
|
||||
return
|
||||
}
|
||||
if (this.rewriteAbort.signal.aborted)
|
||||
return
|
||||
const prompt = commentRewritePrompt({
|
||||
skill,
|
||||
comment,
|
||||
source,
|
||||
startLine: block.startLine,
|
||||
endLine: block.endLine,
|
||||
earlierKinds: siblings.slice(0, index).map(item => item.kind),
|
||||
...(following !== undefined && { following }),
|
||||
})
|
||||
if (!prompt) {
|
||||
this.failRewrite(skill === undefined ? '找不到写注释的技能' : '这段注释的定界符认不出来,没有改文件。')
|
||||
return
|
||||
}
|
||||
const at = locate(document.uri)
|
||||
const profile = await this.owner.qa.profiles.current()
|
||||
let text: string
|
||||
try {
|
||||
const answer = await askClaude({
|
||||
prompt,
|
||||
images: [],
|
||||
...(profile && { profilePath: profile.path }),
|
||||
cwd: at?.folder.uri.fsPath ?? dirname(document.uri.fsPath),
|
||||
signal: this.rewriteAbort.signal,
|
||||
onEvent: () => {},
|
||||
})
|
||||
text = answer.text
|
||||
}
|
||||
catch (err) {
|
||||
if (!this.rewriteAbort.signal.aborted)
|
||||
this.failRewrite(askFailure(err))
|
||||
return
|
||||
}
|
||||
if (this.rewriteAbort.signal.aborted)
|
||||
return
|
||||
const body = modelCommentBody(text)
|
||||
if (!body) {
|
||||
this.failRewrite('模型没有给出注释正文,没有改文件。')
|
||||
return
|
||||
}
|
||||
if (block.endLine >= document.lineCount) {
|
||||
this.failRewrite('这段注释在重写期间变了,没有写回。')
|
||||
return
|
||||
}
|
||||
const current = Array.from({ length: block.endLine - block.startLine + 1 }, (_, i) => document.lineAt(block.startLine + i).text).join('\n')
|
||||
if (current !== snapshot) {
|
||||
this.failRewrite('这段注释在重写期间变了,没有写回。')
|
||||
return
|
||||
}
|
||||
const filled = refillComment(comment, body)
|
||||
if (!filled) {
|
||||
this.failRewrite('这段注释的定界符认不出来,没有改文件。')
|
||||
return
|
||||
}
|
||||
await this.applyEdit(block.startLine, block.endLine, filled)
|
||||
}
|
||||
|
||||
private async applyEdit(startLine: number, endLine: number, text: string): Promise<void> {
|
||||
const { document } = this
|
||||
const replacement = blockReplacement(line => document.lineAt(line).text, document.lineCount, startLine, endLine, text)
|
||||
|
||||
Reference in new issue
Block a user