From 2c6f3cf1bbdc59a1a77f851e768a9a6ee598d49b Mon Sep 17 00:00:00 2001 From: Adam Coster Date: Fri, 31 Mar 2023 18:05:51 -0500 Subject: [PATCH] feat: Improved running GM from vscode --- packages/vscode/src/extension.config.mts | 2 + packages/vscode/src/extension.gml.mts | 3 ++ packages/vscode/src/extension.project.mts | 61 ++++++++++++++++++++++ packages/vscode/src/extension.provider.mts | 33 ++++-------- 4 files changed, 75 insertions(+), 24 deletions(-) diff --git a/packages/vscode/src/extension.config.mts b/packages/vscode/src/extension.config.mts index c448cfa3..5e228da5 100644 --- a/packages/vscode/src/extension.config.mts +++ b/packages/vscode/src/extension.config.mts @@ -46,3 +46,5 @@ export class StitchConfig { return this.config.get('task.run.defaultConfig') || null; } } + +export const config = new StitchConfig(); diff --git a/packages/vscode/src/extension.gml.mts b/packages/vscode/src/extension.gml.mts index 7fa0f44d..f7562908 100644 --- a/packages/vscode/src/extension.gml.mts +++ b/packages/vscode/src/extension.gml.mts @@ -11,6 +11,7 @@ export class GmlFile { readonly dir: string; readonly resourceType: YyResourceType; readonly resourceName: string; + readonly globalFunctions: Map = new Map(); // Track any globals defined in this file readonly globals: Set = new Set(); @@ -70,6 +71,8 @@ export class GmlFile { if (!node.name) { continue; } + this.globalFunctions.set(name, node); + const paramNames = node.info.map((p) => p.name); this.addProjectCompletion( name, diff --git a/packages/vscode/src/extension.project.mts b/packages/vscode/src/extension.project.mts index c60ecd6b..a11f184b 100644 --- a/packages/vscode/src/extension.project.mts +++ b/packages/vscode/src/extension.project.mts @@ -8,6 +8,7 @@ import { import { Yy, Yyp, YypResource } from '@bscotch/yy'; import path from 'path'; import vscode from 'vscode'; +import { StitchTaskDefinition, config } from './extension.config.mjs'; import { GmlFile } from './extension.gml.mjs'; import { GameMakerResource } from './extension.resource.mjs'; @@ -50,6 +51,64 @@ export class GameMakerProject protected gmlFiles: Map = new Map(); + /** + * Generate a GML-parseable string showing how all global + * functions map to their scripts, in the format: + * `func1:script1,func2:script2,func3:script3` + */ + createFunctionScriptString() { + const asString = [...this.resources] + .reduce((acc, [, resource]) => { + if (resource.type !== 'scripts') { + return acc; + } + resource.gmlFiles.forEach((gmlFile) => { + gmlFile.globalFunctions.forEach((func) => { + if (!func.name) { + return; + } + acc.push(`${func.name}:${resource.name}`); + }); + }); + + return acc; + }, [] as string[]) + .join(','); + return asString; + } + + async asRunTask() { + const taskDefinition: StitchTaskDefinition = { + type: 'stitch', + task: 'run', + compiler: config.runCompilerDefault, + config: + config.runConfigDefault || + this.yyp.configs.children[0]?.name || + this.yyp.configs.name, + projectName: this.name, + }; + const command = await this.computeRunCommand(taskDefinition); + if (!command) { + return; + } + return new vscode.Task( + taskDefinition, + vscode.TaskScope.Workspace, + taskDefinition.task, + this.name, + new vscode.ProcessExecution(command.cmd, command.args, { + cwd: this.rootPath, + env: { + VSCODE_STITCH_VERSION: vscode.extensions.getExtension( + 'bscotch.bscotch-stitch-vscode', + )?.packageJSON.version, + VSCODE_STITCH_SCRIPT_FUNCTIONS: this.createFunctionScriptString(), + }, + }), + ); + } + async openInIde() { vscode.window.showInformationMessage( `Opening project with GameMaker v${this.ideVersion}...`, @@ -100,6 +159,8 @@ export class GameMakerProject project: this.yypPath.fsPath, config: options?.config ?? undefined, yyc: options?.compiler === 'yyc', + noCache: false, + quiet: true, }); } diff --git a/packages/vscode/src/extension.provider.mts b/packages/vscode/src/extension.provider.mts index 6eccb24f..37881eaa 100644 --- a/packages/vscode/src/extension.provider.mts +++ b/packages/vscode/src/extension.provider.mts @@ -7,7 +7,7 @@ import os from 'os'; import process from 'process'; import vscode from 'vscode'; import { debounce } from './debounce.mjs'; -import { StitchConfig, StitchTaskDefinition } from './extension.config.mjs'; +import { config } from './extension.config.mjs'; import { GameMakerProject } from './extension.project.mjs'; import { GmlSpec, parseSpec } from './spec.mjs'; @@ -40,7 +40,7 @@ export class GmlProvider globalHovers: Map = new Map(); globalSignatures: Map = new Map(); protected projects: GameMakerProject[] = []; - static config = new StitchConfig(); + static config = config; protected constructor(readonly spec: GmlSpec) { for (const func of spec.functions) { @@ -126,34 +126,19 @@ export class GmlProvider return project; } + // TODO: Make the runner use F5 somehow? + // TODO: Investigate GameMaker error objects/messages -- how can we get a problem matcher working? + // TODO: Add a problem matcher. What happens with a compile error vs. a runtime error? async provideTasks(): Promise { const tasks: vscode.Task[] = []; if (!this.projects.length || !GmlProvider.config.autoDetectTasks) { return tasks; } for (const project of this.projects) { - const taskDefinition: StitchTaskDefinition = { - type: 'stitch', - task: 'run', - compiler: GmlProvider.config.runCompilerDefault, - config: - GmlProvider.config.runConfigDefault || - project.yyp.configs.children[0]?.name || - project.yyp.configs.name, - projectName: project.name, - }; - const command = await project.computeRunCommand(taskDefinition); - if (!command) continue; - const task = new vscode.Task( - taskDefinition, - vscode.TaskScope.Workspace, - taskDefinition.task, - `Run ${project.name}`, - new vscode.ProcessExecution(command.cmd, command.args, { - cwd: project.rootPath, - }), - ); - tasks.push(task); + const task = await project.asRunTask(); + if (task) { + tasks.push(task); + } } return tasks; }