replacePattern.test.ts 10.4 KB
Newer Older
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as assert from 'assert';
A
Alex Dima 已提交
7
import { ReplacePattern, ReplacePiece, parseReplaceString } from 'vs/editor/contrib/find/replacePattern';
8
import { buildReplaceStringWithCasePreserved } from 'vs/base/common/search';
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101

suite('Replace Pattern test', () => {

	test('parse replace string', () => {
		let testParse = (input: string, expectedPieces: ReplacePiece[]) => {
			let actual = parseReplaceString(input);
			let expected = new ReplacePattern(expectedPieces);
			assert.deepEqual(actual, expected, 'Parsing ' + input);
		};

		// no backslash => no treatment
		testParse('hello', [ReplacePiece.staticValue('hello')]);

		// \t => TAB
		testParse('\\thello', [ReplacePiece.staticValue('\thello')]);
		testParse('h\\tello', [ReplacePiece.staticValue('h\tello')]);
		testParse('hello\\t', [ReplacePiece.staticValue('hello\t')]);

		// \n => LF
		testParse('\\nhello', [ReplacePiece.staticValue('\nhello')]);

		// \\t => \t
		testParse('\\\\thello', [ReplacePiece.staticValue('\\thello')]);
		testParse('h\\\\tello', [ReplacePiece.staticValue('h\\tello')]);
		testParse('hello\\\\t', [ReplacePiece.staticValue('hello\\t')]);

		// \\\t => \TAB
		testParse('\\\\\\thello', [ReplacePiece.staticValue('\\\thello')]);

		// \\\\t => \\t
		testParse('\\\\\\\\thello', [ReplacePiece.staticValue('\\\\thello')]);

		// \ at the end => no treatment
		testParse('hello\\', [ReplacePiece.staticValue('hello\\')]);

		// \ with unknown char => no treatment
		testParse('hello\\x', [ReplacePiece.staticValue('hello\\x')]);

		// \ with back reference => no treatment
		testParse('hello\\0', [ReplacePiece.staticValue('hello\\0')]);

		testParse('hello$&', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(0)]);
		testParse('hello$0', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(0)]);
		testParse('hello$02', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(0), ReplacePiece.staticValue('2')]);
		testParse('hello$1', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(1)]);
		testParse('hello$2', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(2)]);
		testParse('hello$9', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(9)]);
		testParse('$9hello', [ReplacePiece.matchIndex(9), ReplacePiece.staticValue('hello')]);

		testParse('hello$12', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(12)]);
		testParse('hello$99', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(99)]);
		testParse('hello$99a', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(99), ReplacePiece.staticValue('a')]);
		testParse('hello$1a', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(1), ReplacePiece.staticValue('a')]);
		testParse('hello$100', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(10), ReplacePiece.staticValue('0')]);
		testParse('hello$100a', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(10), ReplacePiece.staticValue('0a')]);
		testParse('hello$10a0', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(10), ReplacePiece.staticValue('a0')]);
		testParse('hello$$', [ReplacePiece.staticValue('hello$')]);
		testParse('hello$$0', [ReplacePiece.staticValue('hello$0')]);

		testParse('hello$`', [ReplacePiece.staticValue('hello$`')]);
		testParse('hello$\'', [ReplacePiece.staticValue('hello$\'')]);
	});

	test('replace has JavaScript semantics', () => {
		let testJSReplaceSemantics = (target: string, search: RegExp, replaceString: string, expected: string) => {
			let replacePattern = parseReplaceString(replaceString);
			let m = search.exec(target);
			let actual = replacePattern.buildReplaceString(m);

			assert.deepEqual(actual, expected, `${target}.replace(${search}, ${replaceString})`);
		};

		testJSReplaceSemantics('hi', /hi/, 'hello', 'hi'.replace(/hi/, 'hello'));
		testJSReplaceSemantics('hi', /hi/, '\\t', 'hi'.replace(/hi/, '\t'));
		testJSReplaceSemantics('hi', /hi/, '\\n', 'hi'.replace(/hi/, '\n'));
		testJSReplaceSemantics('hi', /hi/, '\\\\t', 'hi'.replace(/hi/, '\\t'));
		testJSReplaceSemantics('hi', /hi/, '\\\\n', 'hi'.replace(/hi/, '\\n'));

		// implicit capture group 0
		testJSReplaceSemantics('hi', /hi/, 'hello$&', 'hi'.replace(/hi/, 'hello$&'));
		testJSReplaceSemantics('hi', /hi/, 'hello$0', 'hi'.replace(/hi/, 'hello$&'));
		testJSReplaceSemantics('hi', /hi/, 'hello$&1', 'hi'.replace(/hi/, 'hello$&1'));
		testJSReplaceSemantics('hi', /hi/, 'hello$01', 'hi'.replace(/hi/, 'hello$&1'));

		// capture groups have funny semantics in replace strings
		// the replace string interprets $nn as a captured group only if it exists in the search regex
		testJSReplaceSemantics('hi', /(hi)/, 'hello$10', 'hi'.replace(/(hi)/, 'hello$10'));
		testJSReplaceSemantics('hi', /(hi)()()()()()()()()()/, 'hello$10', 'hi'.replace(/(hi)()()()()()()()()()/, 'hello$10'));
		testJSReplaceSemantics('hi', /(hi)/, 'hello$100', 'hi'.replace(/(hi)/, 'hello$100'));
		testJSReplaceSemantics('hi', /(hi)/, 'hello$20', 'hi'.replace(/(hi)/, 'hello$20'));
	});

	test('get replace string if given text is a complete match', () => {
A
Hygiene  
Alex Dima 已提交
102
		function assertReplace(target: string, search: RegExp, replaceString: string, expected: string): void {
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
			let replacePattern = parseReplaceString(replaceString);
			let m = search.exec(target);
			let actual = replacePattern.buildReplaceString(m);

			assert.equal(actual, expected, `${target}.replace(${search}, ${replaceString}) === ${expected}`);
		}

		assertReplace('bla', /bla/, 'hello', 'hello');
		assertReplace('bla', /(bla)/, 'hello', 'hello');
		assertReplace('bla', /(bla)/, 'hello$0', 'hellobla');

		let searchRegex = /let\s+(\w+)\s*=\s*require\s*\(\s*['"]([\w\.\-/]+)\s*['"]\s*\)\s*/;
		assertReplace('let fs = require(\'fs\')', searchRegex, 'import * as $1 from \'$2\';', 'import * as fs from \'fs\';');
		assertReplace('let something = require(\'fs\')', searchRegex, 'import * as $1 from \'$2\';', 'import * as something from \'fs\';');
		assertReplace('let something = require(\'fs\')', searchRegex, 'import * as $1 from \'$1\';', 'import * as something from \'something\';');
		assertReplace('let something = require(\'fs\')', searchRegex, 'import * as $2 from \'$1\';', 'import * as fs from \'something\';');
		assertReplace('let something = require(\'fs\')', searchRegex, 'import * as $0 from \'$0\';', 'import * as let something = require(\'fs\') from \'let something = require(\'fs\')\';');
		assertReplace('let fs = require(\'fs\')', searchRegex, 'import * as $1 from \'$2\';', 'import * as fs from \'fs\';');
		assertReplace('for ()', /for(.*)/, 'cat$1', 'cat ()');

		// issue #18111
		assertReplace('HRESULT OnAmbientPropertyChange(DISPID   dispid);', /\b\s{3}\b/, ' ', ' ');
	});

	test('get replace string if match is sub-string of the text', () => {
A
Hygiene  
Alex Dima 已提交
128
		function assertReplace(target: string, search: RegExp, replaceString: string, expected: string): void {
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
			let replacePattern = parseReplaceString(replaceString);
			let m = search.exec(target);
			let actual = replacePattern.buildReplaceString(m);

			assert.equal(actual, expected, `${target}.replace(${search}, ${replaceString}) === ${expected}`);
		}
		assertReplace('this is a bla text', /bla/, 'hello', 'hello');
		assertReplace('this is a bla text', /this(?=.*bla)/, 'that', 'that');
		assertReplace('this is a bla text', /(th)is(?=.*bla)/, '$1at', 'that');
		assertReplace('this is a bla text', /(th)is(?=.*bla)/, '$1e', 'the');
		assertReplace('this is a bla text', /(th)is(?=.*bla)/, '$1ere', 'there');
		assertReplace('this is a bla text', /(th)is(?=.*bla)/, '$1', 'th');
		assertReplace('this is a bla text', /(th)is(?=.*bla)/, 'ma$1', 'math');
		assertReplace('this is a bla text', /(th)is(?=.*bla)/, 'ma$1s', 'maths');
		assertReplace('this is a bla text', /(th)is(?=.*bla)/, '$0', 'this');
		assertReplace('this is a bla text', /(th)is(?=.*bla)/, '$0$1', 'thisth');
		assertReplace('this is a bla text', /bla(?=\stext$)/, 'foo', 'foo');
		assertReplace('this is a bla text', /b(la)(?=\stext$)/, 'f$1', 'fla');
		assertReplace('this is a bla text', /b(la)(?=\stext$)/, 'f$0', 'fbla');
		assertReplace('this is a bla text', /b(la)(?=\stext$)/, '$0ah', 'blaah');
	});
150 151 152 153 154 155 156

	test('issue #19740 Find and replace capture group/backreference inserts `undefined` instead of empty string', () => {
		let replacePattern = parseReplaceString('a{$1}');
		let matches = /a(z)?/.exec('abcd');
		let actual = replacePattern.buildReplaceString(matches);
		assert.equal(actual, 'a{}');
	});
P
Peng Lyu 已提交
157

158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
	test('buildReplaceStringWithCasePreserved test', () => {
		let replacePattern = 'Def';
		let actual: string | string[] = 'abc';

		assert.equal(buildReplaceStringWithCasePreserved([actual], replacePattern), 'def');
		actual = 'Abc';
		assert.equal(buildReplaceStringWithCasePreserved([actual], replacePattern), 'Def');
		actual = 'ABC';
		assert.equal(buildReplaceStringWithCasePreserved([actual], replacePattern), 'DEF');

		actual = ['abc', 'Abc'];
		assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'def');
		actual = ['Abc', 'abc'];
		assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'Def');
		actual = ['ABC', 'abc'];
		assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'DEF');

		actual = ['AbC'];
		assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'Def');
		actual = ['aBC'];
		assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'Def');
	});

P
Peng Lyu 已提交
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
	test('preserve case', () => {
		let replacePattern = parseReplaceString('Def');
		let actual = replacePattern.buildReplaceString(['abc'], true);
		assert.equal(actual, 'def');
		actual = replacePattern.buildReplaceString(['Abc'], true);
		assert.equal(actual, 'Def');
		actual = replacePattern.buildReplaceString(['ABC'], true);
		assert.equal(actual, 'DEF');

		actual = replacePattern.buildReplaceString(['abc', 'Abc'], true);
		assert.equal(actual, 'def');
		actual = replacePattern.buildReplaceString(['Abc', 'abc'], true);
		assert.equal(actual, 'Def');
		actual = replacePattern.buildReplaceString(['ABC', 'abc'], true);
		assert.equal(actual, 'DEF');

		actual = replacePattern.buildReplaceString(['AbC'], true);
		assert.equal(actual, 'Def');
		actual = replacePattern.buildReplaceString(['aBC'], true);
		assert.equal(actual, 'Def');
	});
202
});