also looking at this
fix(loops): render the row a @break or @continue stops in
#2102
2 files
+89
-43
| @@ -394,27 +394,22 @@ function processUnconditionalLoopControl(content: string): string { | ||
| 394 | 394 | } |
| 395 | 395 | |
| 396 | 396 | /** |
| 397 | * Check if content contains a break marker and clean it up | |
| 397 | * Cut a row at its first break or continue marker. | |
| 398 | * | |
| 399 | * Returns which one it was (null when the row has neither) and the content | |
| 400 | * before it, which is still part of the row. | |
| 398 | 401 | */ |
| 399 | function checkAndCleanBreak(content: string): { hasBreak: boolean, content: string } { | |
| 400 | const hasBreak = content.includes(BREAK_MARKER) | |
| 401 | // Remove marker and everything after it | |
| 402 | const cleanContent = hasBreak | |
| 403 | ? content.substring(0, content.indexOf(BREAK_MARKER)) | |
| 404 | : content | |
| 405 | return { hasBreak, content: cleanContent } | |
| 406 | } | |
| 402 | function cutAtLoopControl(content: string): { control: 'break' | 'continue' | null, content: string } { | |
| 403 | const breakAt = content.indexOf(BREAK_MARKER) | |
| 404 | const continueAt = content.indexOf(CONTINUE_MARKER) | |
| 405 | if (breakAt === -1 && continueAt === -1) | |
| 406 | return { control: null, content } | |
| 407 | 407 | |
| 408 | /** | |
| 409 | * Check if content contains a continue marker and clean it up | |
| 410 | */ | |
| 411 | function checkAndCleanContinue(content: string): { hasContinue: boolean, content: string } { | |
| 412 | const hasContinue = content.includes(CONTINUE_MARKER) | |
| 413 | // Remove marker and everything after it | |
| 414 | const cleanContent = hasContinue | |
| 415 | ? content.substring(0, content.indexOf(CONTINUE_MARKER)) | |
| 416 | : content | |
| 417 | return { hasContinue, content: cleanContent } | |
| 408 | const isBreak = continueAt === -1 || (breakAt !== -1 && breakAt < continueAt) | |
| 409 | return { | |
| 410 | control: isBreak ? 'break' : 'continue', | |
| 411 | content: content.substring(0, isBreak ? breakAt : continueAt), | |
| 412 | } | |
| 418 | 413 | } |
| 419 | 414 | |
| 420 | 415 | /** |
| @@ -1044,19 +1039,12 @@ export function processLoops(template: string, context: Record<string, any>, fil | ||
| 1044 | 1039 | // These are evaluated immediately based on the condition expression |
| 1045 | 1040 | let processedContent = processConditionalLoopControl(content, itemContext) |
| 1046 | 1041 | |
| 1047 | // Check for continue marker from conditional @continue(condition) | |
| 1048 | let continueCheck = checkAndCleanContinue(processedContent) | |
| 1049 | if (continueCheck.hasContinue) { | |
| 1050 | loopResult += continueCheck.content | |
| 1051 | continue | |
| 1052 | } | |
| 1053 | ||
| 1054 | // Check for break marker from conditional @break(condition) | |
| 1055 | let breakCheck = checkAndCleanBreak(processedContent) | |
| 1056 | if (breakCheck.hasBreak) { | |
| 1057 | loopResult += breakCheck.content | |
| 1058 | break | |
| 1059 | } | |
| 1042 | // A @break or @continue ends the row where it stands. The content | |
| 1043 | // before it is still this row, so it goes through every step below | |
| 1044 | // with the row's context; appending it here left its for the | |
| 1045 | // page pass, which no longer has the loop variable. | |
| 1046 | let stop = cutAtLoopControl(processedContent) | |
| 1047 | processedContent = stop.content | |
| 1060 | 1048 | |
| 1061 | 1049 | // Step 2a: Process any nested @forelse blocks with the iteration context |
| 1062 | 1050 | processedContent = processForelse(processedContent, itemContext) |
| @@ -1072,17 +1060,12 @@ export function processLoops(template: string, context: Record<string, any>, fil | ||
| 1072 | 1060 | // Now that conditionals are processed, @break/@continue from @if blocks are visible |
| 1073 | 1061 | processedContent = processUnconditionalLoopControl(processedContent) |
| 1074 | 1062 | |
| 1075 | // Check for break/continue markers | |
| 1076 | breakCheck = checkAndCleanBreak(processedContent) | |
| 1077 | if (breakCheck.hasBreak) { | |
| 1078 | loopResult += breakCheck.content | |
| 1079 | break | |
| 1080 | } | |
| 1081 | continueCheck = checkAndCleanContinue(processedContent) | |
| 1082 | if (continueCheck.hasContinue) { | |
| 1083 | loopResult += continueCheck.content | |
| 1084 | continue | |
| 1085 | } | |
| 1063 | // A marker revealed here sits before any found in step 1, since | |
| 1064 | // everything after that one was already cut away. | |
| 1065 | const revealed = cutAtLoopControl(processedContent) | |
| 1066 | if (revealed.control) | |
| 1067 | stop = revealed | |
| 1068 | processedContent = revealed.content | |
| 1086 | 1069 | |
| 1087 | 1070 | // Step 5: Process expressions with the item context |
| 1088 | 1071 | processedContent = processExpressions(processedContent, itemContext, filePath) |
| @@ -1106,6 +1089,8 @@ export function processLoops(template: string, context: Record<string, any>, fil | ||
| 1106 | 1089 | processedContent = preEvaluateIncludeData(processedContent, itemContext) |
| 1107 | 1090 | |
| 1108 | 1091 | loopResult += processedContent |
| 1092 | if (stop.control === 'break') | |
| 1093 | break | |
| 1109 | 1094 | } |
| 1110 | 1095 | |
| 1111 | 1096 | // Replace the @foreach block with the processed result |