openFileHandler.ts 8.1 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
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';

29 30
export class FileQuickOpenModel extends QuickOpenModel {

31
	constructor(entries: QuickOpenEntry[], public stats?: ISearchStats) {
32 33 34 35
		super(entries);
	}
}

E
Erich Gamma 已提交
36 37 38
export class FileEntry extends EditorQuickOpenEntry {
	private range: IRange;

39
	constructor(
40 41 42 43
		private resource: URI,
		private name: string,
		private description: string,
		private icon: string,
E
Erich Gamma 已提交
44 45
		@IWorkbenchEditorService editorService: IWorkbenchEditorService,
		@IInstantiationService private instantiationService: IInstantiationService,
46
		@IConfigurationService private configurationService: IConfigurationService,
E
Erich Gamma 已提交
47 48 49 50 51 52
		@IWorkspaceContextService contextService: IWorkspaceContextService
	) {
		super(editorService);

		this.resource = resource;
		this.name = name;
53
		this.description = description;
E
Erich Gamma 已提交
54 55 56 57 58 59
	}

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

60 61 62 63
	public getAriaLabel(): string {
		return nls.localize('entryAriaLabel', "{0}, file picker", this.getLabel());
	}

E
Erich Gamma 已提交
64 65 66 67 68
	public getDescription(): string {
		return this.description;
	}

	public getIcon(): string {
69
		return this.icon;
E
Erich Gamma 已提交
70 71 72 73 74 75 76 77 78 79
	}

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

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

80 81 82 83
	public isFile(): boolean {
		return true; // TODO@Ben debt with editor history merging
	}

84
	public getInput(): IResourceInput | EditorInput {
85
		const input: IResourceInput = {
E
Erich Gamma 已提交
86
			resource: this.resource,
87
			options: {
88
				pinned: !this.configurationService.getConfiguration<IWorkbenchEditorConfiguration>().workbench.editor.enablePreviewFromQuickOpen
89
			}
E
Erich Gamma 已提交
90 91 92
		};

		if (this.range) {
93
			input.options.selection = this.range;
E
Erich Gamma 已提交
94 95 96 97 98 99
		}

		return input;
	}
}

100 101 102
export interface IOpenFileOptions {
	useIcons: boolean;
}
103

104 105
export class OpenFileHandler extends QuickOpenHandler {
	private options: IOpenFileOptions;
E
Erich Gamma 已提交
106
	private queryBuilder: QueryBuilder;
107
	private cacheState: CacheState;
E
Erich Gamma 已提交
108 109

	constructor(
110
		@IEditorGroupService private editorGroupService: IEditorGroupService,
E
Erich Gamma 已提交
111 112
		@IInstantiationService private instantiationService: IInstantiationService,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
B
Benjamin Pasero 已提交
113
		@ISearchService private searchService: ISearchService
E
Erich Gamma 已提交
114 115 116 117 118 119
	) {
		super();

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

120 121 122 123
	public setOptions(options: IOpenFileOptions) {
		this.options = options;
	}

124
	public getResults(searchValue: string, maxSortedResults?: number): TPromise<FileQuickOpenModel> {
E
Erich Gamma 已提交
125 126 127 128
		searchValue = searchValue.trim();

		// Respond directly to empty search
		if (!searchValue) {
129
			return TPromise.as(new FileQuickOpenModel([]));
E
Erich Gamma 已提交
130 131
		}

132 133
		// Do find results
		return this.doFindResults(searchValue, this.cacheState.cacheKey, maxSortedResults);
E
Erich Gamma 已提交
134 135
	}

136
	private doFindResults(searchValue: string, cacheKey?: string, maxSortedResults?: number): TPromise<FileQuickOpenModel> {
137
		const query: IQueryOptions = {
138
			folderResources: this.contextService.getWorkspace() ? [this.contextService.getWorkspace().resource] : [],
139
			extraFileResources: getOutOfWorkspaceEditorResources(this.editorGroupService, this.contextService),
140 141
			filePattern: searchValue,
			cacheKey: cacheKey
142
		};
143

144
		if (typeof maxSortedResults === 'number') {
145 146 147
			query.maxResults = maxSortedResults;
			query.sortByScore = true;
		}
E
Erich Gamma 已提交
148

149
		return this.searchService.search(this.queryBuilder.file(query)).then((complete) => {
150
			const results: QuickOpenEntry[] = [];
151
			for (let i = 0; i < complete.results.length; i++) {
152
				const fileMatch = complete.results[i];
153

154 155
				const label = paths.basename(fileMatch.resource.fsPath);
				const description = labels.getPathLabel(paths.dirname(fileMatch.resource.fsPath), this.contextService);
156

157
				results.push(this.instantiationService.createInstance(FileEntry, fileMatch.resource, label, description, (this.options && this.options.useIcons) ? 'file' : null));
158 159
			}

160
			return new FileQuickOpenModel(results, complete.stats);
E
Erich Gamma 已提交
161 162 163
		});
	}

164 165 166 167
	public hasShortResponseTime(): boolean {
		return this.isCacheLoaded;
	}

168
	public onOpen(): void {
169 170
		this.cacheState = new CacheState(cacheKey => this.cacheQuery(cacheKey), query => this.searchService.search(query), cacheKey => this.searchService.clearCache(cacheKey), this.cacheState);
		this.cacheState.load();
171 172
	}

173 174 175
	private cacheQuery(cacheKey: string): ISearchQuery {
		const options: IQueryOptions = {
			folderResources: this.contextService.getWorkspace() ? [this.contextService.getWorkspace().resource] : [],
B
Benjamin Pasero 已提交
176
			extraFileResources: getOutOfWorkspaceEditorResources(this.editorGroupService, this.contextService),
177 178 179 180 181
			filePattern: '',
			cacheKey: cacheKey,
			maxResults: 0,
			sortByScore: true
		};
182

183 184
		const query = this.queryBuilder.file(options);
		this.searchService.extendQuery(query);
185

186 187 188 189 190
		return query;
	}

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

E
Erich Gamma 已提交
193 194 195 196 197 198 199 200 201
	public getGroupLabel(): string {
		return nls.localize('searchResults', "search results");
	}

	public getAutoFocus(searchValue: string): IAutoFocus {
		return {
			autoFocusFirstEntry: true
		};
	}
202 203 204 205 206 207 208 209 210 211 212
}

class CacheState {

	public query: ISearchQuery;

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

	private promise: TPromise<void>;

B
Benjamin Pasero 已提交
213
	constructor(private cacheQuery: (cacheKey: string) => ISearchQuery, private doLoad: (query: ISearchQuery) => TPromise<any>, private doDispose: (cacheKey: string) => TPromise<void>, private previous: CacheState) {
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
		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 => {
242
				errors.onUnexpectedError(err);
243 244 245 246
			});
	}

	public dispose(): void {
B
Benjamin Pasero 已提交
247
		this.promise.then(null, () => { })
248 249 250 251
			.then(() => {
				this._isLoaded = false;
				return this.doDispose(this._cacheKey);
			}).then(null, err => {
252
				errors.onUnexpectedError(err);
253 254 255 256 257 258
			});
		if (this.previous) {
			this.previous.dispose();
			this.previous = null;
		}
	}
E
Erich Gamma 已提交
259
}