diff --git a/core/interfaces/i_parameter_model.ts b/core/interfaces/i_parameter_model.ts index 00d11724034..fe9eda6b0c5 100644 --- a/core/interfaces/i_parameter_model.ts +++ b/core/interfaces/i_parameter_model.ts @@ -25,6 +25,16 @@ export interface IParameterModel { */ setTypes(types: string[]): this; + /** + * Returns the name of this parameter. + */ + getName(): string; + + /** + * Return the types of this parameter. + */ + getTypes(): string[]; + /** * Returns the unique language-neutral ID for the parameter. * diff --git a/core/interfaces/i_procedure_map.ts b/core/interfaces/i_procedure_map.ts new file mode 100644 index 00000000000..0eead4025cc --- /dev/null +++ b/core/interfaces/i_procedure_map.ts @@ -0,0 +1,20 @@ +/** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import {IProcedureModel} from './i_procedure_model.js'; + + +export interface IProcedureMap extends Map { + /** + * Adds the given ProcedureModel to the map of procedure models, so that + * blocks can find it. + */ + add(proc: IProcedureModel): this; + + + /** Returns all of the procedures stored in this map. */ + getProcedures(): IProcedureModel[]; +} diff --git a/core/procedures/observable_parameter_model.ts b/core/procedures/observable_parameter_model.ts index fe0f5bdb1ba..535c6aea164 100644 --- a/core/procedures/observable_parameter_model.ts +++ b/core/procedures/observable_parameter_model.ts @@ -48,6 +48,20 @@ export class ObservableParameterModel implements IParameterModel { 'implement your own custom ParameterModel.'); } + /** + * Returns the name of this parameter. + */ + getName(): string { + return this.variable.name; + } + + /** + * Returns the types of this parameter. + */ + getTypes(): string[] { + return []; + } + /** * Returns the unique language-neutral ID for the parameter. * diff --git a/core/procedures/observable_procedure_map.ts b/core/procedures/observable_procedure_map.ts index 781cb4682fc..a9ad169c57a 100644 --- a/core/procedures/observable_procedure_map.ts +++ b/core/procedures/observable_procedure_map.ts @@ -7,9 +7,11 @@ import type {IProcedureModel} from '../interfaces/i_procedure_model.js'; import {triggerProceduresUpdate} from './update_procedures.js'; import type {Workspace} from '../workspace.js'; +import {IProcedureMap} from '../interfaces/i_procedure_map.js'; -export class ObservableProcedureMap extends Map { +export class ObservableProcedureMap extends + Map implements IProcedureMap { constructor(private readonly workspace: Workspace) { super(); } @@ -52,4 +54,11 @@ export class ObservableProcedureMap extends Map { // TODO(#6526): See if this method is actually useful. return this.set(proc.getId(), proc); } + + /** + * Returns all of the procedures stored in this map. + */ + getProcedures(): IProcedureModel[] { + return [...this.values()]; + } } diff --git a/core/procedures/observable_procedure_model.ts b/core/procedures/observable_procedure_model.ts index e338d5161d8..be8d05fe599 100644 --- a/core/procedures/observable_procedure_model.ts +++ b/core/procedures/observable_procedure_model.ts @@ -13,13 +13,15 @@ import type {Workspace} from '../workspace.js'; export class ObservableProcedureModel implements IProcedureModel { private id: string; - private name = ''; + private name: string; private parameters: IParameterModel[] = []; private returnTypes: string[]|null = null; private enabled = true; - constructor(private readonly workspace: Workspace, id?: string) { + constructor( + private readonly workspace: Workspace, name: string, id?: string) { this.id = id ?? genUid(); + this.name = name; } /** Sets the human-readable name of the procedure. */ @@ -51,13 +53,22 @@ export class ObservableProcedureModel implements IProcedureModel { } /** - * Sets the return type(s) of the procedure. + * Sets whether the procedure has a return value (empty array) or no return + * value (null). * - * Pass null to represent a procedure that does not return. + * The built-in procedure model does not support procedures that have actual + * return types (i.e. non-empty arrays, e.g. ['number']). If you want your + * procedure block to have return types, you need to implement your own + * procedure model. */ setReturnTypes(types: string[]|null): this { - // TODO(#6516): Fire events. + if (types && types.length) { + throw new Error( + 'The built-in ProcedureModel does not support typing. You need to ' + + 'implement your own custom ProcedureModel.'); + } this.returnTypes = types; + // TODO(#6516): Fire events. triggerProceduresUpdate(this.workspace); return this; } diff --git a/core/workspace.ts b/core/workspace.ts index 101596374cf..0602fa8347e 100644 --- a/core/workspace.ts +++ b/core/workspace.ts @@ -32,6 +32,8 @@ import type * as toolbox from './utils/toolbox.js'; import {VariableMap} from './variable_map.js'; import type {VariableModel} from './variable_model.js'; import type {WorkspaceComment} from './workspace_comment.js'; +import {IProcedureMap} from './interfaces/i_procedure_map.js'; +import {ObservableProcedureMap} from './procedures.js'; /** @@ -110,6 +112,7 @@ export class Workspace implements IASTNodeLocation { private readonly blockDB = new Map(); private readonly typedBlocksDB = new Map(); private variableMap: VariableMap; + private procedureMap: IProcedureMap = new ObservableProcedureMap(this); /** * Blocks in the flyout can refer to variables that don't exist in the main @@ -829,6 +832,11 @@ export class Workspace implements IASTNodeLocation { this.variableMap = variableMap; } + /** Returns the map of all procedures on the workpace. */ + getProcedureMap(): IProcedureMap { + return this.procedureMap; + } + /** * Find the workspace with the specified ID. * diff --git a/tests/mocha/procedure_map_test.js b/tests/mocha/procedure_map_test.js index 34426eff493..848dda8dd18 100644 --- a/tests/mocha/procedure_map_test.js +++ b/tests/mocha/procedure_map_test.js @@ -12,9 +12,7 @@ suite('Procedure Map', function() { setup(function() { sharedTestSetup.call(this); this.workspace = new Blockly.Workspace(); - // this.procedureMap = this.workspace.getProcedureMap(); - this.procedureMap = - new Blockly.procedures.ObservableProcedureMap(this.workspace); + this.procedureMap = this.workspace.getProcedureMap(); }); teardown(function() { @@ -40,7 +38,8 @@ suite('Procedure Map', function() { suite('procedure map updates', function() { test('inserting a procedure does not trigger an update', function() { const procedureModel = - new Blockly.procedures.ObservableProcedureModel(this.workspace); + new Blockly.procedures.ObservableProcedureModel( + this.workspace, 'test name'); this.procedureMap.set(procedureModel.getId(), procedureModel); chai.assert.isFalse( @@ -49,7 +48,8 @@ suite('Procedure Map', function() { test('adding a procedure does not trigger an update', function() { this.procedureMap.add( - new Blockly.procedures.ObservableProcedureModel(this.workspace)); + new Blockly.procedures.ObservableProcedureModel( + this.workspace, 'test name')); chai.assert.isFalse( this.updateSpy.called, 'Expected no update to be triggered'); @@ -57,7 +57,8 @@ suite('Procedure Map', function() { test('deleting a procedure triggers an update', function() { const procedureModel = - new Blockly.procedures.ObservableProcedureModel(this.workspace); + new Blockly.procedures.ObservableProcedureModel( + this.workspace, 'test name'); this.procedureMap.add(procedureModel); this.procedureMap.delete(procedureModel.getId()); @@ -70,7 +71,8 @@ suite('Procedure Map', function() { suite('procedure model updates', function() { test('setting the name triggers an update', function() { const procedureModel = - new Blockly.procedures.ObservableProcedureModel(this.workspace); + new Blockly.procedures.ObservableProcedureModel( + this.workspace, 'test name'); this.procedureMap.add(procedureModel); procedureModel.setName('new name'); @@ -81,10 +83,11 @@ suite('Procedure Map', function() { test('setting the return type triggers an update', function() { const procedureModel = - new Blockly.procedures.ObservableProcedureModel(this.workspace); + new Blockly.procedures.ObservableProcedureModel( + this.workspace, 'test name'); this.procedureMap.add(procedureModel); - procedureModel.setReturnTypes(['return type 1', 'return type 2']); + procedureModel.setReturnTypes([]); chai.assert.isTrue( this.updateSpy.calledOnce, 'Expected an update to be triggered'); @@ -92,8 +95,9 @@ suite('Procedure Map', function() { test('removing the return type triggers an update', function() { const procedureModel = - new Blockly.procedures.ObservableProcedureModel(this.workspace) - .setReturnTypes(['return type']); + new Blockly.procedures.ObservableProcedureModel( + this.workspace, 'test name') + .setReturnTypes([]); this.procedureMap.add(procedureModel); this.updateSpy.resetHistory(); @@ -105,7 +109,8 @@ suite('Procedure Map', function() { test('disabling the procedure triggers an update', function() { const procedureModel = - new Blockly.procedures.ObservableProcedureModel(this.workspace); + new Blockly.procedures.ObservableProcedureModel( + this.workspace, 'test name'); this.procedureMap.add(procedureModel); procedureModel.setEnabled(false); @@ -116,7 +121,8 @@ suite('Procedure Map', function() { test('enabling the procedure triggers an update', function() { const procedureModel = - new Blockly.procedures.ObservableProcedureModel(this.workspace) + new Blockly.procedures.ObservableProcedureModel( + this.workspace, 'test name') .setEnabled(false); this.procedureMap.add(procedureModel); this.updateSpy.resetHistory(); @@ -129,7 +135,8 @@ suite('Procedure Map', function() { test('inserting a parameter triggers an update', function() { const procedureModel = - new Blockly.procedures.ObservableProcedureModel(this.workspace); + new Blockly.procedures.ObservableProcedureModel( + this.workspace, 'test name'); this.procedureMap.add(procedureModel); procedureModel.insertParameter( @@ -141,7 +148,8 @@ suite('Procedure Map', function() { test('deleting a parameter triggers an update', function() { const procedureModel = - new Blockly.procedures.ObservableProcedureModel(this.workspace) + new Blockly.procedures.ObservableProcedureModel( + this.workspace, 'test name') .insertParameter( new Blockly.procedures.ObservableParameterModel( this.workspace)); @@ -161,7 +169,8 @@ suite('Procedure Map', function() { new Blockly.procedures.ObservableParameterModel( this.workspace, 'test1'); this.procedureMap.add( - new Blockly.procedures.ObservableProcedureModel(this.workspace) + new Blockly.procedures.ObservableProcedureModel( + this.workspace, 'test name') .insertParameter(parameterModel)); this.updateSpy.resetHistory(); @@ -176,7 +185,8 @@ suite('Procedure Map', function() { new Blockly.procedures.ObservableParameterModel( this.workspace, 'test1'); this.procedureMap.add( - new Blockly.procedures.ObservableProcedureModel(this.workspace) + new Blockly.procedures.ObservableProcedureModel( + this.workspace, 'test name') .insertParameter(parameterModel)); this.updateSpy.resetHistory();