diff --git a/src/ui-utils.ts b/src/ui-utils.ts index d7be044..396301b 100644 --- a/src/ui-utils.ts +++ b/src/ui-utils.ts @@ -1,104 +1,255 @@ +// 导入 common/fileSystemConfig 模块中的 FileSystemConfig 类型和 parseConnectionString 函数 import { FileSystemConfig, parseConnectionString } from 'common/fileSystemConfig'; +// 导入 vscode 模块 import * as vscode from 'vscode'; +// 从./config 模块中导入 getConfigs 函数 import { getConfigs } from './config'; +// 从./connection 模块中导入 Connection 和 ConnectionManager 类型 import type { Connection, ConnectionManager } from './connection'; +// 从./manager 模块中导入 Manager 类型 import type { Manager } from './manager'; +// 从./pseudoTerminal 模块中导入 SSHPseudoTerminal 类型 import type { SSHPseudoTerminal } from './pseudoTerminal'; +// 从./sshFileSystem 模块中导入 SSHFileSystem 类型 import type { SSHFileSystem } from './sshFileSystem'; +/** + * 定义了一个格式化的项,它继承自 vscode.QuickPickItem 和 vscode.TreeItem。 + * 这个接口用于表示一个可以在快速选择列表和树视图中显示的项。 + */ export interface FormattedItem extends vscode.QuickPickItem, vscode.TreeItem { + /** + * 表示与格式化项关联的原始项。 + * 这个属性可以是任何类型,具体取决于使用场景。 + */ item: any; + /** + * 表示格式化项的标签,通常用于在快速选择列表或树视图中显示。 + */ label: string; + /** + * 表示格式化项的描述,通常用于提供额外的信息或上下文。 + * 这个属性是可选的。 + */ description?: string; } +/** + * 格式化地址 + * @param config - 文件系统配置对象 + * @returns 格式化后的地址字符串 + * @description + * 这个函数用于格式化文件系统配置对象中的地址信息。 + * 它将用户名、主机名和端口号组合成一个标准的地址格式。 + */ export function formatAddress(config: FileSystemConfig): string { const { username, host, port } = config; return `${username ? `${username}@` : ''}${host}${port ? `:${port}` : ''}`; } export function setWhenClauseContext(key: string, value: any) { + // 执行 setContext 命令,设置名为 sshfs.key 的上下文,其值为 value return vscode.commands.executeCommand('setContext', `sshfs.${key}`, value); } +/** + * 设置条件语句上下文 + * @param connectionManager - 连接管理器 + * @returns 一个 Promise,当命令执行完成时解决 + * @description + * 这个函数用于设置 VS Code 扩展的上下文。上下文可以用于在不同的条件下启用或禁用特定的功能或命令。 + * 通过设置上下文,扩展可以根据当前的状态或条件来动态地改变其行为。 + */ export function setupWhenClauseContexts(connectionManager: ConnectionManager): Promise { async function refresh() { + // 获取所有活动连接 const active = connectionManager.getActiveConnections(); + // 获取所有待处理连接 const pending = connectionManager.getPendingConnections(); + // 设置上下文 'openConnections' 的值为活动连接和待处理连接的总数 await setWhenClauseContext('openConnections', active.length + pending.length); + // 设置上下文 'openTerminals' 的值为所有活动连接中终端的总数 await setWhenClauseContext('openTerminals', active.reduce((tot, con) => tot + con.terminals.length, 0)); + // 设置上下文 'openFileSystems' 的值为所有活动连接中文件系统的总数 await setWhenClauseContext('openFileSystems', active.reduce((tot, con) => tot + con.filesystems.length, 0)); } + /** + * 为连接管理器的各种事件注册 refresh 函数,以便在连接状态发生变化时更新上下文 + * @param connectionManager - 连接管理器实例 + */ connectionManager.onConnectionAdded(refresh); connectionManager.onConnectionRemoved(refresh); connectionManager.onConnectionUpdated(refresh); connectionManager.onPendingChanged(refresh); + /** + * 调用 refresh 函数,初始化上下文信息 + * @returns 一个 Promise,当 refresh 函数完成时解决 + */ return refresh(); } +/** + * 用于将相对路径转换为绝对路径的函数。 + * @type {vscode.ExtensionContext['asAbsolutePath'] | undefined} + */ export let asAbsolutePath: vscode.ExtensionContext['asAbsolutePath'] | undefined; +/** + * 设置用于将相对路径转换为绝对路径的函数。 + * @param {typeof asAbsolutePath} value - 要设置的函数。 + */ export const setAsAbsolutePath = (value: typeof asAbsolutePath) => asAbsolutePath = value; +/** + * 将文件系统配置、连接、SSH 文件系统或 SSH 伪终端格式化为格式化项 + * @param item - 要格式化的项,可以是文件系统配置、连接、SSH 文件系统或 SSH 伪终端 + * @param iconInLabel - 是否在标签中包含图标 + * @returns 格式化后的项 + * @description + * 这个函数用于将不同类型的项格式化为统一的格式,以便在用户界面中显示。 + * 根据项的类型,它会设置不同的标签、描述、详细信息、图标和上下文值。 + */ /** Converts the supported types to something basically ready-to-use as vscode.QuickPickItem and vscode.TreeItem */ export function formatItem(item: FileSystemConfig | Connection | SSHFileSystem | SSHPseudoTerminal, iconInLabel = false): FormattedItem { + /** + * 检查 item 是否为 SSHPseudoTerminal 类型 + * 如果是,则返回一个格式化的终端项 + */ if ('handleInput' in item) { // SSHPseudoTerminal + // 返回一个格式化的终端项 return { - item, contextValue: 'terminal', - label: `${iconInLabel ? '$(terminal) ' : ''}${item.terminal?.name || 'Unnamed terminal'}`, - iconPath: new vscode.ThemeIcon('terminal'), + // 原始项 + item, + // 上下文值,用于标识该项是一个终端 + contextValue: 'terminal', + // 标签,包含终端的名称,如果没有名称则显示为“Unnamed terminal” + label: `${iconInLabel? '$(terminal) ' : ''}${item.terminal?.name || 'Unnamed terminal'}`, + // 图标路径,使用 VS Code 的主题图标“terminal” + iconPath: new vscode.ThemeIcon('terminal'), + // 命令,当该项被选中时执行的命令 command: { - title: 'Focus', - command: 'sshfs.focusTerminal', - arguments: [item], + // 命令标题 + title: 'Focus', + // 命令 ID + command: 'sshfs.focusTerminal', + // 命令参数,包含要聚焦的终端项 + arguments: [item], }, }; } else if ('client' in item) { // Connection + // 从 item.config 中提取 label、name 和 group 属性 const { label, name, group } = item.config; + // 根据 group 是否存在来生成描述信息 const description = group ? `${group}.${name} ` : (label && name); + // 使用 formatAddress 函数格式化 item.actualConfig 来生成详细信息 const detail = formatAddress(item.actualConfig); + // 返回一个格式化的项对象 return { - item, description, detail, tooltip: detail, - label: `${iconInLabel ? '$(plug) ' : ''}${label || name} `, - iconPath: new vscode.ThemeIcon('plug'), - collapsibleState: vscode.TreeItemCollapsibleState.Expanded, - contextValue: 'connection', + // 原始项 + item, + // 描述信息 + description, + // 详细信息 + detail, + // 工具提示信息 + tooltip: detail, + // 标签,包含一个图标(如果 iconInLabel 为真)和标签或名称 + label: `${iconInLabel ? '$(plug) ' : ''}${label || name} `, + // 图标路径,使用 VS Code 的主题图标“plug” + iconPath: new vscode.ThemeIcon('plug'), + // 可折叠状态,设置为展开 + collapsibleState: vscode.TreeItemCollapsibleState.Expanded, + // 上下文值,用于标识该项是一个连接 + contextValue: 'connection', }; } else if ('onDidChangeFile' in item) { // SSHFileSystem + // 从 item.config 中提取 label、name 和 group 属性 const { label, name, group } = item.config; + // 根据 group 是否存在来生成描述信息 const description = group ? `${group}.${name} ` : (label && name); + // 返回一个格式化的项对象 return { - item, description, contextValue: 'filesystem', - label: `${iconInLabel ? '$(root-folder) ' : ''}ssh://${item.authority}/`, - iconPath: asAbsolutePath?.('resources/icon.svg'), + // 原始项 + item, + // 描述信息 + description, + // 上下文值,用于标识该项是一个文件系统 + contextValue: 'filesystem', + // 标签,包含一个图标(如果 iconInLabel 为真)和标签或名称 + label: `${iconInLabel ? '$(root-folder) ' : ''}ssh://${item.authority}/`, + // 图标路径,使用 VS Code 的主题图标“root-folder” + iconPath: asAbsolutePath?.('resources/icon.svg'), } } // FileSystemConfig const { label, name, group, putty } = item; + // 根据 group 是否存在来生成描述信息 const description = group ? `${group}.${name} ` : (label && name); + // 根据 putty 的值生成详细信息 const detail = putty === true ? 'PuTTY session (decuded from config)' : (typeof putty === 'string' ? `PuTTY session '${putty}'` : formatAddress(item)); + // 返回一个格式化的项对象 return { - item: item, description, detail, tooltip: detail, contextValue: 'config', - label: `${iconInLabel ? '$(settings-gear) ' : ''}${item.label || item.name} `, - iconPath: new vscode.ThemeIcon('settings-gear'), + // 原始项 + item: item, + // 描述信息 + description, + // 详细信息 + detail, + // 工具提示信息 + tooltip: detail, + // 上下文值,用于标识该项是一个配置 + contextValue: 'config', + // 标签,包含一个图标(如果 iconInLabel 为真)和标签或名称 + label: `${iconInLabel ? '$(settings-gear) ' : ''}${item.label || item.name} `, + // 图标路径,使用 VS Code 的主题图标“settings-gear” + iconPath: new vscode.ThemeIcon('settings-gear'), } } +// 定义了一个扩展了 vscode.QuickPickItem 的类型,增加了一个名为 item 的属性。 type QuickPickItemWithItem = vscode.QuickPickItem & { item: any }; +/** + * 定义了一个接口,用于配置一个复杂的选择操作。 + * 这个接口包含了多个属性,用于控制选择操作的行为。 + */ export interface PickComplexOptions { /** If true and there is only one or none item is available, immediately resolve with it/undefined */ + /** + * 如果为真,并且只有一个或没有可用项,则立即用它/未定义解析。 + * @type {boolean} + */ immediateReturn?: boolean; /** If true, add all connections. If this is a string, filter by config name first */ + /** + * 如果为真,添加所有连接。如果这是一个字符串,首先按配置名称过滤。 + * @type {boolean | string} + */ promptConnections?: boolean | string; /** If true, add an option to enter a connection string */ + /** + * 如果为真,添加一个选项以输入连接字符串。 + * @type {boolean} + */ promptInstantConnection?: boolean; /** If true, add all configurations. If this is a string, filter by config name first */ + /** + * 如果为真,添加所有配置。如果这是一个字符串,首先按配置名称过滤。 + * @type {boolean | string} + */ promptConfigs?: boolean | string; /** If true, add all terminals. If this is a string, filter by config name first */ + /** + * 如果为真,添加所有终端。如果这是一个字符串,首先按配置名称过滤。 + * @type {boolean | string} + */ promptTerminals?: boolean | string; /** If set, filter the connections/configs by (config) name first */ + /** + * 如果设置,首先按(配置)名称过滤连接/配置。 + * @type {string} + */ nameFilter?: string; }