files.ts 9.9 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';
19
import { IEditableData } from 'vs/workbench/common/views';
I
isidor 已提交
20
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
21
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
22
import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel';
B
Benjamin Pasero 已提交
23
import { once } from 'vs/base/common/functional';
24 25
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
E
Erich Gamma 已提交
26 27 28 29 30

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

I
isidor 已提交
32
export interface IExplorerService {
33
	_serviceBrand: undefined;
I
isidor 已提交
34
	readonly roots: ExplorerItem[];
I
isidor 已提交
35
	readonly sortOrder: SortOrder;
I
isidor 已提交
36
	readonly onDidChangeRoots: Event<void>;
37
	readonly onDidChangeItem: Event<{ item?: ExplorerItem, recursive: boolean }>;
38
	readonly onDidChangeEditable: Event<ExplorerItem>;
I
isidor 已提交
39
	readonly onDidSelectResource: Event<{ resource?: URI, reveal?: boolean }>;
I
isidor 已提交
40
	readonly onDidCopyItems: Event<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }>;
I
isidor 已提交
41

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

	/**
54 55
	 * 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 已提交
56
	 */
57
	select(resource: URI, reveal?: boolean): Promise<void>;
58 59 60 61 62 63

	registerContextProvider(contextProvider: IContextProvider): void;
}

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

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

68 69 70
/**
 * Context Keys to use with keybindings for the Explorer and Open Editors view
 */
I
isidor 已提交
71 72 73
export const ExplorerViewletVisibleContext = new RawContextKey<boolean>('explorerViewletVisible', true);
export const ExplorerFolderContext = new RawContextKey<boolean>('explorerResourceIsFolder', false);
export const ExplorerResourceReadonlyContext = new RawContextKey<boolean>('explorerResourceReadonly', false);
74
export const ExplorerResourceNotReadonlyContext = ExplorerResourceReadonlyContext.toNegated();
I
isidor 已提交
75 76
export const ExplorerRootContext = new RawContextKey<boolean>('explorerResourceIsRoot', false);
export const ExplorerResourceCut = new RawContextKey<boolean>('explorerResourceCut', false);
77
export const ExplorerResourceMoveableToTrash = new RawContextKey<boolean>('explorerResourceMoveableToTrash', false);
I
isidor 已提交
78 79 80 81
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 已提交
82

83 84 85 86 87
// 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 已提交
88 89
export const FilesExplorerFocusCondition = ContextKeyExpr.and(ExplorerViewletVisibleContext, FilesExplorerFocusedContext, ContextKeyExpr.not(InputFocusedContextKey));
export const ExplorerFocusCondition = ContextKeyExpr.and(ExplorerViewletVisibleContext, ExplorerFocusedContext, ContextKeyExpr.not(InputFocusedContextKey));
A
Alex Dima 已提交
90

I
isidor 已提交
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
/**
 * 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';

106
export interface IFilesConfiguration extends PlatformIFilesConfiguration, IWorkbenchEditorConfiguration {
E
Erich Gamma 已提交
107
	explorer: {
I
isidor 已提交
108
		openEditors: {
109
			visible: number;
E
Erich Gamma 已提交
110
		};
111
		autoReveal: boolean;
112
		enableDragAndDrop: boolean;
113
		confirmDelete: boolean;
114
		sortOrder: SortOrder;
115 116 117
		decorations: {
			colors: boolean;
			badges: boolean;
J
Johannes Rieken 已提交
118
		};
I
isidor 已提交
119
		incrementalNaming: 'simple' | 'smart';
E
Erich Gamma 已提交
120 121 122 123 124 125
	};
	editor: IEditorOptions;
}

export interface IFileResource {
	resource: URI;
126
	isDirectory?: boolean;
E
Erich Gamma 已提交
127 128
}

129 130 131 132 133 134 135
export const enum SortOrder {
	Default = 'default',
	Mixed = 'mixed',
	FilesFirst = 'filesFirst',
	Type = 'type',
	Modified = 'modified'
}
136

137 138
export class TextFileContentProvider extends Disposable implements ITextModelContentProvider {
	private readonly fileWatcherDisposable = this._register(new MutableDisposable());
139 140

	constructor(
141 142 143
		@ITextFileService private readonly textFileService: ITextFileService,
		@IFileService private readonly fileService: IFileService,
		@IModeService private readonly modeService: IModeService,
144
		@IModelService private readonly modelService: IModelService
145 146 147
	) {
		super();
	}
148

149 150 151 152 153 154 155
	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
		});
156 157 158 159 160 161 162 163
	}

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

166
	async provideTextContent(resource: URI): Promise<ITextModel> {
167
		const savedFileResource = TextFileContentProvider.textFileToResource(resource);
168

169
		// Make sure our text file is resolved up to date
170
		const codeEditorModel = await this.resolveEditorModel(resource);
171

172
		// Make sure to keep contents up to date when it changes
173 174
		if (!this.fileWatcherDisposable.value) {
			this.fileWatcherDisposable.value = this.fileService.onFileChanges(changes => {
175 176
				if (changes.contains(savedFileResource, FileChangeType.UPDATED)) {
					this.resolveEditorModel(resource, false /* do not create if missing */); // update model when resource changes
M
Matt Bierner 已提交
177
				}
178 179 180
			});

			if (codeEditorModel) {
181
				once(codeEditorModel.onWillDispose)(() => this.fileWatcherDisposable.clear());
182
			}
183
		}
184

185
		return codeEditorModel;
186 187
	}

J
Johannes Rieken 已提交
188 189
	private resolveEditorModel(resource: URI, createAsNeeded?: true): Promise<ITextModel>;
	private resolveEditorModel(resource: URI, createAsNeeded?: boolean): Promise<ITextModel | null>;
190
	private async resolveEditorModel(resource: URI, createAsNeeded: boolean = true): Promise<ITextModel | null> {
191
		const savedFileResource = TextFileContentProvider.textFileToResource(resource);
192

193
		const content = await this.textFileService.readStream(savedFileResource);
194

195 196 197 198 199 200 201 202 203 204
		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 已提交
205
				languageSelector = this.modeService.createByFilepathOrFirstLine(savedFileResource);
206 207
			}

208 209 210 211
			codeEditorModel = this.modelService.createModel(content.value, languageSelector, resource);
		}

		return codeEditorModel;
212
	}
213
}
I
isidor 已提交
214 215 216 217 218 219 220

export class OpenEditor implements IEditorIdentifier {

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

221
	get editor() {
I
isidor 已提交
222 223 224
		return this._editor;
	}

225
	get editorIndex() {
I
isidor 已提交
226 227 228
		return this._group.getIndexOfEditor(this.editor);
	}

229
	get group() {
I
isidor 已提交
230 231 232
		return this._group;
	}

233
	get groupId() {
I
isidor 已提交
234 235 236
		return this._group.id;
	}

237
	getId(): string {
I
isidor 已提交
238 239 240
		return `openeditor:${this.groupId}:${this.editorIndex}:${this.editor.getName()}:${this.editor.getDescription()}`;
	}

241
	isPreview(): boolean {
I
isidor 已提交
242 243 244
		return this._group.previewEditor === this.editor;
	}

245
	isDirty(): boolean {
I
isidor 已提交
246 247 248
		return this.editor.isDirty();
	}

249
	getResource(): URI | undefined {
B
Benjamin Pasero 已提交
250
		return toResource(this.editor, { supportSideBySide: SideBySideEditor.MASTER });
I
isidor 已提交
251 252
	}
}