|
|
|
@ -1,30 +1,75 @@
|
|
|
|
|
// 导入 VSCode 模块,用于访问 VSCode 的 API
|
|
|
|
|
import * as vscode from 'vscode';
|
|
|
|
|
// 从 './flags' 模块中导入 getFlag 和 subscribeToGlobalFlags 函数
|
|
|
|
|
import { getFlag, subscribeToGlobalFlags } from './flags';
|
|
|
|
|
// 从 './logging' 模块中导入 Logging 类
|
|
|
|
|
import { Logging } from './logging';
|
|
|
|
|
// 从 './manager' 模块中导入 Manager 类型
|
|
|
|
|
import type { Manager } from './manager';
|
|
|
|
|
|
|
|
|
|
// 定义一个包含所有可能的调试标志的数组
|
|
|
|
|
const ALL_DEBUG_FLAGS = [
|
|
|
|
|
'stat', 'readDirectory', 'createDirectory',
|
|
|
|
|
'readFile', 'writeFile', 'delete', 'rename',
|
|
|
|
|
].map(v => v.toLowerCase());
|
|
|
|
|
// 文件状态检查操作的调试标志
|
|
|
|
|
'stat',
|
|
|
|
|
// 读取目录操作的调试标志
|
|
|
|
|
'readDirectory',
|
|
|
|
|
// 创建目录操作的调试标志
|
|
|
|
|
'createDirectory',
|
|
|
|
|
// 读取文件操作的调试标志
|
|
|
|
|
'readFile',
|
|
|
|
|
// 写入文件操作的调试标志
|
|
|
|
|
'writeFile',
|
|
|
|
|
// 删除文件或目录操作的调试标志
|
|
|
|
|
'delete',
|
|
|
|
|
// 重命名文件或目录操作的调试标志
|
|
|
|
|
'rename',
|
|
|
|
|
].map(v => v.toLowerCase()); // 将所有标志转换为小写形式,以便于比较和匹配
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* FileSystemRouter 类实现了 vscode.FileSystemProvider 接口,用于处理文件系统的各种操作
|
|
|
|
|
*/
|
|
|
|
|
export class FileSystemRouter implements vscode.FileSystemProvider {
|
|
|
|
|
// 定义一个事件,用于通知文件系统的变化
|
|
|
|
|
public onDidChangeFile: vscode.Event<vscode.FileChangeEvent[]>;
|
|
|
|
|
// 定义一个事件发射器,用于发射文件系统变化的事件
|
|
|
|
|
protected onDidChangeFileEmitter = new vscode.EventEmitter<vscode.FileChangeEvent[]>();
|
|
|
|
|
// 定义一个数组,用于存储调试标志
|
|
|
|
|
protected debugFlags: string[];
|
|
|
|
|
/**
|
|
|
|
|
* 构造函数,初始化 FileSystemRouter 类的实例
|
|
|
|
|
* @param manager - Manager 类的实例,用于管理文件系统
|
|
|
|
|
*/
|
|
|
|
|
constructor(protected readonly manager: Manager) {
|
|
|
|
|
// 将 onDidChangeFileEmitter 的事件绑定到 onDidChangeFile
|
|
|
|
|
this.onDidChangeFile = this.onDidChangeFileEmitter.event;
|
|
|
|
|
// 订阅全局标志,当全局标志发生变化时,更新 debugFlags
|
|
|
|
|
subscribeToGlobalFlags(() => {
|
|
|
|
|
// 获取 DEBUG_FSR 标志的值,并将其转换为小写,然后分割成数组
|
|
|
|
|
this.debugFlags = `${getFlag('DEBUG_FSR')?.[0] || ''}`.toLowerCase().split(/,\s*|\s+/g);
|
|
|
|
|
// 如果 debugFlags 中包含 'all',则将 ALL_DEBUG_FLAGS 中的所有标志添加到 debugFlags 中
|
|
|
|
|
if (this.debugFlags.includes('all')) this.debugFlags.push(...ALL_DEBUG_FLAGS);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 断言文件系统是否存在,如果不存在则创建一个新的文件系统
|
|
|
|
|
* @param uri - 文件系统的 URI
|
|
|
|
|
* @returns 文件系统的实例
|
|
|
|
|
*/
|
|
|
|
|
public async assertFs(uri: vscode.Uri): Promise<vscode.FileSystemProvider> {
|
|
|
|
|
// 从 manager 中获取文件系统实例
|
|
|
|
|
const fs = this.manager.getFs(uri);
|
|
|
|
|
// 如果文件系统实例存在,则返回该实例
|
|
|
|
|
if (fs) return fs;
|
|
|
|
|
// 如果文件系统实例不存在,则创建一个新的文件系统实例并返回
|
|
|
|
|
return this.manager.createFileSystem(uri.authority);
|
|
|
|
|
}
|
|
|
|
|
/* FileSystemProvider */
|
|
|
|
|
/**
|
|
|
|
|
* 监视文件系统的变化
|
|
|
|
|
* @param uri - 文件系统的 URI
|
|
|
|
|
* @param options - 监视选项
|
|
|
|
|
* @returns 一个可取消的订阅,用于停止监视
|
|
|
|
|
*/
|
|
|
|
|
public watch(uri: vscode.Uri, options: { recursive: boolean; excludes: string[]; }): vscode.Disposable {
|
|
|
|
|
// TODO: Store watched files/directories in an array and periodically check if they're modified
|
|
|
|
|
/*let disp = () => {};
|
|
|
|
@ -34,42 +79,98 @@ export class FileSystemRouter implements vscode.FileSystemProvider {
|
|
|
|
|
return new vscode.Disposable(() => disp());*/
|
|
|
|
|
return new vscode.Disposable(() => { });
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 获取文件或目录的状态
|
|
|
|
|
* @param uri - 文件或目录的 URI
|
|
|
|
|
* @returns 文件或目录的状态
|
|
|
|
|
*/
|
|
|
|
|
public async stat(uri: vscode.Uri): Promise<vscode.FileStat> {
|
|
|
|
|
// 如果 debugFlags 中包含 'stat',则输出调试信息
|
|
|
|
|
if (this.debugFlags.includes('stat'))
|
|
|
|
|
Logging.debug`Performing stat for ${uri}`;
|
|
|
|
|
// 获取文件系统实例,并调用其 stat 方法
|
|
|
|
|
return (await this.assertFs(uri)).stat(uri);
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 读取目录的内容
|
|
|
|
|
* @param uri - 目录的 URI
|
|
|
|
|
* @returns 目录内容的数组,每个元素包含文件名和文件类型
|
|
|
|
|
*/
|
|
|
|
|
public async readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
|
|
|
|
|
// 如果 debugFlags 中包含 'readdirectory',则输出调试信息
|
|
|
|
|
if (this.debugFlags.includes('readdirectory'))
|
|
|
|
|
Logging.debug`Reading directory ${uri}`;
|
|
|
|
|
// 获取文件系统实例,并调用其 readDirectory 方法
|
|
|
|
|
return (await this.assertFs(uri)).readDirectory(uri);
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 创建目录
|
|
|
|
|
* @param uri - 目录的 URI
|
|
|
|
|
* @returns 一个 Promise,当目录创建成功时解析
|
|
|
|
|
*/
|
|
|
|
|
public async createDirectory(uri: vscode.Uri): Promise<void> {
|
|
|
|
|
// 如果 debugFlags 中包含 'createdirectory',则输出调试信息
|
|
|
|
|
if (this.debugFlags.includes('createdirectory'))
|
|
|
|
|
Logging.debug`Creating directory ${uri}`;
|
|
|
|
|
// 获取文件系统实例,并调用其 createDirectory 方法
|
|
|
|
|
return (await this.assertFs(uri)).createDirectory(uri);
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 读取文件的内容
|
|
|
|
|
* @param uri - 文件的 URI
|
|
|
|
|
* @returns 文件内容的 Uint8Array
|
|
|
|
|
*/
|
|
|
|
|
public async readFile(uri: vscode.Uri): Promise<Uint8Array> {
|
|
|
|
|
// 如果 debugFlags 中包含 'readfile',则输出调试信息
|
|
|
|
|
if (this.debugFlags.includes('readfile'))
|
|
|
|
|
Logging.debug`Reading ${uri}`;
|
|
|
|
|
// 获取文件系统实例,并调用其 readFile 方法
|
|
|
|
|
return (await this.assertFs(uri)).readFile(uri);
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 写入文件的内容
|
|
|
|
|
* @param uri - 文件的 URI
|
|
|
|
|
* @param content - 要写入的内容
|
|
|
|
|
* @param options - 写入选项
|
|
|
|
|
* @returns 一个 Promise,当文件写入成功时解析
|
|
|
|
|
*/
|
|
|
|
|
public async writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean; }): Promise<void> {
|
|
|
|
|
// 如果 debugFlags 中包含 'writefile',则输出调试信息
|
|
|
|
|
if (this.debugFlags.includes('writefile'))
|
|
|
|
|
Logging.debug`Writing ${content.length} bytes to ${uri} (options: ${options})`;
|
|
|
|
|
// 获取文件系统实例,并调用其 writeFile 方法
|
|
|
|
|
return (await this.assertFs(uri)).writeFile(uri, content, options);
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 删除文件或目录
|
|
|
|
|
* @param uri - 文件或目录的 URI
|
|
|
|
|
* @param options - 删除选项
|
|
|
|
|
* @returns 一个 Promise,当文件或目录删除成功时解析
|
|
|
|
|
*/
|
|
|
|
|
public async delete(uri: vscode.Uri, options: { recursive: boolean; }): Promise<void> {
|
|
|
|
|
// 如果 debugFlags 中包含 'delete',则输出调试信息
|
|
|
|
|
if (this.debugFlags.includes('delete'))
|
|
|
|
|
Logging.debug`Deleting ${uri} (options: ${options})`;
|
|
|
|
|
// 获取文件系统实例,并调用其 delete 方法
|
|
|
|
|
return (await this.assertFs(uri)).delete(uri, options);
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 重命名文件或目录
|
|
|
|
|
* @param oldUri - 旧的 URI
|
|
|
|
|
* @param newUri - 新的 URI
|
|
|
|
|
* @param options - 重命名选项
|
|
|
|
|
* @returns 一个 Promise,当文件或目录重命名成功时解析
|
|
|
|
|
*/
|
|
|
|
|
public async rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): Promise<void> {
|
|
|
|
|
// 如果 debugFlags 中包含 'rename',则输出调试信息
|
|
|
|
|
if (this.debugFlags.includes('rename'))
|
|
|
|
|
Logging.debug`Renaming ${oldUri} to ${newUri}`;
|
|
|
|
|
// 获取旧的文件系统实例
|
|
|
|
|
const fs = await this.assertFs(oldUri);
|
|
|
|
|
// 如果新的文件系统实例与旧的不同,则抛出错误
|
|
|
|
|
if (fs !== (await this.assertFs(newUri)))
|
|
|
|
|
throw new Error(`Can't rename between different SSH filesystems`);
|
|
|
|
|
// 调用文件系统实例的 rename 方法
|
|
|
|
|
return fs.rename(oldUri, newUri, options);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|