<commit_before>/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

import { TPromise } from 'vs/base/common/winjs.base';
import * as errors from 'vs/base/common/errors';
import * as nls from 'vs/nls';
import * as paths from 'vs/base/common/paths';
import * as labels from 'vs/base/common/labels';
import * as objects from 'vs/base/common/objects';
import { defaultGenerator } from 'vs/base/common/idGenerator';
import URI from 'vs/base/common/uri';
import * as resources from 'vs/base/common/resources';
import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IModeService } from 'vs/editor/common/services/modeService';
import { getIconClasses } from 'vs/workbench/browser/labels';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen';
import { QuickOpenEntry, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel';
import { QuickOpenHandler, EditorQuickOpenEntry } from 'vs/workbench/browser/quickopen';
import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder';
import { EditorInput, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor';
import { IResourceInput } from 'vs/platform/editor/common/editor';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IQueryOptions, ISearchService, ISearchStats, ISearchQuery, ISearchComplete } from 'vs/platform/search/common/search';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IRange } from 'vs/editor/common/core/range';
import { getOutOfWorkspaceEditorResources } from 'vs/workbench/parts/search/common/search';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { prepareQuery, IPreparedQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer';
import { IFileService } from 'vs/platform/files/common/files';

export class FileQuickOpenModel extends QuickOpenModel {

	constructor(entries: QuickOpenEntry[], public stats?: ISearchStats) {
		super(entries);
	}
}

export class FileEntry extends EditorQuickOpenEntry {
	private range: IRange;

	constructor(
		private resource: URI,
		private name: string,
		private description: string,
		private icon: string,
		@IEditorService editorService: IEditorService,
		@IModeService private modeService: IModeService,
		@IModelService private modelService: IModelService,
		@IConfigurationService private configurationService: IConfigurationService,
		@IWorkspaceContextService contextService: IWorkspaceContextService
	) {
		super(editorService);
	}

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

	public getLabelOptions(): IIconLabelValueOptions {
		return {
			extraClasses: getIconClasses(this.modelService, this.modeService, this.resource)
		};
	}

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

	public getDescription(): string {
		return this.description;
	}

	public getIcon(): string {
		return this.icon;
	}

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

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

	public mergeWithEditorHistory(): boolean {
		return true;
	}

	public getInput(): IResourceInput | EditorInput {
		const input: IResourceInput = {
			resource: this.resource,
			options: {
				pinned: !this.configurationService.getValue<IWorkbenchEditorConfiguration>().workbench.editor.enablePreviewFromQuickOpen
			}
		};

		if (this.range) {
			input.options.selection = this.range;
		}

		return input;
	}
}

export interface IOpenFileOptions {
	forceUseIcons: boolean;
}

export class OpenFileHandler extends QuickOpenHandler {
	private options: IOpenFileOptions;
	private queryBuilder: QueryBuilder;
	private cacheState: CacheState;

	constructor(
		@IEditorService private editorService: IEditorService,
		@IInstantiationService private instantiationService: IInstantiationService,
		@IWorkbenchThemeService private themeService: IWorkbenchThemeService,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
		@ISearchService private searchService: ISearchService,
		@IEnvironmentService private environmentService: IEnvironmentService,
		@IFileService private fileService: IFileService
	) {
		super();

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

	public setOptions(options: IOpenFileOptions) {
		this.options = options;
	}

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

		// Respond directly to empty search
		if (!query.value) {
			return TPromise.as(new FileQuickOpenModel([]));
		}

		// Untildify file pattern
		query.value = labels.untildify(query.value, this.environmentService.userHome);

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

	private doFindResults(query: IPreparedQuery, cacheKey?: string, maxSortedResults?: number): TPromise<FileQuickOpenModel> {
		let iconClass: string;
		const queryOptions = this.doResolveQueryOptions(query, cacheKey, maxSortedResults);
		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 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));
			}
		}).then(complete => {
			const results: QuickOpenEntry[] = [];
			for (let i = 0; i < complete.results.length; i++) {
				const fileMatch = complete.results[i];

				const label = paths.basename(fileMatch.resource.fsPath);
				const description = labels.getPathLabel(resources.dirname(fileMatch.resource), this.environmentService, this.contextService);

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

			return new FileQuickOpenModel(results, complete.stats);
		});
	}

	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);
		}
	}

	private doResolveQueryOptions(query: IPreparedQuery, cacheKey?: string, maxSortedResults?: number): IQueryOptions {
		const queryOptions: IQueryOptions = {
			extraFileResources: getOutOfWorkspaceEditorResources(this.editorService, this.contextService),
			filePattern: query.value,
			cacheKey
		};

		if (typeof maxSortedResults === 'number') {
			queryOptions.maxResults = maxSortedResults;
			queryOptions.sortByScore = true;
		}

		return queryOptions;
	}

	public hasShortResponseTime(): boolean {
		return this.isCacheLoaded;
	}

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

	private cacheQuery(cacheKey: string): ISearchQuery {
		const options: IQueryOptions = {
			extraFileResources: getOutOfWorkspaceEditorResources(this.editorService, this.contextService),
			filePattern: '',
			cacheKey: cacheKey,
			maxResults: 0,
			sortByScore: true,
		};

		const folderResources = this.contextService.getWorkspace().folders.map(folder => folder.uri);
		const query = this.queryBuilder.file(folderResources, options);

		return query;
	}

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

	public getGroupLabel(): string {
		return nls.localize('searchResults', "search results");
	}

	public getAutoFocus(searchValue: string): IAutoFocus {
		return {
			autoFocusFirstEntry: true
		};
	}
}

enum LoadingPhase {
	Created = 1,
	Loading,
	Loaded,
	Errored,
	Disposed
}

/**
 * Exported for testing.
 */
export class CacheState {

	private _cacheKey = defaultGenerator.nextId();
	private query: ISearchQuery;

	private loadingPhase = LoadingPhase.Created;
	private promise: TPromise<void>;

	constructor(cacheQuery: (cacheKey: string) => ISearchQuery, private doLoad: (query: ISearchQuery) => TPromise<any>, private doDispose: (cacheKey: string) => TPromise<void>, private previous: CacheState) {
		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.loadingPhase === LoadingPhase.Loaded || !this.previous ? this._cacheKey : this.previous.cacheKey;
	}

	public get isLoaded(): boolean {
		const isLoaded = this.loadingPhase === LoadingPhase.Loaded;
		return isLoaded || !this.previous ? isLoaded : this.previous.isLoaded;
	}

	public get isUpdating(): boolean {
		const isUpdating = this.loadingPhase === LoadingPhase.Loading;
		return isUpdating || !this.previous ? isUpdating : this.previous.isUpdating;
	}

	public load(): void {
		if (this.isUpdating) {
			return;
		}
		this.loadingPhase = LoadingPhase.Loading;
		this.promise = this.doLoad(this.query)
			.then(() => {
				this.loadingPhase = LoadingPhase.Loaded;
				if (this.previous) {
					this.previous.dispose();
					this.previous = null;
				}
			}, err => {
				this.loadingPhase = LoadingPhase.Errored;
				errors.onUnexpectedError(err);
			});
	}

	public dispose(): void {
		if (this.promise) {
			this.promise.then(null, () => { })
				.then(() => {
					this.loadingPhase = LoadingPhase.Disposed;
					return this.doDispose(this._cacheKey);
				}).then(null, err => {
					errors.onUnexpectedError(err);
				});
		} else {
			this.loadingPhase = LoadingPhase.Disposed;
		}
		if (this.previous) {
			this.previous.dispose();
			this.previous = null;
		}
	}
}
<commit_msg>#51597 - PR feedback<commit_after>/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

import { TPromise } from 'vs/base/common/winjs.base';
import * as errors from 'vs/base/common/errors';
import * as nls from 'vs/nls';
import * as paths from 'vs/base/common/paths';
import * as labels from 'vs/base/common/labels';
import * as objects from 'vs/base/common/objects';
import { defaultGenerator } from 'vs/base/common/idGenerator';
import URI from 'vs/base/common/uri';
import * as resources from 'vs/base/common/resources';
import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IModeService } from 'vs/editor/common/services/modeService';
import { getIconClasses } from 'vs/workbench/browser/labels';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen';
import { QuickOpenEntry, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel';
import { QuickOpenHandler, EditorQuickOpenEntry } from 'vs/workbench/browser/quickopen';
import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder';
import { EditorInput, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor';
import { IResourceInput } from 'vs/platform/editor/common/editor';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IQueryOptions, ISearchService, ISearchStats, ISearchQuery, ISearchComplete } from 'vs/platform/search/common/search';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IRange } from 'vs/editor/common/core/range';
import { getOutOfWorkspaceEditorResources } from 'vs/workbench/parts/search/common/search';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { prepareQuery, IPreparedQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer';
import { IFileService } from 'vs/platform/files/common/files';

export class FileQuickOpenModel extends QuickOpenModel {

	constructor(entries: QuickOpenEntry[], public stats?: ISearchStats) {
		super(entries);
	}
}

export class FileEntry extends EditorQuickOpenEntry {
	private range: IRange;

	constructor(
		private resource: URI,
		private name: string,
		private description: string,
		private icon: string,
		@IEditorService editorService: IEditorService,
		@IModeService private modeService: IModeService,
		@IModelService private modelService: IModelService,
		@IConfigurationService private configurationService: IConfigurationService,
		@IWorkspaceContextService contextService: IWorkspaceContextService
	) {
		super(editorService);
	}

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

	public getLabelOptions(): IIconLabelValueOptions {
		return {
			extraClasses: getIconClasses(this.modelService, this.modeService, this.resource)
		};
	}

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

	public getDescription(): string {
		return this.description;
	}

	public getIcon(): string {
		return this.icon;
	}

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

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

	public mergeWithEditorHistory(): boolean {
		return true;
	}

	public getInput(): IResourceInput | EditorInput {
		const input: IResourceInput = {
			resource: this.resource,
			options: {
				pinned: !this.configurationService.getValue<IWorkbenchEditorConfiguration>().workbench.editor.enablePreviewFromQuickOpen
			}
		};

		if (this.range) {
			input.options.selection = this.range;
		}

		return input;
	}
}

export interface IOpenFileOptions {
	forceUseIcons: boolean;
}

export class OpenFileHandler extends QuickOpenHandler {
	private options: IOpenFileOptions;
	private queryBuilder: QueryBuilder;
	private cacheState: CacheState;

	constructor(
		@IEditorService private editorService: IEditorService,
		@IInstantiationService private instantiationService: IInstantiationService,
		@IWorkbenchThemeService private themeService: IWorkbenchThemeService,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
		@ISearchService private searchService: ISearchService,
		@IEnvironmentService private environmentService: IEnvironmentService,
		@IFileService private fileService: IFileService
	) {
		super();

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

	public setOptions(options: IOpenFileOptions) {
		this.options = options;
	}

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

		// Respond directly to empty search
		if (!query.value) {
			return TPromise.as(new FileQuickOpenModel([]));
		}

		// Untildify file pattern
		query.value = labels.untildify(query.value, this.environmentService.userHome);

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

	private doFindResults(query: IPreparedQuery, cacheKey?: string, maxSortedResults?: number): 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 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));
			}
		}).then(complete => {
			const results: QuickOpenEntry[] = [];
			for (let i = 0; i < complete.results.length; i++) {
				const fileMatch = complete.results[i];

				const label = paths.basename(fileMatch.resource.fsPath);
				const description = labels.getPathLabel(resources.dirname(fileMatch.resource), this.environmentService, this.contextService);

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

			return new FileQuickOpenModel(results, complete.stats);
		});
	}

	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);
		}
	}

	private doResolveQueryOptions(query: IPreparedQuery, cacheKey?: string, maxSortedResults?: number): IQueryOptions {
		const queryOptions: IQueryOptions = {
			extraFileResources: getOutOfWorkspaceEditorResources(this.editorService, this.contextService),
			filePattern: query.value,
			cacheKey
		};

		if (typeof maxSortedResults === 'number') {
			queryOptions.maxResults = maxSortedResults;
			queryOptions.sortByScore = true;
		}

		return queryOptions;
	}

	public hasShortResponseTime(): boolean {
		return this.isCacheLoaded;
	}

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

	private cacheQuery(cacheKey: string): ISearchQuery {
		const options: IQueryOptions = {
			extraFileResources: getOutOfWorkspaceEditorResources(this.editorService, this.contextService),
			filePattern: '',
			cacheKey: cacheKey,
			maxResults: 0,
			sortByScore: true,
		};

		const folderResources = this.contextService.getWorkspace().folders.map(folder => folder.uri);
		const query = this.queryBuilder.file(folderResources, options);

		return query;
	}

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

	public getGroupLabel(): string {
		return nls.localize('searchResults', "search results");
	}

	public getAutoFocus(searchValue: string): IAutoFocus {
		return {
			autoFocusFirstEntry: true
		};
	}
}

enum LoadingPhase {
	Created = 1,
	Loading,
	Loaded,
	Errored,
	Disposed
}

/**
 * Exported for testing.
 */
export class CacheState {

	private _cacheKey = defaultGenerator.nextId();
	private query: ISearchQuery;

	private loadingPhase = LoadingPhase.Created;
	private promise: TPromise<void>;

	constructor(cacheQuery: (cacheKey: string) => ISearchQuery, private doLoad: (query: ISearchQuery) => TPromise<any>, private doDispose: (cacheKey: string) => TPromise<void>, private previous: CacheState) {
		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.loadingPhase === LoadingPhase.Loaded || !this.previous ? this._cacheKey : this.previous.cacheKey;
	}

	public get isLoaded(): boolean {
		const isLoaded = this.loadingPhase === LoadingPhase.Loaded;
		return isLoaded || !this.previous ? isLoaded : this.previous.isLoaded;
	}

	public get isUpdating(): boolean {
		const isUpdating = this.loadingPhase === LoadingPhase.Loading;
		return isUpdating || !this.previous ? isUpdating : this.previous.isUpdating;
	}

	public load(): void {
		if (this.isUpdating) {
			return;
		}
		this.loadingPhase = LoadingPhase.Loading;
		this.promise = this.doLoad(this.query)
			.then(() => {
				this.loadingPhase = LoadingPhase.Loaded;
				if (this.previous) {
					this.previous.dispose();
					this.previous = null;
				}
			}, err => {
				this.loadingPhase = LoadingPhase.Errored;
				errors.onUnexpectedError(err);
			});
	}

	public dispose(): void {
		if (this.promise) {
			this.promise.then(null, () => { })
				.then(() => {
					this.loadingPhase = LoadingPhase.Disposed;
					return this.doDispose(this._cacheKey);
				}).then(null, err => {
					errors.onUnexpectedError(err);
				});
		} else {
			this.loadingPhase = LoadingPhase.Disposed;
		}
		if (this.previous) {
			this.previous.dispose();
			this.previous = null;
		}
	}
}

{'fraction_non_alphanumeric': 0.12999231082364648, 'fraction_numerical': 0.000768917635352119, 'mean_word_length': 4.47412725922258, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5625649', 'n_tokens_mistral': 6927, 'n_tokens_neox': 6327, 'n_words': 1460}