Update sensing_of fields if variable gets renamed

Thanks @adroitwhiz and @fsih for some advice!
This commit is contained in:
apple502j 2020-05-22 19:00:43 +09:00
parent 7af161f1a2
commit 03db30d400
3 changed files with 183 additions and 1 deletions

View file

@ -946,6 +946,32 @@ class Blocks {
}
}
/**
* Update sensing_of blocks after a variable gets renamed.
* @param {string} oldName The old name of the variable that was renamed.
* @param {string} newName The new name of the variable that was renamed.
* @param {string} targetName The name of the target the variable belongs to.
* @return {boolean} Returns true if any of the blocks were updated.
*/
updateSensingOfReference (oldName, newName, targetName) {
const blocks = this._blocks;
let blockUpdated = false;
for (const blockId in blocks) {
const block = blocks[blockId];
if (block.opcode === 'sensing_of' &&
block.fields.PROPERTY.value === oldName &&
// If block and shadow are different, it means a block is inserted to OBJECT, and should be ignored.
block.inputs.OBJECT.block === block.inputs.OBJECT.shadow) {
const inputBlock = this.getBlock(block.inputs.OBJECT.block);
if (inputBlock.fields.OBJECT.value === targetName) {
block.fields.PROPERTY.value = newName;
blockUpdated = true;
}
}
}
return blockUpdated;
}
/**
* Helper function to retrieve a costume menu field from a block given its id.
* @param {string} blockId A unique identifier for a block

View file

@ -322,11 +322,26 @@ class Target extends EventEmitter {
this.runtime.ioDevices.cloud.requestRenameVariable(oldName, newName);
}
if (variable.type === Variable.SCALAR_TYPE) {
// sensing__of may be referencing to this variable.
// Change the reference.
let blockUpdated = false;
this.runtime.targets.forEach(t => {
blockUpdated = t.blocks.updateSensingOfReference(
oldName,
newName,
this.isStage ? '__stage__' : this.getName()
) || blockUpdated;
});
// Request workspace change only if sensing_of blocks were actually updated.
if (blockUpdated) this.runtime.requestBlocksUpdate();
}
const blocks = this.runtime.monitorBlocks;
blocks.changeBlock({
id: id,
element: 'field',
name: variable.type === 'list' ? 'LIST' : 'VARIABLE',
name: variable.type === Variable.LIST_TYPE ? 'LIST' : 'VARIABLE',
value: id
}, this.runtime);
const monitorBlock = blocks.getBlock(variable.id);

View file

@ -26,6 +26,7 @@ test('spec', t => {
t.type(b.getBranch, 'function');
t.type(b.getOpcode, 'function');
t.type(b.mutationToXML, 'function');
t.type(b.updateSensingOfReference, 'function');
t.end();
});
@ -807,6 +808,146 @@ test('updateAssetName doesn\'t update name if name isn\'t being used', t => {
t.end();
});
test('updateSensingOfReference renames variables in sensing_of block', t => {
const b = new Blocks(new Runtime());
b.createBlock({
id: 'id1',
opcode: 'sensing_of',
fields: {
PROPERTY: {
name: 'PROPERTY',
value: 'foo'
}
},
inputs: {
OBJECT: {
name: 'OBJECT',
block: 'id2',
shadow: 'id2'
}
}
});
b.createBlock({
id: 'id2',
fields: {
OBJECT: {
name: 'OBJECT',
value: '_stage_'
}
}
});
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo');
b.updateSensingOfReference('foo', 'bar', '_stage_');
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'bar');
t.end();
});
test('updateSensingOfReference doesn\'t rename if block is inserted', t => {
const b = new Blocks(new Runtime());
b.createBlock({
id: 'id1',
opcode: 'sensing_of',
fields: {
PROPERTY: {
name: 'PROPERTY',
value: 'foo'
}
},
inputs: {
OBJECT: {
name: 'OBJECT',
block: 'id3',
shadow: 'id2'
}
}
});
b.createBlock({
id: 'id2',
fields: {
OBJECT: {
name: 'OBJECT',
value: '_stage_'
}
}
});
b.createBlock({
id: 'id3',
opcode: 'answer'
});
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo');
b.updateSensingOfReference('foo', 'bar', '_stage_');
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo');
t.end();
});
test('updateSensingOfReference doesn\'t rename if name is not being used', t => {
const b = new Blocks(new Runtime());
b.createBlock({
id: 'id1',
opcode: 'sensing_of',
fields: {
PROPERTY: {
name: 'PROPERTY',
value: 'foo'
}
},
inputs: {
OBJECT: {
name: 'OBJECT',
block: 'id2',
shadow: 'id2'
}
}
});
b.createBlock({
id: 'id2',
fields: {
OBJECT: {
name: 'OBJECT',
value: '_stage_'
}
}
});
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo');
b.updateSensingOfReference('meow', 'meow2', '_stage_');
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo');
t.end();
});
test('updateSensingOfReference doesn\'t rename other targets\' variables', t => {
const b = new Blocks(new Runtime());
b.createBlock({
id: 'id1',
opcode: 'sensing_of',
fields: {
PROPERTY: {
name: 'PROPERTY',
value: 'foo'
}
},
inputs: {
OBJECT: {
name: 'OBJECT',
block: 'id2',
shadow: 'id2'
}
}
});
b.createBlock({
id: 'id2',
fields: {
OBJECT: {
name: 'OBJECT',
value: '_stage_'
}
}
});
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo');
b.updateSensingOfReference('foo', 'bar', 'Cat');
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo');
t.end();
});
test('updateTargetSpecificBlocks changes sprite clicked hat to stage clicked for stage', t => {
const b = new Blocks(new Runtime());
b.createBlock({