Skip to content

Preserve replace string when match has no cased characters - #336815

Open
Lakshya Pandey (pandeylakshya207-max) wants to merge 5 commits into
microsoft:mainfrom
pandeylakshya207-max:fix/192168-preserve-case-uncased-match
Open

Lakshya Pandey (pandeylakshya207-max) wants to merge 5 commits into
microsoft:mainfrom
pandeylakshya207-max:fix/192168-preserve-case-uncased-match

Conversation

@pandeylakshya207-max

Copy link
Copy Markdown

Fixes #192168

Problem

With "Preserve Case" enabled, replacing a match that contains no cased
characters uppercases the replace string. Searching for () and replacing
with fontSize: 20 produces FONTSIZE: 20.

buildReplaceStringWithCasePreserved decides the match is uppercase using
matches[0].toUpperCase() === matches[0]. Punctuation and digits are
unchanged by toUpperCase(), so that check passes for matches that have no
case at all, and the replace string is uppercased. The same applies to
matches like 123.

Fix

Return the replace pattern unchanged when the match contains no cased
characters, detected with toLowerCase() === toUpperCase(). This is true
only for strings with no cased characters, so it also covers whitespace and
scripts without case.

The check runs before the hyphen/underscore handling, which recurses into
this function per segment, so caseless segments are left alone too.

Testing

  • Added cases to both buildReplaceStringWithCasePreserved test and
    preserve case in replacePattern.test.ts. All 16 tests in the suite
    pass; the 22 existing assertions are unaffected since every one of them
    has a match containing letters.
  • npm run eslint is clean.
  • Verified manually in Code - OSS on Windows 11: searching () and
    replacing with fontSize: 20 with Preserve Case on now yields
    fontSize: 20. Checked that abcDef still gives def, ABCDef
    gives DEF, and AbcDef gives Def.

buildReplaceStringWithCasePreserved treated a match unchanged by toUpperCase() as uppercase, so matches made only of punctuation or digits caused the replace string to be uppercased. Return the pattern unchanged when the match contains no cased characters. Fixes microsoft#192168
Copilot AI balanced review requested due to automatic review settings September 18, 2026 18:55
@pandeylakshya207-max

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The segmented-match assertions do not exercise the recursive behavior they intend to cover.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Fixes preserve-case replacement for matches containing no cased characters.

Changes:

  • Returns replacement patterns unchanged for caseless matches.
  • Adds regression coverage for punctuation and numeric matches.
File summaries
File Description
src/vs/base/common/search.ts Detects caseless matches before transforming replacement case.
src/vs/editor/contrib/find/test/browser/replacePattern.test.ts Adds caseless-match tests.
Review details

Suppressed comments (1)

src/vs/editor/contrib/find/test/browser/replacePattern.test.ts:262

  • This wrapper assertion also ignores the second array item because only matches[0] drives case preservation, so it duplicates the punctuation case rather than covering separator recursion. Use a mixed cased/caseless segmented match instead.
		assertReplace(['(', ')'], 'fontSize: 20', 'fontSize: 20');
  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

assertReplace(['()'], 'fontSize: 20', 'fontSize: 20');
assertReplace(['123'], 'Def', 'Def');
assertReplace(['!'], 'someValue', 'someValue');
assertReplace(['(', ')'], 'fontSize: 20', 'fontSize: 20');
Comment thread src/vs/base/common/search.ts Outdated
Comment on lines +10 to +11
// The match has no cased characters (e.g. punctuation or digits), so there is
// no case to preserve and the replace pattern is used as-is.
@pandeylakshya207-max

Copy link
Copy Markdown
Author

Thanks — both good catches, addressed in fb2a976.

The ['(', ')'] case only ever read matches[0], so it was a duplicate of the punctuation test rather than covering separator recursion. Replaced it with ['ABC-123']'Def-someValue''DEF-someValue', which exercises the hyphen path with one cased and one caseless segment.

Comment condensed to a single line. All 16 tests in the suite still pass locally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"Preserve case" in search/replace defaults to caps if there is no case

3 participants