@@ -424,20 +424,20 @@ TCVB 格式规范:
4244241. 全局替换操作(GLOBAL-REPLACE):
425425## OPERATION:GLOBAL-REPLACE
426426## OLD_CONTENT
427- [markdown代码块:被全局替换的内容]
427+ [markdown代码块:被全局替换的内容, 可以在需要被替换的文本前后包含一些上下文帮助精确替换,但是不要太长 ]
428428## NEW_CONTENT
429429[markdown代码块:新内容]
430430
431- 2. 精确替换操作(EXACT-REPLACE),用于替换全局替换无法精准定位的情况:
431+ 2. 精确替换操作(EXACT-REPLACE),用于替换全局替换无法精准定位的情况(如果GLOBAL-REPLACE可以定位到,没有歧义,就优先用GLOBAL-REPLACE) :
432432## OPERATION:EXACT-REPLACE
433+ ## BEFORE_ANCHOR
434+ [markdown代码块:OLD_CONTENT之前的几行内容, 用来划定范围上半段锚点,避免有多个类似匹配, 不能和OLD_CONTENT重合。不要太长,可以精确定位到位置即可]
435+ ## AFTER_ANCHOR
436+ [markdown代码块:OLD_CONTENT之后的几行内容, 用来划定范围下半段锚点,避免有多个类似匹配, 不能和OLD_CONTENT重合,不要太长,可以精确定位到位置即可]
433437## OLD_CONTENT
434438[markdown代码块:被替换内容]
435439## NEW_CONTENT
436440[markdown代码块:新内容]
437- ## BEFORE_ANCHOR
438- [markdown代码块:OLD_CONTENT之前的几行内容, 用来划定范围上半段锚点,避免有多个类似匹配, 不能和OLD_CONTENT重合]
439- ## AFTER_ANCHOR
440- [markdown代码块:OLD_CONTENT之后的几行内容, 用来划定范围下半段锚点,避免有多个类似匹配, 不能和OLD_CONTENT重合]
441441
4424423. 创建操作(CREATE):
443443## OPERATION:CREATE
@@ -501,41 +501,105 @@ export function mergeCvb(baseCvb: Cvb, tcvb: TCVB) : Cvb
501501 }
502502 }
503503 catch ( err : any ) {
504- throw new Error ( `合并CVB失败 : ${ err . message } ` ) ;
504+ throw new Error ( `TCVB格式可能有问题,尝试增量修改CVB时出错 : ${ err . message } ` ) ;
505505 }
506506
507507 return rebuildCvb ( baseCvb , mapMergedFiles ) ;
508508}
509509
510- function applyExactReplace ( strContent : string , op : ExactReplaceOperation ) : string {
511- // 构建匹配模式:前锚点 + 中间内容1 + 旧内容 + 中间内容2 + 后锚点
512- const regPattern = buildPattern ( op . m_strBeforeAnchor , op . m_strOldContent , op . m_strAfterAnchor ) ;
513- // 替换为:前锚点 + 中间内容1 + 新内容 + 中间内容2 + 后锚点
514- const strReplacement = op . m_strBeforeAnchor + '$1' + op . m_strNewContent + '$2' + op . m_strAfterAnchor ;
510+ function diagnoseMatchFailure ( strContent : string , op : ExactReplaceOperation ) : string
511+ {
512+ function findLineNumber ( content : string , pattern : RegExp ) : number [ ]
513+ {
514+ const lines = content . split ( "\n" ) ;
515+ return lines
516+ . map ( ( line , index ) => ( pattern . test ( line ) ? index + 1 : - 1 ) )
517+ . filter ( index => index !== - 1 ) ;
518+ }
515519
516- regPattern . lastIndex = 0 ; // 重置正则表达式的状态
517- if ( ! regPattern . test ( strContent ) ) {
518- console . log ( "以下表达式:\n" + regPattern + "\n 无法匹配:\n" ) ;
519- throw new Error ( `Exact-replace操作失败:文件 "${ op . m_strFilePath } " 中未找到匹配项。请检查前后锚点及旧内容是否正确。## OLD_CONTENT\n${ op . m_strOldContent } \n\n## BEFORE_ANCHOR\n${ op . m_strBeforeAnchor } \n\n## AFTER_ANCHOR\n${ op . m_strAfterAnchor } ` ) ;
520- }
521- regPattern . lastIndex = 0 ; // 再次重置以备替换
520+ let errorMessages : string [ ] = [ ] ;
521+ const beforeAnchorPattern = new RegExp ( op . m_strBeforeAnchor , "gm" ) ;
522+ const afterAnchorPattern = new RegExp ( op . m_strAfterAnchor , "gm" ) ;
523+ const oldContentPattern = new RegExp ( op . m_strOldContent , "gm" ) ;
524+
525+ const beforeAnchorLines = findLineNumber ( strContent , beforeAnchorPattern ) ;
526+ const afterAnchorLines = findLineNumber ( strContent , afterAnchorPattern ) ;
527+ const oldContentLines = findLineNumber ( strContent , oldContentPattern ) ;
528+
529+ if ( beforeAnchorLines . length === 0 )
530+ {
531+ errorMessages . push ( `FILE: ${ op . m_strFilePath } 未找到 BEFORE_ANCHOR:\n\`\`\`\n${ op . m_strBeforeAnchor } \n\`\`\`` ) ;
532+ }
533+
534+ if ( afterAnchorLines . length === 0 )
535+ {
536+ errorMessages . push ( `FILE: ${ op . m_strFilePath } 未找到 AFTER_ANCHOR:\n\`\`\`\n${ op . m_strAfterAnchor } \n\`\`\`` ) ;
537+ }
538+
539+ if ( oldContentLines . length === 0 )
540+ {
541+ errorMessages . push ( `FILE: ${ op . m_strFilePath } 未找到 OLD_CONTENT:\n\`\`\`\n${ op . m_strOldContent } \n\`\`\`` ) ;
542+ }
543+
544+ if ( errorMessages . length === 0 )
545+ {
546+ const minBeforeLine = Math . min ( ...beforeAnchorLines ) ;
547+ const maxAfterLine = Math . max ( ...afterAnchorLines ) ;
548+ const minOldLine = Math . min ( ...oldContentLines ) ;
549+ const maxOldLine = Math . max ( ...oldContentLines ) ;
550+
551+ if ( minOldLine < minBeforeLine || maxOldLine > maxAfterLine )
552+ {
553+ errorMessages . push (
554+ `FILE: ${ op . m_strFilePath } OLD_CONTENT 应该在 BEFORE_ANCHOR 和 AFTER_ANCHOR 之间:\nBEFORE_ANCHOR:\n\`\`\`\n${ op . m_strBeforeAnchor } \n\`\`\`\nOLD_CONTENT:\n\`\`\`\n${ op . m_strOldContent } \n\`\`\`\nAFTER_ANCHOR:\n\`\`\`\n${ op . m_strAfterAnchor } \n\`\`\``
555+ ) ;
556+ }
557+ }
558+
559+ return errorMessages . length > 0 ? errorMessages . join ( "\n" ) : "" ;
560+ }
561+
562+ function applyExactReplace ( strContent : string , op : ExactReplaceOperation ) : string
563+ {
564+ const regPattern = buildPattern ( op . m_strBeforeAnchor , op . m_strOldContent , op . m_strAfterAnchor ) ;
565+ const strReplacement = op . m_strBeforeAnchor + '$1' + op . m_strNewContent + '$2' + op . m_strAfterAnchor ;
566+
567+ regPattern . lastIndex = 0 ;
568+ if ( ! regPattern . test ( strContent ) )
569+ {
570+ const diagnosticMessage = diagnoseMatchFailure ( strContent , op ) ;
571+ const errorMsg = `## EXACT-REPLACE 失败\n` +
572+ `### FILE: ${ op . m_strFilePath } \n` +
573+ `### BEFORE_ANCHOR:\n\`\`\`\n${ op . m_strBeforeAnchor } \n\`\`\`\n` +
574+ `### AFTER_ANCHOR:\n\`\`\`\n${ op . m_strAfterAnchor } \n\`\`\`\n` +
575+ `### OLD_CONTENT:\n\`\`\`\n${ op . m_strOldContent } \n\`\`\`\n` +
576+ `### NEW_CONTENT:\n\`\`\`\n${ op . m_strNewContent } \n\`\`\`\n` +
577+ `### 错误:\n${ diagnosticMessage } ` ;
578+
579+ console . log ( errorMsg ) ;
580+ throw new Error ( errorMsg ) ;
581+ }
522582
523- return strContent . replace ( regPattern , strReplacement ) ;
583+ regPattern . lastIndex = 0 ;
584+ return strContent . replace ( regPattern , strReplacement ) ;
524585}
525586
526587function applyGlobalReplace ( strContent : string , op : GlobalReplaceOperation ) : string
527588{
528589 if ( op . m_strOldContent === "" )
529590 {
530- throw new Error ( `全局替换为空` ) ;
591+ const errorMsg = `GLOBAL-REPLACE 失败:FILE:"${ op . m_strFilePath } " OLD_CONTENT 是空的"` ;
592+ console . log ( errorMsg ) ;
593+ throw new Error ( errorMsg ) ;
531594 }
532595
533596 const regPattern : RegExp = new RegExp ( normalizeLineWhitespace ( escapeRegExp ( op . m_strOldContent ) ) , 'gs' ) ;
534597
535598 regPattern . lastIndex = 0 ;
536599 if ( ! regPattern . test ( strContent ) ) {
537- console . log ( "以下表达式:\n" + regPattern + "\n 无法匹配" ) ;
538- throw new Error ( `全局替换失败:文件 "${ op . m_strFilePath } " 中未找到旧内容 "${ op . m_strOldContent } "。` ) ;
600+ const errorMsg = `GLOBAL-REPLACE 失败:FILE:"${ op . m_strFilePath } " 中未找到OLD_CONTENT: "${ op . m_strOldContent } "` ;
601+ console . log ( errorMsg ) ;
602+ throw new Error ( errorMsg ) ;
539603 }
540604 regPattern . lastIndex = 0 ;
541605
0 commit comments