Skip to content

Commit 1a0f213

Browse files
committed
加上校验
1 parent 4e2626f commit 1a0f213

File tree

4 files changed

+220
-54
lines changed

4 files changed

+220
-54
lines changed

prompt/savefile.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
tcvb���ؽ��յ�ʱ�������̣���׺tcvb��������merge֮���cvb�ļ�һ����
2+
analyze�Ľ��Ҳ���̣������md����������Ҳһ����
3+
ע�⣬�¼ӵ��������̶�Ҫ����sidebar����ʾ

prompt/testdata2.txt

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
## BEGIN_TCVB
2+
## FILE:k:\lab\CodeReDesign-dummy\src\siderBar.ts
3+
## OPERATION:INSERT
4+
## BEFORE_ANCHOR
5+
```typescript
6+
getTreeItem(element: CvbFile): vscode.TreeItem {
7+
return element;
8+
}
9+
```
10+
## AFTER_ANCHOR
11+
```typescript
12+
async getChildren(element?: CvbFile): Promise<CvbFile[]> {
13+
if (element) {
14+
```
15+
## INSERT_CONTENT
16+
```typescript
17+
// 新增文件类型过滤
18+
private isSupportedFileType(filename: string): boolean {
19+
return filename.endsWith('.cvb') || filename.endsWith('.tcvb') || filename.endsWith('.md');
20+
}
21+
```
22+
23+
## OPERATION:SINGLE-REPLACE
24+
## BEFORE_ANCHOR
25+
```typescript
26+
if (file.endsWith('.cvb')) {
27+
const filePath = path.join(targetFolder, file);
28+
cvbFiles.push(new CvbFile(file, vscode.Uri.file(filePath)));
29+
}
30+
```
31+
## AFTER_ANCHOR
32+
```typescript
33+
// 新增排序逻辑
34+
cvbFiles.sort((a, b) =>
35+
```
36+
## OLD_CONTENT
37+
```typescript
38+
if (file.endsWith('.cvb')) {
39+
const filePath = path.join(targetFolder, file);
40+
cvbFiles.push(new CvbFile(file, vscode.Uri.file(filePath)));
41+
}
42+
```
43+
## NEW_CONTENT
44+
```typescript
45+
if (this.isSupportedFileType(file)) {
46+
const filePath = path.join(targetFolder, file);
47+
cvbFiles.push(new CvbFile(file, vscode.Uri.file(filePath)));
48+
}
49+
```
50+
51+
## FILE:k:\lab\CodeReDesign-dummy\src\deepseekApi.ts
52+
## OPERATION:INSERT
53+
## BEFORE_ANCHOR
54+
```typescript
55+
fs.writeFileSync(newCvbFilePath, cvb.toString(), 'utf-8');
56+
vscode.window.showInformationMessage(`API response saved as CVB file: ${newCvbFilePath}`);
57+
```
58+
## AFTER_ANCHOR
59+
```typescript
60+
}
61+
clearCurrentOperationController();
62+
```
63+
## INSERT_CONTENT
64+
```typescript
65+
// 保存原始TCVB内容
66+
const tcvbFilePath = path.join(tmpDir, fileName.replace(/\.cvb$/, '.tcvb'));
67+
fs.writeFileSync(tcvbFilePath, apiResponse, 'utf-8');
68+
```
69+
70+
## OPERATION:INSERT
71+
## BEFORE_ANCHOR
72+
```typescript
73+
if (analysisResult) {
74+
vscode.window.showInformationMessage('Analysis completed. Check the output channel for details.');
75+
```
76+
## AFTER_ANCHOR
77+
```typescript
78+
}
79+
clearCurrentOperationController();
80+
```
81+
## INSERT_CONTENT
82+
```typescript
83+
// 保存分析结果到Markdown
84+
const mdFileName = path.basename(filePath).replace(/\.cvb$/, '_analysis.md');
85+
const mdFilePath = path.join(path.dirname(filePath), mdFileName);
86+
fs.writeFileSync(mdFilePath, `# Analysis Report\n\n${analysisResult}`, 'utf-8');
87+
```
88+
89+
## FILE:k:\lab\CodeReDesign-dummy\src\cvbManager.ts
90+
## OPERATION:INSERT
91+
## BEFORE_ANCHOR
92+
```typescript
93+
class CvbFile extends vscode.TreeItem {
94+
constructor(
95+
public readonly label: string,
96+
public readonly uri: vscode.Uri
97+
) {
98+
```
99+
## AFTER_ANCHOR
100+
```typescript
101+
// 设置图标(可选)
102+
this.iconPath = vscode.ThemeIcon.File;
103+
```
104+
## INSERT_CONTENT
105+
```typescript
106+
// 根据文件类型设置不同图标
107+
if (uri.fsPath.endsWith('.tcvb')) {
108+
this.iconPath = new vscode.ThemeIcon('diff');
109+
} else if (uri.fsPath.endsWith('.md')) {
110+
this.iconPath = new vscode.ThemeIcon('markdown');
111+
}
112+
```
113+
114+
## END_TCVB
115+
116+
这个TCVB实现了:
117+
1. 在侧边栏增加.tcvb和.md文件支持
118+
2. 上传TCVB时保存原始TCVB文件
119+
3. 分析结果保存为Markdown文件
120+
4. 不同文件类型显示不同图标
121+
5. 文件名保持与CVB文件一致(自动替换扩展名)
122+
123+
所有修改都保持原有功能完整,新增文件会在.CodReDesignWorkSpace目录生成,并能在侧边栏正确显示和操作。

src/cvbManager.ts

Lines changed: 91 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ TCVB 格式规范:
475475
## END_TCVB
476476
477477
文件块格式:
478-
## FILE:<文件路径>
478+
## FILE:<文件绝对路径>
479479
[操作1]
480480
[操作2]
481481
...
@@ -484,48 +484,49 @@ TCVB 格式规范:
484484
1. 单个替换操作(SINGLE-REPLACE):
485485
## OPERATION:SINGLE-REPLACE
486486
## BEFORE_ANCHOR
487-
[代码块:被替换行之前的锚点内容,用来划定范围,避免混淆, 注意这个不需要紧挨着被替换行]
487+
[markdown代码块:被替换行之前的锚点内容,用来划定范围,避免混淆, 注意这个不需要紧挨着被替换行]
488488
## AFTER_ANCHOR
489-
[代码块:被替换行之后的锚点内容,用来划定范围,避免混淆, 注意这个不需要紧挨着被替换行]
489+
[markdown代码块:被替换行之后的锚点内容,用来划定范围,避免混淆, 注意这个不需要紧挨着被替换行]
490490
## OLD_CONTENT
491-
[代码块:被替换内容]
491+
[markdown代码块:被替换内容]
492492
## NEW_CONTENT
493-
[代码块:新内容]
493+
[markdown代码块:新内容]
494494
495495
2. 全局替换操作(GLOBAL-REPLACE):
496496
## OPERATION:GLOBAL-REPLACE
497497
## OLD_CONTENT
498-
[代码块:被全局替换的内容]
498+
[markdown代码块:被全局替换的内容]
499499
## NEW_CONTENT
500-
[代码块:新内容]
500+
[markdown代码块:新内容]
501501
502502
3. 插入操作(INSERT):
503503
## OPERATION:INSERT
504504
## BEFORE_ANCHOR
505-
[代码块:插入位置前的锚点内容]
505+
[markdown码块:插入位置前的锚点内容,要紧挨着插入的行位置]
506506
## AFTER_ANCHOR
507-
[代码块:插入位置后的锚点内容]
507+
[markdown代码块:插入位置后的锚点内容, 要紧挨着插入的行位置,注意不要与插入的新内容相同,是原始内容插入位置之后的内容]
508508
## INSERT_CONTENT
509-
[代码块:插入内容]
509+
[markdown代码块:插入的新内容]
510510
511511
4. 删除操作(DELETE):
512512
## OPERATION:DELETE
513513
## BEFORE_ANCHOR
514-
[代码块:被删除行前的锚点内容]
514+
[markdown代码块:被删除行前的锚点内容]
515515
## AFTER_ANCHOR
516-
[代码块:被删除行后的锚点内容]
516+
[markdown代码块:被删除行后的锚点内容,注意不要与要删除的内容相同,是原始内容被删除代码块之后的内容]
517517
## DELETE_CONTENT
518-
[代码块:被删除行的内容]
518+
[markdown代码块:被删除行的内容]
519+
519520
520521
5. 创建操作(CREATE):
521522
## OPERATION:CREATE
522-
[代码块:直接跟正文内容,表示新文件的全部内容]
523+
[markdown代码块:直接跟正文内容,表示新文件的全部内容]
523524
524525
注意:
525526
1. 所有OPERATION操作以行为单位
526527
2. 一个'## FILE'下可以有多个'## OPERATION'
527528
3. 锚点为连续的多行内容:使用至少3行唯一文本作为锚点,用来标定范围,防止混淆(如果需要可以超过3行)
528-
4. 代码块,(规则中的[代码块:...]) 用 markdown 格式包裹
529+
4. [markdown代码块], 一定要用\`\`\` ... \`\`\` 包裹,仔细检查不要漏掉
529530
5. 注意TCVB和CVB的区别。CVB是完整的内容,而TCVB是用来生成差量同步的,通过多个OPERATION去操作已有CVB合成新CVB
530531
`;
531532
}
@@ -549,35 +550,40 @@ export function mergeCvb(baseCvb: Cvb, tcvb: TCVB) : Cvb
549550
mapOperationsByFile.get(op.m_strFilePath)!.push(op);
550551
}
551552

552-
// 对每个文件执行所有操作(按顺序执行)
553-
for (const [strFilePath, arrOperations] of mapOperationsByFile)
554-
{
555-
let strContent: string = mapMergedFiles.get(strFilePath) || '';
556-
for (const op of arrOperations)
557-
{
558-
if (op instanceof SingleReplaceOperation)
559-
{
560-
strContent = applySingleReplace(strContent, op);
561-
}
562-
else if (op instanceof GlobalReplaceOperation)
563-
{
564-
strContent = applyGlobalReplace(strContent, op);
565-
}
566-
else if (op instanceof InsertOperation)
553+
try {
554+
// 对每个文件执行所有操作(按顺序执行)
555+
for (const [strFilePath, arrOperations] of mapOperationsByFile)
567556
{
568-
strContent = applyInsert(strContent, op);
569-
}
570-
else if (op instanceof DeleteOperation)
571-
{
572-
strContent = applyDelete(strContent, op);
573-
}
574-
else if (op instanceof CreateOperation)
575-
{
576-
// CREATE 操作:直接以新内容覆盖原有内容
577-
strContent = op.m_strContent;
557+
let strContent: string = mapMergedFiles.get(strFilePath) || '';
558+
for (const op of arrOperations)
559+
{
560+
if (op instanceof SingleReplaceOperation)
561+
{
562+
strContent = applySingleReplace(strContent, op);
563+
}
564+
else if (op instanceof GlobalReplaceOperation)
565+
{
566+
strContent = applyGlobalReplace(strContent, op);
567+
}
568+
else if (op instanceof InsertOperation)
569+
{
570+
strContent = applyInsert(strContent, op);
571+
}
572+
else if (op instanceof DeleteOperation)
573+
{
574+
strContent = applyDelete(strContent, op);
575+
}
576+
else if (op instanceof CreateOperation)
577+
{
578+
// CREATE 操作:直接以新内容覆盖原有内容
579+
strContent = op.m_strContent;
580+
}
581+
}
582+
mapMergedFiles.set(strFilePath, strContent);
578583
}
579-
}
580-
mapMergedFiles.set(strFilePath, strContent);
584+
}
585+
catch (err: any) {
586+
throw new Error(`合并CVB失败: ${err.message}`);
581587
}
582588

583589
return rebuildCvb(baseCvb, mapMergedFiles);
@@ -588,6 +594,13 @@ function applySingleReplace(strContent: string, op: SingleReplaceOperation): str
588594
const regPattern = buildPattern(op.m_strBeforeAnchor, op.m_strOldContent, op.m_strAfterAnchor);
589595
// 替换为:前锚点 + 中间内容1 + 新内容 + 中间内容2 + 后锚点
590596
const strReplacement = op.m_strBeforeAnchor + '$1' + op.m_strNewContent + '$2' + op.m_strAfterAnchor;
597+
598+
regPattern.lastIndex = 0; // 重置正则表达式的状态
599+
if (!regPattern.test(strContent)) {
600+
throw new Error(`Single-replace操作失败:文件 "${op.m_strFilePath}" 中未找到匹配项。请检查前后锚点及旧内容是否正确。`);
601+
}
602+
regPattern.lastIndex = 0; // 再次重置以备替换
603+
591604
return strContent.replace(regPattern, strReplacement);
592605
}
593606

@@ -598,7 +611,14 @@ function applyGlobalReplace(strContent: string, op: GlobalReplaceOperation) : st
598611
throw new Error(`全局替换为空`);
599612
}
600613

601-
const regPattern: RegExp = new RegExp(escapeRegExp(op.m_strOldContent), 'gs');
614+
const regPattern: RegExp = new RegExp(normalizeLineWhitespace(escapeRegExp(op.m_strOldContent)), 'gs');
615+
616+
regPattern.lastIndex = 0;
617+
if (!regPattern.test(strContent)) {
618+
throw new Error(`全局替换失败:文件 "${op.m_strFilePath}" 中未找到旧内容 "${op.m_strOldContent}"。`);
619+
}
620+
regPattern.lastIndex = 0;
621+
602622
return strContent.replace(regPattern, op.m_strNewContent);
603623
}
604624

@@ -610,14 +630,21 @@ function applyInsert( strContent: string, op: InsertOperation ) : string
610630
}
611631

612632
// 将前后锚点转义后使用
613-
const strBeforeRegex: string = escapeRegExp( op.m_strBeforeAnchor );
614-
const strAfterRegex: string = escapeRegExp( op.m_strAfterAnchor );
633+
const strBeforeRegex: string = normalizeLineWhitespace(escapeRegExp( op.m_strBeforeAnchor ));
634+
const strAfterRegex: string = normalizeLineWhitespace(escapeRegExp( op.m_strAfterAnchor ));
615635
// 中间部分直接作为正则片段,不做转义
616-
const strMiddlePattern: string = "((?:[ \\t]*(?:\\r?\\n))*)";
636+
const strMiddlePattern: string = "[ \\t\\r\\n]*";
617637

618638
const regPattern: RegExp = new RegExp( strBeforeRegex + strMiddlePattern + strAfterRegex, 'gs' );
619639
const strReplacement: string = op.m_strBeforeAnchor + op.m_strInsertContent + op.m_strAfterAnchor;
620640

641+
console.log(regPattern);
642+
regPattern.lastIndex = 0;
643+
if (!regPattern.test(strContent)) {
644+
throw new Error(`插入失败:文件 "${op.m_strFilePath}" 插入前锚点:"${op.m_strBeforeAnchor}", 插入内容: "${op.m_strInsertContent}", 插入后锚点: "${op.m_strAfterAnchor}"`);
645+
}
646+
regPattern.lastIndex = 0;
647+
621648
return strContent.replace( regPattern, strReplacement );
622649
}
623650

@@ -636,17 +663,23 @@ function applyDelete( strContent: string, op: DeleteOperation ) : string
636663
// 使用捕获组 $1 和 $2 来保留匹配到的前后附加内容(不删除)
637664
const strReplacement: string = op.m_strBeforeAnchor + "$1" + "$2" + op.m_strAfterAnchor;
638665

666+
regPattern.lastIndex = 0;
667+
if (!regPattern.test(strContent)) {
668+
throw new Error(`删除失败:文件 "${op.m_strFilePath}" 需要删除的内容:"${op.m_strDeleteContent}"`);
669+
}
670+
regPattern.lastIndex = 0;
671+
639672
return strContent.replace( regPattern, strReplacement );
640673
}
641674

642675
// 根据前锚点、内容、后锚点构建正则表达式(dotall 模式)
643676
function buildPattern(strBefore: string, strContent: string, strAfter: string): RegExp {
644677
return new RegExp(
645-
escapeRegExp(strBefore) +
678+
normalizeLineWhitespace(escapeRegExp(strBefore)) +
646679
'([\\s\\S]*?)' + // 捕获前锚点与旧内容之间的任意字符(非贪婪)
647-
escapeRegExp(strContent) +
680+
normalizeLineWhitespace(escapeRegExp(strContent)) +
648681
'([\\s\\S]*?)' + // 捕获旧内容与后锚点之间的任意字符(非贪婪)
649-
escapeRegExp(strAfter),
682+
normalizeLineWhitespace(escapeRegExp(strAfter)),
650683
'gs' // 全局匹配且允许跨行
651684
);
652685
}
@@ -683,6 +716,15 @@ function escapeRegExp(str: string) : string
683716
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
684717
}
685718

719+
function normalizeLineWhitespace(anchor: string): string {
720+
return anchor.split('\n')
721+
.map(line => {
722+
// 对每一行的空白字符做更精确的处理
723+
return `\\s*${line.trim()}\\s*`; // 保留行首和行尾的空白字符处理
724+
})
725+
.join('\n'); // 行与行之间允许有空白字符(空格、换行符等)
726+
}
727+
686728
function filePathNormalize(strRawPath: string) : string
687729
{
688730
return path.normalize(strRawPath.replace(/^[\\/]+/, ''));

src/siderBar.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,7 @@ function applyThisCvb(filePath: string) {
160160
* @param filePath .cvb 文件的路径
161161
*/
162162
async function uploadThisCvb(filePath: string) {
163-
164-
/*
163+
/*
165164
// 测试 begin
166165
{
167166
const workspaceFolders = vscode.workspace.workspaceFolders;
@@ -170,7 +169,7 @@ async function uploadThisCvb(filePath: string) {
170169
return;
171170
}
172171
const workspacePath = workspaceFolders[0].uri.fsPath;
173-
const filepath = path.join(workspacePath, "/prompt/testdata.txt");
172+
const filepath = path.join(workspacePath, "/prompt/testdata2.txt");
174173
let tcvbContent = fs.readFileSync(filepath, 'utf-8');
175174
tcvbContent = tcvbContent.replace(/\r\n?/g, "\n");
176175
const tcvb = new TCVB(tcvbContent);
@@ -180,8 +179,7 @@ async function uploadThisCvb(filePath: string) {
180179
const cvb = mergeCvb(oldCvb, tcvb);
181180
}
182181
// 测试 end
183-
*/
184-
182+
*/
185183
const userPrompt = await vscode.window.showInputBox({
186184
prompt: 'Enter your prompt for the refactoring',
187185
placeHolder: 'e.g., Refactor the code to improve readability',

0 commit comments

Comments
 (0)