Skip to content

Commit 42715c1

Browse files
committed
Merge branch 'main' into jq/sound-setting-improvements
2 parents 95222a9 + 5fcf6f0 commit 42715c1

File tree

12 files changed

+493
-403
lines changed

12 files changed

+493
-403
lines changed

.github/workflows/code-qa.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,4 @@ jobs:
4242
run: npm run install:all
4343

4444
- name: Run unit tests
45-
run: npx jest
45+
run: npm test

CHANGELOG.md

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,9 @@
11
# Roo Cline Changelog
22

3-
## [2.2.9]
4-
5-
- Fix a bug where Gemini was including line numbers in the search/replace content
6-
7-
## [2.2.8]
8-
9-
- More work on diff editing (better matching, indentation, logging)
10-
11-
## [2.2.7]
3+
## [2.2.6 - 2.2.10]
124

135
- More fixes to search/replace diffs
146

15-
## [2.2.6]
16-
17-
- Add a fuzzy match tolerance when applying diffs
18-
197
## [2.2.5]
208

219
- Allow MCP servers to be enabled/disabled

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: 2 additions & 2 deletions
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.9",
6+
"version": "2.2.10",
77
"icon": "assets/icons/rocket.png",
88
"galleryBanner": {
99
"color": "#617A91",
@@ -157,7 +157,7 @@
157157
"package": "npm run build:webview && npm run check-types && npm run lint && node esbuild.js --production",
158158
"pretest": "npm run compile-tests && npm run compile && npm run lint",
159159
"start:webview": "cd webview-ui && npm run start",
160-
"test": "jest",
160+
"test": "jest && npm run test:webview",
161161
"test:webview": "cd webview-ui && npm run test",
162162
"prepare": "husky",
163163
"publish:marketplace": "vsce publish",

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

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,153 @@ this.init();
564564
});
565565
})
566566

567+
describe('line number stripping', () => {
568+
describe('line number stripping', () => {
569+
let strategy: SearchReplaceDiffStrategy
570+
571+
beforeEach(() => {
572+
strategy = new SearchReplaceDiffStrategy()
573+
})
574+
575+
it('should strip line numbers from both search and replace sections', () => {
576+
const originalContent = 'function test() {\n return true;\n}\n'
577+
const diffContent = `test.ts
578+
<<<<<<< SEARCH
579+
1 | function test() {
580+
2 | return true;
581+
3 | }
582+
=======
583+
1 | function test() {
584+
2 | return false;
585+
3 | }
586+
>>>>>>> REPLACE`
587+
588+
const result = strategy.applyDiff(originalContent, diffContent)
589+
expect(result.success).toBe(true)
590+
if (result.success) {
591+
expect(result.content).toBe('function test() {\n return false;\n}\n')
592+
}
593+
})
594+
595+
it('should not strip when not all lines have numbers in either section', () => {
596+
const originalContent = 'function test() {\n return true;\n}\n'
597+
const diffContent = `test.ts
598+
<<<<<<< SEARCH
599+
1 | function test() {
600+
2 | return true;
601+
3 | }
602+
=======
603+
1 | function test() {
604+
return false;
605+
3 | }
606+
>>>>>>> REPLACE`
607+
608+
const result = strategy.applyDiff(originalContent, diffContent)
609+
expect(result.success).toBe(false)
610+
})
611+
612+
it('should preserve content that naturally starts with pipe', () => {
613+
const originalContent = '|header|another|\n|---|---|\n|data|more|\n'
614+
const diffContent = `test.ts
615+
<<<<<<< SEARCH
616+
1 | |header|another|
617+
2 | |---|---|
618+
3 | |data|more|
619+
=======
620+
1 | |header|another|
621+
2 | |---|---|
622+
3 | |data|updated|
623+
>>>>>>> REPLACE`
624+
625+
const result = strategy.applyDiff(originalContent, diffContent)
626+
expect(result.success).toBe(true)
627+
if (result.success) {
628+
expect(result.content).toBe('|header|another|\n|---|---|\n|data|updated|\n')
629+
}
630+
})
631+
632+
it('should preserve indentation when stripping line numbers', () => {
633+
const originalContent = ' function test() {\n return true;\n }\n'
634+
const diffContent = `test.ts
635+
<<<<<<< SEARCH
636+
1 | function test() {
637+
2 | return true;
638+
3 | }
639+
=======
640+
1 | function test() {
641+
2 | return false;
642+
3 | }
643+
>>>>>>> REPLACE`
644+
645+
const result = strategy.applyDiff(originalContent, diffContent)
646+
expect(result.success).toBe(true)
647+
if (result.success) {
648+
expect(result.content).toBe(' function test() {\n return false;\n }\n')
649+
}
650+
})
651+
652+
it('should handle different line numbers between sections', () => {
653+
const originalContent = 'function test() {\n return true;\n}\n'
654+
const diffContent = `test.ts
655+
<<<<<<< SEARCH
656+
10 | function test() {
657+
11 | return true;
658+
12 | }
659+
=======
660+
20 | function test() {
661+
21 | return false;
662+
22 | }
663+
>>>>>>> REPLACE`
664+
665+
const result = strategy.applyDiff(originalContent, diffContent)
666+
expect(result.success).toBe(true)
667+
if (result.success) {
668+
expect(result.content).toBe('function test() {\n return false;\n}\n')
669+
}
670+
})
671+
672+
it('should not strip content that starts with pipe but no line number', () => {
673+
const originalContent = '| Pipe\n|---|\n| Data\n'
674+
const diffContent = `test.ts
675+
<<<<<<< SEARCH
676+
| Pipe
677+
|---|
678+
| Data
679+
=======
680+
| Pipe
681+
|---|
682+
| Updated
683+
>>>>>>> REPLACE`
684+
685+
const result = strategy.applyDiff(originalContent, diffContent)
686+
expect(result.success).toBe(true)
687+
if (result.success) {
688+
expect(result.content).toBe('| Pipe\n|---|\n| Updated\n')
689+
}
690+
})
691+
692+
it('should handle mix of line-numbered and pipe-only content', () => {
693+
const originalContent = '| Pipe\n|---|\n| Data\n'
694+
const diffContent = `test.ts
695+
<<<<<<< SEARCH
696+
| Pipe
697+
|---|
698+
| Data
699+
=======
700+
1 | | Pipe
701+
2 | |---|
702+
3 | | NewData
703+
>>>>>>> REPLACE`
704+
705+
const result = strategy.applyDiff(originalContent, diffContent)
706+
expect(result.success).toBe(true)
707+
if (result.success) {
708+
expect(result.content).toBe('1 | | Pipe\n2 | |---|\n3 | | NewData\n')
709+
}
710+
})
711+
})
712+
});
713+
567714
describe('fuzzy matching', () => {
568715
let strategy: SearchReplaceDiffStrategy
569716

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ The tool will maintain proper indentation and formatting while making changes.
6262
Only a single operation is allowed per tool use.
6363
The SEARCH section must exactly match existing content including whitespace and indentation.
6464
If you're not confident in the exact content to search for, use the read_file tool first to get the exact content.
65-
IMPORTANT: The read_file tool returns the file content with line numbers prepended to each line. However, DO NOT include line numbers in the SEARCH and REPLACE sections of the diff content.
6665
6766
Parameters:
6867
- path: (required) The path of the file to modify (relative to the current working directory ${cwd})
@@ -132,10 +131,25 @@ Your search/replace content here
132131
};
133132
}
134133

135-
const [_, searchContent, replaceContent] = match;
134+
let [_, searchContent, replaceContent] = match;
136135

137136
// Detect line ending from original content
138137
const lineEnding = originalContent.includes('\r\n') ? '\r\n' : '\n';
138+
139+
// Strip line numbers from search and replace content if every line starts with a line number
140+
const hasLineNumbers = (content: string) => {
141+
const lines = content.split(/\r?\n/);
142+
return lines.length > 0 && lines.every(line => /^\d+\s+\|(?!\|)/.test(line));
143+
};
144+
145+
if (hasLineNumbers(searchContent) && hasLineNumbers(replaceContent)) {
146+
const stripLineNumbers = (content: string) => {
147+
return content.replace(/^\d+\s+\|(?!\|)/gm, '')
148+
};
149+
150+
searchContent = stripLineNumbers(searchContent);
151+
replaceContent = stripLineNumbers(replaceContent);
152+
}
139153

140154
// Split content into lines, handling both \n and \r\n
141155
const searchLines = searchContent.split(/\r?\n/);

webview-ui/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"scripts": {
3131
"start": "react-scripts start",
3232
"build": "node ./scripts/build-react-no-split.js",
33-
"test": "react-scripts test",
33+
"test": "react-scripts test --watchAll=false",
3434
"eject": "react-scripts eject"
3535
},
3636
"eslintConfig": {
@@ -57,7 +57,7 @@
5757
},
5858
"jest": {
5959
"transformIgnorePatterns": [
60-
"/node_modules/(?!(rehype-highlight|react-remark|unist-util-visit|vfile|unified|bail|is-plain-obj|trough|vfile-message|unist-util-stringify-position|mdast-util-from-markdown|mdast-util-to-string|micromark|decode-named-character-reference|character-entities|markdown-table|zwitch|longest-streak|escape-string-regexp|unist-util-is|hast-util-to-text)/)"
60+
"/node_modules/(?!(rehype-highlight|react-remark|unist-util-visit|vfile|unified|bail|is-plain-obj|trough|vfile-message|unist-util-stringify-position|mdast-util-from-markdown|mdast-util-to-string|micromark|decode-named-character-reference|character-entities|markdown-table|zwitch|longest-streak|escape-string-regexp|unist-util-is|hast-util-to-text|@vscode/webview-ui-toolkit|@microsoft/fast-react-wrapper|@microsoft/fast-element|@microsoft/fast-foundation|@microsoft/fast-web-utilities|exenv-es6)/)"
6161
],
6262
"moduleNameMapper": {
6363
"\\.(css|less|scss|sass)$": "identity-obj-proxy"

0 commit comments

Comments
 (0)