feat:将extension文件拆分成不同功能的独立组件

This commit is contained in:
Roe-xin
2025-12-11 10:54:46 +08:00
parent eec915421a
commit b3c8344d82
9 changed files with 547 additions and 40 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 889 KiB

View File

@ -1,24 +1,63 @@
{
"name": "ic-coder",
"name": "ic-coder-plugin",
"displayName": "IC Coder plugin",
"description": "Agentic Verilog Coding Platform for Real-World FPGAs",
"version": "0.0.1",
"version": "0.0.2",
"engines": {
"vscode": "^1.107.0"
"vscode": "^1.106.3"
},
"icon": "media/IC Coder主页标志.png",
"categories": [
"Other"
],
"activationEvents": [],
"keywords": [
"IC",
"coder",
"verilog",
"FPGA",
"eda",
"assistant"
],
"activationEvents": [
"onCommand:ic-coder.openPanel",
"onView:ic-coder-sidebar",
"onLanguage:verilog",
"onLanguage:vhdl",
"onStartupFinished"
],
"main": "./dist/extension.js",
"contributes": {
"commands": [
{
"command": "ic-coder.helloWorld",
"title": "Hello World"
"command": "ic-coder.openPanel",
"title": "打开 IC Coder 助手",
"category": "IC Coder"
},
{
"command": "ic-coder.openChat",
"title": "IC Coder: 打开聊天",
"category": "IC Coder"
}
],
"viewsContainers": {
"activitybar": [
{
"id": "ic-coder-sidebar",
"title": "IC Coder",
"icon": "media/图案(方底).png"
}
]
},
"views": {
"ic-coder-sidebar": [
{
"id": "ic-coder.mainView",
"name": "IC 助手",
"type": "webview"
}
]
}
},
"scripts": {
"vscode:prepublish": "pnpm run package",
"compile": "webpack",
@ -28,7 +67,8 @@
"watch-tests": "tsc -p . -w --outDir out",
"pretest": "pnpm run compile-tests && pnpm run compile && pnpm run lint",
"lint": "eslint src",
"test": "vscode-test"
"test": "vscode-test",
"build": "pnpm run compile"
},
"devDependencies": {
"@types/vscode": "^1.107.0",

View File

@ -1,26 +1,39 @@
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
import * as vscode from "vscode";
import { ICViewProvider } from "./views/ICViewProvider";
import { showICHelperPanel } from "./panels/ICHelperPanel";
// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {
console.log("🎉 IC Coder 插件已激活!");
// Use the console to output diagnostic information (console.log) and errors (console.error)
// This line of code will only be executed once when your extension is activated
console.log('Congratulations, your extension "ic-coder" is now active!');
// 注册命令:打开助手面板
const openPanelCommand = vscode.commands.registerCommand(
"ic-coder.openPanel",
() => {
showICHelperPanel(context);
}
);
// The command has been defined in the package.json file
// Now provide the implementation of the command with registerCommand
// The commandId parameter must match the command field in package.json
const disposable = vscode.commands.registerCommand('ic-coder.helloWorld', () => {
// The code you place here will be executed every time your command is executed
// Display a message box to the user
vscode.window.showInformationMessage('Hello World from IC Coder plugin!');
});
// 注册命令:打开聊天(用于侧边栏)
const openChatCommand = vscode.commands.registerCommand(
"ic-coder.openChat",
() => {
showICHelperPanel(context);
}
);
context.subscriptions.push(disposable);
// 注册侧边栏视图
const viewProvider = new ICViewProvider();
const viewRegistration = vscode.window.registerWebviewViewProvider(
"ic-coder.mainView",
viewProvider
);
// 添加到订阅
context.subscriptions.push(
openPanelCommand,
openChatCommand,
viewRegistration
);
}
// This method is called when your extension is deactivated
export function deactivate() {}

View File

@ -0,0 +1,41 @@
import * as vscode from "vscode";
import { getWebviewContent } from "../utils/webviewContent";
import { handleUserMessage, insertCodeToEditor } from "../utils/messageHandler";
/**
* 创建并显示 IC 助手面板
*/
export function showICHelperPanel(context: vscode.ExtensionContext) {
// 创建WebView面板
const panel = vscode.window.createWebviewPanel(
"icCoder", // 面板ID
"IC Coder", // 面板标题
vscode.ViewColumn.Beside, // 显示在旁边
{
enableScripts: true,
retainContextWhenHidden: true,
}
);
// 设置HTML内容
panel.webview.html = getWebviewContent();
// 处理消息
panel.webview.onDidReceiveMessage(
(message) => {
switch (message.command) {
case "sendMessage":
handleUserMessage(panel, message.text);
break;
case "insertCode":
insertCodeToEditor(message.code);
break;
case "showInfo":
vscode.window.showInformationMessage(message.text);
break;
}
},
undefined,
context.subscriptions
);
}

View File

@ -0,0 +1,67 @@
import * as vscode from "vscode";
/**
* 处理用户消息
*/
export function handleUserMessage(panel: vscode.WebviewPanel, text: string) {
// 模拟AI回复
const reply = getMockReply(text);
// 延迟回复模拟AI思考
setTimeout(() => {
panel.webview.postMessage({
command: "receiveMessage",
text: reply,
});
}, 500);
}
/**
* 获取模拟回复
*/
function getMockReply(question: string): string {
const replies = [
`已收到您的问题:"${question}"
这是一个演示版本实际需要连接AI服务。
示例回复:这是一个计数器模板:
\`\`\`verilog
module counter (
input clk,
input rst_n,
output reg [3:0] count
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) count <= 0;
else count <= count + 1;
end
endmodule
\`\`\``,
`感谢提问!关于"${question}",在真实版本中我会:
1. 分析您的代码上下文
2. 提供优化建议
3. 生成完整代码
4. 解释设计原理
当前是演示版,请点击侧边栏按钮快速生成代码。`,
];
return replies[Math.floor(Math.random() * replies.length)];
}
/**
* 将代码插入到编辑器
*/
export function insertCodeToEditor(code: string) {
const editor = vscode.window.activeTextEditor;
if (editor) {
editor.edit((editBuilder) => {
editBuilder.insert(editor.selection.active, code);
});
vscode.window.showInformationMessage("代码已插入");
} else {
vscode.window.showWarningMessage("请先打开一个编辑器");
}
}

177
src/utils/webviewContent.ts Normal file
View File

@ -0,0 +1,177 @@
/**
* 获取 WebView 面板的 HTML 内容
*/
export function getWebviewContent(): string {
return `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IC Coder 助手</title>
<style>
body {
font-family: var(--vscode-font-family);
background: var(--vscode-editor-background);
color: var(--vscode-foreground);
margin: 0;
padding: 20px;
height: 100vh;
display: flex;
flex-direction: column;
}
.header {
text-align: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid var(--vscode-panel-border);
}
.header h1 {
color: var(--vscode-button-background);
margin: 0 0 8px 0;
}
.chat-container {
flex: 1;
display: flex;
flex-direction: column;
}
.messages {
flex: 1;
overflow-y: auto;
margin-bottom: 15px;
}
.message {
margin-bottom: 12px;
padding: 10px 15px;
border-radius: 8px;
max-width: 80%;
}
.user-message {
background: var(--vscode-button-secondaryBackground);
margin-left: auto;
}
.bot-message {
background: var(--vscode-button-background);
color: var(--vscode-button-foreground);
margin-right: auto;
}
.input-area {
border-top: 1px solid var(--vscode-panel-border);
padding-top: 15px;
}
.input-group {
display: flex;
gap: 10px;
}
textarea {
flex: 1;
padding: 10px;
background: var(--vscode-input-background);
color: var(--vscode-input-foreground);
border: 1px solid var(--vscode-input-border);
border-radius: 4px;
font-family: inherit;
resize: none;
min-height: 40px;
}
button {
padding: 0 20px;
background: var(--vscode-button-background);
color: var(--vscode-button-foreground);
border: none;
border-radius: 4px;
cursor: pointer;
}
.quick-actions {
display: flex;
gap: 8px;
margin-top: 10px;
flex-wrap: wrap;
}
.quick-btn {
padding: 6px 12px;
background: var(--vscode-button-secondaryBackground);
color: var(--vscode-button-secondaryForeground);
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
}
</style>
</head>
<body>
<div class="header">
<h1>IC Coder</h1>
<p>专注于真实FPGA研发的Verilog智能体编程平台</p>
</div>
<div class="chat-container">
<div id="messages" class="messages">
<div class="message bot-message">
👋 你好!我是 IC Coder 助手,可以帮你生成代码、回答问题。
</div>
</div>
<div class="quick-actions">
<button class="quick-btn" onclick="quickAction('counter')">生成计数器</button>
<button class="quick-btn" onclick="quickAction('fsm')">生成状态机</button>
<button class="quick-btn" onclick="quickAction('testbench')">生成测试平台</button>
</div>
<div class="input-area">
<div class="input-group">
<textarea
id="messageInput"
placeholder="输入您的问题..."
onkeydown="if(event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); sendMessage(); }"
></textarea>
<button onclick="sendMessage()">发送</button>
</div>
</div>
</div>
<script>
const vscode = acquireVsCodeApi();
const messagesEl = document.getElementById('messages');
const messageInput = document.getElementById('messageInput');
function sendMessage() {
const text = messageInput.value.trim();
if (!text) return;
addMessage(text, 'user');
vscode.postMessage({ command: 'sendMessage', text: text });
messageInput.value = '';
messageInput.focus();
}
function quickAction(type) {
const questions = {
counter: '生成一个4位同步计数器',
fsm: '生成一个状态机',
testbench: '生成测试平台'
};
if (questions[type]) {
messageInput.value = questions[type];
sendMessage();
}
}
function addMessage(text, sender) {
const div = document.createElement('div');
div.className = \`message \${sender}-message\`;
div.textContent = text;
messagesEl.appendChild(div);
messagesEl.scrollTop = messagesEl.scrollHeight;
}
window.addEventListener('message', event => {
if (event.data.command === 'receiveMessage') {
addMessage(event.data.text, 'bot');
}
});
messageInput.focus();
</script>
</body>
</html>`;
}

176
src/views/ICViewProvider.ts Normal file
View File

@ -0,0 +1,176 @@
import * as vscode from "vscode";
/**
* 侧边栏视图提供者
*/
export class ICViewProvider implements vscode.WebviewViewProvider {
resolveWebviewView(webviewView: vscode.WebviewView) {
webviewView.webview.options = {
enableScripts: true,
};
webviewView.webview.html = this.getWebviewContent();
// 处理侧边栏的消息
webviewView.webview.onDidReceiveMessage((message) => {
if (message.command === "openChat") {
vscode.commands.executeCommand("ic-coder.openChat");
}
});
}
private getWebviewContent(): string {
return `
<!DOCTYPE html>
<html>
<head>
<style>
body {
padding: 10px;
font-family: var(--vscode-font-family);
color: var(--vscode-foreground);
}
.btn {
width: 100%;
padding: 8px 12px;
margin: 4px 0;
background: var(--vscode-button-background);
color: var(--vscode-button-foreground);
border: none;
border-radius: 4px;
cursor: pointer;
text-align: center;
}
.btn:hover {
background: var(--vscode-button-hoverBackground);
}
.section {
margin: 15px 0;
}
h3 {
margin: 0 0 8px 0;
font-size: 12px;
color: var(--vscode-descriptionForeground);
}
</style>
</head>
<body>
<button class="btn" onclick="openChat()">💬 打开聊天助手</button>
<div class="section">
<h3>快速生成</h3>
<button class="btn" onclick="generateCode('counter')">🔢 计数器</button>
<button class="btn" onclick="generateCode('fsm')">🔄 状态机</button>
<button class="btn" onclick="generateCode('fifo')">📦 FIFO</button>
</div>
<script>
const vscode = acquireVsCodeApi();
function openChat() {
vscode.postMessage({ command: 'openChat' });
}
function generateCode(type) {
const code = getCodeTemplate(type);
vscode.postMessage({
command: 'insertCode',
code: code
});
}
function getCodeTemplate(type) {
const templates = {
counter: \`module counter #(
parameter WIDTH = 4
)(
input wire clk,
input wire rst_n,
input wire enable,
output reg [WIDTH-1:0] count
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
count <= 0;
end else if (enable) begin
count <= count + 1;
end
end
endmodule\`,
fsm: \`module fsm (
input wire clk,
input wire rst_n,
input wire start,
output reg done
);
parameter IDLE = 2'b00;
parameter STATE1 = 2'b01;
parameter STATE2 = 2'b10;
reg [1:0] state, next_state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
end else begin
state <= next_state;
end
end
always @(*) begin
case (state)
IDLE: next_state = start ? STATE1 : IDLE;
STATE1: next_state = STATE2;
STATE2: next_state = IDLE;
default: next_state = IDLE;
endcase
end
assign done = (state == STATE2);
endmodule\`,
fifo: \`module sync_fifo #(
parameter DATA_WIDTH = 8,
parameter DEPTH = 16
)(
input wire clk,
input wire rst_n,
input wire wr_en,
input wire [DATA_WIDTH-1:0] din,
input wire rd_en,
output reg [DATA_WIDTH-1:0] dout,
output wire full,
output wire empty
);
reg [DATA_WIDTH-1:0] mem [0:DEPTH-1];
reg [$clog2(DEPTH):0] wr_ptr, rd_ptr;
assign full = (wr_ptr == rd_ptr + DEPTH);
assign empty = (wr_ptr == rd_ptr);
always @(posedge clk) begin
if (!rst_n) wr_ptr <= 0;
else if (wr_en && !full) begin
mem[wr_ptr] <= din;
wr_ptr <= wr_ptr + 1;
end
end
always @(posedge clk) begin
if (!rst_n) begin
rd_ptr <= 0;
dout <= 0;
end else if (rd_en && !empty) begin
dout <= mem[rd_ptr];
rd_ptr <= rd_ptr + 1;
end
end
endmodule\`
};
return templates[type] || '// 代码模板';
}
</script>
</body>
</html>
`;
}
}

View File

@ -1,16 +1,9 @@
{
"compilerOptions": {
"module": "Node16",
"target": "ES2022",
"lib": [
"ES2022"
],
"sourceMap": true,
"rootDir": "src",
"strict": true, /* enable all strict type-checking options */
/* Additional Checks */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
"module": "commonjs",
"target": "ES2020",
"outDir": "./dist",
"rootDir": "./src",
"strict": true
}
}