parent
5721f1ca09
commit
11b8f052db
@ -0,0 +1,180 @@
|
||||
import * as semver from 'semver';
|
||||
import * as vscode from 'vscode';
|
||||
import { Logging } from './logging';
|
||||
import { catchingPromise } from './utils';
|
||||
|
||||
/* List of flags
|
||||
DF-GE (boolean) (default=false)
|
||||
- Disables the 'diffie-hellman-group-exchange' kex algorithm as a default option
|
||||
- Originally for issue #239
|
||||
- Automatically enabled for Electron v11.0, v11.1 and v11.2
|
||||
DEBUG_SSH2 (boolean) (default=false)
|
||||
- Enables debug logging in the ssh2 library (set at the start of each connection)
|
||||
WINDOWS_COMMAND_SEPARATOR (boolean) (default=false)
|
||||
- Makes it that commands are joined together using ` && ` instead of `; `
|
||||
- Automatically enabled when the remote shell is detected to be PowerShell or Command Prompt (cmd.exe)
|
||||
CHECK_HOME (boolean) (default=true)
|
||||
- Determines whether we check if the home directory exists during `createFileSystem` in the Manager
|
||||
- If `tryGetHome` fails while creating the connection, throw an error if this flag is set, otherwise default to `/`
|
||||
REMOTE_COMMANDS (boolean) (default=false)
|
||||
- Enables automatically launching a background command terminal during connection setup
|
||||
- Enables attempting to inject a file to be sourced by the remote shells (which adds the `code` alias)
|
||||
DEBUG_REMOTE_COMMANDS (boolean) (default=false)
|
||||
- Enables debug logging for the remote command terminal (thus useless if REMOTE_COMMANDS isn't true)
|
||||
DEBUG_FS (string) (default='')
|
||||
- A comma-separated list of debug flags for logging errors in the sshFileSystem
|
||||
- The presence of `showignored` will log `FileNotFound` that got ignored
|
||||
- The presence of `disableignored` will make the code ignore nothing (making `showignored` useless)
|
||||
- The presence of `minimal` will log all errors as single lines, but not `FileNotFound`
|
||||
- The presence of `full` is the same as `minimal` but with full stacktraces
|
||||
- The presence of `converted` will log the resulting converted errors (if required and successful)
|
||||
- The presence of `all` enables all of the above except `disableignored` (similar to `showignored,full,converted`)
|
||||
DEBUG_FSR (string) (default='', global)
|
||||
- A comma-separated list of method names to enable logging for in the FileSystemRouter
|
||||
- The presence of `all` is equal to `stat,readDirectory,createDirectory,readFile,writeFile,delete,rename`
|
||||
- The router logs handles `ssh://`, and will even log operations to non-existing configurations/connections
|
||||
FS_NOTIFY_ERRORS (string) (default='')
|
||||
- A comma-separated list of operations to display notifications for should they error
|
||||
- Mind that `FileNotFound` errors for ignored paths are always ignored, except with `DEBUG_FS=showignored`
|
||||
- The presence of `all` will show notification for every operation
|
||||
- The presence of `write` is equal to `createDirectory,writeFile,delete,rename`
|
||||
- Besides those provided by `write`, there's also `readDirectory`, `readFile` and `stat`
|
||||
- Automatically set to `write` for VS Code 1.56 and later (see issue #282)
|
||||
SHELL_CONFIG (string)
|
||||
- Forces the use of a specific shell configuration. Check shellConfig.ts for possible values
|
||||
- By default, when this flag is absent (or an empty or not a string), the extension will try to detect the correct type to use
|
||||
*/
|
||||
|
||||
function parseFlagList(list: string[] | undefined, origin: string): Record<string, FlagCombo> {
|
||||
if (list === undefined)
|
||||
return {};
|
||||
if (!Array.isArray(list))
|
||||
throw new Error(`Expected string array for flags, but got: ${list}`);
|
||||
const scope: Record<string, FlagCombo> = {};
|
||||
for (const flag of list) {
|
||||
let name: string = flag;
|
||||
let value: FlagValue = null;
|
||||
const eq = flag.indexOf('=');
|
||||
if (eq !== -1) {
|
||||
name = flag.substring(0, eq);
|
||||
value = flag.substring(eq + 1);
|
||||
} else if (flag.startsWith('+')) {
|
||||
name = flag.substring(1);
|
||||
value = true;
|
||||
} else if (flag.startsWith('-')) {
|
||||
name = flag.substring(1);
|
||||
value = false;
|
||||
}
|
||||
name = name.toLocaleLowerCase();
|
||||
if (name in scope)
|
||||
continue;
|
||||
scope[name] = [value, origin];
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
|
||||
export type FlagValue = string | boolean | null;
|
||||
export type FlagCombo<V extends FlagValue = FlagValue> = [value: V, origin: string];
|
||||
const globalFlagsSubscribers = new Set<() => void>();
|
||||
export function subscribeToGlobalFlags(listener: () => void): vscode.Disposable {
|
||||
listener();
|
||||
globalFlagsSubscribers.add(listener);
|
||||
return new vscode.Disposable(() => globalFlagsSubscribers.delete(listener));
|
||||
}
|
||||
|
||||
const DEFAULT_FLAGS: string[] = [];
|
||||
let cachedFlags: Record<string, FlagCombo> = {};
|
||||
function calculateFlags(): Record<string, FlagCombo> {
|
||||
const flags: Record<string, FlagCombo> = {};
|
||||
const config = vscode.workspace.getConfiguration('sshfs').inspect<string[]>('flags');
|
||||
if (!config)
|
||||
throw new Error(`Could not inspect "sshfs.flags" config field`);
|
||||
const applyList = (list: string[] | undefined, origin: string) => Object.assign(flags, parseFlagList(list, origin));
|
||||
applyList(DEFAULT_FLAGS, 'Built-in Default');
|
||||
applyList(config.defaultValue, 'Default Settings');
|
||||
// Electron v11 crashes for DiffieHellman GroupExchange, although it's fixed in 11.3.0
|
||||
if ((process.versions as { electron?: string; }).electron?.match(/^11\.(0|1|2)\./)) {
|
||||
applyList(['+DF-GE'], 'Fix for issue #239');
|
||||
}
|
||||
// Starting with 1.56, FileSystemProvider errors aren't shown to the user and just silently fail
|
||||
// https://github.com/SchoofsKelvin/vscode-sshfs/issues/282
|
||||
if (semver.gte(vscode.version, '1.56.0')) {
|
||||
applyList(['+FS_NOTIFY_ERRORS'], 'Fix for issue #282');
|
||||
}
|
||||
applyList(config.globalValue, 'Global Settings');
|
||||
applyList(config.workspaceValue, 'Workspace Settings');
|
||||
applyList(config.workspaceFolderValue, 'WorkspaceFolder Settings');
|
||||
Logging.info`Calculated config flags: ${flags}`;
|
||||
for (const listener of globalFlagsSubscribers) {
|
||||
catchingPromise(listener).catch(e => Logging.error`onGlobalFlagsChanged listener errored: ${e}`);
|
||||
}
|
||||
return cachedFlags = flags;
|
||||
}
|
||||
vscode.workspace.onDidChangeConfiguration(event => {
|
||||
if (event.affectsConfiguration('sshfs.flags'))
|
||||
calculateFlags();
|
||||
});
|
||||
calculateFlags();
|
||||
|
||||
/**
|
||||
* Returns (a copy of the) global flags. Gets updated by ConfigurationChangeEvent events.
|
||||
* In case `flags` is given, flags specified in this array will override global ones in the returned result.
|
||||
* @param flags An optional array of flags to check before the global ones
|
||||
*/
|
||||
function getFlags(flags?: string[]): Record<string, FlagCombo> {
|
||||
return {
|
||||
...cachedFlags,
|
||||
...parseFlagList(flags, 'Override'),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the `sshfs.flags` config (overridable by e.g. workspace settings).
|
||||
* - Flag names are case-insensitive
|
||||
* - If a flag appears twice, the first mention of it is used
|
||||
* - If a flag appears as "NAME", `null` is returned
|
||||
* - If a flag appears as "FLAG=VALUE", `VALUE` is returned as a string
|
||||
* - If a flag appears as `+FLAG` (and no `=`), `true` is returned (as a boolean)
|
||||
* - If a flag appears as `-FLAG` (and no `=`), `false` is returned (as a boolean)
|
||||
* - If a flag is missing, `undefined` is returned (different from `null`!)
|
||||
*
|
||||
* For `undefined`, an actual `undefined` is returned. For all other cases, a FlagCombo
|
||||
* is returned, e.g. "NAME" returns `[null, "someOrigin"]` and `"+F"` returns `[true, "someOrigin"]`
|
||||
* @param target The name of the flag to look for
|
||||
* @param flags An optional array of flags to check before the global ones
|
||||
*/
|
||||
export function getFlag(target: string, flags?: string[]): FlagCombo | undefined {
|
||||
return getFlags(flags)[target.toLowerCase()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Built on top of getFlag. Tries to convert the flag value to a boolean using these rules:
|
||||
* - If the flag isn't present, `missingValue` is returned
|
||||
* Although this probably means I'm using a flag that I never added to `DEFAULT_FLAGS`
|
||||
* - Booleans are kept
|
||||
* - `null` is counted as `true` (means a flag like "NAME" was present without any value or prefix)
|
||||
* - Strings try to get converted in a case-insensitive way:
|
||||
* - `true/t/yes/y` becomes true
|
||||
* - `false/f/no/n` becomes false
|
||||
* - All other strings result in an error
|
||||
* @param target The name of the flag to look for
|
||||
* @param defaultValue The value to return when no flag with the given name is present
|
||||
* @param flags An optional array of flags to check before the global ones
|
||||
* @returns The matching FlagCombo or `[missingValue, 'missing']` instead
|
||||
*/
|
||||
export function getFlagBoolean(target: string, missingValue: boolean, flags?: string[]): FlagCombo<boolean> {
|
||||
const combo = getFlag(target, flags);
|
||||
if (!combo)
|
||||
return [missingValue, 'missing'];
|
||||
const [value, reason] = combo;
|
||||
if (value == null)
|
||||
return [true, reason];
|
||||
if (typeof value === 'boolean')
|
||||
return [value, reason];
|
||||
const lower = value.toLowerCase();
|
||||
if (lower === 'true' || lower === 't' || lower === 'yes' || lower === 'y')
|
||||
return [true, reason];
|
||||
if (lower === 'false' || lower === 'f' || lower === 'no' || lower === 'n')
|
||||
return [false, reason];
|
||||
throw new Error(`Could not convert '${value}' for flag '${target}' to a boolean!`);
|
||||
}
|
Loading…
Reference in new issue