diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 218e9271d..eba537af9 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -693,12 +693,15 @@ class Blocks { /** * Returns a map of all references to variables or lists from blocks * in this block container. + * @param {Array} optBlocks Optional list of blocks to constrain the search to. + * This is useful for getting variable/list references for a stack of blocks instead + * of all blocks on the workspace * @return {object} A map of variable ID to a list of all variable references * for that ID. A variable reference contains the field referencing that variable * and also the type of the variable being referenced. */ - getAllVariableAndListReferences () { - const blocks = this._blocks; + getAllVariableAndListReferences (optBlocks) { + const blocks = optBlocks ? optBlocks : this._blocks; const allReferences = Object.create(null); for (const blockId in blocks) { let varOrListField = null; diff --git a/src/virtual-machine.js b/src/virtual-machine.js index d46e1e68d..87a81e8eb 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -1000,7 +1000,66 @@ class VirtualMachine extends EventEmitter { * @param {!string} targetId Id of target to add blocks to. */ shareBlocksToTarget (blocks, targetId) { + const currTarget = this.runtime.getEditingTarget(); const target = this.runtime.getTargetById(targetId); + // Resolve any potential local variable conflicts if the current sprite (sharing the love) + // is a non-stage target. + if (!currTarget.isStage) { + const allVarListRefs = currTarget.blocks.getAllVariableAndListReferences(blocks); + for (const varId in allVarListRefs) { + const currVarListRefs = allVarListRefs[varId]; + const currVar = currTarget.variables[varId]; + if (!currVar) continue; // If the currVar is global, skip it + + const varName = currVar.name; + const varType = currVar.type; + let newVarId = ''; + let existingVar; + if (target.isStage) { + // If a local var is being shared with the stage, + // we'll prefix the ID of the current variable, + // and use a fresh, prefixed name for the variable. + // This way, multiple share the loves from the same target, referring + // the same variable will map to the same new variable on the stage. + const varIdForStage = `StageVarFromLocal_${varId}`; + existingVar = target.lookupVariableById(varIdForStage); + if (!existingVar) { + let allVarNames = []; + for (const t of this.runtime.targets) { + // TODO allVarNames will probably contain duplicates, should we filter them out + // or create a set instead? + allVarNames = allVarNames.concat(t.getAllVariableNamesInScopeByType(varType)); + } + const newStageVarName = StringUtil.unusedName(`Stage: ${varName}`, allVarNames); + existingVar = new Variable(varIdForStage, newStageVarName, varType); + target.variables[varIdForStage] = existingVar; + } + // Update all variable references to use the new name + currVarListRefs.map(ref => { + const field = ref.referencingField; + field.value = existingVar.name; + field.id = existingVar.id; + return ref; + }); + } else { + existingVar = target.lookupVariableByNameAndType( + varName, varType); + if (existingVar) { + newVarId = existingVar.id; + } else { + const newVar = new Variable(null, varName, varType); + newVarId = newVar.id; + target.variables[newVarId] = newVar; + } + // Update all the variable references to use the new ID + currVarListRefs.map(ref => { + ref.referencingField.id = newVarId; + return ref; + }); + } + } + } + for (let i = 0; i < blocks.length; i++) { target.blocks.createBlock(blocks[i]); }