@ -1,137 +1,262 @@
// 从 'ssh2' 模块中导入类型 'ConnectConfig'。
// 'ConnectConfig' 可能是用于配置 SSH 连接的类型,导入这个类型可以在当前模块中使用它进行类型标注等操作。
import type { ConnectConfig } from 'ssh2' ;
// 导入当前目录下的 './ssh2' 文件或模块。
// 这行代码的具体作用取决于 './ssh2' 的内容,可能是加载一个自定义的模块扩展、设置一些全局变量或者执行一些初始化操作。
import './ssh2' ;
// 定义一个接口,表示代理配置。
export interface ProxyConfig {
// 代理类型,可以是'socks4'、'socks5'或'http'。
type : 'socks4' | 'socks5' | 'http' ;
// 代理服务器的主机地址。
host : string ;
// 代理服务器的端口号。
port : number ;
}
// 定义一个类型别名 ConfigLocation。
// 这个类型可以是数字或者字符串。
// 可能用于表示配置项的位置,可以是一个数字索引或者一个字符串标识符等,具体用途取决于使用这个类型的上下文。
export type ConfigLocation = number | string ;
/** Might support conditional stuff later, although ssh2/OpenSSH might not support that natively */
// 定义一个接口,表示环境变量。
export interface EnvironmentVariable {
// 环境变量的名称。
key : string ;
// 环境变量的值。
value : string ;
}
// 定义一个名为 formatConfigLocation 的函数,用于格式化配置位置信息。
export function formatConfigLocation ( location? : ConfigLocation ) : string {
// 如果没有传入配置位置,则返回一个表示未知位置的字符串。
if ( ! location ) return 'Unknown location' ;
// 如果配置位置是一个数字类型。
if ( typeof location === 'number' ) {
// 根据数字索引返回对应的配置位置描述字符串,否则返回'Unknown'。
return ` ${ [ , 'Global' , 'Workspace' , 'WorkspaceFolder' ] [ location ] || 'Unknown' } settings.json ` ;
}
// 如果配置位置是一个字符串类型,则直接返回这个字符串。
return location ;
}
// 定义一个名为 getLocations 的函数,该函数接受一个 FileSystemConfig 类型的数组作为参数,返回一个 ConfigLocation 类型的数组。
export function getLocations ( configs : FileSystemConfig [ ] ) : ConfigLocation [ ] {
// 初始化结果数组, 包含全局配置( 1) 和工作区配置( 2) , 暂不支持工作区文件夹配置( 3) 。
const res : ConfigLocation [ ] = [ 1 , 2 /*, 3*/ ] ; // No WorkspaceFolder support (for now)
// TODO: 这里的注释表明未来可能实现的功能,建议在当前工作区文件夹中创建 sshfs.jsonc 等文件(可能是一个用户界面功能)。
// TODO: Suggest creating sshfs.jsonc etc in current workspace folder(s) (UI feature?)
// 遍历输入的配置数组。
for ( const { _location } of configs ) {
// 如果当前配置项没有位置信息,则继续下一个配置项。
if ( ! _location ) continue ;
// 如果结果数组中不存在当前配置项的位置信息,则将其添加到结果数组中。
if ( ! res . find ( l = > l === _location ) ) {
res . push ( _location ) ;
}
}
// 返回包含所有不同位置信息的数组。
return res ;
}
// 定义一个名为 getGroups 的函数,该函数接受一个 FileSystemConfig 类型的数组和一个布尔值参数 expanded, 返回一个字符串数组。
export function getGroups ( configs : FileSystemConfig [ ] , expanded = false ) : string [ ] {
// 初始化结果数组,用于存储不同的组名。
const res : string [ ] = [ ] ;
// 定义一个内部函数 addGroup, 用于将一个组名添加到结果数组中, 如果该组名不存在于结果数组中。
function addGroup ( group : string ) {
if ( ! res . find ( l = > l === group ) ) {
res . push ( group ) ;
}
}
// 遍历输入的配置数组。
for ( const { group } of configs ) {
// 如果当前配置项没有组信息,则继续下一个配置项。
if ( ! group ) continue ;
// 根据 expanded 参数确定如何处理组名。
const groups = expanded ? group . split ( '.' ) : [ group ] ;
// 遍历处理后的组名数组,构建并添加不同层级的组名到结果数组中。
groups . forEach ( ( g , i ) = > addGroup ( [ . . . groups . slice ( 0 , i ) , g ] . join ( '.' ) ) ) ;
}
// 返回包含所有不同组名的数组。
return res ;
}
// 定义一个名为 groupByLocation 的函数,该函数接受一个 FileSystemConfig 类型的数组作为参数,返回一个由配置位置和对应的配置数组组成的数组。
export function groupByLocation ( configs : FileSystemConfig [ ] ) : [ ConfigLocation , FileSystemConfig [ ] ] [ ] {
// 初始化结果数组,用于存储配置位置和对应的配置数组。
const res : [ ConfigLocation , FileSystemConfig [ ] ] [ ] = [ ] ;
// 定义一个内部函数 getForLoc, 用于根据给定的配置位置获取对应的配置数组, 如果不存在则创建一个新的。
function getForLoc ( loc : ConfigLocation = 'Unknown' ) {
// 在结果数组中查找与给定位置匹配的项。
let found = res . find ( ( [ l ] ) = > l === loc ) ;
if ( found ) return found ;
// 如果没有找到匹配的项,则创建一个新的项并添加到结果数组中。
found = [ loc , [ ] ] ;
res . push ( found ) ;
return found ;
}
// 遍历输入的配置数组。
for ( const config of configs ) {
// 根据当前配置项的位置获取对应的配置数组,并将当前配置项添加到该数组中。
getForLoc ( config . _location ! ) [ 1 ] . push ( config ) ;
}
// 返回分组后的结果数组。
return res ;
}
// 定义一个名为 groupByGroup 的函数,该函数接受一个 FileSystemConfig 类型的数组作为参数,返回一个由组名和对应的配置数组组成的数组。
export function groupByGroup ( configs : FileSystemConfig [ ] ) : [ string , FileSystemConfig [ ] ] [ ] {
// 初始化结果数组,用于存储组名和对应的配置数组。
const res : [ string , FileSystemConfig [ ] ] [ ] = [ ] ;
// 定义一个内部函数 getForGroup, 用于根据给定的组名获取对应的配置数组, 如果不存在则创建一个新的。
function getForGroup ( group : string = '' ) {
// 在结果数组中查找与给定组名匹配的项。
let found = res . find ( ( [ l ] ) = > l === group ) ;
if ( found ) return found ;
// 如果没有找到匹配的项,则创建一个新的项并添加到结果数组中。
found = [ group , [ ] ] ;
res . push ( found ) ;
return found ;
}
// 遍历输入的配置数组。
for ( const config of configs ) {
// 根据当前配置项的组名获取对应的配置数组,并将当前配置项添加到该数组中。
getForGroup ( config . group ) [ 1 ] . push ( config ) ;
}
// 返回分组后的结果数组。
return res ;
}
// 定义一个接口,表示文件系统配置,继承自 ConnectConfig。
export interface FileSystemConfig extends ConnectConfig {
/** Name of the config. Can only exists of lowercase alphanumeric characters, slashes and any of these: _.+-@ */
/ * *
* Name of the config . Can only exists of lowercase alphanumeric characters , slashes and any of these : _. + - @ .
* 配 置 的 名 称 。 只 能 由 小 写 字 母 数 字 字 符 、 斜 杠 以 及 _ . + - @ 中 的 任 意 字 符 组 成 。
* /
name : string ;
/** Optional label to display in some UI places (e.g. popups) */
/ * *
* Optional label to display in some UI places ( e . g . popups ) .
* 可 选 的 标 签 , 用 于 在 一 些 用 户 界 面 位 置 ( 例 如 弹 出 窗 口 ) 中 显 示 。
* /
label? : string ;
/** Optional group for this config, to group configs together in some UI places. Allows subgroups, in the format "Group1.SubGroup1.Subgroup2" */
/ * *
* Optional group for this config , to group configs together in some UI places . Allows subgroups , in the format "Group1.SubGroup1.Subgroup2" .
* 此 配 置 的 可 选 组 , 用 于 在 一 些 用 户 界 面 位 置 中 将 配 置 分 组 。 允 许 子 组 , 格 式 为 "Group1.SubGroup1.Subgroup2" 。
* /
group? : string ;
/** Whether to merge this "lower" config (e.g. from workspace settings) into higher configs (e.g. from global settings) */
/ * *
* Whether to merge this "lower" config ( e . g . from workspace settings ) into higher configs ( e . g . from global settings ) .
* 是 否 将 此 “ 较 低 级 别 ” 的 配 置 ( 例 如 来 自 工 作 区 设 置 ) 合 并 到 更 高 级 别 的 配 置 ( 例 如 来 自 全 局 设 置 ) 中 。
* /
merge? : boolean ;
/** Names of other existing configs to merge into this config. Earlier entries overridden by later entries overridden by this config itself */
/ * *
* Names of other existing configs to merge into this config . Earlier entries overridden by later entries overridden by this config itself .
* 要 合 并 到 此 配 置 中 的 其 他 现 有 配 置 的 名 称 。 较 早 的 条 目 被 较 晚 的 条 目 覆 盖 , 较 晚 的 条 目 又 被 此 配 置 本 身 覆 盖 。
* /
extend? : string | string [ ] ;
/** Path on the remote server that should be opened by default when creating a terminal or using the `Add as Workspace folder` command/button. Defaults to `/` */
/ * *
* Path on the remote server that should be opened by default when creating a terminal or using the ` Add as Workspace folder ` command / button . Defaults to ` / ` .
* 在 创 建 终 端 或 使 用 “ 添 加 为 工 作 区 文 件 夹 ” 命 令 / 按 钮 时 , 应 默 认 打 开 的 远 程 服 务 器 上 的 路 径 。 默 认 为 ` / ` 。
* /
root? : string ;
/** A name of a PuTTY session, or `true` to find the PuTTY session from the host address */
/ * *
* A name of a PuTTY session , or ` true ` to find the PuTTY session from the host address .
* PuTTY 会 话 的 名 称 , 或 者 为 ` true ` 时 从 主 机 地 址 查 找 PuTTY 会 话 。
* /
putty? : string | boolean ;
/** Optional object defining a proxy to use */
/ * *
* Optional object defining a proxy to use .
* 定 义 要 使 用 的 代 理 的 可 选 对 象 。
* /
proxy? : ProxyConfig ;
/** Optional path to a private keyfile to authenticate with */
/ * *
* Optional path to a private keyfile to authenticate with .
* 用 于 身 份 验 证 的 私 钥 文 件 的 可 选 路 径 。
* /
privateKeyPath? : string ;
/** A name of another config to use as a hop */
/ * *
* A name of another config to use as a hop .
* 用 作 跳 转 的 另 一 个 配 置 的 名 称 。
* /
hop? : string ;
/** The command to run on the remote SSH session to start a SFTP session (defaults to sftp subsystem) */
/ * *
* The command to run on the remote SSH session to start a SFTP session ( defaults to sftp subsystem ) .
* 在 远 程 SSH 会 话 上 运 行 以 启 动 SFTP 会 话 的 命 令 ( 默 认 为 sftp 子 系 统 ) 。
* /
sftpCommand? : string ;
/** Whether to use a sudo shell (and for which user) to run the sftpCommand in (sftpCommand defaults to /usr/lib/openssh/sftp-server if missing) */
/ * *
* Whether to use a sudo shell ( and for which user ) to run the sftpCommand in ( sftpCommand defaults to / usr / lib / openssh / sftp - server if missing ) .
* 是 否 使 用 sudo 外 壳 ( 以 及 针 对 哪 个 用 户 ) 来 运 行 sftpCommand ( 如 果 缺 少 , sftpCommand 默 认 为 / usr / lib / openssh / sftp - server ) 。
* /
sftpSudo? : string | boolean ;
/** The command(s) to run when a new SSH terminal gets created. Defaults to `$SHELL`. Internally the command `cd ...` is run first */
/ * *
* The command ( s ) to run when a new SSH terminal gets created . Defaults to ` $ SHELL ` . Internally the command ` cd... ` is run first .
* 当 创 建 新 的 SSH 终 端 时 要 运 行 的 命 令 。 默 认 为 ` $ SHELL ` 。 内 部 首 先 运 行 ` cd... ` 命 令 。
* /
terminalCommand? : string | string [ ] ;
/** The command(s) to run when a `ssh-shell` task gets run. Defaults to the placeholder `$COMMAND`. Internally the command `cd ...` is run first */
/ * *
* The command ( s ) to run when a ` ssh-shell ` task gets run . Defaults to the placeholder ` $ COMMAND ` . Internally the command ` cd... ` is run first .
* 当 运 行 ` ssh-shell ` 任 务 时 要 运 行 的 命 令 。 默 认 为 占 位 符 ` $ COMMAND ` 。 内 部 首 先 运 行 ` cd... ` 命 令 。
* /
taskCommand? : string | string [ ] ;
/** An object with environment variables to add to the SSH connection. Affects the whole connection thus all terminals */
/ * *
* An object with environment variables to add to the SSH connection . Affects the whole connection thus all terminals .
* 一 个 包 含 要 添 加 到 SSH 连 接 的 环 境 变 量 的 对 象 。 影 响 整 个 连 接 , 因 此 影 响 所 有 终 端 。
* /
environment? : EnvironmentVariable [ ] | Record < string , string > ;
/** The filemode to assign to new files created using VS Code, not the terminal. Similar to umask. Defaults to `rw-rw-r--` (regardless of server config, whether you are root, ...) */
/ * *
* The filemode to assign to new files created using VS Code , not the terminal . Similar to umask . Defaults to ` rw-rw-r-- ` ( regardless of server config , whether you are root , . . . ) .
* 分 配 给 使 用 VS Code 创 建 的 新 文 件 的 文 件 模 式 , 而 不 是 终 端 。 类 似 于 umask 。 默 认 为 ` rw-rw-r-- ` ( 无 论 服 务 器 配 置 如 何 , 无 论 你 是 否 是 root 用 户 等 ) 。
* /
newFileMode? : number | string ;
/** Whether this config was created from an instant connection string. Enables fuzzy matching for e.g. PuTTY, config-by-host, ... */
/ * *
* Whether this config was created from an instant connection string . Enables fuzzy matching for e . g . PuTTY , config - by - host , . . .
* 此 配 置 是 否 是 从 即 时 连 接 字 符 串 创 建 的 。 例 如 , 为 PuTTY 、 按 主 机 配 置 等 启 用 模 糊 匹 配 。
* /
instantConnection? : boolean ;
/** List of special flags to enable/disable certain fixes/features. Flags are usually used for issues or beta testing. Flags can disappear/change anytime! */
/ * *
* List of special flags to enable / disable certain fixes / features . Flags are usually used for issues or beta testing . Flags can disappear / change anytime !
* 用 于 启 用 / 禁 用 某 些 修 复 / 功 能 的 特 殊 标 志 列 表 。 标 志 通 常 用 于 问 题 或 测 试 版 。 标 志 可 能 随 时 消 失 / 更 改 !
* /
flags? : string [ ] ;
/** Internal property saying where this config comes from. Undefined if this config is merged or something */
/ * *
* Internal property saying where this config comes from . Undefined if this config is merged or something .
* 内 部 属 性 , 表 示 此 配 置 的 来 源 。 如 果 此 配 置 是 合 并 的 或 其 他 情 况 , 则 为 未 定 义 。
* /
_location? : ConfigLocation ;
/** Internal property keeping track of where this config comes from (including merges) */
/ * *
* Internal property keeping track of where this config comes from ( including merges ) .
* 内 部 属 性 , 跟 踪 此 配 置 的 来 源 ( 包 括 合 并 的 情 况 ) 。
* /
_locations : ConfigLocation [ ] ;
/** Internal property keeping track of whether this config is an actually calculated one, and if so, which config it originates from (normally itself) */
/ * *
* Internal property keeping track of whether this config is an actually calculated one , and if so , which config it originates from ( normally itself ) .
* 内 部 属 性 , 跟 踪 此 配 置 是 否 是 实 际 计 算 得 到 的 配 置 , 如 果 是 , 它 源 自 哪 个 配 置 ( 通 常 是 它 自 己 ) 。
* /
_calculated? : FileSystemConfig ;
}
// 定义一个名为 isFileSystemConfig 的函数,用于判断一个对象是否是文件系统配置类型。
export function isFileSystemConfig ( config : any ) : config is FileSystemConfig {
return typeof config === 'object' && typeof config . name === 'string' && Array . isArray ( config . _locations ) ;
// 首先判断传入的参数是否是一个对象。
return typeof config === 'object' &&
// 然后判断对象是否有一个名为 'name' 的属性且该属性是字符串类型。
typeof config . name === 'string' &&
// 最后判断对象是否有一个名为 '_locations' 的属性且该属性是数组类型。
Array . isArray ( config . _locations ) ;
}
// 定义一个名为 invalidConfigName 的函数,该函数接受一个字符串参数 name, 用于检查 SSH 文件系统配置的名称是否有效。
export function invalidConfigName ( name : string ) {
// 如果名称为空,返回错误信息表示缺少 SSH 文件系统的名称。
if ( ! name ) return 'Missing a name for this SSH FS' ;
// 使用正则表达式检查名称是否符合要求,如果符合则返回 null, 表示名称有效。
if ( name . match ( /^[\w_\\/.@\-+]+$/ ) ) return null ;
// 如果名称不符合要求,返回错误信息说明有效的 SSH 文件系统名称的规则。
return ` A SSH FS name can only exists of lowercase alphanumeric characters, slashes and any of these: _.+-@ ` ;
}
@ -148,17 +273,28 @@ export function invalidConfigName(name: string) {
* The resulting FileSystemConfig will have as name basically the input , but without the path . If there is no
* username given , the name will start with ` @ ` , as to differentiate between connection strings and config names .
* /
// 定义一个名为 CONNECTION_REGEX 的正则表达式,用于匹配 SSH 连接字符串的格式。
const CONNECTION_REGEX = /^((?<user>[\w\-._]+)?(;[\w-]+=[\w\d-]+(,[\w\d-]+=[\w\d-]+)*)?@)?(?<host>[^\s@\\/:,=]+)(:(?<port>\d+))?(?<path>\/\S*)?$/ ;
// 定义一个名为 parseConnectionString 的函数,用于解析连接字符串,返回一个包含文件系统配置对象和可选路径的数组或者错误信息字符串。
export function parseConnectionString ( input : string ) : [ config : FileSystemConfig , path? : string ] | string {
// 去除输入字符串两端的空白字符。
input = input . trim ( ) ;
// 使用 CONNECTION_REGEX 正则表达式对输入字符串进行匹配。
const match = input . match ( CONNECTION_REGEX ) ;
// 如果没有匹配结果,返回错误信息表示输入格式无效,期望的格式类似于 "user@example.com:22/some/path"。
if ( ! match ) return 'Invalid format, expected something like "user@example.com:22/some/path"' ;
// 从匹配结果的 groups 中解构出用户、主机和路径。
const { user , host , path } = match . groups ! ;
// 获取端口字符串。
const portStr = match . groups ! . port ;
// 如果端口字符串存在,将其转换为数字类型的端口号;否则端口号为 undefined。
const port = portStr ? Number . parseInt ( portStr ) : undefined ;
// 如果端口字符串存在且端口号无效(小于 1 或大于 65535) , 返回错误信息表示端口号无效。
if ( portStr && ( ! port || port < 1 || port > 65535 ) ) return ` The string ' ${ port } ' is not a valid port number ` ;
// 根据用户、主机、端口和路径生成配置的名称。
const name = ` ${ user || '' } @ ${ host } ${ port ? ` : ${ port } ` : '' } ${ path || '' } ` ;
// 返回包含文件系统配置对象和可选路径的数组,配置对象包含名称、主机、端口、设置了 instantConnection 为 true、用户名( 如果用户为空则为 '$USERNAME')以及初始的 _locations 为空数组。
return [ {
name , host , port ,
instantConnection : true ,