@@ -96,6 +96,7 @@ static bool SanityCheckDeletions(const Attributes *a, const Promise *pp);
9696static bool SelectLine (EvalContext * ctx , const char * line , const Attributes * a );
9797static bool NotAnchored (char * s );
9898static bool SelectRegion (EvalContext * ctx , Item * start , Item * * begin_ptr , Item * * end_ptr , const Attributes * a , EditContext * edcontext );
99+ static PromiseResult HandleSelectRegionFailure (EvalContext * ctx , const Promise * pp , const Attributes * a , EditContext * edcontext , const char * operation_type );
99100static bool MultiLineString (char * s );
100101static bool InsertFileAtLocation (EvalContext * ctx , Item * * start , Item * begin_ptr , Item * end_ptr , Item * location , Item * prev , const Attributes * a , const Promise * pp , EditContext * edcontext , PromiseResult * result );
101102
@@ -131,6 +132,7 @@ bool ScheduleEditLineOperations(EvalContext *ctx, const Bundle *bp, const Attrib
131132
132133 for (pass = 1 ; pass < CF_DONEPASSES ; pass ++ )
133134 {
135+ edcontext -> pass = pass ; // Track current pass for convergence
134136 for (type = 0 ; EDITLINETYPESEQUENCE [type ] != NULL ; type ++ )
135137 {
136138 const BundleSection * sp = BundleGetSection (bp , EDITLINETYPESEQUENCE [type ]);
@@ -390,22 +392,7 @@ static PromiseResult VerifyLineDeletions(EvalContext *ctx, const Promise *pp, Ed
390392 }
391393 else if (!SelectRegion (ctx , * start , & begin_ptr , & end_ptr , & a , edcontext ))
392394 {
393- if (a .region .include_end || a .region .include_start )
394- {
395- cfPS (ctx , LOG_LEVEL_ERR , PROMISE_RESULT_INTERRUPTED , pp , & a ,
396- "The promised line deletion '%s' could not select an edit region in '%s'"
397- " (this is a good thing, as policy suggests deleting the markers)" ,
398- pp -> promiser , edcontext -> filename );
399- }
400- else
401- {
402- cfPS (ctx , LOG_LEVEL_ERR , PROMISE_RESULT_INTERRUPTED , pp , & a ,
403- "The promised line deletion '%s' could not select an edit region in '%s'"
404- " (but the delimiters were expected in the file)" ,
405- pp -> promiser , edcontext -> filename );
406- }
407- result = PromiseResultUpdate (result , PROMISE_RESULT_INTERRUPTED );
408- return result ;
395+ return HandleSelectRegionFailure (ctx , pp , & a , edcontext , "line deletion" );
409396 }
410397 if (!end_ptr && a .region .select_end && !a .region .select_end_match_eof )
411398 {
@@ -502,11 +489,7 @@ static PromiseResult VerifyColumnEdits(EvalContext *ctx, const Promise *pp, Edit
502489 }
503490 else if (!SelectRegion (ctx , * start , & begin_ptr , & end_ptr , & a , edcontext ))
504491 {
505- cfPS (ctx , LOG_LEVEL_ERR , PROMISE_RESULT_INTERRUPTED , pp , & a ,
506- "The promised column edit '%s' could not select an edit region in '%s'" ,
507- pp -> promiser , edcontext -> filename );
508- result = PromiseResultUpdate (result , PROMISE_RESULT_INTERRUPTED );
509- return result ;
492+ return HandleSelectRegionFailure (ctx , pp , & a , edcontext , "column edit" );
510493 }
511494
512495/* locate and split line */
@@ -581,11 +564,7 @@ static PromiseResult VerifyPatterns(EvalContext *ctx, const Promise *pp, EditCon
581564 }
582565 else if (!SelectRegion (ctx , * start , & begin_ptr , & end_ptr , & a , edcontext ))
583566 {
584- cfPS (ctx , LOG_LEVEL_ERR , PROMISE_RESULT_INTERRUPTED , pp , & a ,
585- "The promised pattern replace '%s' could not select an edit region in '%s'" ,
586- pp -> promiser , edcontext -> filename );
587- result = PromiseResultUpdate (result , PROMISE_RESULT_INTERRUPTED );
588- return result ;
567+ return HandleSelectRegionFailure (ctx , pp , & a , edcontext , "pattern replace" );
589568 }
590569
591570 snprintf (lockname , CF_BUFSIZE - 1 , "replace-%s-%s" , pp -> promiser , edcontext -> filename );
@@ -774,11 +753,7 @@ static PromiseResult VerifyLineInsertions(EvalContext *ctx, const Promise *pp, E
774753 }
775754 else if (!SelectRegion (ctx , * start , & begin_ptr , & end_ptr , & a , edcontext ))
776755 {
777- cfPS (ctx , LOG_LEVEL_ERR , PROMISE_RESULT_INTERRUPTED , pp , & a ,
778- "The promised line insertion '%s' could not select an edit region in '%s'" ,
779- pp -> promiser , edcontext -> filename );
780- result = PromiseResultUpdate (result , PROMISE_RESULT_INTERRUPTED );
781- return result ;
756+ return HandleSelectRegionFailure (ctx , pp , & a , edcontext , "line insertion" );
782757 }
783758
784759 if (!end_ptr && a .region .select_end && !a .region .select_end_match_eof )
@@ -939,6 +914,60 @@ If no such region matches, begin_ptr and end_ptr should point to NULL
939914
940915/*****************************************************************************/
941916
917+ static PromiseResult HandleSelectRegionFailure (EvalContext * ctx , const Promise * pp ,
918+ const Attributes * a , EditContext * edcontext ,
919+ const char * operation_type )
920+ /*
921+ Common error handling for SelectRegion failures across multiple edit operations.
922+ Returns appropriate PromiseResult based on whether we're in final pass or not.
923+ Special handling for line deletions where missing region markers may be intended.
924+ */
925+ {
926+ assert (pp != NULL );
927+ assert (a != NULL );
928+ assert (edcontext != NULL );
929+ assert (operation_type != NULL );
930+
931+ if (!EDIT_CONTEXT_IS_FINAL_PASS (edcontext ))
932+ {
933+ int remaining_passes = (CF_DONEPASSES - 1 ) - edcontext -> pass ;
934+ Log (LOG_LEVEL_VERBOSE ,
935+ "The promised %s '%s' could not select edit region in '%s' (pass %d/%d, %d more %s to try)" ,
936+ operation_type , pp -> promiser , edcontext -> filename ,
937+ edcontext -> pass , CF_DONEPASSES - 1 , remaining_passes ,
938+ remaining_passes == 1 ? "pass" : "passes" );
939+ return PROMISE_RESULT_NOOP ; // Allow retry in next pass
940+ }
941+
942+ // Special case for line deletions: missing markers might be intentional
943+ if (StringEqual (operation_type , "line deletion" ))
944+ {
945+ if (a -> region .include_end || a -> region .include_start )
946+ {
947+ cfPS (ctx , LOG_LEVEL_ERR , PROMISE_RESULT_INTERRUPTED , pp , a ,
948+ "The promised %s '%s' could not select an edit region in '%s' after %d passes"
949+ " (this is a good thing, as policy suggests deleting the markers)" ,
950+ operation_type , pp -> promiser , edcontext -> filename , CF_DONEPASSES - 1 );
951+ }
952+ else
953+ {
954+ cfPS (ctx , LOG_LEVEL_ERR , PROMISE_RESULT_INTERRUPTED , pp , a ,
955+ "The promised %s '%s' could not select an edit region in '%s' after %d passes"
956+ " (but the delimiters were expected in the file)" ,
957+ operation_type , pp -> promiser , edcontext -> filename , CF_DONEPASSES - 1 );
958+ }
959+ return PROMISE_RESULT_INTERRUPTED ;
960+ }
961+
962+ // Standard error for final pass
963+ cfPS (ctx , LOG_LEVEL_ERR , PROMISE_RESULT_INTERRUPTED , pp , a ,
964+ "The promised %s '%s' could not select an edit region in '%s' after %d passes" ,
965+ operation_type , pp -> promiser , edcontext -> filename , CF_DONEPASSES - 1 );
966+ return PROMISE_RESULT_INTERRUPTED ;
967+ }
968+
969+ /*****************************************************************************/
970+
942971static int MatchRegion (EvalContext * ctx , const char * chunk , const Item * begin , const Item * end , bool regex )
943972/*
944973 Match a region in between the selection delimiters. It is
0 commit comments