diff --git a/core/names.js b/core/names.js index bfe942ab9..6278c7dd2 100644 --- a/core/names.js +++ b/core/names.js @@ -26,6 +26,14 @@ goog.provide('Blockly.Names'); +/** + * Constant to separate developer variable names from user-defined variable + * names when running generators. + * A developer variable will be declared as a global in the generated code, but + * will never be shown to the user in the workspace or stored in the variable + * map. + */ +Blockly.Names.DEVELOPER_VARIABLE_TYPE = 'DEVELOPER_VARIABLE'; /** * Class for a database of entity names (variables, functions, etc). @@ -62,6 +70,41 @@ Blockly.Names = function(reservedWords, opt_variablePrefix) { Blockly.Names.prototype.reset = function() { this.db_ = Object.create(null); this.dbReverse_ = Object.create(null); + this.variableMap_ = null; +}; + +/** + * Set the variable map that maps from variable name to variable object. + * @param {!Blockly.VariableMap} map The map to track. + * @package + */ +Blockly.Names.prototype.setVariableMap = function(map) { + this.variableMap_ = map; +}; + +/** + * Get the name for a user-defined variable, based on its ID. + * This should only be used for variables of type Blockly.Variables.NAME_TYPE. + * @param {string} id The ID to look up in the variable map. + * @return {?string} The name of the referenced variable, or null if there was + * no variable map or the variable was not found in the map. + * @private + */ +Blockly.Names.prototype.getNameForUserVariable_ = function(id) { + if (!this.variableMap_) { + console.log('Deprecated call to Blockly.Names.prototype.getName without ' + + 'defining a variable map. To fix, add the folowing code in your ' + + 'generator\'s init() function:\n' + + 'Blockly.YourGeneratorName.variableDB_.setVariableMap(' + + 'workspace.getVariableMap());'); + return null; + } + var variable = this.variableMap_.getVariableById(id); + if (variable) { + return variable.name; + } else { + return null; + } }; /** @@ -72,9 +115,18 @@ Blockly.Names.prototype.reset = function() { * @return {string} An entity name legal for the exported language. */ Blockly.Names.prototype.getName = function(name, type) { + if (type == Blockly.Variables.NAME_TYPE) { + var varName = this.getNameForUserVariable_(name); + if (varName) { + name = varName; + } + } var normalized = name.toLowerCase() + '_' + type; - var prefix = (type == Blockly.Variables.NAME_TYPE) ? - this.variablePrefix_ : ''; + + var isVarType = type == Blockly.Variables.NAME_TYPE || + type == Blockly.Names.DEVELOPER_VARIABLE_TYPE; + + var prefix = isVarType ? this.variablePrefix_ : ''; if (normalized in this.db_) { return prefix + this.db_[normalized]; } @@ -103,8 +155,9 @@ Blockly.Names.prototype.getDistinctName = function(name, type) { } safeName += i; this.dbReverse_[safeName] = true; - var prefix = (type == Blockly.Variables.NAME_TYPE) ? - this.variablePrefix_ : ''; + var isVarType = type == Blockly.Variables.NAME_TYPE || + type == Blockly.Names.DEVELOPER_VARIABLE_TYPE; + var prefix = isVarType ? this.variablePrefix_ : ''; return prefix + safeName; }; diff --git a/core/variables.js b/core/variables.js index b8d40028b..3eb3bcedf 100644 --- a/core/variables.js +++ b/core/variables.js @@ -100,6 +100,38 @@ Blockly.Variables.allVariables = function(root) { return root.getAllVariables(); }; +/** + * Find all developer variables used by blocks in the workspace. + * Developer variables are never shown to the user, but are declared as global + * variables in the generated code. + * To declare developer variables, define the getDeveloperVariables function on + * your block and return a list of variable names. + * For use by generators. + * @param {!Blockly.Workspace} workspace The workspace to search. + * @return {!Array.} A list of non-duplicated variable names. + * @package + */ +Blockly.Variables.allDeveloperVariables = function(workspace) { + var blocks = workspace.getAllBlocks(); + var hash = {}; + for (var i = 0; i < blocks.length; i++) { + var block = blocks[i]; + if (block.getDeveloperVars) { + var devVars = block.getDeveloperVars(); + for (var j = 0; j < devVars.length; j++) { + hash[devVars[j]] = devVars[j]; + } + } + } + + // Flatten the hash into a list. + var list = []; + for (var name in hash) { + list.push(hash[name]); + } + return list; +}; + /** * Construct the elements (blocks and button) required by the flyout for the * variable category. diff --git a/generators/dart.js b/generators/dart.js index 83ef76cd6..1d3c93b75 100644 --- a/generators/dart.js +++ b/generators/dart.js @@ -102,13 +102,25 @@ Blockly.Dart.init = function(workspace) { Blockly.Dart.variableDB_.reset(); } + Blockly.Dart.variableDB_.setVariableMap(workspace.getVariableMap()); + var defvars = []; + // Add user variables. var variables = workspace.getAllVariables(); - if (variables.length) { - for (var i = 0; i < variables.length; i++) { - defvars[i] = Blockly.Dart.variableDB_.getName(variables[i].name, - Blockly.Variables.NAME_TYPE); - } + for (var i = 0; i < variables.length; i++) { + defvars[i] = Blockly.Dart.variableDB_.getName(variables[i].name, + Blockly.Variables.NAME_TYPE); + } + + // Add developer variables (not created or named by the user). + var devVarList = Blockly.Variables.allDeveloperVariables(workspace); + for (var i = 0; i < devVarList.length; i++) { + defvars.push(Blockly.Dart.variableDB_.getName(devVarList[i], + Blockly.Names.DEVELOPER_VARIABLE_TYPE)); + } + + // Declare all of the variables. + if (defvars.length) { Blockly.Dart.definitions_['variables'] = 'var ' + defvars.join(', ') + ';'; } diff --git a/generators/javascript.js b/generators/javascript.js index 00b96bcc1..864ae50c3 100644 --- a/generators/javascript.js +++ b/generators/javascript.js @@ -152,13 +152,25 @@ Blockly.JavaScript.init = function(workspace) { Blockly.JavaScript.variableDB_.reset(); } + Blockly.JavaScript.variableDB_.setVariableMap(workspace.getVariableMap()); + var defvars = []; + // Add user variables. var variables = workspace.getAllVariables(); - if (variables.length) { - for (var i = 0; i < variables.length; i++) { - defvars[i] = Blockly.JavaScript.variableDB_.getName(variables[i].name, - Blockly.Variables.NAME_TYPE); - } + for (var i = 0; i < variables.length; i++) { + defvars[i] = Blockly.JavaScript.variableDB_.getName(variables[i].name, + Blockly.Variables.NAME_TYPE); + } + + // Add developer variables (not created or named by the user). + var devVarList = Blockly.Variables.allDeveloperVariables(workspace); + for (var i = 0; i < devVarList.length; i++) { + defvars.push(Blockly.JavaScript.variableDB_.getName(devVarList[i], + Blockly.Names.DEVELOPER_VARIABLE_TYPE)); + } + + // Declare all of the variables. + if (defvars.length) { Blockly.JavaScript.definitions_['variables'] = 'var ' + defvars.join(', ') + ';'; } diff --git a/generators/lua.js b/generators/lua.js index 1118a31e4..b7f8d5c90 100644 --- a/generators/lua.js +++ b/generators/lua.js @@ -109,6 +109,7 @@ Blockly.Lua.init = function(workspace) { } else { Blockly.Lua.variableDB_.reset(); } + Blockly.Lua.variableDB_.setVariableMap(workspace.getVariableMap()); }; /** diff --git a/generators/php.js b/generators/php.js index 2c65a6809..6dc81ba62 100644 --- a/generators/php.js +++ b/generators/php.js @@ -149,14 +149,25 @@ Blockly.PHP.init = function(workspace) { Blockly.PHP.variableDB_.reset(); } + Blockly.PHP.variableDB_.setVariableMap(workspace.getVariableMap()); + var defvars = []; var varName; - var variables = Blockly.Variables.allVariables(workspace); + var variables = workspace.getAllVariables(); for (var i = 0, variable; variable = variables[i]; i++) { varName = variable.name; defvars[i] = Blockly.PHP.variableDB_.getName(varName, Blockly.Variables.NAME_TYPE) + ';'; } + + // Add developer variables (not created or named by the user). + var devVarList = Blockly.Variables.allDeveloperVariables(workspace); + for (var i = 0; i < devVarList.length; i++) { + defvars.push(Blockly.PHP.variableDB_.getName(devVarList[i], + Blockly.Names.DEVELOPER_VARIABLE_TYPE) + ';'); + } + + // Declare all of the variables. Blockly.PHP.definitions_['variables'] = defvars.join('\n'); }; diff --git a/generators/python.js b/generators/python.js index e81a04234..ab927d13b 100644 --- a/generators/python.js +++ b/generators/python.js @@ -160,12 +160,21 @@ Blockly.Python.init = function(workspace) { Blockly.Python.variableDB_.reset(); } + Blockly.Python.variableDB_.setVariableMap(workspace.getVariableMap()); + var defvars = []; var variables = workspace.getAllVariables(); for (var i = 0; i < variables.length; i++) { defvars[i] = Blockly.Python.variableDB_.getName(variables[i].name, Blockly.Variables.NAME_TYPE) + ' = None'; } + + // Add developer variables (not created or named by the user). + var devVarList = Blockly.Variables.allDeveloperVariables(workspace); + for (var i = 0; i < devVarList.length; i++) { + defvars.push(Blockly.Python.variableDB_.getName(devVarList[i], + Blockly.Names.DEVELOPER_VARIABLE_TYPE) + ' = None'); + } Blockly.Python.definitions_['variables'] = defvars.join('\n'); }; diff --git a/tests/generators/unittest.js b/tests/generators/unittest.js index 5c471d1b7..6b7378210 100644 --- a/tests/generators/unittest.js +++ b/tests/generators/unittest.js @@ -34,7 +34,7 @@ Blockly.Blocks['unittest_main'] = { this.setTooltip('Executes the enclosed unit tests,\n' + 'then prints a summary.'); }, - getVars: function() { + getDeveloperVars: function() { return ['unittestResults']; } }; @@ -54,7 +54,7 @@ Blockly.Blocks['unittest_assertequals'] = { .appendField('expected'); this.setTooltip('Tests that "actual == expected".'); }, - getVars: function() { + getDeveloperVars: function() { return ['unittestResults']; } }; @@ -74,7 +74,7 @@ Blockly.Blocks['unittest_assertvalue'] = { [['true', 'TRUE'], ['false', 'FALSE'], ['null', 'NULL']]), 'EXPECTED'); this.setTooltip('Tests that the value is true, false, or null.'); }, - getVars: function() { + getDeveloperVars: function() { return ['unittestResults']; } }; @@ -90,7 +90,7 @@ Blockly.Blocks['unittest_fail'] = { .appendField('fail'); this.setTooltip('Records an error.'); }, - getVars: function() { + getDeveloperVars: function() { return ['unittestResults']; } }; diff --git a/tests/generators/unittest_dart.js b/tests/generators/unittest_dart.js index 97e6ae466..436bcff63 100644 --- a/tests/generators/unittest_dart.js +++ b/tests/generators/unittest_dart.js @@ -27,7 +27,7 @@ Blockly.Dart['unittest_main'] = function(block) { // Container for unit tests. var resultsVar = Blockly.Dart.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.Dart.provideFunction_( 'unittest_report', [ 'String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '() {', @@ -71,7 +71,7 @@ Blockly.Dart['unittest_main'] = function(block) { Blockly.Dart['unittest_main'].defineAssert_ = function() { var resultsVar = Blockly.Dart.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.Dart.provideFunction_( 'unittest_assertequals', [ 'void ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + @@ -139,7 +139,7 @@ Blockly.Dart['unittest_assertvalue'] = function(block) { Blockly.Dart['unittest_fail'] = function(block) { // Always assert an error. var resultsVar = Blockly.Dart.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var message = Blockly.Dart.quote_(block.getFieldValue('MESSAGE')); var functionName = Blockly.Dart.provideFunction_( 'unittest_fail', diff --git a/tests/generators/unittest_javascript.js b/tests/generators/unittest_javascript.js index 9fda5be30..92d8a2913 100644 --- a/tests/generators/unittest_javascript.js +++ b/tests/generators/unittest_javascript.js @@ -27,7 +27,7 @@ Blockly.JavaScript['unittest_main'] = function(block) { // Container for unit tests. var resultsVar = Blockly.JavaScript.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.JavaScript.provideFunction_( 'unittest_report', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '() {', @@ -72,7 +72,7 @@ Blockly.JavaScript['unittest_main'] = function(block) { Blockly.JavaScript['unittest_main'].defineAssert_ = function(block) { var resultsVar = Blockly.JavaScript.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.JavaScript.provideFunction_( 'assertEquals', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + @@ -143,7 +143,7 @@ Blockly.JavaScript['unittest_assertvalue'] = function(block) { Blockly.JavaScript['unittest_fail'] = function(block) { // Always assert an error. var resultsVar = Blockly.JavaScript.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var message = Blockly.JavaScript.quote_(block.getFieldValue('MESSAGE')); var functionName = Blockly.JavaScript.provideFunction_( 'unittest_fail', diff --git a/tests/generators/unittest_lua.js b/tests/generators/unittest_lua.js index 73c5dbe0d..2a9015c10 100644 --- a/tests/generators/unittest_lua.js +++ b/tests/generators/unittest_lua.js @@ -27,7 +27,7 @@ Blockly.Lua['unittest_main'] = function(block) { // Container for unit tests. var resultsVar = Blockly.Lua.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.Lua.provideFunction_( 'unittest_report', ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '()', @@ -70,7 +70,7 @@ Blockly.Lua['unittest_main'] = function(block) { Blockly.Lua['unittest_main'].defineAssert_ = function(block) { var resultsVar = Blockly.Lua.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.Lua.provideFunction_( 'assertEquals', ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + @@ -149,7 +149,7 @@ Blockly.Lua['unittest_assertvalue'] = function(block) { Blockly.Lua['unittest_fail'] = function(block) { // Always assert an error. var resultsVar = Blockly.Lua.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var message = Blockly.Lua.quote_(block.getFieldValue('MESSAGE')); var functionName = Blockly.Lua.provideFunction_( 'unittest_fail', diff --git a/tests/generators/unittest_php.js b/tests/generators/unittest_php.js index 528e5971d..54d7e4e97 100644 --- a/tests/generators/unittest_php.js +++ b/tests/generators/unittest_php.js @@ -27,7 +27,7 @@ Blockly.PHP['unittest_main'] = function(block) { // Container for unit tests. var resultsVar = Blockly.PHP.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.PHP.provideFunction_( 'unittest_report', [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '() {', @@ -72,7 +72,7 @@ Blockly.PHP['unittest_main'] = function(block) { Blockly.PHP['unittest_main'].defineAssert_ = function(block) { var resultsVar = Blockly.PHP.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.PHP.provideFunction_( 'assertEquals', ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + @@ -129,7 +129,7 @@ Blockly.PHP['unittest_assertvalue'] = function(block) { Blockly.PHP['unittest_fail'] = function(block) { // Always assert an error. var resultsVar = Blockly.PHP.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var message = Blockly.PHP.quote_(block.getFieldValue('MESSAGE')); var functionName = Blockly.PHP.provideFunction_( 'unittest_fail', diff --git a/tests/generators/unittest_python.js b/tests/generators/unittest_python.js index 5e00e26be..91fabd179 100644 --- a/tests/generators/unittest_python.js +++ b/tests/generators/unittest_python.js @@ -27,7 +27,7 @@ Blockly.Python['unittest_main'] = function(block) { // Container for unit tests. var resultsVar = Blockly.Python.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.Python.provideFunction_( 'unittest_report', ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + '():', @@ -68,7 +68,7 @@ Blockly.Python['unittest_main'] = function(block) { Blockly.Python['unittest_main'].defineAssert_ = function() { var resultsVar = Blockly.Python.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.Python.provideFunction_( 'assertEquals', ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + @@ -117,7 +117,7 @@ Blockly.Python['unittest_assertvalue'] = function(block) { Blockly.Python['unittest_fail'] = function(block) { // Always assert an error. var resultsVar = Blockly.Python.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var message = Blockly.Python.quote_(block.getFieldValue('MESSAGE')); var functionName = Blockly.Python.provideFunction_( 'fail',