files.ts 10.5 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 20
import { Registry } from 'vs/platform/registry/common/platform';
import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainer } 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';
E
Erich Gamma 已提交
27 28 29 30 31

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

S
Sandeep Somavarapu 已提交
33 34 35 36
/**
 * Explorer viewlet container.
 */
export const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer(VIEWLET_ID);
E
Erich Gamma 已提交
37

38
export interface IEditableData {
M
Matt Bierner 已提交
39
	validationMessage: (value: string) => string | null;
40
	onFinish: (value: string, success: boolean) => void;
41 42
}

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

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

	/**
65 66
	 * 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 已提交
67
	 */
68
	select(resource: URI, reveal?: boolean): Promise<void>;
69 70 71 72 73 74

	registerContextProvider(contextProvider: IContextProvider): void;
}

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

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

79 80 81
/**
 * Context Keys to use with keybindings for the Explorer and Open Editors view
 */
I
isidor 已提交
82 83 84
export const ExplorerViewletVisibleContext = new RawContextKey<boolean>('explorerViewletVisible', true);
export const ExplorerFolderContext = new RawContextKey<boolean>('explorerResourceIsFolder', false);
export const ExplorerResourceReadonlyContext = new RawContextKey<boolean>('explorerResourceReadonly', false);
85
export const ExplorerResourceNotReadonlyContext = ExplorerResourceReadonlyContext.toNegated();
I
isidor 已提交
86 87
export const ExplorerRootContext = new RawContextKey<boolean>('explorerResourceIsRoot', false);
export const ExplorerResourceCut = new RawContextKey<boolean>('explorerResourceCut', false);
88
export const ExplorerResourceMoveableToTrash = new RawContextKey<boolean>('explorerResourceMoveableToTrash', false);
I
isidor 已提交
89 90 91 92
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 已提交
93

94 95 96 97 98
// 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 已提交
99 100
export const FilesExplorerFocusCondition = ContextKeyExpr.and(ExplorerViewletVisibleContext, FilesExplorerFocusedContext, ContextKeyExpr.not(InputFocusedContextKey));
export const ExplorerFocusCondition = ContextKeyExpr.and(ExplorerViewletVisibleContext, ExplorerFocusedContext, ContextKeyExpr.not(InputFocusedContextKey));
A
Alex Dima 已提交
101

I
isidor 已提交
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
/**
 * 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';

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

export interface IFileResource {
	resource: URI;
137
	isDirectory?: boolean;
E
Erich Gamma 已提交
138 139
}

140 141 142 143 144 145 146 147
export const SortOrderConfiguration = {
	DEFAULT: 'default',
	MIXED: 'mixed',
	FILES_FIRST: 'filesFirst',
	TYPE: 'type',
	MODIFIED: 'modified'
};

148 149
export type SortOrder = 'default' | 'mixed' | 'filesFirst' | 'type' | 'modified';

150 151
export class TextFileContentProvider extends Disposable implements ITextModelContentProvider {
	private readonly fileWatcherDisposable = this._register(new MutableDisposable());
152 153

	constructor(
154 155 156
		@ITextFileService private readonly textFileService: ITextFileService,
		@IFileService private readonly fileService: IFileService,
		@IModeService private readonly modeService: IModeService,
157
		@IModelService private readonly modelService: IModelService
158 159 160
	) {
		super();
	}
161

162 163 164 165 166 167 168
	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
		});
169 170 171 172 173 174 175 176
	}

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

179
	async provideTextContent(resource: URI): Promise<ITextModel> {
180
		const savedFileResource = TextFileContentProvider.textFileToResource(resource);
181

182
		// Make sure our text file is resolved up to date
183
		const codeEditorModel = await this.resolveEditorModel(resource);
184

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

			if (codeEditorModel) {
194
				once(codeEditorModel.onWillDispose)(() => this.fileWatcherDisposable.clear());
195
			}
196
		}
197

198
		return codeEditorModel;
199 200
	}

J
Johannes Rieken 已提交
201 202
	private resolveEditorModel(resource: URI, createAsNeeded?: true): Promise<ITextModel>;
	private resolveEditorModel(resource: URI, createAsNeeded?: boolean): Promise<ITextModel | null>;
203
	private async resolveEditorModel(resource: URI, createAsNeeded: boolean = true): Promise<ITextModel | null> {
204
		const savedFileResource = TextFileContentProvider.textFileToResource(resource);
205

206
		const content = await this.textFileService.readStream(savedFileResource);
207

208 209 210 211 212 213 214 215 216 217
		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 已提交
218
				languageSelector = this.modeService.createByFilepathOrFirstLine(savedFileResource);
219 220
			}

221 222 223 224
			codeEditorModel = this.modelService.createModel(content.value, languageSelector, resource);
		}

		return codeEditorModel;
225
	}
226
}
I
isidor 已提交
227 228 229 230 231 232 233

export class OpenEditor implements IEditorIdentifier {

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

234
	get editor() {
I
isidor 已提交
235 236 237
		return this._editor;
	}

238
	get editorIndex() {
I
isidor 已提交
239 240 241
		return this._group.getIndexOfEditor(this.editor);
	}

242
	get group() {
I
isidor 已提交
243 244 245
		return this._group;
	}

246
	get groupId() {
I
isidor 已提交
247 248 249
		return this._group.id;
	}

250
	getId(): string {
I
isidor 已提交
251 252 253
		return `openeditor:${this.groupId}:${this.editorIndex}:${this.editor.getName()}:${this.editor.getDescription()}`;
	}

254
	isPreview(): boolean {
I
isidor 已提交
255 256 257
		return this._group.previewEditor === this.editor;
	}

258
	isDirty(): boolean {
I
isidor 已提交
259 260 261
		return this.editor.isDirty();
	}

262
	getResource(): URI | undefined {
B
Benjamin Pasero 已提交
263
		return toResource(this.editor, { supportSideBySide: SideBySideEditor.MASTER });
I
isidor 已提交
264 265
	}
}