diff --git a/extension.js b/extension.js index 74b6f88..cee6dca 100644 --- a/extension.js +++ b/extension.js @@ -5,11 +5,14 @@ const cp = require('child_process'); const path = require('path'); const os = require('os'); const fs = require('fs'); +const admzip = require('adm-zip'); // stores all the instances of Love2d running let currentInstances = new Map(); // actual "Run L5" button in menu let statusBarItem; +let statusBarStarter; +let statusBarBuild; let outputChannel; // property to check if this is the first time L5 installed @@ -49,7 +52,6 @@ function findExecutableInPath(executable) { // check whether or not Love2d is on the path for the specific platform function validateLovePath(lovePath, platform) { - // if the path is absolute, then it should be fine if (path.isAbsolute(lovePath)) { if (fs.existsSync(lovePath)) { @@ -90,24 +92,25 @@ function checkFirstRun(context) { // is the current workspace a L5 project? function isLoveProject() { const workspaceFolders = vscode.workspace.workspaceFolders; - if (!workspaceFolders || workspaceFolders.length === 0) { - return false; - } + // if (!workspaceFolders || workspaceFolders.length === 0) { + // return false; + // } const rootPath = workspaceFolders[0].uri.fsPath; const mainLuaPath = path.join(rootPath, 'main.lua'); const libLuaPath = path.join(rootPath, 'L5.lua'); - return fs.existsSync(mainLuaPath) || fs.existsSync(libLuaPath); + // return fs.existsSync(mainLuaPath) || fs.existsSync(libLuaPath); + return true; } // is an L5 project already running using Love2d? async function updateStatusBar() { // is this project not L5 project? don't show "Run L5" button - if (!isLoveProject()) { - statusBarItem?.hide(); - return; - } + // if (!isLoveProject()) { + // statusBarItem?.hide(); + // return; + // } // does button exist? if not, add it to the status bar if (!statusBarItem) { @@ -116,20 +119,45 @@ async function updateStatusBar() { 100, ); } + if (!statusBarStarter) { + statusBarStarter = vscode.window.createStatusBarItem( + vscode.StatusBarAlignment.Left, + 99, + ); + } + + if (!statusBarBuild) { + statusBarBuild = vscode.window.createStatusBarItem( + vscode.StatusBarAlignment.Left, + 98, + ); + } // if love is running, change language to "Stop Love" if (currentInstances.size > 0) { - statusBarItem.text = '$(debug-stop) Stop LOVE'; - statusBarItem.tooltip = 'Stop running LOVE instance'; + statusBarItem.text = '$(debug-stop) Stop L5'; + statusBarItem.tooltip = 'Stop running L5 instance'; statusBarItem.command = 'l5.stop'; - } else { // otherwise, language of button should be "Run L5" + } else { + // otherwise, language of button should be "Run L5" statusBarItem.text = '$(play) Run L5'; - statusBarItem.tooltip = 'Launch LOVE project (Alt+L)'; + statusBarItem.tooltip = 'Launch L5 project (Alt+L)'; statusBarItem.command = 'l5.launch'; } + statusBarStarter.text = '$(add) Create New Project'; + statusBarStarter.tooltip = 'Create a New L5 Project in the current folder'; + statusBarStarter.command = 'l5.new'; + + statusBarBuild.text = '$(game) Build L5 Project'; + statusBarBuild.tooltip = + 'Build a shareable L5 executable in the current folder'; + statusBarBuild.command = 'l5.build'; + // update the display of the button statusBarItem.show(); + statusBarBuild.show(); + statusBarStarter.show(); } // display message when first running @@ -183,6 +211,9 @@ async function activate(context) { .get('maxInstances'); let overwrite = vscode.workspace.getConfiguration('l5').get('overwrite'); + // current folder of the project + const currentFolder = vscode.workspace.workspaceFolders[0].uri.fsPath; + // outputChannels are read-only textual information outputChannel = vscode.window.createOutputChannel('LOVE'); // subscriptions are the disposables @@ -217,6 +248,58 @@ async function activate(context) { context.subscriptions.push(stopCommand); // add stop event handling to extension monitoring + const newProjectCommand = vscode.commands.registerCommand( + 'l5.new', + async () => { + const destPath = path.join(currentFolder, 'download.zip'); + fs.mkdirSync(path.dirname(destPath), { recursive: true }); + await fetch('https://l5lua.org/L5-starter.zip').then( + async (response) => { + // download the zip + const buffer = await response.arrayBuffer(); + console.log('destPath:', destPath); + fs.writeFileSync(destPath, Buffer.from(buffer)); + + // extract the zip + const zip = new admzip(destPath); + zip.extractAllTo(currentFolder, true); + + fs.unlinkSync(destPath); + }, + (error) => { + vscode.window.showErrorMessage('Failed to download L5 starter'); + }, + ); + }, + ); + context.subscriptions.push(newProjectCommand); + + const buildProjectCommand = vscode.commands.registerCommand( + 'l5.build', + async () => { + const zip = new admzip(); + + const whereL5 = await vscode.window.showOpenDialog({ + canSelectFolders: true, + canSelectFiles: false, + canSelectMany: false, + openLabel: 'Select folder to zip', + }); + zip.addLocalFolder(whereL5[0].fsPath); + + const zipName = await vscode.window.showInputBox({ + prompt: 'Enter a name for the zip file', + placeHolder: 'L5-project', + }); + if (!zipName) { + return; + } + const validName = zipName.replace(/\.zip$/i, ''); + zip.writeZip(path.join(currentFolder, validName + '.love')); + }, + ); + context.subscriptions.push(buildProjectCommand); + // check if file was saved const saveListener = vscode.workspace.onDidSaveTextDocument((document) => { // retrieve preference on auto-restart on saving @@ -318,7 +401,7 @@ async function activate(context) { } // this will be the node.child_process - // retrieves the Love2d binary / executable + // retrieves the Love2d binary / executable let process; if (platform === 'win32') { @@ -350,7 +433,7 @@ async function activate(context) { outputChannel?.show(true); // yes we want to show these to the user console - // if close event detected, remove it from tracked instances + // if close event detected, remove it from tracked instances // and close Love2d instance running process.on('exit', (code, signal) => { if (process.pid && !process.spawnargs.includes('open')) { @@ -400,11 +483,15 @@ async function activate(context) { if (statusBarItem) { context.subscriptions.push(statusBarItem); + context.subscriptions.push(statusBarStarter); + context.subscriptions.push(statusBarBuild); } } function deactivate() { statusBarItem?.dispose(); + statusBarBuild?.dispose(); + statusBarStarter?.dispose(); } module.exports = { diff --git a/package-lock.json b/package-lock.json index 04c2ade..f98f9a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,16 @@ { "name": "l5", - "version": "0.0.1", + "version": "0.0.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "l5", - "version": "0.0.1", + "version": "0.0.2", + "license": "MIT", + "dependencies": { + "adm-zip": "^0.6.0" + }, "devDependencies": { "@vscode/test-cli": "^0.0.12", "@vscode/test-electron": "^2.5.2", @@ -443,6 +447,15 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/adm-zip": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.6.0.tgz", + "integrity": "sha512-XleryMhbuksdKtofnWZ9Sk+4CUTbms4Mb/EU32SZwToAyZ5RgVos/ki8n+yr0LWHOGKuakbXTuuYNHLQjhddgg==", + "license": "MIT", + "engines": { + "node": ">=14.0" + } + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", diff --git a/package.json b/package.json index 0e7c651..099b34f 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,7 @@ "tomblind.local-lua-debugger-vscode" ], "activationEvents": [ - "workspaceContains:L5.lua", - "workspaceContains:main.lua" + "onStartupFinished" ], "keywords": [ "L5", @@ -51,6 +50,14 @@ { "command": "l5.stop", "title": "Close L5" + }, + { + "command": "l5.new", + "title": "New L5 project" + }, + { + "command": "l5.build", + "title": "Build L5 project" } ], "configuration": { @@ -108,5 +115,8 @@ "homepage": "https://github.com/L5lua/L5-vscode-extension/blob/main/README.md", "bugs": { "url": "https://github.com/L5lua/L5-vscode-extension/issues" + }, + "dependencies": { + "adm-zip": "^0.6.0" } } \ No newline at end of file