首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

开发共享vscode文件书签插件

  • 25-04-18 18:21
  • 4742
  • 11654
juejin.cn

介绍

目前工作上开发的项目很大,找文件很辛苦,所以想找一个文件书签的vscode插件,但看了一圈,发现目前市面上的vscode文件书签插件功能缺少分组(文件夹)、共享书签功能,因此有了开发File-Keeper插件的想法。大致上是基于Explorer-Bookmark做了分组、拖拽、导入导出功能,方便在团队中复用该插件。

功能演示

在项目文件点击右键进行加入书签 add.png

通过新增目录与拖拽文件,整理书签 detail.png

导出书签数据,可以提供给他人复用 exports.png

功能分划

加入书签功能

在项目目录中需增加右键加入书签功能

书签展示

新建一个视图展示书签列表

点击书签

点击书签打开指定文件

书签备注功能

书签新增备注,方便用户识别文件

新建目录与拖拽节点功能

提供书签分组与移动分组功能

导入导出功能

方便多人使用书签,可共享部分或全部书签

删除书签/目录功能

提供删除功能,并添加确认提示

书签本地存储

防止在vscode关闭时书签丢失

技术方案确认

vscode插件提供了 Tree View API。方便展示树状结构的数据,还可以设置右键菜单与视图右上角菜单。

在Tree View中,每个节点是按照vscode.TreeItem的格式展示的,其中包括了id、label、command(点击节点后执行的命令)、tooltip等功能。所以在需要展示时,生成vscode.TreeItem格式的列表即可。

而我们使用时,还需要用一个自定义列表bookmarkedDirectories存储节点信息,这个列表将存储在用户本地,导出导入功能借助这个列表即可丝滑同步。

每次用户操作后更新bookmarkedDirectories列表,并在每次操作更新后刷新视图,读取bookmarkedDirectories列表生成vscode.TreeItem的数据结构返回到视图中。

技术实现

树状视图

可以使用内置方法vscode.window.createTreeView创建树状视图,其中可入参配置树状视图数据管理、拖拽管理等方法。

js
代码解读
复制代码
const filerKeeperWorker = new FileKeeperWorker( context, vscode.workspace.workspaceFolders ); const fileKeeperDataProvider = new FileKeeperDataProvider( filerKeeperWorker ); this.ftpViewer = vscode.window.createTreeView("file-keeper", { treeDataProvider: fileKeeperDataProvider, dragAndDropController: fileKeeperDataProvider, });

树状视图数据管理可以基于TreeDataProvider实现,必须包含getChildren方法,这个方案就是用于展示数据,返回vscode.TreeItem格式的列表,如果是打开目录,就会入参vscode.TreeItem格式的数据,需要返回该目录下一级的数据。

js
代码解读
复制代码
// 展示子节点 public async getChildren( element?: FileSystemObject ): Promise<FileSystemObject[]> { let children: TypedDirectory[] = []; if (element) { const childrenIds = this.bookmarkedDirectoriesMap.get(element.id)?.children || []; children = childrenIds .map((child) => { return this.bookmarkedDirectoriesMap.get(child); }) .filter(Boolean) as TypedDirectory[]; // return this.directorySearch(element.resourceUri); } else { children = this.bookmarkedDirectories.filter((dir) => !dir.parent); } return children.length > 0 ? this.createEntries(children) : Promise.resolve([]); } // 生成文件列表 private async createEntries(bookmarkedDirectories: TypedDirectory[]) { let fileSystem: FileSystemObject[] = []; for (const dir of bookmarkedDirectories) { const { path: filePath, type: type, id, parent, label } = dir; const file = vscode.Uri.file(filePath); let fileLabel: vscode.TreeItemLabel | string = ""; // 存在备注的文件节点名称:备注(文件名) if (label && type === vscode.FileType.File) { const filename = `${path.basename(dir.path)}`; const longLabel = `${label} (${filename})`; fileLabel = { label: longLabel, highlights: [[label.length + 2, longLabel.length - 1]], }; } else { fileLabel = label || `${path.basename(dir.path)}`; } fileSystem.push( new FileSystemObject({ id, parent, label: fileLabel, collapsibleState: type === vscode.FileType.File ? vscode.TreeItemCollapsibleState.None : vscode.TreeItemCollapsibleState.Expanded, uri: file, }).setContextValue(this.bookmarkedDirectoryContextValue) ); } return fileSystem; }

拖拽管理可以基于TreeDragAndDropController实现,必须包含dragMimeTypes、dropMimeTypes参数与handleDrag、handleDrop方法,dragMimeTypes声明了可拖拽的类型(具体节点类型可以通过Debug模式查看),dropMimeTypes是用于拖拽过程中获取拖拽过程的数据。handleDrag开始拖拽节点,handleDrop放置节点。

js
代码解读
复制代码
// Drag and drop controller // 放置节点,移动整个节点树 public async handleDrop( target: FileSystemObject | undefined, sources: vscode.DataTransfer, _token: vscode.CancellationToken ): Promise<void> { const transferItem = sources.get( "application/vnd.code.tree.fileKeeperViewDragAndDrop" ); if (!transferItem) { return; } const treeItems: FileSystemObject[] = transferItem.value; let newParent: TypedDirectory | undefined; if (target?.collapsibleState === vscode.TreeItemCollapsibleState.None) { newParent = target.parent ? this.bookmarkedDirectoriesMap.get(target.parent) : undefined; } else if (target?.collapsibleState) { newParent = this.bookmarkedDirectoriesMap.get(target.id); } treeItems.forEach((item) => { const typedDirectory = this.bookmarkedDirectoriesMap.get(item.id); if (typedDirectory && item.id !== newParent?.id) { // Remove from previous parent if (item.parent) { const typedDirectoryParent = this.bookmarkedDirectoriesMap.get( item.parent ); if (typedDirectoryParent?.children?.length) { typedDirectoryParent.children.splice( typedDirectoryParent.children.indexOf(typedDirectory.id), 1 ); } } typedDirectory.parent = newParent?.id; newParent?.children.push(typedDirectory.id); } }); this.saveBookmarks(); } // 拖拽节点,将节点信息存储在 DataTransfer 中 public async handleDrag( source: FileSystemObject[], treeDataTransfer: vscode.DataTransfer, _token: vscode.CancellationToken ): Promise<void> { treeDataTransfer.set( "application/vnd.code.tree.fileKeeperViewDragAndDrop", new vscode.DataTransferItem(source) ); }

导出功能需要遍历节点树后,利用vscode.workspace.openTextDocument与vscode.window.showTextDocument展示输出数据。而导入时需注意在部分修改的节点应该如何处理。对于文件的地址也需要对项目根地址vscode.workspace.workspaceFolders做替换操作,以免在不同电脑上找不到对应位置。

js
代码解读
复制代码
// 根据节点遍历出所有子节点 getTypedDirectoryTree(element: TypedDirectory) { let children: TypedDirectory[] = []; if (element) { const childrenIds = element.children || []; childrenIds.forEach((child) => { const el = this.bookmarkedDirectoriesMap.get(child); if (el) { children = [...children, ...this.getTypedDirectoryTree(el)]; } }); } return [element, ...children]; } // 导出文件书签 public async exportBookmarks(element?: FileSystemObject) { let list: TypedDirectory[] = this.bookmarkedDirectories; if (element) { const typedDirectory = this.bookmarkedDirectoriesMap.get(element.id); if (typedDirectory) { list = this.getTypedDirectoryTree(typedDirectory); } } // 获取当前工作区的文件夹 const workspaceFolders = vscode.workspace.workspaceFolders; if (workspaceFolders && workspaceFolders.length > 0) { // 获取第一个工作区文件夹的路径 const projectPath = workspaceFolders[0].uri.fsPath; list = list.map((dir) => { const path = dir.path?.replace(projectPath, PROJECT_PATH); return { ...dir, path }; }); } const contentInfo = JSON.stringify(list, undefined, 2); // 创建一个新的未保存的文件 const document = await vscode.workspace.openTextDocument({ content: contentInfo, }); vscode.env.clipboard.writeText(contentInfo); // 显示该文件 await vscode.window.showTextDocument(document); vscode.window.showInformationMessage("信息已经复制至剪切版"); } // 导入文件书签 public async importBookmarks() { // 获取当前工作区的文件夹 const workspaceFolders = vscode.workspace.workspaceFolders; if (workspaceFolders && workspaceFolders.length > 0) { try { const clipboardContent = await vscode.env.clipboard.readText(); const newList = JSON.parse(clipboardContent) as TypedDirectory[]; if (Array.isArray(newList)) { newList.forEach((dir) => { if (!this.bookmarkedDirectoriesMap.has(dir.id)) { if (dir.path && dir.path.startsWith(PROJECT_PATH)) { const projectPath = workspaceFolders[0].uri.fsPath; dir.path = dir.path.replace(PROJECT_PATH, projectPath); } // 子节点的位置被变换,不做移动 if (dir?.children?.length) { dir.children = dir.children.filter((child) => { const childTypeDir = this.bookmarkedDirectoriesMap.get(child); return childTypeDir?.parent === dir.id; }); } // 如果有父节点,将子节点添加到父节点的 children 中 if (dir.parent) { const parentTypeDir = this.bookmarkedDirectoriesMap.get(dir.parent); if (parentTypeDir) { if (!parentTypeDir.children?.length) { parentTypeDir.children = [dir.id]; } else if (!parentTypeDir.children.includes(dir.id)) { parentTypeDir.children.push(dir.id); } } } const typedDirectory = new TypedDirectory(dir); this.bookmarkedDirectories.push(typedDirectory); this.bookmarkedDirectoriesMap.set( typedDirectory.id, typedDirectory ); } }); this.saveBookmarks(); } } catch (e) { vscode.window.showErrorMessage("Invalid JSON content"); } } else { vscode.window.showErrorMessage("项目不存在"); } }

package.json规则:

contributes.views.explorer 注册窗口

contributes.commands 命令列表,在此注册的命令后续才能被其他规则使用

contributes.menus.explorer/context 资源管理器的右键菜单

contributes.menus.view/title 自定义视口的右上角菜单

contributes.menus.view/item/context 自定义的右键菜单

技术要点

  1. 将bookmarkedDirectories列表数据存储在的Map中,方便后续查询元素
  2. 每次删除、移动操作时,需修改父级节点的children参数
  3. 在view/title定义的指令也会入参当前选中的节点参数,所以如需要不受影响,应新增一个无入参的命令
  4. vscode.window.showWarningMessage 打开确认弹窗
  5. vscode.window.showInputBox 打开输入框
  6. vscode.window.showErrorMessage 提示
  7. vscode.env.clipboard.writeText/readText 剪切板写/读
  8. vscode.workspace.openTextDocument 创建一个新的未保存的文件

总结

由于是第一次开发vscode插件,对于vscode的语法不熟悉,但是在github copilot协助下,开发、调研时间大大缩短,在前期调研时通过功能寻找相似插件,在开发中对遇到的错误,api都可以获得有效解答。

copilot.png

此外通过stable-diffusion,也可以快速地生成一个图标。

fk-logo.png

参考链接

code.visualstudio.com/api/extensi…

code.visualstudio.com/api/referen…

github.com/microsoft/v…

github.com/UrosVuj/Exp…

注:本文转载自juejin.cn的深海丧鱼的文章"https://juejin.cn/post/7493964577256849459"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

133
开发工具
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top