提交 0fb670f9 编写于 作者: R Rob Lourens

Fix #9049 - text search API

上级 a739c620
......@@ -17,9 +17,9 @@ declare module 'vscode' {
export interface TextSearchQuery {
pattern: string;
isRegExp: boolean;
isCaseSensitive: boolean;
isWordMatch: boolean;
isRegExp?: boolean;
isCaseSensitive?: boolean;
isWordMatch?: boolean;
}
export interface SearchOptions {
......@@ -51,8 +51,20 @@ declare module 'vscode' {
provideTextSearchResults?(query: TextSearchQuery, options: TextSearchOptions, progress: Progress<TextSearchResult>, token: CancellationToken): Thenable<void>;
}
export interface FindTextInFilesOptions {
includes?: GlobPattern[];
excludes?: GlobPattern[];
maxResults?: number;
useIgnoreFiles?: boolean;
followSymlinks?: boolean;
encoding?: string;
}
export namespace workspace {
export function registerSearchProvider(scheme: string, provider: SearchProvider): Disposable;
// Maybe keep includes and excludes separate to mirror findFiles and make it more natural to pass 'null' to disable excludes
export function findTextInFiles(query: TextSearchQuery, options: FindTextInFilesOptions, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable<void>;
}
//#endregion
......
......@@ -5,18 +5,20 @@
'use strict';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import URI, { UriComponents } from 'vs/base/common/uri';
import { ISearchService, QueryType, ISearchQuery, IFolderQuery, ISearchConfiguration } from 'vs/platform/search/common/search';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { TPromise } from 'vs/base/common/winjs.base';
import { MainThreadWorkspaceShape, ExtHostWorkspaceShape, ExtHostContext, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import { localize } from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IFileMatch, IFolderQuery, IPatternInfo, IQueryOptions, ISearchConfiguration, ISearchQuery, ISearchService, QueryType } from 'vs/platform/search/common/search';
import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape } from '../node/extHost.protocol';
@extHostNamedCustomer(MainContext.MainThreadWorkspace)
export class MainThreadWorkspace implements MainThreadWorkspaceShape {
......@@ -32,7 +34,8 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
@ITextFileService private readonly _textFileService: ITextFileService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IWorkspaceEditingService private readonly _workspaceEditingService: IWorkspaceEditingService,
@IStatusbarService private readonly _statusbarService: IStatusbarService
@IStatusbarService private readonly _statusbarService: IStatusbarService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostWorkspace);
this._contextService.onDidChangeWorkspaceFolders(this._onDidChangeWorkspace, this, this._toDispose);
......@@ -97,7 +100,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
// --- search ---
$startSearch(includePattern: string, includeFolder: string, excludePatternOrDisregardExcludes: string | false, maxResults: number, requestId: number): Thenable<URI[]> {
$startFileSearch(includePattern: string, includeFolder: string, excludePatternOrDisregardExcludes: string | false, maxResults: number, requestId: number): Thenable<URI[]> {
const workspace = this._contextService.getWorkspace();
if (!workspace.folders.length) {
return undefined;
......@@ -158,6 +161,37 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
return search;
}
$startTextSearch(pattern: IPatternInfo, options: IQueryOptions, requestId: number): TPromise<void, IFileMatch> {
const workspace = this._contextService.getWorkspace();
const folders = workspace.folders.map(folder => folder.uri);
const queryBuilder = this._instantiationService.createInstance(QueryBuilder);
const query = queryBuilder.text(pattern, folders, options);
return new TPromise((resolve, reject) => {
const search = this._searchService.search(query).then(
() => {
delete this._activeSearches[requestId];
resolve(null);
},
err => {
delete this._activeSearches[requestId];
if (!isPromiseCanceledError(err)) {
reject(TPromise.wrapError(err));
}
return undefined;
},
p => {
if (p.lineMatches) {
this._proxy.$handleTextSearchResult(p, requestId);
}
});
this._activeSearches[requestId] = search;
});
}
$cancelSearch(requestId: number): Thenable<boolean> {
const search = this._activeSearches[requestId];
if (search) {
......
......@@ -500,6 +500,9 @@ export function createApiFactory(
findFiles: (include, exclude, maxResults?, token?) => {
return extHostWorkspace.findFiles(typeConverters.GlobPattern.from(include), typeConverters.GlobPattern.from(exclude), maxResults, extension.id, token);
},
findTextInFiles: (query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, token?: vscode.CancellationToken) => {
return extHostWorkspace.findTextInFiles(query, options, callback, extension.id, token);
},
saveAll: (includeUntitled?) => {
return extHostWorkspace.saveAll(includeUntitled);
},
......
......@@ -4,52 +4,43 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { createMainContextProxyIdentifier as createMainId, createExtHostContextProxyIdentifier as createExtId, ProxyIdentifier, IRPCProtocol } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import * as vscode from 'vscode';
import URI, { UriComponents } from 'vs/base/common/uri';
import { SerializedError } from 'vs/base/common/errors';
import { IDisposable } from 'vs/base/common/lifecycle';
import Severity from 'vs/base/common/severity';
import URI, { UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IMarkerData } from 'vs/platform/markers/common/markers';
import { EditorViewColumn } from 'vs/workbench/api/shared/editor';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar';
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { IProgressOptions, IProgressStep } from 'vs/workbench/services/progress/common/progress';
import * as editorCommon from 'vs/editor/common/editorCommon';
import * as modes from 'vs/editor/common/modes';
import { IConfigurationData, ConfigurationTarget, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { IConfig, IAdapterExecutable, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug';
import { IQuickPickItem, IPickOptions, IQuickInputButton } from 'vs/platform/quickinput/common/quickInput';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
import { EndOfLine, TextEditorLineNumbersStyle, IFileOperationOptions } from 'vs/workbench/api/node/extHostTypes';
import { TaskSet } from 'vs/workbench/parts/tasks/common/tasks';
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange } from 'vs/editor/common/core/range';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
import { ITreeItem } from 'vs/workbench/common/views';
import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { IDisposable } from 'vs/base/common/lifecycle';
import { SerializedError } from 'vs/base/common/errors';
import { IStat, FileChangeType, IWatchOptions, FileSystemProviderCapabilities, FileWriteOptions, FileType, FileOverwriteOptions, FileDeleteOptions } from 'vs/platform/files/common/files';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { CommentRule, CharacterPair, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ISingleEditOperation } from 'vs/editor/common/model';
import { IPatternInfo, IRawSearchQuery, IRawFileMatch2, ISearchCompleteStats } from 'vs/platform/search/common/search';
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import * as modes from 'vs/editor/common/modes';
import { CharacterPair, CommentRule, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ConfigurationTarget, IConfigurationData, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { FileChangeType, FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, FileWriteOptions, IStat, IWatchOptions } from 'vs/platform/files/common/files';
import { LogLevel } from 'vs/platform/log/common/log';
import { TaskExecutionDTO, TaskDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO } from 'vs/workbench/api/shared/tasks';
import { IMarkerData } from 'vs/platform/markers/common/markers';
import { IPickOptions, IQuickInputButton, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { IPatternInfo, IQueryOptions, IRawFileMatch2, IRawSearchQuery, ISearchCompleteStats } from 'vs/platform/search/common/search';
import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar';
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { EndOfLine, IFileOperationOptions, TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes';
import { EditorViewColumn } from 'vs/workbench/api/shared/editor';
import { TaskDTO, TaskExecutionDTO, TaskFilterDTO, TaskHandleDTO, TaskProcessEndedDTO, TaskProcessStartedDTO, TaskSystemInfoDTO } from 'vs/workbench/api/shared/tasks';
import { ITreeItem } from 'vs/workbench/common/views';
import { IAdapterExecutable, IConfig, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug';
import { TaskSet } from 'vs/workbench/parts/tasks/common/tasks';
import { ITerminalDimensions } from 'vs/workbench/parts/terminal/common/terminal';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol, ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import { IProgressOptions, IProgressStep } from 'vs/workbench/services/progress/common/progress';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import * as vscode from 'vscode';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
......@@ -471,7 +462,8 @@ export interface ExtHostUrlsShape {
}
export interface MainThreadWorkspaceShape extends IDisposable {
$startSearch(includePattern: string, includeFolder: string, excludePatternOrDisregardExcludes: string | false, maxResults: number, requestId: number): Thenable<UriComponents[]>;
$startFileSearch(includePattern: string, includeFolder: string, excludePatternOrDisregardExcludes: string | false, maxResults: number, requestId: number): Thenable<UriComponents[]>;
$startTextSearch(query: IPatternInfo, options: IQueryOptions, requestId: number): TPromise<void>;
$cancelSearch(requestId: number): Thenable<boolean>;
$saveAll(includeUntitled?: boolean): Thenable<boolean>;
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string }[]): Thenable<void>;
......@@ -671,6 +663,7 @@ export interface ExtHostTreeViewsShape {
export interface ExtHostWorkspaceShape {
$acceptWorkspaceData(workspace: IWorkspaceData): void;
$handleTextSearchResult(result: IRawFileMatch2, requestId: number): void;
}
export interface ExtHostFileSystemShape {
......
......@@ -4,22 +4,25 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { normalize } from 'vs/base/common/paths';
import { posix, relative } from 'path';
import { delta as arrayDelta } from 'vs/base/common/arrays';
import { relative, posix } from 'path';
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceData, ExtHostWorkspaceShape, MainContext, MainThreadWorkspaceShape, IMainContext, MainThreadMessageServiceShape } from './extHost.protocol';
import * as vscode from 'vscode';
import { compare } from 'vs/base/common/strings';
import { Emitter, Event } from 'vs/base/common/event';
import { TernarySearchTree } from 'vs/base/common/map';
import { basenameOrAuthority, isEqual } from 'vs/base/common/resources';
import { normalize } from 'vs/base/common/paths';
import { isLinux } from 'vs/base/common/platform';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { basenameOrAuthority, isEqual } from 'vs/base/common/resources';
import { compare } from 'vs/base/common/strings';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { localize } from 'vs/nls';
import { Severity } from 'vs/platform/notification/common/notification';
import { ILogService } from 'vs/platform/log/common/log';
import { Severity } from 'vs/platform/notification/common/notification';
import { IQueryOptions, IRawFileMatch2 } from 'vs/platform/search/common/search';
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { Range } from 'vs/workbench/api/node/extHostTypes';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import * as vscode from 'vscode';
import { ExtHostWorkspaceShape, IMainContext, IWorkspaceData, MainContext, MainThreadMessageServiceShape, MainThreadWorkspaceShape } from './extHost.protocol';
function isFolderEqual(folderA: URI, folderB: URI): boolean {
return isEqual(folderA, folderB, !isLinux);
......@@ -145,6 +148,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
readonly onDidChangeWorkspace: Event<vscode.WorkspaceFoldersChangeEvent> = this._onDidChangeWorkspace.event;
private readonly _activeSearchCallbacks = [];
constructor(
mainContext: IMainContext,
data: IWorkspaceData,
......@@ -358,13 +363,62 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
}
}
const result = this._proxy.$startSearch(includePattern, includeFolder, excludePatternOrDisregardExcludes, maxResults, requestId);
const result = this._proxy.$startFileSearch(includePattern, includeFolder, excludePatternOrDisregardExcludes, maxResults, requestId);
if (token) {
token.onCancellationRequested(() => this._proxy.$cancelSearch(requestId));
}
return result.then(data => Array.isArray(data) ? data.map(URI.revive) : []);
}
findTextInFiles(query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, extensionId: string, token?: vscode.CancellationToken) {
this._logService.trace(`extHostWorkspace#findTextInFiles: textSearch, extension: ${extensionId}, entryPoint: findTextInFiles`);
const requestId = ExtHostWorkspace._requestIdPool++;
const queryOptions: IQueryOptions = {
ignoreSymlinks: typeof options.followSymlinks === 'boolean' ? !options.followSymlinks : undefined,
disregardIgnoreFiles: typeof options.useIgnoreFiles === 'boolean' ? !options.useIgnoreFiles : undefined,
fileEncoding: options.encoding,
maxResults: options.maxResults,
// TODO
// includePattern: options.includes
// excludePattern: options.excludes
};
this._activeSearchCallbacks[requestId] = p => {
p.lineMatches.forEach(lineMatch => {
lineMatch.offsetAndLengths.forEach(offsetAndLength => {
const range = new Range(lineMatch.lineNumber, offsetAndLength[0], lineMatch.lineNumber, offsetAndLength[0] + offsetAndLength[1]);
callback({
path: URI.revive(p.resource).fsPath,
preview: { text: lineMatch.preview, match: range },
range
});
});
});
};
if (token) {
token.onCancellationRequested(() => this._proxy.$cancelSearch(requestId));
}
return this._proxy.$startTextSearch(query, queryOptions, requestId).then(
() => {
delete this._activeSearchCallbacks[requestId];
},
err => {
delete this._activeSearchCallbacks[requestId];
return TPromise.wrapError(err);
});
}
$handleTextSearchResult(result: IRawFileMatch2, requestId: number): void {
if (this._activeSearchCallbacks[requestId]) {
this._activeSearchCallbacks[requestId](result);
}
}
saveAll(includeUntitled?: boolean): Thenable<boolean> {
return this._proxy.$saveAll(includeUntitled);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册