123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- import { buildReplaceStringWithCasePreserved } from '../../../base/common/search.js';
- /**
- * Assigned when the replace pattern is entirely static.
- */
- class StaticValueReplacePattern {
- constructor(staticValue) {
- this.staticValue = staticValue;
- this.kind = 0 /* StaticValue */;
- }
- }
- /**
- * Assigned when the replace pattern has replacement patterns.
- */
- class DynamicPiecesReplacePattern {
- constructor(pieces) {
- this.pieces = pieces;
- this.kind = 1 /* DynamicPieces */;
- }
- }
- export class ReplacePattern {
- constructor(pieces) {
- if (!pieces || pieces.length === 0) {
- this._state = new StaticValueReplacePattern('');
- }
- else if (pieces.length === 1 && pieces[0].staticValue !== null) {
- this._state = new StaticValueReplacePattern(pieces[0].staticValue);
- }
- else {
- this._state = new DynamicPiecesReplacePattern(pieces);
- }
- }
- static fromStaticValue(value) {
- return new ReplacePattern([ReplacePiece.staticValue(value)]);
- }
- get hasReplacementPatterns() {
- return (this._state.kind === 1 /* DynamicPieces */);
- }
- buildReplaceString(matches, preserveCase) {
- if (this._state.kind === 0 /* StaticValue */) {
- if (preserveCase) {
- return buildReplaceStringWithCasePreserved(matches, this._state.staticValue);
- }
- else {
- return this._state.staticValue;
- }
- }
- let result = '';
- for (let i = 0, len = this._state.pieces.length; i < len; i++) {
- let piece = this._state.pieces[i];
- if (piece.staticValue !== null) {
- // static value ReplacePiece
- result += piece.staticValue;
- continue;
- }
- // match index ReplacePiece
- let match = ReplacePattern._substitute(piece.matchIndex, matches);
- if (piece.caseOps !== null && piece.caseOps.length > 0) {
- let repl = [];
- let lenOps = piece.caseOps.length;
- let opIdx = 0;
- for (let idx = 0, len = match.length; idx < len; idx++) {
- if (opIdx >= lenOps) {
- repl.push(match.slice(idx));
- break;
- }
- switch (piece.caseOps[opIdx]) {
- case 'U':
- repl.push(match[idx].toUpperCase());
- break;
- case 'u':
- repl.push(match[idx].toUpperCase());
- opIdx++;
- break;
- case 'L':
- repl.push(match[idx].toLowerCase());
- break;
- case 'l':
- repl.push(match[idx].toLowerCase());
- opIdx++;
- break;
- default:
- repl.push(match[idx]);
- }
- }
- match = repl.join('');
- }
- result += match;
- }
- return result;
- }
- static _substitute(matchIndex, matches) {
- if (matches === null) {
- return '';
- }
- if (matchIndex === 0) {
- return matches[0];
- }
- let remainder = '';
- while (matchIndex > 0) {
- if (matchIndex < matches.length) {
- // A match can be undefined
- let match = (matches[matchIndex] || '');
- return match + remainder;
- }
- remainder = String(matchIndex % 10) + remainder;
- matchIndex = Math.floor(matchIndex / 10);
- }
- return '$' + remainder;
- }
- }
- /**
- * A replace piece can either be a static string or an index to a specific match.
- */
- export class ReplacePiece {
- constructor(staticValue, matchIndex, caseOps) {
- this.staticValue = staticValue;
- this.matchIndex = matchIndex;
- if (!caseOps || caseOps.length === 0) {
- this.caseOps = null;
- }
- else {
- this.caseOps = caseOps.slice(0);
- }
- }
- static staticValue(value) {
- return new ReplacePiece(value, -1, null);
- }
- static caseOps(index, caseOps) {
- return new ReplacePiece(null, index, caseOps);
- }
- }
- class ReplacePieceBuilder {
- constructor(source) {
- this._source = source;
- this._lastCharIndex = 0;
- this._result = [];
- this._resultLen = 0;
- this._currentStaticPiece = '';
- }
- emitUnchanged(toCharIndex) {
- this._emitStatic(this._source.substring(this._lastCharIndex, toCharIndex));
- this._lastCharIndex = toCharIndex;
- }
- emitStatic(value, toCharIndex) {
- this._emitStatic(value);
- this._lastCharIndex = toCharIndex;
- }
- _emitStatic(value) {
- if (value.length === 0) {
- return;
- }
- this._currentStaticPiece += value;
- }
- emitMatchIndex(index, toCharIndex, caseOps) {
- if (this._currentStaticPiece.length !== 0) {
- this._result[this._resultLen++] = ReplacePiece.staticValue(this._currentStaticPiece);
- this._currentStaticPiece = '';
- }
- this._result[this._resultLen++] = ReplacePiece.caseOps(index, caseOps);
- this._lastCharIndex = toCharIndex;
- }
- finalize() {
- this.emitUnchanged(this._source.length);
- if (this._currentStaticPiece.length !== 0) {
- this._result[this._resultLen++] = ReplacePiece.staticValue(this._currentStaticPiece);
- this._currentStaticPiece = '';
- }
- return new ReplacePattern(this._result);
- }
- }
- /**
- * \n => inserts a LF
- * \t => inserts a TAB
- * \\ => inserts a "\".
- * \u => upper-cases one character in a match.
- * \U => upper-cases ALL remaining characters in a match.
- * \l => lower-cases one character in a match.
- * \L => lower-cases ALL remaining characters in a match.
- * $$ => inserts a "$".
- * $& and $0 => inserts the matched substring.
- * $n => Where n is a non-negative integer lesser than 100, inserts the nth parenthesized submatch string
- * everything else stays untouched
- *
- * Also see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_string_as_a_parameter
- */
- export function parseReplaceString(replaceString) {
- if (!replaceString || replaceString.length === 0) {
- return new ReplacePattern(null);
- }
- let caseOps = [];
- let result = new ReplacePieceBuilder(replaceString);
- for (let i = 0, len = replaceString.length; i < len; i++) {
- let chCode = replaceString.charCodeAt(i);
- if (chCode === 92 /* Backslash */) {
- // move to next char
- i++;
- if (i >= len) {
- // string ends with a \
- break;
- }
- let nextChCode = replaceString.charCodeAt(i);
- // let replaceWithCharacter: string | null = null;
- switch (nextChCode) {
- case 92 /* Backslash */:
- // \\ => inserts a "\"
- result.emitUnchanged(i - 1);
- result.emitStatic('\\', i + 1);
- break;
- case 110 /* n */:
- // \n => inserts a LF
- result.emitUnchanged(i - 1);
- result.emitStatic('\n', i + 1);
- break;
- case 116 /* t */:
- // \t => inserts a TAB
- result.emitUnchanged(i - 1);
- result.emitStatic('\t', i + 1);
- break;
- // Case modification of string replacements, patterned after Boost, but only applied
- // to the replacement text, not subsequent content.
- case 117 /* u */:
- // \u => upper-cases one character.
- case 85 /* U */:
- // \U => upper-cases ALL following characters.
- case 108 /* l */:
- // \l => lower-cases one character.
- case 76 /* L */:
- // \L => lower-cases ALL following characters.
- result.emitUnchanged(i - 1);
- result.emitStatic('', i + 1);
- caseOps.push(String.fromCharCode(nextChCode));
- break;
- }
- continue;
- }
- if (chCode === 36 /* DollarSign */) {
- // move to next char
- i++;
- if (i >= len) {
- // string ends with a $
- break;
- }
- let nextChCode = replaceString.charCodeAt(i);
- if (nextChCode === 36 /* DollarSign */) {
- // $$ => inserts a "$"
- result.emitUnchanged(i - 1);
- result.emitStatic('$', i + 1);
- continue;
- }
- if (nextChCode === 48 /* Digit0 */ || nextChCode === 38 /* Ampersand */) {
- // $& and $0 => inserts the matched substring.
- result.emitUnchanged(i - 1);
- result.emitMatchIndex(0, i + 1, caseOps);
- caseOps.length = 0;
- continue;
- }
- if (49 /* Digit1 */ <= nextChCode && nextChCode <= 57 /* Digit9 */) {
- // $n
- let matchIndex = nextChCode - 48 /* Digit0 */;
- // peek next char to probe for $nn
- if (i + 1 < len) {
- let nextNextChCode = replaceString.charCodeAt(i + 1);
- if (48 /* Digit0 */ <= nextNextChCode && nextNextChCode <= 57 /* Digit9 */) {
- // $nn
- // move to next char
- i++;
- matchIndex = matchIndex * 10 + (nextNextChCode - 48 /* Digit0 */);
- result.emitUnchanged(i - 2);
- result.emitMatchIndex(matchIndex, i + 1, caseOps);
- caseOps.length = 0;
- continue;
- }
- }
- result.emitUnchanged(i - 1);
- result.emitMatchIndex(matchIndex, i + 1, caseOps);
- caseOps.length = 0;
- continue;
- }
- }
- }
- return result.finalize();
- }
|