|
|
|
@ -1,6 +1,10 @@
|
|
|
|
|
// 引入 semver 模块,用于版本比较和解析
|
|
|
|
|
import * as semver from 'semver';
|
|
|
|
|
// 引入 vscode 模块,用于 VSCode 扩展开发
|
|
|
|
|
import * as vscode from 'vscode';
|
|
|
|
|
// 引入 logging 模块,用于日志记录
|
|
|
|
|
import { Logging } from './logging';
|
|
|
|
|
// 引入 catchingPromise 模块,用于捕获 Promise 中的错误
|
|
|
|
|
import { catchingPromise } from './utils';
|
|
|
|
|
|
|
|
|
|
/* List of flags
|
|
|
|
@ -50,89 +54,171 @@ import { catchingPromise } from './utils';
|
|
|
|
|
- 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
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 解析标志列表并将其转换为键值对对象。
|
|
|
|
|
*
|
|
|
|
|
* @param list - 要解析的标志列表。可以是 undefined,表示没有提供标志列表。
|
|
|
|
|
* @param origin - 标志的来源,通常是配置文件的名称或其他标识符。
|
|
|
|
|
* @returns 一个对象,其中键是标志的名称,值是一个包含标志值和来源的数组。
|
|
|
|
|
*
|
|
|
|
|
* @throws {Error} 如果提供的列表不是数组,则抛出错误。
|
|
|
|
|
*/
|
|
|
|
|
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);
|
|
|
|
|
// 如果标志以加号(+)开头,则将其值设置为 true
|
|
|
|
|
} else if (flag.startsWith('+')) {
|
|
|
|
|
name = flag.substring(1);
|
|
|
|
|
value = true;
|
|
|
|
|
// 如果标志以减号(-)开头,则将其值设置为 false
|
|
|
|
|
} else if (flag.startsWith('-')) {
|
|
|
|
|
name = flag.substring(1);
|
|
|
|
|
value = false;
|
|
|
|
|
}
|
|
|
|
|
// 将标志名称转换为小写
|
|
|
|
|
name = name.toLocaleLowerCase();
|
|
|
|
|
// 如果对象中已经存在该标志,则忽略它
|
|
|
|
|
if (name in scope)
|
|
|
|
|
continue;
|
|
|
|
|
// 将标志名称和值添加到对象中
|
|
|
|
|
scope[name] = [value, origin];
|
|
|
|
|
}
|
|
|
|
|
// 返回解析后的标志对象
|
|
|
|
|
return scope;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 定义了一个联合类型 FlagValue,它可以是字符串、布尔值或 null。
|
|
|
|
|
// 这个类型用于表示标志的值。
|
|
|
|
|
export type FlagValue = string | boolean | null;
|
|
|
|
|
// 定义了一个类型 FlagCombo,它是一个元组,包含两个元素:
|
|
|
|
|
// - value: 一个 FlagValue 类型的值,表示标志的值。
|
|
|
|
|
// - origin: 一个字符串,表示标志的来源。
|
|
|
|
|
// V 是一个泛型参数,默认值为 FlagValue,这意味着 FlagCombo 可以接受任何 FlagValue 类型的值。
|
|
|
|
|
export type FlagCombo<V extends FlagValue = FlagValue> = [value: V, origin: string];
|
|
|
|
|
// 定义了一个 Set 类型的常量 globalFlagsSubscribers,用于存储订阅全局标志变化的回调函数。
|
|
|
|
|
// 每当全局标志发生变化时,所有订阅者都会被通知。
|
|
|
|
|
const globalFlagsSubscribers = new Set<() => void>();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 订阅全局标志的变化。
|
|
|
|
|
*
|
|
|
|
|
* @param listener - 当全局标志发生变化时要调用的回调函数。
|
|
|
|
|
* @returns 一个可处置对象,用于取消订阅。
|
|
|
|
|
*/
|
|
|
|
|
export function subscribeToGlobalFlags(listener: () => void): vscode.Disposable {
|
|
|
|
|
// 立即调用回调函数,以便在订阅时获取当前的全局标志状态。
|
|
|
|
|
listener();
|
|
|
|
|
// 将回调函数添加到全局标志订阅者集合中。
|
|
|
|
|
globalFlagsSubscribers.add(listener);
|
|
|
|
|
// 返回一个可处置对象,当调用其 dispose 方法时,会从订阅者集合中删除回调函数。
|
|
|
|
|
return new vscode.Disposable(() => globalFlagsSubscribers.delete(listener));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 定义一个默认的标志列表,初始为空数组
|
|
|
|
|
const DEFAULT_FLAGS: string[] = [];
|
|
|
|
|
// 定义一个缓存的标志对象,初始为空对象
|
|
|
|
|
let cachedFlags: Record<string, FlagCombo> = {};
|
|
|
|
|
// 初始化一个缓存对象,用于存储计算出的标志
|
|
|
|
|
let cachedFlags: Record<string, FlagCombo> = {};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 计算并返回 SSHFS 扩展的全局标志。
|
|
|
|
|
*
|
|
|
|
|
* @returns 一个包含所有解析后的标志的对象。
|
|
|
|
|
*
|
|
|
|
|
* @throws {Error} 如果无法检查 "sshfs.flags" 配置字段,则抛出错误。
|
|
|
|
|
*/
|
|
|
|
|
function calculateFlags(): Record<string, FlagCombo> {
|
|
|
|
|
// 创建一个新的对象来存储计算出的标志
|
|
|
|
|
const flags: Record<string, FlagCombo> = {};
|
|
|
|
|
// 从 VSCode 工作区配置中获取 "sshfs.flags" 的配置信息
|
|
|
|
|
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
|
|
|
|
|
// 针对特定版本的 Electron 进行修复,避免 DiffieHellman GroupExchange 导致的崩溃
|
|
|
|
|
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
|
|
|
|
|
// 针对 VSCode 1.56 及以上版本,启用文件系统提供程序错误通知
|
|
|
|
|
if (semver.gte(vscode.version, '1.56.0')) {
|
|
|
|
|
applyList(['FS_NOTIFY_ERRORS=write'], '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 => {
|
|
|
|
|
// 检查配置变化是否影响到 'sshfs.flags'
|
|
|
|
|
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
|
|
|
|
|
*/
|
|
|
|
|
/**
|
|
|
|
|
* 获取 SSHFS 扩展的全局标志,并允许通过传入一个标志列表来覆盖默认的全局标志。
|
|
|
|
|
*
|
|
|
|
|
* @param flags - 一个可选的标志列表,用于覆盖默认的全局标志。
|
|
|
|
|
* @returns 一个包含所有解析后的标志的对象。
|
|
|
|
|
*/
|
|
|
|
|
function getFlags(flags?: string[]): Record<string, FlagCombo> {
|
|
|
|
|
// 返回一个新的对象,包含缓存的全局标志和传入的覆盖标志
|
|
|
|
|
return {
|
|
|
|
|
...cachedFlags,
|
|
|
|
|
...parseFlagList(flags, 'Override'),
|
|
|
|
|
// 扩展运算符... 用于将 cachedFlags 对象的所有属性复制到新对象中
|
|
|
|
|
...cachedFlags,
|
|
|
|
|
// 调用 parseFlagList 函数解析传入的 flags 数组,并将结果合并到新对象中
|
|
|
|
|
...parseFlagList(flags, 'Override'),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Checks the `sshfs.flags` config (overridable by e.g. workspace settings).
|
|
|
|
|
* - Flag names are case-insensitive
|
|
|
|
@ -148,10 +234,19 @@ function getFlags(flags?: string[]): Record<string, FlagCombo> {
|
|
|
|
|
* @param target The name of the flag to look for
|
|
|
|
|
* @param flags An optional array of flags to check before the global ones
|
|
|
|
|
*/
|
|
|
|
|
/**
|
|
|
|
|
* 获取指定名称的标志(flag)的值。
|
|
|
|
|
*
|
|
|
|
|
* @param target - 要获取的标志的名称。
|
|
|
|
|
* @param flags - 一个可选的标志列表,用于覆盖默认的全局标志。
|
|
|
|
|
* @returns 一个包含标志值和来源的数组,如果标志不存在,则返回 undefined。
|
|
|
|
|
*/
|
|
|
|
|
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
|
|
|
|
@ -167,19 +262,37 @@ export function getFlag(target: string, flags?: string[]): FlagCombo | undefined
|
|
|
|
|
* @param flags An optional array of flags to check before the global ones
|
|
|
|
|
* @returns The matching FlagCombo or `[missingValue, 'missing']` instead
|
|
|
|
|
*/
|
|
|
|
|
/**
|
|
|
|
|
* 获取指定名称的标志(flag)的值,并尝试将其转换为布尔值。
|
|
|
|
|
*
|
|
|
|
|
* @param target - 要获取的标志的名称。
|
|
|
|
|
* @param missingValue - 当标志不存在时返回的默认布尔值。
|
|
|
|
|
* @param flags - 一个可选的标志列表,用于覆盖默认的全局标志。
|
|
|
|
|
* @returns 一个包含布尔值和来源的数组,如果标志不存在,则返回包含默认值和 'missing' 来源的数组。
|
|
|
|
|
* @throws {Error} 如果标志的值无法转换为布尔值,则抛出错误。
|
|
|
|
|
*/
|
|
|
|
|
export function getFlagBoolean(target: string, missingValue: boolean, flags?: string[]): FlagCombo<boolean> {
|
|
|
|
|
// 调用 getFlag 函数获取指定名称的标志值,如果没有找到则返回 undefined
|
|
|
|
|
const combo = getFlag(target, flags);
|
|
|
|
|
// 如果没有找到标志值,则返回一个包含默认值和 'missing' 来源的数组
|
|
|
|
|
if (!combo)
|
|
|
|
|
return [missingValue, 'missing'];
|
|
|
|
|
// 解构赋值,从 combo 中提取出值和来源
|
|
|
|
|
const [value, reason] = combo;
|
|
|
|
|
// 如果值为 null,则返回一个包含 true 和来源的数组
|
|
|
|
|
if (value == null)
|
|
|
|
|
return [true, reason];
|
|
|
|
|
// 如果值已经是布尔类型,则直接返回这个值和来源
|
|
|
|
|
if (typeof value === 'boolean')
|
|
|
|
|
return [value, reason];
|
|
|
|
|
// 将值转换为小写字符串,以便进行不区分大小写的比较
|
|
|
|
|
const lower = value.toLowerCase();
|
|
|
|
|
// 如果值是 'true'、't'、'yes' 或 'y' 中的任意一个,则返回一个包含 true 和来源的数组
|
|
|
|
|
if (lower === 'true' || lower === 't' || lower === 'yes' || lower === 'y')
|
|
|
|
|
return [true, reason];
|
|
|
|
|
// 如果值是 'false'、'f'、'no' 或 'n' 中的任意一个,则返回一个包含 false 和来源的数组
|
|
|
|
|
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!`);
|
|
|
|
|
}
|
|
|
|
|