|
|
|
@ -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<void> {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|