diff --git a/src/flags.ts b/src/flags.ts index e2d08c9..7e41f35 100644 --- a/src/flags.ts +++ b/src/flags.ts @@ -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 { + // 如果列表未定义,则返回一个空对象 if (list === undefined) return {}; + // 如果列表不是数组,则抛出错误 if (!Array.isArray(list)) throw new Error(`Expected string array for flags, but got: ${list}`); + // 创建一个对象来存储解析后的标志 const scope: Record = {}; + // 遍历列表中的每个标志 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 = [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 = {}; +// 初始化一个缓存对象,用于存储计算出的标志 let cachedFlags: Record = {}; + +/** + * 计算并返回 SSHFS 扩展的全局标志。 + * + * @returns 一个包含所有解析后的标志的对象。 + * + * @throws {Error} 如果无法检查 "sshfs.flags" 配置字段,则抛出错误。 + */ function calculateFlags(): Record { + // 创建一个新的对象来存储计算出的标志 const flags: Record = {}; + // 从 VSCode 工作区配置中获取 "sshfs.flags" 的配置信息 const config = vscode.workspace.getConfiguration('sshfs').inspect('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 { + // 返回一个新的对象,包含缓存的全局标志和传入的覆盖标志 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 { * @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 { + // 调用 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!`); }