From 03fd60d36e04c365adfc3ebf7c99d18509e9e7ef Mon Sep 17 00:00:00 2001 From: Kelvin Schoofs Date: Fri, 14 Aug 2020 20:37:03 +0200 Subject: [PATCH] Make createTerminal use exec (with $SHELL for shell) Task type ssh-shell now supports workingDirectory option Uses config.root as default (fixes #210) createTerminal no longer needs active FS for relative workingDirectory --- package.json | 4 ++++ src/manager.ts | 30 ++++++++++++++++++++---------- src/pseudoTerminal.ts | 13 ++++++------- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 487e0e4..c4b88ee 100644 --- a/package.json +++ b/package.json @@ -217,6 +217,10 @@ "command": { "type": "string", "description": "The command to run on the server" + }, + "workingDirectory": { + "type": "string", + "description": "The working directory (relative to the config-defined root) to run the command in" } }, "required": [ diff --git a/src/manager.ts b/src/manager.ts index 7692292..210594f 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -8,6 +8,7 @@ import type { SSHPseudoTerminal } from './pseudoTerminal'; import type { SSHFileSystem } from './sshFileSystem'; import { catchingPromise, toPromise } from './toPromise'; import { Navigation } from './webviewMessages'; +import * as path from 'path'; export enum ConfigStatus { Idle = 'Idle', @@ -57,6 +58,12 @@ interface Connection { pendingUserCount: number; } +interface SSHShellTaskOptions { + host: string; + command: string; + workingDirectory?: string; +} + export class Manager implements vscode.TreeDataProvider, vscode.TaskProvider { public onDidChangeTreeData: vscode.Event; protected connections: Connection[] = []; @@ -233,20 +240,21 @@ export class Manager implements vscode.TreeDataProvider { const { createTerminal } = await import('./pseudoTerminal'); // Create connection (early so we have .actualConfig.root) const con = await this.createConnection(name, config); // Calculate working directory if applicable let workingDirectory: string | undefined = uri && uri.path; - if (workingDirectory) { - // Normally there should be a fs, as (currently) workingDirectory is only provided - // when the user uses "Open remote SSH terminal" on a directory in the explorer view - const fs = this.fileSystems.find(fs => fs.config.name === name); - workingDirectory = fs ? fs.relative(workingDirectory) : (con.actualConfig.root || '/'); - // If we don't have an FS (e.g. SSH View > Open Terminal without an active FS), we - // just use the declared `root` field, which _hopefully_ works out nicely with `cd` - } + if (workingDirectory) workingDirectory = this.getRemotePath(con.actualConfig, workingDirectory); // Create pseudo terminal con.pendingUserCount++; const pty = await createTerminal({ client: con.client, config: con.actualConfig, workingDirectory }); @@ -299,11 +307,13 @@ export class Manager implements vscode.TreeDataProvider { - const { host, command } = task.definition as { host?: string, command?: string }; + let { host, command, workingDirectory } = task.definition as unknown as SSHShellTaskOptions; if (!host) throw new Error('Missing field \'host\' for ssh-shell task'); if (!command) throw new Error('Missing field \'command\' for ssh-shell task'); const config = getConfig(host); if (!config) throw new Error(`No configuration with the name '${host}' found for ssh-shell task`); + // Calculate working directory if applicable + if (workingDirectory) workingDirectory = this.getRemotePath(config, workingDirectory); return new vscode.Task( task.definition, vscode.TaskScope.Workspace, @@ -314,7 +324,7 @@ export class Manager implements vscode.TreeDataProvider(cb => command ? - client.exec(setupCommand ? `${setupCommand}; ${command}` : command, { pty: pseudoTtyOptions }, cb) : - client.shell(pseudoTtyOptions, cb)); + const channel = await toPromise(cb => client.exec(commands.join(';'), { pty: pseudoTtyOptions }, cb)); if (!channel) throw new Error('Could not create remote terminal'); - if (!command && setupCommand) channel.write(setupCommand + '\n'); pseudo.channel = channel; channel.on('exit', onDidClose.fire); channel.on('close', () => onDidClose.fire(0));