Skip to content

Commit

Permalink
Fixes #312
Browse files Browse the repository at this point in the history
  • Loading branch information
alexdima committed Nov 23, 2015
1 parent 203f945 commit 47b7ef5
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 7 deletions.
2 changes: 1 addition & 1 deletion src/vs/editor/contrib/find/browser/find.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {EditorBrowserRegistry} from 'vs/editor/browser/editorBrowserExtensions';
import {CommonEditorRegistry, ContextKey, EditorActionDescriptor} from 'vs/editor/common/editorCommonExtensions';
import {EditorAction, Behaviour} from 'vs/editor/common/editorAction';
import FindWidget = require('./findWidget');
import FindModel = require('./findModel');
import FindModel = require('vs/editor/contrib/find/common/findModel');
import nls = require('vs/nls');
import EventEmitter = require('vs/base/common/eventEmitter');
import EditorBrowser = require('vs/editor/browser/editorBrowser');
Expand Down
2 changes: 1 addition & 1 deletion src/vs/editor/contrib/find/browser/findWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import InputBox = require('vs/base/browser/ui/inputbox/inputBox');
import Findinput = require('vs/base/browser/ui/findinput/findInput');
import EditorBrowser = require('vs/editor/browser/editorBrowser');
import EditorCommon = require('vs/editor/common/editorCommon');
import FindModel = require('./findModel');
import FindModel = require('vs/editor/contrib/find/common/findModel');
import Lifecycle = require('vs/base/common/lifecycle');
import {CommonKeybindings} from 'vs/base/common/keyCodes';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ export class FindModelBoundToEditorModel extends Events.EventEmitter implements
this.findScopeDecorationId = decorations[0];
}

this.addMatchesDecorations(changeAccessor, this.editor.getModel().findMatches(this.searchString, this._getSearchRange(), this.isRegex, this.matchCase, this.wholeWord));
this.addMatchesDecorations(changeAccessor, this._findMatches());
});
this.highlightedDecorationId = null;

Expand Down Expand Up @@ -326,7 +326,6 @@ export class FindModelBoundToEditorModel extends Events.EventEmitter implements
}
}


private setSelectionToDecoration(decorationId:string): void {
this.editor.changeDecorations((changeAccessor: EditorCommon.IModelDecorationsChangeAccessor) => {
if (this.highlightedDecorationId !== null) {
Expand All @@ -346,8 +345,10 @@ export class FindModelBoundToEditorModel extends Events.EventEmitter implements
if (!this.isRegex) {
return this.replaceString;
}
var regexp = Strings.createRegExp(this.searchString, this.isRegex, this.matchCase, this.wholeWord);
return matchedString.replace(regexp, this.replaceString);
let regexp = Strings.createRegExp(this.searchString, this.isRegex, this.matchCase, this.wholeWord);
// Parse the replace string to support that \t or \n mean the right thing
let parsedReplaceString = parseReplaceString(this.replaceString);
return matchedString.replace(regexp, parsedReplaceString);
}

public replace(): void {
Expand Down Expand Up @@ -379,6 +380,10 @@ export class FindModelBoundToEditorModel extends Events.EventEmitter implements
}
}

private _findMatches(limitResultCount?:number): EditorCommon.IEditorRange[] {
return this.editor.getModel().findMatches(this.searchString, this._getSearchRange(), this.isRegex, this.matchCase, this.wholeWord, limitResultCount);
}

public replaceAll(): void {
if (this.decorations.length === 0) {
return;
Expand All @@ -387,7 +392,7 @@ export class FindModelBoundToEditorModel extends Events.EventEmitter implements
let model = this.editor.getModel();

// Get all the ranges (even more than the highlighted ones)
let ranges = this.editor.getModel().findMatches(this.searchString, this._getSearchRange(), this.isRegex, this.matchCase, this.wholeWord, Number.MAX_VALUE);
let ranges = this._findMatches(Number.MAX_VALUE);

// Remove all decorations
this.editor.changeDecorations((changeAccessor:EditorCommon.IModelDecorationsChangeAccessor) => {
Expand Down Expand Up @@ -474,3 +479,64 @@ export class FindModelBoundToEditorModel extends Events.EventEmitter implements

}

const BACKSLASH_CHAR_CODE = '\\'.charCodeAt(0);
const n_CHAR_CODE = 'n'.charCodeAt(0);
const t_CHAR_CODE = 't'.charCodeAt(0);

/**
* \n => LF
* \t => TAB
* \\ => \
* everything else stays untouched
*/
export function parseReplaceString(input:string): string {
if (!input || input.length === 0) {
return input;
}

let substrFrom = 0, result = '';
for (let i = 0, len = input.length; i < len; i++) {
let chCode = input.charCodeAt(i);

if (chCode === BACKSLASH_CHAR_CODE) {

// move to next char
i++;

if (i >= len) {
// string ends with a \
break;
}

let nextChCode = input.charCodeAt(i);
let replaceWithCharacter: string = null;

switch (nextChCode) {
case BACKSLASH_CHAR_CODE:
// \\ => \
replaceWithCharacter = '\\';
break;
case n_CHAR_CODE:
// \n => LF
replaceWithCharacter = '\n';
break;
case t_CHAR_CODE:
// \t => TAB
replaceWithCharacter = '\t';
break;
}

if (replaceWithCharacter) {
result += input.substring(substrFrom, i - 1) + replaceWithCharacter;
substrFrom = i + 1;
}
}
}

if (substrFrom === 0) {
// no replacement occured
return input;
}

return result + input.substring(substrFrom);
}
48 changes: 48 additions & 0 deletions src/vs/editor/contrib/find/test/common/findModel.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';

import assert = require('assert');
import {FindModelBoundToEditorModel, parseReplaceString} from 'vs/editor/contrib/find/common/findModel';

suite('FindModel', () => {

test('parseFindWidgetString', () => {
let testParse = (input:string, expected:string) => {
let actual = parseReplaceString(input);
assert.equal(actual, expected);

let actual2 = parseReplaceString('hello' + input + 'hi');
assert.equal(actual2, 'hello' + expected + 'hi');
};

// no backslash => no treatment
testParse('hello', 'hello');

// \t => TAB
testParse('\\thello', '\thello');

// \n => LF
testParse('\\nhello', '\nhello');

// \\t => \t
testParse('\\\\thello', '\\thello');

// \\\t => \TAB
testParse('\\\\\\thello', '\\\thello');

// \\\\t => \\t
testParse('\\\\\\\\thello', '\\\\thello');

// \ at the end => no treatment
testParse('hello\\', 'hello\\');

// \ with unknown char => no treatment
testParse('hello\\x', 'hello\\x');

// \ with back reference => no treatment
testParse('hello\\0', 'hello\\0');
});
});

0 comments on commit 47b7ef5

Please sign in to comment.