还在cv复用代码片段?试试自己写一个vscode插件来管理代码片段

126 阅读5分钟

说在前面

🤯平时我们写代码的时候总是少不了cv,cv的时候还要先去找到旧的代码片段,然后将其复制过来,这样操作还是显得有些繁琐,针对这个问题,我们可以写一个代码片段保存复用 vscode 插件,支持同步代码片段到自己指定的 gitee 仓库,实现在不同电脑上 vscode 中共享代码片段📑

image.png

一、准备工作

新建 gitee 仓库

直接在gitee上新建仓库即可。

我们不想要书签信息公开,所以选择勾选上私有:

image.png

创建完的初始仓库是这样的:

image.png

我们再新增一个目录codeSnippets,用于存放代码片段相关的文件:

image.png

在该目录下新增一个文件snippets.json,用于保存代码片段数据:

image.png

二、插件实现

1、环境准备

确保你已经安装了 Node.js 和 npm(Node.js 包管理器),因为 VSCode 插件是使用 JavaScript 或 TypeScript 编写的,并且依赖于这些工具进行开发和构建。

2、生成插件项目脚手架

  • 可以使用 Yeoman 和 VSCode Extension Generator 来快速生成插件项目的基础结构。首先,全局安装 Yeoman 和 VSCode Extension Generator:
npm install -g yo generator-code
  • 然后,在你想要创建插件项目的目录下,运行以下命令来生成项目脚手架:
yo code

这会启动一个交互式的向导,按提示填写信息即可:

3、gitee仓库配置

(1)注册命令

const giteeConfigDisposable = vscode.commands.registerCommand( "saveCodeSnippetsAndReusePlugins.giteeConfig", async () => { // 后续具体操作逻辑在此处 } ); context.subscriptions.push(giteeConfigDisposable);

通过 vscode.commands.registerCommand 注册了一个名为 "saveCodeSnippetsAndReusePlugins.giteeConfig" 的命令。当这个命令在 VSCode 中被触发时,会执行后续定义的异步函数中的操作。

(2)获取gitee配置信息

async function getGiteeConfig(context) {
  const gitConfigFilePath = path.join(
    context.extensionPath,
    "config/gitConfig.json"
  );
  let gitConfigData = {};
  if (fs.existsSync(gitConfigFilePath)) {
    gitConfigData = JSON.parse(
      fs.readFileSync(gitConfigFilePath, "utf8") || "{}"
    );
  }
  return gitConfigData;
}

读取本地配置记录文件,获取旧的配置信息。

(3)获取用户输入配置信息

async function getStr(name, value = "") {
  const token = await vscode.window.showInputBox({
    placeHolder: `请输入${name}`,
    value,
  });
  return token;
}
const gitConfigData = await getGiteeConfig(context);
const token = await getStr("token", gitConfigData.token);
if (!token) {
vscode.window.showInformationMessage(`请输入token`);
return;
}
const owner = await getStr("owner", gitConfigData.owner);
if (!owner) {
vscode.window.showInformationMessage(`请输入owner`);
return;
}
const repo = await getStr("仓库名", gitConfigData.repo);
if (!repo) {
vscode.window.showInformationMessage(`请输入仓库名`);
return;
}

先获取本地配置记录,获取旧的配置信息,有配置信息的话会在用户输入框默认回填。

(4)保存gitee配置信息

async function saveGiteeConfig(gitConfigData, context) {
  const gitConfigFilePath = path.join(
    context.extensionPath,
    "config/gitConfig.json"
  );
  fs.writeFileSync(
    gitConfigFilePath,
    JSON.stringify(gitConfigData, null, 2),
    "utf8"
  );
  vscode.window.showInformationMessage(`gitee配置已成功保存!`);
}
await saveGiteeConfig(
    {
      token,
      owner,
      repo,
    },
    context
);

将gitee配置信息保存到本地配置文件中去。

(5)同步仓库代码片段

const url = `    https://gitee.com/api/v5/repos/${owner}/${repo}/contents/codeSnippets/snippets.json`;
  const file = await fetchFileContent(url, token);
  if (file.message) {
    vscode.window.showInformationMessage(
      `${file.message},获取gitee代码片段出错,请检查gitee配置信息`
    );
    return;
  }
  const fileContent = file.content || "";
  const content = await getDecodedContent(fileContent);
  const snippetsFilePath = path.join(
    context.extensionPath,
    "config/snippets.json"
  );
  const snippetsData = JSON.parse(
    fs.readFileSync(snippetsFilePath, "utf8")
  );
  fs.writeFileSync(
    snippetsFilePath,
    JSON.stringify(Object.assign(content, snippetsData), null, 2),
    "utf8"
  );

  const modifiedContent = JSON.stringify(
    Object.assign(content, snippetsData),
    null,
    2
  );
  const encoder = new TextEncoder();
  const data = encoder.encode(modifiedContent);
  const encodedContent = btoa(
    String.fromCharCode.apply(null, new Uint8Array(data))
  );
  await putFileContent(
    url,
    token,
    encodedContent,
    file.sha,
    "更新代码片段"
  );
  vscode.window.showInformationMessage("已同步gitee最新代码片段");

根据输入的gitee配置信息去获取gitee仓库中的代码片段,并将本地代码片段与仓库中的代码进行合并,合并后同步更新本地和仓库的代码片段。

4、代码片段同步

function codeSnippetSynchronization(context) {
  const codeSnippetSynchronizationDisposable = vscode.commands.registerCommand(
    "saveCodeSnippetsAndReusePlugins.codeSnippetSynchronization",
    async () => {
      const gitConfigData = await getGiteeConfig(context);
      const { owner, repo, token } = gitConfigData;
      const url = `https://gitee.com/api/v5/repos/${owner}/${repo}/contents/codeSnippets/snippets.json`;
      const file = await fetchFileContent(url, token);
      if (file.message) {
        vscode.window.showInformationMessage(
          `${file.message},获取gitee代码片段出错,请检查gitee配置信息`
        );
        return;
      }
      const fileContent = file.content || "";
      const content = await getDecodedContent(fileContent);
      const snippetsFilePath = path.join(
        context.extensionPath,
        "config/snippets.json"
      );
      const snippetsData = JSON.parse(
        fs.readFileSync(snippetsFilePath, "utf8")
      );
      fs.writeFileSync(
        snippetsFilePath,
        JSON.stringify(Object.assign(content, snippetsData), null, 2),
        "utf8"
      );

      const modifiedContent = JSON.stringify(
        Object.assign(content, snippetsData),
        null,
        2
      );
      const encoder = new TextEncoder();
      const data = encoder.encode(modifiedContent);
      const encodedContent = btoa(
        String.fromCharCode.apply(null, new Uint8Array(data))
      );
      await putFileContent(
        url,
        token,
        encodedContent,
        file.sha,
        "更新代码片段"
      );
      vscode.window.showInformationMessage("已同步gitee最新代码片段");
    }
  );
  context.subscriptions.push(codeSnippetSynchronizationDisposable);
}

根据配置信息获取gitee仓库中的代码片段,并将本地代码片段与仓库中的代码进行合并,合并后同步更新本地和仓库的代码片段。

5、保存代码片段

function saveSnippet(context) {
  const saveSnippetDisposable = vscode.commands.registerCommand(
    "saveCodeSnippetsAndReusePlugins.saveSnippet",
    async () => {
      const editor = vscode.window.activeTextEditor;
      if (editor) {
        const selection = editor.selection;
        const text = editor.document.getText(selection);

        // 提示用户输入自定义名称
        const snippetName = await vscode.window.showInputBox({
          placeHolder: "请输入代码片段的自定义名称",
        });

        if (snippetName) {
          // 读取已有的代码片段数据
          const snippetsFilePath = path.join(
            context.extensionPath,
            "config/snippets.json"
          );
          let snippetsData = {};
          if (fs.existsSync(snippetsFilePath)) {
            snippetsData = JSON.parse(
              fs.readFileSync(snippetsFilePath, "utf8")
            );
          }
          // 将新的代码片段添加到数据中
          snippetsData.snippets[snippetName] = text;
          // 保存更新后的代码片段数据
          fs.writeFileSync(
            snippetsFilePath,
            JSON.stringify(snippetsData, null, 2),
            "utf8"
          );
          vscode.window.showInformationMessage(`代码片段已保存到本地!`);

          const giteeConfig = await getGiteeConfig(context);
          const { token, owner, repo } = giteeConfig;
          const url = `https://gitee.com/api/v5/repos/${owner}/${repo}/contents/codeSnippets/snippets.json`;
          const file = await fetchFileContent(url, token);
          const modifiedContent = JSON.stringify(snippetsData, null, 2);
          const encoder = new TextEncoder();
          const data = encoder.encode(modifiedContent);
          const encodedContent = btoa(
            String.fromCharCode.apply(null, new Uint8Array(data))
          );
          const res = await putFileContent(
            url,
            token,
            encodedContent,
            file.sha,
            "更新代码片段"
          );
          vscode.window.showInformationMessage(
            `代码片段 "${snippetName}" 同步到gitee仓库 ${
              res ? "成功" : "失败"
            }!`
          );
        }
      }
    }
  );
  context.subscriptions.push(saveSnippetDisposable);
}

选择代码块后,鼠标右键(快捷键ctrl + alt + s)选择保存代码片段,输入代码片段名称后,将选择的代码片段保存到本地的文件中,并同步更新到gitee仓库。

6、插入代码片段

(1)获取当前活动文本编辑器

const editor = vscode.window.activeTextEditor;

首先尝试获取 VSCode 窗口中的当前活动文本编辑器实例。如果获取成功(即 editor 不为 null),则继续执行后续的代码片段插入相关操作;如果获取失败(比如没有打开任何文本编辑器窗口),则整个函数的后续操作不会执行,因为没有合适的目标编辑器来插入代码片段。

(2)读取代码片段数据

const snippetsFilePath = path.join(
  context.extensionPath,
  "config/snippets.json"
);
let snippetsData = {};
if (fs.existsSync(snippetsFilePath)) {
  snippetsData = JSON.parse(fs.readFileSync(snippetsFilePath, "utf8"));
}
  • 构建了指向本地存储代码片段数据的文件路径,该文件位于插件的扩展路径下的 config 目录中,文件名为 snippets.json。
  • 初始化一个空对象 snippetsData 用于存储读取到的代码片段数据。然后通过 fs.existsSync 检查该文件是否存在,如果存在,则使用 JSON.parse 解析从文件中读取到的内容(通过 fs.readFileSync 读取文件内容),并将解析后的结果赋值给 snippetsData。

(3)获取代码片段名称列表并容用户选择

const snippetNames = Object.keys(snippetsData.snippets);
const selectedSnippetName = await vscode.window.showQuickPick(
  snippetNames,
  {
    placeHolder: "请选择要插入的代码片段",
  }
);

if (selectedSnippetName) {
  //后续操作
}
  • 首先通过 Object.keys 获取 snippetsData.snippets 中的所有键(这里假设 snippetsData 是一个包含 snippets 属性的对象,且 snippets 属性的值是一个以代码片段名称为键的对象或数组),这些键就是代码片段的名称,将它们存储在 snippetNames 数组中。
  • 然后使用 vscode.window.showQuickPick 弹出一个快速选择列表,让用户从 snippetNames 数组中的代码片段名称列表中选择要插入的代码片段。如果用户选择了一个名称(即 selectedSnippetName 不为 null),则继续执行后续操作来插入所选的代码片段;如果用户没有选择(比如关闭了选择列表窗口而没有做出选择),则整个函数的后续操作不会执行。

(4)插入所选代码片段

const selectedSnippet = snippetsData.snippets[selectedSnippetName];
if (selectedSnippet) {
    const position = editor.selection.start;
    editor.edit((editBuilder) => {
      editBuilder.insert(position, selectedSnippet);
    });
}
  • 首先根据用户选择的代码片段名称 selectedSnippetName 从 snippetsData.snippets 中获取对应的代码片段内容(这里假设 snippetsData.snippets 是以代码片段名称为键,代码片段内容为值的对象),将其存储在 selectedSnippet 变量中。
  • 如果获取到了有效的代码片段内容(即 selectedSnippet 不为 null),则获取当前编辑器中光标所在的位置(通过 editor.selection.start),并使用 editor.edit 方法来创建一个编辑操作构建器 editBuilder。通过 editBuilder.insert 将所选的代码片段内容插入到光标所在的位置。

7、删除代码片段

(1)获取代码片段数据

const snippetsFilePath = path.join(
  context.extensionPath,
  "config/snippets.json"
);
let snippetsData = {};
if (fs.existsSync(snippetsFilePath)) {
  snippetsData = JSON.parse(fs.readFileSync(snippetsFilePath, "utf8"));
}

(2)选择需要删除的代码片段并进行二次确认

const snippetNames = Object.keys(snippetsData.snippets);
const selectedSnippetName = await vscode.window.showQuickPick(
  snippetNames,
  {
    placeHolder: "请选择要删除的代码片段",
  }
);

if (selectedSnippetName) {
  const confirmation = await vscode.window.showWarningMessage(
    `你确定要删除${selectedSnippetName}这个代码片段吗?此操作不可撤销。`,
    "是",
    "否"
  );
  if (confirmation === "是") {
  //执行删除操作
  }
}

(3)删除代码片段并同步更新到仓库

delete snippetsData.snippets[selectedSnippetName];
// 保存更新后的代码片段数据
fs.writeFileSync(
  snippetsFilePath,
  JSON.stringify(snippetsData, null, 2),
  "utf8"
);
vscode.window.showInformationMessage(`本地代码片段已删除!`);
const giteeConfig = await getGiteeConfig(context);
const { token, owner, repo } = giteeConfig;
const url = `https://gitee.com/api/v5/repos/${owner}/${repo}/contents/codeSnippets/snippets.json`;
const file = await fetchFileContent(url, token);
const modifiedContent = JSON.stringify(snippetsData, null, 2);
const encoder = new TextEncoder();
const data = encoder.encode(modifiedContent);
const encodedContent = btoa(
  String.fromCharCode.apply(null, new Uint8Array(data))
);
const res = await putFileContent(
  url,
  token,
  encodedContent,
  file.sha,
  "更新代码片段"
);
vscode.window.showInformationMessage(
  `gitee仓库代码片段删除${res ? "成功" : "失败"}!`
);

8、package.json配置

(1)命令配置

"commands": [
  {
    "command": "saveCodeSnippetsAndReusePlugins.saveSnippet",
    "title": "保存代码片段"
  },
  {
    "command": "saveCodeSnippetsAndReusePlugins.insertSnippet",
    "title": "插入代码片段"
  },
  {
    "command": "saveCodeSnippetsAndReusePlugins.deleteSnippet",
    "title": "删除代码片段"
  },
  {
    "command": "saveCodeSnippetsAndReusePlugins.giteeConfig",
    "title": "仓库配置修改"
  },
  {
    "command": "saveCodeSnippetsAndReusePlugins.codeSnippetSynchronization",
    "title": "代码片段同步"
  }
]

(2)快捷键配置

"keybindings": [
  {
    "command": "saveCodeSnippetsAndReusePlugins.saveSnippet",
    "key": "ctrl+alt+s",
    "mac": "cmd+alt+s",
    "when": "editorHasSelection"
  },
  {
    "command": "saveCodeSnippetsAndReusePlugins.insertSnippet",
    "key": "ctrl+alt+i",
    "mac": "cmd+alt+i",
    "when": "editorTextFocus"
  },
  {
    "command": "saveCodeSnippetsAndReusePlugins.deleteSnippet",
    "key": "ctrl+alt+d",
    "mac": "cmd+alt+d"
  }
]

(3)菜单配置

"menus": {
  "editor/context": [
    {
      "submenu": "saveCodeSnippetsAndReusePlugins",
      "group": "navigation"
    }
  ],
  "saveCodeSnippetsAndReusePlugins": [
    {
      "command": "saveCodeSnippetsAndReusePlugins.giteeConfig",
      "group": "group1"
    },
    {
      "command": "saveCodeSnippetsAndReusePlugins.codeSnippetSynchronization",
      "group": "group1"
    },
    {
      "command": "saveCodeSnippetsAndReusePlugins.saveSnippet",
      "group": "group2",
      "when": "editorHasSelection"
    },
    {
      "command": "saveCodeSnippetsAndReusePlugins.insertSnippet",
      "group": "group2",
      "when": "editorTextFocus"
    },
    {
      "command": "saveCodeSnippetsAndReusePlugins.deleteSnippet",
      "group": "group2"
    }
  ]
},
"submenus": [
  {
    "id": "saveCodeSnippetsAndReusePlugins",
    "label": "代码片段仓库"
  }
]

三、插件使用

1、插件安装

直接在vscode插件商场中搜索saveCodeSnippetsAndReusePlugins

1732084277206.jpg

点击安装即可:

image.png

2、仓库配置

cf2cd2bf7b88fb0f4ff632dca0b6125.png

需要配置以下三个信息:

  • token

在gitee设置中生成然后复制填写即可: image.png

  • owner

image.png

  • repo

前面准备工作中新建的仓库,填写仓库名

image.png

3、代码片段同步

在多机使用时,可以同步拉取其他电脑保存的代码片段

image.png

4、保存代码片段

  • 快捷键(ctrl + alt + s)

将选中的代码保存为代码片段,可以自己命名

image.png

5、插入代码片段

  • 快捷键(ctrl + alt + i)

选择已保存的代码片段插入到当前代码

a8f808d63cac7ae7ab4c99502731341.png

6、删除代码片段

  • 快捷键(ctrl + alt + d)

选择需要删除的代码片段进行删除

image.png

源码

组件库已开源到gitee,有兴趣的也可以到这里看看:gitee.com/zheng_yongt…

🌟觉得有帮助的可以点个star~

🖊有什么问题或错误可以指出,欢迎pr~

📬有什么想要实现的功能或想法可以联系我~

公众号

关注公众号『前端也能这么有趣』,获取更多有趣内容。

说在后面

🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。