mirror of
https://github.com/microsoft/vscode.git
synced 2026-03-03 07:19:22 +00:00
Merge branch 'rebornix/search-opt'
This commit is contained in:
@@ -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));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user