Files
vscode-extension-samples/tree-view-sample/src/ftpExplorer.ts
2019-05-10 14:43:54 -07:00

190 lines
5.0 KiB
TypeScript

import * as vscode from 'vscode';
import * as Client from 'ftp';
import { basename, dirname, join } from 'path';
interface IEntry {
name: string;
type: string;
}
export interface FtpNode {
resource: vscode.Uri;
isDirectory: boolean;
}
export class FtpModel {
private nodes: Map<string, FtpNode> = new Map<string, FtpNode>();
constructor(readonly host: string, private user: string, private password: string) {
}
public connect(): Thenable<Client> {
return new Promise((c, e) => {
const client = new Client();
client.on('ready', () => {
c(client);
});
client.on('error', error => {
e('Error while connecting: ' + error.message);
});
client.connect({
host: this.host,
username: this.user,
password: this.password
});
});
}
public get roots(): Thenable<FtpNode[]> {
return this.connect().then(client => {
return new Promise((c, e) => {
client.list((err, list) => {
if (err) {
return e(err);
}
client.end();
return c(this.sort(list.map(entry => ({ resource: vscode.Uri.parse(`ftp://${this.host}///${entry.name}`), isDirectory: entry.type === 'd' }))));
});
});
});
}
public getChildren(node: FtpNode): Thenable<FtpNode[]> {
return this.connect().then(client => {
return new Promise((c, e) => {
client.list(node.resource.fsPath, (err, list) => {
if (err) {
return e(err);
}
client.end();
return c(this.sort(list.map(entry => ({ resource: vscode.Uri.parse(`${node.resource.fsPath}/${entry.name}`), isDirectory: entry.type === 'd' }))));
});
});
});
}
private sort(nodes: FtpNode[]): FtpNode[] {
return nodes.sort((n1, n2) => {
if (n1.isDirectory && !n2.isDirectory) {
return -1;
}
if (!n1.isDirectory && n2.isDirectory) {
return 1;
}
return basename(n1.resource.fsPath).localeCompare(basename(n2.resource.fsPath));
});
}
public getContent(resource: vscode.Uri): Thenable<string> {
return this.connect().then(client => {
return new Promise((c, e) => {
client.get(resource.path.substr(2), (err, stream) => {
if (err) {
return e(err);
}
let string = '';
stream.on('data', function (buffer) {
if (buffer) {
var part = buffer.toString();
string += part;
}
});
stream.on('end', function () {
client.end();
c(string);
});
});
});
});
}
}
export class FtpTreeDataProvider implements vscode.TreeDataProvider<FtpNode>, vscode.TextDocumentContentProvider {
private _onDidChangeTreeData: vscode.EventEmitter<any> = new vscode.EventEmitter<any>();
readonly onDidChangeTreeData: vscode.Event<any> = this._onDidChangeTreeData.event;
constructor(private readonly model: FtpModel) { }
public refresh(): any {
this._onDidChangeTreeData.fire();
}
public getTreeItem(element: FtpNode): vscode.TreeItem {
return {
resourceUri: element.resource,
collapsibleState: element.isDirectory ? vscode.TreeItemCollapsibleState.Collapsed : void 0,
command: element.isDirectory ? void 0 : {
command: 'ftpExplorer.openFtpResource',
arguments: [element.resource],
title: 'Open FTP Resource'
}
};
}
public getChildren(element?: FtpNode): FtpNode[] | Thenable<FtpNode[]> {
return element ? this.model.getChildren(element) : this.model.roots;
}
public getParent(element: FtpNode): FtpNode {
const parent = element.resource.with({ path: dirname(element.resource.path) });
return parent.path !== '//' ? { resource: parent, isDirectory: true } : null;
}
public provideTextDocumentContent(uri: vscode.Uri, token: vscode.CancellationToken): vscode.ProviderResult<string> {
return this.model.getContent(uri).then(content => content);
}
}
export class FtpExplorer {
private ftpViewer: vscode.TreeView<FtpNode>;
constructor(context: vscode.ExtensionContext) {
/* Please note that login information is hardcoded only for this example purpose and recommended not to do it in general. */
const ftpModel = new FtpModel('mirror.switch.ch', 'anonymous', 'anonymous@anonymous.de');
const treeDataProvider = new FtpTreeDataProvider(ftpModel);
context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider('ftp', treeDataProvider));
this.ftpViewer = vscode.window.createTreeView('ftpExplorer', { treeDataProvider });
vscode.commands.registerCommand('ftpExplorer.refresh', () => treeDataProvider.refresh());
vscode.commands.registerCommand('ftpExplorer.openFtpResource', resource => this.openResource(resource));
vscode.commands.registerCommand('ftpExplorer.revealResource', () => this.reveal());
}
private openResource(resource: vscode.Uri): void {
vscode.window.showTextDocument(resource);
}
private reveal(): Thenable<void> {
const node = this.getNode();
if (node) {
return this.ftpViewer.reveal(node);
}
return null;
}
private getNode(): FtpNode {
if (vscode.window.activeTextEditor) {
if (vscode.window.activeTextEditor.document.uri.scheme === 'ftp') {
return { resource: vscode.window.activeTextEditor.document.uri, isDirectory: false };
}
}
return null;
}
}