files.ts 10.1 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, 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 { IDisposable, dispose } 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 44 45
export interface IExplorerService {
	_serviceBrand: any;
	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;
54
	getEditableData(stat: ExplorerItem): IEditableData | undefined;
I
isidor 已提交
55 56
	// If undefined is passed checks if any element is currently being edited.
	isEditable(stat: ExplorerItem | undefined): boolean;
I
isidor 已提交
57
	findClosest(resource: URI): ExplorerItem | null;
I
isidor 已提交
58
	refresh(): void;
I
isidor 已提交
59 60
	setToCopy(stats: ExplorerItem[], cut: boolean): void;
	isCut(stat: ExplorerItem): boolean;
I
isidor 已提交
61 62

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

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

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

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


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

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

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

134 135
export type SortOrder = 'default' | 'mixed' | 'filesFirst' | 'type' | 'modified';

136
export class TextFileContentProvider implements ITextModelContentProvider {
B
Benjamin Pasero 已提交
137
	private fileWatcherDisposable: IDisposable | undefined;
138 139

	constructor(
140 141 142
		@ITextFileService private readonly textFileService: ITextFileService,
		@IFileService private readonly fileService: IFileService,
		@IModeService private readonly modeService: IModeService,
143
		@IModelService private readonly modelService: IModelService
144 145
	) { }

146 147 148 149 150 151 152
	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
		});
153 154 155 156 157 158 159 160
	}

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

163
	async provideTextContent(resource: URI): Promise<ITextModel> {
164
		const savedFileResource = TextFileContentProvider.textFileToResource(resource);
165

166
		// Make sure our text file is resolved up to date
167
		const codeEditorModel = await this.resolveEditorModel(resource);
168

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

			if (codeEditorModel) {
				once(codeEditorModel.onWillDispose)(() => {
					dispose(this.fileWatcherDisposable);
					this.fileWatcherDisposable = undefined;
				});
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 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 {
				languageSelector = this.modeService.createByFilepathOrFirstLine(savedFileResource.path);
206 207
			}

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

		return codeEditorModel;
212 213
	}

B
Benjamin Pasero 已提交
214
	dispose(): void {
B
Benjamin Pasero 已提交
215 216
		dispose(this.fileWatcherDisposable);
		this.fileWatcherDisposable = undefined;
217
	}
218
}
I
isidor 已提交
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 247 248 249 250

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 {
251
		return !!toResource(this.editor, { supportSideBySide: SideBySideEditor.MASTER, filterByScheme: Schemas.untitled });
I
isidor 已提交
252 253 254 255 256 257
	}

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

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