refactor🎨: (阅读代码):fileSystemConfig.ts增加注释

master
yetao 3 weeks ago
parent 027e6b710c
commit e33cf4b6b5

@ -1,137 +1,262 @@
// 从 'ssh2' 模块中导入类型 'ConnectConfig'。
// 'ConnectConfig' 可能是用于配置 SSH 连接的类型,导入这个类型可以在当前模块中使用它进行类型标注等操作。
import type { ConnectConfig } from 'ssh2'; import type { ConnectConfig } from 'ssh2';
// 导入当前目录下的 './ssh2' 文件或模块。
// 这行代码的具体作用取决于 './ssh2' 的内容,可能是加载一个自定义的模块扩展、设置一些全局变量或者执行一些初始化操作。
import './ssh2'; import './ssh2';
// 定义一个接口,表示代理配置。
export interface ProxyConfig { export interface ProxyConfig {
// 代理类型,可以是'socks4'、'socks5'或'http'。
type: 'socks4' | 'socks5' | 'http'; type: 'socks4' | 'socks5' | 'http';
// 代理服务器的主机地址。
host: string; host: string;
// 代理服务器的端口号。
port: number; port: number;
} }
// 定义一个类型别名 ConfigLocation。
// 这个类型可以是数字或者字符串。
// 可能用于表示配置项的位置,可以是一个数字索引或者一个字符串标识符等,具体用途取决于使用这个类型的上下文。
export type ConfigLocation = number | string; export type ConfigLocation = number | string;
/** Might support conditional stuff later, although ssh2/OpenSSH might not support that natively */ /** Might support conditional stuff later, although ssh2/OpenSSH might not support that natively */
// 定义一个接口,表示环境变量。
export interface EnvironmentVariable { export interface EnvironmentVariable {
// 环境变量的名称。
key: string; key: string;
// 环境变量的值。
value: string; value: string;
} }
// 定义一个名为 formatConfigLocation 的函数,用于格式化配置位置信息。
export function formatConfigLocation(location?: ConfigLocation): string { export function formatConfigLocation(location?: ConfigLocation): string {
// 如果没有传入配置位置,则返回一个表示未知位置的字符串。
if (!location) return 'Unknown location'; if (!location) return 'Unknown location';
// 如果配置位置是一个数字类型。
if (typeof location === 'number') { if (typeof location === 'number') {
// 根据数字索引返回对应的配置位置描述字符串,否则返回'Unknown'。
return `${[, 'Global', 'Workspace', 'WorkspaceFolder'][location] || 'Unknown'} settings.json`; return `${[, 'Global', 'Workspace', 'WorkspaceFolder'][location] || 'Unknown'} settings.json`;
} }
// 如果配置位置是一个字符串类型,则直接返回这个字符串。
return location; return location;
} }
// 定义一个名为 getLocations 的函数,该函数接受一个 FileSystemConfig 类型的数组作为参数,返回一个 ConfigLocation 类型的数组。
export function getLocations(configs: FileSystemConfig[]): ConfigLocation[] { export function getLocations(configs: FileSystemConfig[]): ConfigLocation[] {
// 初始化结果数组包含全局配置1和工作区配置2暂不支持工作区文件夹配置3
const res: ConfigLocation[] = [1, 2 /*, 3*/]; // No WorkspaceFolder support (for now) 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?) // TODO: Suggest creating sshfs.jsonc etc in current workspace folder(s) (UI feature?)
// 遍历输入的配置数组。
for (const { _location } of configs) { for (const { _location } of configs) {
// 如果当前配置项没有位置信息,则继续下一个配置项。
if (!_location) continue; if (!_location) continue;
// 如果结果数组中不存在当前配置项的位置信息,则将其添加到结果数组中。
if (!res.find(l => l === _location)) { if (!res.find(l => l === _location)) {
res.push(_location); res.push(_location);
} }
} }
// 返回包含所有不同位置信息的数组。
return res; return res;
} }
// 定义一个名为 getGroups 的函数,该函数接受一个 FileSystemConfig 类型的数组和一个布尔值参数 expanded返回一个字符串数组。
export function getGroups(configs: FileSystemConfig[], expanded = false): string[] { export function getGroups(configs: FileSystemConfig[], expanded = false): string[] {
// 初始化结果数组,用于存储不同的组名。
const res: string[] = []; const res: string[] = [];
// 定义一个内部函数 addGroup用于将一个组名添加到结果数组中如果该组名不存在于结果数组中。
function addGroup(group: string) { function addGroup(group: string) {
if (!res.find(l => l === group)) { if (!res.find(l => l === group)) {
res.push(group); res.push(group);
} }
} }
// 遍历输入的配置数组。
for (const { group } of configs) { for (const { group } of configs) {
// 如果当前配置项没有组信息,则继续下一个配置项。
if (!group) continue; if (!group) continue;
// 根据 expanded 参数确定如何处理组名。
const groups = expanded ? group.split('.') : [group]; const groups = expanded ? group.split('.') : [group];
// 遍历处理后的组名数组,构建并添加不同层级的组名到结果数组中。
groups.forEach((g, i) => addGroup([...groups.slice(0, i), g].join('.'))); groups.forEach((g, i) => addGroup([...groups.slice(0, i), g].join('.')));
} }
// 返回包含所有不同组名的数组。
return res; return res;
} }
// 定义一个名为 groupByLocation 的函数,该函数接受一个 FileSystemConfig 类型的数组作为参数,返回一个由配置位置和对应的配置数组组成的数组。
export function groupByLocation(configs: FileSystemConfig[]): [ConfigLocation, FileSystemConfig[]][] { export function groupByLocation(configs: FileSystemConfig[]): [ConfigLocation, FileSystemConfig[]][] {
// 初始化结果数组,用于存储配置位置和对应的配置数组。
const res: [ConfigLocation, FileSystemConfig[]][] = []; const res: [ConfigLocation, FileSystemConfig[]][] = [];
// 定义一个内部函数 getForLoc用于根据给定的配置位置获取对应的配置数组如果不存在则创建一个新的。
function getForLoc(loc: ConfigLocation = 'Unknown') { function getForLoc(loc: ConfigLocation = 'Unknown') {
// 在结果数组中查找与给定位置匹配的项。
let found = res.find(([l]) => l === loc); let found = res.find(([l]) => l === loc);
if (found) return found; if (found) return found;
// 如果没有找到匹配的项,则创建一个新的项并添加到结果数组中。
found = [loc, []]; found = [loc, []];
res.push(found); res.push(found);
return found; return found;
} }
// 遍历输入的配置数组。
for (const config of configs) { for (const config of configs) {
// 根据当前配置项的位置获取对应的配置数组,并将当前配置项添加到该数组中。
getForLoc(config._location!)[1].push(config); getForLoc(config._location!)[1].push(config);
} }
// 返回分组后的结果数组。
return res; return res;
} }
// 定义一个名为 groupByGroup 的函数,该函数接受一个 FileSystemConfig 类型的数组作为参数,返回一个由组名和对应的配置数组组成的数组。
export function groupByGroup(configs: FileSystemConfig[]): [string, FileSystemConfig[]][] { export function groupByGroup(configs: FileSystemConfig[]): [string, FileSystemConfig[]][] {
// 初始化结果数组,用于存储组名和对应的配置数组。
const res: [string, FileSystemConfig[]][] = []; const res: [string, FileSystemConfig[]][] = [];
// 定义一个内部函数 getForGroup用于根据给定的组名获取对应的配置数组如果不存在则创建一个新的。
function getForGroup(group: string = '') { function getForGroup(group: string = '') {
// 在结果数组中查找与给定组名匹配的项。
let found = res.find(([l]) => l === group); let found = res.find(([l]) => l === group);
if (found) return found; if (found) return found;
// 如果没有找到匹配的项,则创建一个新的项并添加到结果数组中。
found = [group, []]; found = [group, []];
res.push(found); res.push(found);
return found; return found;
} }
// 遍历输入的配置数组。
for (const config of configs) { for (const config of configs) {
// 根据当前配置项的组名获取对应的配置数组,并将当前配置项添加到该数组中。
getForGroup(config.group)[1].push(config); getForGroup(config.group)[1].push(config);
} }
// 返回分组后的结果数组。
return res; return res;
} }
// 定义一个接口,表示文件系统配置,继承自 ConnectConfig。
export interface FileSystemConfig extends 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; 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; 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; 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; 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[]; 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; 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; putty?: string | boolean;
/** Optional object defining a proxy to use */ /**
* Optional object defining a proxy to use.
* 使
*/
proxy?: ProxyConfig; proxy?: ProxyConfig;
/** Optional path to a private keyfile to authenticate with */ /**
* Optional path to a private keyfile to authenticate with.
*
*/
privateKeyPath?: string; privateKeyPath?: string;
/** A name of another config to use as a hop */ /**
* A name of another config to use as a hop.
*
*/
hop?: string; 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; 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 sftpCommandsftpCommand /usr/lib/openssh/sftp-server
*/
sftpSudo?: string | boolean; 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[]; 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[]; 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>; 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; 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; 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[]; 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; _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[]; _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; _calculated?: FileSystemConfig;
} }
// 定义一个名为 isFileSystemConfig 的函数,用于判断一个对象是否是文件系统配置类型。
export function isFileSystemConfig(config: any): config is FileSystemConfig { 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) { export function invalidConfigName(name: string) {
// 如果名称为空,返回错误信息表示缺少 SSH 文件系统的名称。
if (!name) return 'Missing a name for this SSH FS'; if (!name) return 'Missing a name for this SSH FS';
// 使用正则表达式检查名称是否符合要求,如果符合则返回 null表示名称有效。
if (name.match(/^[\w_\\/.@\-+]+$/)) return 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: _.+-@`; 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 * 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. * 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*)?$/; 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 { export function parseConnectionString(input: string): [config: FileSystemConfig, path?: string] | string {
// 去除输入字符串两端的空白字符。
input = input.trim(); input = input.trim();
// 使用 CONNECTION_REGEX 正则表达式对输入字符串进行匹配。
const match = input.match(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"'; if (!match) return 'Invalid format, expected something like "user@example.com:22/some/path"';
// 从匹配结果的 groups 中解构出用户、主机和路径。
const { user, host, path } = match.groups!; const { user, host, path } = match.groups!;
// 获取端口字符串。
const portStr = match.groups!.port; const portStr = match.groups!.port;
// 如果端口字符串存在,将其转换为数字类型的端口号;否则端口号为 undefined。
const port = portStr ? Number.parseInt(portStr) : 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`; if (portStr && (!port || port < 1 || port > 65535)) return `The string '${port}' is not a valid port number`;
// 根据用户、主机、端口和路径生成配置的名称。
const name = `${user || ''}@${host}${port ? `:${port}` : ''}${path || ''}`; const name = `${user || ''}@${host}${port ? `:${port}` : ''}${path || ''}`;
// 返回包含文件系统配置对象和可选路径的数组,配置对象包含名称、主机、端口、设置了 instantConnection 为 true、用户名如果用户为空则为 '$USERNAME')以及初始的 _locations 为空数组。
return [{ return [{
name, host, port, name, host, port,
instantConnection: true, instantConnection: true,

Loading…
Cancel
Save