diff options
Diffstat (limited to 'packages/core/src/utils/positionBasedEditProcessor.ts')
| -rw-r--r-- | packages/core/src/utils/positionBasedEditProcessor.ts | 239 |
1 files changed, 0 insertions, 239 deletions
diff --git a/packages/core/src/utils/positionBasedEditProcessor.ts b/packages/core/src/utils/positionBasedEditProcessor.ts deleted file mode 100644 index 977d6859..00000000 --- a/packages/core/src/utils/positionBasedEditProcessor.ts +++ /dev/null @@ -1,239 +0,0 @@ -/** - * @license - * Copyright 2025 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * Single edit operation with position information - */ -export interface PositionedEdit { - /** - * Original edit operation - */ - original: EditOperation; - - /** - * Index in the original edits array - */ - index: number; - - /** - * Start position in the file content - */ - startPos: number; - - /** - * End position in the file content - */ - endPos: number; - - /** - * The replacement text - */ - newString: string; -} - -/** - * Result of failed edit with reason - */ -export interface FailedEdit { - index: number; - edit: EditOperation; - reason: 'not_found' | 'multiple_matches' | 'empty_old_string'; -} - -/** - * Result of position-based edit processing - */ -export interface PositionBasedEditResult { - finalContent: string; - appliedEdits: PositionedEdit[]; - failedEdits: FailedEdit[]; -} - -/** - * Edit operation interface (matches existing EditOperation) - */ -export interface EditOperation { - old_string: string; - new_string: string; -} - -/** - * Efficient position-based edit processor that minimizes memory usage - * and applies edits in optimal order to prevent position conflicts. - */ -export class PositionBasedEditProcessor { - /** - * Process edits with minimal memory usage and optimal ordering - */ - processEdits( - content: string, - edits: EditOperation[], - ): PositionBasedEditResult { - // Phase 1: Find positions for all edits - const { validEdits, failedEdits } = this.analyzeEdits(content, edits); - - // Phase 2: Sort by position (highest first) to prevent position drift - const sortedEdits = validEdits.sort((a, b) => b.startPos - a.startPos); - - // Phase 3: Build final content in single pass - const finalContent = this.buildFinalContent(content, sortedEdits); - - return { - finalContent, - appliedEdits: sortedEdits, - failedEdits, - }; - } - - /** - * Analyze all edits and categorize into valid/failed - */ - private analyzeEdits( - content: string, - edits: EditOperation[], - ): { - validEdits: PositionedEdit[]; - failedEdits: FailedEdit[]; - } { - const validEdits: PositionedEdit[] = []; - const failedEdits: FailedEdit[] = []; - - for (let i = 0; i < edits.length; i++) { - const edit = edits[i]; - - // Handle empty old_string (file creation case) - if (edit.old_string === '') { - failedEdits.push({ - index: i, - edit, - reason: 'empty_old_string', - }); - continue; - } - - // Find all positions of old_string - const positions = this.findAllPositions(content, edit.old_string); - - if (positions.length === 0) { - failedEdits.push({ - index: i, - edit, - reason: 'not_found', - }); - } else if (positions.length > 1) { - failedEdits.push({ - index: i, - edit, - reason: 'multiple_matches', - }); - } else { - // Exactly one match - valid edit - const startPos = positions[0]; - validEdits.push({ - original: edit, - index: i, - startPos, - endPos: startPos + edit.old_string.length, - newString: edit.new_string, - }); - } - } - - return { validEdits, failedEdits }; - } - - /** - * Find all positions where searchString occurs in content - */ - private findAllPositions(content: string, searchString: string): number[] { - const positions: number[] = []; - let index = content.indexOf(searchString); - - while (index !== -1) { - positions.push(index); - index = content.indexOf(searchString, index + 1); - } - - return positions; - } - - /** - * Build final content in single pass with minimal memory allocation - */ - private buildFinalContent(original: string, edits: PositionedEdit[]): string { - if (edits.length === 0) { - return original; - } - - // Check for overlapping edits (should not happen with our validation, but safety check) - if (this.hasOverlappingEdits(edits)) { - throw new Error('Overlapping edits detected - this should not happen'); - } - - // Build segments array working backwards through the string - const segments: string[] = []; - let currentPos = original.length; - - // Process edits from end to beginning (edits are already sorted by startPos desc) - for (const edit of edits) { - // Add text after this edit position - if (currentPos > edit.endPos) { - segments.unshift(original.slice(edit.endPos, currentPos)); - } - - // Add the replacement text - segments.unshift(edit.newString); - - // Update current position - currentPos = edit.startPos; - } - - // Add remaining text from beginning - if (currentPos > 0) { - segments.unshift(original.slice(0, currentPos)); - } - - // Single join operation creates final string - return segments.join(''); - } - - /** - * Check if any edits overlap (safety validation) - */ - private hasOverlappingEdits(edits: PositionedEdit[]): boolean { - for (let i = 0; i < edits.length - 1; i++) { - for (let j = i + 1; j < edits.length; j++) { - const edit1 = edits[i]; - const edit2 = edits[j]; - - // Check if ranges overlap - if ( - !(edit1.endPos <= edit2.startPos || edit2.endPos <= edit1.startPos) - ) { - return true; - } - } - } - - return false; - } - - /** - * Get human-readable error message for failed edit reason - */ - static getErrorMessage(reason: FailedEdit['reason']): string { - switch (reason) { - case 'not_found': - return 'Old string not found in current content'; - case 'multiple_matches': - return 'Old string found multiple times - please be more specific'; - case 'empty_old_string': - return 'Cannot use empty old_string on existing file'; - default: - return 'Unknown error'; - } - } -} |
