diff --git a/src/fileSystemConfig.ts b/src/fileSystemConfig.ts index 17d3e97..1c911d2 100644 --- a/src/fileSystemConfig.ts +++ b/src/fileSystemConfig.ts @@ -70,6 +70,10 @@ export interface FileSystemConfig extends ConnectConfig { _location?: ConfigLocation; /* Internal property keeping track of where this config comes from (including merges) */ _locations: ConfigLocation[]; + /* Debug port to attach */ + debugPort?: number; + /* Task to run before debug session starts. SSH command: e.g. `/opt/vistec/python/bin/python -m ptvsd --host ${config.host} --port ${config.debugPort} --wait ${file}` */ + debugPreLaunch?: string; } export function invalidConfigName(name: string) { diff --git a/src/manager.ts b/src/manager.ts index 94b3eb4..ac23e2f 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -146,6 +146,89 @@ export class Manager implements vscode.FileSystemProvider, vscode.TreeDataProvid } root = root.replace(/^~/, home.replace(/\/$/, '')); } + + vscode.debug.registerDebugConfigurationProvider("*", { + resolveDebugConfiguration(folder: vscode.WorkspaceFolder, debugConfig: vscode.DebugConfiguration) { + + if ((folder.uri.scheme === 'ssh') && config) { + debugConfig['request'] = 'attach'; + debugConfig['pathMappings'] = []; + debugConfig['pathMappings'].push({ 'localRoot': 'ssh://' + config.name, 'remoteRoot': config.root }); + + if (config.debugPort) { + if (debugConfig['port'] && (debugConfig['port'] !== config.port)) + Logging.warning(`Invalid port in 'launch.json' working on '${config.name}'`); + debugConfig['port'] = config.debugPort; + + if (debugConfig['host'] && (debugConfig['host'] !== config.host)) + Logging.warning(`Invalid host in 'launch.json' working on '${config.name}'`); + debugConfig['host'] = config.host; + } + + if (debugConfig['port'] === undefined) + Logging.error(`Missing property "debugPort" for remote debugging on '${config.name}'`); + + let workspaceFolder = root; + let file = "${file}"; // default to get error message to open an editor + if (vscode.window.activeTextEditor) + file = root + vscode.window.activeTextEditor.document.uri.path; // '${file}' can not be resolved per default + if (debugConfig['program']) + debugConfig['program'] = eval('`' + debugConfig['program'] + '`'); // resolve ${file}, ${workspaceFolder} + else + debugConfig['program'] = file; + } + return debugConfig; + } + }); + + + vscode.debug.registerDebugAdapterTrackerFactory('*', { + createDebugAdapterTracker(session: vscode.DebugSession) { + if (config && config.debugPreLaunch) { + return { + onWillStartSession: () => { + if (config && config.debugPreLaunch) { + const file = session.configuration['program']; + const command = eval('`' + config.debugPreLaunch + '`'); // resolve ${file} and config. + + new Promise((resolve, reject) => { + Logging.info(`Start preLaunch Task for remote debugging: ${command}`); + client.exec(command, (err, stream) => { + if (err) return reject(err); + stream.stderr.on('data', (d) => { + Logging.error(`Stderr from remote debugging: ${d}`); + }) + stream.on('data', (d) => { + Logging.debug(`Stdout from remote debugging: ${d}`); + }) + stream.on('error', reject); + stream.on('close', (exitCode, signal) => { + if (exitCode || signal) { + return reject(new Error("error listing directory")); + } + resolve(); + }); + }); + }); + }}}; + }} + }); + + vscode.debug.registerDebugAdapterTrackerFactory('*', { + createDebugAdapterTracker(session: vscode.DebugSession) { + if (session.configuration['dapTrace']) { + return { + onWillStartSession: () => console.log(`start: ${session.id}`), + onWillReceiveMessage: m => console.log(`===> ${JSON.stringify(m, undefined, 2)}`), + onDidSendMessage: m => console.log(`<=== ${JSON.stringify(m, undefined, 2)}`), + onWillStopSession: () => console.log(`stop: ${session.id}`), + onError: err => console.log(`error: ${err}`), + onExit: (code, signal) => console.log(`exit: ${code}`) + }; + } + } + }); + const sftp = await getSFTP(client, config); const fs = new SSHFileSystem(name, sftp, root, config!); Logging.info(`Created SSHFileSystem for ${name}, reading root directory...`); diff --git a/webview/src/ConfigEditor/fields.tsx b/webview/src/ConfigEditor/fields.tsx index 268dec9..2e4544d 100644 --- a/webview/src/ConfigEditor/fields.tsx +++ b/webview/src/ConfigEditor/fields.tsx @@ -100,5 +100,18 @@ export function passphrase(config: FileSystemConfig, onChange: FSCChanged<'passp return } +export function debugPort(config: FileSystemConfig, onChange: FSCChanged<'debugPort'>): React.ReactElement { + const callback = (value?: number) => onChange('debugPort', value); + const description = 'Debug port to attach'; + return +} + +export function debugPreLaunch(config: FileSystemConfig, onChange: FSCChanged<'debugPreLaunch'>): React.ReactElement { + const callback = (value?: string) => onChange('debugPreLaunch', value); + const description = 'Task to run before debug session starts. SSH command: e.g. `/opt/vistec/python/bin/python -m ptvsd --host ${config.host} --port ${config.debugPort} --wait ${file}`'; + return +} + + type FieldFactory = (config: FileSystemConfig, onChange: FSCChanged) => React.ReactElement; -export const FIELDS: FieldFactory[] = [name, merge, label, putty, host, port, root, agent, username, password, privateKeyPath, passphrase]; +export const FIELDS: FieldFactory[] = [name, merge, label, putty, host, port, root, agent, username, password, privateKeyPath, passphrase, debugPort, debugPreLaunch]; diff --git a/webview/src/types/fileSystemConfig.ts b/webview/src/types/fileSystemConfig.ts index 8e23789..b59042c 100644 --- a/webview/src/types/fileSystemConfig.ts +++ b/webview/src/types/fileSystemConfig.ts @@ -43,7 +43,7 @@ export function groupByLocation(configs: FileSystemConfig[]): Array<[ConfigLocat } export interface FileSystemConfig extends ConnectConfig { - /* Name of the config. Can only exists of lowercase alphanumeric characters, slashes and any of these: _.+-@ */ + /* Name of the config. Can only exists of lowercase alphanumeric characters, slashes and any of these: _.+-@ */ name: string; /* Optional label to display in some UI places (e.g. popups) */ label?: string; @@ -69,6 +69,10 @@ export interface FileSystemConfig extends ConnectConfig { _location?: ConfigLocation; /* Internal property keeping track of where this config comes from (including merges) */ _locations: ConfigLocation[]; + /* Debug port to attach */ + debugPort?: number; + /* Task to run before debug session starts. SSH command: e.g. `/opt/vistec/python/bin/python -m ptvsd --host ${config.host} --port ${config.debugPort} --wait ${file}` */ + debugPreLaunch?: string; } export function invalidConfigName(name: string) {