mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-06 23:06:20 +01:00
@@ -67,7 +67,7 @@ export class RipgrepTextSearchEngine {
|
||||
});
|
||||
|
||||
let gotResult = false;
|
||||
this.ripgrepParser = new RipgrepParser(MAX_TEXT_RESULTS, cwd);
|
||||
this.ripgrepParser = new RipgrepParser(MAX_TEXT_RESULTS, cwd, options.previewOptions);
|
||||
this.ripgrepParser.on('result', (match: vscode.TextSearchResult) => {
|
||||
gotResult = true;
|
||||
progress.report(match);
|
||||
@@ -160,7 +160,7 @@ export class RipgrepParser extends EventEmitter {
|
||||
|
||||
private numResults = 0;
|
||||
|
||||
constructor(private maxResults: number, private rootFolder: string) {
|
||||
constructor(private maxResults: number, private rootFolder: string, private previewOptions?: vscode.TextSearchPreviewOptions) {
|
||||
super();
|
||||
this.stringDecoder = new StringDecoder();
|
||||
}
|
||||
@@ -293,12 +293,22 @@ export class RipgrepParser extends EventEmitter {
|
||||
|
||||
lineMatches
|
||||
.map(range => {
|
||||
let trimmedPreview = preview;
|
||||
let trimmedPreviewRange = range;
|
||||
if (this.previewOptions) {
|
||||
const previewStart = Math.max(range.start.character - this.previewOptions.leadingChars, 0);
|
||||
trimmedPreview = preview.substr(previewStart, this.previewOptions.totalChars - previewStart);
|
||||
if (previewStart > 0) {
|
||||
trimmedPreviewRange = new vscode.Range(0, range.start.character - previewStart, 0, range.end.character - previewStart);
|
||||
}
|
||||
}
|
||||
|
||||
return <vscode.TextSearchResult>{
|
||||
uri: vscode.Uri.file(path.join(this.rootFolder, this.currentFile)),
|
||||
range,
|
||||
preview: {
|
||||
text: preview,
|
||||
match: new vscode.Range(0, range.start.character, 0, range.end.character)
|
||||
text: trimmedPreview,
|
||||
match: trimmedPreviewRange || new vscode.Range(0, range.start.character, 0, range.end.character)
|
||||
}
|
||||
};
|
||||
})
|
||||
|
||||
@@ -85,6 +85,7 @@ export interface ICommonQueryOptions<U> {
|
||||
disregardExcludeSettings?: boolean;
|
||||
ignoreSymlinks?: boolean;
|
||||
maxFileSize?: number;
|
||||
previewOptions?: ITextSearchPreviewOptions;
|
||||
}
|
||||
|
||||
export interface IQueryOptions extends ICommonQueryOptions<uri> {
|
||||
@@ -132,15 +133,33 @@ export interface IPatternInfo {
|
||||
|
||||
export interface IFileMatch<U extends UriComponents = uri> {
|
||||
resource?: U;
|
||||
lineMatches?: ILineMatch[];
|
||||
matches?: ITextSearchResult[];
|
||||
}
|
||||
|
||||
export type IRawFileMatch2 = IFileMatch<UriComponents>;
|
||||
|
||||
export interface ILineMatch {
|
||||
preview: string;
|
||||
lineNumber: number;
|
||||
offsetAndLengths: number[][];
|
||||
export interface ITextSearchPreviewOptions {
|
||||
maxLines: number;
|
||||
leadingChars: number;
|
||||
totalChars: number;
|
||||
}
|
||||
|
||||
export interface ISearchRange {
|
||||
readonly startLineNumber: number;
|
||||
readonly startColumn: number;
|
||||
readonly endLineNumber: number;
|
||||
readonly endColumn: number;
|
||||
}
|
||||
|
||||
export interface ITextSearchResultPreview {
|
||||
text: string;
|
||||
match: ISearchRange;
|
||||
}
|
||||
|
||||
export interface ITextSearchResult {
|
||||
uri?: uri;
|
||||
range: ISearchRange;
|
||||
preview: ITextSearchResultPreview;
|
||||
}
|
||||
|
||||
export interface IProgress {
|
||||
@@ -204,18 +223,47 @@ export interface IFileIndexProviderStats {
|
||||
filesWalked: number;
|
||||
}
|
||||
|
||||
// ---- very simple implementation of the search model --------------------
|
||||
|
||||
export class FileMatch implements IFileMatch {
|
||||
public lineMatches: LineMatch[] = [];
|
||||
public matches: ITextSearchResult[] = [];
|
||||
constructor(public resource: uri) {
|
||||
// empty
|
||||
}
|
||||
}
|
||||
|
||||
export class LineMatch implements ILineMatch {
|
||||
constructor(public preview: string, public lineNumber: number, public offsetAndLengths: number[][]) {
|
||||
// empty
|
||||
export class TextSearchResult implements ITextSearchResult {
|
||||
range: ISearchRange;
|
||||
preview: ITextSearchResultPreview;
|
||||
|
||||
constructor(fullLine: string, range: ISearchRange, previewOptions?: ITextSearchPreviewOptions) {
|
||||
this.range = range;
|
||||
if (previewOptions) {
|
||||
const previewStart = Math.max(range.startColumn - previewOptions.leadingChars, 0);
|
||||
const previewEnd = Math.max(previewOptions.totalChars + previewStart, range.endColumn);
|
||||
|
||||
this.preview = {
|
||||
text: fullLine.substring(previewStart, previewEnd),
|
||||
match: new OneLineRange(0, range.startColumn - previewStart, range.endColumn - previewStart)
|
||||
};
|
||||
} else {
|
||||
this.preview = {
|
||||
text: fullLine,
|
||||
match: new OneLineRange(0, range.startColumn, range.endColumn)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class OneLineRange implements ISearchRange {
|
||||
startLineNumber: number;
|
||||
startColumn: number;
|
||||
endLineNumber: number;
|
||||
endColumn: number;
|
||||
|
||||
constructor(lineNumber: number, startColumn: number, endColumn: number) {
|
||||
this.startLineNumber = lineNumber;
|
||||
this.startColumn = startColumn;
|
||||
this.endLineNumber = lineNumber;
|
||||
this.endColumn = endColumn;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+7
-4
@@ -77,6 +77,12 @@ declare module 'vscode' {
|
||||
followSymlinks: boolean;
|
||||
}
|
||||
|
||||
export interface TextSearchPreviewOptions {
|
||||
maxLines: number;
|
||||
leadingChars: number;
|
||||
totalChars: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options that apply to text search.
|
||||
*/
|
||||
@@ -86,10 +92,7 @@ declare module 'vscode' {
|
||||
*/
|
||||
maxResults: number;
|
||||
|
||||
/**
|
||||
* TODO@roblou - total length? # of context lines? leading and trailing # of chars?
|
||||
*/
|
||||
previewOptions?: any;
|
||||
previewOptions?: TextSearchPreviewOptions;
|
||||
|
||||
/**
|
||||
* Exclude files larger than `maxFileSize` in bytes.
|
||||
|
||||
@@ -78,7 +78,7 @@ class SearchOperation {
|
||||
addMatch(match: IFileMatch): void {
|
||||
if (this.matches.has(match.resource.toString())) {
|
||||
// Merge with previous IFileMatches
|
||||
this.matches.get(match.resource.toString()).lineMatches.push(...match.lineMatches);
|
||||
this.matches.get(match.resource.toString()).matches.push(...match.matches);
|
||||
} else {
|
||||
this.matches.set(match.resource.toString(), match);
|
||||
}
|
||||
@@ -149,10 +149,10 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable {
|
||||
|
||||
const searchOp = this._searches.get(session);
|
||||
dataOrUri.forEach(result => {
|
||||
if ((<IRawFileMatch2>result).lineMatches) {
|
||||
if ((<IRawFileMatch2>result).matches) {
|
||||
searchOp.addMatch({
|
||||
resource: URI.revive((<IRawFileMatch2>result).resource),
|
||||
lineMatches: (<IRawFileMatch2>result).lineMatches
|
||||
matches: (<IRawFileMatch2>result).matches
|
||||
});
|
||||
} else {
|
||||
searchOp.addMatch({
|
||||
|
||||
@@ -181,7 +181,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
const query = queryBuilder.text(pattern, folders, options);
|
||||
|
||||
const onProgress = (p: ISearchProgressItem) => {
|
||||
if (p.lineMatches) {
|
||||
if (p.matches) {
|
||||
this._proxy.$handleTextSearchResult(p, requestId);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
'use strict';
|
||||
|
||||
import * as path from 'path';
|
||||
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import * as glob from 'vs/base/common/glob';
|
||||
import { toDisposable } from 'vs/base/common/lifecycle';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import URI, { UriComponents } from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as extfs from 'vs/base/node/extfs';
|
||||
import { IFileMatch, IFolderQuery, IPatternInfo, IRawSearchQuery, ISearchCompleteStats, ISearchQuery, IFileSearchProviderStats } from 'vs/platform/search/common/search';
|
||||
import { IFileMatch, IFileSearchProviderStats, IFolderQuery, IPatternInfo, IRawSearchQuery, ISearchCompleteStats, ISearchQuery, ITextSearchResult } from 'vs/platform/search/common/search';
|
||||
import { FileIndexSearchManager, IDirectoryEntry, IDirectoryTree, IInternalFileMatch, QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/api/node/extHostSearch.fileIndex';
|
||||
import * as vscode from 'vscode';
|
||||
import { ExtHostSearchShape, IMainContext, MainContext, MainThreadSearchShape } from './extHost.protocol';
|
||||
import { toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IInternalFileMatch, QueryGlobTester, resolvePatternsForProvider, IDirectoryTree, IDirectoryEntry, FileIndexSearchManager } from 'vs/workbench/api/node/extHostSearch.fileIndex';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import { isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
|
||||
export interface ISchemeTransformer {
|
||||
transformOutgoing(scheme: string): string;
|
||||
@@ -172,22 +172,16 @@ class TextSearchResultsCollector {
|
||||
if (!this._currentFileMatch) {
|
||||
this._currentFileMatch = {
|
||||
resource: data.uri,
|
||||
lineMatches: []
|
||||
matches: []
|
||||
};
|
||||
}
|
||||
|
||||
// TODO@roblou - line text is sent for every match
|
||||
const matchRange = data.preview.match;
|
||||
this._currentFileMatch.lineMatches.push({
|
||||
lineNumber: data.range.start.line,
|
||||
preview: data.preview.text,
|
||||
offsetAndLengths: [[matchRange.start.character, matchRange.end.character - matchRange.start.character]]
|
||||
});
|
||||
this._currentFileMatch.matches.push(extensionResultToFrontendResult(data));
|
||||
}
|
||||
|
||||
private pushToCollector(): void {
|
||||
const size = this._currentFileMatch ?
|
||||
this._currentFileMatch.lineMatches.reduce((acc, match) => acc + match.offsetAndLengths.length, 0) :
|
||||
this._currentFileMatch.matches.length :
|
||||
0;
|
||||
this._batchedCollector.addItem(this._currentFileMatch, size);
|
||||
}
|
||||
@@ -202,6 +196,26 @@ class TextSearchResultsCollector {
|
||||
}
|
||||
}
|
||||
|
||||
function extensionResultToFrontendResult(data: vscode.TextSearchResult): ITextSearchResult {
|
||||
return {
|
||||
preview: {
|
||||
match: {
|
||||
startLineNumber: data.preview.match.start.line,
|
||||
startColumn: data.preview.match.start.character,
|
||||
endLineNumber: data.preview.match.end.line,
|
||||
endColumn: data.preview.match.end.character
|
||||
},
|
||||
text: data.preview.text
|
||||
},
|
||||
range: {
|
||||
startLineNumber: data.range.start.line,
|
||||
startColumn: data.range.start.character,
|
||||
endLineNumber: data.range.end.line,
|
||||
endColumn: data.range.end.character
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects items that have a size - before the cumulative size of collected items reaches START_BATCH_AFTER_COUNT, the callback is called for every
|
||||
* set of items collected.
|
||||
@@ -414,7 +428,8 @@ class TextSearchEngine {
|
||||
followSymlinks: !this.config.ignoreSymlinks,
|
||||
encoding: this.config.fileEncoding,
|
||||
maxFileSize: this.config.maxFileSize,
|
||||
maxResults: this.config.maxResults
|
||||
maxResults: this.config.maxResults,
|
||||
previewOptions: this.config.previewOptions
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +108,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
|
||||
private readonly selectCurrentMatchEmitter: Emitter<string>;
|
||||
private delayedRefresh: Delayer<void>;
|
||||
private changedWhileHidden: boolean;
|
||||
private isWide: boolean;
|
||||
|
||||
private searchWithoutFolderMessageBuilder: Builder;
|
||||
|
||||
@@ -826,8 +827,10 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
|
||||
}
|
||||
|
||||
if (this.size.width >= SearchView.WIDE_VIEW_SIZE) {
|
||||
this.isWide = true;
|
||||
dom.addClass(this.getContainer(), SearchView.WIDE_CLASS_NAME);
|
||||
} else {
|
||||
this.isWide = false;
|
||||
dom.removeClass(this.getContainer(), SearchView.WIDE_CLASS_NAME);
|
||||
}
|
||||
|
||||
@@ -1081,7 +1084,12 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
|
||||
disregardIgnoreFiles: !useExcludesAndIgnoreFiles,
|
||||
disregardExcludeSettings: !useExcludesAndIgnoreFiles,
|
||||
excludePattern,
|
||||
includePattern
|
||||
includePattern,
|
||||
previewOptions: {
|
||||
leadingChars: 5,
|
||||
maxLines: 1,
|
||||
totalChars: this.isWide ? 1000 : 100
|
||||
}
|
||||
};
|
||||
const folderResources = this.contextService.getWorkspace().folders;
|
||||
|
||||
|
||||
@@ -95,7 +95,8 @@ export class QueryBuilder {
|
||||
useRipgrep,
|
||||
disregardIgnoreFiles: options.disregardIgnoreFiles || !useIgnoreFiles,
|
||||
disregardExcludeSettings: options.disregardExcludeSettings,
|
||||
ignoreSymlinks
|
||||
ignoreSymlinks,
|
||||
previewOptions: options.previewOptions
|
||||
};
|
||||
|
||||
// Filter extraFileResources against global include/exclude patterns - they are already expected to not belong to a workspace
|
||||
|
||||
@@ -3,39 +3,50 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { anyEvent, Emitter, Event, fromPromise, stopwatch } from 'vs/base/common/event';
|
||||
import { getBaseLabel } from 'vs/base/common/labels';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ResourceMap, TernarySearchTree, values } from 'vs/base/common/map';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { values, ResourceMap, TernarySearchTree } from 'vs/base/common/map';
|
||||
import { Event, Emitter, fromPromise, stopwatch, anyEvent } from 'vs/base/common/event';
|
||||
import { ISearchService, ISearchProgressItem, ISearchComplete, ISearchQuery, IPatternInfo, IFileMatch, ITextSearchStats } from 'vs/platform/search/common/search';
|
||||
import { ReplacePattern } from 'vs/platform/search/common/replace';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ITextModel, IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness, FindMatch } from 'vs/editor/common/model';
|
||||
import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IReplaceService } from 'vs/workbench/parts/search/common/replace';
|
||||
import { IProgressRunner } from 'vs/platform/progress/common/progress';
|
||||
import { FindMatch, IModelDeltaDecoration, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IProgressRunner } from 'vs/platform/progress/common/progress';
|
||||
import { ReplacePattern } from 'vs/platform/search/common/replace';
|
||||
import { IFileMatch, IPatternInfo, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats, TextSearchResult } from 'vs/platform/search/common/search';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { overviewRulerFindMatchForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
|
||||
import { getBaseLabel } from 'vs/base/common/labels';
|
||||
import { IReplaceService } from 'vs/workbench/parts/search/common/replace';
|
||||
|
||||
export class Match {
|
||||
|
||||
private _lineText: string;
|
||||
private _id: string;
|
||||
private _range: Range;
|
||||
private _previewText: string;
|
||||
private _rangeInPreviewText: Range;
|
||||
|
||||
constructor(private _parent: FileMatch, text: string, lineNumber: number, offset: number, length: number) {
|
||||
this._lineText = text;
|
||||
this._range = new Range(1 + lineNumber, 1 + offset, 1 + lineNumber, 1 + offset + length);
|
||||
this._id = this._parent.id() + '>' + lineNumber + '>' + offset + this.getMatchString();
|
||||
constructor(private _parent: FileMatch, _result: ITextSearchResult) {
|
||||
this._range = new Range(
|
||||
_result.range.startLineNumber + 1,
|
||||
_result.range.startColumn + 1,
|
||||
_result.range.endLineNumber + 1,
|
||||
_result.range.endColumn + 1);
|
||||
|
||||
this._rangeInPreviewText = new Range(
|
||||
_result.preview.match.startLineNumber + 1,
|
||||
_result.preview.match.startColumn + 1,
|
||||
_result.preview.match.endLineNumber + 1,
|
||||
_result.preview.match.endColumn + 1);
|
||||
this._previewText = _result.preview.text;
|
||||
|
||||
this._id = this._parent.id() + '>' + this._range + this.getMatchString();
|
||||
}
|
||||
|
||||
public id(): string {
|
||||
@@ -47,7 +58,7 @@ export class Match {
|
||||
}
|
||||
|
||||
public text(): string {
|
||||
return this._lineText;
|
||||
return this._previewText;
|
||||
}
|
||||
|
||||
public range(): Range {
|
||||
@@ -55,11 +66,9 @@ export class Match {
|
||||
}
|
||||
|
||||
public preview(): { before: string; inside: string; after: string; } {
|
||||
let before = this._lineText.substring(0, this._range.startColumn - 1),
|
||||
const before = this._previewText.substring(0, this._rangeInPreviewText.startColumn - 1),
|
||||
inside = this.getMatchString(),
|
||||
after = this._lineText.substring(this._range.endColumn - 1, Math.min(this._range.endColumn + 150, this._lineText.length));
|
||||
|
||||
before = strings.lcut(before, 26);
|
||||
after = this._previewText.substring(this._rangeInPreviewText.endColumn - 1);
|
||||
|
||||
return {
|
||||
before,
|
||||
@@ -75,7 +84,7 @@ export class Match {
|
||||
|
||||
// If match string is not matching then regex pattern has a lookahead expression
|
||||
if (replaceString === null) {
|
||||
replaceString = searchModel.replacePattern.getReplaceString(matchString + this._lineText.substring(this._range.endColumn - 1));
|
||||
replaceString = searchModel.replacePattern.getReplaceString(matchString + this._previewText.substring(this._rangeInPreviewText.endColumn - 1));
|
||||
}
|
||||
|
||||
// Match string is still not matching. Could be unsupported matches (multi-line).
|
||||
@@ -87,7 +96,7 @@ export class Match {
|
||||
}
|
||||
|
||||
public getMatchString(): string {
|
||||
return this._lineText.substring(this._range.startColumn - 1, this._range.endColumn - 1);
|
||||
return this._previewText.substring(this._rangeInPreviewText.startColumn - 1, this._rangeInPreviewText.endColumn - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +143,7 @@ export class FileMatch extends Disposable {
|
||||
private _updateScheduler: RunOnceScheduler;
|
||||
private _modelDecorations: string[] = [];
|
||||
|
||||
constructor(private _query: IPatternInfo, private _maxResults: number, private _parent: FolderMatch, private rawMatch: IFileMatch,
|
||||
constructor(private _query: IPatternInfo, private _previewOptions: ITextSearchPreviewOptions, private _maxResults: number, private _parent: FolderMatch, private rawMatch: IFileMatch,
|
||||
@IModelService private modelService: IModelService, @IReplaceService private replaceService: IReplaceService) {
|
||||
super();
|
||||
this._resource = this.rawMatch.resource;
|
||||
@@ -152,11 +161,9 @@ export class FileMatch extends Disposable {
|
||||
this.bindModel(model);
|
||||
this.updateMatchesForModel();
|
||||
} else {
|
||||
this.rawMatch.lineMatches.forEach((rawLineMatch) => {
|
||||
rawLineMatch.offsetAndLengths.forEach(offsetAndLength => {
|
||||
let match = new Match(this, rawLineMatch.preview, rawLineMatch.lineNumber, offsetAndLength[0], offsetAndLength[1]);
|
||||
this.add(match);
|
||||
});
|
||||
this.rawMatch.matches.forEach((rawLineMatch) => {
|
||||
let match = new Match(this, rawLineMatch);
|
||||
this.add(match);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -222,7 +229,12 @@ export class FileMatch extends Disposable {
|
||||
|
||||
private updateMatches(matches: FindMatch[], modelChange: boolean) {
|
||||
matches.forEach(m => {
|
||||
let match = new Match(this, this._model.getLineContent(m.range.startLineNumber), m.range.startLineNumber - 1, m.range.startColumn - 1, m.range.endColumn - m.range.startColumn);
|
||||
const textSearchResult = new TextSearchResult(
|
||||
this._model.getLineContent(m.range.startLineNumber),
|
||||
new Range(m.range.startLineNumber - 1, m.range.startColumn - 1, m.range.startLineNumber - 1, m.range.endColumn),
|
||||
this._previewOptions);
|
||||
const match = new Match(this, textSearchResult);
|
||||
|
||||
if (!this._removedMatches.has(match.id())) {
|
||||
this.add(match);
|
||||
if (this.isMatchSelected(match)) {
|
||||
@@ -392,16 +404,16 @@ export class FolderMatch extends Disposable {
|
||||
}
|
||||
|
||||
public add(raw: IFileMatch[], silent: boolean): void {
|
||||
let changed: FileMatch[] = [];
|
||||
const changed: FileMatch[] = [];
|
||||
raw.forEach((rawFileMatch) => {
|
||||
if (this._fileMatches.has(rawFileMatch.resource)) {
|
||||
this._fileMatches.get(rawFileMatch.resource).dispose();
|
||||
}
|
||||
|
||||
let fileMatch = this.instantiationService.createInstance(FileMatch, this._query.contentPattern, this._query.maxResults, this, rawFileMatch);
|
||||
const fileMatch = this.instantiationService.createInstance(FileMatch, this._query.contentPattern, this._query.previewOptions, this._query.maxResults, this, rawFileMatch);
|
||||
this.doAdd(fileMatch);
|
||||
changed.push(fileMatch);
|
||||
let disposable = fileMatch.onChange(() => this.onFileChange(fileMatch));
|
||||
const disposable = fileMatch.onChange(() => this.onFileChange(fileMatch));
|
||||
fileMatch.onDispose(() => disposable.dispose());
|
||||
});
|
||||
if (!silent && changed.length) {
|
||||
|
||||
@@ -129,13 +129,26 @@ suite('Search Actions', () => {
|
||||
function aFileMatch(): FileMatch {
|
||||
let rawMatch: IFileMatch = {
|
||||
resource: URI.file('somepath' + ++counter),
|
||||
lineMatches: []
|
||||
matches: []
|
||||
};
|
||||
return instantiationService.createInstance(FileMatch, null, null, null, rawMatch);
|
||||
return instantiationService.createInstance(FileMatch, null, null, null, null, rawMatch);
|
||||
}
|
||||
|
||||
function aMatch(fileMatch: FileMatch): Match {
|
||||
let match = new Match(fileMatch, 'some match', ++counter, 0, 2);
|
||||
const line = ++counter;
|
||||
const range = {
|
||||
startLineNumber: line,
|
||||
startColumn: 0,
|
||||
endLineNumber: line,
|
||||
endColumn: 2
|
||||
};
|
||||
let match = new Match(fileMatch, {
|
||||
preview: {
|
||||
text: 'some match',
|
||||
match: range
|
||||
},
|
||||
range
|
||||
});
|
||||
fileMatch.add(match);
|
||||
return match;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import uri from 'vs/base/common/uri';
|
||||
import { Match, FileMatch, SearchResult } from 'vs/workbench/parts/search/common/searchModel';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { SearchDataSource, SearchSorter } from 'vs/workbench/parts/search/browser/searchResultsView';
|
||||
import { IFileMatch, ILineMatch } from 'vs/platform/search/common/search';
|
||||
import { IFileMatch, TextSearchResult, OneLineRange, ITextSearchResult } from 'vs/platform/search/common/search';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
|
||||
@@ -31,9 +31,22 @@ suite('Search - Viewlet', () => {
|
||||
let ds = instantiation.createInstance(SearchDataSource);
|
||||
let result: SearchResult = instantiation.createInstance(SearchResult, null);
|
||||
result.query = { type: 1, folderQueries: [{ folder: uri.parse('file://c:/') }] };
|
||||
|
||||
const range = {
|
||||
startLineNumber: 1,
|
||||
startColumn: 0,
|
||||
endLineNumber: 1,
|
||||
endColumn: 1
|
||||
};
|
||||
result.add([{
|
||||
resource: uri.parse('file:///c:/foo'),
|
||||
lineMatches: [{ lineNumber: 1, preview: 'bar', offsetAndLengths: [[0, 1]] }]
|
||||
matches: [{
|
||||
preview: {
|
||||
text: 'bar',
|
||||
match: range
|
||||
},
|
||||
range
|
||||
}]
|
||||
}]);
|
||||
|
||||
let fileMatch = result.matches()[0];
|
||||
@@ -41,7 +54,7 @@ suite('Search - Viewlet', () => {
|
||||
|
||||
assert.equal(ds.getId(null, result), 'root');
|
||||
assert.equal(ds.getId(null, fileMatch), 'file:///c%3A/foo');
|
||||
assert.equal(ds.getId(null, lineMatch), 'file:///c%3A/foo>1>0b');
|
||||
assert.equal(ds.getId(null, lineMatch), 'file:///c%3A/foo>[2,1 -> 2,2]b');
|
||||
|
||||
assert(!ds.hasChildren(null, 'foo'));
|
||||
assert(ds.hasChildren(null, result));
|
||||
@@ -53,9 +66,9 @@ suite('Search - Viewlet', () => {
|
||||
let fileMatch1 = aFileMatch('C:\\foo');
|
||||
let fileMatch2 = aFileMatch('C:\\with\\path');
|
||||
let fileMatch3 = aFileMatch('C:\\with\\path\\foo');
|
||||
let lineMatch1 = new Match(fileMatch1, 'bar', 1, 1, 1);
|
||||
let lineMatch2 = new Match(fileMatch1, 'bar', 2, 1, 1);
|
||||
let lineMatch3 = new Match(fileMatch1, 'bar', 2, 1, 1);
|
||||
let lineMatch1 = new Match(fileMatch1, new TextSearchResult('bar', new OneLineRange(0, 1, 1)));
|
||||
let lineMatch2 = new Match(fileMatch1, new TextSearchResult('bar', new OneLineRange(2, 1, 1)));
|
||||
let lineMatch3 = new Match(fileMatch1, new TextSearchResult('bar', new OneLineRange(2, 1, 1)));
|
||||
|
||||
let s = new SearchSorter();
|
||||
|
||||
@@ -69,12 +82,12 @@ suite('Search - Viewlet', () => {
|
||||
assert(s.compare(null, lineMatch2, lineMatch3) === 0);
|
||||
});
|
||||
|
||||
function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ILineMatch[]): FileMatch {
|
||||
function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ITextSearchResult[]): FileMatch {
|
||||
let rawMatch: IFileMatch = {
|
||||
resource: uri.file('C:\\' + path),
|
||||
lineMatches: lineMatches
|
||||
matches: lineMatches
|
||||
};
|
||||
return instantiation.createInstance(FileMatch, null, null, searchResult, rawMatch);
|
||||
return instantiation.createInstance(FileMatch, null, null, null, searchResult, rawMatch);
|
||||
}
|
||||
|
||||
function stubModelService(instantiationService: TestInstantiationService): IModelService {
|
||||
|
||||
@@ -16,7 +16,7 @@ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { IFileMatch, IFileSearchStats, IFolderQuery, ILineMatch, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService } from 'vs/platform/search/common/search';
|
||||
import { IFileMatch, IFileSearchStats, IFolderQuery, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, ITextSearchResult, TextSearchResult, OneLineRange } from 'vs/platform/search/common/search';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { SearchModel } from 'vs/workbench/parts/search/common/searchModel';
|
||||
@@ -41,6 +41,7 @@ const nullEvent = new class {
|
||||
}
|
||||
};
|
||||
|
||||
const lineOneRange = new OneLineRange(1, 0, 1);
|
||||
|
||||
suite('SearchModel', () => {
|
||||
|
||||
@@ -104,7 +105,11 @@ suite('SearchModel', () => {
|
||||
}
|
||||
|
||||
test('Search Model: Search adds to results', async () => {
|
||||
let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))];
|
||||
let results = [
|
||||
aRawMatch('file://c:/1',
|
||||
new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)),
|
||||
new TextSearchResult('preview 1', new OneLineRange(1, 4, 11))),
|
||||
aRawMatch('file://c:/2', new TextSearchResult('preview 2', lineOneRange))];
|
||||
instantiationService.stub(ISearchService, searchServiceWithResults(results));
|
||||
|
||||
let testObject: SearchModel = instantiationService.createInstance(SearchModel);
|
||||
@@ -130,7 +135,12 @@ suite('SearchModel', () => {
|
||||
|
||||
test('Search Model: Search reports telemetry on search completed', async () => {
|
||||
let target = instantiationService.spy(ITelemetryService, 'publicLog');
|
||||
let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))];
|
||||
let results = [
|
||||
aRawMatch('file://c:/1',
|
||||
new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)),
|
||||
new TextSearchResult('preview 1', new OneLineRange(1, 4, 11))),
|
||||
aRawMatch('file://c:/2',
|
||||
new TextSearchResult('preview 2', lineOneRange))];
|
||||
instantiationService.stub(ISearchService, searchServiceWithResults(results));
|
||||
|
||||
let testObject: SearchModel = instantiationService.createInstance(SearchModel);
|
||||
@@ -168,7 +178,7 @@ suite('SearchModel', () => {
|
||||
instantiationService.stub(ITelemetryService, 'publicLog', target1);
|
||||
|
||||
instantiationService.stub(ISearchService, searchServiceWithResults(
|
||||
[aRawMatch('file://c:/1', aLineMatch('some preview'))],
|
||||
[aRawMatch('file://c:/1', new TextSearchResult('some preview', lineOneRange))],
|
||||
{ results: [], stats: testSearchStats }));
|
||||
|
||||
let testObject = instantiationService.createInstance(SearchModel);
|
||||
@@ -229,7 +239,12 @@ suite('SearchModel', () => {
|
||||
});
|
||||
|
||||
test('Search Model: Search results are cleared during search', async () => {
|
||||
let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))];
|
||||
let results = [
|
||||
aRawMatch('file://c:/1',
|
||||
new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)),
|
||||
new TextSearchResult('preview 1', new OneLineRange(1, 4, 11))),
|
||||
aRawMatch('file://c:/2',
|
||||
new TextSearchResult('preview 2', lineOneRange))];
|
||||
instantiationService.stub(ISearchService, searchServiceWithResults(results));
|
||||
let testObject: SearchModel = instantiationService.createInstance(SearchModel);
|
||||
await testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
|
||||
@@ -254,7 +269,10 @@ suite('SearchModel', () => {
|
||||
});
|
||||
|
||||
test('getReplaceString returns proper replace string for regExpressions', async () => {
|
||||
let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]]))];
|
||||
let results = [
|
||||
aRawMatch('file://c:/1',
|
||||
new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)),
|
||||
new TextSearchResult('preview 1', new OneLineRange(1, 4, 11)))];
|
||||
instantiationService.stub(ISearchService, searchServiceWithResults(results));
|
||||
|
||||
let testObject: SearchModel = instantiationService.createInstance(SearchModel);
|
||||
@@ -281,12 +299,8 @@ suite('SearchModel', () => {
|
||||
assert.equal('helloe', match.replaceString);
|
||||
});
|
||||
|
||||
function aRawMatch(resource: string, ...lineMatches: ILineMatch[]): IFileMatch {
|
||||
return { resource: URI.parse(resource), lineMatches };
|
||||
}
|
||||
|
||||
function aLineMatch(preview: string, lineNumber: number = 1, offsetAndLengths: number[][] = [[0, 1]]): ILineMatch {
|
||||
return { preview, lineNumber, offsetAndLengths };
|
||||
function aRawMatch(resource: string, ...matches: ITextSearchResult[]): IFileMatch {
|
||||
return { resource: URI.parse(resource), matches };
|
||||
}
|
||||
|
||||
function stub(arg1: any, arg2: any, arg3: any): sinon.SinonStub {
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as sinon from 'sinon';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { Match, FileMatch, SearchResult, SearchModel } from 'vs/workbench/parts/search/common/searchModel';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IFileMatch, ILineMatch } from 'vs/platform/search/common/search';
|
||||
import { IFileMatch, TextSearchResult, OneLineRange, ITextSearchResult } from 'vs/platform/search/common/search';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
@@ -19,6 +19,8 @@ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IReplaceService } from 'vs/workbench/parts/search/common/replace';
|
||||
|
||||
const lineOneRange = new OneLineRange(1, 0, 1);
|
||||
|
||||
suite('SearchResult', () => {
|
||||
|
||||
let instantiationService: TestInstantiationService;
|
||||
@@ -33,21 +35,17 @@ suite('SearchResult', () => {
|
||||
|
||||
test('Line Match', function () {
|
||||
let fileMatch = aFileMatch('folder/file.txt', null);
|
||||
let lineMatch = new Match(fileMatch, 'foo bar', 1, 0, 3);
|
||||
let lineMatch = new Match(fileMatch, new TextSearchResult('foo bar', new OneLineRange(1, 0, 3)));
|
||||
assert.equal(lineMatch.text(), 'foo bar');
|
||||
assert.equal(lineMatch.range().startLineNumber, 2);
|
||||
assert.equal(lineMatch.range().endLineNumber, 2);
|
||||
assert.equal(lineMatch.range().startColumn, 1);
|
||||
assert.equal(lineMatch.range().endColumn, 4);
|
||||
assert.equal('file:///folder/file.txt>1>0foo', lineMatch.id());
|
||||
assert.equal('file:///folder/file.txt>[2,1 -> 2,4]foo', lineMatch.id());
|
||||
});
|
||||
|
||||
test('Line Match - Remove', function () {
|
||||
let fileMatch = aFileMatch('folder/file.txt', aSearchResult(), ...[{
|
||||
preview: 'foo bar',
|
||||
lineNumber: 1,
|
||||
offsetAndLengths: [[0, 3]]
|
||||
}]);
|
||||
let fileMatch = aFileMatch('folder/file.txt', aSearchResult(), new TextSearchResult('foo bar', new OneLineRange(1, 0, 3)));
|
||||
let lineMatch = fileMatch.matches()[0];
|
||||
fileMatch.remove(lineMatch);
|
||||
assert.equal(fileMatch.matches().length, 0);
|
||||
@@ -66,15 +64,11 @@ suite('SearchResult', () => {
|
||||
});
|
||||
|
||||
test('File Match: Select an existing match', function () {
|
||||
let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{
|
||||
preview: 'foo',
|
||||
lineNumber: 1,
|
||||
offsetAndLengths: [[0, 3]]
|
||||
}, {
|
||||
preview: 'bar',
|
||||
lineNumber: 1,
|
||||
offsetAndLengths: [[5, 3]]
|
||||
}]);
|
||||
let testObject = aFileMatch(
|
||||
'folder/file.txt',
|
||||
aSearchResult(),
|
||||
new TextSearchResult('foo', new OneLineRange(1, 0, 3)),
|
||||
new TextSearchResult('bar', new OneLineRange(1, 5, 3)));
|
||||
|
||||
testObject.setSelectedMatch(testObject.matches()[0]);
|
||||
|
||||
@@ -82,15 +76,11 @@ suite('SearchResult', () => {
|
||||
});
|
||||
|
||||
test('File Match: Select non existing match', function () {
|
||||
let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{
|
||||
preview: 'foo',
|
||||
lineNumber: 1,
|
||||
offsetAndLengths: [[0, 3]]
|
||||
}, {
|
||||
preview: 'bar',
|
||||
lineNumber: 1,
|
||||
offsetAndLengths: [[5, 3]]
|
||||
}]);
|
||||
let testObject = aFileMatch(
|
||||
'folder/file.txt',
|
||||
aSearchResult(),
|
||||
new TextSearchResult('foo', new OneLineRange(1, 0, 3)),
|
||||
new TextSearchResult('bar', new OneLineRange(1, 5, 3)));
|
||||
let target = testObject.matches()[0];
|
||||
testObject.remove(target);
|
||||
|
||||
@@ -100,15 +90,11 @@ suite('SearchResult', () => {
|
||||
});
|
||||
|
||||
test('File Match: isSelected return true for selected match', function () {
|
||||
let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{
|
||||
preview: 'foo',
|
||||
lineNumber: 1,
|
||||
offsetAndLengths: [[0, 3]]
|
||||
}, {
|
||||
preview: 'bar',
|
||||
lineNumber: 1,
|
||||
offsetAndLengths: [[5, 3]]
|
||||
}]);
|
||||
let testObject = aFileMatch(
|
||||
'folder/file.txt',
|
||||
aSearchResult(),
|
||||
new TextSearchResult('foo', new OneLineRange(1, 0, 3)),
|
||||
new TextSearchResult('bar', new OneLineRange(1, 5, 3)));
|
||||
let target = testObject.matches()[0];
|
||||
testObject.setSelectedMatch(target);
|
||||
|
||||
@@ -116,32 +102,20 @@ suite('SearchResult', () => {
|
||||
});
|
||||
|
||||
test('File Match: isSelected return false for un-selected match', function () {
|
||||
let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{
|
||||
preview: 'foo',
|
||||
lineNumber: 1,
|
||||
offsetAndLengths: [[0, 3]]
|
||||
}, {
|
||||
preview: 'bar',
|
||||
lineNumber: 1,
|
||||
offsetAndLengths: [[5, 3]]
|
||||
}]);
|
||||
|
||||
let testObject = aFileMatch('folder/file.txt',
|
||||
aSearchResult(),
|
||||
new TextSearchResult('foo', new OneLineRange(1, 0, 3)),
|
||||
new TextSearchResult('bar', new OneLineRange(1, 5, 3)));
|
||||
testObject.setSelectedMatch(testObject.matches()[0]);
|
||||
|
||||
assert.ok(!testObject.isMatchSelected(testObject.matches()[1]));
|
||||
});
|
||||
|
||||
test('File Match: unselect', function () {
|
||||
let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{
|
||||
preview: 'foo',
|
||||
lineNumber: 1,
|
||||
offsetAndLengths: [[0, 3]]
|
||||
}, {
|
||||
preview: 'bar',
|
||||
lineNumber: 1,
|
||||
offsetAndLengths: [[5, 3]]
|
||||
}]);
|
||||
|
||||
let testObject = aFileMatch(
|
||||
'folder/file.txt',
|
||||
aSearchResult(),
|
||||
new TextSearchResult('foo', new OneLineRange(1, 0, 3)),
|
||||
new TextSearchResult('bar', new OneLineRange(1, 5, 3)));
|
||||
testObject.setSelectedMatch(testObject.matches()[0]);
|
||||
testObject.setSelectedMatch(null);
|
||||
|
||||
@@ -149,16 +123,11 @@ suite('SearchResult', () => {
|
||||
});
|
||||
|
||||
test('File Match: unselect when not selected', function () {
|
||||
let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{
|
||||
preview: 'foo',
|
||||
lineNumber: 1,
|
||||
offsetAndLengths: [[0, 3]]
|
||||
}, {
|
||||
preview: 'bar',
|
||||
lineNumber: 1,
|
||||
offsetAndLengths: [[5, 3]]
|
||||
}]);
|
||||
|
||||
let testObject = aFileMatch(
|
||||
'folder/file.txt',
|
||||
aSearchResult(),
|
||||
new TextSearchResult('foo', new OneLineRange(1, 0, 3)),
|
||||
new TextSearchResult('bar', new OneLineRange(1, 5, 3)));
|
||||
testObject.setSelectedMatch(null);
|
||||
|
||||
assert.equal(null, testObject.getSelectedMatch());
|
||||
@@ -167,7 +136,7 @@ suite('SearchResult', () => {
|
||||
test('Alle Drei Zusammen', function () {
|
||||
let searchResult = instantiationService.createInstance(SearchResult, null);
|
||||
let fileMatch = aFileMatch('far/boo', searchResult);
|
||||
let lineMatch = new Match(fileMatch, 'foo bar', 1, 0, 3);
|
||||
let lineMatch = new Match(fileMatch, new TextSearchResult('foo bar', new OneLineRange(1, 0, 3)));
|
||||
|
||||
assert(lineMatch.parent() === fileMatch);
|
||||
assert(fileMatch.parent() === searchResult);
|
||||
@@ -175,7 +144,10 @@ suite('SearchResult', () => {
|
||||
|
||||
test('Adding a raw match will add a file match with line matches', function () {
|
||||
let testObject = aSearchResult();
|
||||
let target = [aRawMatch('file://c:/', aLineMatch('preview 1', 1, [[1, 3], [4, 7]]), aLineMatch('preview 2'))];
|
||||
let target = [aRawMatch('file://c:/',
|
||||
new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)),
|
||||
new TextSearchResult('preview 1', new OneLineRange(1, 4, 11)),
|
||||
new TextSearchResult('preview 2', lineOneRange))];
|
||||
|
||||
testObject.add(target);
|
||||
|
||||
@@ -200,7 +172,12 @@ suite('SearchResult', () => {
|
||||
|
||||
test('Adding multiple raw matches', function () {
|
||||
let testObject = aSearchResult();
|
||||
let target = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))];
|
||||
let target = [
|
||||
aRawMatch('file://c:/1',
|
||||
new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)),
|
||||
new TextSearchResult('preview 1', new OneLineRange(1, 4, 11))),
|
||||
aRawMatch('file://c:/2',
|
||||
new TextSearchResult('preview 2', lineOneRange))];
|
||||
|
||||
testObject.add(target);
|
||||
|
||||
@@ -228,7 +205,11 @@ suite('SearchResult', () => {
|
||||
let target2 = sinon.spy();
|
||||
|
||||
let testObject = aSearchResult();
|
||||
testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1')), aRawMatch('file://c:/2', aLineMatch('preview 2'))]);
|
||||
testObject.add([
|
||||
aRawMatch('file://c:/1',
|
||||
new TextSearchResult('preview 1', lineOneRange)),
|
||||
aRawMatch('file://c:/2',
|
||||
new TextSearchResult('preview 2', lineOneRange))]);
|
||||
|
||||
testObject.matches()[0].onDispose(target1);
|
||||
testObject.matches()[1].onDispose(target2);
|
||||
@@ -243,7 +224,9 @@ suite('SearchResult', () => {
|
||||
test('remove triggers change event', function () {
|
||||
let target = sinon.spy();
|
||||
let testObject = aSearchResult();
|
||||
testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1'))]);
|
||||
testObject.add([
|
||||
aRawMatch('file://c:/1',
|
||||
new TextSearchResult('preview 1', lineOneRange))]);
|
||||
let objectRoRemove = testObject.matches()[0];
|
||||
testObject.onChange(target);
|
||||
|
||||
@@ -256,7 +239,9 @@ suite('SearchResult', () => {
|
||||
test('remove triggers change event', function () {
|
||||
let target = sinon.spy();
|
||||
let testObject = aSearchResult();
|
||||
testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1'))]);
|
||||
testObject.add([
|
||||
aRawMatch('file://c:/1',
|
||||
new TextSearchResult('preview 1', lineOneRange))]);
|
||||
let objectRoRemove = testObject.matches()[0];
|
||||
testObject.onChange(target);
|
||||
|
||||
@@ -268,7 +253,9 @@ suite('SearchResult', () => {
|
||||
|
||||
test('Removing all line matches and adding back will add file back to result', function () {
|
||||
let testObject = aSearchResult();
|
||||
testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1'))]);
|
||||
testObject.add([
|
||||
aRawMatch('file://c:/1',
|
||||
new TextSearchResult('preview 1', lineOneRange))]);
|
||||
let target = testObject.matches()[0];
|
||||
let matchToRemove = target.matches()[0];
|
||||
target.remove(matchToRemove);
|
||||
@@ -283,7 +270,9 @@ suite('SearchResult', () => {
|
||||
test('replace should remove the file match', function () {
|
||||
instantiationService.stubPromise(IReplaceService, 'replace', null);
|
||||
let testObject = aSearchResult();
|
||||
testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1'))]);
|
||||
testObject.add([
|
||||
aRawMatch('file://c:/1',
|
||||
new TextSearchResult('preview 1', lineOneRange))]);
|
||||
|
||||
testObject.replace(testObject.matches()[0]);
|
||||
|
||||
@@ -294,7 +283,9 @@ suite('SearchResult', () => {
|
||||
let target = sinon.spy();
|
||||
instantiationService.stubPromise(IReplaceService, 'replace', null);
|
||||
let testObject = aSearchResult();
|
||||
testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1'))]);
|
||||
testObject.add([
|
||||
aRawMatch('file://c:/1',
|
||||
new TextSearchResult('preview 1', lineOneRange))]);
|
||||
testObject.onChange(target);
|
||||
let objectRoRemove = testObject.matches()[0];
|
||||
|
||||
@@ -307,7 +298,11 @@ suite('SearchResult', () => {
|
||||
test('replaceAll should remove all file matches', function () {
|
||||
instantiationService.stubPromise(IReplaceService, 'replace', null);
|
||||
let testObject = aSearchResult();
|
||||
testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1')), aRawMatch('file://c:/2', aLineMatch('preview 2'))]);
|
||||
testObject.add([
|
||||
aRawMatch('file://c:/1',
|
||||
new TextSearchResult('preview 1', lineOneRange)),
|
||||
aRawMatch('file://c:/2',
|
||||
new TextSearchResult('preview 2', lineOneRange))]);
|
||||
|
||||
testObject.replaceAll(null);
|
||||
|
||||
@@ -358,12 +353,12 @@ suite('SearchResult', () => {
|
||||
// lineHasNoDecoration(oneModel, 2);
|
||||
//});
|
||||
|
||||
function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ILineMatch[]): FileMatch {
|
||||
function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ITextSearchResult[]): FileMatch {
|
||||
let rawMatch: IFileMatch = {
|
||||
resource: URI.file('/' + path),
|
||||
lineMatches: lineMatches
|
||||
matches: lineMatches
|
||||
};
|
||||
return instantiationService.createInstance(FileMatch, null, null, searchResult, rawMatch);
|
||||
return instantiationService.createInstance(FileMatch, null, null, null, searchResult, rawMatch);
|
||||
}
|
||||
|
||||
function aSearchResult(): SearchResult {
|
||||
@@ -372,12 +367,8 @@ suite('SearchResult', () => {
|
||||
return searchModel.searchResult;
|
||||
}
|
||||
|
||||
function aRawMatch(resource: string, ...lineMatches: ILineMatch[]): IFileMatch {
|
||||
return { resource: URI.parse(resource), lineMatches };
|
||||
}
|
||||
|
||||
function aLineMatch(preview: string, lineNumber: number = 1, offsetAndLengths: number[][] = [[0, 1]]): ILineMatch {
|
||||
return { preview, lineNumber, offsetAndLengths };
|
||||
function aRawMatch(resource: string, ...matches: ITextSearchResult[]): IFileMatch {
|
||||
return { resource: URI.parse(resource), matches };
|
||||
}
|
||||
|
||||
function stubModelService(instantiationService: TestInstantiationService): IModelService {
|
||||
|
||||
@@ -16,9 +16,10 @@ import * as strings from 'vs/base/common/strings';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as encoding from 'vs/base/node/encoding';
|
||||
import * as extfs from 'vs/base/node/extfs';
|
||||
import { IProgress, ITextSearchStats } from 'vs/platform/search/common/search';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { IProgress, ITextSearchPreviewOptions, ITextSearchStats, TextSearchResult } from 'vs/platform/search/common/search';
|
||||
import { rgPath } from 'vscode-ripgrep';
|
||||
import { FileMatch, IFolderSearch, IRawSearch, ISerializedFileMatch, LineMatch, ISerializedSearchSuccess } from './search';
|
||||
import { FileMatch, IFolderSearch, IRawSearch, ISerializedFileMatch, ISerializedSearchSuccess } from './search';
|
||||
|
||||
// If vscode-ripgrep is in an .asar file, then the binary is unpacked.
|
||||
const rgDiskPath = rgPath.replace(/\bnode_modules\.asar\b/, 'node_modules.asar.unpacked');
|
||||
@@ -77,7 +78,7 @@ export class RipgrepEngine {
|
||||
this.rgProc = cp.spawn(rgDiskPath, rgArgs.args, { cwd });
|
||||
process.once('exit', this.killRgProcFn);
|
||||
|
||||
this.ripgrepParser = new RipgrepParser(this.config.maxResults, cwd, this.config.extraFiles);
|
||||
this.ripgrepParser = new RipgrepParser(this.config.maxResults, cwd, this.config.extraFiles, this.config.previewOptions);
|
||||
this.ripgrepParser.on('result', (match: ISerializedFileMatch) => {
|
||||
if (this.postProcessExclusions) {
|
||||
const handleResultP = (<TPromise<string>>this.postProcessExclusions(match.path, undefined, glob.hasSiblingPromiseFn(() => getSiblings(match.path))))
|
||||
@@ -197,7 +198,7 @@ export class RipgrepParser extends EventEmitter {
|
||||
|
||||
private numResults = 0;
|
||||
|
||||
constructor(private maxResults: number, private rootFolder: string, extraFiles?: string[]) {
|
||||
constructor(private maxResults: number, private rootFolder: string, extraFiles?: string[], private previewOptions?: ITextSearchPreviewOptions) {
|
||||
super();
|
||||
this.stringDecoder = new StringDecoder();
|
||||
|
||||
@@ -275,7 +276,6 @@ export class RipgrepParser extends EventEmitter {
|
||||
text = strings.stripUTF8BOM(text);
|
||||
}
|
||||
|
||||
const lineMatch = new LineMatch(text, lineNum);
|
||||
if (!this.fileMatch) {
|
||||
// When searching a single file and no folderQueries, rg does not print the file line, so create it here
|
||||
const singleFile = this.extraSearchFiles[0];
|
||||
@@ -286,8 +286,6 @@ export class RipgrepParser extends EventEmitter {
|
||||
this.fileMatch = this.getFileMatch(singleFile);
|
||||
}
|
||||
|
||||
this.fileMatch.addMatch(lineMatch);
|
||||
|
||||
let lastMatchEndPos = 0;
|
||||
let matchTextStartPos = -1;
|
||||
|
||||
@@ -296,6 +294,7 @@ export class RipgrepParser extends EventEmitter {
|
||||
let textRealIdx = 0;
|
||||
let hitLimit = false;
|
||||
|
||||
const matchRanges: IRange[] = [];
|
||||
const realTextParts: string[] = [];
|
||||
|
||||
for (let i = 0; i < text.length - (RipgrepParser.MATCH_END_MARKER.length - 1);) {
|
||||
@@ -311,7 +310,7 @@ export class RipgrepParser extends EventEmitter {
|
||||
const chunk = text.slice(matchTextStartPos, i);
|
||||
realTextParts.push(chunk);
|
||||
if (!hitLimit) {
|
||||
lineMatch.addMatch(matchTextStartRealIdx, textRealIdx - matchTextStartRealIdx);
|
||||
matchRanges.push(new Range(lineNum, matchTextStartRealIdx, lineNum, textRealIdx));
|
||||
}
|
||||
|
||||
matchTextStartPos = -1;
|
||||
@@ -336,7 +335,9 @@ export class RipgrepParser extends EventEmitter {
|
||||
|
||||
// Replace preview with version without color codes
|
||||
const preview = realTextParts.join('');
|
||||
lineMatch.preview = preview;
|
||||
matchRanges
|
||||
.map(r => new TextSearchResult(preview, r, this.previewOptions))
|
||||
.forEach(m => this.fileMatch.addMatch(m));
|
||||
|
||||
if (hitLimit) {
|
||||
this.cancel();
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IExpression } from 'vs/base/common/glob';
|
||||
import { IProgress, ILineMatch, IPatternInfo, IFileSearchStats, ISearchEngineStats, ITextSearchStats } from 'vs/platform/search/common/search';
|
||||
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IExpression } from 'vs/base/common/glob';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IFileSearchStats, IPatternInfo, IProgress, ISearchEngineStats, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats } from 'vs/platform/search/common/search';
|
||||
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
export interface IFolderSearch {
|
||||
folder: string;
|
||||
@@ -34,6 +34,7 @@ export interface IRawSearch {
|
||||
maxFilesize?: number;
|
||||
useRipgrep?: boolean;
|
||||
disregardIgnoreFiles?: boolean;
|
||||
previewOptions?: ITextSearchPreviewOptions;
|
||||
}
|
||||
|
||||
export interface ITelemetryEvent {
|
||||
@@ -96,7 +97,7 @@ export function isSerializedSearchSuccess(arg: ISerializedSearchComplete): arg i
|
||||
|
||||
export interface ISerializedFileMatch {
|
||||
path: string;
|
||||
lineMatches?: ILineMatch[];
|
||||
matches?: ITextSearchResult[];
|
||||
numMatches?: number;
|
||||
}
|
||||
|
||||
@@ -107,56 +108,22 @@ export type IFileSearchProgressItem = IRawFileMatch | IRawFileMatch[] | IProgres
|
||||
|
||||
export class FileMatch implements ISerializedFileMatch {
|
||||
path: string;
|
||||
lineMatches: LineMatch[];
|
||||
matches: ITextSearchResult[];
|
||||
|
||||
constructor(path: string) {
|
||||
this.path = path;
|
||||
this.lineMatches = [];
|
||||
this.matches = [];
|
||||
}
|
||||
|
||||
addMatch(lineMatch: LineMatch): void {
|
||||
this.lineMatches.push(lineMatch);
|
||||
addMatch(match: ITextSearchResult): void {
|
||||
this.matches.push(match);
|
||||
}
|
||||
|
||||
serialize(): ISerializedFileMatch {
|
||||
let lineMatches: ILineMatch[] = [];
|
||||
let numMatches = 0;
|
||||
|
||||
for (let i = 0; i < this.lineMatches.length; i++) {
|
||||
numMatches += this.lineMatches[i].offsetAndLengths.length;
|
||||
lineMatches.push(this.lineMatches[i].serialize());
|
||||
}
|
||||
|
||||
return {
|
||||
path: this.path,
|
||||
lineMatches,
|
||||
numMatches
|
||||
matches: this.matches,
|
||||
numMatches: this.matches.length
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class LineMatch implements ILineMatch {
|
||||
preview: string;
|
||||
lineNumber: number;
|
||||
offsetAndLengths: number[][];
|
||||
|
||||
constructor(preview: string, lineNumber: number) {
|
||||
this.preview = preview.replace(/(\r|\n)*$/, '');
|
||||
this.lineNumber = lineNumber;
|
||||
this.offsetAndLengths = [];
|
||||
}
|
||||
|
||||
addMatch(offset: number, length: number): void {
|
||||
this.offsetAndLengths.push([offset, length]);
|
||||
}
|
||||
|
||||
serialize(): ILineMatch {
|
||||
const result = {
|
||||
preview: this.preview,
|
||||
lineNumber: this.lineNumber,
|
||||
offsetAndLengths: this.offsetAndLengths
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -22,13 +22,14 @@ import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IDebugParams, IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { FileMatch, ICachedSearchStats, IFileMatch, IFolderQuery, IProgress, ISearchComplete, ISearchConfiguration, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, LineMatch, pathIncludedInQuery, QueryType, SearchProviderType, IFileSearchStats } from 'vs/platform/search/common/search';
|
||||
import { FileMatch, ICachedSearchStats, IFileMatch, IFolderQuery, IProgress, ISearchComplete, ISearchConfiguration, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, pathIncludedInQuery, QueryType, SearchProviderType, IFileSearchStats, TextSearchResult } from 'vs/platform/search/common/search';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IRawSearch, IRawSearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess } from './search';
|
||||
import { ISearchChannel, SearchChannelClient } from './searchIpc';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
|
||||
export class SearchService extends Disposable implements ISearchService {
|
||||
public _serviceBrand: any;
|
||||
@@ -346,7 +347,10 @@ export class SearchService extends Disposable implements ISearchService {
|
||||
localResults.set(resource, fileMatch);
|
||||
|
||||
matches.forEach((match) => {
|
||||
fileMatch.lineMatches.push(new LineMatch(model.getLineContent(match.range.startLineNumber), match.range.startLineNumber - 1, [[match.range.startColumn - 1, match.range.endColumn - match.range.startColumn]]));
|
||||
fileMatch.matches.push(new TextSearchResult(
|
||||
model.getLineContent(match.range.startLineNumber),
|
||||
new Range(match.range.startLineNumber - 1, match.range.startColumn - 1, match.range.startLineNumber - 1, match.range.endColumn),
|
||||
query.previewOptions));
|
||||
});
|
||||
} else {
|
||||
localResults.set(resource, null);
|
||||
@@ -458,7 +462,8 @@ export class DiskSearch implements ISearchResultProvider {
|
||||
cacheKey: query.cacheKey,
|
||||
useRipgrep: query.useRipgrep,
|
||||
disregardIgnoreFiles: query.disregardIgnoreFiles,
|
||||
ignoreSymlinks: query.ignoreSymlinks
|
||||
ignoreSymlinks: query.ignoreSymlinks,
|
||||
previewOptions: query.previewOptions
|
||||
};
|
||||
|
||||
for (const q of existingFolders) {
|
||||
@@ -536,10 +541,8 @@ export class DiskSearch implements ISearchResultProvider {
|
||||
|
||||
private static createFileMatch(data: ISerializedFileMatch): FileMatch {
|
||||
let fileMatch = new FileMatch(uri.file(data.path));
|
||||
if (data.lineMatches) {
|
||||
for (let j = 0; j < data.lineMatches.length; j++) {
|
||||
fileMatch.lineMatches.push(new LineMatch(data.lineMatches[j].preview, data.lineMatches[j].lineNumber, data.lineMatches[j].offsetAndLengths));
|
||||
}
|
||||
if (data.matches) {
|
||||
fileMatch.matches.push(...data.matches); // TODO why
|
||||
}
|
||||
return fileMatch;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { IProgress } from 'vs/platform/search/common/search';
|
||||
import { FileWalker } from 'vs/workbench/services/search/node/fileSearch';
|
||||
import { IRawSearch, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch } from './search';
|
||||
import { ITextSearchWorkerProvider } from './textSearchWorkerProvider';
|
||||
import { ISearchWorker } from './worker/searchWorkerIpc';
|
||||
import { ISearchWorker, ISearchWorkerSearchArgs } from './worker/searchWorkerIpc';
|
||||
|
||||
export class Engine implements ISearchEngine<ISerializedFileMatch[]> {
|
||||
|
||||
@@ -95,7 +95,7 @@ export class Engine implements ISearchEngine<ISerializedFileMatch[]> {
|
||||
this.nextWorker = (this.nextWorker + 1) % this.workers.length;
|
||||
|
||||
const maxResults = this.config.maxResults && (this.config.maxResults - this.numResults);
|
||||
const searchArgs = { absolutePaths: batch, maxResults, pattern: this.config.contentPattern, fileEncoding };
|
||||
const searchArgs: ISearchWorkerSearchArgs = { absolutePaths: batch, maxResults, pattern: this.config.contentPattern, fileEncoding, previewOptions: this.config.previewOptions };
|
||||
worker.search(searchArgs).then(result => {
|
||||
if (!result || this.limitReached || this.isCanceled) {
|
||||
return unwind(batchBytes);
|
||||
|
||||
@@ -7,16 +7,17 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as gracefulFs from 'graceful-fs';
|
||||
gracefulFs.gracefulify(fs);
|
||||
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { LineMatch, FileMatch } from '../search';
|
||||
import { UTF16le, UTF16be, UTF8, UTF8_with_bom, encodingExists, decode, bomLength, detectEncodingFromBuffer } from 'vs/base/node/encoding';
|
||||
|
||||
import { bomLength, decode, detectEncodingFromBuffer, encodingExists, UTF16be, UTF16le, UTF8, UTF8_with_bom } from 'vs/base/node/encoding';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ITextSearchPreviewOptions, TextSearchResult } from 'vs/platform/search/common/search';
|
||||
import { FileMatch } from '../search';
|
||||
import { ISearchWorker, ISearchWorkerSearchArgs, ISearchWorkerSearchResult } from './searchWorkerIpc';
|
||||
|
||||
gracefulFs.gracefulify(fs);
|
||||
|
||||
interface ReadLinesOptions {
|
||||
bufferLength: number;
|
||||
encoding: string;
|
||||
@@ -95,7 +96,7 @@ export class SearchWorkerEngine {
|
||||
|
||||
// Search in the given path, and when it's finished, search in the next path in absolutePaths
|
||||
const startSearchInFile = (absolutePath: string): TPromise<void> => {
|
||||
return this.searchInFile(absolutePath, contentPattern, fileEncoding, args.maxResults && (args.maxResults - result.numMatches)).then(fileResult => {
|
||||
return this.searchInFile(absolutePath, contentPattern, fileEncoding, args.maxResults && (args.maxResults - result.numMatches), args.previewOptions).then(fileResult => {
|
||||
// Finish early if search is canceled
|
||||
if (this.isCanceled) {
|
||||
return;
|
||||
@@ -124,13 +125,12 @@ export class SearchWorkerEngine {
|
||||
this.isCanceled = true;
|
||||
}
|
||||
|
||||
private searchInFile(absolutePath: string, contentPattern: RegExp, fileEncoding: string, maxResults?: number): TPromise<IFileSearchResult> {
|
||||
private searchInFile(absolutePath: string, contentPattern: RegExp, fileEncoding: string, maxResults?: number, previewOptions?: ITextSearchPreviewOptions): TPromise<IFileSearchResult> {
|
||||
let fileMatch: FileMatch = null;
|
||||
let limitReached = false;
|
||||
let numMatches = 0;
|
||||
|
||||
const perLineCallback = (line: string, lineNumber: number) => {
|
||||
let lineMatch: LineMatch = null;
|
||||
let match = contentPattern.exec(line);
|
||||
|
||||
// Record all matches into file result
|
||||
@@ -139,12 +139,8 @@ export class SearchWorkerEngine {
|
||||
fileMatch = new FileMatch(absolutePath);
|
||||
}
|
||||
|
||||
if (lineMatch === null) {
|
||||
lineMatch = new LineMatch(line, lineNumber);
|
||||
fileMatch.addMatch(lineMatch);
|
||||
}
|
||||
|
||||
lineMatch.addMatch(match.index, match[0].length);
|
||||
const lineMatch = new TextSearchResult(line, new Range(lineNumber, match.index, lineNumber, match.index + match[0].length), previewOptions);
|
||||
fileMatch.addMatch(lineMatch);
|
||||
|
||||
numMatches++;
|
||||
if (maxResults && numMatches >= maxResults) {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IChannel } from 'vs/base/parts/ipc/node/ipc';
|
||||
import { ISerializedFileMatch } from '../search';
|
||||
import { IPatternInfo } from 'vs/platform/search/common/search';
|
||||
import { IPatternInfo, ITextSearchPreviewOptions } from 'vs/platform/search/common/search';
|
||||
import { SearchWorker } from './searchWorker';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
@@ -17,6 +17,7 @@ export interface ISearchWorkerSearchArgs {
|
||||
fileEncoding: string;
|
||||
absolutePaths: string[];
|
||||
maxResults?: number;
|
||||
previewOptions?: ITextSearchPreviewOptions;
|
||||
}
|
||||
|
||||
export interface ISearchWorkerSearchResult {
|
||||
|
||||
@@ -5,16 +5,13 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as path from 'path';
|
||||
import * as assert from 'assert';
|
||||
|
||||
import * as path from 'path';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
|
||||
import { RipgrepParser, getAbsoluteGlob, fixDriveC, fixRegexEndingPattern } from 'vs/workbench/services/search/node/ripgrepTextSearch';
|
||||
import { fixDriveC, fixRegexEndingPattern, getAbsoluteGlob, RipgrepParser } from 'vs/workbench/services/search/node/ripgrepTextSearch';
|
||||
import { ISerializedFileMatch } from 'vs/workbench/services/search/node/search';
|
||||
|
||||
|
||||
suite('RipgrepParser', () => {
|
||||
const rootFolder = '/workspace';
|
||||
const fileSectionEnd = '\n';
|
||||
@@ -76,16 +73,40 @@ suite('RipgrepParser', () => {
|
||||
<ISerializedFileMatch>{
|
||||
numMatches: 2,
|
||||
path: path.join(rootFolder, 'a.txt'),
|
||||
lineMatches: [
|
||||
matches: [
|
||||
{
|
||||
lineNumber: 0,
|
||||
preview: 'beforematchafter',
|
||||
offsetAndLengths: [[6, 5]]
|
||||
preview: {
|
||||
match: {
|
||||
endColumn: 11,
|
||||
endLineNumber: 0,
|
||||
startColumn: 6,
|
||||
startLineNumber: 0,
|
||||
},
|
||||
text: 'beforematchafter'
|
||||
},
|
||||
range: {
|
||||
endColumn: 11,
|
||||
endLineNumber: 0,
|
||||
startColumn: 6,
|
||||
startLineNumber: 0,
|
||||
}
|
||||
},
|
||||
{
|
||||
lineNumber: 1,
|
||||
preview: 'beforematchafter',
|
||||
offsetAndLengths: [[6, 5]]
|
||||
preview: {
|
||||
match: {
|
||||
endColumn: 11,
|
||||
endLineNumber: 0,
|
||||
startColumn: 6,
|
||||
startLineNumber: 0,
|
||||
},
|
||||
text: 'beforematchafter'
|
||||
},
|
||||
range: {
|
||||
endColumn: 11,
|
||||
endLineNumber: 1,
|
||||
startColumn: 6,
|
||||
startLineNumber: 1,
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -168,8 +189,9 @@ suite('RipgrepParser', () => {
|
||||
|
||||
const results = parseInput(inputBufs);
|
||||
assert.equal(results.length, 1);
|
||||
assert.equal(results[0].lineMatches.length, 1);
|
||||
assert.deepEqual(results[0].lineMatches[0].offsetAndLengths, [[7, 5]]);
|
||||
assert.equal(results[0].matches.length, 1);
|
||||
assert.equal(results[0].matches[0].range.startColumn, 7);
|
||||
assert.equal(results[0].matches[0].range.endColumn, 12);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -650,14 +650,15 @@ suite('ExtHostSearch', () => {
|
||||
const actualTextSearchResults: vscode.TextSearchResult[] = [];
|
||||
for (let fileMatch of actual) {
|
||||
// Make relative
|
||||
for (let lineMatch of fileMatch.lineMatches) {
|
||||
for (let [offset, length] of lineMatch.offsetAndLengths) {
|
||||
actualTextSearchResults.push({
|
||||
preview: { text: lineMatch.preview, match: null },
|
||||
range: new Range(lineMatch.lineNumber, offset, lineMatch.lineNumber, length + offset),
|
||||
uri: fileMatch.resource
|
||||
});
|
||||
}
|
||||
for (let lineMatch of fileMatch.matches) {
|
||||
actualTextSearchResults.push({
|
||||
preview: {
|
||||
text: lineMatch.preview.text,
|
||||
match: new Range(lineMatch.preview.match.startLineNumber, lineMatch.preview.match.startColumn, lineMatch.preview.match.endLineNumber, lineMatch.preview.match.endColumn)
|
||||
},
|
||||
range: new Range(lineMatch.range.startLineNumber, lineMatch.range.startColumn, lineMatch.range.endLineNumber, lineMatch.range.endColumn),
|
||||
uri: fileMatch.resource
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user