Make use of improved logger with template literal support

issue/311
Kelvin Schoofs 3 years ago
parent 3e8aa832f5
commit bd25e7322d

@ -40,7 +40,7 @@ export async function renameNameless() {
} }
}); });
if (okay) return; if (okay) return;
return conf.update('configs', v, loc).then(() => { }, res => logging.error(`Error while saving configs (CT=${loc}): ${res}`)); return conf.update('configs', v, loc).then(() => { }, res => logging.error`Error while saving configs (CT=${loc}): ${res}`);
} }
await patch(inspect.globalValue, vscode.ConfigurationTarget.Global); await patch(inspect.globalValue, vscode.ConfigurationTarget.Global);
await patch(inspect.workspaceValue, vscode.ConfigurationTarget.Workspace); await patch(inspect.workspaceValue, vscode.ConfigurationTarget.Workspace);
@ -58,18 +58,18 @@ async function readConfigFile(location: string, shouldExist = false): Promise<Fi
const content = await toPromise<Buffer>(cb => readFile(location, cb)).catch((e: NodeJS.ErrnoException) => e); const content = await toPromise<Buffer>(cb => readFile(location, cb)).catch((e: NodeJS.ErrnoException) => e);
if (content instanceof Error) { if (content instanceof Error) {
if (content.code === 'ENOENT' && !shouldExist) return []; if (content.code === 'ENOENT' && !shouldExist) return [];
logging.error(`Error while reading config file ${location}: ${content.message}`); logging.error`Error while reading config file ${location}: ${content}`;
return []; return [];
} }
const errors: ParseError[] = []; const errors: ParseError[] = [];
const parsed: FileSystemConfig[] | null = parseJsonc(content.toString(), errors); const parsed: FileSystemConfig[] | null = parseJsonc(content.toString(), errors);
if (!parsed || errors.length) { if (!parsed || errors.length) {
logging.error(`Couldn't parse ${location} as a 'JSON with Comments' file`); logging.error`Couldn't parse ${location} as a 'JSON with Comments' file`;
vscode.window.showErrorMessage(`Couldn't parse ${location} as a 'JSON with Comments' file`); vscode.window.showErrorMessage(`Couldn't parse ${location} as a 'JSON with Comments' file`);
return []; return [];
} }
parsed.forEach(c => c._locations = [c._location = location]); parsed.forEach(c => c._locations = [c._location = location]);
logging.debug(`Read ${parsed.length} configs from ${location}`); logging.debug`Read ${parsed.length} configs from ${location}`;
return parsed; return parsed;
} }
@ -132,7 +132,7 @@ export async function loadConfigsRaw(): Promise<FileSystemConfig[]> {
const fConfig = vscode.workspace.getConfiguration('sshfs', uri).inspect<FileSystemConfig[]>('configs'); const fConfig = vscode.workspace.getConfiguration('sshfs', uri).inspect<FileSystemConfig[]>('configs');
const fConfigs = fConfig && fConfig.workspaceFolderValue || []; const fConfigs = fConfig && fConfig.workspaceFolderValue || [];
if (fConfigs.length) { if (fConfigs.length) {
logging.debug(`Read ${fConfigs.length} configs from workspace folder ${uri}`); logging.debug`Read ${fConfigs.length} configs from workspace folder ${uri}`;
fConfigs.forEach(c => c._locations = [c._location = `WorkspaceFolder ${uri}`]); fConfigs.forEach(c => c._locations = [c._location = `WorkspaceFolder ${uri}`]);
} }
layered.folder = [ layered.folder = [
@ -149,7 +149,7 @@ export async function loadConfigsRaw(): Promise<FileSystemConfig[]> {
// Let the user do some cleaning with the raw configs // Let the user do some cleaning with the raw configs
for (const conf of all) { for (const conf of all) {
if (!conf.name) { if (!conf.name) {
logging.error(`Skipped an invalid SSH FS config (missing a name field):\n${JSON.stringify(conf, undefined, 4)}`); logging.error`Skipped an invalid SSH FS config (missing a name field):\n${conf}`;
vscode.window.showErrorMessage(`Skipped an invalid SSH FS config (missing a name field)`); vscode.window.showErrorMessage(`Skipped an invalid SSH FS config (missing a name field)`);
} else if (invalidConfigName(conf.name)) { } else if (invalidConfigName(conf.name)) {
logging.warning(`Found a SSH FS config with the invalid name "${conf.name}", prompting user how to handle`); logging.warning(`Found a SSH FS config with the invalid name "${conf.name}", prompting user how to handle`);
@ -158,14 +158,14 @@ export async function loadConfigsRaw(): Promise<FileSystemConfig[]> {
const name = await vscode.window.showInputBox({ prompt: `New name for: ${conf.name}`, validateInput: invalidConfigName, placeHolder: 'New name' }); const name = await vscode.window.showInputBox({ prompt: `New name for: ${conf.name}`, validateInput: invalidConfigName, placeHolder: 'New name' });
if (name) { if (name) {
const oldName = conf.name; const oldName = conf.name;
logging.info(`Renaming config "${oldName}" to "${name}"`); logging.info`Renaming config "${oldName}" to "${name}"`;
conf.name = name; conf.name = name;
return updateConfig(conf, oldName); return updateConfig(conf, oldName);
} }
} else if (answer === 'Delete') { } else if (answer === 'Delete') {
return deleteConfig(conf); return deleteConfig(conf);
} }
logging.warning(`Skipped SSH FS config '${conf.name}'`); logging.warning`Skipped SSH FS config '${conf.name}'`;
vscode.window.showWarningMessage(`Skipped SSH FS config '${conf.name}'`); vscode.window.showWarningMessage(`Skipped SSH FS config '${conf.name}'`);
}); });
} }
@ -186,19 +186,19 @@ export async function loadConfigs(): Promise<FileSystemConfig[]> {
// The folder settings should overwrite the higher up defined settings // The folder settings should overwrite the higher up defined settings
// Since .sshfs.json gets read after vscode settings, these can overwrite configs // Since .sshfs.json gets read after vscode settings, these can overwrite configs
// of the same level, which I guess is a nice feature? // of the same level, which I guess is a nice feature?
logging.debug(`\tMerging duplicate ${conf.name} from ${conf._locations}`); logging.debug`\tMerging duplicate ${conf.name} from ${conf._locations}`;
dup._locations = [...dup._locations, ...conf._locations]; dup._locations = [...dup._locations, ...conf._locations];
Object.assign(dup, Object.assign(conf, dup)); Object.assign(dup, Object.assign(conf, dup));
} else { } else {
logging.debug(`\tIgnoring duplicate ${conf.name} from ${conf._locations}`); logging.debug`\tIgnoring duplicate ${conf.name} from ${conf._locations}`;
} }
} else { } else {
logging.debug(`\tAdded configuration ${conf.name} from ${conf._locations}`); logging.debug`\tAdded configuration ${conf.name} from ${conf._locations}`;
configs.push(conf); configs.push(conf);
} }
} }
loadedConfigs = configs; loadedConfigs = configs;
logging.info(`Found ${loadedConfigs.length} configurations`); logging.info`Found ${loadedConfigs.length} configurations`;
UPDATE_LISTENERS.forEach(listener => listener(loadedConfigs)); UPDATE_LISTENERS.forEach(listener => listener(loadedConfigs));
return loadedConfigs; return loadedConfigs;
} }
@ -223,7 +223,7 @@ export async function alterConfigs(location: ConfigLocation, alterer: ConfigAlte
return newConfig; return newConfig;
}); });
await conf.update('configs', modified, location); await conf.update('configs', modified, location);
logging.debug(`\tUpdated configs in ${[, 'Global', 'Workspace', 'WorkspaceFolder'][location]} settings.json`); logging.debug`\tUpdated configs in ${[, 'Global', 'Workspace', 'WorkspaceFolder'][location]} settings.json`;
return; return;
} }
if (typeof location !== 'string') throw new Error(`Invalid _location field: ${location}`); if (typeof location !== 'string') throw new Error(`Invalid _location field: ${location}`);
@ -240,10 +240,10 @@ export async function alterConfigs(location: ConfigLocation, alterer: ConfigAlte
const data = JSON.stringify(altered, null, 4); const data = JSON.stringify(altered, null, 4);
await toPromise(cb => writeFile(location, data, cb)) await toPromise(cb => writeFile(location, data, cb))
.catch((e: NodeJS.ErrnoException) => { .catch((e: NodeJS.ErrnoException) => {
logging.error(`Error while writing configs to ${location}: ${e.message}`); logging.error`Error while writing configs to ${location}: ${e}`;
throw e; throw e;
}); });
logging.debug(`\tWritten modified configs to ${location}`); logging.debug`\tWritten modified configs to ${location}`;
await loadConfigs(); await loadConfigs();
} }
@ -251,18 +251,18 @@ export async function updateConfig(config: FileSystemConfig, oldName = config.na
const { name, _location } = config; const { name, _location } = config;
if (!name) throw new Error(`The given config has no name field`); if (!name) throw new Error(`The given config has no name field`);
if (!_location) throw new Error(`The given config has no _location field`); if (!_location) throw new Error(`The given config has no _location field`);
logging.info(`Saving config ${name} to ${_location}`); logging.info`Saving config ${name} to ${_location}`;
if (oldName !== config.name) { if (oldName !== config.name) {
logging.debug(`\tSaving ${name} will try to overwrite old config ${oldName}`); logging.debug`\tSaving ${name} will try to overwrite old config ${oldName}`;
} }
await alterConfigs(_location, (configs) => { await alterConfigs(_location, (configs) => {
logging.debug(`\tConfig location '${_location}' has following configs: ${configs.map(c => c.name).join(', ')}`); logging.debug`\tConfig location '${_location}' has following configs: ${configs.map(c => c.name).join(', ')}`;
const index = configs.findIndex(c => c.name ? c.name.toLowerCase() === oldName.toLowerCase() : false); const index = configs.findIndex(c => c.name ? c.name.toLowerCase() === oldName.toLowerCase() : false);
if (index === -1) { if (index === -1) {
logging.debug(`\tAdding the new config to the existing configs`); logging.debug`\tAdding the new config to the existing configs`;
configs.push(config); configs.push(config);
} else { } else {
logging.debug(`\tOverwriting config '${configs[index].name}' at index ${index} with the new config`); logging.debug`\tOverwriting config '${configs[index].name}' at index ${index} with the new config`;
configs[index] = config; configs[index] = config;
} }
return configs; return configs;
@ -273,12 +273,12 @@ export async function deleteConfig(config: FileSystemConfig) {
const { name, _location } = config; const { name, _location } = config;
if (!name) throw new Error(`The given config has no name field`); if (!name) throw new Error(`The given config has no name field`);
if (!_location) throw new Error(`The given config has no _location field`); if (!_location) throw new Error(`The given config has no _location field`);
logging.info(`Deleting config ${name} in ${_location}`); logging.info`Deleting config ${name} in ${_location}`;
await alterConfigs(_location, (configs) => { await alterConfigs(_location, (configs) => {
logging.debug(`\tConfig location '${_location}' has following configs: ${configs.map(c => c.name).join(', ')}`); logging.debug`\tConfig location '${_location}' has following configs: ${configs.map(c => c.name).join(', ')}`;
const index = configs.findIndex(c => c.name ? c.name.toLowerCase() === name.toLowerCase() : false); const index = configs.findIndex(c => c.name ? c.name.toLowerCase() === name.toLowerCase() : false);
if (index === -1) throw new Error(`Config '${name}' not found in ${_location}`); if (index === -1) throw new Error(`Config '${name}' not found in ${_location}`);
logging.debug(`\tDeleting config '${configs[index].name}' at index ${index}`); logging.debug`\tDeleting config '${configs[index].name}' at index ${index}`;
configs.splice(index, 1); configs.splice(index, 1);
return configs; return configs;
}); });
@ -299,7 +299,7 @@ export function getConfig(input: string): FileSystemConfig | undefined {
// If we're using the instant connection string, the host name might be a config name // If we're using the instant connection string, the host name might be a config name
const existing = getConfigs().find(c => c.name.toLowerCase() === parsed.host!.toLowerCase()); const existing = getConfigs().find(c => c.name.toLowerCase() === parsed.host!.toLowerCase());
if (existing) { if (existing) {
Logging.info(`getConfig('${input}') led to '${parsed.name}' which matches config '${existing.name}'`); Logging.info`getConfig('${input}') led to '${parsed.name}' which matches config '${existing.name}'`;
// Take the existing config, but (more or less) override it with the values present in `parsed` // Take the existing config, but (more or less) override it with the values present in `parsed`
// `name` be the same as in `parsed`, meaning it can be reused with `getConfig` on window reload. // `name` be the same as in `parsed`, meaning it can be reused with `getConfig` on window reload.
return { return {
@ -404,7 +404,7 @@ function calculateFlags(): Record<string, FlagCombo> {
applyList(config.globalValue, 'Global Settings'); applyList(config.globalValue, 'Global Settings');
applyList(config.workspaceValue, 'Workspace Settings'); applyList(config.workspaceValue, 'Workspace Settings');
applyList(config.workspaceFolderValue, 'WorkspaceFolder Settings'); applyList(config.workspaceFolderValue, 'WorkspaceFolder Settings');
Logging.info(`Calculated config flags: ${JSON.stringify(flags)}`); Logging.info`Calculated config flags: ${flags}`;
return cachedFlags = flags; return cachedFlags = flags;
} }

@ -6,7 +6,7 @@ import { SFTPStream } from 'ssh2-streams';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { getConfig, getFlagBoolean } from './config'; import { getConfig, getFlagBoolean } from './config';
import type { FileSystemConfig } from './fileSystemConfig'; import type { FileSystemConfig } from './fileSystemConfig';
import { censorConfig, Logging } from './logging'; import { Logging } from './logging';
import type { PuttySession } from './putty'; import type { PuttySession } from './putty';
import { toPromise, validatePort } from './utils'; import { toPromise, validatePort } from './utils';
@ -38,7 +38,7 @@ async function promptFields(config: FileSystemConfig, ...fields: (keyof FileSyst
for (const field of fields) { for (const field of fields) {
const prompt = PROMPT_FIELDS[field]; const prompt = PROMPT_FIELDS[field];
if (!prompt) { if (!prompt) {
Logging.error(`Prompting unexpected field '${field}'`); Logging.error`Prompting unexpected field '${field}'`;
continue; continue;
} }
const value = config[field]; const value = config[field];
@ -69,7 +69,7 @@ export async function calculateActualConfig(config: FileSystemConfig): Promise<F
config.port = port ? validatePort(port) : 22; config.port = port ? validatePort(port) : 22;
config.agent = replaceVariables(config.agent); config.agent = replaceVariables(config.agent);
config.privateKeyPath = replaceVariables(config.privateKeyPath); config.privateKeyPath = replaceVariables(config.privateKeyPath);
logging.info(`Calculating actual config`); logging.info`Calculating actual config`;
if (config.instantConnection) { if (config.instantConnection) {
// Created from an instant connection string, so enable PuTTY (in try mode) // Created from an instant connection string, so enable PuTTY (in try mode)
config.putty = '<TRY>'; // Could just set it to `true` but... consistency? config.putty = '<TRY>'; // Could just set it to `true` but... consistency?
@ -86,7 +86,7 @@ export async function calculateActualConfig(config: FileSystemConfig): Promise<F
if (tryPutty) { if (tryPutty) {
// If we're trying to find one, we also check whether `config.host` represents the name of a PuTTY session // If we're trying to find one, we also check whether `config.host` represents the name of a PuTTY session
session = await getSession(config.host); session = await getSession(config.host);
logging.info(`\ttryPutty is true, tried finding a config named '${config.host}' and found ${session ? `'${session.name}'` : 'no match'}`); logging.info`\ttryPutty is true, tried finding a config named '${config.host}' and found ${session ? `'${session.name}'` : 'no match'}`;
} }
if (!session) { if (!session) {
let nameOnly = true; let nameOnly = true;
@ -129,11 +129,11 @@ export async function calculateActualConfig(config: FileSystemConfig): Promise<F
default: default:
throw new Error(`The requested PuTTY session uses an unsupported proxy method`); throw new Error(`The requested PuTTY session uses an unsupported proxy method`);
} }
logging.debug(`\tReading PuTTY configuration lead to the following configuration:\n${JSON.stringify(config, null, 4)}`); logging.debug`\tReading PuTTY configuration lead to the following configuration:\n${JSON.stringify(config, null, 4)}`;
} else if (!tryPutty) { } else if (!tryPutty) {
throw new Error(`Couldn't find the requested PuTTY session`); throw new Error(`Couldn't find the requested PuTTY session`);
} else { } else {
logging.debug(`\tConfig suggested finding a PuTTY configuration, did not find one`); logging.debug`\tConfig suggested finding a PuTTY configuration, did not find one`;
} }
} }
// If the username is (still) `$USER` at this point, use the local user's username // If the username is (still) `$USER` at this point, use the local user's username
@ -142,7 +142,7 @@ export async function calculateActualConfig(config: FileSystemConfig): Promise<F
try { try {
const key = await toPromise<Buffer>(cb => readFile(config.privateKeyPath!, cb)); const key = await toPromise<Buffer>(cb => readFile(config.privateKeyPath!, cb));
config.privateKey = key; config.privateKey = key;
logging.debug(`\tRead private key from ${config.privateKeyPath}`); logging.debug`\tRead private key from ${config.privateKeyPath}`;
} catch (e) { } catch (e) {
throw new Error(`Error while reading the keyfile at:\n${config.privateKeyPath}`); throw new Error(`Error while reading the keyfile at:\n${config.privateKeyPath}`);
} }
@ -166,11 +166,11 @@ export async function calculateActualConfig(config: FileSystemConfig): Promise<F
delete config.passphrase; delete config.passphrase;
} }
if (config.agentForward && !config.agent) { if (config.agentForward && !config.agent) {
logging.debug(`\tNo agent while having agentForward, disabling agent forwarding`); logging.debug`\tNo agent while having agentForward, disabling agent forwarding`;
config.agentForward = false; config.agentForward = false;
} }
if (!config.privateKey && !config.agent && !config.password) { if (!config.privateKey && !config.agent && !config.password) {
logging.debug(`\tNo privateKey, agent or password. Gonna prompt for password`); logging.debug`\tNo privateKey, agent or password. Gonna prompt for password`;
config.password = true as any; config.password = true as any;
await promptFields(config, 'password'); await promptFields(config, 'password');
} }
@ -182,25 +182,25 @@ export async function createSocket(config: FileSystemConfig): Promise<NodeJS.Rea
config = (await calculateActualConfig(config))!; config = (await calculateActualConfig(config))!;
if (!config) return null; if (!config) return null;
const logging = Logging.scope(`createSocket(${config.name})`); const logging = Logging.scope(`createSocket(${config.name})`);
logging.info(`Creating socket`); logging.info`Creating socket`;
if (config.hop) { if (config.hop) {
logging.debug(`\tHopping through ${config.hop}`); logging.debug`\tHopping through ${config.hop}`;
const hop = getConfig(config.hop); const hop = getConfig(config.hop);
if (!hop) throw new Error(`A SSH FS configuration with the name '${config.hop}' doesn't exist`); if (!hop) throw new Error(`A SSH FS configuration with the name '${config.hop}' doesn't exist`);
const ssh = await createSSH(hop); const ssh = await createSSH(hop);
if (!ssh) { if (!ssh) {
logging.debug(`\tFailed in connecting to hop ${config.hop}`); logging.debug`\tFailed in connecting to hop ${config.hop}`;
return null; return null;
} }
return new Promise<NodeJS.ReadableStream>((resolve, reject) => { return new Promise<NodeJS.ReadableStream>((resolve, reject) => {
ssh.forwardOut('localhost', 0, config.host!, config.port || 22, (err, channel) => { ssh.forwardOut('localhost', 0, config.host!, config.port || 22, (err, channel) => {
if (err) { if (err) {
logging.debug(`\tError connecting to hop ${config.hop} for ${config.name}: ${err}`); logging.debug`\tError connecting to hop ${config.hop} for ${config.name}: ${err}`;
err.message = `Couldn't connect through the hop:\n${err.message}`; err.message = `Couldn't connect through the hop:\n${err}`;
return reject(err); return reject(err);
} else if (!channel) { } else if (!channel) {
err = new Error('Did not receive a channel'); err = new Error('Did not receive a channel');
logging.debug(`\tGot no channel when connecting to hop ${config.hop} for ${config.name}`); logging.debug`\tGot no channel when connecting to hop ${config.hop} for ${config.name}`;
return reject(err); return reject(err);
} }
channel.once('close', () => ssh.destroy()); channel.once('close', () => ssh.destroy());
@ -221,7 +221,7 @@ export async function createSocket(config: FileSystemConfig): Promise<NodeJS.Rea
throw new Error(`Unknown proxy method`); throw new Error(`Unknown proxy method`);
} }
return new Promise<NodeJS.ReadableStream>((resolve, reject) => { return new Promise<NodeJS.ReadableStream>((resolve, reject) => {
logging.debug(`Connecting to ${config.host}:${config.port || 22}`); logging.debug`Connecting to ${config.host}:${config.port || 22}`;
const socket = new Socket(); const socket = new Socket();
socket.connect(config.port || 22, config.host!, () => resolve(socket as NodeJS.ReadableStream)); socket.connect(config.port || 22, config.host!, () => resolve(socket as NodeJS.ReadableStream));
socket.once('error', reject); socket.once('error', reject);
@ -239,7 +239,7 @@ export async function createSSH(config: FileSystemConfig, sock?: NodeJS.Readable
client.once('ready', () => resolve(client)); client.once('ready', () => resolve(client));
client.once('timeout', () => reject(new Error(`Socket timed out while connecting SSH FS '${config.name}'`))); client.once('timeout', () => reject(new Error(`Socket timed out while connecting SSH FS '${config.name}'`)));
client.on('keyboard-interactive', (name, instructions, lang, prompts, finish) => { client.on('keyboard-interactive', (name, instructions, lang, prompts, finish) => {
logging.debug(`Received keyboard-interactive request with prompts "${JSON.stringify(prompts)}"`); logging.debug`Received keyboard-interactive request with prompts ${prompts}`;
Promise.all<string>(prompts.map(prompt => Promise.all<string>(prompts.map(prompt =>
vscode.window.showInputBox({ vscode.window.showInputBox({
password: true, // prompt.echo was false for me while testing password prompting password: true, // prompt.echo was false for me while testing password prompting
@ -250,9 +250,9 @@ export async function createSSH(config: FileSystemConfig, sock?: NodeJS.Readable
}); });
client.on('error', (error: Error & { description?: string }) => { client.on('error', (error: Error & { description?: string }) => {
if (error.description) { if (error.description) {
error.message = `${error.description}\n${error.message}`; error.message = `${error.description}\n${error}`;
} }
logging.error(`${error.message || error}`); logging.error(error);
reject(error); reject(error);
}); });
try { try {
@ -265,10 +265,10 @@ export async function createSSH(config: FileSystemConfig, sock?: NodeJS.Readable
// Note: If the config already specifies a custom `algorithms.key`, ignore it (trust the user?) // Note: If the config already specifies a custom `algorithms.key`, ignore it (trust the user?)
const [flagV, flagR] = getFlagBoolean('DF-GE', false, config.flags); const [flagV, flagR] = getFlagBoolean('DF-GE', false, config.flags);
if (flagV) { if (flagV) {
logging.info(`Flag "DF-GE" enabled due to '${flagR}', disabling DiffieHellman kex groupex algorithms`); logging.info`Flag "DF-GE" enabled due to '${flagR}', disabling DiffieHellman kex groupex algorithms`;
let kex: string[] = require('ssh2-streams/lib/constants').ALGORITHMS.KEX; let kex: string[] = require('ssh2-streams/lib/constants').ALGORITHMS.KEX;
kex = kex.filter(algo => !algo.includes('diffie-hellman-group-exchange')); kex = kex.filter(algo => !algo.includes('diffie-hellman-group-exchange'));
logging.debug(`\tResulting algorithms.kex: ${kex}`); logging.debug`\tResulting algorithms.kex: ${kex}`;
finalConfig.algorithms = { ...finalConfig.algorithms, kex }; finalConfig.algorithms = { ...finalConfig.algorithms, kex };
} }
client.connect(finalConfig); client.connect(finalConfig);
@ -279,14 +279,14 @@ export async function createSSH(config: FileSystemConfig, sock?: NodeJS.Readable
} }
function startSudo(shell: ClientChannel, config: FileSystemConfig, user: string | boolean = true): Promise<void> { function startSudo(shell: ClientChannel, config: FileSystemConfig, user: string | boolean = true): Promise<void> {
Logging.debug(`Turning shell into a sudo shell for ${typeof user === 'string' ? `'${user}'` : 'default sudo user'}`); Logging.debug`Turning shell into a sudo shell for ${typeof user === 'string' ? `'${user}'` : 'default sudo user'}`;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
function stdout(data: Buffer | string) { function stdout(data: Buffer | string) {
data = data.toString(); data = data.toString();
if (data.trim() === 'SUDO OK') { if (data.trim() === 'SUDO OK') {
return cleanup(), resolve(); return cleanup(), resolve();
} else { } else {
Logging.debug(`Unexpected STDOUT: ${data}`); Logging.debug`Unexpected STDOUT: ${data}`;
} }
} }
async function stderr(data: Buffer | string) { async function stderr(data: Buffer | string) {
@ -342,14 +342,14 @@ export async function getSFTP(client: Client, config: FileSystemConfig): Promise
config.sftpCommand = '/usr/lib/openssh/sftp-server'; config.sftpCommand = '/usr/lib/openssh/sftp-server';
} }
if (!config.sftpCommand) { if (!config.sftpCommand) {
logging.info(`Creating SFTP session using standard sftp subsystem`); logging.info`Creating SFTP session using standard sftp subsystem`;
return toPromise<SFTPWrapper>(cb => client.sftp(cb)); return toPromise<SFTPWrapper>(cb => client.sftp(cb));
} }
let cmd = config.sftpCommand; let cmd = config.sftpCommand;
logging.info(`Creating SFTP session using specified command: ${cmd}`); logging.info`Creating SFTP session using specified command: ${cmd}`;
const shell = await toPromise<ClientChannel>(cb => client.shell(false, cb)); const shell = await toPromise<ClientChannel>(cb => client.shell(false, cb));
// shell.stdout.on('data', (d: string | Buffer) => logging.debug(`[SFTP-STDOUT] ${d}`)); // shell.stdout.on('data', (d: string | Buffer) => logging.debug`[SFTP-STDOUT] ${d}`);
// shell.stderr.on('data', (d: string | Buffer) => logging.debug(`[SFTP-STDERR] ${d}`)); // shell.stderr.on('data', (d: string | Buffer) => logging.debug`[SFTP-STDERR] ${d}`);
// Maybe the user hasn't specified `sftpSudo`, but did put `sudo` in `sftpCommand` // Maybe the user hasn't specified `sftpSudo`, but did put `sudo` in `sftpCommand`
// I can't find a good way of differentiating welcome messages, SFTP traffic, sudo password prompts, ... // I can't find a good way of differentiating welcome messages, SFTP traffic, sudo password prompts, ...
// so convert the `sftpCommand` to make use of `sftpSudo`, since that seems to work // so convert the `sftpCommand` to make use of `sftpSudo`, since that seems to work

@ -97,12 +97,12 @@ export class ConnectionManager {
case 'code': case 'code':
let [pwd, target] = args.split(':::'); let [pwd, target] = args.split(':::');
if (!pwd || !target) { if (!pwd || !target) {
logging.error(`Malformed 'code' command args: ${args}`); logging.error`Malformed 'code' command args: ${args}`;
return; return;
} }
pwd = pwd.trim(); pwd = pwd.trim();
target = target.trim(); target = target.trim();
logging.info(`Received command to open '${target}' while in '${pwd}'`); logging.info`Received command to open '${target}' while in '${pwd}'`;
const absolutePath = target.startsWith('/') ? target : path.join(pwd, target); const absolutePath = target.startsWith('/') ? target : path.join(pwd, target);
const uri = vscode.Uri.parse(`ssh://${authority}/${absolutePath}`); const uri = vscode.Uri.parse(`ssh://${authority}/${absolutePath}`);
try { try {
@ -133,14 +133,14 @@ export class ConnectionManager {
} }
return; return;
default: default:
logging.error(`Unrecognized command ${cmd} with args: ${args}`); logging.error`Unrecognized command ${cmd} with args: ${args}`;
} }
}); });
}) })
} }
protected async _createConnection(name: string, config?: FileSystemConfig): Promise<Connection> { protected async _createConnection(name: string, config?: FileSystemConfig): Promise<Connection> {
const logging = Logging.scope(`createConnection(${name},${config ? 'config' : 'undefined'})`); const logging = Logging.scope(`createConnection(${name},${config ? 'config' : 'undefined'})`);
logging.info(`Creating a new connection for '${name}'`); logging.info`Creating a new connection for '${name}'`;
const { createSSH, calculateActualConfig } = await import('./connect'); const { createSSH, calculateActualConfig } = await import('./connect');
// Query and calculate the actual config // Query and calculate the actual config
config = config || (await loadConfigs()).find(c => c.name === name); config = config || (await loadConfigs()).find(c => c.name === name);
@ -150,7 +150,7 @@ export class ConnectionManager {
// Start the actual SSH connection // Start the actual SSH connection
const client = await createSSH(actualConfig); const client = await createSSH(actualConfig);
if (!client) throw new Error(`Could not create SSH session for '${name}'`); if (!client) throw new Error(`Could not create SSH session for '${name}'`);
logging.info(`Remote version: ${(client as any)._remoteVer || 'N/A'}`); logging.info`Remote version: ${(client as any)._remoteVer || 'N/A'}`;
// Query home directory // Query home directory
let home = await tryGetHome(client).catch((e: Error) => e); let home = await tryGetHome(client).catch((e: Error) => e);
if (typeof home !== 'string') { if (typeof home !== 'string') {
@ -175,7 +175,7 @@ export class ConnectionManager {
if (flagRCV) { if (flagRCV) {
const [flagRCDV, flagRCDR] = getFlagBoolean('DEBUG_REMOTE_COMMANDS', false, actualConfig.flags); const [flagRCDV, flagRCDR] = getFlagBoolean('DEBUG_REMOTE_COMMANDS', false, actualConfig.flags);
const withDebugStr = flagRCDV ? ` with debug logging enabled by '${flagRCDR}'` : ''; const withDebugStr = flagRCDV ? ` with debug logging enabled by '${flagRCDR}'` : '';
logging.info(`Flag REMOTE_COMMANDS provided in '${flagRCR}', setting up command terminal${withDebugStr}`); logging.info`Flag REMOTE_COMMANDS provided in '${flagRCR}', setting up command terminal${withDebugStr}`;
const cmdPath = await this._createCommandTerminal(client, name, flagRCDV); const cmdPath = await this._createCommandTerminal(client, name, flagRCDV);
environment.push({ key: 'KELVIN_SSHFS_CMD_PATH', value: cmdPath }); environment.push({ key: 'KELVIN_SSHFS_CMD_PATH', value: cmdPath });
const sftp = await toPromise<SFTPWrapper>(cb => client.sftp(cb)); const sftp = await toPromise<SFTPWrapper>(cb => client.sftp(cb));
@ -221,7 +221,7 @@ export class ConnectionManager {
const index = this.connections.indexOf(connection); const index = this.connections.indexOf(connection);
if (index === -1) return; if (index === -1) return;
reason = reason ? `'${reason}' as reason` : ' no reason given'; reason = reason ? `'${reason}' as reason` : ' no reason given';
Logging.info(`Closing connection to '${connection.actualConfig.name}' with ${reason}`); Logging.info`Closing connection to '${connection.actualConfig.name}' with ${reason}`;
this.connections.splice(index, 1); this.connections.splice(index, 1);
clearInterval(connection.idleTimer); clearInterval(connection.idleTimer);
this.onConnectionRemovedEmitter.fire(connection); this.onConnectionRemovedEmitter.fire(connection);

@ -27,7 +27,7 @@ interface CommandHandler {
} }
export function activate(context: vscode.ExtensionContext) { export function activate(context: vscode.ExtensionContext) {
Logging.info(`Extension activated, version ${getVersion()}, mode ${context.extensionMode}`); Logging.info`Extension activated, version ${getVersion()}, mode ${context.extensionMode}`;
setDebug(process.env.VSCODE_SSHFS_DEBUG?.toLowerCase() === 'true'); setDebug(process.env.VSCODE_SSHFS_DEBUG?.toLowerCase() === 'true');
@ -39,7 +39,7 @@ export function activate(context: vscode.ExtensionContext) {
if (!previousVersion) { if (!previousVersion) {
Logging.info('No previous version detected. Fresh or pre-v1.21.0 installation?'); Logging.info('No previous version detected. Fresh or pre-v1.21.0 installation?');
} else if (previousVersion !== getVersion()) { } else if (previousVersion !== getVersion()) {
Logging.info(`Previously used version ${previousVersion}, first run after install.`); Logging.info`Previously used version ${previousVersion}, first run after install.`;
} }
// Really too bad we *need* the ExtensionContext for relative resources // Really too bad we *need* the ExtensionContext for relative resources

@ -33,19 +33,19 @@ export class FileSystemRouter implements vscode.FileSystemProvider {
return (await this.assertFs(uri)).createDirectory(uri); return (await this.assertFs(uri)).createDirectory(uri);
} }
public async readFile(uri: vscode.Uri): Promise<Uint8Array> { public async readFile(uri: vscode.Uri): Promise<Uint8Array> {
Logging.debug(`Reading ${uri}`); Logging.debug`Reading ${uri}`;
return (await this.assertFs(uri)).readFile(uri); return (await this.assertFs(uri)).readFile(uri);
} }
public async writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean; }): Promise<void> { public async writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean; }): Promise<void> {
Logging.debug(`Writing ${content.length} bytes to ${uri}`); Logging.debug`Writing ${content.length} bytes to ${uri}`;
return (await this.assertFs(uri)).writeFile(uri, content, options); return (await this.assertFs(uri)).writeFile(uri, content, options);
} }
public async delete(uri: vscode.Uri, options: { recursive: boolean; }): Promise<void> { public async delete(uri: vscode.Uri, options: { recursive: boolean; }): Promise<void> {
Logging.debug(`Deleting ${uri}`); Logging.debug`Deleting ${uri}`;
return (await this.assertFs(uri)).delete(uri, options); return (await this.assertFs(uri)).delete(uri, options);
} }
public async rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): Promise<void> { public async rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): Promise<void> {
Logging.debug(`Renaming ${oldUri} to ${newUri}`); Logging.debug`Renaming ${oldUri} to ${newUri}`;
const fs = await this.assertFs(oldUri); const fs = await this.assertFs(oldUri);
if (fs !== (await this.assertFs(newUri))) throw new Error(`Can't rename between different SSH filesystems`); if (fs !== (await this.assertFs(newUri))) throw new Error(`Can't rename between different SSH filesystems`);
return fs.rename(oldUri, newUri, options); return fs.rename(oldUri, newUri, options);

@ -105,7 +105,7 @@ class Logger {
protected formatValue(value: any, options: LoggingOptions): string { protected formatValue(value: any, options: LoggingOptions): string {
if (typeof value === 'string') return value; if (typeof value === 'string') return value;
if (value instanceof Error && value.stack) { if (value instanceof Error && value.stack) {
// Format errors with stacktraces to display the JSON and the stacktrace if needed // Format errors with stacktraces to display the JSON and the stacktrace if needed
let result = `${value.name}: ${value.message}`; let result = `${value.name}: ${value.message}`;
try { try {
const json = JSON.stringify(value); const json = JSON.stringify(value);
@ -170,7 +170,7 @@ class Logger {
return result.logger.print(result.type, message, result.options) return result.logger.print(result.type, message, result.options)
} else if (Array.isArray(message)) { } else if (Array.isArray(message)) {
return result.logger.printTemplate(result.type, message, args, result.options) return result.logger.printTemplate(result.type, message, args, result.options)
} }
result.logger.error`Trying to log type ${type} with message=${message} and args=${args}`; result.logger.error`Trying to log type ${type} with message=${message} and args=${args}`;
}; };
result.logger = this; result.logger = this;

@ -60,7 +60,7 @@ export class Manager implements vscode.TaskProvider, vscode.TerminalLinkProvider
// Create the actual SFTP session (using the connection's actualConfig, otherwise it'll reprompt for passwords etc) // Create the actual SFTP session (using the connection's actualConfig, otherwise it'll reprompt for passwords etc)
const sftp = await getSFTP(con.client, con.actualConfig); const sftp = await getSFTP(con.client, con.actualConfig);
const fs = new SSHFileSystem(name, sftp, con.actualConfig); const fs = new SSHFileSystem(name, sftp, con.actualConfig);
Logging.info(`Created SSHFileSystem for ${name}, reading root directory...`); Logging.info`Created SSHFileSystem for ${name}, reading root directory...`;
this.connectionManager.update(con, con => con.filesystems.push(fs)); this.connectionManager.update(con, con => con.filesystems.push(fs));
this.fileSystems.push(fs); this.fileSystems.push(fs);
delete this.creatingFileSystems[name]; delete this.creatingFileSystems[name];
@ -96,8 +96,7 @@ export class Manager implements vscode.TaskProvider, vscode.TerminalLinkProvider
this.commandDisconnect(name); this.commandDisconnect(name);
throw e; throw e;
} }
Logging.error(`Error while connecting to SSH FS ${name}:\n${e.message}`); Logging.error`Error while connecting to SSH FS ${name}:\n${e}`;
Logging.error(e);
vscode.window.showErrorMessage(`Error while connecting to SSH FS ${name}:\n${e.message}`, 'Retry', 'Configure', 'Ignore').then((chosen) => { vscode.window.showErrorMessage(`Error while connecting to SSH FS ${name}:\n${e.message}`, 'Retry', 'Configure', 'Ignore').then((chosen) => {
delete this.creatingFileSystems[name]; delete this.creatingFileSystems[name];
if (chosen === 'Retry') { if (chosen === 'Retry') {
@ -213,7 +212,7 @@ export class Manager implements vscode.TaskProvider, vscode.TerminalLinkProvider
} }
/* Commands (stuff for e.g. context menu for ssh-configs tree) */ /* Commands (stuff for e.g. context menu for ssh-configs tree) */
public async commandConnect(config: FileSystemConfig) { public async commandConnect(config: FileSystemConfig) {
Logging.info(`Command received to connect ${config.name}`); Logging.info`Command received to connect ${config.name}`;
const folders = vscode.workspace.workspaceFolders!; const folders = vscode.workspace.workspaceFolders!;
const folder = folders && folders.find(f => f.uri.scheme === 'ssh' && f.uri.authority === config.name); const folder = folders && folders.find(f => f.uri.scheme === 'ssh' && f.uri.authority === config.name);
if (folder) return vscode.commands.executeCommand('workbench.files.action.refreshFilesExplorer'); if (folder) return vscode.commands.executeCommand('workbench.files.action.refreshFilesExplorer');
@ -229,7 +228,7 @@ export class Manager implements vscode.TaskProvider, vscode.TerminalLinkProvider
}); });
} }
public commandDisconnect(target: string | Connection) { public commandDisconnect(target: string | Connection) {
Logging.info(`Command received to disconnect ${commandArgumentToName(target)}`); Logging.info`Command received to disconnect ${commandArgumentToName(target)}`;
let cons: Connection[]; let cons: Connection[];
if (typeof target === 'object' && 'client' in target) { if (typeof target === 'object' && 'client' in target) {
cons = [target]; cons = [target];
@ -256,11 +255,12 @@ export class Manager implements vscode.TaskProvider, vscode.TerminalLinkProvider
vscode.workspace.updateWorkspaceFolders(start, folders.length - start, ...left); vscode.workspace.updateWorkspaceFolders(start, folders.length - start, ...left);
} }
public async commandTerminal(target: FileSystemConfig | Connection, uri?: vscode.Uri) { public async commandTerminal(target: FileSystemConfig | Connection, uri?: vscode.Uri) {
Logging.info(`Command received to open a terminal for ${commandArgumentToName(target)}${uri ? ` in ${uri}` : ''}`); Logging.info`Command received to open a terminal for ${commandArgumentToName(target)}${uri ? ` in ${uri}` : ''}`;
const config = 'client' in target ? target.actualConfig : target; const config = 'client' in target ? target.actualConfig : target;
try { try {
await this.createTerminal(config.label || config.name, target, uri); await this.createTerminal(config.label || config.name, target, uri);
} catch (e) { } catch (e) {
Logging.error`Error while creating terminal:\n${e}`;
const choice = await vscode.window.showErrorMessage<vscode.MessageItem>( const choice = await vscode.window.showErrorMessage<vscode.MessageItem>(
`Couldn't start a terminal for ${config.name}: ${e.message || e}`, `Couldn't start a terminal for ${config.name}: ${e.message || e}`,
{ title: 'Retry' }, { title: 'Ignore', isCloseAffordance: true }); { title: 'Retry' }, { title: 'Ignore', isCloseAffordance: true });
@ -268,7 +268,7 @@ export class Manager implements vscode.TaskProvider, vscode.TerminalLinkProvider
} }
} }
public async commandConfigure(target: string | FileSystemConfig) { public async commandConfigure(target: string | FileSystemConfig) {
Logging.info(`Command received to configure ${typeof target === 'string' ? target : target.name}`); Logging.info`Command received to configure ${typeof target === 'string' ? target : target.name}`;
if (typeof target === 'object') { if (typeof target === 'object') {
if (!target._location && !target._locations.length) { if (!target._location && !target._locations.length) {
vscode.window.showErrorMessage('Cannot configure a config-less connection!'); vscode.window.showErrorMessage('Cannot configure a config-less connection!');
@ -282,7 +282,7 @@ export class Manager implements vscode.TaskProvider, vscode.TerminalLinkProvider
configs = configs.filter(c => c.name === target); configs = configs.filter(c => c.name === target);
if (configs.length === 0) { if (configs.length === 0) {
vscode.window.showErrorMessage(`Found no matching configs for '${target}'`); vscode.window.showErrorMessage(`Found no matching configs for '${target}'`);
return Logging.error(`Unexpectedly found no matching configs for '${target}' in commandConfigure?`); return Logging.error`Unexpectedly found no matching configs for '${target}' in commandConfigure?`;
} }
const config = configs.length === 1 ? configs[0] : configs; const config = configs.length === 1 ? configs[0] : configs;
this.openSettings({ config, type: 'editconfig' }); this.openSettings({ config, type: 'editconfig' });

@ -8,7 +8,7 @@ import { toPromise, validatePort } from './utils';
async function resolveHostname(hostname: string): Promise<string> { async function resolveHostname(hostname: string): Promise<string> {
return toPromise<string>(cb => dns.lookup(hostname, cb)).then((ip) => { return toPromise<string>(cb => dns.lookup(hostname, cb)).then((ip) => {
Logging.debug(`Resolved hostname "${hostname}" to: ${ip}`); Logging.debug`Resolved hostname "${hostname}" to: ${ip}`;
return ip; return ip;
}); });
} }
@ -22,7 +22,7 @@ function validateConfig(config: FileSystemConfig) {
} }
export async function socks(config: FileSystemConfig): Promise<NodeJS.ReadableStream> { export async function socks(config: FileSystemConfig): Promise<NodeJS.ReadableStream> {
Logging.info(`Creating socks proxy connection for ${config.name}`); Logging.info`Creating socks proxy connection for ${config.name}`;
validateConfig(config); validateConfig(config);
if (config.proxy!.type !== 'socks4' && config.proxy!.type !== 'socks5') { if (config.proxy!.type !== 'socks4' && config.proxy!.type !== 'socks5') {
throw new Error(`Expected 'config.proxy.type' to be 'socks4' or 'socks5'`); throw new Error(`Expected 'config.proxy.type' to be 'socks4' or 'socks5'`);
@ -30,7 +30,7 @@ export async function socks(config: FileSystemConfig): Promise<NodeJS.ReadableSt
try { try {
const ipaddress = (await resolveHostname(config.proxy!.host)); const ipaddress = (await resolveHostname(config.proxy!.host));
if (!ipaddress) throw new Error(`Couldn't resolve '${config.proxy!.host}'`); if (!ipaddress) throw new Error(`Couldn't resolve '${config.proxy!.host}'`);
Logging.debug(`\tConnecting to ${config.host}:${config.port} over ${config.proxy!.type} proxy at ${ipaddress}:${config.proxy!.port}`); Logging.debug`\tConnecting to ${config.host}:${config.port} over ${config.proxy!.type} proxy at ${ipaddress}:${config.proxy!.port}`;
const con = await SocksClient.createConnection({ const con = await SocksClient.createConnection({
command: 'connect', command: 'connect',
destination: { destination: {
@ -50,14 +50,14 @@ export async function socks(config: FileSystemConfig): Promise<NodeJS.ReadableSt
} }
export function http(config: FileSystemConfig): Promise<NodeJS.ReadableStream> { export function http(config: FileSystemConfig): Promise<NodeJS.ReadableStream> {
Logging.info(`Creating http proxy connection for ${config.name}`); Logging.info`Creating http proxy connection for ${config.name}`;
validateConfig(config); validateConfig(config);
return new Promise<NodeJS.ReadableStream>((resolve, reject) => { return new Promise<NodeJS.ReadableStream>((resolve, reject) => {
if (config.proxy!.type !== 'http') { if (config.proxy!.type !== 'http') {
reject(new Error(`Expected config.proxy.type' to be 'http'`)); reject(new Error(`Expected config.proxy.type' to be 'http'`));
} }
try { try {
Logging.debug(`\tConnecting to ${config.host}:${config.port} over http proxy at ${config.proxy!.host}:${config.proxy!.port}`); Logging.debug`\tConnecting to ${config.host}:${config.port} over http proxy at ${config.proxy!.host}:${config.proxy!.port}`;
const req = request({ const req = request({
port: config.proxy!.port, port: config.proxy!.port,
hostname: config.proxy!.host, hostname: config.proxy!.host,

@ -49,7 +49,7 @@ export function formatSession(session: PuttySession): string {
} }
export async function getSessions() { export async function getSessions() {
Logging.info(`Fetching PuTTY sessions from registry`); Logging.info`Fetching PuTTY sessions from registry`;
const values = await toPromise<Winreg.Registry[]>(cb => winreg.keys(cb)); const values = await toPromise<Winreg.Registry[]>(cb => winreg.keys(cb));
const sessions: PuttySession[] = []; const sessions: PuttySession[] = [];
await Promise.all(values.map(regSession => (async () => { await Promise.all(values.map(regSession => (async () => {
@ -59,8 +59,8 @@ export async function getSessions() {
props.forEach(prop => properties[prop.name.toLowerCase()] = valueFromItem(prop)); props.forEach(prop => properties[prop.name.toLowerCase()] = valueFromItem(prop));
sessions.push({ name, ...(properties as any) }); sessions.push({ name, ...(properties as any) });
})())); })()));
Logging.debug(`\tFound ${sessions.length} sessions:`); Logging.debug`\tFound ${sessions.length} sessions:`;
sessions.forEach(s => Logging.debug(`\t- ${formatSession(s)}`)); sessions.forEach(s => Logging.debug`\t- ${formatSession(s)}`);
return sessions; return sessions;
} }

@ -104,8 +104,7 @@ export class SSHFileSystem implements vscode.FileSystemProvider {
// tslint:disable-next-line:no-bitwise // tslint:disable-next-line:no-bitwise
return [file.filename, type | link] as [string, vscode.FileType]; return [file.filename, type | link] as [string, vscode.FileType];
} catch (e) { } catch (e) {
this.logging.warning(`Error in readDirectory for ${furi}`, LOGGING_NO_STACKTRACE); this.logging.warning.withOptions(LOGGING_SINGLE_LINE_STACKTRACE)`Error in readDirectory for ${furi}: ${e}`;
this.logging.warning(e, LOGGING_SINGLE_LINE_STACKTRACE);
// tslint:disable-next-line:no-bitwise // tslint:disable-next-line:no-bitwise
return [file.filename, vscode.FileType.Unknown | link] as [string, vscode.FileType]; return [file.filename, vscode.FileType.Unknown | link] as [string, vscode.FileType];
} }
@ -182,8 +181,7 @@ export class SSHFileSystem implements vscode.FileSystemProvider {
this.logging.debug(`Ignored FileNotFound error for: ${uri}`, LOGGING_NO_STACKTRACE); this.logging.debug(`Ignored FileNotFound error for: ${uri}`, LOGGING_NO_STACKTRACE);
if (doThrow === true) throw e; else if (doThrow) return doThrow(e); else return; if (doThrow === true) throw e; else if (doThrow) return doThrow(e); else return;
} }
Logging.error(`Error handling uri: ${uri}`, LOGGING_NO_STACKTRACE); Logging.error.withOptions(LOGGING_HANDLE_ERROR)`Error handling uri: ${uri}\n${e}`;
Logging.error(e, LOGGING_HANDLE_ERROR);
// Convert SSH2Stream error codes into VS Code errors // Convert SSH2Stream error codes into VS Code errors
if (doThrow && typeof e.code === 'number') { if (doThrow && typeof e.code === 'number') {
const oldE = e; const oldE = e;

@ -67,7 +67,7 @@ export async function open() {
} }
export async function navigate(navigation: Navigation) { export async function navigate(navigation: Navigation) {
Logging.debug(`Navigation requested: ${JSON.stringify(navigation, null, 4)}`); Logging.debug`Navigation requested: ${navigation}`;
pendingNavigation = navigation; pendingNavigation = navigation;
postMessage({ navigation, type: 'navigate' }); postMessage({ navigation, type: 'navigate' });
return open(); return open();
@ -78,8 +78,8 @@ function postMessage<T extends Message>(message: T) {
} }
async function handleMessage(message: Message): Promise<any> { async function handleMessage(message: Message): Promise<any> {
if (!webviewPanel) return Logging.warning(`Got message without webviewPanel: ${JSON.stringify(message, null, 4)}`); if (!webviewPanel) return Logging.warning`Got message without webviewPanel: ${message}`;
Logging.debug(`Got message: ${JSON.stringify(message, null, 4)}`); Logging.debug`Got message: ${message}`;
if (pendingNavigation) { if (pendingNavigation) {
postMessage({ postMessage({
type: 'navigate', type: 'navigate',
@ -124,9 +124,7 @@ async function handleMessage(message: Message): Promise<any> {
const uris = await vscode.window.showOpenDialog({}); const uris = await vscode.window.showOpenDialog({});
if (uris) [uri] = uris; if (uris) [uri] = uris;
} catch (e) { } catch (e) {
Logging.error('Error handling promptPath message for settings UI:', LOGGING_NO_STACKTRACE); Logging.error`Error handling promptPath message for settings UI:\n${message}\n${e}`;
Logging.error(JSON.stringify(message), LOGGING_NO_STACKTRACE);
Logging.error(e);
error = e.message; error = e.message;
} }
return postMessage({ return postMessage({

Loading…
Cancel
Save