mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-09 19:11:28 -05:00
TextModelSearch can return captured groups
This commit is contained in:
@@ -1814,13 +1814,15 @@ export interface ITextModel {
|
||||
export class FindMatch {
|
||||
_findMatchBrand: void;
|
||||
|
||||
public readonly captures: Range[];
|
||||
public readonly range: Range;
|
||||
public readonly matches: string[];
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor(captures: Range[]) {
|
||||
this.captures = captures;
|
||||
constructor(range: Range, matches: string[]) {
|
||||
this.range = range;
|
||||
this.matches = matches;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -842,17 +842,19 @@ export class TextModel extends OrderGuaranteeEventEmitter implements editorCommo
|
||||
searchRange = this.getFullModelRange();
|
||||
}
|
||||
|
||||
return TextModelSearch.findMatches(this, new SearchParams(searchString, isRegex, matchCase, wholeWord), searchRange, limitResultCount);
|
||||
return TextModelSearch.findMatches(this, new SearchParams(searchString, isRegex, matchCase, wholeWord), searchRange, false, limitResultCount).map(e => e.range);
|
||||
}
|
||||
|
||||
public findNextMatch(searchString: string, rawSearchStart: editorCommon.IPosition, isRegex: boolean, matchCase: boolean, wholeWord: boolean): Range {
|
||||
this._assertNotDisposed();
|
||||
return TextModelSearch.findNextMatch(this, new SearchParams(searchString, isRegex, matchCase, wholeWord), rawSearchStart);
|
||||
let r = TextModelSearch.findNextMatch(this, new SearchParams(searchString, isRegex, matchCase, wholeWord), rawSearchStart, false);
|
||||
return r ? r.range : null;
|
||||
}
|
||||
|
||||
public findPreviousMatch(searchString: string, rawSearchStart: editorCommon.IPosition, isRegex: boolean, matchCase: boolean, wholeWord: boolean): Range {
|
||||
this._assertNotDisposed();
|
||||
return TextModelSearch.findPreviousMatch(this, new SearchParams(searchString, isRegex, matchCase, wholeWord), rawSearchStart);
|
||||
let r = TextModelSearch.findPreviousMatch(this, new SearchParams(searchString, isRegex, matchCase, wholeWord), rawSearchStart, false);
|
||||
return r ? r.range : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IPosition } from 'vs/editor/common/editorCommon';
|
||||
import { IPosition, FindMatch } from 'vs/editor/common/editorCommon';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
|
||||
@@ -87,25 +87,36 @@ export class SearchParams {
|
||||
}
|
||||
}
|
||||
|
||||
function createFindMatch(range: Range, rawMatches: RegExpExecArray, captureMatches: boolean): FindMatch {
|
||||
if (!captureMatches) {
|
||||
return new FindMatch(range, null);
|
||||
}
|
||||
let matches: string[] = [];
|
||||
for (let i = 0, len = rawMatches.length; i < len; i++) {
|
||||
matches[i] = rawMatches[i];
|
||||
}
|
||||
return new FindMatch(range, matches);
|
||||
}
|
||||
|
||||
export class TextModelSearch {
|
||||
|
||||
public static findMatches(model: TextModel, searchParams: SearchParams, searchRange: Range, limitResultCount: number): Range[] {
|
||||
public static findMatches(model: TextModel, searchParams: SearchParams, searchRange: Range, captureMatches: boolean, limitResultCount: number): FindMatch[] {
|
||||
const regex = searchParams.parseSearchRequest();
|
||||
if (!regex) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (regex.multiline) {
|
||||
return this._doFindMatchesMultiline(model, searchRange, regex, limitResultCount);
|
||||
return this._doFindMatchesMultiline(model, searchRange, regex, captureMatches, limitResultCount);
|
||||
}
|
||||
return this._doFindMatchesLineByLine(model, searchRange, regex, limitResultCount);
|
||||
return this._doFindMatchesLineByLine(model, searchRange, regex, captureMatches, limitResultCount);
|
||||
}
|
||||
|
||||
private static _doFindMatchesMultiline(model: TextModel, searchRange: Range, searchRegex: RegExp, limitResultCount: number): Range[] {
|
||||
private static _doFindMatchesMultiline(model: TextModel, searchRange: Range, searchRegex: RegExp, captureMatches: boolean, limitResultCount: number): FindMatch[] {
|
||||
const deltaOffset = model.getOffsetAt(searchRange.getStartPosition());
|
||||
const text = model.getValueInRange(searchRange);
|
||||
|
||||
const result: Range[] = [];
|
||||
const result: FindMatch[] = [];
|
||||
let prevStartOffset = 0;
|
||||
let prevEndOffset = 0;
|
||||
let counter = 0;
|
||||
@@ -123,7 +134,11 @@ export class TextModelSearch {
|
||||
const startPosition = model.getPositionAt(startOffset);
|
||||
const endPosition = model.getPositionAt(endOffset);
|
||||
|
||||
result[counter++] = new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column);
|
||||
result[counter++] = createFindMatch(
|
||||
new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column),
|
||||
m,
|
||||
captureMatches
|
||||
);
|
||||
if (counter >= limitResultCount) {
|
||||
return result;
|
||||
}
|
||||
@@ -135,171 +150,36 @@ export class TextModelSearch {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static _doFindMatchesLineByLine(model: TextModel, searchRange: Range, searchRegex: RegExp, limitResultCount: number): Range[] {
|
||||
const result: Range[] = [];
|
||||
private static _doFindMatchesLineByLine(model: TextModel, searchRange: Range, searchRegex: RegExp, captureMatches: boolean, limitResultCount: number): FindMatch[] {
|
||||
const result: FindMatch[] = [];
|
||||
let counter = 0;
|
||||
|
||||
// Early case for a search range that starts & stops on the same line number
|
||||
if (searchRange.startLineNumber === searchRange.endLineNumber) {
|
||||
const text = model.getLineContent(searchRange.startLineNumber).substring(searchRange.startColumn - 1, searchRange.endColumn - 1);
|
||||
counter = this._findMatchesInLine(searchRegex, text, searchRange.startLineNumber, searchRange.startColumn - 1, counter, result, limitResultCount);
|
||||
counter = this._findMatchesInLine(searchRegex, text, searchRange.startLineNumber, searchRange.startColumn - 1, counter, result, captureMatches, limitResultCount);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Collect results from first line
|
||||
const text = model.getLineContent(searchRange.startLineNumber).substring(searchRange.startColumn - 1);
|
||||
counter = this._findMatchesInLine(searchRegex, text, searchRange.startLineNumber, searchRange.startColumn - 1, counter, result, limitResultCount);
|
||||
counter = this._findMatchesInLine(searchRegex, text, searchRange.startLineNumber, searchRange.startColumn - 1, counter, result, captureMatches, limitResultCount);
|
||||
|
||||
// Collect results from middle lines
|
||||
for (let lineNumber = searchRange.startLineNumber + 1; lineNumber < searchRange.endLineNumber && counter < limitResultCount; lineNumber++) {
|
||||
counter = this._findMatchesInLine(searchRegex, model.getLineContent(lineNumber), lineNumber, 0, counter, result, limitResultCount);
|
||||
counter = this._findMatchesInLine(searchRegex, model.getLineContent(lineNumber), lineNumber, 0, counter, result, captureMatches, limitResultCount);
|
||||
}
|
||||
|
||||
// Collect results from last line
|
||||
if (counter < limitResultCount) {
|
||||
const text = model.getLineContent(searchRange.endLineNumber).substring(0, searchRange.endColumn - 1);
|
||||
counter = this._findMatchesInLine(searchRegex, text, searchRange.endLineNumber, 0, counter, result, limitResultCount);
|
||||
counter = this._findMatchesInLine(searchRegex, text, searchRange.endLineNumber, 0, counter, result, captureMatches, limitResultCount);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static findNextMatch(model: TextModel, searchParams: SearchParams, rawSearchStart: IPosition): Range {
|
||||
const regex = searchParams.parseSearchRequest();
|
||||
if (!regex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const searchStart = model.validatePosition(rawSearchStart);
|
||||
if (regex.multiline) {
|
||||
return this._doFindNextMatchMultiline(model, searchStart, regex);
|
||||
}
|
||||
return this._doFindNextMatchLineByLine(model, searchStart, regex);
|
||||
|
||||
}
|
||||
|
||||
private static _doFindNextMatchMultiline(model: TextModel, searchStart: Position, searchRegex: RegExp): Range {
|
||||
const searchTextStart: IPosition = { lineNumber: searchStart.lineNumber, column: 1 };
|
||||
const deltaOffset = model.getOffsetAt(searchTextStart);
|
||||
const lineCount = model.getLineCount();
|
||||
const text = model.getValueInRange(new Range(searchTextStart.lineNumber, searchTextStart.column, lineCount, model.getLineMaxColumn(lineCount)));
|
||||
searchRegex.lastIndex = searchStart.column - 1;
|
||||
let m = searchRegex.exec(text);
|
||||
if (m) {
|
||||
const startOffset = deltaOffset + m.index;
|
||||
const endOffset = startOffset + m[0].length;
|
||||
const startPosition = model.getPositionAt(startOffset);
|
||||
const endPosition = model.getPositionAt(endOffset);
|
||||
return new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column);
|
||||
}
|
||||
|
||||
if (searchStart.lineNumber !== 1 || searchStart.column !== -1) {
|
||||
// Try again from the top
|
||||
return this._doFindNextMatchMultiline(model, new Position(1, 1), searchRegex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static _doFindNextMatchLineByLine(model: TextModel, searchStart: Position, searchRegex: RegExp): Range {
|
||||
const lineCount = model.getLineCount();
|
||||
const startLineNumber = searchStart.lineNumber;
|
||||
|
||||
// Look in first line
|
||||
const text = model.getLineContent(startLineNumber);
|
||||
const r = this._findFirstMatchInLine(searchRegex, text, startLineNumber, searchStart.column);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
for (let i = 1; i <= lineCount; i++) {
|
||||
const lineIndex = (startLineNumber + i - 1) % lineCount;
|
||||
const text = model.getLineContent(lineIndex + 1);
|
||||
const r = this._findFirstMatchInLine(searchRegex, text, lineIndex + 1, 1);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static findPreviousMatch(model: TextModel, searchParams: SearchParams, rawSearchStart: IPosition): Range {
|
||||
const regex = searchParams.parseSearchRequest();
|
||||
if (!regex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const searchStart = model.validatePosition(rawSearchStart);
|
||||
if (regex.multiline) {
|
||||
return this._doFindPreviousMatchMultiline(model, searchStart, regex);
|
||||
}
|
||||
return this._doFindPreviousMatchLineByLine(model, searchStart, regex);
|
||||
}
|
||||
|
||||
private static _doFindPreviousMatchMultiline(model: TextModel, searchStart: Position, searchRegex: RegExp): Range {
|
||||
const matches = this._doFindMatchesMultiline(model, new Range(1, 1, searchStart.lineNumber, searchStart.column), searchRegex, 10 * LIMIT_FIND_COUNT);
|
||||
if (matches.length > 0) {
|
||||
return matches[matches.length - 1];
|
||||
}
|
||||
|
||||
const lineCount = model.getLineCount();
|
||||
if (searchStart.lineNumber !== lineCount || searchStart.column !== model.getLineMaxColumn(lineCount)) {
|
||||
// Try again with all content
|
||||
return this._doFindPreviousMatchMultiline(model, new Position(lineCount, model.getLineMaxColumn(lineCount)), searchRegex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static _doFindPreviousMatchLineByLine(model: TextModel, searchStart: Position, searchRegex: RegExp): Range {
|
||||
const lineCount = model.getLineCount();
|
||||
const startLineNumber = searchStart.lineNumber;
|
||||
|
||||
// Look in first line
|
||||
const text = model.getLineContent(startLineNumber).substring(0, searchStart.column - 1);
|
||||
const r = this._findLastMatchInLine(searchRegex, text, startLineNumber);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
for (let i = 1; i <= lineCount; i++) {
|
||||
const lineIndex = (lineCount + startLineNumber - i - 1) % lineCount;
|
||||
const text = model.getLineContent(lineIndex + 1);
|
||||
const r = this._findLastMatchInLine(searchRegex, text, lineIndex + 1);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static _findFirstMatchInLine(searchRegex: RegExp, text: string, lineNumber: number, fromColumn: number): Range {
|
||||
// Set regex to search from column
|
||||
searchRegex.lastIndex = fromColumn - 1;
|
||||
const m: RegExpExecArray = searchRegex.exec(text);
|
||||
return m ? new Range(lineNumber, m.index + 1, lineNumber, m.index + 1 + m[0].length) : null;
|
||||
}
|
||||
|
||||
private static _findLastMatchInLine(searchRegex: RegExp, text: string, lineNumber: number): Range {
|
||||
let bestResult: Range = null;
|
||||
let m: RegExpExecArray;
|
||||
while ((m = searchRegex.exec(text))) {
|
||||
const result = new Range(lineNumber, m.index + 1, lineNumber, m.index + 1 + m[0].length);
|
||||
if (result.equalsRange(bestResult)) {
|
||||
break;
|
||||
}
|
||||
bestResult = result;
|
||||
if (m.index + m[0].length === text.length) {
|
||||
// Reached the end of the line
|
||||
break;
|
||||
}
|
||||
}
|
||||
return bestResult;
|
||||
}
|
||||
|
||||
private static _findMatchesInLine(searchRegex: RegExp, text: string, lineNumber: number, deltaOffset: number, counter: number, result: Range[], limitResultCount: number): number {
|
||||
private static _findMatchesInLine(searchRegex: RegExp, text: string, lineNumber: number, deltaOffset: number, counter: number, result: FindMatch[], captureMatches: boolean, limitResultCount: number): number {
|
||||
let m: RegExpExecArray;
|
||||
// Reset regex to search from the beginning
|
||||
searchRegex.lastIndex = 0;
|
||||
@@ -307,11 +187,11 @@ export class TextModelSearch {
|
||||
m = searchRegex.exec(text);
|
||||
if (m) {
|
||||
const range = new Range(lineNumber, m.index + 1 + deltaOffset, lineNumber, m.index + 1 + m[0].length + deltaOffset);
|
||||
if (range.equalsRange(result[result.length - 1])) {
|
||||
if (result.length > 0 && range.equalsRange(result[result.length - 1].range)) {
|
||||
// Exit early if the regex matches the same range
|
||||
return counter;
|
||||
}
|
||||
result.push(range);
|
||||
result.push(createFindMatch(range, m, captureMatches));
|
||||
counter++;
|
||||
if (counter >= limitResultCount) {
|
||||
return counter;
|
||||
@@ -324,4 +204,149 @@ export class TextModelSearch {
|
||||
} while (m);
|
||||
return counter;
|
||||
}
|
||||
}
|
||||
|
||||
public static findNextMatch(model: TextModel, searchParams: SearchParams, rawSearchStart: IPosition, captureMatches: boolean): FindMatch {
|
||||
const regex = searchParams.parseSearchRequest();
|
||||
if (!regex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const searchStart = model.validatePosition(rawSearchStart);
|
||||
if (regex.multiline) {
|
||||
return this._doFindNextMatchMultiline(model, searchStart, regex, captureMatches);
|
||||
}
|
||||
return this._doFindNextMatchLineByLine(model, searchStart, regex, captureMatches);
|
||||
}
|
||||
|
||||
private static _doFindNextMatchMultiline(model: TextModel, searchStart: Position, searchRegex: RegExp, captureMatches: boolean): FindMatch {
|
||||
const searchTextStart: IPosition = { lineNumber: searchStart.lineNumber, column: 1 };
|
||||
const deltaOffset = model.getOffsetAt(searchTextStart);
|
||||
const lineCount = model.getLineCount();
|
||||
const text = model.getValueInRange(new Range(searchTextStart.lineNumber, searchTextStart.column, lineCount, model.getLineMaxColumn(lineCount)));
|
||||
searchRegex.lastIndex = searchStart.column - 1;
|
||||
let m = searchRegex.exec(text);
|
||||
if (m) {
|
||||
const startOffset = deltaOffset + m.index;
|
||||
const endOffset = startOffset + m[0].length;
|
||||
const startPosition = model.getPositionAt(startOffset);
|
||||
const endPosition = model.getPositionAt(endOffset);
|
||||
return createFindMatch(
|
||||
new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column),
|
||||
m,
|
||||
captureMatches
|
||||
);
|
||||
}
|
||||
|
||||
if (searchStart.lineNumber !== 1 || searchStart.column !== -1) {
|
||||
// Try again from the top
|
||||
return this._doFindNextMatchMultiline(model, new Position(1, 1), searchRegex, captureMatches);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static _doFindNextMatchLineByLine(model: TextModel, searchStart: Position, searchRegex: RegExp, captureMatches: boolean): FindMatch {
|
||||
const lineCount = model.getLineCount();
|
||||
const startLineNumber = searchStart.lineNumber;
|
||||
|
||||
// Look in first line
|
||||
const text = model.getLineContent(startLineNumber);
|
||||
const r = this._findFirstMatchInLine(searchRegex, text, startLineNumber, searchStart.column, captureMatches);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
for (let i = 1; i <= lineCount; i++) {
|
||||
const lineIndex = (startLineNumber + i - 1) % lineCount;
|
||||
const text = model.getLineContent(lineIndex + 1);
|
||||
const r = this._findFirstMatchInLine(searchRegex, text, lineIndex + 1, 1, captureMatches);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static _findFirstMatchInLine(searchRegex: RegExp, text: string, lineNumber: number, fromColumn: number, captureMatches: boolean): FindMatch {
|
||||
// Set regex to search from column
|
||||
searchRegex.lastIndex = fromColumn - 1;
|
||||
const m: RegExpExecArray = searchRegex.exec(text);
|
||||
if (m) {
|
||||
return createFindMatch(
|
||||
new Range(lineNumber, m.index + 1, lineNumber, m.index + 1 + m[0].length),
|
||||
m,
|
||||
captureMatches
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static findPreviousMatch(model: TextModel, searchParams: SearchParams, rawSearchStart: IPosition, captureMatches: boolean): FindMatch {
|
||||
const regex = searchParams.parseSearchRequest();
|
||||
if (!regex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const searchStart = model.validatePosition(rawSearchStart);
|
||||
if (regex.multiline) {
|
||||
return this._doFindPreviousMatchMultiline(model, searchStart, regex, captureMatches);
|
||||
}
|
||||
return this._doFindPreviousMatchLineByLine(model, searchStart, regex, captureMatches);
|
||||
}
|
||||
|
||||
private static _doFindPreviousMatchMultiline(model: TextModel, searchStart: Position, searchRegex: RegExp, captureMatches: boolean): FindMatch {
|
||||
const matches = this._doFindMatchesMultiline(model, new Range(1, 1, searchStart.lineNumber, searchStart.column), searchRegex, captureMatches, 10 * LIMIT_FIND_COUNT);
|
||||
if (matches.length > 0) {
|
||||
return matches[matches.length - 1];
|
||||
}
|
||||
|
||||
const lineCount = model.getLineCount();
|
||||
if (searchStart.lineNumber !== lineCount || searchStart.column !== model.getLineMaxColumn(lineCount)) {
|
||||
// Try again with all content
|
||||
return this._doFindPreviousMatchMultiline(model, new Position(lineCount, model.getLineMaxColumn(lineCount)), searchRegex, captureMatches);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static _doFindPreviousMatchLineByLine(model: TextModel, searchStart: Position, searchRegex: RegExp, captureMatches: boolean): FindMatch {
|
||||
const lineCount = model.getLineCount();
|
||||
const startLineNumber = searchStart.lineNumber;
|
||||
|
||||
// Look in first line
|
||||
const text = model.getLineContent(startLineNumber).substring(0, searchStart.column - 1);
|
||||
const r = this._findLastMatchInLine(searchRegex, text, startLineNumber, captureMatches);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
for (let i = 1; i <= lineCount; i++) {
|
||||
const lineIndex = (lineCount + startLineNumber - i - 1) % lineCount;
|
||||
const text = model.getLineContent(lineIndex + 1);
|
||||
const r = this._findLastMatchInLine(searchRegex, text, lineIndex + 1, captureMatches);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static _findLastMatchInLine(searchRegex: RegExp, text: string, lineNumber: number, captureMatches: boolean): FindMatch {
|
||||
let bestResult: FindMatch = null;
|
||||
let m: RegExpExecArray;
|
||||
while ((m = searchRegex.exec(text))) {
|
||||
const result = new Range(lineNumber, m.index + 1, lineNumber, m.index + 1 + m[0].length);
|
||||
if (bestResult && result.equalsRange(bestResult.range)) {
|
||||
break;
|
||||
}
|
||||
bestResult = createFindMatch(result, m, captureMatches);
|
||||
if (m.index + m[0].length === text.length) {
|
||||
// Reached the end of the line
|
||||
break;
|
||||
}
|
||||
}
|
||||
return bestResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { FindMatch } from 'vs/editor/common/editorCommon';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { TextModelSearch, SearchParams } from 'vs/editor/common/model/textModelSearch';
|
||||
@@ -13,36 +14,38 @@ import { TextModelSearch, SearchParams } from 'vs/editor/common/model/textModelS
|
||||
// --------- Find
|
||||
suite('TextModelSearch', () => {
|
||||
|
||||
function toArrRange(r: Range): [number, number, number, number] {
|
||||
return [r.startLineNumber, r.startColumn, r.endLineNumber, r.endColumn];
|
||||
function assertFindMatch(actual: FindMatch, expectedRange: Range, expectedMatches: string[] = null): void {
|
||||
assert.deepEqual(actual, new FindMatch(expectedRange, expectedMatches));
|
||||
}
|
||||
|
||||
function assertFindMatches(text: string, searchString: string, isRegex: boolean, matchCase: boolean, wholeWord: boolean, expected: [number, number, number, number][]): void {
|
||||
function assertFindMatches(text: string, searchString: string, isRegex: boolean, matchCase: boolean, wholeWord: boolean, _expected: [number, number, number, number][]): void {
|
||||
let expectedRanges = _expected.map(entry => new Range(entry[0], entry[1], entry[2], entry[3]));
|
||||
let expectedMatches = expectedRanges.map(entry => new FindMatch(entry, null));
|
||||
let model = new TextModel([], TextModel.toRawText(text, TextModel.DEFAULT_CREATION_OPTIONS));
|
||||
|
||||
let actualRanges = model.findMatches(searchString, false, isRegex, matchCase, wholeWord);
|
||||
let actual = actualRanges.map(toArrRange);
|
||||
let searchParams = new SearchParams(searchString, isRegex, matchCase, wholeWord);
|
||||
|
||||
assert.deepEqual(actual, expected, 'findMatches OK');
|
||||
let actual = TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), false, 1000);
|
||||
assert.deepEqual(actual, expectedMatches, 'findMatches OK');
|
||||
|
||||
// test `findNextMatch`
|
||||
let startPos = new Position(1, 1);
|
||||
let match = TextModelSearch.findNextMatch(model, new SearchParams(searchString, isRegex, matchCase, wholeWord), startPos);
|
||||
assert.deepEqual(toArrRange(match), expected[0], `findNextMatch ${startPos}`);
|
||||
for (let i = 0; i < expected.length; i++) {
|
||||
startPos = new Position(expected[i][0], expected[i][1]);
|
||||
match = TextModelSearch.findNextMatch(model, new SearchParams(searchString, isRegex, matchCase, wholeWord), startPos);
|
||||
assert.deepEqual(toArrRange(match), expected[i], `findNextMatch ${startPos}`);
|
||||
let match = TextModelSearch.findNextMatch(model, searchParams, startPos, false);
|
||||
assert.deepEqual(match, expectedMatches[0], `findNextMatch ${startPos}`);
|
||||
for (let i = 0; i < expectedMatches.length; i++) {
|
||||
startPos = expectedMatches[i].range.getStartPosition();;
|
||||
match = TextModelSearch.findNextMatch(model, searchParams, startPos, false);
|
||||
assert.deepEqual(match, expectedMatches[i], `findNextMatch ${startPos}`);
|
||||
}
|
||||
|
||||
// test `findPrevMatch`
|
||||
startPos = new Position(model.getLineCount(), model.getLineMaxColumn(model.getLineCount()));
|
||||
match = TextModelSearch.findPreviousMatch(model, new SearchParams(searchString, isRegex, matchCase, wholeWord), startPos);
|
||||
assert.deepEqual(toArrRange(match), expected[expected.length - 1], `findPrevMatch ${startPos}`);
|
||||
for (let i = 0; i < expected.length; i++) {
|
||||
startPos = new Position(expected[i][2], expected[i][3]);
|
||||
match = TextModelSearch.findPreviousMatch(model, new SearchParams(searchString, isRegex, matchCase, wholeWord), startPos);
|
||||
assert.deepEqual(toArrRange(match), expected[i], `findPrevMatch ${startPos}`);
|
||||
match = TextModelSearch.findPreviousMatch(model, searchParams, startPos, false);
|
||||
assert.deepEqual(match, expectedMatches[expectedMatches.length - 1], `findPrevMatch ${startPos}`);
|
||||
for (let i = 0; i < expectedMatches.length; i++) {
|
||||
startPos = expectedMatches[i].range.getEndPosition();
|
||||
match = TextModelSearch.findPreviousMatch(model, searchParams, startPos, false);
|
||||
assert.deepEqual(match, expectedMatches[i], `findPrevMatch ${startPos}`);
|
||||
}
|
||||
|
||||
model.dispose();
|
||||
@@ -327,20 +330,20 @@ suite('TextModelSearch', () => {
|
||||
|
||||
let searchParams = new SearchParams('line', false, false, false);
|
||||
|
||||
let actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 1 });
|
||||
assert.equal(new Range(1, 1, 1, 5).toString(), actual.toString());
|
||||
let actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 1 }, false);
|
||||
assertFindMatch(actual, new Range(1, 1, 1, 5));
|
||||
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
|
||||
assert.equal(new Range(1, 6, 1, 10).toString(), actual.toString());
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
|
||||
assertFindMatch(actual, new Range(1, 6, 1, 10));
|
||||
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 3 });
|
||||
assert.equal(new Range(1, 6, 1, 10).toString(), actual.toString());
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 3 }, false);
|
||||
assertFindMatch(actual, new Range(1, 6, 1, 10));
|
||||
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
|
||||
assert.equal(new Range(2, 1, 2, 5).toString(), actual.toString());
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
|
||||
assertFindMatch(actual, new Range(2, 1, 2, 5));
|
||||
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
|
||||
assert.equal(new Range(1, 1, 1, 5).toString(), actual.toString());
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
|
||||
assertFindMatch(actual, new Range(1, 1, 1, 5));
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
@@ -350,17 +353,17 @@ suite('TextModelSearch', () => {
|
||||
|
||||
let searchParams = new SearchParams('^line', true, false, false);
|
||||
|
||||
let actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 1 });
|
||||
assert.equal(new Range(1, 1, 1, 5).toString(), actual.toString());
|
||||
let actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 1 }, false);
|
||||
assertFindMatch(actual, new Range(1, 1, 1, 5));
|
||||
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
|
||||
assert.equal(new Range(2, 1, 2, 5).toString(), actual.toString());
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
|
||||
assertFindMatch(actual, new Range(2, 1, 2, 5));
|
||||
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 3 });
|
||||
assert.equal(new Range(2, 1, 2, 5).toString(), actual.toString());
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 3 }, false);
|
||||
assertFindMatch(actual, new Range(2, 1, 2, 5));
|
||||
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
|
||||
assert.equal(new Range(1, 1, 1, 5).toString(), actual.toString());
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
|
||||
assertFindMatch(actual, new Range(1, 1, 1, 5));
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
@@ -370,17 +373,17 @@ suite('TextModelSearch', () => {
|
||||
|
||||
let searchParams = new SearchParams('^line', true, false, false);
|
||||
|
||||
let actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 1 });
|
||||
assert.equal(new Range(1, 1, 1, 5).toString(), actual.toString());
|
||||
let actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 1 }, false);
|
||||
assertFindMatch(actual, new Range(1, 1, 1, 5));
|
||||
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
|
||||
assert.equal(new Range(2, 1, 2, 5).toString(), actual.toString());
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
|
||||
assertFindMatch(actual, new Range(2, 1, 2, 5));
|
||||
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 3 });
|
||||
assert.equal(new Range(2, 1, 2, 5).toString(), actual.toString());
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 3 }, false);
|
||||
assertFindMatch(actual, new Range(2, 1, 2, 5));
|
||||
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
|
||||
assert.equal(new Range(1, 1, 1, 5).toString(), actual.toString());
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
|
||||
assertFindMatch(actual, new Range(1, 1, 1, 5));
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
@@ -390,14 +393,14 @@ suite('TextModelSearch', () => {
|
||||
|
||||
let searchParams = new SearchParams('^line.*\\nline', true, false, false);
|
||||
|
||||
let actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 1 });
|
||||
assert.equal(new Range(1, 1, 2, 5).toString(), actual.toString());
|
||||
let actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 1 }, false);
|
||||
assertFindMatch(actual, new Range(1, 1, 2, 5));
|
||||
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
|
||||
assert.equal(new Range(3, 1, 4, 5).toString(), actual.toString());
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
|
||||
assertFindMatch(actual, new Range(3, 1, 4, 5));
|
||||
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 2, column: 1 });
|
||||
assert.equal(new Range(2, 1, 3, 5).toString(), actual.toString());
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 2, column: 1 }, false);
|
||||
assertFindMatch(actual, new Range(2, 1, 3, 5));
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
@@ -407,17 +410,90 @@ suite('TextModelSearch', () => {
|
||||
|
||||
let searchParams = new SearchParams('line$', true, false, false);
|
||||
|
||||
let actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 1 });
|
||||
assert.equal(new Range(1, 10, 1, 14).toString(), actual.toString());
|
||||
let actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 1 }, false);
|
||||
assertFindMatch(actual, new Range(1, 10, 1, 14));
|
||||
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 4 });
|
||||
assert.equal(new Range(1, 10, 1, 14).toString(), actual.toString());
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 4 }, false);
|
||||
assertFindMatch(actual, new Range(1, 10, 1, 14));
|
||||
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
|
||||
assert.equal(new Range(2, 5, 2, 9).toString(), actual.toString());
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
|
||||
assertFindMatch(actual, new Range(2, 5, 2, 9));
|
||||
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
|
||||
assert.equal(new Range(1, 10, 1, 14).toString(), actual.toString());
|
||||
actual = TextModelSearch.findNextMatch(model, searchParams, actual.range.getEndPosition(), false);
|
||||
assertFindMatch(actual, new Range(1, 10, 1, 14));
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('findMatches with capturing matches', () => {
|
||||
let model = new TextModel([], TextModel.toRawText('one line line\ntwo line\nthree', TextModel.DEFAULT_CREATION_OPTIONS));
|
||||
|
||||
let searchParams = new SearchParams('(l(in)e)', true, false, false);
|
||||
|
||||
let actual = TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), true, 100);
|
||||
assert.deepEqual(actual, [
|
||||
new FindMatch(new Range(1, 5, 1, 9), ['line', 'line', 'in']),
|
||||
new FindMatch(new Range(1, 10, 1, 14), ['line', 'line', 'in']),
|
||||
new FindMatch(new Range(2, 5, 2, 9), ['line', 'line', 'in']),
|
||||
]);
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('findMatches multiline with capturing matches', () => {
|
||||
let model = new TextModel([], TextModel.toRawText('one line line\ntwo line\nthree', TextModel.DEFAULT_CREATION_OPTIONS));
|
||||
|
||||
let searchParams = new SearchParams('(l(in)e)\\n', true, false, false);
|
||||
|
||||
let actual = TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), true, 100);
|
||||
assert.deepEqual(actual, [
|
||||
new FindMatch(new Range(1, 10, 2, 1), ['line\n', 'line', 'in']),
|
||||
new FindMatch(new Range(2, 5, 3, 1), ['line\n', 'line', 'in']),
|
||||
]);
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('findNextMatch with capturing matches', () => {
|
||||
let model = new TextModel([], TextModel.toRawText('one line line\ntwo line\nthree', TextModel.DEFAULT_CREATION_OPTIONS));
|
||||
|
||||
let searchParams = new SearchParams('(l(in)e)', true, false, false);
|
||||
|
||||
let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), true);
|
||||
assertFindMatch(actual, new Range(1, 5, 1, 9), ['line', 'line', 'in']);
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('findNextMatch multiline with capturing matches', () => {
|
||||
let model = new TextModel([], TextModel.toRawText('one line line\ntwo line\nthree', TextModel.DEFAULT_CREATION_OPTIONS));
|
||||
|
||||
let searchParams = new SearchParams('(l(in)e)\\n', true, false, false);
|
||||
|
||||
let actual = TextModelSearch.findNextMatch(model, searchParams, new Position(1, 1), true);
|
||||
assertFindMatch(actual, new Range(1, 10, 2, 1), ['line\n', 'line', 'in']);
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('findPreviousMatch with capturing matches', () => {
|
||||
let model = new TextModel([], TextModel.toRawText('one line line\ntwo line\nthree', TextModel.DEFAULT_CREATION_OPTIONS));
|
||||
|
||||
let searchParams = new SearchParams('(l(in)e)', true, false, false);
|
||||
|
||||
let actual = TextModelSearch.findPreviousMatch(model, searchParams, new Position(1, 1), true);
|
||||
assertFindMatch(actual, new Range(2, 5, 2, 9), ['line', 'line', 'in']);
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('findPreviousMatch multiline with capturing matches', () => {
|
||||
let model = new TextModel([], TextModel.toRawText('one line line\ntwo line\nthree', TextModel.DEFAULT_CREATION_OPTIONS));
|
||||
|
||||
let searchParams = new SearchParams('(l(in)e)\\n', true, false, false);
|
||||
|
||||
let actual = TextModelSearch.findPreviousMatch(model, searchParams, new Position(1, 1), true);
|
||||
assertFindMatch(actual, new Range(2, 5, 3, 1), ['line\n', 'line', 'in']);
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
5
src/vs/monaco.d.ts
vendored
5
src/vs/monaco.d.ts
vendored
@@ -2090,8 +2090,9 @@ declare module monaco.editor {
|
||||
}
|
||||
|
||||
export class FindMatch {
|
||||
readonly captures: Range[];
|
||||
constructor(captures: Range[]);
|
||||
_findMatchBrand: void;
|
||||
readonly range: Range;
|
||||
readonly matches: string[];
|
||||
}
|
||||
|
||||
export interface IReadOnlyModel extends ITextModel {
|
||||
|
||||
Reference in New Issue
Block a user