diff --git a/core/generator.js b/core/generator.js index f6f5e7a75..b5a794959 100644 --- a/core/generator.js +++ b/core/generator.js @@ -158,17 +158,18 @@ Blockly.Generator.prototype.allNestedComments = function(block) { /** * Generate code for the specified block (and attached blocks). * @param {Blockly.Block} block The block to generate code for. + * @param {boolean=} opt_thisOnly True to generate code for only this statement. * @return {string|!Array} For statement blocks, the generated code. * For value blocks, an array containing the generated code and an * operator order value. Returns '' if block is null. */ -Blockly.Generator.prototype.blockToCode = function(block) { +Blockly.Generator.prototype.blockToCode = function(block, opt_thisOnly) { if (!block) { return ''; } if (block.disabled) { // Skip past this block if it is disabled. - return this.blockToCode(block.getNextBlock()); + return opt_thisOnly ? '' : this.blockToCode(block.getNextBlock()); } var func = this[block.type]; @@ -186,13 +187,13 @@ Blockly.Generator.prototype.blockToCode = function(block) { if (!block.outputConnection) { throw TypeError('Expecting string from statement block: ' + block.type); } - return [this.scrub_(block, code[0]), code[1]]; + return [this.scrub_(block, code[0], opt_thisOnly), code[1]]; } else if (typeof code == 'string') { var id = block.id.replace(/\$/g, '$$$$'); // Issue 251. if (this.STATEMENT_PREFIX) { code = this.STATEMENT_PREFIX.replace(/%1/g, '\'' + id + '\'') + code; } - return this.scrub_(block, code); + return this.scrub_(block, code, opt_thisOnly); } else if (code === null) { // Block has handled code generation itself. return ''; diff --git a/generators/dart.js b/generators/dart.js index 4dfb9971f..cc24151d8 100644 --- a/generators/dart.js +++ b/generators/dart.js @@ -188,10 +188,11 @@ Blockly.Dart.quote_ = function(string) { * Calls any statements following this block. * @param {!Blockly.Block} block The current block. * @param {string} code The Dart code created for this block. + * @param {boolean=} opt_thisOnly True to generate code for only this statement. * @return {string} Dart code with comments and subsequent blocks added. * @private */ -Blockly.Dart.scrub_ = function(block, code) { +Blockly.Dart.scrub_ = function(block, code, opt_thisOnly) { var commentCode = ''; // Only collect comments for blocks that aren't inline. if (!block.outputConnection || !block.outputConnection.targetConnection) { @@ -221,7 +222,7 @@ Blockly.Dart.scrub_ = function(block, code) { } } var nextBlock = block.nextConnection && block.nextConnection.targetBlock(); - var nextCode = Blockly.Dart.blockToCode(nextBlock); + var nextCode = opt_thisOnly ? '' : Blockly.Dart.blockToCode(nextBlock); return commentCode + code + nextCode; }; diff --git a/generators/javascript.js b/generators/javascript.js index aa05826c2..ce38b726a 100644 --- a/generators/javascript.js +++ b/generators/javascript.js @@ -229,10 +229,11 @@ Blockly.JavaScript.quote_ = function(string) { * Calls any statements following this block. * @param {!Blockly.Block} block The current block. * @param {string} code The JavaScript code created for this block. + * @param {boolean=} opt_thisOnly True to generate code for only this statement. * @return {string} JavaScript code with comments and subsequent blocks added. * @private */ -Blockly.JavaScript.scrub_ = function(block, code) { +Blockly.JavaScript.scrub_ = function(block, code, opt_thisOnly) { var commentCode = ''; // Only collect comments for blocks that aren't inline. if (!block.outputConnection || !block.outputConnection.targetConnection) { @@ -264,7 +265,7 @@ Blockly.JavaScript.scrub_ = function(block, code) { } } var nextBlock = block.nextConnection && block.nextConnection.targetBlock(); - var nextCode = Blockly.JavaScript.blockToCode(nextBlock); + var nextCode = opt_thisOnly ? '' : Blockly.JavaScript.blockToCode(nextBlock); return commentCode + code + nextCode; }; diff --git a/generators/lua.js b/generators/lua.js index b7f8d5c90..0e05c24dc 100644 --- a/generators/lua.js +++ b/generators/lua.js @@ -162,10 +162,11 @@ Blockly.Lua.quote_ = function(string) { * Calls any statements following this block. * @param {!Blockly.Block} block The current block. * @param {string} code The Lua code created for this block. + * @param {boolean=} opt_thisOnly True to generate code for only this statement. * @return {string} Lua code with comments and subsequent blocks added. * @private */ -Blockly.Lua.scrub_ = function(block, code) { +Blockly.Lua.scrub_ = function(block, code, opt_thisOnly) { var commentCode = ''; // Only collect comments for blocks that aren't inline. if (!block.outputConnection || !block.outputConnection.targetConnection) { @@ -190,6 +191,6 @@ Blockly.Lua.scrub_ = function(block, code) { } } var nextBlock = block.nextConnection && block.nextConnection.targetBlock(); - var nextCode = Blockly.Lua.blockToCode(nextBlock); + var nextCode = opt_thisOnly ? '' : Blockly.Lua.blockToCode(nextBlock); return commentCode + code + nextCode; }; diff --git a/generators/php.js b/generators/php.js index cd53662dc..cd717abe3 100644 --- a/generators/php.js +++ b/generators/php.js @@ -218,10 +218,11 @@ Blockly.PHP.quote_ = function(string) { * Calls any statements following this block. * @param {!Blockly.Block} block The current block. * @param {string} code The PHP code created for this block. + * @param {boolean=} opt_thisOnly True to generate code for only this statement. * @return {string} PHP code with comments and subsequent blocks added. * @private */ -Blockly.PHP.scrub_ = function(block, code) { +Blockly.PHP.scrub_ = function(block, code, opt_thisOnly) { var commentCode = ''; // Only collect comments for blocks that aren't inline. if (!block.outputConnection || !block.outputConnection.targetConnection) { @@ -246,7 +247,7 @@ Blockly.PHP.scrub_ = function(block, code) { } } var nextBlock = block.nextConnection && block.nextConnection.targetBlock(); - var nextCode = Blockly.PHP.blockToCode(nextBlock); + var nextCode = opt_thisOnly ? '' : Blockly.PHP.blockToCode(nextBlock); return commentCode + code + nextCode; }; diff --git a/generators/python.js b/generators/python.js index 1c25f7ba7..c3d45b8c1 100644 --- a/generators/python.js +++ b/generators/python.js @@ -244,10 +244,11 @@ Blockly.Python.quote_ = function(string) { * Calls any statements following this block. * @param {!Blockly.Block} block The current block. * @param {string} code The Python code created for this block. + * @param {boolean=} opt_thisOnly True to generate code for only this statement. * @return {string} Python code with comments and subsequent blocks added. * @private */ -Blockly.Python.scrub_ = function(block, code) { +Blockly.Python.scrub_ = function(block, code, opt_thisOnly) { var commentCode = ''; // Only collect comments for blocks that aren't inline. if (!block.outputConnection || !block.outputConnection.targetConnection) { @@ -277,7 +278,7 @@ Blockly.Python.scrub_ = function(block, code) { } } var nextBlock = block.nextConnection && block.nextConnection.targetBlock(); - var nextCode = Blockly.Python.blockToCode(nextBlock); + var nextCode = opt_thisOnly ? '' : Blockly.Python.blockToCode(nextBlock); return commentCode + code + nextCode; }; diff --git a/tests/jsunit/generator_test.js b/tests/jsunit/generator_test.js index 069f1c5b3..f1a04cd29 100644 --- a/tests/jsunit/generator_test.js +++ b/tests/jsunit/generator_test.js @@ -19,10 +19,169 @@ */ 'use strict'; +goog.require('Blockly.Dart'); +goog.require('Blockly.JavaScript'); +goog.require('Blockly.Lua'); +goog.require('Blockly.PHP'); +goog.require('Blockly.Python'); + +var workspace; + +function defineGeneratorTestBlocks() { + Blockly.defineBlocksWithJsonArray([{ + "type": "stack_block", + "message0": "", + "previousStatement": null, + "nextStatement": null + }, + { + "type": "row_block", + "message0": "%1", + "args0": [ + { + "type": "input_value", + "name": "INPUT" + } + ], + "output": null, + "nextStatement": null + }, + { + "type": "controls_repeat_ext", + "message0": "Repeat Loop", + "message1": "%1", + "args1": [{ + "type": "input_statement", + "name": "DO" + }], + "previousStatement": null, + "nextStatement": null + }]); +} + +function undefineGeneratorTestBlocks() { + delete Blockly.Blocks['stack_block']; + delete Blockly.Blocks['row_block']; +} + +function generatorTest_setUp() { + defineGeneratorTestBlocks(); + workspace = new Blockly.Workspace(); +} + +function generatorTest_tearDown() { + undefineGeneratorTestBlocks(); + workspace.dispose(); +} + +function blockToCodeTestSetup(generator) { + var row_block = workspace.newBlock('row_block'); + var stack_block = workspace.newBlock('stack_block'); + + generator.row_block = function(block){return 'row_block'}; + generator.stack_block = function(block){return 'stack_block'}; + row_block.nextConnection.connect(stack_block.previousConnection); + return row_block; +} + +function blockToCodeTest(generator, opt_thisOnly) { + var row_block = blockToCodeTestSetup(generator); + return generator.blockToCode(row_block, opt_thisOnly); +} + +function disabledBlockTest(generator, opt_thisOnly) { + var row_block = blockToCodeTestSetup(generator); + row_block.disabled=true; + return generator.blockToCode(row_block, opt_thisOnly); +} + +function nestedLoopTest(generator, opt_thisOnly) { + Blockly.Msg['CONTROLS_REPEAT_TITLE'] = 'repeat %1 times'; + var blockA = workspace.newBlock('controls_repeat_ext'); + var blockB = workspace.newBlock('controls_repeat_ext'); + var blockC = workspace.newBlock('controls_repeat_ext'); + generator.controls_repeat_ext = getControlRepeatFunc(generator); + + blockA.getInput('DO').connection.connect(blockB.previousConnection); + blockA.nextConnection.connect(blockC.previousConnection); + return generator.blockToCode(blockA, opt_thisOnly); +} + +function getControlRepeatFunc(generator) { + return function(block){ + return '{' + generator.statementToCode(block, 'DO') + '}'; + }; +} + +function test_blockToCodeOnAllGeneartors() { + generatorTest_setUp(); + + assertEquals(blockToCodeTest(Blockly.Dart, true), 'row_block'); + assertEquals(blockToCodeTest(Blockly.Dart, false), 'row_blockstack_block'); + + assertEquals(blockToCodeTest(Blockly.JavaScript, true), 'row_block'); + assertEquals(blockToCodeTest(Blockly.JavaScript, false), 'row_blockstack_block'); + + assertEquals(blockToCodeTest(Blockly.Lua, true), 'row_block'); + assertEquals(blockToCodeTest(Blockly.Lua, false), 'row_blockstack_block'); + + assertEquals(blockToCodeTest(Blockly.PHP, true), 'row_block'); + assertEquals(blockToCodeTest(Blockly.PHP, false), 'row_blockstack_block'); + + assertEquals(blockToCodeTest(Blockly.Python, true), 'row_block'); + assertEquals(blockToCodeTest(Blockly.Python, false), 'row_blockstack_block'); + + generatorTest_tearDown(); +} + +function test_disabledBlockToCode() { + generatorTest_setUp(); + + assertEquals(disabledBlockTest(Blockly.Dart, true), ''); + assertEquals(disabledBlockTest(Blockly.Dart, false), 'stack_block'); + + assertEquals(disabledBlockTest(Blockly.JavaScript, true), ''); + assertEquals(disabledBlockTest(Blockly.JavaScript, false), 'stack_block'); + + assertEquals(disabledBlockTest(Blockly.Lua, true), ''); + assertEquals(disabledBlockTest(Blockly.Lua, false), 'stack_block'); + + assertEquals(disabledBlockTest(Blockly.PHP, true), ''); + assertEquals(disabledBlockTest(Blockly.PHP, false), 'stack_block'); + + assertEquals(disabledBlockTest(Blockly.Python, true), ''); + assertEquals(disabledBlockTest(Blockly.Python, false), 'stack_block'); + + generatorTest_tearDown(); +} + +// { {}} represents a nested block +//{ {}}{} represents a nested block with a block after it +function test_nestedLoopBlockToCode() { + generatorTest_setUp(); + + assertEquals(nestedLoopTest(Blockly.Dart, true), '{ {}}'); + assertEquals(nestedLoopTest(Blockly.Dart, false), '{ {}}{}'); + + assertEquals(nestedLoopTest(Blockly.JavaScript, true), '{ {}}'); + assertEquals(nestedLoopTest(Blockly.JavaScript, false), '{ {}}{}'); + + assertEquals(nestedLoopTest(Blockly.Lua, true), '{ {}}'); + assertEquals(nestedLoopTest(Blockly.Lua, false), '{ {}}{}'); + + assertEquals(nestedLoopTest(Blockly.PHP, true), '{ {}}'); + assertEquals(nestedLoopTest(Blockly.PHP, false), '{ {}}{}'); + + assertEquals(nestedLoopTest(Blockly.Python, true), '{ {}}'); + assertEquals(nestedLoopTest(Blockly.Python, false), '{ {}}{}'); + + generatorTest_tearDown(); +} + function test_prefix() { var generator = new Blockly.Generator('INTERCAL'); assertEquals('Prefix nothing.', '', generator.prefixLines('', '')); assertEquals('Prefix a word.', '@Hello', generator.prefixLines('Hello', '@')); assertEquals('Prefix one line.', '12Hello\n', generator.prefixLines('Hello\n', '12')); assertEquals('Prefix two lines.', '***Hello\n***World\n', generator.prefixLines('Hello\nWorld\n', '***')); -} +} \ No newline at end of file diff --git a/tests/jsunit/index.html b/tests/jsunit/index.html index 82a1d2deb..c84375f88 100644 --- a/tests/jsunit/index.html +++ b/tests/jsunit/index.html @@ -4,6 +4,11 @@