凌晨2点17分,我第37次按下了Ctrl+Space。代码补全弹出的那一刻,我突然想:如果这背后是我的OpenClaw呢?
把OpenClaw接入VS Code,实现智能代码补全、重构建议、Bug修复、文档生成。不需要依赖GitHub Copilot,完全自托管,代码不上云。
// .vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "OpenClaw: 解释代码",
"type": "shell",
"command": "openclaw",
"args": [
"chat",
"--message",
"解释这段代码:${selectedText}",
"--output",
"markdown"
],
"group": "none",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared"
}
},
{
"label": "OpenClaw: 重构代码",
"type": "shell",
"command": "openclaw",
"args": [
"chat",
"--message",
"重构这段代码,提高可读性:${selectedText}"
]
}
]
}
// .vscode/keybindings.json
[
{
"key": "ctrl+shift+e",
"command": "workbench.action.tasks.runTask",
"args": "OpenClaw: 解释代码",
"when": "editorHasSelection"
},
{
"key": "ctrl+shift+r",
"command": "workbench.action.tasks.runTask",
"args": "OpenClaw: 重构代码",
"when": "editorHasSelection"
}
]
选中代码 → 按 Ctrl+Shift+E → 看OpenClaw解释代码
openclaw-vscode/
├── package.json # 扩展配置
├── src/
│ ├── extension.ts # 入口文件
│ ├── openclawClient.ts # API客户端
│ └── providers/ # 各种Provider
│ ├── completionProvider.ts
│ ├── hoverProvider.ts
│ └── codeActionProvider.ts
└── README.md
{
"name": "openclaw-vscode",
"displayName": "OpenClaw AI Assistant",
"version": "1.0.0",
"engines": {
"vscode": "^1.74.0"
},
"categories": ["Machine Learning", "Snippets"],
"activationEvents": ["onLanguage:python", "onLanguage:javascript"],
"main": "./out/extension.js",
"contributes": {
"configuration": {
"title": "OpenClaw",
"properties": {
"openclaw.apiUrl": {
"type": "string",
"default": "http://localhost:3000",
"description": "OpenClaw API地址"
},
"openclaw.apiKey": {
"type": "string",
"default": "",
"description": "API密钥"
}
}
},
"commands": [
{
"command": "openclaw.explain",
"title": "用OpenClaw解释代码",
"category": "OpenClaw"
},
{
"command": "openclaw.generateDoc",
"title": "生成文档注释",
"category": "OpenClaw"
}
],
"menus": {
"editor/context": [
{
"command": "openclaw.explain",
"group": "9_cutcopypaste@5",
"when": "editorHasSelection"
}
]
}
}
}
import * as vscode from 'vscode';
import { OpenClawClient } from './openclawClient';
export function activate(context: vscode.ExtensionContext) {
const config = vscode.workspace.getConfiguration('openclaw');
const client = new OpenClawClient(
config.get('apiUrl') || 'http://localhost:3000',
config.get('apiKey') || ''
);
// 注册:解释代码命令
let explainDisposable = vscode.commands.registerCommand(
'openclaw.explain',
async () => {
const editor = vscode.window.activeTextEditor;
if (!editor) return;
const selectedText = editor.document.getText(editor.selection);
if (!selectedText) {
vscode.window.showWarningMessage('请先选中代码');
return;
}
// 显示进度
await vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: "OpenClaw正在分析代码...",
cancellable: false
}, async () => {
try {
const explanation = await client.explainCode(selectedText);
// 在输出通道显示结果
const outputChannel = vscode.window.createOutputChannel('OpenClaw');
outputChannel.clear();
outputChannel.appendLine('=== 代码解释 ===');
outputChannel.appendLine(explanation);
outputChannel.show();
} catch (error) {
vscode.window.showErrorMessage(`OpenClaw错误: ${error}`);
}
});
}
);
context.subscriptions.push(explainDisposable);
}
export function deactivate() {}
import axios from 'axios';
export class OpenClawClient {
constructor(private baseUrl: string, private apiKey: string) {}
async explainCode(code: string, language?: string): Promise {
const response = await axios.post(
`${this.baseUrl}/api/v1/chat`,
{
message: `解释以下${language || ''}代码:\n\n\`\`\`\n${code}\n\`\`\`\n\n请说明:\n1. 这段代码的功能\n2. 关键逻辑\n3. 潜在问题(如有)`,
context: {
temperature: 0.3,
max_tokens: 2048
}
},
{
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
}
}
);
return response.data.content;
}
async generateDoc(code: string): Promise {
const response = await axios.post(
`${this.baseUrl}/api/v1/chat`,
{
message: `为以下代码生成文档注释:\n\n${code}`,
context: {
temperature: 0.2
}
},
{
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
}
);
return response.data.content;
}
}
import * as vscode from 'vscode';
import { OpenClawClient } from '../openclawClient';
export class OpenClawCompletionProvider implements vscode.CompletionItemProvider {
constructor(private client: OpenClawClient) {}
async provideCompletionItems(
document: vscode.TextDocument,
position: vscode.Position
): Promise {
// 获取当前行和上下文
const linePrefix = document.lineAt(position).text.substr(0, position.character);
// 只在特定触发条件下提供AI补全
if (!linePrefix.includes('// AI:')) {
return [];
}
const codeContext = this.getCodeContext(document, position);
try {
const suggestion = await this.client.completeCode(codeContext);
const item = new vscode.CompletionItem(
'AI生成代码',
vscode.CompletionItemKind.Snippet
);
item.insertText = suggestion;
item.detail = '由OpenClaw生成';
item.documentation = new vscode.MarkdownString(suggestion);
return [item];
} catch (error) {
return [];
}
}
private getCodeContext(document: vscode.TextDocument, position: vscode.Position): string {
// 获取前后50行作为上下文
const startLine = Math.max(0, position.line - 50);
const endLine = Math.min(document.lineCount, position.line + 50);
let context = '';
for (let i = startLine; i < endLine; i++) {
context += document.lineAt(i).text + '\n';
}
return context;
}
}
// 在编辑器内直接对话
const inlineChat = vscode.languages.registerInlineChatProvider(
{ pattern: '**' },
{
provideResponse: async (request, progress, token) => {
const code = request.doc.getText(request.selection);
const response = await client.chat(code, request.prompt);
return { edits: [{ range: request.selection, text: response }] };
}
}
);
// 创建Webview面板
const panel = vscode.window.createWebviewPanel(
'openclawChat',
'OpenClaw Chat',
vscode.ViewColumn.Beside,
{ enableScripts: true }
);
panel.webview.html = getChatWebviewContent();