实现一个vscode插件:打开多个vscode项目时根据.nvmrc文件自动切换nvm

1,196 阅读1分钟

在这里插入图片描述

开发背景与最终功能

需要维护一些老项目,同时开发新项目时,切换nvm很烦人 最终实现vscode插件:每个vscode实例打开一个项目,切换vscode实例时能自动切换版本(需要项目根目录有一个.nvmrc文件)

插件下载

vscode插件市场搜索vscode-nvmrc在这里插入图片描述

设计思路

项目根目录新建.nvmrc文件,这是nvm的官方文件,当使用nvm use时会自动查找这个文件,而windows系统一般使用的是nvm-for-windows,它是由另一个开发者维护的windows版本,并不支持nvm use查找.nvmrc 不过这并不影响vscode插件中实现nvm use功能,只不过了解下.nvmrc是nvm的官方文件

第一版

话不多说,上代码,很简单,vscode插件方法vscode.window.onDidChangeWindowState中读取下.nvmrc文件,e.focused表示当vscode窗口显示时触发,切换vscode实例时能够触发,然后调用child_process.exec 运行nvm use

import * as vscode from "vscode";
import { exec } from "child_process";
import { readFile } from "fs";
import { resolve } from "path";

let statusBar: vscode.StatusBarItem | undefined;
let timeout: NodeJS.Timeout;

enum Status {
  error = "error",
}
function customStatusBar(text: string, type?: Status, time = 4000) {
  if (statusBar) {
    statusBar.dispose();
  }
  if (timeout) {
    clearTimeout(timeout);
  }
  statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
  statusBar.color = "#ffffff";

  if (type === Status.error) {
    statusBar.backgroundColor = new vscode.ThemeColor(
      "statusBarItem.errorBackground"
    );
  }
  if (!type) {
    statusBar.backgroundColor = new vscode.ThemeColor(
      "statusBarItem.warningBackground"
    );
  }
  statusBar.text = "vscode-nvmrc: " + text;
  statusBar.show();
  timeout = setTimeout(() => {
    if (statusBar) {
      statusBar.dispose();
    }
  }, time);
  return;
}

function execute(cmd: string) {
  exec(cmd, (error, stdout, stderr) => {
    if (error) {
      customStatusBar(`${error}`);
    } else {
      if (stderr) {
        customStatusBar(stderr);
      } else {
        customStatusBar(stdout);
      }
    }
  });
}

function nvmuse(url: string) {
  readFile(url, { encoding: "utf8" }, (err, data) => {
    if (err) {
      customStatusBar(".nvmrc file not found.");
      return;
    }
    execute("nvm use " + data);
  });
}

function resolveRootPathAndNvmuse() {
  const workspaceFolders = vscode.workspace.workspaceFolders;
  if (workspaceFolders && workspaceFolders.length > 0) {
    const rootPath = workspaceFolders[0].uri.fsPath;
    if (rootPath) {
      const url = resolve(rootPath, ".nvmrc");
      nvmuse(url);
    }
  }
}

export function activate(context: vscode.ExtensionContext) {
  resolveRootPathAndNvmuse();
  const disposable = vscode.window.onDidChangeWindowState((e) => {
    if (e.focused) {
      resolveRootPathAndNvmuse();
    }
  });
  context.subscriptions.push(disposable);
}

export function deactivate() {}

第二版-解决第一版重大问题

第一版在1.1.12版本中会出现重大问题,nvm限制只能在终端中调用,否则一直弹窗提示,导致无法正常使用,太坑了

第二版更改为创建vscode终端运行命名,这样就不会有这个问题了,并且添加了插件配置项,创建终端运行nvm use后会自动关闭终端,默认5s后·,可以在插件配置中设置延迟秒数,如果不想自动关闭,可以在插件配置中取消自动关闭。

function nvmuse(url: string, context: ExtensionContext) {
  readFile(url, { encoding: "utf8" }, (err, data) => {
    if (err) {
      return customStatusBar(".nvmrc file not found.", Status.error);
    }
    const nvmrcData = context.globalState.get(".nvmrc");
    if (nvmrcData === data.replace("v", "")) {
      return;
    }
    context.globalState.update(".nvmrc", data);
    const terminal = window.createTerminal("run 'nvm use' (vscode-nvmrc)");
    terminal.sendText("nvm use " + data);
    terminal.show();
    const config = workspace.getConfiguration("vscode-nvmrc");
    const autoCloseNvmTerminal = config.get("autoCloseNvmTerminal") as boolean;
    if (autoCloseNvmTerminal) {
      const delayBeforeCloseNvmTerminal = config.get(
        "delayBeforeCloseNvmTerminal"
      ) as number;
      customStatusBar(
        `The terminal running 'nvm use' will close in ${delayBeforeCloseNvmTerminal} seconds. `
      );
      setTimeout(() => {
        terminal.dispose();
      }, delayBeforeCloseNvmTerminal * 1000);
    }
  });
}