Browse Source

Don't break if an end_line is passed into a diff (#2743)

Matt Rubens 8 months ago
parent
commit
06882f5687

+ 19 - 0
src/core/diff/strategies/__tests__/multi-search-replace.test.ts

@@ -159,6 +159,25 @@ function helloWorld() {
 				}
 			})
 
+			it("should replace matching content when end_line is passed in", async () => {
+				const originalContent = 'function hello() {\n    console.log("hello")\n}\n'
+				const diffContent = `test.ts
+<<<<<<< SEARCH
+:start_line:1
+:end_line:1
+-------
+function hello() {
+=======
+function helloWorld() {
+>>>>>>> REPLACE`
+
+				const result = await strategy.applyDiff(originalContent, diffContent)
+				expect(result.success).toBe(true)
+				if (result.success) {
+					expect(result.content).toBe('function helloWorld() {\n    console.log("hello")\n}\n')
+				}
+			})
+
 			it("should match content with different surrounding whitespace", async () => {
 				const originalContent = "\nfunction example() {\n    return 42;\n}\n\n"
 				const diffContent = `test.ts

+ 12 - 8
src/core/diff/strategies/multi-search-replace.ts

@@ -186,6 +186,7 @@ Only use a single line of '=======' between search and replacement content, beca
 			.replace(/^\\=======/gm, "=======")
 			.replace(/^\\>>>>>>>/gm, ">>>>>>>")
 			.replace(/^\\-------/gm, "-------")
+			.replace(/^\\:end_line:/gm, ":end_line:")
 			.replace(/^\\:start_line:/gm, ":start_line:")
 	}
 
@@ -322,25 +323,28 @@ Only use a single line of '=======' between search and replacement content, beca
 			3. ((?:\:start_line:\s*(\d+)\s*\n))?  
 			  Optionally matches a “:start_line:” line. The outer capturing group is group 1 and the inner (\d+) is group 2.
 
-			4. ((?<!\\)-------\s*\n)?  
+			4. ((?:\:end_line:\s*(\d+)\s*\n))?  
+			  Optionally matches a “:end_line:” line. Group 3 is the whole match and group 4 is the digits.
+
+			5. ((?<!\\)-------\s*\n)?  
 			  Optionally matches the “-------” marker line (group 5).
 
-			5. ([\s\S]*?)(?:\n)?  
+			6. ([\s\S]*?)(?:\n)?  
 			  Non‐greedy match for the “search content” (group 6) up to the next marker.
 
-			6. (?:(?<=\n)(?<!\\)=======\s*\n)  
+			7. (?:(?<=\n)(?<!\\)=======\s*\n)  
 			  Matches the “=======” marker on its own line.
 
-			7. ([\s\S]*?)(?:\n)?  
+			8. ([\s\S]*?)(?:\n)?  
 			  Non‐greedy match for the “replace content” (group 7).
 
-			8. (?:(?<=\n)(?<!\\)>>>>>>> REPLACE)(?=\n|$)  
+			9. (?:(?<=\n)(?<!\\)>>>>>>> REPLACE)(?=\n|$)  
 			  Matches the final “>>>>>>> REPLACE” marker on its own line (and requires a following newline or the end of file).
 		*/
 
 		let matches = [
 			...diffContent.matchAll(
-				/(?:^|\n)(?<!\\)<<<<<<< SEARCH\s*\n((?:\:start_line:\s*(\d+)\s*\n))?((?<!\\)-------\s*\n)?([\s\S]*?)(?:\n)?(?:(?<=\n)(?<!\\)=======\s*\n)([\s\S]*?)(?:\n)?(?:(?<=\n)(?<!\\)>>>>>>> REPLACE)(?=\n|$)/g,
+				/(?:^|\n)(?<!\\)<<<<<<< SEARCH\s*\n((?:\:start_line:\s*(\d+)\s*\n))?((?:\:end_line:\s*(\d+)\s*\n))?((?<!\\)-------\s*\n)?([\s\S]*?)(?:\n)?(?:(?<=\n)(?<!\\)=======\s*\n)([\s\S]*?)(?:\n)?(?:(?<=\n)(?<!\\)>>>>>>> REPLACE)(?=\n|$)/g,
 			),
 		]
 
@@ -359,8 +363,8 @@ Only use a single line of '=======' between search and replacement content, beca
 		const replacements = matches
 			.map((match) => ({
 				startLine: Number(match[2] ?? 0),
-				searchContent: match[4],
-				replaceContent: match[5],
+				searchContent: match[6],
+				replaceContent: match[7],
 			}))
 			.sort((a, b) => a.startLine - b.startLine)