openFileHandler.ts 7.9 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

B
Benjamin Pasero 已提交
7
import {TPromise} from 'vs/base/common/winjs.base';
8
import errors = require('vs/base/common/errors');
E
Erich Gamma 已提交
9 10 11
import nls = require('vs/nls');
import paths = require('vs/base/common/paths');
import labels = require('vs/base/common/labels');
12
import * as objects from 'vs/base/common/objects';
13
import uuid = require('vs/base/common/uuid');
E
Erich Gamma 已提交
14 15
import URI from 'vs/base/common/uri';
import {IRange} from 'vs/editor/common/editorCommon';
16
import {IAutoFocus} from 'vs/base/parts/quickopen/common/quickOpen';
B
Benjamin Pasero 已提交
17
import {QuickOpenEntry, QuickOpenModel} from 'vs/base/parts/quickopen/browser/quickOpenModel';
E
Erich Gamma 已提交
18 19
import {QuickOpenHandler, EditorQuickOpenEntry} from 'vs/workbench/browser/quickopen';
import {QueryBuilder} from 'vs/workbench/parts/search/common/searchQuery';
20
import {EditorInput, getOutOfWorkspaceEditorResources, IWorkbenchEditorConfiguration} from 'vs/workbench/common/editor';
21
import {IEditorGroupService} from 'vs/workbench/services/group/common/groupService';
22 23
import {IResourceInput} from 'vs/platform/editor/common/editor';
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
E
Erich Gamma 已提交
24 25
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
26
import {IQueryOptions, ISearchService, ISearchStats, ISearchQuery} from 'vs/platform/search/common/search';
E
Erich Gamma 已提交
27 28 29 30 31 32 33 34
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';

export class FileEntry extends EditorQuickOpenEntry {
	private name: string;
	private description: string;
	private resource: URI;
	private range: IRange;

35 36 37 38
	constructor(
		name: string,
		description: string,
		resource: URI,
E
Erich Gamma 已提交
39 40
		@IWorkbenchEditorService editorService: IWorkbenchEditorService,
		@IInstantiationService private instantiationService: IInstantiationService,
41
		@IConfigurationService private configurationService: IConfigurationService,
E
Erich Gamma 已提交
42 43 44 45 46 47
		@IWorkspaceContextService contextService: IWorkspaceContextService
	) {
		super(editorService);

		this.resource = resource;
		this.name = name;
48
		this.description = description;
E
Erich Gamma 已提交
49 50 51 52 53 54
	}

	public getLabel(): string {
		return this.name;
	}

55 56 57 58
	public getAriaLabel(): string {
		return nls.localize('entryAriaLabel', "{0}, file picker", this.getLabel());
	}

E
Erich Gamma 已提交
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
	public getDescription(): string {
		return this.description;
	}

	public getIcon(): string {
		return 'file';
	}

	public getResource(): URI {
		return this.resource;
	}

	public setRange(range: IRange): void {
		this.range = range;
	}

75 76
	public getInput(): IResourceInput | EditorInput {
		let input: IResourceInput = {
E
Erich Gamma 已提交
77
			resource: this.resource,
78
			options: {
79
				pinned: !this.configurationService.getConfiguration<IWorkbenchEditorConfiguration>().workbench.editor.enablePreviewFromQuickOpen
80
			}
E
Erich Gamma 已提交
81 82 83
		};

		if (this.range) {
84
			input.options.selection = this.range;
E
Erich Gamma 已提交
85 86 87 88 89 90 91
		}

		return input;
	}
}

export class OpenFileHandler extends QuickOpenHandler {
92

E
Erich Gamma 已提交
93
	private queryBuilder: QueryBuilder;
94
	private cacheState: CacheState;
E
Erich Gamma 已提交
95 96

	constructor(
97
		@IEditorGroupService private editorGroupService: IEditorGroupService,
E
Erich Gamma 已提交
98 99
		@IInstantiationService private instantiationService: IInstantiationService,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
B
Benjamin Pasero 已提交
100
		@ISearchService private searchService: ISearchService
E
Erich Gamma 已提交
101 102 103 104 105 106 107
	) {
		super();

		this.queryBuilder = this.instantiationService.createInstance(QueryBuilder);
	}

	public getResults(searchValue: string): TPromise<QuickOpenModel> {
C
chrmarti 已提交
108 109 110 111
		return this.getResultsWithStats(searchValue)
			.then(result => result[0]);
	}

112
	public getResultsWithStats(searchValue: string, maxSortedResults?: number): TPromise<[QuickOpenModel, ISearchStats]> {
E
Erich Gamma 已提交
113
		searchValue = searchValue.trim();
C
chrmarti 已提交
114
		let promise: TPromise<[QuickOpenEntry[], ISearchStats]>;
E
Erich Gamma 已提交
115 116 117

		// Respond directly to empty search
		if (!searchValue) {
C
chrmarti 已提交
118
			promise = TPromise.as(<[QuickOpenEntry[], ISearchStats]>[[], undefined]);
E
Erich Gamma 已提交
119
		} else {
120
			promise = this.doFindResults(searchValue, this.cacheState.cacheKey, maxSortedResults);
E
Erich Gamma 已提交
121 122
		}

C
chrmarti 已提交
123
		return promise.then(result => [new QuickOpenModel(result[0]), result[1]]);
E
Erich Gamma 已提交
124 125
	}

126
	private doFindResults(searchValue: string, cacheKey?: string, maxSortedResults?: number): TPromise<[QuickOpenEntry[], ISearchStats]> {
127
		const query: IQueryOptions = {
128
			folderResources: this.contextService.getWorkspace() ? [this.contextService.getWorkspace().resource] : [],
129
			extraFileResources: getOutOfWorkspaceEditorResources(this.editorGroupService, this.contextService),
130 131
			filePattern: searchValue,
			cacheKey: cacheKey
132
		};
133
		if (typeof maxSortedResults === 'number') {
134 135 136
			query.maxResults = maxSortedResults;
			query.sortByScore = true;
		}
E
Erich Gamma 已提交
137

138
		return this.searchService.search(this.queryBuilder.file(query)).then((complete) => {
139
			let results: QuickOpenEntry[] = [];
140 141 142 143 144
			for (let i = 0; i < complete.results.length; i++) {
				let fileMatch = complete.results[i];

				let label = paths.basename(fileMatch.resource.fsPath);
				let description = labels.getPathLabel(paths.dirname(fileMatch.resource.fsPath), this.contextService);
145

146
				results.push(this.instantiationService.createInstance(FileEntry, label, description, fileMatch.resource));
147 148
			}

C
chrmarti 已提交
149
			return [results, complete.stats];
E
Erich Gamma 已提交
150 151 152
		});
	}

153
	public onOpen(): void {
154 155
		this.cacheState = new CacheState(cacheKey => this.cacheQuery(cacheKey), query => this.searchService.search(query), cacheKey => this.searchService.clearCache(cacheKey), this.cacheState);
		this.cacheState.load();
156 157
	}

158 159 160
	private cacheQuery(cacheKey: string): ISearchQuery {
		const options: IQueryOptions = {
			folderResources: this.contextService.getWorkspace() ? [this.contextService.getWorkspace().resource] : [],
B
Benjamin Pasero 已提交
161
			extraFileResources: getOutOfWorkspaceEditorResources(this.editorGroupService, this.contextService),
162 163 164 165 166 167 168 169 170 171 172 173
			filePattern: '',
			cacheKey: cacheKey,
			maxResults: 0,
			sortByScore: true
		};
		const query = this.queryBuilder.file(options);
		this.searchService.extendQuery(query);
		return query;
	}

	public get isCacheLoaded(): boolean {
		return this.cacheState && this.cacheState.isLoaded;
174 175
	}

E
Erich Gamma 已提交
176 177 178 179 180 181 182 183 184
	public getGroupLabel(): string {
		return nls.localize('searchResults', "search results");
	}

	public getAutoFocus(searchValue: string): IAutoFocus {
		return {
			autoFocusFirstEntry: true
		};
	}
185 186 187 188 189 190 191 192 193 194 195
}

class CacheState {

	public query: ISearchQuery;

	private _cacheKey = uuid.generateUuid();
	private _isLoaded = false;

	private promise: TPromise<void>;

B
Benjamin Pasero 已提交
196
	constructor(private cacheQuery: (cacheKey: string) => ISearchQuery, private doLoad: (query: ISearchQuery) => TPromise<any>, private doDispose: (cacheKey: string) => TPromise<void>, private previous: CacheState) {
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
		this.query = cacheQuery(this._cacheKey);
		if (this.previous) {
			const current = objects.assign({}, this.query, { cacheKey: null });
			const previous = objects.assign({}, this.previous.query, { cacheKey: null });
			if (!objects.equals(current, previous)) {
				this.previous.dispose();
				this.previous = null;
			}
		}
	}

	public get cacheKey(): string {
		return this._isLoaded || !this.previous ? this._cacheKey : this.previous.cacheKey;
	}

	public get isLoaded(): boolean {
		return this._isLoaded || !this.previous ? this._isLoaded : this.previous.isLoaded;
	}

	public load(): void {
		this.promise = this.doLoad(this.query)
			.then(() => {
				this._isLoaded = true;
				if (this.previous) {
					this.previous.dispose();
					this.previous = null;
				}
			}, err => {
				console.error(errors.toErrorMessage(err));
			});
	}

	public dispose(): void {
B
Benjamin Pasero 已提交
230
		this.promise.then(null, () => { })
231 232 233 234 235 236 237 238 239 240 241
			.then(() => {
				this._isLoaded = false;
				return this.doDispose(this._cacheKey);
			}).then(null, err => {
				console.error(errors.toErrorMessage(err));
			});
		if (this.previous) {
			this.previous.dispose();
			this.previous = null;
		}
	}
E
Erich Gamma 已提交
242
}