460 lines
14 KiB
TypeScript
460 lines
14 KiB
TypeScript
import * as vscode from "vscode";
|
||
import { ICViewProvider } from "./views/ICViewProvider";
|
||
import { showICHelperPanel } from "./panels/ICHelperPanel";
|
||
import { VCDViewerPanel, VCDViewerEditorProvider } from "./panels/VCDViewerPanel";
|
||
import { ChatHistoryManager } from "./utils/chatHistoryManager";
|
||
import { ICCoderAuthenticationProvider } from "./services/icCoderAuthProvider";
|
||
import { VCDFileServer } from "./services/vcdFileServer";
|
||
import { initUserService } from "./services/userService";
|
||
import { initCreditsService } from "./services/creditsService";
|
||
import { isTokenExpired } from "./utils/jwtUtils";
|
||
import { NotificationService } from "./services/notificationService";
|
||
import { InvitationService } from "./services/invitationService";
|
||
import { ICCoderCodeActionProvider } from "./providers/codeActionProvider";
|
||
|
||
export async function activate(context: vscode.ExtensionContext) {
|
||
console.log("🎉 IC Coder 插件已激活!");
|
||
|
||
// 创建装饰类型(代码旁边的提示)
|
||
const decorationType = vscode.window.createTextEditorDecorationType({
|
||
after: {
|
||
contentText: ' Ctrl+L 添加到 IC Coder 对话',
|
||
color: '#888',
|
||
fontStyle: 'italic',
|
||
margin: '0 0 0 1em'
|
||
}
|
||
});
|
||
|
||
// 更新装饰
|
||
const updateDecorations = () => {
|
||
const editor = vscode.window.activeTextEditor;
|
||
if (!editor) return;
|
||
|
||
if (!editor.selection.isEmpty) {
|
||
// 找到选区末尾所在的行,并将提示放在该行的末尾
|
||
const { anchor, active } = editor.selection;
|
||
const endPos = anchor.isAfter(active) ? anchor : active;
|
||
const lineEndPos = editor.document.lineAt(endPos.line).range.end;
|
||
const range = new vscode.Range(lineEndPos, lineEndPos);
|
||
const decoration = { range };
|
||
editor.setDecorations(decorationType, [decoration]);
|
||
} else {
|
||
editor.setDecorations(decorationType, []);
|
||
}
|
||
};
|
||
|
||
context.subscriptions.push(
|
||
vscode.window.onDidChangeTextEditorSelection(updateDecorations),
|
||
vscode.window.onDidChangeActiveTextEditor(updateDecorations)
|
||
);
|
||
|
||
updateDecorations();
|
||
|
||
// 初始化通知服务
|
||
const notificationService = NotificationService.getInstance(context);
|
||
console.log('[Extension] 通知服务已初始化');
|
||
|
||
// 【关键】在创建 AuthProvider 之前,先检查并清除过期的 session
|
||
const storedSessions = context.globalState.get<any[]>('icCoderSessions', []);
|
||
console.log('[Extension] 检查 sessions 数量:', storedSessions.length);
|
||
|
||
if (storedSessions.length > 0) {
|
||
const session = storedSessions[0];
|
||
const token = session.accessToken;
|
||
console.log('[Extension] 检查 token 是否过期...');
|
||
|
||
if (token) {
|
||
const expired = isTokenExpired(token);
|
||
console.log('[Extension] token 过期检查结果:', expired);
|
||
|
||
if (expired) {
|
||
// 必须等待清除完成后再创建 AuthProvider
|
||
await context.globalState.update('icCoderSessions', []);
|
||
await context.globalState.update('icCoderUserInfo', undefined);
|
||
console.log('[Extension] Token 已过期,已清除所有登录状态');
|
||
}
|
||
}
|
||
}
|
||
|
||
// 初始化用户服务
|
||
initUserService(context);
|
||
|
||
// 初始化 Credits 服务
|
||
initCreditsService(context);
|
||
|
||
// 初始化 VCD 文件服务器
|
||
const vcdFileServer = new VCDFileServer(context.extensionUri);
|
||
vcdFileServer.start().then((port) => {
|
||
console.log(`VCD 文件服务器已启动,端口: ${port}`);
|
||
}).catch((error) => {
|
||
console.error("启动 VCD 文件服务器失败:", error);
|
||
});
|
||
|
||
// 在插件停用时关闭服务器
|
||
context.subscriptions.push({
|
||
dispose: () => vcdFileServer.stop()
|
||
});
|
||
|
||
// 注册 Authentication Provider(此时 icCoderSessions 已经被清除)
|
||
const authProvider = new ICCoderAuthenticationProvider(context);
|
||
context.subscriptions.push(
|
||
vscode.authentication.registerAuthenticationProvider(
|
||
"iccoder",
|
||
"IC Coder",
|
||
authProvider
|
||
)
|
||
);
|
||
|
||
// 检查登录状态,如果已登录则自动打开聊天面板
|
||
vscode.authentication.getSession("iccoder", [], { createIfNone: false })
|
||
.then((session) => {
|
||
if (session) {
|
||
vscode.commands.executeCommand("ic-coder.openChat");
|
||
}
|
||
}, () => {
|
||
// 未登录,不做任何操作
|
||
});
|
||
|
||
// 注册命令:打开助手面板
|
||
const openPanelCommand = vscode.commands.registerCommand(
|
||
"ic-coder.openPanel",
|
||
async () => {
|
||
await showICHelperPanel(context);
|
||
}
|
||
);
|
||
|
||
// 注册命令:打开聊天(用于侧边栏)
|
||
const openChatCommand = vscode.commands.registerCommand(
|
||
"ic-coder.openChat",
|
||
async () => {
|
||
await showICHelperPanel(context);
|
||
}
|
||
);
|
||
|
||
// 注册命令:打开 VCD 波形查看器
|
||
const openVCDViewerCommand = vscode.commands.registerCommand(
|
||
"ic-coder.openVCDViewer",
|
||
async (vcdFilePath?: string) => {
|
||
if (!vcdFilePath) {
|
||
// 如果没有提供文件路径,让用户选择 VCD 文件
|
||
const fileUri = await vscode.window.showOpenDialog({
|
||
canSelectFiles: true,
|
||
canSelectFolders: false,
|
||
canSelectMany: false,
|
||
filters: {
|
||
"VCD 文件": ["vcd"],
|
||
"所有文件": ["*"],
|
||
},
|
||
title: "选择 VCD 文件",
|
||
});
|
||
|
||
if (fileUri && fileUri[0]) {
|
||
vcdFilePath = fileUri[0].fsPath;
|
||
} else {
|
||
return;
|
||
}
|
||
}
|
||
|
||
VCDViewerPanel.createOrShow(context.extensionUri, vcdFilePath, vcdFileServer);
|
||
}
|
||
);
|
||
|
||
// 注册命令:在浏览器中打开 VCD 波形查看器
|
||
const openVCDViewerInBrowserCommand = vscode.commands.registerCommand(
|
||
"ic-coder.openVCDViewerInBrowser",
|
||
async (vcdFilePath?: string) => {
|
||
if (!vcdFilePath) {
|
||
const fileUri = await vscode.window.showOpenDialog({
|
||
canSelectFiles: true,
|
||
canSelectFolders: false,
|
||
canSelectMany: false,
|
||
filters: {
|
||
"VCD 文件": ["vcd"],
|
||
"所有文件": ["*"],
|
||
},
|
||
title: "选择 VCD 文件",
|
||
});
|
||
|
||
if (fileUri && fileUri[0]) {
|
||
vcdFilePath = fileUri[0].fsPath;
|
||
} else {
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 注册文件到服务器
|
||
const fileId = vcdFileServer.registerFile(vcdFilePath);
|
||
const viewerUrl = vcdFileServer.getViewerUrl(fileId);
|
||
|
||
// 在默认浏览器中打开
|
||
vscode.env.openExternal(vscode.Uri.parse(viewerUrl));
|
||
vscode.window.showInformationMessage(`波形查看器已在浏览器中打开`);
|
||
}
|
||
);
|
||
|
||
// 注册命令:用户登录
|
||
const loginCommand = vscode.commands.registerCommand(
|
||
"ic-coder.login",
|
||
async (options?: { forceReauth?: boolean }) => {
|
||
try {
|
||
const forceReauth = options?.forceReauth === true;
|
||
const session = await vscode.authentication.getSession("iccoder", [], {
|
||
createIfNone: false,
|
||
});
|
||
const expired = session?.accessToken
|
||
? isTokenExpired(session.accessToken)
|
||
: null;
|
||
|
||
// 会话仍有效时,直接打开聊天面板
|
||
if (session && expired === false && !forceReauth) {
|
||
vscode.commands.executeCommand("ic-coder.openChat");
|
||
return;
|
||
}
|
||
|
||
// 1) 清空当前登录状态信息
|
||
await authProvider.clearSessionsForRelogin();
|
||
await context.globalState.update("icCoderSessions", []);
|
||
await context.globalState.update("icCoderUserInfo", undefined);
|
||
|
||
// 2) 重新登录(强制新会话)
|
||
await vscode.authentication.getSession("iccoder", [], {
|
||
clearSessionPreference: true,
|
||
forceNewSession: true,
|
||
});
|
||
} catch (error) {
|
||
vscode.window.showErrorMessage(`登录失败: ${error}`);
|
||
}
|
||
}
|
||
);
|
||
|
||
// 注册命令:用户登出
|
||
const logoutCommand = vscode.commands.registerCommand(
|
||
"ic-coder.logout",
|
||
async () => {
|
||
try {
|
||
const session = await vscode.authentication.getSession("iccoder", [], { createIfNone: false });
|
||
if (session) {
|
||
// 调用 authProvider 的 removeSession 方法
|
||
await authProvider.removeSession(session.id);
|
||
// 清除邀请码验证状态
|
||
await InvitationService.clearVerificationStatus(context);
|
||
} else {
|
||
vscode.window.showInformationMessage("当前未登录");
|
||
}
|
||
} catch (error) {
|
||
vscode.window.showInformationMessage("当前未登录");
|
||
}
|
||
}
|
||
);
|
||
|
||
// 注册命令:更换邀请码
|
||
const changeInvitationCodeCommand = vscode.commands.registerCommand(
|
||
"ic-coder.changeInvitationCode",
|
||
async () => {
|
||
const confirm = await vscode.window.showWarningMessage(
|
||
'确定要更换邀请码吗?',
|
||
'确定',
|
||
'取消'
|
||
);
|
||
|
||
if (confirm === '确定') {
|
||
await InvitationService.clearVerificationStatus(context);
|
||
vscode.window.showInformationMessage('已清除邀请码,请重新验证');
|
||
}
|
||
}
|
||
);
|
||
|
||
// 注册命令:测试系统通知
|
||
const testNotificationCommand = vscode.commands.registerCommand(
|
||
"ic-coder.testNotification",
|
||
() => {
|
||
console.log('[Extension] ========== 测试通知命令被调用 ==========');
|
||
|
||
// 先显示 VS Code 通知确认命令执行
|
||
vscode.window.showInformationMessage('正在测试系统通知...');
|
||
|
||
// 发送系统通知
|
||
notificationService.success(
|
||
'IC Coder - 测试通知',
|
||
'系统通知功能正常工作!',
|
||
() => {
|
||
vscode.window.showInformationMessage('您点击了系统通知!');
|
||
}
|
||
);
|
||
|
||
console.log('[Extension] 测试通知命令执行完成');
|
||
}
|
||
);
|
||
|
||
// 注册命令:将选中代码添加到对话
|
||
const addCodeToChat = vscode.commands.registerCommand(
|
||
"ic-coder.addCodeToChat",
|
||
async () => {
|
||
console.log('[addCodeToChat] 命令触发');
|
||
const editor = vscode.window.activeTextEditor;
|
||
if (!editor) {
|
||
console.log('[addCodeToChat] 没有活动编辑器');
|
||
return;
|
||
}
|
||
|
||
const selection = editor.selection;
|
||
const selectedText = editor.document.getText(selection);
|
||
|
||
if (!selectedText) {
|
||
vscode.window.showWarningMessage("请先选择代码");
|
||
return;
|
||
}
|
||
|
||
const fileName = editor.document.fileName;
|
||
const startLine = selection.start.line + 1;
|
||
const endLine = selection.end.line + 1;
|
||
|
||
// 检查是否已有打开的面板
|
||
let panel = (global as any).currentICHelperPanel;
|
||
let needCreatePanel = false;
|
||
|
||
if (!panel) {
|
||
needCreatePanel = true;
|
||
} else {
|
||
// 尝试访问 webview,如果抛出异常说明已销毁
|
||
try {
|
||
const _ = panel.webview;
|
||
} catch (e) {
|
||
needCreatePanel = true;
|
||
}
|
||
}
|
||
|
||
console.log('[addCodeToChat] 需要创建面板:', needCreatePanel);
|
||
|
||
if (needCreatePanel) {
|
||
console.log('[addCodeToChat] 正在打开面板...');
|
||
await showICHelperPanel(context);
|
||
panel = (global as any).currentICHelperPanel;
|
||
console.log('[addCodeToChat] 面板打开后状态:', panel ? '成功' : '失败');
|
||
|
||
// 如果面板仍未创建(如未登录),直接返回
|
||
if (!panel) {
|
||
console.log('[addCodeToChat] 面板创建失败,退出');
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 发送代码上下文
|
||
console.log('[addCodeToChat] 准备发送代码到面板');
|
||
setTimeout(() => {
|
||
try {
|
||
if (panel?.webview) {
|
||
console.log('[addCodeToChat] 发送 addCodeContext 消息');
|
||
panel.webview.postMessage({
|
||
command: 'addCodeContext',
|
||
fileName,
|
||
startLine,
|
||
endLine,
|
||
code: selectedText,
|
||
languageId: editor.document.languageId
|
||
});
|
||
}
|
||
} catch (e) {
|
||
console.log('[addCodeToChat] 发送消息失败:', e);
|
||
}
|
||
}, 500);
|
||
}
|
||
);
|
||
|
||
// 注册命令:查看会话历史
|
||
// TODO: 这些命令需要根据新的任务架构重新实现
|
||
// 暂时注释掉,等待重新实现
|
||
/*
|
||
const viewHistoryCommand = vscode.commands.registerCommand(
|
||
"ic-coder.viewHistory",
|
||
() => {
|
||
vscode.window.showInformationMessage("此功能正在重构中,敬请期待");
|
||
}
|
||
);
|
||
|
||
const newSessionCommand = vscode.commands.registerCommand(
|
||
"ic-coder.newSession",
|
||
() => {
|
||
vscode.window.showInformationMessage("此功能正在重构中,敬请期待");
|
||
}
|
||
);
|
||
|
||
const exportSessionCommand = vscode.commands.registerCommand(
|
||
"ic-coder.exportSession",
|
||
() => {
|
||
vscode.window.showInformationMessage("此功能正在重构中,敬请期待");
|
||
}
|
||
);
|
||
|
||
const deleteSessionCommand = vscode.commands.registerCommand(
|
||
"ic-coder.deleteSession",
|
||
() => {
|
||
vscode.window.showInformationMessage("此功能正在重构中,敬请期待");
|
||
}
|
||
);
|
||
|
||
const clearHistoryCommand = vscode.commands.registerCommand(
|
||
"ic-coder.clearHistory",
|
||
() => {
|
||
vscode.window.showInformationMessage("此功能正在重构中,敬请期待");
|
||
}
|
||
);
|
||
|
||
const searchSessionCommand = vscode.commands.registerCommand(
|
||
"ic-coder.searchSession",
|
||
() => {
|
||
vscode.window.showInformationMessage("此功能正在重构中,敬请期待");
|
||
}
|
||
);
|
||
*/
|
||
|
||
// 注册侧边栏视图
|
||
const viewProvider = new ICViewProvider(context.extensionUri, context);
|
||
const viewRegistration = vscode.window.registerWebviewViewProvider(
|
||
"ic-coder.mainView",
|
||
viewProvider,
|
||
{
|
||
webviewOptions: {
|
||
retainContextWhenHidden: true
|
||
}
|
||
}
|
||
);
|
||
|
||
// 注册 VCD 自定义编辑器
|
||
const vcdEditorProvider = VCDViewerEditorProvider.register(context, vcdFileServer);
|
||
|
||
// 注册 Code Action Provider
|
||
const codeActionProvider = vscode.languages.registerCodeActionsProvider(
|
||
{ scheme: 'file' },
|
||
new ICCoderCodeActionProvider(),
|
||
{ providedCodeActionKinds: [vscode.CodeActionKind.RefactorRewrite] }
|
||
);
|
||
|
||
// 添加到订阅
|
||
context.subscriptions.push(
|
||
openPanelCommand,
|
||
openChatCommand,
|
||
openVCDViewerCommand,
|
||
openVCDViewerInBrowserCommand,
|
||
loginCommand,
|
||
logoutCommand,
|
||
changeInvitationCodeCommand,
|
||
testNotificationCommand,
|
||
addCodeToChat,
|
||
// testTrialUserCommand,
|
||
// testExpiredUserCommand,
|
||
// TODO: 等待重新实现这些命令
|
||
// viewHistoryCommand,
|
||
// newSessionCommand,
|
||
// exportSessionCommand,
|
||
// deleteSessionCommand,
|
||
// clearHistoryCommand,
|
||
// searchSessionCommand,
|
||
viewRegistration,
|
||
vcdEditorProvider,
|
||
codeActionProvider
|
||
);
|
||
}
|
||
|
||
export function deactivate() {}
|