feat:将extension文件拆分成不同功能的独立组件
This commit is contained in:
BIN
media/IC Coder主页标志.png
Normal file
BIN
media/IC Coder主页标志.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 160 KiB |
BIN
media/图案(方底).png
Normal file
BIN
media/图案(方底).png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 889 KiB |
56
package.json
56
package.json
@ -1,23 +1,62 @@
|
||||
{
|
||||
"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",
|
||||
@ -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",
|
||||
|
||||
@ -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() {}
|
||||
|
||||
41
src/panels/ICHelperPanel.ts
Normal file
41
src/panels/ICHelperPanel.ts
Normal 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
|
||||
);
|
||||
}
|
||||
67
src/utils/messageHandler.ts
Normal file
67
src/utils/messageHandler.ts
Normal 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
177
src/utils/webviewContent.ts
Normal 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
176
src/views/ICViewProvider.ts
Normal 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>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@ -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. */
|
||||
}
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "ES2020",
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user