提交 eda16e32 编写于 作者: A Alex Dima

Introduce and adopt SearchParams

上级 48815028
......@@ -344,6 +344,7 @@ export function createMonacoEditorAPI(): typeof monaco.editor {
BareFontInfo: <any>BareFontInfo,
FontInfo: <any>FontInfo,
TextModelResolvedOptions: <any>editorCommon.TextModelResolvedOptions,
FindMatch: <any>editorCommon.FindMatch,
// vars
EditorType: editorCommon.EditorType,
......
......@@ -1811,6 +1811,19 @@ export interface ITextModel {
findPreviousMatch(searchString: string, searchStart: IPosition, isRegex: boolean, matchCase: boolean, wholeWord: boolean): Range;
}
export class FindMatch {
_findMatchBrand: void;
public readonly captures: Range[];
/**
* @internal
*/
constructor(captures: Range[]) {
this.captures = captures;
}
}
export interface IReadOnlyModel extends ITextModel {
/**
* Gets the resource associated with this editor model.
......
......@@ -14,7 +14,7 @@ import { guessIndentation } from 'vs/editor/common/model/indentationGuesser';
import { DEFAULT_INDENTATION, DEFAULT_TRIM_AUTO_WHITESPACE } from 'vs/editor/common/config/defaultConfig';
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
import { IndentRange, computeRanges } from 'vs/editor/common/model/indentRanges';
import { TextModelSearch } from 'vs/editor/common/model/textModelSearch';
import { TextModelSearch, SearchParams } from 'vs/editor/common/model/textModelSearch';
const LIMIT_FIND_COUNT = 999;
export const LONG_LINE_BOUNDARY = 1000;
......@@ -842,17 +842,17 @@ export class TextModel extends OrderGuaranteeEventEmitter implements editorCommo
searchRange = this.getFullModelRange();
}
return TextModelSearch.findMatches(this, searchString, searchRange, isRegex, matchCase, wholeWord, limitResultCount);
return TextModelSearch.findMatches(this, new SearchParams(searchString, isRegex, matchCase, wholeWord), searchRange, limitResultCount);
}
public findNextMatch(searchString: string, rawSearchStart: editorCommon.IPosition, isRegex: boolean, matchCase: boolean, wholeWord: boolean): Range {
this._assertNotDisposed();
return TextModelSearch.findNextMatch(this, searchString, rawSearchStart, isRegex, matchCase, wholeWord);
return TextModelSearch.findNextMatch(this, new SearchParams(searchString, isRegex, matchCase, wholeWord), rawSearchStart);
}
public findPreviousMatch(searchString: string, rawSearchStart: editorCommon.IPosition, isRegex: boolean, matchCase: boolean, wholeWord: boolean): Range {
this._assertNotDisposed();
return TextModelSearch.findPreviousMatch(this, searchString, rawSearchStart, isRegex, matchCase, wholeWord);
return TextModelSearch.findPreviousMatch(this, new SearchParams(searchString, isRegex, matchCase, wholeWord), rawSearchStart);
}
}
......
......@@ -13,7 +13,18 @@ import { TextModel } from 'vs/editor/common/model/textModel';
const LIMIT_FIND_COUNT = 999;
export class TextModelSearch {
export class SearchParams {
public readonly searchString: string;
public readonly isRegex: boolean;
public readonly matchCase: boolean;
public readonly wholeWord: boolean;
constructor(searchString: string, isRegex: boolean, matchCase: boolean, wholeWord: boolean) {
this.searchString = searchString;
this.isRegex = isRegex;
this.matchCase = matchCase;
this.wholeWord = wholeWord;
}
private static _isMultilineRegexSource(searchString: string): boolean {
if (!searchString || searchString.length === 0) {
......@@ -43,22 +54,27 @@ export class TextModelSearch {
return false;
}
public static parseSearchRequest(searchString: string, isRegex: boolean, matchCase: boolean, wholeWord: boolean): RegExp {
if (searchString === '') {
public parseSearchRequest(): RegExp {
if (this.searchString === '') {
return null;
}
// Try to create a RegExp out of the params
let multiline: boolean;
if (isRegex) {
multiline = this._isMultilineRegexSource(searchString);
if (this.isRegex) {
multiline = SearchParams._isMultilineRegexSource(this.searchString);
} else {
multiline = (searchString.indexOf('\n') >= 0);
multiline = (this.searchString.indexOf('\n') >= 0);
}
let regex: RegExp = null;
try {
regex = strings.createRegExp(searchString, isRegex, { matchCase, wholeWord, multiline, global: true });
regex = strings.createRegExp(this.searchString, this.isRegex, {
matchCase: this.matchCase,
wholeWord: this.wholeWord,
multiline,
global: true
});
} catch (err) {
return null;
}
......@@ -69,9 +85,12 @@ export class TextModelSearch {
return regex;
}
}
export class TextModelSearch {
public static findMatches(model: TextModel, searchString: string, searchRange: Range, isRegex: boolean, matchCase: boolean, wholeWord: boolean, limitResultCount: number): Range[] {
const regex = this.parseSearchRequest(searchString, isRegex, matchCase, wholeWord);
public static findMatches(model: TextModel, searchParams: SearchParams, searchRange: Range, limitResultCount: number): Range[] {
const regex = searchParams.parseSearchRequest();
if (!regex) {
return [];
}
......@@ -145,8 +164,8 @@ export class TextModelSearch {
return result;
}
public static findNextMatch(model: TextModel, searchString: string, rawSearchStart: IPosition, isRegex: boolean, matchCase: boolean, wholeWord: boolean): Range {
const regex = this.parseSearchRequest(searchString, isRegex, matchCase, wholeWord);
public static findNextMatch(model: TextModel, searchParams: SearchParams, rawSearchStart: IPosition): Range {
const regex = searchParams.parseSearchRequest();
if (!regex) {
return null;
}
......@@ -205,8 +224,8 @@ export class TextModelSearch {
return null;
}
public static findPreviousMatch(model: TextModel, searchString: string, rawSearchStart: IPosition, isRegex: boolean, matchCase: boolean, wholeWord: boolean): Range {
const regex = this.parseSearchRequest(searchString, isRegex, matchCase, wholeWord);
public static findPreviousMatch(model: TextModel, searchParams: SearchParams, rawSearchStart: IPosition): Range {
const regex = searchParams.parseSearchRequest();
if (!regex) {
return null;
}
......
......@@ -17,7 +17,7 @@ import { ReplaceAllCommand } from './replaceAllCommand';
import { Selection } from 'vs/editor/common/core/selection';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { IKeybindings } from 'vs/platform/keybinding/common/keybinding';
import { TextModelSearch } from 'vs/editor/common/model/textModelSearch';
import { SearchParams } from 'vs/editor/common/model/textModelSearch';
export const ToggleCaseSensitiveKeybinding: IKeybindings = {
primary: KeyMod.Alt | KeyCode.KEY_C,
......@@ -339,7 +339,8 @@ export class FindModelBoundToEditorModel {
private getReplaceString(matchRange: Range): string {
if (this._state.isRegex) {
let regExp = TextModelSearch.parseSearchRequest(this._state.searchString, this._state.isRegex, this._state.matchCase, this._state.wholeWord);
let searchParams = new SearchParams(this._state.searchString, this._state.isRegex, this._state.matchCase, this._state.wholeWord);
let regExp = searchParams.parseSearchRequest();
let replacePattern = new ReplacePattern(this._state.replaceString, true, regExp);
let model = this._editor.getModel();
let matchedString = model.getValueInRange(matchRange);
......
......@@ -8,7 +8,7 @@ import * as assert from 'assert';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { TextModel } from 'vs/editor/common/model/textModel';
import { TextModelSearch } from 'vs/editor/common/model/textModelSearch';
import { TextModelSearch, SearchParams } from 'vs/editor/common/model/textModelSearch';
// --------- Find
suite('TextModelSearch', () => {
......@@ -27,21 +27,21 @@ suite('TextModelSearch', () => {
// test `findNextMatch`
let startPos = new Position(1, 1);
let match = model.findNextMatch(searchString, startPos, isRegex, matchCase, wholeWord);
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 = model.findNextMatch(searchString, startPos, isRegex, matchCase, wholeWord);
match = TextModelSearch.findNextMatch(model, new SearchParams(searchString, isRegex, matchCase, wholeWord), startPos);
assert.deepEqual(toArrRange(match), expected[i], `findNextMatch ${startPos}`);
}
// test `findPrevMatch`
startPos = new Position(model.getLineCount(), model.getLineMaxColumn(model.getLineCount()));
match = model.findPreviousMatch(searchString, startPos, isRegex, matchCase, wholeWord);
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 = model.findPreviousMatch(searchString, startPos, isRegex, matchCase, wholeWord);
match = TextModelSearch.findPreviousMatch(model, new SearchParams(searchString, isRegex, matchCase, wholeWord), startPos);
assert.deepEqual(toArrRange(match), expected[i], `findPrevMatch ${startPos}`);
}
......@@ -323,97 +323,108 @@ suite('TextModelSearch', () => {
});
test('findNextMatch without regex', () => {
var testObject = new TextModel([], TextModel.toRawText('line line one\nline two\nthree', TextModel.DEFAULT_CREATION_OPTIONS));
let model = new TextModel([], TextModel.toRawText('line line one\nline two\nthree', TextModel.DEFAULT_CREATION_OPTIONS));
let actual = testObject.findNextMatch('line', { lineNumber: 1, column: 1 }, false, false, false);
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());
actual = testObject.findNextMatch('line', actual.getEndPosition(), false, false, false);
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
assert.equal(new Range(1, 6, 1, 10).toString(), actual.toString());
actual = testObject.findNextMatch('line', { lineNumber: 1, column: 3 }, false, false, false);
actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 3 });
assert.equal(new Range(1, 6, 1, 10).toString(), actual.toString());
actual = testObject.findNextMatch('line', actual.getEndPosition(), false, false, false);
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
assert.equal(new Range(2, 1, 2, 5).toString(), actual.toString());
actual = testObject.findNextMatch('line', actual.getEndPosition(), false, false, false);
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
assert.equal(new Range(1, 1, 1, 5).toString(), actual.toString());
testObject.dispose();
model.dispose();
});
test('findNextMatch with beginning boundary regex', () => {
var testObject = new TextModel([], TextModel.toRawText('line one\nline two\nthree', TextModel.DEFAULT_CREATION_OPTIONS));
let model = new TextModel([], TextModel.toRawText('line one\nline two\nthree', TextModel.DEFAULT_CREATION_OPTIONS));
let searchParams = new SearchParams('^line', true, false, false);
let actual = testObject.findNextMatch('^line', { lineNumber: 1, column: 1 }, true, false, false);
let actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 1 });
assert.equal(new Range(1, 1, 1, 5).toString(), actual.toString());
actual = testObject.findNextMatch('^line', actual.getEndPosition(), true, false, false);
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
assert.equal(new Range(2, 1, 2, 5).toString(), actual.toString());
actual = testObject.findNextMatch('^line', { lineNumber: 1, column: 3 }, true, false, false);
actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 3 });
assert.equal(new Range(2, 1, 2, 5).toString(), actual.toString());
actual = testObject.findNextMatch('^line', actual.getEndPosition(), true, false, false);
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
assert.equal(new Range(1, 1, 1, 5).toString(), actual.toString());
testObject.dispose();
model.dispose();
});
test('findNextMatch with beginning boundary regex and line has repetitive beginnings', () => {
var testObject = new TextModel([], TextModel.toRawText('line line one\nline two\nthree', TextModel.DEFAULT_CREATION_OPTIONS));
let model = new TextModel([], TextModel.toRawText('line line one\nline two\nthree', TextModel.DEFAULT_CREATION_OPTIONS));
let searchParams = new SearchParams('^line', true, false, false);
let actual = testObject.findNextMatch('^line', { lineNumber: 1, column: 1 }, true, false, false);
let actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 1 });
assert.equal(new Range(1, 1, 1, 5).toString(), actual.toString());
actual = testObject.findNextMatch('^line', actual.getEndPosition(), true, false, false);
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
assert.equal(new Range(2, 1, 2, 5).toString(), actual.toString());
actual = testObject.findNextMatch('^line', { lineNumber: 1, column: 3 }, true, false, false);
actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 3 });
assert.equal(new Range(2, 1, 2, 5).toString(), actual.toString());
actual = testObject.findNextMatch('^line', actual.getEndPosition(), true, false, false);
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
assert.equal(new Range(1, 1, 1, 5).toString(), actual.toString());
testObject.dispose();
model.dispose();
});
test('findNextMatch with beginning boundary multiline regex and line has repetitive beginnings', () => {
var testObject = new TextModel([], TextModel.toRawText('line line one\nline two\nline three\nline four', TextModel.DEFAULT_CREATION_OPTIONS));
let model = new TextModel([], TextModel.toRawText('line line one\nline two\nline three\nline four', TextModel.DEFAULT_CREATION_OPTIONS));
let searchParams = new SearchParams('^line.*\\nline', true, false, false);
let actual = testObject.findNextMatch('^line.*\\nline', { lineNumber: 1, column: 1 }, true, false, false);
let actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 1 });
assert.equal(new Range(1, 1, 2, 5).toString(), actual.toString());
actual = testObject.findNextMatch('^line.*\\nline', actual.getEndPosition(), true, false, false);
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
assert.equal(new Range(3, 1, 4, 5).toString(), actual.toString());
actual = testObject.findNextMatch('^line.*\\nline', { lineNumber: 2, column: 1 }, true, false, false);
actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 2, column: 1 });
assert.equal(new Range(2, 1, 3, 5).toString(), actual.toString());
testObject.dispose();
model.dispose();
});
test('findNextMatch with ending boundary regex', () => {
var testObject = new TextModel([], TextModel.toRawText('one line line\ntwo line\nthree', TextModel.DEFAULT_CREATION_OPTIONS));
let model = new TextModel([], TextModel.toRawText('one line line\ntwo line\nthree', TextModel.DEFAULT_CREATION_OPTIONS));
let actual = testObject.findNextMatch('line$', { lineNumber: 1, column: 1 }, true, false, false);
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());
actual = testObject.findNextMatch('line$', { lineNumber: 1, column: 4 }, true, false, false);
actual = TextModelSearch.findNextMatch(model, searchParams, { lineNumber: 1, column: 4 });
assert.equal(new Range(1, 10, 1, 14).toString(), actual.toString());
actual = testObject.findNextMatch('line$', actual.getEndPosition(), true, false, false);
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
assert.equal(new Range(2, 5, 2, 9).toString(), actual.toString());
actual = testObject.findNextMatch('line$', actual.getEndPosition(), true, false, false);
actual = TextModelSearch.findNextMatch(model, searchParams, actual.getEndPosition());
assert.equal(new Range(1, 10, 1, 14).toString(), actual.toString());
testObject.dispose();
model.dispose();
});
function assertParseSearchResult(searchString: string, isRegex: boolean, matchCase: boolean, wholeWord: boolean, expected: RegExp): void {
let actual = TextModelSearch.parseSearchRequest(searchString, isRegex, matchCase, wholeWord);
let searchParams = new SearchParams(searchString, isRegex, matchCase, wholeWord);
let actual = searchParams.parseSearchRequest();
assert.deepEqual(actual, expected);
}
......
......@@ -2089,6 +2089,11 @@ declare module monaco.editor {
findPreviousMatch(searchString: string, searchStart: IPosition, isRegex: boolean, matchCase: boolean, wholeWord: boolean): Range;
}
export class FindMatch {
readonly captures: Range[];
constructor(captures: Range[]);
}
export interface IReadOnlyModel extends ITextModel {
/**
* Gets the resource associated with this editor model.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册