提交 17faeb91 编写于 作者: P Peng Lyu

Merge branch 'rebornix/search-opt'

......@@ -551,7 +551,7 @@ export class PieceTreeBase {
return this.getOffsetAt(lineNumber + 1, 1) - this.getOffsetAt(lineNumber, 1) - this._EOLLength;
}
public findMatchesInNode(node: TreeNode, searcher: Searcher, startLineNumber: number, startCursor: BufferCursor, endCursor: BufferCursor, searchData: SearchData, captureMatches: boolean, limitResultCount: number, resultLen: number, result: FindMatch[]) {
public findMatchesInNode(node: TreeNode, searcher: Searcher, startLineNumber: number, startColumn: number, startCursor: BufferCursor, endCursor: BufferCursor, searchData: SearchData, captureMatches: boolean, limitResultCount: number, resultLen: number, result: FindMatch[]) {
let buffer = this._buffers[node.piece.bufferIndex];
let startOffsetInBuffer = this.offsetInBuffer(node.piece.bufferIndex, node.piece.start);
let start = this.offsetInBuffer(node.piece.bufferIndex, startCursor);
......@@ -571,7 +571,9 @@ export class PieceTreeBase {
}
this.positionInBuffer(node, m.index - startOffsetInBuffer, ret);
let lineFeedCnt = this.getLineFeedCnt(node.piece.bufferIndex, startCursor, ret);
result[resultLen++] = createFindMatch(new Range(startLineNumber + lineFeedCnt, ret.column + 1, startLineNumber + lineFeedCnt, ret.column + 1 + m[0].length), m, captureMatches);
let retStartColumn = ret.line === startCursor.line ? ret.column - startCursor.column + startColumn : ret.column + 1;
let retEndColumn = retStartColumn + m[0].length;
result[resultLen++] = createFindMatch(new Range(startLineNumber + lineFeedCnt, retStartColumn, startLineNumber + lineFeedCnt, retEndColumn), m, captureMatches);
if (m.index + m[0].length >= end) {
return resultLen;
......@@ -603,7 +605,7 @@ export class PieceTreeBase {
let end = this.positionInBuffer(endPosition.node, endPosition.remainder);
if (startPostion.node === endPosition.node) {
this.findMatchesInNode(startPostion.node, searcher, searchRange.startLineNumber, start, end, searchData, captureMatches, limitResultCount, resultLen, result);
this.findMatchesInNode(startPostion.node, searcher, searchRange.startLineNumber, searchRange.startColumn, start, end, searchData, captureMatches, limitResultCount, resultLen, result);
return result;
}
......@@ -618,7 +620,8 @@ export class PieceTreeBase {
let lineStarts = this._buffers[currentNode.piece.bufferIndex].lineStarts;
let startOffsetInBuffer = this.offsetInBuffer(currentNode.piece.bufferIndex, currentNode.piece.start);
let nextLineStartOffset = lineStarts[start.line + lineBreakCnt];
resultLen = this.findMatchesInNode(currentNode, searcher, startLineNumber, start, this.positionInBuffer(currentNode, nextLineStartOffset - startOffsetInBuffer), searchData, captureMatches, limitResultCount, resultLen, result);
let startColumn = startLineNumber === searchRange.startLineNumber ? searchRange.startColumn : 1;
resultLen = this.findMatchesInNode(currentNode, searcher, startLineNumber, startColumn, start, this.positionInBuffer(currentNode, nextLineStartOffset - startOffsetInBuffer), searchData, captureMatches, limitResultCount, resultLen, result);
if (resultLen >= limitResultCount) {
return result;
......@@ -627,14 +630,15 @@ export class PieceTreeBase {
startLineNumber += lineBreakCnt;
}
let startColumn = startLineNumber === searchRange.startLineNumber ? searchRange.startColumn - 1 : 0;
// search for the remaining content
if (startLineNumber === searchRange.endLineNumber) {
const text = this.getLineContent(startLineNumber).substring(0, searchRange.endColumn - 1);
resultLen = this._findMatchesInLine(searchData, searcher, text, searchRange.endLineNumber, 0, resultLen, result, captureMatches, limitResultCount);
const text = this.getLineContent(startLineNumber).substring(startColumn, searchRange.endColumn - 1);
resultLen = this._findMatchesInLine(searchData, searcher, text, searchRange.endLineNumber, startColumn, resultLen, result, captureMatches, limitResultCount);
return result;
}
resultLen = this._findMatchesInLine(searchData, searcher, this.getLineContent(startLineNumber), startLineNumber, 0, resultLen, result, captureMatches, limitResultCount);
resultLen = this._findMatchesInLine(searchData, searcher, this.getLineContent(startLineNumber).substr(startColumn), startLineNumber, startColumn, resultLen, result, captureMatches, limitResultCount);
if (resultLen >= limitResultCount) {
return result;
......@@ -647,12 +651,14 @@ export class PieceTreeBase {
}
if (startLineNumber === searchRange.endLineNumber) {
const text = this.getLineContent(startLineNumber).substring(0, searchRange.endColumn - 1);
resultLen = this._findMatchesInLine(searchData, searcher, text, searchRange.endLineNumber, 0, resultLen, result, captureMatches, limitResultCount);
let startColumn = startLineNumber === searchRange.startLineNumber ? searchRange.startColumn - 1 : 0;
const text = this.getLineContent(startLineNumber).substring(startColumn, searchRange.endColumn - 1);
resultLen = this._findMatchesInLine(searchData, searcher, text, searchRange.endLineNumber, startColumn, resultLen, result, captureMatches, limitResultCount);
return result;
}
resultLen = this.findMatchesInNode(endPosition.node, searcher, startLineNumber, start, end, searchData, captureMatches, limitResultCount, resultLen, result);
let startColumn = startLineNumber === searchRange.startLineNumber ? searchRange.startColumn : 1;
resultLen = this.findMatchesInNode(endPosition.node, searcher, startLineNumber, startColumn, start, end, searchData, captureMatches, limitResultCount, resultLen, result);
return result;
}
......
......@@ -1015,6 +1015,28 @@ export class TextModel extends Disposable implements model.ITextModel {
public findNextMatch(searchString: string, rawSearchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean): model.FindMatch {
this._assertNotDisposed();
const searchStart = this.validatePosition(rawSearchStart);
if (!isRegex && searchString.indexOf('\n') < 0 && OPTIONS.TEXT_BUFFER_IMPLEMENTATION === TextBufferType.PieceTree) {
const searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators);
const searchData = searchParams.parseSearchRequest();
const lineCount = this.getLineCount();
let searchRange = new Range(searchStart.lineNumber, searchStart.column, lineCount, this.getLineMaxColumn(lineCount));
let ret = this.findMatchesLineByLine(searchRange, searchData, captureMatches, 1);
TextModelSearch.findNextMatch(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchStart, captureMatches);
if (ret.length > 0) {
return ret[0];
}
searchRange = new Range(1, 1, searchStart.lineNumber, this.getLineMaxColumn(searchStart.lineNumber));
ret = this.findMatchesLineByLine(searchRange, searchData, captureMatches, 1);
if (ret.length > 0) {
return ret[0];
}
return null;
}
return TextModelSearch.findNextMatch(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchStart, captureMatches);
}
......
......@@ -208,6 +208,50 @@ export class FindDecorations implements IDisposable {
});
}
public matchBeforePosition(position: Position): Range {
if (this._decorations.length === 0) {
return null;
}
for (let i = this._decorations.length - 1; i >= 0; i--) {
let decorationId = this._decorations[i];
let r = this._editor.getModel().getDecorationRange(decorationId);
if (!r || r.endLineNumber > position.lineNumber) {
continue;
}
if (r.endLineNumber < position.lineNumber) {
return r;
}
if (r.endColumn > position.column) {
continue;
}
return r;
}
return this._editor.getModel().getDecorationRange(this._decorations[this._decorations.length - 1]);
}
public matchAfterPosition(position: Position): Range {
if (this._decorations.length === 0) {
return null;
}
for (let i = 0, len = this._decorations.length; i < len; i++) {
let decorationId = this._decorations[i];
let r = this._editor.getModel().getDecorationRange(decorationId);
if (!r || r.startLineNumber < position.lineNumber) {
continue;
}
if (r.startLineNumber > position.lineNumber) {
return r;
}
if (r.startColumn < position.column) {
continue;
}
return r;
}
return this._editor.getModel().getDecorationRange(this._decorations[0]);
}
private _allDecorations(): string[] {
let result: string[] = [];
result = result.concat(this._decorations);
......
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { RunOnceScheduler } from 'vs/base/common/async';
import { RunOnceScheduler, TimeoutTimer } from 'vs/base/common/async';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ReplacePattern, parseReplaceString } from 'vs/editor/contrib/find/replacePattern';
import { ReplaceCommand, ReplaceCommandThatPreservesSelection } from 'vs/editor/common/commands/replaceCommand';
......@@ -73,6 +73,7 @@ export const FIND_IDS = {
};
export const MATCHES_LIMIT = 19999;
const RESEARCH_DELAY = 240;
export class FindModelBoundToEditorModel {
......@@ -81,6 +82,7 @@ export class FindModelBoundToEditorModel {
private _toDispose: IDisposable[];
private _decorations: FindDecorations;
private _ignoreModelContentChanged: boolean;
private _startSearchingTimer: TimeoutTimer;
private _updateDecorationsScheduler: RunOnceScheduler;
private _isDisposed: boolean;
......@@ -90,6 +92,7 @@ export class FindModelBoundToEditorModel {
this._state = state;
this._toDispose = [];
this._isDisposed = false;
this._startSearchingTimer = new TimeoutTimer();
this._decorations = new FindDecorations(editor);
this._toDispose.push(this._decorations);
......@@ -127,6 +130,7 @@ export class FindModelBoundToEditorModel {
public dispose(): void {
this._isDisposed = true;
dispose(this._startSearchingTimer);
this._toDispose = dispose(this._toDispose);
}
......@@ -140,10 +144,24 @@ export class FindModelBoundToEditorModel {
return;
}
if (e.searchString || e.isReplaceRevealed || e.isRegex || e.wholeWord || e.matchCase || e.searchScope) {
if (e.searchScope) {
this.research(e.moveCursor, this._state.searchScope);
let model = this._editor.getModel();
if (model.isTooLargeForHavingARichMode()) {
this._startSearchingTimer.cancel();
this._startSearchingTimer.setIfNotSet(() => {
if (e.searchScope) {
this.research(e.moveCursor, this._state.searchScope);
} else {
this.research(e.moveCursor);
}
}, RESEARCH_DELAY);
} else {
this.research(e.moveCursor);
if (e.searchScope) {
this.research(e.moveCursor, this._state.searchScope);
} else {
this.research(e.moveCursor);
}
}
}
}
......@@ -215,7 +233,44 @@ export class FindModelBoundToEditorModel {
this._editor.revealRangeInCenterIfOutsideViewport(match, editorCommon.ScrollType.Smooth);
}
private _prevSearchPosition(before: Position) {
let isUsingLineStops = this._state.isRegex && (
this._state.searchString.indexOf('^') >= 0
|| this._state.searchString.indexOf('$') >= 0
);
let { lineNumber, column } = before;
let model = this._editor.getModel();
if (isUsingLineStops || column === 1) {
if (lineNumber === 1) {
lineNumber = model.getLineCount();
} else {
lineNumber--;
}
column = model.getLineMaxColumn(lineNumber);
} else {
column--;
}
return new Position(lineNumber, column);
}
private _moveToPrevMatch(before: Position, isRecursed: boolean = false): void {
if (this._decorations.getCount() < MATCHES_LIMIT) {
let prevMatchRange = this._decorations.matchBeforePosition(before);
if (prevMatchRange && prevMatchRange.isEmpty() && prevMatchRange.getStartPosition().equals(before)) {
before = this._prevSearchPosition(before);
prevMatchRange = this._decorations.matchBeforePosition(before);
}
if (prevMatchRange) {
this._setCurrentFindMatch(prevMatchRange);
}
return;
}
if (this._cannotFind()) {
return;
}
......@@ -242,24 +297,7 @@ export class FindModelBoundToEditorModel {
if (prevMatch && prevMatch.range.isEmpty() && prevMatch.range.getStartPosition().equals(position)) {
// Looks like we're stuck at this position, unacceptable!
let isUsingLineStops = this._state.isRegex && (
this._state.searchString.indexOf('^') >= 0
|| this._state.searchString.indexOf('$') >= 0
);
if (isUsingLineStops || column === 1) {
if (lineNumber === 1) {
lineNumber = model.getLineCount();
} else {
lineNumber--;
}
column = model.getLineMaxColumn(lineNumber);
} else {
column--;
}
position = new Position(lineNumber, column);
position = this._prevSearchPosition(position);
prevMatch = model.findPreviousMatch(this._state.searchString, position, this._state.isRegex, this._state.matchCase, this._state.wholeWord ? this._editor.getConfiguration().wordSeparators : null, false);
}
......@@ -279,7 +317,45 @@ export class FindModelBoundToEditorModel {
this._moveToPrevMatch(this._editor.getSelection().getStartPosition());
}
private _nextSearchPosition(after: Position) {
let isUsingLineStops = this._state.isRegex && (
this._state.searchString.indexOf('^') >= 0
|| this._state.searchString.indexOf('$') >= 0
);
let { lineNumber, column } = after;
let model = this._editor.getModel();
if (isUsingLineStops || column === model.getLineMaxColumn(lineNumber)) {
if (lineNumber === model.getLineCount()) {
lineNumber = 1;
} else {
lineNumber++;
}
column = 1;
} else {
column++;
}
return new Position(lineNumber, column);
}
private _moveToNextMatch(after: Position): void {
if (this._decorations.getCount() < MATCHES_LIMIT) {
let nextMatchRange = this._decorations.matchAfterPosition(after);
if (nextMatchRange && nextMatchRange.isEmpty() && nextMatchRange.getStartPosition().equals(after)) {
// Looks like we're stuck at this position, unacceptable!
after = this._nextSearchPosition(after);
nextMatchRange = this._decorations.matchAfterPosition(after);
}
if (nextMatchRange) {
this._setCurrentFindMatch(nextMatchRange);
}
return;
}
let nextMatch = this._getNextMatch(after, false, true);
if (nextMatch) {
this._setCurrentFindMatch(nextMatch.range);
......@@ -313,24 +389,7 @@ export class FindModelBoundToEditorModel {
if (forceMove && nextMatch && nextMatch.range.isEmpty() && nextMatch.range.getStartPosition().equals(position)) {
// Looks like we're stuck at this position, unacceptable!
let isUsingLineStops = this._state.isRegex && (
this._state.searchString.indexOf('^') >= 0
|| this._state.searchString.indexOf('$') >= 0
);
if (isUsingLineStops || column === model.getLineMaxColumn(lineNumber)) {
if (lineNumber === model.getLineCount()) {
lineNumber = 1;
} else {
lineNumber++;
}
column = 1;
} else {
column++;
}
position = new Position(lineNumber, column);
position = this._nextSearchPosition(position);
nextMatch = model.findNextMatch(this._state.searchString, position, this._state.isRegex, this._state.matchCase, this._state.wholeWord ? this._editor.getConfiguration().wordSeparators : null, captureMatches);
}
......
......@@ -1810,4 +1810,22 @@ suite('chunk based search', () => {
assert.deepEqual(ret[1].range, new Range(3, 3, 3, 4));
assert.deepEqual(ret[2].range, new Range(4, 3, 4, 4));
});
test('search searching from the middle', () => {
let pieceTree = createTextBuffer([
[
'def',
'dbcabc'
].join('\n')
]);
pieceTree.delete(4, 1);
let ret = pieceTree.findMatchesLineByLine(new Range(2, 3, 2, 6), new SearchData(/a/gi, null, 'a'), true, 1000);
assert.equal(ret.length, 1);
assert.deepEqual(ret[0].range, new Range(2, 3, 2, 4));
pieceTree.delete(4, 1);
ret = pieceTree.findMatchesLineByLine(new Range(2, 2, 2, 5), new SearchData(/a/gi, null, 'a'), true, 1000);
assert.equal(ret.length, 1);
assert.deepEqual(ret[0].range, new Range(2, 2, 2, 3));
});
});
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册