Skip to content

Commit 0fb670f

Browse files
committed
Fix #9049 - text search API
1 parent a739c62 commit 0fb670f

File tree

5 files changed

+159
-63
lines changed

5 files changed

+159
-63
lines changed

src/vs/vscode.proposed.d.ts

+15-3
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ declare module 'vscode' {
1717

1818
export interface TextSearchQuery {
1919
pattern: string;
20-
isRegExp: boolean;
21-
isCaseSensitive: boolean;
22-
isWordMatch: boolean;
20+
isRegExp?: boolean;
21+
isCaseSensitive?: boolean;
22+
isWordMatch?: boolean;
2323
}
2424

2525
export interface SearchOptions {
@@ -51,8 +51,20 @@ declare module 'vscode' {
5151
provideTextSearchResults?(query: TextSearchQuery, options: TextSearchOptions, progress: Progress<TextSearchResult>, token: CancellationToken): Thenable<void>;
5252
}
5353

54+
export interface FindTextInFilesOptions {
55+
includes?: GlobPattern[];
56+
excludes?: GlobPattern[];
57+
maxResults?: number;
58+
useIgnoreFiles?: boolean;
59+
followSymlinks?: boolean;
60+
encoding?: string;
61+
}
62+
5463
export namespace workspace {
5564
export function registerSearchProvider(scheme: string, provider: SearchProvider): Disposable;
65+
66+
// Maybe keep includes and excludes separate to mirror findFiles and make it more natural to pass 'null' to disable excludes
67+
export function findTextInFiles(query: TextSearchQuery, options: FindTextInFilesOptions, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable<void>;
5668
}
5769

5870
//#endregion

src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts

+44-10
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,20 @@
55
'use strict';
66

77
import { isPromiseCanceledError } from 'vs/base/common/errors';
8+
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
89
import URI, { UriComponents } from 'vs/base/common/uri';
9-
import { ISearchService, QueryType, ISearchQuery, IFolderQuery, ISearchConfiguration } from 'vs/platform/search/common/search';
10-
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
11-
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
1210
import { TPromise } from 'vs/base/common/winjs.base';
13-
import { MainThreadWorkspaceShape, ExtHostWorkspaceShape, ExtHostContext, MainContext, IExtHostContext } from '../node/extHost.protocol';
14-
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
15-
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
16-
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
17-
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
1811
import { localize } from 'vs/nls';
12+
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
13+
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
14+
import { IFileMatch, IFolderQuery, IPatternInfo, IQueryOptions, ISearchConfiguration, ISearchQuery, ISearchService, QueryType } from 'vs/platform/search/common/search';
1915
import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
16+
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
17+
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
18+
import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder';
19+
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
20+
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
21+
import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape } from '../node/extHost.protocol';
2022

2123
@extHostNamedCustomer(MainContext.MainThreadWorkspace)
2224
export class MainThreadWorkspace implements MainThreadWorkspaceShape {
@@ -32,7 +34,8 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
3234
@ITextFileService private readonly _textFileService: ITextFileService,
3335
@IConfigurationService private readonly _configurationService: IConfigurationService,
3436
@IWorkspaceEditingService private readonly _workspaceEditingService: IWorkspaceEditingService,
35-
@IStatusbarService private readonly _statusbarService: IStatusbarService
37+
@IStatusbarService private readonly _statusbarService: IStatusbarService,
38+
@IInstantiationService private readonly _instantiationService: IInstantiationService,
3639
) {
3740
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostWorkspace);
3841
this._contextService.onDidChangeWorkspaceFolders(this._onDidChangeWorkspace, this, this._toDispose);
@@ -97,7 +100,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
97100

98101
// --- search ---
99102

100-
$startSearch(includePattern: string, includeFolder: string, excludePatternOrDisregardExcludes: string | false, maxResults: number, requestId: number): Thenable<URI[]> {
103+
$startFileSearch(includePattern: string, includeFolder: string, excludePatternOrDisregardExcludes: string | false, maxResults: number, requestId: number): Thenable<URI[]> {
101104
const workspace = this._contextService.getWorkspace();
102105
if (!workspace.folders.length) {
103106
return undefined;
@@ -158,6 +161,37 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
158161
return search;
159162
}
160163

164+
$startTextSearch(pattern: IPatternInfo, options: IQueryOptions, requestId: number): TPromise<void, IFileMatch> {
165+
const workspace = this._contextService.getWorkspace();
166+
const folders = workspace.folders.map(folder => folder.uri);
167+
168+
const queryBuilder = this._instantiationService.createInstance(QueryBuilder);
169+
const query = queryBuilder.text(pattern, folders, options);
170+
171+
return new TPromise((resolve, reject) => {
172+
const search = this._searchService.search(query).then(
173+
() => {
174+
delete this._activeSearches[requestId];
175+
resolve(null);
176+
},
177+
err => {
178+
delete this._activeSearches[requestId];
179+
if (!isPromiseCanceledError(err)) {
180+
reject(TPromise.wrapError(err));
181+
}
182+
183+
return undefined;
184+
},
185+
p => {
186+
if (p.lineMatches) {
187+
this._proxy.$handleTextSearchResult(p, requestId);
188+
}
189+
});
190+
191+
this._activeSearches[requestId] = search;
192+
});
193+
}
194+
161195
$cancelSearch(requestId: number): Thenable<boolean> {
162196
const search = this._activeSearches[requestId];
163197
if (search) {

src/vs/workbench/api/node/extHost.api.impl.ts

+3
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,9 @@ export function createApiFactory(
500500
findFiles: (include, exclude, maxResults?, token?) => {
501501
return extHostWorkspace.findFiles(typeConverters.GlobPattern.from(include), typeConverters.GlobPattern.from(exclude), maxResults, extension.id, token);
502502
},
503+
findTextInFiles: (query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, token?: vscode.CancellationToken) => {
504+
return extHostWorkspace.findTextInFiles(query, options, callback, extension.id, token);
505+
},
503506
saveAll: (includeUntitled?) => {
504507
return extHostWorkspace.saveAll(includeUntitled);
505508
},

src/vs/workbench/api/node/extHost.protocol.ts

+31-38
Original file line numberDiff line numberDiff line change
@@ -4,52 +4,43 @@
44
*--------------------------------------------------------------------------------------------*/
55
'use strict';
66

7-
import { createMainContextProxyIdentifier as createMainId, createExtHostContextProxyIdentifier as createExtId, ProxyIdentifier, IRPCProtocol } from 'vs/workbench/services/extensions/node/proxyIdentifier';
8-
9-
import * as vscode from 'vscode';
10-
11-
import URI, { UriComponents } from 'vs/base/common/uri';
7+
import { SerializedError } from 'vs/base/common/errors';
8+
import { IDisposable } from 'vs/base/common/lifecycle';
129
import Severity from 'vs/base/common/severity';
10+
import URI, { UriComponents } from 'vs/base/common/uri';
1311
import { TPromise } from 'vs/base/common/winjs.base';
14-
15-
import { IMarkerData } from 'vs/platform/markers/common/markers';
16-
import { EditorViewColumn } from 'vs/workbench/api/shared/editor';
17-
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
18-
import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar';
19-
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
20-
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
21-
import { IProgressOptions, IProgressStep } from 'vs/workbench/services/progress/common/progress';
22-
23-
import * as editorCommon from 'vs/editor/common/editorCommon';
24-
import * as modes from 'vs/editor/common/modes';
25-
26-
import { IConfigurationData, ConfigurationTarget, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
27-
import { IConfig, IAdapterExecutable, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug';
28-
29-
import { IQuickPickItem, IPickOptions, IQuickInputButton } from 'vs/platform/quickinput/common/quickInput';
30-
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
3112
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
32-
import { EndOfLine, TextEditorLineNumbersStyle, IFileOperationOptions } from 'vs/workbench/api/node/extHostTypes';
33-
34-
35-
import { TaskSet } from 'vs/workbench/parts/tasks/common/tasks';
36-
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
3713
import { IPosition } from 'vs/editor/common/core/position';
3814
import { IRange } from 'vs/editor/common/core/range';
3915
import { ISelection, Selection } from 'vs/editor/common/core/selection';
40-
41-
import { ITreeItem } from 'vs/workbench/common/views';
42-
import { ThemeColor } from 'vs/platform/theme/common/themeService';
43-
import { IDisposable } from 'vs/base/common/lifecycle';
44-
import { SerializedError } from 'vs/base/common/errors';
45-
import { IStat, FileChangeType, IWatchOptions, FileSystemProviderCapabilities, FileWriteOptions, FileType, FileOverwriteOptions, FileDeleteOptions } from 'vs/platform/files/common/files';
46-
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
47-
import { CommentRule, CharacterPair, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
16+
import * as editorCommon from 'vs/editor/common/editorCommon';
4817
import { ISingleEditOperation } from 'vs/editor/common/model';
49-
import { IPatternInfo, IRawSearchQuery, IRawFileMatch2, ISearchCompleteStats } from 'vs/platform/search/common/search';
18+
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
19+
import * as modes from 'vs/editor/common/modes';
20+
import { CharacterPair, CommentRule, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
21+
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
22+
import { ConfigurationTarget, IConfigurationData, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
23+
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
24+
import { FileChangeType, FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, FileWriteOptions, IStat, IWatchOptions } from 'vs/platform/files/common/files';
5025
import { LogLevel } from 'vs/platform/log/common/log';
51-
import { TaskExecutionDTO, TaskDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO } from 'vs/workbench/api/shared/tasks';
26+
import { IMarkerData } from 'vs/platform/markers/common/markers';
27+
import { IPickOptions, IQuickInputButton, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
28+
import { IPatternInfo, IQueryOptions, IRawFileMatch2, IRawSearchQuery, ISearchCompleteStats } from 'vs/platform/search/common/search';
29+
import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar';
30+
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
31+
import { ThemeColor } from 'vs/platform/theme/common/themeService';
32+
import { EndOfLine, IFileOperationOptions, TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes';
33+
import { EditorViewColumn } from 'vs/workbench/api/shared/editor';
34+
import { TaskDTO, TaskExecutionDTO, TaskFilterDTO, TaskHandleDTO, TaskProcessEndedDTO, TaskProcessStartedDTO, TaskSystemInfoDTO } from 'vs/workbench/api/shared/tasks';
35+
import { ITreeItem } from 'vs/workbench/common/views';
36+
import { IAdapterExecutable, IConfig, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug';
37+
import { TaskSet } from 'vs/workbench/parts/tasks/common/tasks';
5238
import { ITerminalDimensions } from 'vs/workbench/parts/terminal/common/terminal';
39+
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
40+
import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol, ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
41+
import { IProgressOptions, IProgressStep } from 'vs/workbench/services/progress/common/progress';
42+
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
43+
import * as vscode from 'vscode';
5344

5445
export interface IEnvironment {
5546
isExtensionDevelopmentDebug: boolean;
@@ -471,7 +462,8 @@ export interface ExtHostUrlsShape {
471462
}
472463

473464
export interface MainThreadWorkspaceShape extends IDisposable {
474-
$startSearch(includePattern: string, includeFolder: string, excludePatternOrDisregardExcludes: string | false, maxResults: number, requestId: number): Thenable<UriComponents[]>;
465+
$startFileSearch(includePattern: string, includeFolder: string, excludePatternOrDisregardExcludes: string | false, maxResults: number, requestId: number): Thenable<UriComponents[]>;
466+
$startTextSearch(query: IPatternInfo, options: IQueryOptions, requestId: number): TPromise<void>;
475467
$cancelSearch(requestId: number): Thenable<boolean>;
476468
$saveAll(includeUntitled?: boolean): Thenable<boolean>;
477469
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string }[]): Thenable<void>;
@@ -671,6 +663,7 @@ export interface ExtHostTreeViewsShape {
671663

672664
export interface ExtHostWorkspaceShape {
673665
$acceptWorkspaceData(workspace: IWorkspaceData): void;
666+
$handleTextSearchResult(result: IRawFileMatch2, requestId: number): void;
674667
}
675668

676669
export interface ExtHostFileSystemShape {

src/vs/workbench/api/node/extHostWorkspace.ts

+66-12
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,25 @@
44
*--------------------------------------------------------------------------------------------*/
55
'use strict';
66

7-
import URI from 'vs/base/common/uri';
8-
import { Event, Emitter } from 'vs/base/common/event';
9-
import { normalize } from 'vs/base/common/paths';
7+
import { posix, relative } from 'path';
108
import { delta as arrayDelta } from 'vs/base/common/arrays';
11-
import { relative, posix } from 'path';
12-
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
13-
import { IWorkspaceData, ExtHostWorkspaceShape, MainContext, MainThreadWorkspaceShape, IMainContext, MainThreadMessageServiceShape } from './extHost.protocol';
14-
import * as vscode from 'vscode';
15-
import { compare } from 'vs/base/common/strings';
9+
import { Emitter, Event } from 'vs/base/common/event';
1610
import { TernarySearchTree } from 'vs/base/common/map';
17-
import { basenameOrAuthority, isEqual } from 'vs/base/common/resources';
11+
import { normalize } from 'vs/base/common/paths';
1812
import { isLinux } from 'vs/base/common/platform';
19-
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
13+
import { basenameOrAuthority, isEqual } from 'vs/base/common/resources';
14+
import { compare } from 'vs/base/common/strings';
15+
import URI from 'vs/base/common/uri';
16+
import { TPromise } from 'vs/base/common/winjs.base';
2017
import { localize } from 'vs/nls';
21-
import { Severity } from 'vs/platform/notification/common/notification';
2218
import { ILogService } from 'vs/platform/log/common/log';
19+
import { Severity } from 'vs/platform/notification/common/notification';
20+
import { IQueryOptions, IRawFileMatch2 } from 'vs/platform/search/common/search';
21+
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
22+
import { Range } from 'vs/workbench/api/node/extHostTypes';
23+
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
24+
import * as vscode from 'vscode';
25+
import { ExtHostWorkspaceShape, IMainContext, IWorkspaceData, MainContext, MainThreadMessageServiceShape, MainThreadWorkspaceShape } from './extHost.protocol';
2326

2427
function isFolderEqual(folderA: URI, folderB: URI): boolean {
2528
return isEqual(folderA, folderB, !isLinux);
@@ -145,6 +148,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
145148

146149
readonly onDidChangeWorkspace: Event<vscode.WorkspaceFoldersChangeEvent> = this._onDidChangeWorkspace.event;
147150

151+
private readonly _activeSearchCallbacks = [];
152+
148153
constructor(
149154
mainContext: IMainContext,
150155
data: IWorkspaceData,
@@ -358,13 +363,62 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
358363
}
359364
}
360365

361-
const result = this._proxy.$startSearch(includePattern, includeFolder, excludePatternOrDisregardExcludes, maxResults, requestId);
366+
const result = this._proxy.$startFileSearch(includePattern, includeFolder, excludePatternOrDisregardExcludes, maxResults, requestId);
362367
if (token) {
363368
token.onCancellationRequested(() => this._proxy.$cancelSearch(requestId));
364369
}
365370
return result.then(data => Array.isArray(data) ? data.map(URI.revive) : []);
366371
}
367372

373+
findTextInFiles(query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, extensionId: string, token?: vscode.CancellationToken) {
374+
this._logService.trace(`extHostWorkspace#findTextInFiles: textSearch, extension: ${extensionId}, entryPoint: findTextInFiles`);
375+
376+
const requestId = ExtHostWorkspace._requestIdPool++;
377+
378+
const queryOptions: IQueryOptions = {
379+
ignoreSymlinks: typeof options.followSymlinks === 'boolean' ? !options.followSymlinks : undefined,
380+
disregardIgnoreFiles: typeof options.useIgnoreFiles === 'boolean' ? !options.useIgnoreFiles : undefined,
381+
fileEncoding: options.encoding,
382+
maxResults: options.maxResults,
383+
384+
// TODO
385+
// includePattern: options.includes
386+
// excludePattern: options.excludes
387+
};
388+
389+
this._activeSearchCallbacks[requestId] = p => {
390+
p.lineMatches.forEach(lineMatch => {
391+
lineMatch.offsetAndLengths.forEach(offsetAndLength => {
392+
const range = new Range(lineMatch.lineNumber, offsetAndLength[0], lineMatch.lineNumber, offsetAndLength[0] + offsetAndLength[1]);
393+
callback({
394+
path: URI.revive(p.resource).fsPath,
395+
preview: { text: lineMatch.preview, match: range },
396+
range
397+
});
398+
});
399+
});
400+
};
401+
402+
if (token) {
403+
token.onCancellationRequested(() => this._proxy.$cancelSearch(requestId));
404+
}
405+
406+
return this._proxy.$startTextSearch(query, queryOptions, requestId).then(
407+
() => {
408+
delete this._activeSearchCallbacks[requestId];
409+
},
410+
err => {
411+
delete this._activeSearchCallbacks[requestId];
412+
return TPromise.wrapError(err);
413+
});
414+
}
415+
416+
$handleTextSearchResult(result: IRawFileMatch2, requestId: number): void {
417+
if (this._activeSearchCallbacks[requestId]) {
418+
this._activeSearchCallbacks[requestId](result);
419+
}
420+
}
421+
368422
saveAll(includeUntitled?: boolean): Thenable<boolean> {
369423
return this._proxy.$saveAll(includeUntitled);
370424
}

0 commit comments

Comments
 (0)