Skip to content

Commit 3449033

Browse files
authored
Merge pull request #134 from RooVetGit/apply_diff_insert_delete
Handle pure insertions and deletions with diffs
2 parents 4756833 + 905c68d commit 3449033

File tree

5 files changed

+290
-21
lines changed

5 files changed

+290
-21
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Roo Cline Changelog
22

3+
## [2.2.12]
4+
5+
- Better support for pure deletion and insertion diffs
6+
37
## [2.2.11]
48

59
- Added settings checkbox for verbose diff debugging

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"displayName": "Roo Cline",
44
"description": "A fork of Cline, an autonomous coding agent, with some added experimental configuration and automation features.",
55
"publisher": "RooVeterinaryInc",
6-
"version": "2.2.11",
6+
"version": "2.2.12",
77
"icon": "assets/icons/rocket.png",
88
"galleryBanner": {
99
"color": "#617A91",

src/core/diff/strategies/__tests__/search-replace.test.ts

Lines changed: 208 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,212 @@ this.init();
711711
})
712712
});
713713

714+
describe('insertion/deletion', () => {
715+
let strategy: SearchReplaceDiffStrategy
716+
717+
beforeEach(() => {
718+
strategy = new SearchReplaceDiffStrategy()
719+
})
720+
721+
describe('deletion', () => {
722+
it('should delete code when replace block is empty', () => {
723+
const originalContent = `function test() {
724+
console.log("hello");
725+
// Comment to remove
726+
console.log("world");
727+
}`
728+
const diffContent = `test.ts
729+
<<<<<<< SEARCH
730+
// Comment to remove
731+
=======
732+
>>>>>>> REPLACE`
733+
734+
const result = strategy.applyDiff(originalContent, diffContent)
735+
expect(result.success).toBe(true)
736+
if (result.success) {
737+
expect(result.content).toBe(`function test() {
738+
console.log("hello");
739+
console.log("world");
740+
}`)
741+
}
742+
})
743+
744+
it('should delete multiple lines when replace block is empty', () => {
745+
const originalContent = `class Example {
746+
constructor() {
747+
// Initialize
748+
this.value = 0;
749+
// Set defaults
750+
this.name = "";
751+
// End init
752+
}
753+
}`
754+
const diffContent = `test.ts
755+
<<<<<<< SEARCH
756+
// Initialize
757+
this.value = 0;
758+
// Set defaults
759+
this.name = "";
760+
// End init
761+
=======
762+
>>>>>>> REPLACE`
763+
764+
const result = strategy.applyDiff(originalContent, diffContent)
765+
expect(result.success).toBe(true)
766+
if (result.success) {
767+
expect(result.content).toBe(`class Example {
768+
constructor() {
769+
}
770+
}`)
771+
}
772+
})
773+
774+
it('should preserve indentation when deleting nested code', () => {
775+
const originalContent = `function outer() {
776+
if (true) {
777+
// Remove this
778+
console.log("test");
779+
// And this
780+
}
781+
return true;
782+
}`
783+
const diffContent = `test.ts
784+
<<<<<<< SEARCH
785+
// Remove this
786+
console.log("test");
787+
// And this
788+
=======
789+
>>>>>>> REPLACE`
790+
791+
const result = strategy.applyDiff(originalContent, diffContent)
792+
expect(result.success).toBe(true)
793+
if (result.success) {
794+
expect(result.content).toBe(`function outer() {
795+
if (true) {
796+
}
797+
return true;
798+
}`)
799+
}
800+
})
801+
})
802+
803+
describe('insertion', () => {
804+
it('should insert code at specified line when search block is empty', () => {
805+
const originalContent = `function test() {
806+
const x = 1;
807+
return x;
808+
}`
809+
const diffContent = `test.ts
810+
<<<<<<< SEARCH
811+
=======
812+
console.log("Adding log");
813+
>>>>>>> REPLACE`
814+
815+
const result = strategy.applyDiff(originalContent, diffContent, 2, 2)
816+
expect(result.success).toBe(true)
817+
if (result.success) {
818+
expect(result.content).toBe(`function test() {
819+
console.log("Adding log");
820+
const x = 1;
821+
return x;
822+
}`)
823+
}
824+
})
825+
826+
it('should preserve indentation when inserting at nested location', () => {
827+
const originalContent = `function test() {
828+
if (true) {
829+
const x = 1;
830+
}
831+
}`
832+
const diffContent = `test.ts
833+
<<<<<<< SEARCH
834+
=======
835+
console.log("Before");
836+
console.log("After");
837+
>>>>>>> REPLACE`
838+
839+
const result = strategy.applyDiff(originalContent, diffContent, 3, 3)
840+
expect(result.success).toBe(true)
841+
if (result.success) {
842+
expect(result.content).toBe(`function test() {
843+
if (true) {
844+
console.log("Before");
845+
console.log("After");
846+
const x = 1;
847+
}
848+
}`)
849+
}
850+
})
851+
852+
it('should handle insertion at start of file', () => {
853+
const originalContent = `function test() {
854+
return true;
855+
}`
856+
const diffContent = `test.ts
857+
<<<<<<< SEARCH
858+
=======
859+
// Copyright 2024
860+
// License: MIT
861+
862+
>>>>>>> REPLACE`
863+
864+
const result = strategy.applyDiff(originalContent, diffContent, 1, 1)
865+
expect(result.success).toBe(true)
866+
if (result.success) {
867+
expect(result.content).toBe(`// Copyright 2024
868+
// License: MIT
869+
870+
function test() {
871+
return true;
872+
}`)
873+
}
874+
})
875+
876+
it('should handle insertion at end of file', () => {
877+
const originalContent = `function test() {
878+
return true;
879+
}`
880+
const diffContent = `test.ts
881+
<<<<<<< SEARCH
882+
=======
883+
884+
// End of file
885+
>>>>>>> REPLACE`
886+
887+
const result = strategy.applyDiff(originalContent, diffContent, 4, 4)
888+
expect(result.success).toBe(true)
889+
if (result.success) {
890+
expect(result.content).toBe(`function test() {
891+
return true;
892+
}
893+
894+
// End of file`)
895+
}
896+
})
897+
898+
it('should insert at the start of the file if no start_line is provided for insertion', () => {
899+
const originalContent = `function test() {
900+
return true;
901+
}`
902+
const diffContent = `test.ts
903+
<<<<<<< SEARCH
904+
=======
905+
console.log("test");
906+
>>>>>>> REPLACE`
907+
908+
const result = strategy.applyDiff(originalContent, diffContent)
909+
expect(result.success).toBe(true)
910+
if (result.success) {
911+
expect(result.content).toBe(`console.log("test");
912+
function test() {
913+
return true;
914+
}`)
915+
}
916+
})
917+
})
918+
})
919+
714920
describe('fuzzy matching', () => {
715921
let strategy: SearchReplaceDiffStrategy
716922

@@ -1241,8 +1447,8 @@ function two() {
12411447

12421448
it('should document start_line and end_line parameters', () => {
12431449
const description = strategy.getToolDescription('/test')
1244-
expect(description).toContain('start_line: (required) The line number where the search block starts.')
1245-
expect(description).toContain('end_line: (required) The line number where the search block ends.')
1450+
expect(description).toContain('start_line: (required) The line number where the search block starts (inclusive).')
1451+
expect(description).toContain('end_line: (required) The line number where the search block ends (inclusive).')
12461452
})
12471453
})
12481454
})

0 commit comments

Comments
 (0)