Add preserve case option for buffer replace. References
MODIFY
lib/buffer-search.js
lib/buffer-search.js
const { Point, Range, Emitter, CompositeDisposable, TextBuffer } = require('atom');
const FindOptions = require('./find-options');
const escapeHelper = require('./escape-helper');
+const Util = require('./project/util');
const ResultsMarkerLayersByEditor = new WeakMap;
@@ -92,6 +93,7 @@ class BufferSearch {
if (!markers || markers.length === 0) return;
this.findOptions.set({replacePattern});
+ const preserveCaseOnReplace = atom.config.get('find-and-replace.preserveCaseOnReplace')
this.editor.transact(() => {
let findRegex = null
@@ -104,9 +106,10 @@ class BufferSearch {
for (let i = 0, n = markers.length; i < n; i++) {
const marker = markers[i]
const bufferRange = marker.getBufferRange();
- const replacementText = findRegex ?
+ let replacementText = findRegex ?
this.editor.getTextInBufferRange(bufferRange).replace(findRegex, replacePattern) :
replacePattern;
+ replacementText = preserveCaseOnReplace ? Util.preserveCase(replacementText, this.editor.getTextInBufferRange(bufferRange)): replacementText
this.editor.setTextInBufferRange(bufferRange, replacementText);
marker.destroy();

MODIFY
lib/project/util.coffee
lib/project/util.coffee
@@ -36,7 +36,21 @@ showIf = (condition) ->
else
{display: 'none'}
+capitalize = (str) -> str.replace(/(?:^|\s)\S/g, (capital) -> capital.toUpperCase())
+
+preserveCase = (text, reference) ->
+ # If replaced text is capitalized (strict), capitalize replacement
+ if reference is capitalize(reference.toLowerCase())
+ capitalize(text)
+
+ # If replaced text is uppercase, uppercase replacement
+ else if reference is reference.toUpperCase()
+ text.toUpperCase()
+ else
+ text
+
+
module.exports = {
escapeHtml, escapeRegex, sanitizePattern, getReplacementResultsMessage,
- getSearchResultsMessage, showIf
+ getSearchResultsMessage, showIf, preserveCase
}

MODIFY
package.json
package.json
"default": false,
"title": "Autocomplete Search",
"description": "Autocompletes entries in the find search field."
+ },
+ "preserveCaseOnReplace": {
+ "type": "boolean",
+ "default": false,
+ "title": "Preserve case during replace.",
+ "description": "Keep the replaced text case during replace: replacing 'user' with 'person' will replace 'User' with 'Person' and 'USER' with 'PERSON'."
}
}
}

MODIFY
spec/buffer-search-spec.js
spec/buffer-search-spec.js
@@ -514,3 +514,191 @@ describe("BufferSearch", () => {
})
);
});
+
+describe("BufferSearch", () => {
+ let model, editor, markersListener, currentResultListener;
+
+ beforeEach(() => {
+ editor = new TextEditor();
+ spyOn(editor, 'scanInBufferRange').andCallThrough();
+
+ editor.setText(dedent`
+ -----------
+ aaa bbb ccc
+ ddd Aaa bbb
+ CCC DDD aaa
+ -----------
+ AAA Bbb cCc
+ Ddd Aaa Bbb
+ ccc DDD Aaa
+ -----------
+ `);
+ advanceClock(editor.buffer.stoppedChangingDelay);
+
+ const findOptions = new FindOptions({findPattern: "aaa"});
+ model = new BufferSearch(findOptions);
+
+ markersListener = jasmine.createSpy('markersListener');
+ model.onDidUpdate(markersListener);
+
+ currentResultListener = jasmine.createSpy('currentResultListener');
+ model.onDidChangeCurrentResult(currentResultListener);
+
+ model.setEditor(editor);
+ markersListener.reset();
+ });
+
+ afterEach(() => {
+ model.destroy();
+ editor.destroy();
+ });
+
+
+ describe("when replacing text with preserve case on", () => {
+ beforeEach(() => {
+ atom.config.set('find-and-replace.preserveCaseOnReplace', true)
+ });
+
+ it("preserves case.", () => {
+ model.search("aaa", {
+ caseSensitive: false,
+ useRegex: false,
+ wholeWord: false
+ });
+ const markers = markersListener.mostRecentCall.args[0];
+ markersListener.reset();
+
+ model.replace(markers, "foo");
+
+ expect(editor.getText()).toBe(dedent`
+ -----------
+ foo bbb ccc
+ ddd Foo bbb
+ CCC DDD foo
+ -----------
+ FOO Bbb cCc
+ Ddd Foo Bbb
+ ccc DDD Foo
+ -----------
+ `);
+ });
+
+ it("preserves case using regex search.", () => {
+ model.search("a+", {
+ caseSensitive: false,
+ useRegex: true,
+ wholeWord: false
+ });
+ const markers = markersListener.mostRecentCall.args[0];
+ markersListener.reset();
+
+ model.replace(markers, "foo");
+
+ expect(editor.getText()).toBe(dedent`
+ -----------
+ foo bbb ccc
+ ddd Foo bbb
+ CCC DDD foo
+ -----------
+ FOO Bbb cCc
+ Ddd Foo Bbb
+ ccc DDD Foo
+ -----------
+ `);
+ });
+
+ it("preserves case across words.", () => {
+ model.search("aaa", {
+ caseSensitive: false,
+ useRegex: false,
+ wholeWord: false
+ });
+ const markers = markersListener.mostRecentCall.args[0];
+ markersListener.reset();
+
+ model.replace(markers, "foo bar");
+
+ expect(editor.getText()).toBe(dedent`
+ -----------
+ foo bar bbb ccc
+ ddd Foo Bar bbb
+ CCC DDD foo bar
+ -----------
+ FOO BAR Bbb cCc
+ Ddd Foo Bar Bbb
+ ccc DDD Foo Bar
+ -----------
+ `);
+ });
+
+ it("preserves case only when it's consistent between searched words.", () => {
+ model.search("aaa bbb", {
+ caseSensitive: false,
+ useRegex: false,
+ wholeWord: false
+ });
+ const markers = markersListener.mostRecentCall.args[0];
+ markersListener.reset();
+
+ model.replace(markers, "foo");
+
+ expect(editor.getText()).toBe(dedent`
+ -----------
+ foo ccc
+ ddd foo
+ CCC DDD aaa
+ -----------
+ foo cCc
+ Ddd Foo
+ ccc DDD Aaa
+ -----------
+ `);
+ });
+ it("preserves case and honors caseSensitive option.", () => {
+ model.search("Aaa", {
+ caseSensitive: true,
+ useRegex: false,
+ wholeWord: false
+ });
+ const markers = markersListener.mostRecentCall.args[0];
+ markersListener.reset();
+
+ model.replace(markers, "foo");
+
+ expect(editor.getText()).toBe(dedent`
+ -----------
+ aaa bbb ccc
+ ddd Foo bbb
+ CCC DDD aaa
+ -----------
+ AAA Bbb cCc
+ Ddd Foo Bbb
+ ccc DDD Foo
+ -----------
+ `);
+ });
+ it("preserves case of original replacement when capitalized.", () => {
+ model.search("aaa", {
+ caseSensitive: false,
+ useRegex: false,
+ wholeWord: false
+ });
+ const markers = markersListener.mostRecentCall.args[0];
+ markersListener.reset();
+
+ model.replace(markers, "FoO");
+
+ expect(editor.getText()).toBe(dedent`
+ -----------
+ FoO bbb ccc
+ ddd FoO bbb
+ CCC DDD FoO
+ -----------
+ FOO Bbb cCc
+ Ddd FoO Bbb
+ ccc DDD FoO
+ -----------
+ `);
+ });
+ });
+});

Add preserve case option for buffer replace. References #165.