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

master
yetao 2 weeks ago
parent 54a853f590
commit c84cc72383

@ -1,221 +1,418 @@
// 导入 FileSystemConfig 类型,该类型定义了文件系统的配置
import type { FileSystemConfig } from 'common/fileSystemConfig';
// 导入 path 模块,用于处理和转换文件路径
import * as path from 'path';
// 导入 ssh2 模块,用于实现 SSH 连接和文件传输
import type * as ssh2 from 'ssh2';
// 导入 vscode 模块,用于与 VS Code 编辑器进行交互
import * as vscode from 'vscode';
// 导入 FlagValue 类型和相关函数,用于处理全局标志
import { FlagValue, getFlag, subscribeToGlobalFlags } from './flags';
// 导入 Logger、Logging 类型和相关常量,用于日志记录
import { Logger, Logging, LOGGING_NO_STACKTRACE, LOGGING_SINGLE_LINE_STACKTRACE, withStacktraceOffset } from './logging';
// 导入 toPromise 函数,用于将回调函数转换为 Promise
import { toPromise } from './utils';
// This makes it report a single line of the stacktrace of where the e.g. logger.info() call happened
// while also making it that if we're logging an error, only the first 4 lines of the stack (including the error message) are shown
// (usually the errors we report on happen deep inside ssh2 or ssh2-streams, we don't really care that much about it)
// 配置错误报告的堆栈跟踪为单行,并限制行数为 4
const LOGGING_HANDLE_ERROR = withStacktraceOffset(1, { ...LOGGING_SINGLE_LINE_STACKTRACE, maxErrorStack: 4 });
// All absolute paths (relative to the FS root or a workspace root)
// If it ends with /, .startsWith is used, otherwise a raw equal
// 定义一个数组,包含在文件系统操作中应该忽略的文件或目录路径
const IGNORE_NOT_FOUND: string[] = [
// Visual Studio Code 配置文件夹
'/.vscode',
// Visual Studio Code 配置文件夹(带斜杠)
'/.vscode/',
// Git 版本控制文件夹
'/.git/',
// Node.js 模块文件夹
'/node_modules',
// Maven 项目配置文件
'/pom.xml',
// Android 项目主目录下的 AndroidManifest.xml 文件
'/app/src/main/AndroidManifest.xml',
// Gradle 构建文件
'/build.gradle',
// DevContainer 配置文件
'/.devcontainer/devcontainer.json',
// Python 项目配置文件
'/pyproject.toml',
];
function shouldIgnoreNotFound(target: string) {
/**
*
* @param {string} target -
* @returns {boolean} true false
*/
function shouldIgnoreNotFound(target: string) {
// 如果 IGNORE_NOT_FOUND 数组中存在与 target 完全相等的元素,或者存在以 '/' 结尾且 target 以该元素开头的元素,则返回 true
if (IGNORE_NOT_FOUND.some(entry => entry === target || entry.endsWith('/') && target.startsWith(entry))) return true;
// 遍历当前打开的工作区文件夹
for (const { uri: { path: wsPath } } of vscode.workspace.workspaceFolders || []) {
if (!target.startsWith(wsPath)) continue;
let local = path.posix.relative(wsPath, target);
if (!local.startsWith('/')) local = `/${local}`;
if (IGNORE_NOT_FOUND.some(entry => entry === local || entry.endsWith('/') && local.startsWith(entry))) return true;
// 如果 target 不是以当前工作区文件夹路径开头,则继续下一个循环
if (!target.startsWith(wsPath)) continue;
// 计算 target 相对于工作区文件夹路径的相对路径
let local = path.posix.relative(wsPath, target);
// 如果相对路径不是以 '/' 开头,则在前面添加 '/'
if (!local.startsWith('/')) local = `/${local}`;
// 如果 IGNORE_NOT_FOUND 数组中存在与 local 完全相等的元素,或者存在以 '/' 结尾且 local 以该元素开头的元素,则返回 true
if (IGNORE_NOT_FOUND.some(entry => entry === local || entry.endsWith('/') && local.startsWith(entry))) return true;
}
// 如果都不满足条件,则返回 false
return false;
}
const DEBUG_NOTIFY_FLAGS: Record<string, string[] | undefined> = {};
DEBUG_NOTIFY_FLAGS.write = ['createdirectory', 'writefile', 'delete', 'rename'];
DEBUG_NOTIFY_FLAGS.all = [...DEBUG_NOTIFY_FLAGS.write, 'readdirectory', 'readfile', 'stat'];
/**
* DEBUG_NOTIFY_FLAGS
* undefined
*/
const DEBUG_NOTIFY_FLAGS: Record<string, string[] | undefined> = {};
// 将 'createdirectory'、'writefile'、'delete'、'rename' 操作添加到 DEBUG_NOTIFY_FLAGS 对象中,键为 'write'
DEBUG_NOTIFY_FLAGS.write = ['createdirectory', 'writefile', 'delete', 'rename'];
// 将 DEBUG_NOTIFY_FLAGS.write 中的操作列表和 'readdirectory'、'readfile'、'stat' 操作合并,添加到 DEBUG_NOTIFY_FLAGS 对象中,键为 'all'
DEBUG_NOTIFY_FLAGS.all = [...DEBUG_NOTIFY_FLAGS.write, 'readdirectory', 'readfile', 'stat'];
export class SSHFileSystem implements vscode.FileSystemProvider {
// 定义一个受保护的类属性 onCloseEmitter它是一个 vscode.EventEmitter<void> 类型的事件发射器,用于在关闭时发出无参数的事件。
protected onCloseEmitter = new vscode.EventEmitter<void>();
// 定义一个受保护的类属性 onDidChangeFileEmitter它是一个 vscode.EventEmitter<vscode.FileChangeEvent[]> 类型的事件发射器,用于在文件发生变化时发出文件变化事件数组。
protected onDidChangeFileEmitter = new vscode.EventEmitter<vscode.FileChangeEvent[]>();
// 定义一个受保护的类属性 debugFlags它是一个字符串数组用于存储调试标志。
protected debugFlags: string[];
// 定义一个受保护的类属性 notifyErrorFlags它是一个字符串数组用于存储通知错误标志。
protected notifyErrorFlags: string[];
// 定义一个公共的类属性 closed它是一个布尔值表示是否已关闭。
public closed = false;
// 定义一个公共的类属性 closing它是一个布尔值表示是否正在关闭。
public closing = false;
// 定义一个公共的类属性 copy它的类型为 undefined可能用于存储副本或其他数据。
public copy = undefined;
// 定义一个公共的类属性 onClose它是 onCloseEmitter 的事件,用于订阅关闭事件。
public onClose = this.onCloseEmitter.event;
// 定义一个公共的类属性 onDidChangeFile它是 onDidChangeFileEmitter 的事件,用于订阅文件变化事件。
public onDidChangeFile = this.onDidChangeFileEmitter.event;
// 定义一个受保护的类属性 logging它的类型为 Logger可能用于日志记录。
protected logging: Logger;
/**
* SSH
* @param {string} authority -
* @param {ssh2.SFTP} sftp - ssh2 SFTP
* @param {FileSystemConfig} config -
*/
constructor(public readonly authority: string, protected sftp: ssh2.SFTP, public readonly config: FileSystemConfig) {
// 设置 logging 属性为一个特定作用域的日志记录器,用于记录与当前权限相关的日志信息。
this.logging = Logging.scope(`SSHFileSystem(${authority})`, false);
// 当 SFTP 连接结束时,设置 closed 属性为 true并触发 onCloseEmitter 事件发射器。
this.sftp.on('end', () => (this.closed = true, this.onCloseEmitter.fire()));
// 记录一条信息日志,表示 SSH 文件系统已创建。
this.logging.info('SSHFileSystem created');
// 订阅全局标志,并在标志发生变化时执行相应的逻辑。
const subscription = subscribeToGlobalFlags(() => {
// DEBUG_FS flag, with support for an 'all' alias
this.debugFlags = `${getFlag('DEBUG_FS', this.config.flags)?.[0] || ''}`.toLowerCase().split(/,\s*|\s+/g);
if (this.debugFlags.includes('all')) this.debugFlags.push('showignored', 'full', 'converted');
// FS_NOTIFY_ERRORS flag, with support for a 'write' and 'all' alias, defined in DEBUG_NOTIFY_FLAGS
let notifyErrorFlag: FlagValue = (getFlag('FS_NOTIFY_ERRORS', this.config.flags) || ['write'])[0];
if (notifyErrorFlag === true) notifyErrorFlag = 'all'; // Flag used to be a boolean flag in v1.25.0 and earlier
this.notifyErrorFlags = (typeof notifyErrorFlag === 'string' ? notifyErrorFlag.toLowerCase().split(/,\s*|\s+/g) : []);
for (const flag of this.notifyErrorFlags) {
const alias = DEBUG_NOTIFY_FLAGS[flag];
if (alias) this.notifyErrorFlags.push(...alias);
}
// 获取 DEBUG_FS 标志的值,并处理得到调试标志数组。如果标志包含 'all',则添加一些额外的标志。
this.debugFlags = `${getFlag('DEBUG_FS', this.config.flags)?.[0] || ''}`.toLowerCase().split(/,\s*|\s+/g);
if (this.debugFlags.includes('all')) this.debugFlags.push('showignored', 'full', 'converted');
// FS_NOTIFY_ERRORS flag, with support for a 'write' and 'all' alias, defined in DEBUG_NOTIFY_FLAGS
// 获取 FS_NOTIFY_ERRORS 标志的值,并处理得到通知错误标志数组。支持 'write' 和 'all' 别名,并处理 DEBUG_NOTIFY_FLAGS 中的别名。
let notifyErrorFlag: FlagValue = (getFlag('FS_NOTIFY_ERRORS', this.config.flags) || ['write'])[0];
if (notifyErrorFlag === true) notifyErrorFlag = 'all'; // 在 v1.25.0 及更早版本中,该标志曾是一个布尔值标志。
this.notifyErrorFlags = (typeof notifyErrorFlag === 'string'? notifyErrorFlag.toLowerCase().split(/,\s*|\s+/g) : []);
for (const flag of this.notifyErrorFlags) {
const alias = DEBUG_NOTIFY_FLAGS[flag];
if (alias) this.notifyErrorFlags.push(...alias);
}
});
// 当 onClose 事件触发时,取消订阅全局标志。
this.onClose(() => subscription.dispose());
}
/**
*
* @public
* @method disconnect
* @returns {void}
*/
public disconnect() {
// 设置 closing 属性为 true表示正在关闭连接。
this.closing = true;
// 调用 SFTP 对象的 end 方法来结束 SFTP 连接。
this.sftp.end();
}
/* FileSystemProvider */
/**
* URI
* @public
* @method watch
* @param {vscode.Uri} uri - URI
* @param {Object} options - recursive excludes
* @returns {vscode.Disposable}
*/
public watch(uri: vscode.Uri, options: { recursive: boolean; excludes: string[]; }): vscode.Disposable {
// 当前方法未实现,抛出错误提示。
// throw new Error('Method not implemented.');
// 返回一个空的可处置对象,在实际应用中,这里应该返回一个真正用于取消监视的可处置对象。
return new vscode.Disposable(() => { });
}
/**
* URI
* @public
* @method stat
* @async
* @param {vscode.Uri} uri - URI
* @returns {Promise<vscode.FileStat>} Promise
*/
public async stat(uri: vscode.Uri): Promise<vscode.FileStat> {
// 使用 await 等待一个 Promise该 Promise 通过调用 this.sftp.stat 并传入 uri 的路径和回调函数来创建。
// 如果出现错误,则捕获错误并调用 handleError 方法处理错误,然后将结果作为 never 类型返回。
const stat = await toPromise<ssh2.sftp.Stats>(cb => this.sftp.stat(uri.path, cb))
.catch(e => this.handleError('stat', uri, e, true) as never);
// 从获取到的状态对象中提取 mtime修改时间和 size大小如果不存在则默认为 0。
const { mtime = 0, size = 0 } = stat;
// 初始化文件类型为未知类型。
let type = vscode.FileType.Unknown;
// 以下代码使用位运算来判断文件类型,并设置相应的文件类型标志。
// 如果是文件,则设置文件类型为文件类型标志;如果是目录,则设置为目录类型标志;如果是符号链接,则设置为符号链接类型标志。
// tslint:disable no-bitwise */
if (stat.isFile()) type = type | vscode.FileType.File;
if (stat.isDirectory()) type = type | vscode.FileType.Directory;
if (stat.isSymbolicLink()) type = type | vscode.FileType.SymbolicLink;
// tslint:enable no-bitwise */
// 返回一个包含文件类型、修改时间、大小和创建时间(默认为 0的文件状态对象。
return {
type, mtime, size,
ctime: 0,
type, mtime, size,
ctime: 0,
};
}
/**
* URI
* @public
* @method readDirectory
* @async
* @param {vscode.Uri} uri - URI
* @returns {Promise<[string, vscode.FileType][]>} Promise
*/
public async readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
// 使用 await 等待一个 Promise该 Promise 通过调用 this.sftp.readdir 并传入 uri 的路径和回调函数来创建。
// 如果出现错误,则捕获错误并调用 handleError 方法处理错误,然后将结果作为 never 类型返回。
const entries = await toPromise<ssh2.sftp.DirectoryEntry[]>(cb => this.sftp.readdir(uri.path, cb))
.catch((e) => this.handleError('readDirectory', uri, e, true) as never);
// 使用 Promise.all 并行处理每个目录项,将每个目录项转换为文件名和文件类型的数组。
return Promise.all(entries.map(async (file) => {
const furi = uri.with({ path: `${uri.path}${uri.path.endsWith('/') ? '' : '/'}${file.filename}` });
// Mode in octal representation is 120XXX for links, e.g. 120777
// Any link's mode & 170000 should equal 120000 (using the octal system, at least)
// tslint:disable-next-line:no-bitwise
const link = (file.attrs.mode! & 61440) === 40960 ? vscode.FileType.SymbolicLink : 0;
try {
const type = (await this.stat(furi)).type;
// 根据当前目录的 URI 和目录项的文件名构建文件的 URI。
const furi = uri.with({ path: `${uri.path}${uri.path.endsWith('/') ? '' : '/'}${file.filename}` });
// 判断文件是否为符号链接。通过位运算检查文件的属性模式,确定是否为符号链接,并设置相应的文件类型标志。
// Mode in octal representation is 120XXX for links, e.g. 120777
// Any link's mode & 170000 should equal 120000 (using the octal system, at least)
// tslint:disable-next-line:no-bitwise
return [file.filename, type | link] as [string, vscode.FileType];
} catch (e) {
this.logging.warning.withOptions(LOGGING_SINGLE_LINE_STACKTRACE)`Error in readDirectory for ${furi}: ${e}`;
// tslint:disable-next-line:no-bitwise
return [file.filename, vscode.FileType.Unknown | link] as [string, vscode.FileType];
}
const link = (file.attrs.mode! & 61440) === 40960 ? vscode.FileType.SymbolicLink : 0;
try {
// 调用 stat 方法获取文件的状态,包括文件类型。
const type = (await this.stat(furi)).type;
// tslint:disable-next-line:no-bitwise
// 返回文件名和文件类型(包括可能的符号链接类型)的数组。
return [file.filename, type | link] as [string, vscode.FileType];
} catch (e) {
// 如果获取文件状态时出现错误,记录错误日志,并返回文件名和未知文件类型(包括可能的符号链接类型)的数组。
this.logging.warning.withOptions(LOGGING_SINGLE_LINE_STACKTRACE)`Error in readDirectory for ${furi}: ${e}`;
// tslint:disable-next-line:no-bitwise
return [file.filename, vscode.FileType.Unknown | link] as [string, vscode.FileType];
}
}));
}
/**
*
* @public
* @method createDirectory
* @param {vscode.Uri} uri - URI
* @returns {void | Promise<void>} void Promise void
*/
public createDirectory(uri: vscode.Uri): void | Promise<void> {
// 使用 toPromise 方法将 this.sftp.mkdir 方法包装成一个 Promise该方法用于在 SFTP 连接上创建目录。
// 如果出现错误,则捕获错误并调用 handleError 方法处理错误。
return toPromise<void>(cb => this.sftp.mkdir(uri.path, cb)).catch(e => this.handleError('createDirectory', uri, e, true));
}
/**
* URI
* @public
* @method readFile
* @param {vscode.Uri} uri - URI
* @returns {Uint8Array | Promise<Uint8Array>} Uint8Array Promise Uint8Array
*/
public readFile(uri: vscode.Uri): Uint8Array | Promise<Uint8Array> {
return new Promise((resolve, reject) => {
const stream = this.sftp.createReadStream(uri.path, { autoClose: true });
const bufs = [];
stream.on('data', bufs.push.bind(bufs));
stream.on('error', e => this.handleError('readFile', uri, e, reject));
stream.on('close', () => {
resolve(new Uint8Array(Buffer.concat(bufs)));
});
// 创建一个读取流,用于从 SFTP 连接上读取指定路径的文件内容。
const stream = this.sftp.createReadStream(uri.path, { autoClose: true });
// 存储读取到的文件内容的缓冲区数组。
const bufs = [];
// 当有数据可读时,将数据添加到缓冲区数组中。
stream.on('data', bufs.push.bind(bufs));
// 当读取流发生错误时,调用 handleError 方法处理错误,并将错误传递给 Promise 的 reject 函数。
stream.on('error', e => this.handleError('readFile', uri, e, reject));
// 当读取流关闭时,将缓冲区数组中的内容合并为一个 Uint8Array并将其传递给 Promise 的 resolve 函数。
stream.on('close', () => {
resolve(new Uint8Array(Buffer.concat(bufs)));
});
});
}
/**
* URI
* @public
* @method writeFile
* @param {vscode.Uri} uri - URI
* @param {Uint8Array} content -
* @param {Object} options - create overwrite
* @returns {void | Promise<void>} void Promise void
*/
public writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean; }): void | Promise<void> {
return new Promise(async (resolve, reject) => {
let mode: number | undefined;
let fileExists = false;
try {
const stat = await toPromise<ssh2.sftp.Stats>(cb => this.sftp.stat(uri.path, cb));
mode = stat.mode;
fileExists = true;
} catch (e) {
if (e.message === 'No such file') {
mode = this.config.newFileMode as number;
if (typeof mode === 'string') mode = Number(mode);
if (typeof mode !== 'number') mode = 0o664;
if (Number.isNaN(mode)) throw new Error(`Invalid umask '${this.config.newFileMode}'`);
} else {
this.handleError('writeFile', uri, e);
vscode.window.showWarningMessage(`Couldn't read the permissions for '${uri.path}', permissions might be overwritten`);
// 初始化文件模式变量,用于设置写入文件时的权限。
let mode: number | undefined;
// 标记文件是否已经存在。
let fileExists = false;
try {
// 尝试获取文件的状态信息,包括文件模式等。
const stat = await toPromise<ssh2.sftp.Stats>(cb => this.sftp.stat(uri.path, cb));
mode = stat.mode;
fileExists = true;
} catch (e) {
// 如果文件不存在,根据配置设置新文件的模式,并进行一些类型转换和错误处理。
if (e.message === 'No such file') {
mode = this.config.newFileMode as number;
if (typeof mode === 'string') mode = Number(mode);
if (typeof mode !== 'number') mode = 0o664;
if (Number.isNaN(mode)) throw new Error(`Invalid umask '${this.config.newFileMode}'`);
} else {
// 如果出现其他错误,调用 handleError 方法处理错误。
this.handleError('writeFile', uri, e);
// 显示警告消息,表示无法读取文件权限,权限可能会被覆盖。
vscode.window.showWarningMessage(`Couldn't read the permissions for '${uri.path}', permissions might be overwritten`);
}
}
}
const stream = this.sftp.createWriteStream(uri.path, { mode, flags: 'w' });
stream.on('error', e => this.handleError('writeFile', uri, e, reject));
stream.end(content, () => {
this.onDidChangeFileEmitter.fire([{ uri, type: fileExists ? vscode.FileChangeType.Changed : vscode.FileChangeType.Created }]);
resolve();
});
// 创建一个写入流,用于将内容写入指定路径的文件,并设置文件模式和写入标志。
const stream = this.sftp.createWriteStream(uri.path, { mode, flags: 'w' });
// 当写入流发生错误时,调用 handleError 方法处理错误,并将错误传递给 Promise 的 reject 函数。
stream.on('error', e => this.handleError('writeFile', uri, e, reject));
// 当写入流结束时,触发文件变化事件,并将 Promise 解析为完成状态。
stream.end(content, () => {
this.onDidChangeFileEmitter.fire([{ uri, type: fileExists ? vscode.FileChangeType.Changed : vscode.FileChangeType.Created }]);
resolve();
});
});
}
/**
* URI
* @public
* @method delete
* @async
* @param {vscode.Uri} uri - URI
* @param {Object} options - recursive
* @returns {Promise<any>} Promise
*/
public async delete(uri: vscode.Uri, options: { recursive: boolean; }): Promise<any> {
// 获取指定 URI 的文件状态。
const stats = await this.stat(uri);
// 定义一个函数,用于触发文件删除事件。
const fireEvent = () => this.onDidChangeFileEmitter.fire([{ uri, type: vscode.FileChangeType.Deleted }]);
// 如果是文件或符号链接,则执行 unlink 操作进行删除。
// tslint:disable no-bitwise */
if (stats.type & (vscode.FileType.SymbolicLink | vscode.FileType.File)) {
return toPromise(cb => this.sftp.unlink(uri.path, cb))
.then(fireEvent).catch(e => this.handleError('delete', uri, e, true));
return toPromise(cb => this.sftp.unlink(uri.path, cb))
.then(fireEvent).catch(e => this.handleError('delete', uri, e, true));
} else if ((stats.type & vscode.FileType.Directory) && options.recursive) {
return toPromise(cb => this.sftp.rmdir(uri.path, cb))
.then(fireEvent).catch(e => this.handleError('delete', uri, e, true));
// 如果是目录且 recursive 为 true则执行 rmdir 操作进行删除。
return toPromise(cb => this.sftp.rmdir(uri.path, cb))
.then(fireEvent).catch(e => this.handleError('delete', uri, e, true));
}
// 如果不是上述情况,也尝试执行 unlink 操作进行删除。
return toPromise(cb => this.sftp.unlink(uri.path, cb))
.then(fireEvent).catch(e => this.handleError('delete', uri, e, true));
// tslint:enable no-bitwise */
}
/**
*
* @public
* @method rename
* @param {vscode.Uri} oldUri - URI
* @param {vscode.Uri} newUri - URI
* @param {Object} options - overwrite
* @returns {void | Promise<void>} void Promise void
*/
public rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): void | Promise<void> {
// 使用 toPromise 方法将 this.sftp.rename 方法包装成一个 Promise该方法用于在 SFTP 连接上重命名文件或目录。
return toPromise<void>(cb => this.sftp.rename(oldUri.path, newUri.path, cb))
.then(() => this.onDidChangeFileEmitter.fire([
{ uri: oldUri, type: vscode.FileChangeType.Deleted },
{ uri: newUri, type: vscode.FileChangeType.Created }
]))
.then(() => {
// 当重命名成功后,触发文件变化事件,表示旧资源被删除,新资源被创建。
this.onDidChangeFileEmitter.fire([
{ uri: oldUri, type: vscode.FileChangeType.Deleted },
{ uri: newUri, type: vscode.FileChangeType.Created }
]);
})
.catch(e => this.handleError('rename', newUri, e, true));
}
// Helper function to handle/report errors with proper (and minimal) stacktraces and such
protected handleError(method: string, uri: vscode.Uri, e: Error & { code?: any }, doThrow: (boolean | ((error: any) => void)) = false): any {
const ignore = e.code === 2 && [method === 'stat', shouldIgnoreNotFound(uri.path)];
if (ignore && ignore.includes(true) && !this.debugFlags.includes('disableignored')) {
/**
*
* @protected
* @method handleError
* @param {string} method -
* @param {vscode.Uri} uri - URI
* @param {Error & { code?: any }} e -
* @param {boolean | ((error: any) => void)} doThrow - false
* @returns {any} doThrow false undefined
*/
protected handleError(method: string, uri: vscode.Uri, e: Error & { code?: any }, doThrow: (boolean | ((error: any) => void)) = false): any {
// 判断是否应该忽略错误。如果错误代码为 2通常表示文件不存在且满足特定条件则进行忽略处理。
const ignore = e.code === 2 && [method === 'stat', shouldIgnoreNotFound(uri.path)];
if (ignore && ignore.includes(true) && !this.debugFlags.includes('disableignored')) {
e = vscode.FileSystemError.FileNotFound(uri);
// Whenever a workspace opens, VSCode (and extensions) (indirectly) stat a bunch of files
// (.vscode/tasks.json etc, .git/, node_modules for NodeJS, pom.xml for Maven, ...)
// 如果开启了显示被忽略错误的调试标志,则记录被忽略的错误信息。
if (this.debugFlags.includes('showignored')) {
const flags = `${ignore[0] ? 'F' : ''}${ignore[1] ? 'A' : ''}`;
this.logging.debug(`Ignored (${flags}) FileNotFound error for ${method}: ${uri}`, LOGGING_NO_STACKTRACE);
const flags = `${ignore[0] ? 'F' : ''}${ignore[1] ? 'A' : ''}`;
this.logging.debug(`Ignored (${flags}) FileNotFound error for ${method}: ${uri}`, LOGGING_NO_STACKTRACE);
}
// 如果 doThrow 为 true则抛出错误如果 doThrow 是一个函数,则将错误传递给该函数;否则返回 undefined。
if (doThrow === true) throw e; else if (doThrow) return doThrow(e); else return;
}
else if (this.debugFlags.includes('full')) {
} else if (this.debugFlags.includes('full')) {
// 如果开启了完整错误日志的调试标志则记录详细的错误信息包括方法名、URI 和错误对象。
this.logging.debug.withOptions(LOGGING_HANDLE_ERROR)`Error during ${method} ${uri}: ${e}`;
} else if (this.debugFlags.includes('minimal')) {
} else if (this.debugFlags.includes('minimal')) {
// 如果开启了最小错误日志的调试标志则记录简化的错误信息包括方法名、URI、错误名称和错误消息。
this.logging.debug.withOptions({ ...LOGGING_NO_STACKTRACE, maxErrorStack: 0 })`Error during ${method} ${uri}: ${e.name}: ${e.message}`;
}
// Convert SSH2Stream error codes into VS Code errors
if (doThrow && typeof e.code === 'number') {
}
// Convert SSH2Stream error codes into VS Code errors
// 如果 doThrow 为 true 且错误对象有错误代码,则将 SSH2Stream 的错误代码转换为 VS Code 的错误类型。
if (doThrow && typeof e.code === 'number') {
const oldE = e;
if (e.code === 2) { // No such file or directory
e = vscode.FileSystemError.FileNotFound(uri);
e = vscode.FileSystemError.FileNotFound(uri);
} else if (e.code === 3) { // Permission denied
e = vscode.FileSystemError.NoPermissions(uri);
e = vscode.FileSystemError.NoPermissions(uri);
} else if (e.code === 6) { // No connection
e = vscode.FileSystemError.Unavailable(uri);
e = vscode.FileSystemError.Unavailable(uri);
} else if (e.code === 7) { // Connection lost
e = vscode.FileSystemError.Unavailable(uri);
e = vscode.FileSystemError.Unavailable(uri);
}
// 如果错误被转换且开启了转换错误的调试标志,则记录转换后的错误信息。
if (e !== oldE && this.debugFlags.includes('converted'))
Logging.debug(`Error converted to: ${e}`);
}
// Display an error notification if the FS_ERROR_NOTIFICATION flag is enabled
if (this.notifyErrorFlags.includes(method.toLowerCase())) {
Logging.debug(`Error converted to: ${e}`);
}
// Display an error notification if the FS_ERROR_NOTIFICATION flag is enabled
// 如果开启了通知错误的标志且当前错误方法在通知错误列表中,则显示错误消息通知。
if (this.notifyErrorFlags.includes(method.toLowerCase())) {
vscode.window.showErrorMessage(`Error handling ${method} for: ${uri}\n${e.message || e}`);
}
if (doThrow === true) throw e;
if (doThrow) return doThrow(e);
}
// 如果 doThrow 为 true则抛出错误如果 doThrow 是一个函数,则将错误传递给该函数。
if (doThrow === true) throw e;
if (doThrow) return doThrow(e);
}
}

Loading…
Cancel
Save