Skip to content

Commit

Permalink
wire in CancellationToken in quick open (for #56137)
Browse files Browse the repository at this point in the history
  • Loading branch information
bpasero committed Sep 3, 2018
1 parent 83a7946 commit 2c894f1
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 19 deletions.
20 changes: 16 additions & 4 deletions src/vs/workbench/parts/search/browser/openAnythingHandler.ts
Expand Up @@ -20,10 +20,11 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IWorkbenchSearchConfiguration } from 'vs/workbench/parts/search/common/search';
import { IRange } from 'vs/editor/common/core/range';
import { compareItemsByScore, scoreItem, ScorerCache, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer';

export import OpenSymbolHandler = openSymbolHandler.OpenSymbolHandler; // OpenSymbolHandler is used from an extension and must be in the main bundle file so it can load
import { INotificationService } from 'vs/platform/notification/common/notification';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { CancellationTokenSource } from 'vs/base/common/cancellation';

export import OpenSymbolHandler = openSymbolHandler.OpenSymbolHandler; // OpenSymbolHandler is used from an extension and must be in the main bundle file so it can load

interface ISearchWithRange {
search: string;
Expand All @@ -45,6 +46,7 @@ export class OpenAnythingHandler extends QuickOpenHandler {
private openFileHandler: OpenFileHandler;
private searchDelayer: ThrottledDelayer<QuickOpenModel>;
private pendingSearch: TPromise<QuickOpenModel>;
private pendingSearchTokenSource: CancellationTokenSource;
private isClosed: boolean;
private scorerCache: ScorerCache;
private includeSymbols: boolean;
Expand Down Expand Up @@ -105,20 +107,23 @@ export class OpenAnythingHandler extends QuickOpenHandler {

// The throttler needs a factory for its promises
const promiseFactory = () => {
this.pendingSearchTokenSource = new CancellationTokenSource();

const resultPromises: TPromise<QuickOpenModel | FileQuickOpenModel>[] = [];

// File Results
const filePromise = this.openFileHandler.getResults(query.original, OpenAnythingHandler.MAX_DISPLAYED_RESULTS);
const filePromise = this.openFileHandler.getResults(query.original, OpenAnythingHandler.MAX_DISPLAYED_RESULTS, this.pendingSearchTokenSource.token);
resultPromises.push(filePromise);

// Symbol Results (unless disabled or a range or absolute path is specified)
if (this.includeSymbols && !searchWithRange) {
resultPromises.push(this.openSymbolHandler.getResults(query.original));
resultPromises.push(this.openSymbolHandler.getResults(query.original, this.pendingSearchTokenSource.token));
}

// Join and sort unified
this.pendingSearch = TPromise.join(resultPromises).then(results => {
this.pendingSearch = null;
this.pendingSearchTokenSource.dispose();

// If the quick open widget has been closed meanwhile, ignore the result
if (this.isClosed) {
Expand All @@ -145,6 +150,7 @@ export class OpenAnythingHandler extends QuickOpenHandler {
return TPromise.as<QuickOpenModel>(new QuickOpenModel(viewResults));
}, error => {
this.pendingSearch = null;
this.pendingSearchTokenSource.dispose();

if (!isPromiseCanceledError(error)) {
if (error && error[0] && error[0].message) {
Expand Down Expand Up @@ -262,5 +268,11 @@ export class OpenAnythingHandler extends QuickOpenHandler {
this.pendingSearch.cancel();
this.pendingSearch = null;
}

if (this.pendingSearchTokenSource) {
this.pendingSearchTokenSource.cancel();
this.pendingSearchTokenSource.dispose();
this.pendingSearchTokenSource = null;
}
}
}
22 changes: 15 additions & 7 deletions src/vs/workbench/parts/search/browser/openFileHandler.ts
Expand Up @@ -35,6 +35,7 @@ import { prepareQuery, IPreparedQuery } from 'vs/base/parts/quickopen/common/qui
import { IFileService } from 'vs/platform/files/common/files';
import { ILabelService } from 'vs/platform/label/common/label';
import { untildify } from 'vs/base/common/labels';
import { CancellationToken } from 'vs/base/common/cancellation';

export class FileQuickOpenModel extends QuickOpenModel {

Expand Down Expand Up @@ -138,7 +139,7 @@ export class OpenFileHandler extends QuickOpenHandler {
this.options = options;
}

getResults(searchValue: string, maxSortedResults?: number): TPromise<FileQuickOpenModel> {
getResults(searchValue: string, maxSortedResults?: number, token?: CancellationToken): TPromise<FileQuickOpenModel> {
const query = prepareQuery(searchValue);

// Respond directly to empty search
Expand All @@ -150,23 +151,29 @@ export class OpenFileHandler extends QuickOpenHandler {
query.value = untildify(query.value, this.environmentService.userHome);

// Do find results
return this.doFindResults(query, this.cacheState.cacheKey, maxSortedResults);
return this.doFindResults(query, this.cacheState.cacheKey, maxSortedResults, token);
}

private doFindResults(query: IPreparedQuery, cacheKey?: string, maxSortedResults?: number): TPromise<FileQuickOpenModel> {
private doFindResults(query: IPreparedQuery, cacheKey?: string, maxSortedResults?: number, token?: CancellationToken): TPromise<FileQuickOpenModel> {
const queryOptions = this.doResolveQueryOptions(query, cacheKey, maxSortedResults);

let iconClass: string;
if (this.options && this.options.forceUseIcons && !this.themeService.getFileIconTheme()) {
iconClass = 'file'; // only use a generic file icon if we are forced to use an icon and have no icon theme set otherwise
}

return this.getAbsolutePathResult(query).then(result => {
if (token.isCancellationRequested) {
return TPromise.wrap(<ISearchComplete>{ results: [] });
}

// If the original search value is an existing file on disk, return it immediately and bypass the search service
if (result) {
return TPromise.wrap(<ISearchComplete>{ results: [{ resource: result }] });
} else {
return this.searchService.search(this.queryBuilder.file(this.contextService.getWorkspace().folders.map(folder => folder.uri), queryOptions));
}

// TODO@Rob support cancellation
return this.searchService.search(this.queryBuilder.file(this.contextService.getWorkspace().folders.map(folder => folder.uri), queryOptions));
}).then(complete => {
const results: QuickOpenEntry[] = [];
for (let i = 0; i < complete.results.length; i++) {
Expand All @@ -185,10 +192,11 @@ export class OpenFileHandler extends QuickOpenHandler {
private getAbsolutePathResult(query: IPreparedQuery): TPromise<URI> {
if (paths.isAbsolute(query.original)) {
const resource = URI.file(query.original);

return this.fileService.resolveFile(resource).then(stat => stat.isDirectory ? void 0 : resource, error => void 0);
} else {
return TPromise.as(null);
}

return TPromise.as(null);
}

private doResolveQueryOptions(query: IPreparedQuery, cacheKey?: string, maxSortedResults?: number): IQueryOptions {
Expand Down
15 changes: 8 additions & 7 deletions src/vs/workbench/parts/search/browser/openSymbolHandler.ts
Expand Up @@ -24,6 +24,7 @@ import { IWorkspaceSymbolProvider, getWorkspaceSymbols, IWorkspaceSymbol } from
import { basename } from 'vs/base/common/paths';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ILabelService } from 'vs/platform/label/common/label';
import { CancellationToken } from 'vs/base/common/cancellation';

class SymbolEntry extends EditorQuickOpenEntry {

Expand Down Expand Up @@ -149,21 +150,21 @@ export class OpenSymbolHandler extends QuickOpenHandler {
return true;
}

public getResults(searchValue: string): TPromise<QuickOpenModel> {
public getResults(searchValue: string, token?: CancellationToken): TPromise<QuickOpenModel> {
searchValue = searchValue.trim();

let promise: TPromise<QuickOpenEntry[]>;
if (!this.options.skipDelay) {
promise = this.delayer.trigger(() => this.doGetResults(searchValue)); // Run search with delay as needed
promise = this.delayer.trigger(() => this.doGetResults(searchValue, token)); // Run search with delay as needed
} else {
promise = this.doGetResults(searchValue);
promise = this.doGetResults(searchValue, token);
}

return promise.then(e => new QuickOpenModel(e));
}

private doGetResults(searchValue: string): TPromise<SymbolEntry[]> {
return getWorkspaceSymbols(searchValue).then(tuples => {
private doGetResults(searchValue: string, token?: CancellationToken): TPromise<SymbolEntry[]> {
return getWorkspaceSymbols(searchValue, token).then(tuples => {
const result: SymbolEntry[] = [];
for (let tuple of tuples) {
const [provider, bearings] = tuple;
Expand All @@ -174,9 +175,9 @@ export class OpenSymbolHandler extends QuickOpenHandler {
if (!this.options.skipSorting) {
searchValue = searchValue ? strings.stripWildcards(searchValue.toLowerCase()) : searchValue;
return result.sort((a, b) => SymbolEntry.compare(a, b, searchValue));
} else {
return result;
}

return result;
});
}

Expand Down
4 changes: 3 additions & 1 deletion src/vs/workbench/parts/search/common/search.ts
Expand Up @@ -14,6 +14,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
import { URI } from 'vs/base/common/uri';
import { toResource } from 'vs/workbench/common/editor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { CancellationToken } from 'vs/base/common/cancellation';

export interface IWorkspaceSymbol {
name: string;
Expand Down Expand Up @@ -55,11 +56,12 @@ export namespace WorkspaceSymbolProviderRegistry {
}
}

export function getWorkspaceSymbols(query: string): TPromise<[IWorkspaceSymbolProvider, IWorkspaceSymbol[]][]> {
export function getWorkspaceSymbols(query: string, token?: CancellationToken): TPromise<[IWorkspaceSymbolProvider, IWorkspaceSymbol[]][]> {

const result: [IWorkspaceSymbolProvider, IWorkspaceSymbol[]][] = [];

const promises = WorkspaceSymbolProviderRegistry.all().map(support => {
// TODO@Joh support cancellation
return support.provideWorkspaceSymbols(query).then(value => {
if (Array.isArray(value)) {
result.push([support, value]);
Expand Down

0 comments on commit 2c894f1

Please sign in to comment.