TextModelSearch can return captured groups

This commit is contained in:
Alex Dima
2017-01-11 10:25:05 +01:00
parent eda16e3271
commit 1220948f12
5 changed files with 324 additions and 218 deletions

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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
View File

@@ -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 {