mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-31 01:25:38 -05:00
Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 (#6381)
* Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 * disable strict null check
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user