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

/**
 * Explorer viewlet id.
 */
export const VIEWLET_ID = 'workbench.view.explorer';
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

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

	/**
64 65
	 * 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 已提交
66
	 */
67
	select(resource: URI, reveal?: boolean): Promise<void>;
I
isidor 已提交
68 69 70
}
export const IExplorerService = createDecorator<IExplorerService>('explorerService');

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

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

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


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

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

128 129 130 131 132 133 134 135
export const SortOrderConfiguration = {
	DEFAULT: 'default',
	MIXED: 'mixed',
	FILES_FIRST: 'filesFirst',
	TYPE: 'type',
	MODIFIED: 'modified'
};

136 137
export type SortOrder = 'default' | 'mixed' | 'filesFirst' | 'type' | 'modified';

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

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

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

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

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

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

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

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

186
		return codeEditorModel;
187 188
	}

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

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

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

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

		return codeEditorModel;
213
	}
214
}
I
isidor 已提交
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 242 243 244 245 246

export class OpenEditor implements IEditorIdentifier {

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

	public get editor() {
		return this._editor;
	}

	public get editorIndex() {
		return this._group.getIndexOfEditor(this.editor);
	}

	public get group() {
		return this._group;
	}

	public get groupId() {
		return this._group.id;
	}

	public getId(): string {
		return `openeditor:${this.groupId}:${this.editorIndex}:${this.editor.getName()}:${this.editor.getDescription()}`;
	}

	public isPreview(): boolean {
		return this._group.previewEditor === this.editor;
	}

	public isUntitled(): boolean {
247
		return !!toResource(this.editor, { supportSideBySide: SideBySideEditor.MASTER, filterByScheme: Schemas.untitled });
I
isidor 已提交
248 249 250 251 252 253
	}

	public isDirty(): boolean {
		return this.editor.isDirty();
	}

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