From 33bb60eae5ae2bc2aa4118cd10c40a639388559f Mon Sep 17 00:00:00 2001 From: yetao Date: Tue, 29 Oct 2024 17:05:34 +0800 Subject: [PATCH] =?UTF-8?q?refactor=F0=9F=8E=A8:=20=20(=E9=98=85=E8=AF=BB?= =?UTF-8?q?=E4=BB=A3=E7=A0=81)=EF=BC=9Aconnection.ts=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/connection.ts | 48 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/connection.ts b/src/connection.ts index cea9e3b..59b583b 100644 --- a/src/connection.ts +++ b/src/connection.ts @@ -230,40 +230,55 @@ export class ConnectionManager { * @param config 可选的文件系统配置 * @returns 一个 Promise,解析为新创建的连接对象 */ + // 定义一个受保护的异步方法 _createConnection,用于创建一个连接对象。 protected async _createConnection(name: string, config?: FileSystemConfig): Promise { + // 创建一个日志作用域,用于记录创建连接的过程。 const logging = Logging.scope(`createConnection(${name},${config ? 'config' : 'undefined'})`); + // 记录正在为指定名称创建新连接的信息。 logging.info`Creating a new connection for '${name}'`; + // 动态导入 './connect' 模块中的 createSSH 和 calculateActualConfig 函数。 const { createSSH, calculateActualConfig } = await import('./connect'); // Query and calculate the actual config + // 查询并计算实际的配置。 config = config || (await loadConfigs()).find(c => c.name === name); if (!config) throw new Error(`No configuration with name '${name}' found`); + // 计算实际的配置,如果没有配置则抛出错误。 const actualConfig = await calculateActualConfig(config); if (!actualConfig) throw new Error('Connection cancelled'); // Start the actual SSH connection + // 创建实际的 SSH 连接。 const client = await createSSH(actualConfig); if (!client) throw new Error(`Could not create SSH session for '${name}'`); + // 记录远程版本信息。 logging.info`Remote version: ${(client as any)._remoteVer || 'N/A'}`; // Calculate shell config + // 计算 shell 配置。 let shellConfig: ShellConfig; const [flagSCV, flagSCR] = getFlag("SHELL_CONFIG", config.flags) || []; if (flagSCV && typeof flagSCV === 'string') { + // 如果有强制的 shell 配置标志,使用指定的 shell 配置并记录相关信息。 logging.info`Using forced shell config '${flagSCV}' set by ${flagSCR}`; shellConfig = KNOWN_SHELL_CONFIGS[flagSCV]; if (!shellConfig) throw new Error(`The forced shell config '${flagSCV}' does not exist`); } else { + // 否则,异步计算 shell 配置。 shellConfig = await calculateShellConfig(client, logging); } // Query home directory + // 查询主目录。 let home: string | Error | null; if (shellConfig.isWindows) { + // 在 Windows 环境下尝试获取用户主目录。 home = await tryCommand(client, "echo %USERPROFILE%").catch((e: Error) => e); if (home === null) home = new Error(`No output for "echo %USERPROFILE%"`); if (typeof home === 'string') home = home.trim(); if (home === "%USERPROFILE%") home = new Error(`Non-substituted output for "echo %USERPROFILE%"`); } else { + // 在非 Windows 环境下尝试获取用户主目录。 home = await tryEcho(client, shellConfig, '~').catch((e: Error) => e); } if (typeof home !== 'string') { + // 如果无法获取主目录且 CHECK_HOME 标志为 true,则抛出错误并显示错误信息。 const [flagCH] = getFlagBoolean('CHECK_HOME', true, config.flags); logging.error('Could not detect home directory', LOGGING_NO_STACKTRACE); if (flagCH) { @@ -273,29 +288,32 @@ export class ConnectionManager { await vscode.window.showErrorMessage(`Couldn't detect the home directory for '${name}'`, 'Okay'); throw new Error(`Could not detect home directory`); } else { + // 如果 CHECK_HOME 标志为 false,则记录警告信息并将主目录设置为空字符串。 if (home) logging.warning(home); logging.warning('The CHECK_HOME flag is disabled, default to \'/\' and ignore the error'); home = ''; } } logging.debug`Home path: ${home}`; - // Calculate the environment + // 计算环境变量。 const environment: EnvironmentVariable[] = mergeEnvironment([], config.environment); - // Set up stuff for receiving remote commands + // 根据 REMOTE_COMMANDS 标志设置接收远程命令的配置。 const [flagRCV, flagRCR] = getFlagBoolean('REMOTE_COMMANDS', false, actualConfig.flags); if (flagRCV) { const [flagRCDV, flagRCDR] = getFlagBoolean('DEBUG_REMOTE_COMMANDS', false, actualConfig.flags); - const withDebugStr = flagRCDV ? ` with debug logging enabled by '${flagRCDR}'` : ''; + const withDebugStr = flagRCDV? ` with debug logging enabled by '${flagRCDR}'` : ''; logging.info`Flag REMOTE_COMMANDS provided in '${flagRCR}', setting up command terminal${withDebugStr}`; if (shellConfig.isWindows) { + // 在 Windows 环境下记录不支持命令终端的错误信息。 logging.error(`Windows detected, command terminal is not yet supported`, LOGGING_NO_STACKTRACE); } else { + // 在非 Windows 环境下创建命令终端并将其路径添加到环境变量中。 const cmdPath = await this._createCommandTerminal(client, shellConfig, name, flagRCDV); environment.push({ key: 'KELVIN_SSHFS_CMD_PATH', value: cmdPath }); } } logging.debug`Environment: ${environment}`; - // Set up the Connection object + // 创建连接对象并设置相关属性。 let timeoutCounter = 0; const con: Connection = { config, client, actualConfig, home, shellConfig, environment, @@ -304,6 +322,7 @@ export class ConnectionManager { cache: {}, pendingUserCount: 0, idleTimer: setInterval(() => { // Automatically close connection when idle for a while + // 自动关闭连接的定时器函数,当连接空闲一段时间后关闭连接。 timeoutCounter = timeoutCounter ? timeoutCounter - 1 : 0; if (con.pendingUserCount) return; // Still got starting filesystems/terminals on this connection con.filesystems = con.filesystems.filter(fs => !fs.closed && !fs.closing); @@ -311,35 +330,53 @@ export class ConnectionManager { if (con.terminals.length) return; // Still got active terminals on this connection if (timeoutCounter !== 1) return timeoutCounter = 2; // timeoutCounter === 1, so it's been inactive for at least 5 seconds, close it! + // 连接空闲一段时间后关闭连接,并记录关闭原因。 this.closeConnection(con, 'Idle with no active filesystems/terminals'); }, 5e3), }; + // 将连接对象添加到连接列表中,并触发连接添加事件。 this.connections.push(con); this.onConnectionAddedEmitter.fire(con); + // 返回创建的连接对象。 return con; } + // 定义一个公共的异步方法 createConnection,用于创建一个连接对象。 public async createConnection(name: string, config?: FileSystemConfig): Promise { + // 尝试获取已激活的连接对象,如果存在则直接返回。 const con = this.getActiveConnection(name, config); if (con) return con; + // 检查是否有正在等待的连接,如果有则直接返回等待中的连接对象。 let pending = this.pendingConnections[name]; if (pending) return pending[0]; + // 创建一个等待中的连接对象,并将其和配置一起存储在 pendingConnections 中。 pending = [this._createConnection(name, config), config]; this.pendingConnections[name] = pending; + // 触发正在等待的连接发生变化的事件。 this.onPendingChangedEmitter.fire(); + // 当等待中的连接完成时,删除对应的等待项并触发事件。 pending[0].finally(() => { delete this.pendingConnections[name]; this.onPendingChangedEmitter.fire(); }); + // 返回等待中的连接对象。 return pending[0]; } + // 定义一个公共方法 closeConnection,用于关闭指定的连接。 public closeConnection(connection: Connection, reason?: string) { + // 查找连接在连接列表中的索引,如果不存在则直接返回。 const index = this.connections.indexOf(connection); if (index === -1) return; + // 如果有原因则格式化原因字符串,否则设置为'no reason given'。 reason = reason ? `'${reason}' as reason` : ' no reason given'; + // 记录关闭连接的信息,包括连接的名称和关闭原因。 Logging.info`Closing connection to '${connection.actualConfig.name}' with ${reason}`; + // 从连接列表中移除指定的连接。 this.connections.splice(index, 1); + // 清除连接的空闲定时器。 clearInterval(connection.idleTimer); + // 触发连接被移除的事件。 this.onConnectionRemovedEmitter.fire(connection); + // 关闭连接的客户端。 connection.client.end(); } // Without making createConnection return a Proxy, or making Connection a class with @@ -347,8 +384,11 @@ export class ConnectionManager { // So stuff that updates connections should inform us by calling this method. // (currently the only thing this matters for is the 'sshfs-connections' tree view) // The updater callback just allows for syntactic sugar e.g. update(con, con => modifyCon(con)) + // 定义一个公共方法 update,用于更新指定的连接。 public update(connection: Connection, updater?: (con: Connection) => void) { + // 如果提供了更新函数,则调用更新函数对连接进行更新。 updater?.(connection); + // 触发连接被更新的事件。 this.onConnectionUpdatedEmitter.fire(connection); } }