Improve SSHFileSystem API and constructor a bit

feature/search
Kelvin Schoofs 6 years ago
parent c796e430df
commit b63d2488e8

@ -147,7 +147,7 @@ export class Manager implements vscode.FileSystemProvider, vscode.TreeDataProvid
root = root.replace(/^~/, home.replace(/\/$/, '')); root = root.replace(/^~/, home.replace(/\/$/, ''));
} }
const sftp = await getSFTP(client, config); const sftp = await getSFTP(client, config);
const fs = new SSHFileSystem(name, sftp, root, config!); const fs = new SSHFileSystem(name, client, sftp, root, config!);
Logging.info(`Created SSHFileSystem for ${name}, reading root directory...`); Logging.info(`Created SSHFileSystem for ${name}, reading root directory...`);
try { try {
const rootUri = vscode.Uri.parse(`ssh://${name}/`); const rootUri = vscode.Uri.parse(`ssh://${name}/`);
@ -311,5 +311,3 @@ export class Manager implements vscode.FileSystemProvider, vscode.TreeDataProvid
return navigation ? navigate(navigation) : open(); return navigation ? navigate(navigation) : open();
} }
} }
export default Manager;

@ -14,8 +14,11 @@ export class SSHFileSystem implements vscode.FileSystemProvider {
public onDidChangeFile: vscode.Event<vscode.FileChangeEvent[]>; public onDidChangeFile: vscode.Event<vscode.FileChangeEvent[]>;
protected onDidChangeFileEmitter = new vscode.EventEmitter<vscode.FileChangeEvent[]>(); protected onDidChangeFileEmitter = new vscode.EventEmitter<vscode.FileChangeEvent[]>();
constructor(public readonly authority: string, protected sftp: ssh2.SFTPWrapper, constructor(public readonly authority: string,
public readonly root: string, public readonly config: FileSystemConfig) { public readonly client: ssh2.Client,
public readonly sftp: ssh2.SFTPWrapper,
public readonly root: string,
public readonly config: FileSystemConfig) {
this.onDidChangeFile = this.onDidChangeFileEmitter.event; this.onDidChangeFile = this.onDidChangeFileEmitter.event;
this.sftp.on('end', () => this.closed = true); this.sftp.on('end', () => this.closed = true);
} }
@ -25,10 +28,14 @@ export class SSHFileSystem implements vscode.FileSystemProvider {
this.sftp.end(); this.sftp.end();
} }
public relative(relPath: string) { public absoluteFromRelative(relPath: string) {
if (relPath.startsWith('/')) relPath = relPath.substr(1); if (relPath.startsWith('/')) relPath = relPath.substr(1);
return path.posix.resolve(this.root, relPath); return path.posix.resolve(this.root, relPath);
} }
public relativeFromAbsolute(absPath: string) {
if (!absPath.startsWith('/')) throw new Error('Not an absolute path');
return path.posix.relative(this.root, absPath);
}
public continuePromise<T>(func: (cb: (err: Error | null, res?: T) => void) => boolean): Promise<T> { public continuePromise<T>(func: (cb: (err: Error | null, res?: T) => void) => boolean): Promise<T> {
return new Promise<T>((resolve, reject) => { return new Promise<T>((resolve, reject) => {
@ -55,7 +62,7 @@ export class SSHFileSystem implements vscode.FileSystemProvider {
return new vscode.Disposable(() => { }); return new vscode.Disposable(() => { });
} }
public async stat(uri: vscode.Uri): Promise<vscode.FileStat> { public async stat(uri: vscode.Uri): Promise<vscode.FileStat> {
const stat = await this.continuePromise<ssh2s.Stats>(cb => this.sftp.stat(this.relative(uri.path), cb)).catch((e: Error & { code: number }) => { const stat = await this.continuePromise<ssh2s.Stats>(cb => this.sftp.stat(this.absoluteFromRelative(uri.path), cb)).catch((e: Error & { code: number }) => {
throw e.code === 2 ? vscode.FileSystemError.FileNotFound(uri) : e; throw e.code === 2 ? vscode.FileSystemError.FileNotFound(uri) : e;
}); });
const { mtime, size } = stat; const { mtime, size } = stat;
@ -71,7 +78,7 @@ export class SSHFileSystem implements vscode.FileSystemProvider {
}; };
} }
public async readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> { public async readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
const entries = await this.continuePromise<ssh2s.FileEntry[]>(cb => this.sftp.readdir(this.relative(uri.path), cb)).catch((e) => { const entries = await this.continuePromise<ssh2s.FileEntry[]>(cb => this.sftp.readdir(this.absoluteFromRelative(uri.path), cb)).catch((e) => {
throw e === 2 ? vscode.FileSystemError.FileNotFound(uri) : e; throw e === 2 ? vscode.FileSystemError.FileNotFound(uri) : e;
}); });
return Promise.all(entries.map(async (file) => { return Promise.all(entries.map(async (file) => {
@ -91,11 +98,11 @@ export class SSHFileSystem implements vscode.FileSystemProvider {
})); }));
} }
public createDirectory(uri: vscode.Uri): void | Promise<void> { public createDirectory(uri: vscode.Uri): void | Promise<void> {
return this.continuePromise(cb => this.sftp.mkdir(this.relative(uri.path), cb)); return this.continuePromise(cb => this.sftp.mkdir(this.absoluteFromRelative(uri.path), cb));
} }
public readFile(uri: vscode.Uri): Uint8Array | Promise<Uint8Array> { public readFile(uri: vscode.Uri): Uint8Array | Promise<Uint8Array> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const stream = this.sftp.createReadStream(this.relative(uri.path), { autoClose: true }); const stream = this.sftp.createReadStream(this.absoluteFromRelative(uri.path), { autoClose: true });
const bufs = []; const bufs = [];
stream.on('data', bufs.push.bind(bufs)); stream.on('data', bufs.push.bind(bufs));
stream.on('error', reject); stream.on('error', reject);
@ -108,18 +115,18 @@ export class SSHFileSystem implements vscode.FileSystemProvider {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
let mode: number | string | undefined; let mode: number | string | undefined;
try { try {
const stat = await this.continuePromise<ssh2s.Stats>(cb => this.sftp.stat(this.relative(uri.path), cb)); const stat = await this.continuePromise<ssh2s.Stats>(cb => this.sftp.stat(this.absoluteFromRelative(uri.path), cb));
mode = stat.mode; mode = stat.mode;
} catch (e) { } catch (e) {
if (e.message === 'No such file') { if (e.message === 'No such file') {
mode = this.config.newFileMode; mode = this.config.newFileMode;
} else { } else {
Logging.error(e); Logging.error(e);
vscode.window.showWarningMessage(`Couldn't read the permissions for '${this.relative(uri.path)}', permissions might be overwritten`); vscode.window.showWarningMessage(`Couldn't read the permissions for '${this.absoluteFromRelative(uri.path)}', permissions might be overwritten`);
} }
} }
mode = mode as number | undefined; // ssh2-streams supports an octal number as string, but ssh2's typings don't reflect this mode = mode as number | undefined; // ssh2-streams supports an octal number as string, but ssh2's typings don't reflect this
const stream = this.sftp.createWriteStream(this.relative(uri.path), { mode, flags: 'w' }); const stream = this.sftp.createWriteStream(this.absoluteFromRelative(uri.path), { mode, flags: 'w' });
stream.on('error', reject); stream.on('error', reject);
stream.end(content, resolve); stream.end(content, resolve);
}); });
@ -128,15 +135,15 @@ export class SSHFileSystem implements vscode.FileSystemProvider {
const stats = await this.stat(uri); const stats = await this.stat(uri);
// tslint:disable no-bitwise */ // tslint:disable no-bitwise */
if (stats.type & (vscode.FileType.SymbolicLink | vscode.FileType.File)) { if (stats.type & (vscode.FileType.SymbolicLink | vscode.FileType.File)) {
return this.continuePromise(cb => this.sftp.unlink(this.relative(uri.path), cb)); return this.continuePromise(cb => this.sftp.unlink(this.absoluteFromRelative(uri.path), cb));
} else if ((stats.type & vscode.FileType.Directory) && options.recursive) { } else if ((stats.type & vscode.FileType.Directory) && options.recursive) {
return this.continuePromise(cb => this.sftp.rmdir(this.relative(uri.path), cb)); return this.continuePromise(cb => this.sftp.rmdir(this.absoluteFromRelative(uri.path), cb));
} }
return this.continuePromise(cb => this.sftp.unlink(this.relative(uri.path), cb)); return this.continuePromise(cb => this.sftp.unlink(this.absoluteFromRelative(uri.path), cb));
// tslint:enable no-bitwise */ // tslint:enable no-bitwise */
} }
public rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): void | Promise<void> { public rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): void | Promise<void> {
return this.continuePromise(cb => this.sftp.rename(this.relative(oldUri.path), this.relative(newUri.path), cb)); return this.continuePromise(cb => this.sftp.rename(this.absoluteFromRelative(oldUri.path), this.absoluteFromRelative(newUri.path), cb));
} }
} }

Loading…
Cancel
Save