mirror of
https://github.com/google/blockly.git
synced 2026-01-04 15:40:08 +01:00
feat: procedure defs deserialize models (#6706)
* feat: add procedure defs deserializing models * chore: rename model to model_ * chore: cleanup * chore: fix tests failures * fix: PR comments
This commit is contained in:
@@ -92,6 +92,7 @@ const blocks = createBlockDefinitionsFromJsonArray([
|
||||
'procedure_def_validator_helper',
|
||||
'procedure_defnoreturn_get_caller_block_mixin',
|
||||
'procedure_defnoreturn_set_comment_helper',
|
||||
'procedure_def_set_no_return_helper',
|
||||
],
|
||||
'mutator': 'procedure_def_mutator',
|
||||
},
|
||||
@@ -168,6 +169,7 @@ const blocks = createBlockDefinitionsFromJsonArray([
|
||||
'procedure_def_validator_helper',
|
||||
'procedure_defreturn_get_caller_block_mixin',
|
||||
'procedure_defreturn_set_comment_helper',
|
||||
'procedure_def_set_return_helper',
|
||||
],
|
||||
'mutator': 'procedure_def_mutator',
|
||||
},
|
||||
@@ -279,10 +281,15 @@ exports.blocks = blocks;
|
||||
/** @this {Block} */
|
||||
const procedureDefGetDefMixin = function() {
|
||||
const mixin = {
|
||||
model: null,
|
||||
model_: null,
|
||||
|
||||
/**
|
||||
* Returns the data model for this procedure block.
|
||||
* @return {!IProcedureModel} The data model for this procedure
|
||||
* block.
|
||||
*/
|
||||
getProcedureModel() {
|
||||
return this.model;
|
||||
return this.model_;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -312,9 +319,9 @@ const procedureDefGetDefMixin = function() {
|
||||
},
|
||||
};
|
||||
|
||||
mixin.model =
|
||||
mixin.model_ =
|
||||
new ObservableProcedureModel(this.workspace, this.getFieldValue('NAME'));
|
||||
this.workspace.getProcedureMap().add(mixin.model);
|
||||
this.workspace.getProcedureMap().add(mixin.getProcedureModel());
|
||||
|
||||
this.mixin(mixin, true);
|
||||
};
|
||||
@@ -395,8 +402,8 @@ const procedureDefUpdateShapeMixin = {
|
||||
* Updates the block to reflect the state of the procedure model.
|
||||
*/
|
||||
doProcedureUpdate: function() {
|
||||
this.setFieldValue(this.model.getName(), 'NAME');
|
||||
this.setEnabled(this.model.getEnabled());
|
||||
this.setFieldValue(this.getProcedureModel().getName(), 'NAME');
|
||||
this.setEnabled(this.getProcedureModel().getEnabled());
|
||||
this.updateParameters_();
|
||||
},
|
||||
|
||||
@@ -405,7 +412,8 @@ const procedureDefUpdateShapeMixin = {
|
||||
* model.
|
||||
*/
|
||||
updateParameters_: function() {
|
||||
const params = this.model.getParameters().map((p) => p.getName());
|
||||
const params =
|
||||
this.getProcedureModel().getParameters().map((p) => p.getName());
|
||||
const paramString = params.length ?
|
||||
`${Msg['PROCEDURES_BEFORE_PARAMS']} ${params.join(', ')}` :
|
||||
'';
|
||||
@@ -551,6 +559,17 @@ const procedureDefMutator = {
|
||||
* @this {Block}
|
||||
*/
|
||||
domToMutation: function(xmlElement) {
|
||||
for (let i = 0; i < xmlElement.childNodes.length; i++) {
|
||||
const node = xmlElement.childNodes[i];
|
||||
if (node.nodeName.toLowerCase() !== 'arg') continue;
|
||||
this.getProcedureModel().insertParameter(
|
||||
new ObservableParameterModel(
|
||||
this.workspace, node.getAttribute('name'),
|
||||
node.getAttribute('varid')),
|
||||
i);
|
||||
}
|
||||
|
||||
// TODO: Remove this data update code.
|
||||
this.arguments_ = [];
|
||||
this.argumentVarModels_ = [];
|
||||
for (let i = 0, childNode; (childNode = xmlElement.childNodes[i]); i++) {
|
||||
@@ -610,6 +629,16 @@ const procedureDefMutator = {
|
||||
* statements.
|
||||
*/
|
||||
loadExtraState: function(state) {
|
||||
if (state['params']) {
|
||||
for (let i = 0; i < state['params'].length; i++) {
|
||||
const param = state['params'][i];
|
||||
this.getProcedureModel().insertParameter(
|
||||
new ObservableParameterModel(this.workspace, param.name, param.id),
|
||||
i);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove this data update code.
|
||||
this.arguments_ = [];
|
||||
this.argumentVarModels_ = [];
|
||||
if (state['params']) {
|
||||
@@ -694,14 +723,16 @@ const procedureDefMutator = {
|
||||
}
|
||||
this.updateParams_();
|
||||
Procedures.mutateCallers(this);
|
||||
for (let i = this.model.getParameters().length; i >= 0; i--) {
|
||||
this.model.deleteParameter(i);
|
||||
|
||||
const model = this.getProcedureModel();
|
||||
for (let i = model.getParameters().length; i >= 0; i--) {
|
||||
model.deleteParameter(i);
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
paramBlock = containerBlock.getInputTargetBlock('STACK');
|
||||
while (paramBlock && !paramBlock.isInsertionMarker()) {
|
||||
this.model.insertParameter(
|
||||
model.insertParameter(
|
||||
new ObservableParameterModel(
|
||||
this.workspace, paramBlock.getFieldValue('NAME'), paramBlock.id),
|
||||
i);
|
||||
@@ -771,8 +802,8 @@ Extensions.registerMixin(
|
||||
const procedureDefOnChangeMixin = {
|
||||
onchange: function(e) {
|
||||
if (e.type === Events.BLOCK_CHANGE && e.blockId === this.id &&
|
||||
e.element == 'disabled') {
|
||||
this.model.setEnabled(!e.newValue);
|
||||
e.element === 'disabled') {
|
||||
this.getProcedureModel().setEnabled(!e.newValue);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -843,6 +874,20 @@ Extensions.registerMixin(
|
||||
'procedure_defreturn_get_caller_block_mixin',
|
||||
procedureDefReturnGetCallerBlockMixin);
|
||||
|
||||
/** @this {Block} */
|
||||
const procedureDefSetNoReturnHelper = function() {
|
||||
this.getProcedureModel().setReturnTypes(null);
|
||||
};
|
||||
Extensions.register(
|
||||
'procedure_def_set_no_return_helper', procedureDefSetNoReturnHelper);
|
||||
|
||||
/** @this {Block} */
|
||||
const procedureDefSetReturnHelper = function() {
|
||||
this.getProcedureModel().setReturnTypes([]);
|
||||
};
|
||||
Extensions.register(
|
||||
'procedure_def_set_return_helper', procedureDefSetReturnHelper);
|
||||
|
||||
const validateProcedureParamMixin = {
|
||||
/**
|
||||
* Obtain a valid name for the procedure argument. Create a variable if
|
||||
|
||||
@@ -24,8 +24,8 @@ export class ObservableParameterModel implements IParameterModel {
|
||||
constructor(
|
||||
private readonly workspace: Workspace, name: string, id?: string) {
|
||||
this.id = id ?? genUid();
|
||||
this.variable =
|
||||
this.workspace.getVariable(name) ?? workspace.createVariable(name);
|
||||
this.variable = this.workspace.getVariable(name) ??
|
||||
workspace.createVariable(name, '', id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -452,6 +452,256 @@ suite('Procedures', function() {
|
||||
});
|
||||
});
|
||||
|
||||
suite('deserializing data models', function() {
|
||||
suite('return types', function() {
|
||||
test('procedure defs without returns have null return types', function() {
|
||||
const json = {
|
||||
'blocks': {
|
||||
'languageVersion': 0,
|
||||
'blocks': [
|
||||
{
|
||||
'type': 'procedures_defnoreturn',
|
||||
'fields': {
|
||||
'NAME': 'test name',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
Blockly.serialization.workspaces.load(json, this.workspace);
|
||||
const procedureModel =
|
||||
this.workspace.getProcedureMap().getProcedures()[0];
|
||||
|
||||
chai.assert.isNull(
|
||||
procedureModel.getReturnTypes(),
|
||||
'Expected the return types to be null');
|
||||
});
|
||||
|
||||
test('procedure defs with returns have array return types', function() {
|
||||
const json = {
|
||||
'blocks': {
|
||||
'languageVersion': 0,
|
||||
'blocks': [
|
||||
{
|
||||
'type': 'procedures_defreturn',
|
||||
'fields': {
|
||||
'NAME': 'test name',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
Blockly.serialization.workspaces.load(json, this.workspace);
|
||||
const procedureModel =
|
||||
this.workspace.getProcedureMap().getProcedures()[0];
|
||||
|
||||
chai.assert.isArray(
|
||||
procedureModel.getReturnTypes(),
|
||||
'Expected the return types to be an array');
|
||||
});
|
||||
});
|
||||
|
||||
suite('json', function() {
|
||||
test('procedure names get deserialized', function() {
|
||||
const json = {
|
||||
'blocks': {
|
||||
'languageVersion': 0,
|
||||
'blocks': [
|
||||
{
|
||||
'type': 'procedures_defnoreturn',
|
||||
'fields': {
|
||||
'NAME': 'test name',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
Blockly.serialization.workspaces.load(json, this.workspace);
|
||||
const procedureModel =
|
||||
this.workspace.getProcedureMap().getProcedures()[0];
|
||||
|
||||
chai.assert.equal(
|
||||
procedureModel.name,
|
||||
'test name',
|
||||
'Expected the name of the procedure model to equal the name ' +
|
||||
'being deserialized.');
|
||||
});
|
||||
|
||||
test('procedure parameter names get deserialized', function() {
|
||||
const json = {
|
||||
'blocks': {
|
||||
'languageVersion': 0,
|
||||
'blocks': [
|
||||
{
|
||||
'type': 'procedures_defnoreturn',
|
||||
'fields': {
|
||||
'NAME': 'test name',
|
||||
},
|
||||
'extraState': {
|
||||
'params': [
|
||||
{
|
||||
'id': 'test id 1',
|
||||
'name': 'test name 1',
|
||||
},
|
||||
{
|
||||
'id': 'test id 2',
|
||||
'name': 'test name 2',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
Blockly.serialization.workspaces.load(json, this.workspace);
|
||||
const procedureModel =
|
||||
this.workspace.getProcedureMap().getProcedures()[0];
|
||||
|
||||
chai.assert.equal(
|
||||
procedureModel.getParameter(0).getName(),
|
||||
'test name 1',
|
||||
'Expected the name of the first parameter to equal the name ' +
|
||||
'being deserialized.');
|
||||
chai.assert.equal(
|
||||
procedureModel.getParameter(1).getName(),
|
||||
'test name 2',
|
||||
'Expected the name of the second parameter to equal the name ' +
|
||||
'being deserialized.');
|
||||
});
|
||||
|
||||
test('procedure variables get matching IDs', function() {
|
||||
const json = {
|
||||
'blocks': {
|
||||
'languageVersion': 0,
|
||||
'blocks': [
|
||||
{
|
||||
'type': 'procedures_defnoreturn',
|
||||
'extraState': {
|
||||
'params': [
|
||||
{
|
||||
'name': 'test param name',
|
||||
'id': 'test param id',
|
||||
},
|
||||
],
|
||||
},
|
||||
'fields': {
|
||||
'NAME': 'test proc name',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
'variables': [
|
||||
{
|
||||
'name': 'test param name',
|
||||
'id': 'test param id',
|
||||
},
|
||||
],
|
||||
};
|
||||
Blockly.serialization.workspaces.load(json, this.workspace);
|
||||
const procedureModel =
|
||||
this.workspace.getProcedureMap().getProcedures()[0];
|
||||
|
||||
chai.assert.equal(
|
||||
procedureModel.getParameter(0).getVariableModel().getId(),
|
||||
'test param id',
|
||||
'Expected the variable id to match the serialized param id');
|
||||
});
|
||||
});
|
||||
|
||||
suite('xml', function() {
|
||||
test('procedure names get deserialized', function() {
|
||||
const xml = Blockly.Xml.textToDom(
|
||||
`<block type="procedures_defnoreturn">` +
|
||||
` <field name="NAME">test name</field>` +
|
||||
`</block>`);
|
||||
Blockly.Xml.domToBlock(xml, this.workspace);
|
||||
const procedureModel =
|
||||
this.workspace.getProcedureMap().getProcedures()[0];
|
||||
|
||||
chai.assert.equal(
|
||||
procedureModel.name,
|
||||
'test name',
|
||||
'Expected the name of the procedure model to equal the name ' +
|
||||
'being deserialized.');
|
||||
});
|
||||
|
||||
test('procedure parameter names get deserialized', function() {
|
||||
const xml = Blockly.Xml.textToDom(
|
||||
`<block type="procedures_defnoreturn">` +
|
||||
` <mutation>` +
|
||||
` <arg name="test name 1" varid="test var id 1"/>` +
|
||||
` <arg name="test name 2" varid="test var id 2"/>` +
|
||||
` </mutation>` +
|
||||
` <field name="NAME">test name</field>` +
|
||||
`</block>`);
|
||||
Blockly.Xml.domToBlock(xml, this.workspace);
|
||||
const procedureModel =
|
||||
this.workspace.getProcedureMap().getProcedures()[0];
|
||||
|
||||
chai.assert.equal(
|
||||
procedureModel.getParameter(0).getName(),
|
||||
'test name 1',
|
||||
'Expected the name of the first parameter to equal the name ' +
|
||||
'being deserialized.');
|
||||
chai.assert.equal(
|
||||
procedureModel.getParameter(1).getName(),
|
||||
'test name 2',
|
||||
'Expected the name of the second parameter to equal the name ' +
|
||||
'being deserialized.');
|
||||
});
|
||||
|
||||
test('procedure variables get matching IDs', function() {
|
||||
const json = {
|
||||
'blocks': {
|
||||
'languageVersion': 0,
|
||||
'blocks': [
|
||||
{
|
||||
'type': 'procedures_defnoreturn',
|
||||
'extraState': {
|
||||
'params': [
|
||||
{
|
||||
'name': 'test param name',
|
||||
'id': 'test param id',
|
||||
},
|
||||
],
|
||||
},
|
||||
'fields': {
|
||||
'NAME': 'test proc name',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
'variables': [
|
||||
{
|
||||
'name': 'test param name',
|
||||
'id': 'test param id',
|
||||
},
|
||||
],
|
||||
};
|
||||
const xml = Blockly.Xml.textToDom(
|
||||
`<xml>` +
|
||||
` <variables>` +
|
||||
` <variable id ="test param id">test param name</variable>` +
|
||||
` </variables>` +
|
||||
` <block type="procedures_defnoreturn">` +
|
||||
` <mutation>` +
|
||||
` <arg name="test param name" varid="test param id"/>` +
|
||||
` </mutation>` +
|
||||
` <field name="NAME">test name</field>` +
|
||||
` </block>` +
|
||||
`</xml>`);
|
||||
Blockly.Xml.domToWorkspace(xml, this.workspace);
|
||||
const procedureModel =
|
||||
this.workspace.getProcedureMap().getProcedures()[0];
|
||||
|
||||
chai.assert.equal(
|
||||
procedureModel.getParameter(0).getVariableModel().getId(),
|
||||
'test param id',
|
||||
'Expected the variable id to match the serialized param id');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('Renaming procedures', function() {
|
||||
test('callers are updated to have the new name', function() {
|
||||
const defBlock = createProcDefBlock(this.workspace);
|
||||
|
||||
Reference in New Issue
Block a user