Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 (#6381)

* Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973

* disable strict null check
This commit is contained in:
Anthony Dresser
2019-07-15 22:35:46 -07:00
committed by GitHub
parent f720ec642f
commit 0b7e7ddbf9
2406 changed files with 59140 additions and 35464 deletions

View File

@@ -0,0 +1,142 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { IFileSystemProviderWithFileReadWriteCapability, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileWriteOptions, FileDeleteOptions, FileSystemProviderCapabilities, IFileSystemProviderWithOpenReadWriteCloseCapability, FileOpenOptions, hasReadWriteCapability, hasOpenReadWriteCloseCapability } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import * as resources from 'vs/base/common/resources';
import { startsWith } from 'vs/base/common/strings';
import { BACKUPS } from 'vs/platform/environment/common/environment';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
export class FileUserDataProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithOpenReadWriteCloseCapability {
readonly capabilities: FileSystemProviderCapabilities = this.fileSystemProvider.capabilities;
readonly onDidChangeCapabilities: Event<void> = Event.None;
private readonly _onDidChangeFile: Emitter<IFileChange[]> = this._register(new Emitter<IFileChange[]>());
readonly onDidChangeFile: Event<IFileChange[]> = this._onDidChangeFile.event;
private readonly userDataHome: URI;
constructor(
private readonly fileSystemUserDataHome: URI,
private readonly fileSystemBackupsHome: URI,
private readonly fileSystemProvider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability,
environmentService: IWorkbenchEnvironmentService
) {
super();
this.userDataHome = environmentService.userRoamingDataHome;
// Assumption: This path always exists
this._register(this.fileSystemProvider.watch(this.fileSystemUserDataHome, { recursive: false, excludes: [] }));
this._register(this.fileSystemProvider.onDidChangeFile(e => this.handleFileChanges(e)));
}
watch(resource: URI, opts: IWatchOptions): IDisposable {
return this.fileSystemProvider.watch(this.toFileSystemResource(resource), opts);
}
stat(resource: URI): Promise<IStat> {
return this.fileSystemProvider.stat(this.toFileSystemResource(resource));
}
mkdir(resource: URI): Promise<void> {
return this.fileSystemProvider.mkdir(this.toFileSystemResource(resource));
}
rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> {
return this.fileSystemProvider.rename(this.toFileSystemResource(from), this.toFileSystemResource(to), opts);
}
readFile(resource: URI): Promise<Uint8Array> {
if (hasReadWriteCapability(this.fileSystemProvider)) {
return this.fileSystemProvider.readFile(this.toFileSystemResource(resource));
}
throw new Error('not supported');
}
readdir(resource: URI): Promise<[string, FileType][]> {
return this.fileSystemProvider.readdir(this.toFileSystemResource(resource));
}
writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
if (hasReadWriteCapability(this.fileSystemProvider)) {
return this.fileSystemProvider.writeFile(this.toFileSystemResource(resource), content, opts);
}
throw new Error('not supported');
}
open(resource: URI, opts: FileOpenOptions): Promise<number> {
if (hasOpenReadWriteCloseCapability(this.fileSystemProvider)) {
return this.fileSystemProvider.open(this.toFileSystemResource(resource), opts);
}
throw new Error('not supported');
}
close(fd: number): Promise<void> {
if (hasOpenReadWriteCloseCapability(this.fileSystemProvider)) {
return this.fileSystemProvider.close(fd);
}
throw new Error('not supported');
}
read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
if (hasOpenReadWriteCloseCapability(this.fileSystemProvider)) {
return this.fileSystemProvider.read(fd, pos, data, offset, length);
}
throw new Error('not supported');
}
write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
if (hasOpenReadWriteCloseCapability(this.fileSystemProvider)) {
return this.fileSystemProvider.write(fd, pos, data, offset, length);
}
throw new Error('not supported');
}
delete(resource: URI, opts: FileDeleteOptions): Promise<void> {
return this.fileSystemProvider.delete(this.toFileSystemResource(resource), opts);
}
private handleFileChanges(changes: IFileChange[]): void {
const userDataChanges: IFileChange[] = [];
for (const change of changes) {
const userDataResource = this.toUserDataResource(change.resource);
if (userDataResource) {
userDataChanges.push({
resource: userDataResource,
type: change.type
});
}
}
if (userDataChanges.length) {
this._onDidChangeFile.fire(userDataChanges);
}
}
private toFileSystemResource(userDataResource: URI): URI {
const relativePath = resources.relativePath(this.userDataHome, userDataResource)!;
if (startsWith(relativePath, BACKUPS)) {
return resources.joinPath(resources.dirname(this.fileSystemBackupsHome), relativePath);
}
return resources.joinPath(this.fileSystemUserDataHome, relativePath);
}
private toUserDataResource(fileSystemResource: URI): URI | null {
if (resources.isEqualOrParent(fileSystemResource, this.fileSystemUserDataHome)) {
const relativePath = resources.relativePath(this.fileSystemUserDataHome, fileSystemResource);
return relativePath ? resources.joinPath(this.userDataHome, relativePath) : this.userDataHome;
}
if (resources.isEqualOrParent(fileSystemResource, this.fileSystemBackupsHome)) {
const relativePath = resources.relativePath(this.fileSystemBackupsHome, fileSystemResource);
return relativePath ? resources.joinPath(this.userDataHome, BACKUPS, relativePath) : resources.joinPath(this.userDataHome, BACKUPS);
}
return null;
}
}

View File

@@ -0,0 +1,232 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import * as resources from 'vs/base/common/resources';
import { FileChangeType, IFileSystemProvider, FileType, IWatchOptions, IStat, FileSystemProviderErrorCode, FileSystemProviderError, FileWriteOptions, IFileChange, FileDeleteOptions, FileSystemProviderCapabilities, FileOverwriteOptions } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
class File implements IStat {
type: FileType;
ctime: number;
mtime: number;
size: number;
name: string;
data?: Uint8Array;
constructor(name: string) {
this.type = FileType.File;
this.ctime = Date.now();
this.mtime = Date.now();
this.size = 0;
this.name = name;
}
}
class Directory implements IStat {
type: FileType;
ctime: number;
mtime: number;
size: number;
name: string;
entries: Map<string, File | Directory>;
constructor(name: string) {
this.type = FileType.Directory;
this.ctime = Date.now();
this.mtime = Date.now();
this.size = 0;
this.name = name;
this.entries = new Map();
}
}
export type Entry = File | Directory;
export class InMemoryUserDataProvider extends Disposable implements IFileSystemProvider {
readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite;
readonly onDidChangeCapabilities: Event<void> = Event.None;
root = new Directory('');
// --- manage file metadata
async stat(resource: URI): Promise<IStat> {
return this._lookup(resource, false);
}
async readdir(resource: URI): Promise<[string, FileType][]> {
const entry = this._lookupAsDirectory(resource, false);
let result: [string, FileType][] = [];
for (const [name, child] of entry.entries) {
result.push([name, child.type]);
}
return result;
}
// --- manage file contents
async readFile(resource: URI): Promise<Uint8Array> {
const data = this._lookupAsFile(resource, false).data;
if (data) {
return data;
}
throw new FileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
}
async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
let basename = resources.basename(resource);
let parent = this._lookupParentDirectory(resource);
let entry = parent.entries.get(basename);
if (entry instanceof Directory) {
throw new FileSystemProviderError('file is directory', FileSystemProviderErrorCode.FileIsADirectory);
}
if (!entry && !opts.create) {
throw new FileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
}
if (entry && opts.create && !opts.overwrite) {
throw new FileSystemProviderError('file exists already', FileSystemProviderErrorCode.FileExists);
}
if (!entry) {
entry = new File(basename);
parent.entries.set(basename, entry);
this._fireSoon({ type: FileChangeType.ADDED, resource });
}
entry.mtime = Date.now();
entry.size = content.byteLength;
entry.data = content;
this._fireSoon({ type: FileChangeType.UPDATED, resource });
}
// --- manage files/folders
async rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> {
if (!opts.overwrite && this._lookup(to, true)) {
throw new FileSystemProviderError('file exists already', FileSystemProviderErrorCode.FileExists);
}
let entry = this._lookup(from, false);
let oldParent = this._lookupParentDirectory(from);
let newParent = this._lookupParentDirectory(to);
let newName = resources.basename(to);
oldParent.entries.delete(entry.name);
entry.name = newName;
newParent.entries.set(newName, entry);
this._fireSoon(
{ type: FileChangeType.DELETED, resource: from },
{ type: FileChangeType.ADDED, resource: to }
);
}
async delete(resource: URI, opts: FileDeleteOptions): Promise<void> {
let dirname = resources.dirname(resource);
let basename = resources.basename(resource);
let parent = this._lookupAsDirectory(dirname, false);
if (!parent.entries.has(basename)) {
throw new FileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
}
parent.entries.delete(basename);
parent.mtime = Date.now();
parent.size -= 1;
this._fireSoon({ type: FileChangeType.UPDATED, resource: dirname }, { resource, type: FileChangeType.DELETED });
}
async mkdir(resource: URI): Promise<void> {
let basename = resources.basename(resource);
let dirname = resources.dirname(resource);
let parent = this._lookupAsDirectory(dirname, false);
let entry = new Directory(basename);
parent.entries.set(entry.name, entry);
parent.mtime = Date.now();
parent.size += 1;
this._fireSoon({ type: FileChangeType.UPDATED, resource: dirname }, { type: FileChangeType.ADDED, resource });
}
// --- lookup
private _lookup(uri: URI, silent: false): Entry;
private _lookup(uri: URI, silent: boolean): Entry | undefined;
private _lookup(uri: URI, silent: boolean): Entry | undefined {
let parts = uri.path.split('/');
let entry: Entry = this.root;
for (const part of parts) {
if (!part) {
continue;
}
let child: Entry | undefined;
if (entry instanceof Directory) {
child = entry.entries.get(part);
}
if (!child) {
if (!silent) {
throw new FileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
} else {
return undefined;
}
}
entry = child;
}
return entry;
}
private _lookupAsDirectory(uri: URI, silent: boolean): Directory {
let entry = this._lookup(uri, silent);
if (entry instanceof Directory) {
return entry;
}
throw new FileSystemProviderError('file not a directory', FileSystemProviderErrorCode.FileNotADirectory);
}
private _lookupAsFile(uri: URI, silent: boolean): File {
let entry = this._lookup(uri, silent);
if (entry instanceof File) {
return entry;
}
throw new FileSystemProviderError('file is a directory', FileSystemProviderErrorCode.FileIsADirectory);
}
private _lookupParentDirectory(uri: URI): Directory {
const dirname = resources.dirname(uri);
return this._lookupAsDirectory(dirname, false);
}
// --- manage file events
private readonly _onDidChangeFile: Emitter<IFileChange[]> = this._register(new Emitter<IFileChange[]>());
readonly onDidChangeFile: Event<IFileChange[]> = this._onDidChangeFile.event;
private _bufferedChanges: IFileChange[] = [];
private _fireSoonHandle?: NodeJS.Timer;
watch(resource: URI, opts: IWatchOptions): IDisposable {
// ignore, fires for all changes...
return Disposable.None;
}
private _fireSoon(...changes: IFileChange[]): void {
this._bufferedChanges.push(...changes);
if (this._fireSoonHandle) {
clearTimeout(this._fireSoonHandle);
}
this._fireSoonHandle = setTimeout(() => {
this._onDidChangeFile.fire(this._bufferedChanges);
this._bufferedChanges.length = 0;
}, 5);
}
}