Skip to content

Commit cd3c0c8

Browse files
committed
fix:修正文件路径错误,简化TCVB格式
1 parent 1a0f213 commit cd3c0c8

File tree

1 file changed

+32
-176
lines changed

1 file changed

+32
-176
lines changed

src/cvbManager.ts

Lines changed: 32 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ export class Cvb
114114
let arrFileMatch: RegExpExecArray | null;
115115
while ((arrFileMatch = regFile.exec(strCvbContentPart)) !== null)
116116
{
117-
const strFilePath: string = arrFileMatch[1];
117+
const strFilePath: string = filePathNormalize(arrFileMatch[1]);
118118
let strFileContent: string = arrFileMatch[2].trim();
119119
// 去除代码块标记
120120
const regCodeBlock: RegExp = /^```.*\n([\s\S]*?)\n```$/m;
@@ -152,14 +152,14 @@ abstract class TcvbOperation
152152
{
153153
constructor(
154154
public readonly m_strFilePath: string,
155-
public readonly m_strType: 'single-replace' | 'global-replace' | 'insert' | 'delete' | 'create'
155+
public readonly m_strType: 'exact-replace' | 'global-replace' | 'create'
156156
)
157157
{
158158
}
159159
}
160160

161-
// 1. 单个替换操作(SINGLE-REPLACE)
162-
class SingleReplaceOperation extends TcvbOperation
161+
// 1. 单个替换操作(EXACT-REPLACE)
162+
class ExactReplaceOperation extends TcvbOperation
163163
{
164164
public m_strBeforeAnchor: string;
165165
public m_strAfterAnchor: string;
@@ -174,7 +174,7 @@ class SingleReplaceOperation extends TcvbOperation
174174
m_strNewContent: string
175175
)
176176
{
177-
super(m_strFilePath, 'single-replace');
177+
super(m_strFilePath, 'exact-replace');
178178
this.m_strBeforeAnchor = m_strBeforeAnchor;
179179
this.m_strAfterAnchor = m_strAfterAnchor;
180180
this.m_strOldContent = m_strOldContent;
@@ -200,49 +200,7 @@ class GlobalReplaceOperation extends TcvbOperation
200200
}
201201
}
202202

203-
// 3. 插入操作(INSERT)
204-
class InsertOperation extends TcvbOperation
205-
{
206-
public m_strBeforeAnchor: string;
207-
public m_strAfterAnchor: string;
208-
public m_strInsertContent: string;
209-
210-
constructor(
211-
m_strFilePath: string,
212-
m_strBeforeAnchor: string,
213-
m_strAfterAnchor: string,
214-
m_strInsertContent: string
215-
)
216-
{
217-
super(m_strFilePath, 'insert');
218-
this.m_strBeforeAnchor = m_strBeforeAnchor;
219-
this.m_strAfterAnchor = m_strAfterAnchor;
220-
this.m_strInsertContent = m_strInsertContent;
221-
}
222-
}
223-
224-
// 4. 删除操作(DELETE)
225-
class DeleteOperation extends TcvbOperation
226-
{
227-
public m_strBeforeAnchor: string;
228-
public m_strAfterAnchor: string;
229-
public m_strDeleteContent: string;
230-
231-
constructor(
232-
m_strFilePath: string,
233-
m_strBeforeAnchor: string,
234-
m_strAfterAnchor: string,
235-
m_strDeleteContent: string
236-
)
237-
{
238-
super(m_strFilePath, 'delete');
239-
this.m_strBeforeAnchor = m_strBeforeAnchor;
240-
this.m_strAfterAnchor = m_strAfterAnchor;
241-
this.m_strDeleteContent = m_strDeleteContent;
242-
}
243-
}
244-
245-
// 5. 创建操作(CREATE)——新写文件,后面直接跟正文内容即可
203+
// 3. 创建操作(CREATE)——新写文件,后面直接跟正文内容即可
246204
class CreateOperation extends TcvbOperation
247205
{
248206
public m_strContent: string;
@@ -275,7 +233,7 @@ export class TCVB
275233
{
276234
const strFilePath: string = filePathNormalize(arrFileMatch[1]);
277235
const strOperationsBlock: string = arrFileMatch[2];
278-
// 支持操作类型中含有 "-" 符号(如 single-replace 等)
236+
// 支持操作类型中含有 "-" 符号(如 exact-replace 等)
279237
const regOperation: RegExp = /^## OPERATION:([\w-]+)\n([\s\S]*?)(?=^## OPERATION:|(?![\s\S]))/gm;
280238
let arrOpMatch: RegExpExecArray | null;
281239
while ((arrOpMatch = regOperation.exec(strOperationsBlock)) !== null)
@@ -293,18 +251,12 @@ export class TCVB
293251
{
294252
switch (strType)
295253
{
296-
case 'single-replace':
297-
this.parseSingleReplace(strFilePath, strContent);
298-
break;
299254
case 'global-replace':
300255
this.parseGlobalReplace(strFilePath, strContent);
301256
break;
302-
case 'insert':
303-
this.parseInsert(strFilePath, strContent);
304-
break;
305-
case 'delete':
306-
this.parseDelete(strFilePath, strContent);
307-
break;
257+
case 'exact-replace':
258+
this.parseExactReplace(strFilePath, strContent);
259+
break;
308260
case 'create':
309261
this.parseCreate(strFilePath, strContent);
310262
break;
@@ -318,11 +270,11 @@ export class TCVB
318270
}
319271
}
320272

321-
// SINGLE-REPLACE 操作解析:要求 BEFORE_ANCHOR、AFTER_ANCHOR、OLD_CONTENT、NEW_CONTENT 四个段落
322-
private parseSingleReplace(strFilePath: string, strContent: string) : void
273+
// Exact-REPLACE 操作解析:要求 BEFORE_ANCHOR、AFTER_ANCHOR、OLD_CONTENT、NEW_CONTENT 四个段落
274+
private parseExactReplace(strFilePath: string, strContent: string) : void
323275
{
324276
const recSections = this.parseSections(strContent, ['BEFORE_ANCHOR', 'AFTER_ANCHOR', 'OLD_CONTENT', 'NEW_CONTENT']);
325-
this.m_arrOperations.push(new SingleReplaceOperation(
277+
this.m_arrOperations.push(new ExactReplaceOperation(
326278
strFilePath,
327279
recSections['BEFORE_ANCHOR'],
328280
recSections['AFTER_ANCHOR'],
@@ -342,30 +294,6 @@ export class TCVB
342294
));
343295
}
344296

345-
// INSERT 操作解析:要求 BEFORE_ANCHOR、AFTER_ANCHOR、INSERT_CONTENT 三个段落
346-
private parseInsert(strFilePath: string, strContent: string) : void
347-
{
348-
const recSections = this.parseSections(strContent, ['BEFORE_ANCHOR', 'AFTER_ANCHOR', 'INSERT_CONTENT']);
349-
this.m_arrOperations.push(new InsertOperation(
350-
strFilePath,
351-
recSections['BEFORE_ANCHOR'],
352-
recSections['AFTER_ANCHOR'],
353-
recSections['INSERT_CONTENT']
354-
));
355-
}
356-
357-
// DELETE 操作解析:要求 BEFORE_ANCHOR、AFTER_ANCHOR、DELETE_CONTENT 三个段落
358-
private parseDelete(strFilePath: string, strContent: string) : void
359-
{
360-
const recSections = this.parseSections(strContent, ['BEFORE_ANCHOR', 'AFTER_ANCHOR', 'DELETE_CONTENT']);
361-
this.m_arrOperations.push(new DeleteOperation(
362-
strFilePath,
363-
recSections['BEFORE_ANCHOR'],
364-
recSections['AFTER_ANCHOR'],
365-
recSections['DELETE_CONTENT']
366-
));
367-
}
368-
369297
// CREATE 操作解析:直接将正文内容作为新文件内容,可选地去除 Markdown 代码块
370298
private parseCreate(strFilePath: string, strContent: string) : void
371299
{
@@ -481,44 +409,26 @@ TCVB 格式规范:
481409
...
482410
483411
操作类型:
484-
1. 单个替换操作(SINGLE-REPLACE):
485-
## OPERATION:SINGLE-REPLACE
486-
## BEFORE_ANCHOR
487-
[markdown代码块:被替换行之前的锚点内容,用来划定范围,避免混淆, 注意这个不需要紧挨着被替换行]
488-
## AFTER_ANCHOR
489-
[markdown代码块:被替换行之后的锚点内容,用来划定范围,避免混淆, 注意这个不需要紧挨着被替换行]
490-
## OLD_CONTENT
491-
[markdown代码块:被替换内容]
492-
## NEW_CONTENT
493-
[markdown代码块:新内容]
494412
495-
2. 全局替换操作GLOBAL-REPLACE:
413+
1. 全局替换操作(GLOBAL-REPLACE):
496414
## OPERATION:GLOBAL-REPLACE
497415
## OLD_CONTENT
498416
[markdown代码块:被全局替换的内容]
499417
## NEW_CONTENT
500418
[markdown代码块:新内容]
501419
502-
3. 插入操作(INSERT):
503-
## OPERATION:INSERT
504-
## BEFORE_ANCHOR
505-
[markdown码块:插入位置前的锚点内容,要紧挨着插入的行位置]
506-
## AFTER_ANCHOR
507-
[markdown代码块:插入位置后的锚点内容, 要紧挨着插入的行位置,注意不要与插入的新内容相同,是原始内容插入位置之后的内容]
508-
## INSERT_CONTENT
509-
[markdown代码块:插入的新内容]
510-
511-
4. 删除操作(DELETE):
512-
## OPERATION:DELETE
420+
2. 精确替换操作(EXACT-REPLACE),用于替换全局替换无法精准定位的情况:
421+
## OPERATION:EXACT-REPLACE
422+
## OLD_CONTENT
423+
[markdown代码块:被替换内容]
424+
## NEW_CONTENT
425+
[markdown代码块:新内容]
513426
## BEFORE_ANCHOR
514-
[markdown代码块:被删除行前的锚点内容]
427+
[markdown代码块:OLD_CONTENT之前的几行内容, 用来划定范围上半段锚点,避免有多个类似匹配, 注意这个不需要紧挨着被替换行。 不能和OLD_CONTENT重合]
515428
## AFTER_ANCHOR
516-
[markdown代码块:被删除行后的锚点内容,注意不要与要删除的内容相同,是原始内容被删除代码块之后的内容]
517-
## DELETE_CONTENT
518-
[markdown代码块:被删除行的内容]
429+
[markdown代码块:OLD_CONTENT之前的几行内容, 用来划定范围下半段锚点,避免有多个类似匹配, 注意这个不需要紧挨着被替换行。 不能和OLD_CONTENT重合]
519430
520-
521-
5. 创建操作(CREATE):
431+
3. 创建操作(CREATE):
522432
## OPERATION:CREATE
523433
[markdown代码块:直接跟正文内容,表示新文件的全部内容]
524434
@@ -528,6 +438,8 @@ TCVB 格式规范:
528438
3. 锚点为连续的多行内容:使用至少3行唯一文本作为锚点,用来标定范围,防止混淆(如果需要可以超过3行)
529439
4. [markdown代码块], 一定要用\`\`\` ... \`\`\` 包裹,仔细检查不要漏掉
530440
5. 注意TCVB和CVB的区别。CVB是完整的内容,而TCVB是用来生成差量同步的,通过多个OPERATION去操作已有CVB合成新CVB
441+
6. 插入和删除操作都可以转化为替换操作
442+
7. 用来匹配的锚点必须和原始传入的数据一致,不能有缺失,比如不能丢弃注释。
531443
`;
532444
}
533445
}
@@ -557,22 +469,14 @@ export function mergeCvb(baseCvb: Cvb, tcvb: TCVB) : Cvb
557469
let strContent: string = mapMergedFiles.get(strFilePath) || '';
558470
for (const op of arrOperations)
559471
{
560-
if (op instanceof SingleReplaceOperation)
472+
if (op instanceof ExactReplaceOperation)
561473
{
562-
strContent = applySingleReplace(strContent, op);
474+
strContent = applyExactReplace(strContent, op);
563475
}
564476
else if (op instanceof GlobalReplaceOperation)
565477
{
566478
strContent = applyGlobalReplace(strContent, op);
567479
}
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-
}
576480
else if (op instanceof CreateOperation)
577481
{
578482
// CREATE 操作:直接以新内容覆盖原有内容
@@ -589,15 +493,16 @@ export function mergeCvb(baseCvb: Cvb, tcvb: TCVB) : Cvb
589493
return rebuildCvb(baseCvb, mapMergedFiles);
590494
}
591495

592-
function applySingleReplace(strContent: string, op: SingleReplaceOperation): string {
496+
function applyExactReplace(strContent: string, op: ExactReplaceOperation): string {
593497
// 构建匹配模式:前锚点 + 中间内容1 + 旧内容 + 中间内容2 + 后锚点
594498
const regPattern = buildPattern(op.m_strBeforeAnchor, op.m_strOldContent, op.m_strAfterAnchor);
595499
// 替换为:前锚点 + 中间内容1 + 新内容 + 中间内容2 + 后锚点
596500
const strReplacement = op.m_strBeforeAnchor + '$1' + op.m_strNewContent + '$2' + op.m_strAfterAnchor;
597501

598502
regPattern.lastIndex = 0; // 重置正则表达式的状态
599503
if (!regPattern.test(strContent)) {
600-
throw new Error(`Single-replace操作失败:文件 "${op.m_strFilePath}" 中未找到匹配项。请检查前后锚点及旧内容是否正确。`);
504+
console.log("以下表达式:\n" + regPattern + "\n 为何无法匹配到原文:\n" + strContent);
505+
throw new Error(`Exact-replace操作失败:文件 "${op.m_strFilePath}" 中未找到匹配项。请检查前后锚点及旧内容是否正确。`);
601506
}
602507
regPattern.lastIndex = 0; // 再次重置以备替换
603508

@@ -615,63 +520,14 @@ function applyGlobalReplace(strContent: string, op: GlobalReplaceOperation) : st
615520

616521
regPattern.lastIndex = 0;
617522
if (!regPattern.test(strContent)) {
523+
console.log("以下表达式:\n" + regPattern + "\n 为何无法匹配到原文:\n" + strContent);
618524
throw new Error(`全局替换失败:文件 "${op.m_strFilePath}" 中未找到旧内容 "${op.m_strOldContent}"。`);
619525
}
620526
regPattern.lastIndex = 0;
621527

622528
return strContent.replace(regPattern, op.m_strNewContent);
623529
}
624530

625-
function applyInsert( strContent: string, op: InsertOperation ) : string
626-
{
627-
if ( op.m_strBeforeAnchor === "" || op.m_strAfterAnchor === "" )
628-
{
629-
throw new Error( "插入操作的前锚点或后锚点为空" );
630-
}
631-
632-
// 将前后锚点转义后使用
633-
const strBeforeRegex: string = normalizeLineWhitespace(escapeRegExp( op.m_strBeforeAnchor ));
634-
const strAfterRegex: string = normalizeLineWhitespace(escapeRegExp( op.m_strAfterAnchor ));
635-
// 中间部分直接作为正则片段,不做转义
636-
const strMiddlePattern: string = "[ \\t\\r\\n]*";
637-
638-
const regPattern: RegExp = new RegExp( strBeforeRegex + strMiddlePattern + strAfterRegex, 'gs' );
639-
const strReplacement: string = op.m_strBeforeAnchor + op.m_strInsertContent + op.m_strAfterAnchor;
640-
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-
648-
return strContent.replace( regPattern, strReplacement );
649-
}
650-
651-
function applyDelete( strContent: string, op: DeleteOperation ) : string
652-
{
653-
if ( op.m_strBeforeAnchor === "" || op.m_strAfterAnchor === "" )
654-
{
655-
throw new Error( "删除操作的前锚点或后锚点为空" );
656-
}
657-
if ( op.m_strDeleteContent === "" )
658-
{
659-
throw new Error( "删除操作的内容为空" );
660-
}
661-
662-
const regPattern: RegExp = buildPattern( op.m_strBeforeAnchor, op.m_strDeleteContent, op.m_strAfterAnchor );
663-
// 使用捕获组 $1 和 $2 来保留匹配到的前后附加内容(不删除)
664-
const strReplacement: string = op.m_strBeforeAnchor + "$1" + "$2" + op.m_strAfterAnchor;
665-
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-
672-
return strContent.replace( regPattern, strReplacement );
673-
}
674-
675531
// 根据前锚点、内容、后锚点构建正则表达式(dotall 模式)
676532
function buildPattern(strBefore: string, strContent: string, strAfter: string): RegExp {
677533
return new RegExp(
@@ -727,7 +583,7 @@ function normalizeLineWhitespace(anchor: string): string {
727583

728584
function filePathNormalize(strRawPath: string) : string
729585
{
730-
return path.normalize(strRawPath.replace(/^[\\/]+/, ''));
586+
return path.normalize(strRawPath.replace(/^[\\/]+/, '').trim());
731587
}
732588

733589
/**

0 commit comments

Comments
 (0)