Files
kitchen/flutter-runner/extension.js
2026-04-15 07:11:28 +08:00

230 lines
6.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const vscode = require('vscode');
let flutterTerminal = null;
function getTerminal() {
if (!flutterTerminal || flutterTerminal.exitStatus !== undefined) {
flutterTerminal = vscode.window.createTerminal('Flutter');
}
return flutterTerminal;
}
function runCommand(cmd) {
const terminal = getTerminal();
terminal.show();
terminal.sendText(cmd);
}
async function selectBuildPlatform() {
const platforms = [
{ label: '$(device-mobile) APK (Android)', value: 'apk' },
{ label: '$(apple) IPA (iOS)', value: 'ios' },
{ label: '$(globe) Web', value: 'web' },
{ label: '$(window) Windows', value: 'windows' },
{ label: '$(vm) macOS', value: 'macos' },
{ label: '$(terminal) Linux', value: 'linux' }
];
const selected = await vscode.window.showQuickPick(platforms, {
placeHolder: '选择构建平台'
});
return selected ? selected.value : null;
}
async function selectDevice() {
const terminal = getTerminal();
terminal.show();
terminal.sendText('flutter devices');
vscode.window.showInformationMessage('请在终端查看可用设备然后输入设备ID');
const deviceId = await vscode.window.showInputBox({
placeHolder: '输入设备ID留空使用默认设备',
title: '选择运行设备'
});
return deviceId || null;
}
async function startDebugRun(deviceId) {
const workspaceFolders = vscode.workspace.workspaceFolders;
if (!workspaceFolders || workspaceFolders.length === 0) {
vscode.window.showErrorMessage('请先打开一个 Flutter 项目');
return;
}
const folder = workspaceFolders[0];
const debugConfig = {
type: 'dart',
name: 'Flutter (Debug)',
request: 'launch',
program: 'lib/main.dart',
flutterMode: 'debug'
};
if (deviceId) {
debugConfig.deviceId = deviceId;
}
const started = await vscode.debug.startDebugging(folder, debugConfig);
if (!started) {
vscode.window.showErrorMessage('启动调试失败,请确保已安装 Dart 扩展');
}
}
async function startProfileRun() {
const workspaceFolders = vscode.workspace.workspaceFolders;
if (!workspaceFolders || workspaceFolders.length === 0) {
vscode.window.showErrorMessage('请先打开一个 Flutter 项目');
return;
}
const folder = workspaceFolders[0];
const debugConfig = {
type: 'dart',
name: 'Flutter (Profile)',
request: 'launch',
program: 'lib/main.dart',
flutterMode: 'profile'
};
const started = await vscode.debug.startDebugging(folder, debugConfig);
if (!started) {
vscode.window.showErrorMessage('启动 Profile 模式失败');
}
}
function activate(context) {
const provider = new FlutterRunnerProvider();
context.subscriptions.push(
vscode.window.registerTreeDataProvider('flutterRunner', provider)
);
context.subscriptions.push(
vscode.commands.registerCommand('flutterRunner.run', async () => {
const deviceId = await selectDevice();
await startDebugRun(deviceId);
})
);
context.subscriptions.push(
vscode.commands.registerCommand('flutterRunner.runDebug', async () => {
await startDebugRun();
})
);
context.subscriptions.push(
vscode.commands.registerCommand('flutterRunner.runProfile', async () => {
await startProfileRun();
})
);
context.subscriptions.push(
vscode.commands.registerCommand('flutterRunner.hotReload', () => {
vscode.commands.executeCommand('flutter.hotReload');
})
);
context.subscriptions.push(
vscode.commands.registerCommand('flutterRunner.hotRestart', () => {
vscode.commands.executeCommand('flutter.hotRestart');
})
);
context.subscriptions.push(
vscode.commands.registerCommand('flutterRunner.stopDebug', () => {
if (vscode.debug.activeDebugSession) {
vscode.debug.stopDebugging();
} else {
vscode.window.showInformationMessage('没有正在运行的调试会话');
}
})
);
context.subscriptions.push(
vscode.commands.registerCommand('flutterRunner.clean', () => {
runCommand('flutter clean');
})
);
context.subscriptions.push(
vscode.commands.registerCommand('flutterRunner.pubGet', () => {
runCommand('flutter pub get');
})
);
context.subscriptions.push(
vscode.commands.registerCommand('flutterRunner.build', async () => {
const platform = await selectBuildPlatform();
if (platform) {
runCommand(`flutter build ${platform}`);
}
})
);
context.subscriptions.push(
vscode.commands.registerCommand('flutterRunner.refresh', () => {
provider.refresh();
})
);
vscode.debug.onDidChangeActiveDebugSession((session) => {
provider.refresh();
});
}
class FlutterRunnerProvider {
constructor() {
this._onDidChangeTreeData = new vscode.EventEmitter();
this.onDidChangeTreeData = this._onDidChangeTreeData.event;
}
refresh() {
this._onDidChangeTreeData.fire();
}
getTreeItem(element) {
return element;
}
getChildren(element) {
if (!element) {
const isDebugging = !!vscode.debug.activeDebugSession;
const items = [
this.createButtonItem('▶️ Run (Debug)', 'flutterRunner.runDebug', '调试模式运行(支持热重载、断点、调试控制台)'),
this.createButtonItem('📱 Run (选择设备)', 'flutterRunner.run', '选择设备后调试运行'),
this.createButtonItem('📊 Run (Profile)', 'flutterRunner.runProfile', 'Profile 模式运行(性能分析)'),
this.createSectionItem('── 调试控制 ──'),
this.createButtonItem('🔥 Hot Reload', 'flutterRunner.hotReload', '热重载(保持状态)'),
this.createButtonItem('🔄 Hot Restart', 'flutterRunner.hotRestart', '热重启(重置状态)'),
this.createButtonItem('⏹️ Stop', 'flutterRunner.stopDebug', '停止调试会话'),
this.createSectionItem('── 项目命令 ──'),
this.createButtonItem('🧹 Clean', 'flutterRunner.clean', '清理构建缓存'),
this.createButtonItem('📦 Pub Get', 'flutterRunner.pubGet', '获取依赖'),
this.createButtonItem('🔨 Build', 'flutterRunner.build', '构建发布包')
];
if (isDebugging) {
items.unshift(this.createStatusItem('🟢 调试运行中'));
} else {
items.unshift(this.createStatusItem('⚪ 未运行'));
}
return items;
}
return [];
}
createButtonItem(label, command, tooltip) {
const item = new vscode.TreeItem(label, vscode.TreeItemCollapsibleState.None);
item.command = { command, title: label };
item.tooltip = tooltip;
return item;
}
createSectionItem(label) {
const item = new vscode.TreeItem(label, vscode.TreeItemCollapsibleState.None);
item.tooltip = label;
return item;
}
createStatusItem(label) {
const item = new vscode.TreeItem(label, vscode.TreeItemCollapsibleState.None);
item.tooltip = label;
return item;
}
}
function deactivate() {}
module.exports = { activate, deactivate };