AI 工具开发实战(4):开发一个 VS Code AI 插件——选中代码一键解释/优化/写测试

前几篇做了 CLI 工具,这篇做点不一样的——VS Code 插件

选中一段代码,右键选"AI 解释这段代码",插件调用 LLM 在旁边显示注释。还有"优化代码"和"生成测试"功能。

插件做什么

选中代码 → 右键菜单:
├─ 🤖 AI 解释这段代码
├─ ⚡ AI 优化这段代码
└─ 🧪 AI 生成单元测试

点击后 → 调用 DeepSeek API → 结果显示在新面板

项目结构

vscode-ai/
├── package.json          # 插件配置
├── extension.js          # 主入口
├── ai.js                 # AI 调用
└── .env

插件配置

// package.json
{
  "name": "vscode-ai",
  "displayName": "AI Code Assistant",
  "version": "1.0.0",
  "engines": { "vscode": "^1.85.0" },
  "activationEvents": [],
  "main": "./extension.js",
  "contributes": {
    "commands": [
      { "command": "vscode-ai.explain", "title": "🤖 AI 解释这段代码" },
      { "command": "vscode-ai.optimize", "title": "⚡ AI 优化这段代码" },
      { "command": "vscode-ai.test", "title": "🧪 AI 生成单元测试" }
    ],
    "menus": {
      "editor/context": [
        { "command": "vscode-ai.explain", "group": "ai" },
        { "command": "vscode-ai.optimize", "group": "ai" },
        { "command": "vscode-ai.test", "group": "ai" }
      ]
    }
  }
}

AI 调用模块

// ai.js
const https = require('https');

const API_KEY = process.env.DEEPSEEK_API_KEY || '';
const API_URL = 'api.deepseek.com';

function callLLM(systemPrompt, userPrompt) {
  return new Promise((resolve, reject) => {
    const data = JSON.stringify({
      model: 'deepseek-chat',
      messages: [
        { role: 'system', content: systemPrompt },
        { role: 'user', content: userPrompt },
      ],
      temperature: 0.3,
      max_tokens: 2048,
    });

    const req = https.request({
      hostname: API_URL,
      path: '/v1/chat/completions',
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${API_KEY}`,
      },
    }, (res) => {
      let body = '';
      res.on('data', (chunk) => body += chunk);
      res.on('end', () => {
        try {
          const json = JSON.parse(body);
          resolve(json.choices[0].message.content);
        } catch (e) {
          reject(e);
        }
      });
    });
    req.on('error', reject);
    req.write(data);
    req.end();
  });
}

// 解释代码
function explainCode(code, language) {
  return callLLM(
    '你是一个代码解释专家。用中文解释代码的功能和逻辑,重点标注关键部分。',
    `解释以下 ${language} 代码:\n\`\`\`${language}\n${code}\n\`\`\``
  );
}

// 优化代码
function optimizeCode(code, language) {
  return callLLM(
    '你是一个代码优化专家。分析代码并给出优化建议,包括性能、可读性、安全性。',
    `审查并优化以下 ${language} 代码,给出具体建议:\n\`\`\`${language}\n${code}\n\`\`\``
  );
}

// 生成测试
function generateTest(code, language) {
  const framework = language === 'python' ? 'pytest' :
                    language === 'javascript' ? 'jest' :
                    language === 'typescript' ? 'jest' : '测试框架';
  return callLLM(
    `你是一个测试工程师。为给定代码生成 ${framework} 单元测试,覆盖正常情况、边界情况和异常情况。`,
    `为以下 ${language} 代码生成测试:\n\`\`\`${language}\n${code}\n\`\`\``
  );
}

module.exports = { explainCode, optimizeCode, generateTest };

插件主入口

// extension.js
const vscode = require('vscode');
const { explainCode, optimizeCode, generateTest } = require('./ai');

function getSelectedCode() {
  const editor = vscode.window.activeTextEditor;
  if (!editor) return null;

  const selection = editor.selection;
  const text = editor.document.getText(selection);
  const language = editor.document.languageId;

  // 如果没有选中文本,取整个文件
  if (!text) {
    return {
      code: editor.document.getText(),
      language: language,
      range: new vscode.Range(0, 0, editor.document.lineCount, 0),
    };
  }
  return { code: text, language, range: selection };
}

function showResult(title, content, language) {
  // 创建或显示输出面板
  const panel = vscode.window.createWebviewPanel(
    'aiResult',
    title,
    vscode.ViewColumn.Beside,
    { enableScripts: true }
  );

  const html = `

  body { font-family: sans-serif; padding: 16px; line-height: 1.6; }
  pre { background: #1e293b; color: #e2e8f0; padding: 16px; border-radius: 8px; overflow-x: auto; }
  code { font-size: 13px; }

${content.replace(/\`\`\`(\w*)\n([\s\S]*?)\`\`\`/g,
  '$2')}
`;
  panel.webview.html = html;
}

async function handleCommand(action) {
  const editor = vscode.window.activeTextEditor;
  if (!editor) {
    vscode.window.showErrorMessage('请先打开一个文件');
    return;
  }

  const { code, language } = getSelectedCode();
  if (!code.trim()) {
    vscode.window.showWarningMessage('没有可分析的代码');
    return;
  }

  vscode.window.showInformationMessage(`🤖 AI 正在${action}...`);

  try {
    let result;
    if (action === '解释') result = await explainCode(code, language);
    else if (action === '优化') result = await optimizeCode(code, language);
    else if (action === '测试') result = await generateTest(code, language);

    showResult(`AI ${action}结果`, result, language);
  } catch (e) {
    vscode.window.showErrorMessage(`AI 调用失败: ${e.message}`);
  }
}

function activate(context) {
  context.subscriptions.push(
    vscode.commands.registerCommand('vscode-ai.explain',
      () => handleCommand('解释')),
    vscode.commands.registerCommand('vscode-ai.optimize',
      () => handleCommand('优化')),
    vscode.commands.registerCommand('vscode-ai.test',
      () => handleCommand('测试')),
  );
}

function deactivate() {}

module.exports = { activate, deactivate };

安装使用

# 1. 安装依赖
npm init -y
npm install

# 2. 配置 Key
echo "DEEPSEEK_API_KEY=sk-your-key" > .env

# 3. 打包插件
npm install -g @vscode/vsce
vsce package

# 4. 在 VS Code 中安装
# Command Palette (Cmd+Shift+P) → Extensions: Install from VSIX → 选择 .vsix 文件

效果

1. 打开任意代码文件
2. 选中一段代码
3. 右键 → 🤖 AI 解释这段代码
4. 右侧面板显示:
   这段代码是一个用户认证中间件,它:
   - 从请求头中提取 JWT Token
   - 验证 Token 的有效性
   - 将用户信息注入到 request.state
   ...

总结

VS Code AI 插件的核心:
1. 获取选中的代码(editor.selection
2. 调用 LLM API
3. 在新的 WebView 面板中展示结果

三个命令覆盖了开发者最常用的场景:看不懂的代码 → 解释、想改进的代码 → 优化、新写的函数 → 生成测试。


本文是 《AI 开发者工具链实战》 系列的第 4 篇。
上一篇:AI 代码审查 CLI
下一篇:AI 浏览器划词翻译插件
本文由 Zyentor(智元界)原创发布