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

6
import { URI } from 'vs/base/common/uri';
7
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
B
Benjamin Pasero 已提交
8
import { IWorkbenchEditorConfiguration, IEditorIdentifier, IEditorInput, toResource, SideBySideEditor } from 'vs/workbench/common/editor';
9
import { IFilesConfiguration as PlatformIFilesConfiguration, FileChangeType, IFileService } from 'vs/platform/files/common/files';
B
Benjamin Pasero 已提交
10
import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
11
import { ITextModelContentProvider } from 'vs/editor/common/services/resolverService';
12
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
A
Alex Dima 已提交
13
import { ITextModel } from 'vs/editor/common/model';
I
isidor 已提交
14
import { Event } from 'vs/base/common/event';
15
import { IModelService } from 'vs/editor/common/services/modelService';
A
Alex Dima 已提交
16
import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService';
17
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
18
import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys';
S
Sandeep Somavarapu 已提交
19
import { Registry } from 'vs/platform/registry/common/platform';
20
import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainer, IEditableData, ViewContainerLocation } from 'vs/workbench/common/views';
I
isidor 已提交
21
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
22
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
23
import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel';
B
Benjamin Pasero 已提交
24
import { once } from 'vs/base/common/functional';
25 26
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
27
import { localize } from 'vs/nls';
E
Erich Gamma 已提交
28 29 30 31 32

/**
 * Explorer viewlet id.
 */
export const VIEWLET_ID = 'workbench.view.explorer';
33

S
Sandeep Somavarapu 已提交
34 35 36
/**
 * Explorer viewlet container.
 */
37
export const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: VIEWLET_ID, name: localize('explore', "Explorer") }, ViewContainerLocation.Sidebar);
E
Erich Gamma 已提交
38

I
isidor 已提交
39
export interface IExplorerService {
40
	_serviceBrand: undefined;
I
isidor 已提交
41
	readonly roots: ExplorerItem[];
I
isidor 已提交
42
	readonly sortOrder: SortOrder;
I
isidor 已提交
43
	readonly onDidChangeRoots: Event<void>;
44
	readonly onDidChangeItem: Event<{ item?: ExplorerItem, recursive: boolean }>;
45
	readonly onDidChangeEditable: Event<ExplorerItem>;
I
isidor 已提交
46
	readonly onDidSelectResource: Event<{ resource?: URI, reveal?: boolean }>;
I
isidor 已提交
47
	readonly onDidCopyItems: Event<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }>;
I
isidor 已提交
48

49
	getContext(respectMultiSelection: boolean): ExplorerItem[];
M
Matt Bierner 已提交
50
	setEditable(stat: ExplorerItem, data: IEditableData | null): void;
J
jeanp413 已提交
51
	getEditable(): { stat: ExplorerItem, data: IEditableData } | undefined;
52
	getEditableData(stat: ExplorerItem): IEditableData | undefined;
I
isidor 已提交
53 54
	// If undefined is passed checks if any element is currently being edited.
	isEditable(stat: ExplorerItem | undefined): boolean;
I
isidor 已提交
55
	findClosest(resource: URI): ExplorerItem | null;
I
isidor 已提交
56
	refresh(): void;
I
isidor 已提交
57 58
	setToCopy(stats: ExplorerItem[], cut: boolean): void;
	isCut(stat: ExplorerItem): boolean;
I
isidor 已提交
59 60

	/**
61 62
	 * Selects and reveal the file element provided by the given resource if its found in the explorer.
	 * Will try to resolve the path in case the explorer is not yet expanded to the file yet.
I
isidor 已提交
63
	 */
64
	select(resource: URI, reveal?: boolean): Promise<void>;
65 66 67 68 69 70

	registerContextProvider(contextProvider: IContextProvider): void;
}

export interface IContextProvider {
	getContext(respectMultiSelection: boolean): ExplorerItem[];
I
isidor 已提交
71
}
72

I
isidor 已提交
73 74
export const IExplorerService = createDecorator<IExplorerService>('explorerService');

75 76 77
/**
 * Context Keys to use with keybindings for the Explorer and Open Editors view
 */
I
isidor 已提交
78 79 80
export const ExplorerViewletVisibleContext = new RawContextKey<boolean>('explorerViewletVisible', true);
export const ExplorerFolderContext = new RawContextKey<boolean>('explorerResourceIsFolder', false);
export const ExplorerResourceReadonlyContext = new RawContextKey<boolean>('explorerResourceReadonly', false);
81
export const ExplorerResourceNotReadonlyContext = ExplorerResourceReadonlyContext.toNegated();
I
isidor 已提交
82 83
export const ExplorerRootContext = new RawContextKey<boolean>('explorerResourceIsRoot', false);
export const ExplorerResourceCut = new RawContextKey<boolean>('explorerResourceCut', false);
84
export const ExplorerResourceMoveableToTrash = new RawContextKey<boolean>('explorerResourceMoveableToTrash', false);
I
isidor 已提交
85 86 87 88
export const FilesExplorerFocusedContext = new RawContextKey<boolean>('filesExplorerFocus', true);
export const OpenEditorsVisibleContext = new RawContextKey<boolean>('openEditorsVisible', false);
export const OpenEditorsFocusedContext = new RawContextKey<boolean>('openEditorsFocus', true);
export const ExplorerFocusedContext = new RawContextKey<boolean>('explorerViewletFocus', true);
B
Benjamin Pasero 已提交
89

90 91 92 93 94
// compressed nodes
export const ExplorerCompressedFocusContext = new RawContextKey<boolean>('explorerViewletCompressedFocus', true);
export const ExplorerCompressedFirstFocusContext = new RawContextKey<boolean>('explorerViewletCompressedFirstFocus', true);
export const ExplorerCompressedLastFocusContext = new RawContextKey<boolean>('explorerViewletCompressedLastFocus', true);

I
isidor 已提交
95 96
export const FilesExplorerFocusCondition = ContextKeyExpr.and(ExplorerViewletVisibleContext, FilesExplorerFocusedContext, ContextKeyExpr.not(InputFocusedContextKey));
export const ExplorerFocusCondition = ContextKeyExpr.and(ExplorerViewletVisibleContext, ExplorerFocusedContext, ContextKeyExpr.not(InputFocusedContextKey));
A
Alex Dima 已提交
97

I
isidor 已提交
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
/**
 * Text file editor id.
 */
export const TEXT_FILE_EDITOR_ID = 'workbench.editors.files.textFileEditor';

/**
 * File editor input id.
 */
export const FILE_EDITOR_INPUT_ID = 'workbench.editors.files.fileEditorInput';

/**
 * Binary file editor id.
 */
export const BINARY_FILE_EDITOR_ID = 'workbench.editors.files.binaryFileEditor';

113
export interface IFilesConfiguration extends PlatformIFilesConfiguration, IWorkbenchEditorConfiguration {
E
Erich Gamma 已提交
114
	explorer: {
I
isidor 已提交
115
		openEditors: {
116
			visible: number;
E
Erich Gamma 已提交
117
		};
118
		autoReveal: boolean;
119
		enableDragAndDrop: boolean;
120
		confirmDelete: boolean;
121
		sortOrder: SortOrder;
122 123 124
		decorations: {
			colors: boolean;
			badges: boolean;
J
Johannes Rieken 已提交
125
		};
I
isidor 已提交
126
		incrementalNaming: 'simple' | 'smart';
E
Erich Gamma 已提交
127 128 129 130 131 132
	};
	editor: IEditorOptions;
}

export interface IFileResource {
	resource: URI;
133
	isDirectory?: boolean;
E
Erich Gamma 已提交
134 135
}

136 137 138 139 140 141 142
export const enum SortOrder {
	Default = 'default',
	Mixed = 'mixed',
	FilesFirst = 'filesFirst',
	Type = 'type',
	Modified = 'modified'
}
143

144 145
export class TextFileContentProvider extends Disposable implements ITextModelContentProvider {
	private readonly fileWatcherDisposable = this._register(new MutableDisposable());
146 147

	constructor(
148 149 150
		@ITextFileService private readonly textFileService: ITextFileService,
		@IFileService private readonly fileService: IFileService,
		@IModeService private readonly modeService: IModeService,
151
		@IModelService private readonly modelService: IModelService
152 153 154
	) {
		super();
	}
155

156 157 158 159 160 161 162
	static async open(resource: URI, scheme: string, label: string, editorService: IEditorService, options?: ITextEditorOptions): Promise<void> {
		await editorService.openEditor({
			leftResource: TextFileContentProvider.resourceToTextFile(scheme, resource),
			rightResource: resource,
			label,
			options
		});
163 164 165 166 167 168 169 170
	}

	private static resourceToTextFile(scheme: string, resource: URI): URI {
		return resource.with({ scheme, query: JSON.stringify({ scheme: resource.scheme }) });
	}

	private static textFileToResource(resource: URI): URI {
		return resource.with({ scheme: JSON.parse(resource.query)['scheme'], query: null });
171 172
	}

173
	async provideTextContent(resource: URI): Promise<ITextModel> {
174
		const savedFileResource = TextFileContentProvider.textFileToResource(resource);
175

176
		// Make sure our text file is resolved up to date
177
		const codeEditorModel = await this.resolveEditorModel(resource);
178

179
		// Make sure to keep contents up to date when it changes
180 181
		if (!this.fileWatcherDisposable.value) {
			this.fileWatcherDisposable.value = this.fileService.onFileChanges(changes => {
182 183
				if (changes.contains(savedFileResource, FileChangeType.UPDATED)) {
					this.resolveEditorModel(resource, false /* do not create if missing */); // update model when resource changes
M
Matt Bierner 已提交
184
				}
185 186 187
			});

			if (codeEditorModel) {
188
				once(codeEditorModel.onWillDispose)(() => this.fileWatcherDisposable.clear());
189
			}
190
		}
191

192
		return codeEditorModel;
193 194
	}

J
Johannes Rieken 已提交
195 196
	private resolveEditorModel(resource: URI, createAsNeeded?: true): Promise<ITextModel>;
	private resolveEditorModel(resource: URI, createAsNeeded?: boolean): Promise<ITextModel | null>;
197
	private async resolveEditorModel(resource: URI, createAsNeeded: boolean = true): Promise<ITextModel | null> {
198
		const savedFileResource = TextFileContentProvider.textFileToResource(resource);
199

200
		const content = await this.textFileService.readStream(savedFileResource);
201

202 203 204 205 206 207 208 209 210 211
		let codeEditorModel = this.modelService.getModel(resource);
		if (codeEditorModel) {
			this.modelService.updateModel(codeEditorModel, content.value);
		} else if (createAsNeeded) {
			const textFileModel = this.modelService.getModel(savedFileResource);

			let languageSelector: ILanguageSelection;
			if (textFileModel) {
				languageSelector = this.modeService.create(textFileModel.getModeId());
			} else {
B
Benjamin Pasero 已提交
212
				languageSelector = this.modeService.createByFilepathOrFirstLine(savedFileResource);
213 214
			}

215 216 217 218
			codeEditorModel = this.modelService.createModel(content.value, languageSelector, resource);
		}

		return codeEditorModel;
219
	}
220
}
I
isidor 已提交
221 222 223 224 225 226 227

export class OpenEditor implements IEditorIdentifier {

	constructor(private _editor: IEditorInput, private _group: IEditorGroup) {
		// noop
	}

228
	get editor() {
I
isidor 已提交
229 230 231
		return this._editor;
	}

232
	get editorIndex() {
I
isidor 已提交
233 234 235
		return this._group.getIndexOfEditor(this.editor);
	}

236
	get group() {
I
isidor 已提交
237 238 239
		return this._group;
	}

240
	get groupId() {
I
isidor 已提交
241 242 243
		return this._group.id;
	}

244
	getId(): string {
I
isidor 已提交
245 246 247
		return `openeditor:${this.groupId}:${this.editorIndex}:${this.editor.getName()}:${this.editor.getDescription()}`;
	}

248
	isPreview(): boolean {
I
isidor 已提交
249 250 251
		return this._group.previewEditor === this.editor;
	}

252
	isDirty(): boolean {
I
isidor 已提交
253 254 255
		return this.editor.isDirty();
	}

256
	getResource(): URI | undefined {
B
Benjamin Pasero 已提交
257
		return toResource(this.editor, { supportSideBySide: SideBySideEditor.MASTER });
I
isidor 已提交
258 259
	}
}