@ -8,6 +8,7 @@ import type { SSHPseudoTerminal } from './pseudoTerminal';
import type { SSHFileSystem } from './sshFileSystem' ;
import type { SSHFileSystem } from './sshFileSystem' ;
import { catchingPromise , toPromise } from './toPromise' ;
import { catchingPromise , toPromise } from './toPromise' ;
import { Navigation } from './webviewMessages' ;
import { Navigation } from './webviewMessages' ;
import * as path from 'path' ;
export enum ConfigStatus {
export enum ConfigStatus {
Idle = 'Idle' ,
Idle = 'Idle' ,
@ -57,6 +58,12 @@ interface Connection {
pendingUserCount : number ;
pendingUserCount : number ;
}
}
interface SSHShellTaskOptions {
host : string ;
command : string ;
workingDirectory? : string ;
}
export class Manager implements vscode . TreeDataProvider < string | FileSystemConfig > , vscode . TaskProvider {
export class Manager implements vscode . TreeDataProvider < string | FileSystemConfig > , vscode . TaskProvider {
public onDidChangeTreeData : vscode.Event < string | null > ;
public onDidChangeTreeData : vscode.Event < string | null > ;
protected connections : Connection [ ] = [ ] ;
protected connections : Connection [ ] = [ ] ;
@ -233,20 +240,21 @@ export class Manager implements vscode.TreeDataProvider<string | FileSystemConfi
} ) ;
} ) ;
return this . creatingFileSystems [ name ] = promise ;
return this . creatingFileSystems [ name ] = promise ;
}
}
public getRemotePath ( config : FileSystemConfig , relativePath : string ) {
if ( relativePath . startsWith ( '/' ) ) relativePath = relativePath . substr ( 1 ) ;
if ( ! config . root ) return '/' + relativePath ;
const result = path . posix . join ( config . root , relativePath ) ;
if ( result . startsWith ( '~' ) ) return result ; // Home directory, leave the ~/
if ( result . startsWith ( '/' ) ) return result ; // Already starts with /
return '/' + result ; // Add the / to make sure it isn't seen as a relative path
}
public async createTerminal ( name : string , config? : FileSystemConfig , uri? : vscode.Uri ) : Promise < void > {
public async createTerminal ( name : string , config? : FileSystemConfig , uri? : vscode.Uri ) : Promise < void > {
const { createTerminal } = await import ( './pseudoTerminal' ) ;
const { createTerminal } = await import ( './pseudoTerminal' ) ;
// Create connection (early so we have .actualConfig.root)
// Create connection (early so we have .actualConfig.root)
const con = await this . createConnection ( name , config ) ;
const con = await this . createConnection ( name , config ) ;
// Calculate working directory if applicable
// Calculate working directory if applicable
let workingDirectory : string | undefined = uri && uri . path ;
let workingDirectory : string | undefined = uri && uri . path ;
if ( workingDirectory ) {
if ( workingDirectory ) workingDirectory = this . getRemotePath ( con . actualConfig , 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`
}
// Create pseudo terminal
// Create pseudo terminal
con . pendingUserCount ++ ;
con . pendingUserCount ++ ;
const pty = await createTerminal ( { client : con.client , config : con.actualConfig , workingDirectory } ) ;
const pty = await createTerminal ( { client : con.client , config : con.actualConfig , workingDirectory } ) ;
@ -299,11 +307,13 @@ export class Manager implements vscode.TreeDataProvider<string | FileSystemConfi
return [ ] ;
return [ ] ;
}
}
public async resolveTask ( task : vscode.Task , token? : vscode.CancellationToken | undefined ) : Promise < vscode.Task > {
public async resolveTask ( task : vscode.Task , token? : vscode.CancellationToken | undefined ) : Promise < vscode.Task > {
cons t { host , command } = task . definition as { host? : string , command? : string } ;
le t { host , command , workingDirectory } = task . definition as unknown as SSHShellTaskOptions ;
if ( ! host ) throw new Error ( 'Missing field \'host\' for ssh-shell task' ) ;
if ( ! host ) throw new Error ( 'Missing field \'host\' for ssh-shell task' ) ;
if ( ! command ) throw new Error ( 'Missing field \'command\' for ssh-shell task' ) ;
if ( ! command ) throw new Error ( 'Missing field \'command\' for ssh-shell task' ) ;
const config = getConfig ( host ) ;
const config = getConfig ( host ) ;
if ( ! config ) throw new Error ( ` No configuration with the name ' ${ host } ' found for ssh-shell task ` ) ;
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 (
return new vscode . Task (
task . definition ,
task . definition ,
vscode . TaskScope . Workspace ,
vscode . TaskScope . Workspace ,
@ -314,7 +324,7 @@ export class Manager implements vscode.TreeDataProvider<string | FileSystemConfi
connection . pendingUserCount ++ ;
connection . pendingUserCount ++ ;
const { createTerminal } = await import ( './pseudoTerminal' ) ;
const { createTerminal } = await import ( './pseudoTerminal' ) ;
const psy = await createTerminal ( {
const psy = await createTerminal ( {
command ,
command , workingDirectory ,
client : connection.client ,
client : connection.client ,
config : connection.actualConfig ,
config : connection.actualConfig ,
} ) ;
} ) ;