Implement file index provider

This powers VSCode's built-in fuzzy file finder.
pull/98/head
Nikhil Benesch 6 years ago
parent 35f58888fa
commit 1ca3f1d73e
No known key found for this signature in database
GPG Key ID: F7386C5DEADABA7F

@ -430,6 +430,7 @@
"vscode": "^1.1.21"
},
"dependencies": {
"binary-split": "^1.0.5",
"jsonc-parser": "^2.0.0",
"socks": "^2.2.0",
"ssh2": "^0.6.0",

@ -31,7 +31,7 @@ export async function calculateActualConfig(config: FileSystemConfig): Promise<F
config.host = replaceVariables(config.host);
const port = replaceVariables((config.port || '') + '');
if (port) config.port = Number(port);
config.agent = replaceVariables(config.agent);
config.agent = replaceVariables(config.agent) || process.env.SSH_AUTH_SOCK;
config.privateKeyPath = replaceVariables(config.privateKeyPath);
if (config.putty) {
let nameOnly = true;

@ -27,6 +27,7 @@ export function activate(context: vscode.ExtensionContext) {
subscribe(vscode.commands.registerCommand(command, callback, thisArg));
subscribe(vscode.workspace.registerFileSystemProvider('ssh', manager, { isCaseSensitive: true }));
subscribe(vscode.workspace.registerFileIndexProvider('ssh', manager));
async function pickAndClick(func: (name: string) => void, name?: string, activeOrNot?: boolean) {
name = name || await pickConfig(manager, activeOrNot);

@ -117,7 +117,7 @@ async function tryGetHome(ssh: Client): Promise<string | null> {
return mat[1];
}
export class Manager implements vscode.FileSystemProvider, vscode.TreeDataProvider<string> {
export class Manager implements vscode.FileSystemProvider, vscode.FileIndexProvider, vscode.TreeDataProvider<string> {
public onDidChangeTreeData: vscode.Event<string>;
public onDidChangeFile: vscode.Event<vscode.FileChangeEvent[]>;
protected fileSystems: SSHFileSystem[] = [];
@ -194,7 +194,7 @@ export class Manager implements vscode.FileSystemProvider, vscode.TreeDataProvid
root = root.replace(/^~/, home.replace(/\/$/, ''));
}
const sftp = await getSFTP(client, config);
const fs = new SSHFileSystem(name, sftp, root, config!);
const fs = new SSHFileSystem(name, client, sftp, root, config!);
try {
const rootUri = vscode.Uri.parse(`ssh://${name}/`);
const stat = await fs.stat(rootUri);
@ -289,6 +289,9 @@ export class Manager implements vscode.FileSystemProvider, vscode.TreeDataProvid
if (fs !== (await assertFs(this, newUri))) throw new Error(`Can't copy between different SSH filesystems`);
return fs.rename(oldUri, newUri, options);
}
public async provideFileIndex(options: vscode.FileSearchOptions, token: vscode.CancellationToken): Promise<vscode.Uri[]> {
return (await assertFs(this, options.folder)).provideFileIndex(options, token);
}
/* TreeDataProvider */
public getTreeItem(element: string): vscode.TreeItem | Thenable<vscode.TreeItem> {
return createTreeItem(this, element);

@ -3,9 +3,10 @@ import * as path from 'path';
import * as ssh2 from 'ssh2';
import * as ssh2s from 'ssh2-streams';
import * as vscode from 'vscode';
import * as split from 'binary-split';
import { FileSystemConfig } from './manager';
export class SSHFileSystem implements vscode.FileSystemProvider {
export class SSHFileSystem implements vscode.FileSystemProvider, vscode.FileIndexProvider {
public waitForContinue = false;
public closed = false;
public closing = false;
@ -13,15 +14,19 @@ export class SSHFileSystem implements vscode.FileSystemProvider {
public onDidChangeFile: vscode.Event<vscode.FileChangeEvent[]>;
protected onDidChangeFileEmitter = new vscode.EventEmitter<vscode.FileChangeEvent[]>();
constructor(public readonly authority: string, protected sftp: ssh2.SFTPWrapper,
public readonly root: string, public readonly config: FileSystemConfig) {
constructor(
public readonly authority: string,
protected ssh: ssh2.Client,
protected sftp: ssh2.SFTPWrapper,
public readonly root: string,
public readonly config: FileSystemConfig) {
this.onDidChangeFile = this.onDidChangeFileEmitter.event;
this.sftp.on('end', () => this.closed = true);
this.ssh.on('end', () => this.closed = true);
}
public disconnect() {
this.closing = true;
this.sftp.end();
this.ssh.end();
}
public relative(relPath: string) {
@ -138,6 +143,39 @@ export class SSHFileSystem implements vscode.FileSystemProvider {
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));
}
public async provideFileIndex(options: vscode.FileSearchOptions, token: vscode.CancellationToken): Promise<vscode.Uri[]> {
const name = this.config.label || this.config.name;
const command = `find ${this.relative(options.folder.path)} -print0`;
return new Promise<vscode.Uri[]>((resolve, reject) => {
const stream = this.ssh.exec(command, (err, stream) => {
if (err) return reject(err);
stream.stderr.on('data', (d) => {
console.log("Stderr from directory listing:", d.toString())
})
const uris: vscode.Uri[] = [];
stream.pipe(split("\x00")).on('data', (d) => {
const p = path.posix.relative(this.root, d.toString());
uris.push(vscode.Uri.parse(`ssh://${name}/${p}`))
});
stream.on('error', reject);
stream.on('close', (exitCode, signal) => {
if (exitCode || signal) {
return reject(new Error("error listing directory"));
}
resolve(uris);
});
});
});
}
public async provideTextSearchResults(
query: vscode.TextSearchQuery,
options: vscode.TextSearchOptions,
progress: vscode.Progress<vscode.TextSearchResult>, token: vscode.CancellationToken
): Promise<void> {
}
}
export default SSHFileSystem;

Loading…
Cancel
Save