mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-22 14:02:37 -05:00
Update sensing_of fields if variable gets renamed
Thanks @adroitwhiz and @fsih for some advice!
This commit is contained in:
parent
7af161f1a2
commit
03db30d400
3 changed files with 183 additions and 1 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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({
|
||||
|
|
Loading…
Reference in a new issue