extHostFileSystemEventService.ts 6.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 7
import { flatten } from 'vs/base/common/arrays';
import { AsyncEmitter, Emitter, Event } from 'vs/base/common/event';
8
import { IRelativePattern, parse } from 'vs/base/common/glob';
9
import { URI, UriComponents } from 'vs/base/common/uri';
J
Johannes Rieken 已提交
10
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
11
import * as vscode from 'vscode';
J
Johannes Rieken 已提交
12
import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, IResourceFileEditDto, IResourceTextEditDto, MainThreadTextEditorsShape } from './extHost.protocol';
13 14
import * as typeConverter from './extHostTypeConverters';
import { Disposable, WorkspaceEdit } from './extHostTypes';
15
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
E
Erich Gamma 已提交
16

17
class FileSystemWatcher implements vscode.FileSystemWatcher {
E
Erich Gamma 已提交
18

19 20 21
	private _onDidCreate = new Emitter<vscode.Uri>();
	private _onDidChange = new Emitter<vscode.Uri>();
	private _onDidDelete = new Emitter<vscode.Uri>();
E
Erich Gamma 已提交
22 23 24
	private _disposable: Disposable;
	private _config: number;

J
Johannes Rieken 已提交
25
	get ignoreCreateEvents(): boolean {
E
Erich Gamma 已提交
26 27 28
		return Boolean(this._config & 0b001);
	}

J
Johannes Rieken 已提交
29
	get ignoreChangeEvents(): boolean {
E
Erich Gamma 已提交
30 31 32
		return Boolean(this._config & 0b010);
	}

J
Johannes Rieken 已提交
33
	get ignoreDeleteEvents(): boolean {
E
Erich Gamma 已提交
34 35 36
		return Boolean(this._config & 0b100);
	}

37
	constructor(dispatcher: Event<FileSystemEvents>, globPattern: string | IRelativePattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean) {
E
Erich Gamma 已提交
38 39

		this._config = 0;
J
Johannes Rieken 已提交
40
		if (ignoreCreateEvents) {
E
Erich Gamma 已提交
41 42
			this._config += 0b001;
		}
J
Johannes Rieken 已提交
43
		if (ignoreChangeEvents) {
E
Erich Gamma 已提交
44 45
			this._config += 0b010;
		}
J
Johannes Rieken 已提交
46
		if (ignoreDeleteEvents) {
E
Erich Gamma 已提交
47 48 49
			this._config += 0b100;
		}

B
Benjamin Pasero 已提交
50
		const parsedPattern = parse(globPattern);
51

52
		const subscription = dispatcher(events => {
E
Erich Gamma 已提交
53 54
			if (!ignoreCreateEvents) {
				for (let created of events.created) {
55
					const uri = URI.revive(created);
J
Johannes Rieken 已提交
56 57
					if (parsedPattern(uri.fsPath)) {
						this._onDidCreate.fire(uri);
E
Erich Gamma 已提交
58 59 60 61 62
					}
				}
			}
			if (!ignoreChangeEvents) {
				for (let changed of events.changed) {
63
					const uri = URI.revive(changed);
J
Johannes Rieken 已提交
64 65
					if (parsedPattern(uri.fsPath)) {
						this._onDidChange.fire(uri);
E
Erich Gamma 已提交
66 67 68 69 70
					}
				}
			}
			if (!ignoreDeleteEvents) {
				for (let deleted of events.deleted) {
71
					const uri = URI.revive(deleted);
J
Johannes Rieken 已提交
72 73
					if (parsedPattern(uri.fsPath)) {
						this._onDidDelete.fire(uri);
E
Erich Gamma 已提交
74 75 76 77 78 79 80 81 82 83 84 85
					}
				}
			}
		});

		this._disposable = Disposable.from(this._onDidCreate, this._onDidChange, this._onDidDelete, subscription);
	}

	dispose() {
		this._disposable.dispose();
	}

86
	get onDidCreate(): Event<vscode.Uri> {
E
Erich Gamma 已提交
87 88 89
		return this._onDidCreate.event;
	}

90
	get onDidChange(): Event<vscode.Uri> {
E
Erich Gamma 已提交
91 92 93
		return this._onDidChange.event;
	}

94
	get onDidDelete(): Event<vscode.Uri> {
E
Erich Gamma 已提交
95 96 97 98
		return this._onDidDelete.event;
	}
}

99 100 101 102 103
interface WillRenameListener {
	extension: IExtensionDescription;
	(e: vscode.FileWillRenameEvent): any;
}

104
export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServiceShape {
E
Erich Gamma 已提交
105

106 107 108
	private readonly _onFileEvent = new Emitter<FileSystemEvents>();
	private readonly _onDidRenameFile = new Emitter<vscode.FileRenameEvent>();
	private readonly _onWillRenameFile = new AsyncEmitter<vscode.FileWillRenameEvent>();
109 110

	readonly onDidRenameFile: Event<vscode.FileRenameEvent> = this._onDidRenameFile.event;
E
Erich Gamma 已提交
111

112 113 114 115 116 117
	constructor(
		mainContext: IMainContext,
		private readonly _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors,
		private readonly _mainThreadTextEditors: MainThreadTextEditorsShape = mainContext.getProxy(MainContext.MainThreadTextEditors)
	) {
		//
E
Erich Gamma 已提交
118 119
	}

120 121
	public createFileSystemWatcher(globPattern: string | IRelativePattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): vscode.FileSystemWatcher {
		return new FileSystemWatcher(this._onFileEvent.event, globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents);
E
Erich Gamma 已提交
122 123
	}

A
Alex Dima 已提交
124
	$onFileEvent(events: FileSystemEvents) {
125 126 127 128 129
		this._onFileEvent.fire(events);
	}

	$onFileRename(oldUri: UriComponents, newUri: UriComponents) {
		this._onDidRenameFile.fire(Object.freeze({ oldUri: URI.revive(oldUri), newUri: URI.revive(newUri) }));
E
Erich Gamma 已提交
130
	}
131

132 133
	getOnWillRenameFileEvent(extension: IExtensionDescription): Event<vscode.FileWillRenameEvent> {
		return (listener, thisArg, disposables) => {
134 135 136
			const wrappedListener: WillRenameListener = <any>((e: vscode.FileWillRenameEvent) => {
				listener.call(thisArg, e);
			});
137 138 139 140
			wrappedListener.extension = extension;
			return this._onWillRenameFile.event(wrappedListener, undefined, disposables);
		};
	}
141

J
Johannes Rieken 已提交
142
	$onWillRename(oldUriDto: UriComponents, newUriDto: UriComponents): Promise<any> {
143 144 145 146
		const oldUri = URI.revive(oldUriDto);
		const newUri = URI.revive(newUriDto);

		const edits: WorkspaceEdit[] = [];
147
		return Promise.resolve(this._onWillRenameFile.fireAsync((bucket, _listener) => {
148 149 150
			return {
				oldUri,
				newUri,
J
Johannes Rieken 已提交
151
				waitUntil: (thenable: Promise<vscode.WorkspaceEdit>): void => {
152 153 154 155
					if (Object.isFrozen(bucket)) {
						throw new TypeError('waitUntil cannot be called async');
					}
					const index = bucket.length;
156
					const wrappedThenable = Promise.resolve(thenable).then(result => {
157 158 159 160 161 162 163 164 165
						// ignore all results except for WorkspaceEdits. Those
						// are stored in a spare array
						if (result instanceof WorkspaceEdit) {
							edits[index] = result;
						}
					});
					bucket.push(wrappedThenable);
				}
			};
166
		}).then((): any => {
167 168 169 170 171
			if (edits.length === 0) {
				return undefined;
			}
			// flatten all WorkspaceEdits collected via waitUntil-call
			// and apply them in one go.
J
Johannes Rieken 已提交
172
			const allEdits = new Array<Array<IResourceFileEditDto | IResourceTextEditDto>>();
173 174 175 176
			for (let edit of edits) {
				if (edit) { // sparse array
					let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);
					allEdits.push(edits);
177 178
				}
			}
179
			return this._mainThreadTextEditors.$tryApplyWorkspaceEdit({ edits: flatten(allEdits) });
180
		}));
181
	}
E
Erich Gamma 已提交
182
}