Prefix and suffix edge cases for flow statements.

Call suffix code on break/continue before executing the break/continue.
Call prefix code for enclosing loop before executing continue.
This commit is contained in:
Neil Fraser
2019-05-13 15:25:52 -07:00
committed by Neil Fraser
parent 0259f8bb48
commit 25adb40e66
16 changed files with 131 additions and 42 deletions

View File

@@ -258,7 +258,7 @@ Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN = {
* @this Blockly.Block
*/
customContextMenu: function(options) {
if (this.isInFlyout){
if (this.isInFlyout) {
return;
}
var variable = this.getField('VAR').getVariable();
@@ -303,7 +303,24 @@ Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN = {
* Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.LOOP_TYPES.push('custom_loop');
*/
LOOP_TYPES: ['controls_repeat', 'controls_repeat_ext', 'controls_forEach',
'controls_for', 'controls_whileUntil'],
'controls_for', 'controls_whileUntil'],
/**
* Is the given block enclosed (at any level) by a loop?
* @param {!Blockly.Block} block Current block.
* @return {Blockly.Block} The nearest surrounding loop, or null if none.
*/
getSurroundLoop: function(block) {
// Is the block nested in a loop?
do {
if (Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.LOOP_TYPES
.indexOf(block.type) != -1) {
return block;
}
block = block.getSurroundParent();
} while (block);
return null;
},
/**
* Called whenever anything on the workspace changes.
@@ -315,17 +332,8 @@ Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN = {
if (!this.workspace.isDragging || this.workspace.isDragging()) {
return; // Don't change state at the start of a drag.
}
var legal = false;
// Is the block nested in a loop?
var block = this;
do {
if (this.LOOP_TYPES.indexOf(block.type) != -1) {
legal = true;
break;
}
block = block.getSurroundParent();
} while (block);
if (legal) {
if (Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN
.getSurroundLoop(this)) {
this.setWarningText(null);
if (!this.isInFlyout) {
this.setEnabled(true);

View File

@@ -349,7 +349,7 @@ Blockly.Blocks['procedures_defnoreturn'] = {
* @this Blockly.Block
*/
customContextMenu: function(options) {
if (this.isInFlyout){
if (this.isInFlyout) {
return;
}
// Add option to create caller.
@@ -489,7 +489,7 @@ Blockly.Blocks['procedures_mutatorcontainer'] = {
}
return;
}
if (event.type != Blockly.Events.BLOCK_CREATE) {
return;
}

View File

@@ -100,7 +100,7 @@ Blockly.Constants.Variables.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = {
* @this Blockly.Block
*/
customContextMenu: function(options) {
if (!this.isInFlyout){
if (!this.isInFlyout) {
// Getter blocks have the option to create a setter block, and vice versa.
if (this.type == 'variables_get') {
var opposite_type = 'variables_set';
@@ -123,7 +123,7 @@ Blockly.Constants.Variables.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = {
options.push(option);
// Getter blocks have the option to rename or delete that variable.
} else {
if (this.type == 'variables_get' || this.type == 'variables_get_reporter'){
if (this.type == 'variables_get' || this.type == 'variables_get_reporter') {
var renameOption = {
text: Blockly.Msg.RENAME_VARIABLE,
enabled: true,

View File

@@ -705,7 +705,7 @@ Blockly.setTheme = function(theme) {
Blockly.refreshTheme_ = function(ws) {
// Update all blocks in workspace that have a style name.
this.updateBlockStyles_(ws.getAllBlocks().filter(
function(block){
function(block) {
return block.getStyleName() !== undefined;
}
));

View File

@@ -73,7 +73,7 @@ Blockly.Extensions.register = function(name, initFn) {
* registered.
*/
Blockly.Extensions.registerMixin = function(name, mixinObj) {
if (!mixinObj || typeof mixinObj != 'object'){
if (!mixinObj || typeof mixinObj != 'object') {
throw Error('Error: Mixin "' + name + '" must be a object');
}
Blockly.Extensions.register(name, function() {

View File

@@ -343,7 +343,7 @@ Blockly.Generator.prototype.addLoopTrap = function(branch, block) {
Blockly.Generator.prototype.injectId = function(msg, block) {
var id = block.id.replace(/\$/g, '$$$$'); // Issue 251.
return msg.replace(/%1/g, '\'' + id + '\'');
}
};
/**
* Comma-separated list of reserved words.

View File

@@ -416,7 +416,7 @@ Blockly.Mutator.prototype.dispose = function() {
Blockly.Mutator.prototype.updateBlockStyle = function() {
var ws = this.workspace_;
if (ws && ws.getAllBlocks()){
if (ws && ws.getAllBlocks()) {
var workspaceBlocks = ws.getAllBlocks();
for (var i = 0; i < workspaceBlocks.length; i++) {
var block = workspaceBlocks[i];

View File

@@ -387,7 +387,7 @@ Blockly.Toolbox.prototype.syncTrees_ = function(treeIn, treeOut, pathToMedia) {
* @param {string} categoryName Name of the toolbox category.
* @private
*/
Blockly.Toolbox.prototype.setColour_ = function(colourValue, childOut, categoryName){
Blockly.Toolbox.prototype.setColour_ = function(colourValue, childOut, categoryName) {
// Decode the colour for any potential message references
// (eg. `%{BKY_MATH_HUE}`).
var colour = Blockly.utils.replaceMessageReferences(colourValue);
@@ -416,7 +416,7 @@ Blockly.Toolbox.prototype.setColour_ = function(colourValue, childOut, categoryN
* @param {string} categoryName Name of the toolbox category.
*/
Blockly.Toolbox.prototype.setColourFromStyle_ = function(
styleName, childOut, categoryName){
styleName, childOut, categoryName) {
childOut.styleName = styleName;
if (styleName && Blockly.getTheme()) {
var style = Blockly.getTheme().getCategoryStyle(styleName);

View File

@@ -244,8 +244,8 @@ Blockly.Touch.splitEventByTouches = function(e) {
type: e.type,
changedTouches: [e.changedTouches[i]],
target: e.target,
stopPropagation: function(){ e.stopPropagation(); },
preventDefault: function(){ e.preventDefault(); }
stopPropagation: function() { e.stopPropagation(); },
preventDefault: function() { e.preventDefault(); }
};
events[i] = newEvent;
}

View File

@@ -153,11 +153,27 @@ Blockly.Dart['controls_forEach'] = function(block) {
Blockly.Dart['controls_flow_statements'] = function(block) {
// Flow statements: continue, break.
switch (block.getFieldValue('FLOW')) {
var flowType = block.getFieldValue('FLOW');
var xfix = '';
if (Blockly.Dart.STATEMENT_SUFFIX) {
// Inject any statement suffix here since the regular one at the end
// will not get executed if the break/continue is triggered.
xfix += Blockly.Dart.injectId(Blockly.Dart.STATEMENT_SUFFIX, block);
}
if (Blockly.Dart.STATEMENT_PREFIX && flowType == 'CONTINUE') {
var loop = Blockly.Constants.Loops
.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.getSurroundLoop(block);
if (loop) {
// Inject loop's statement prefix here since the regular one at the end
// of the loop will not get executed if the continue is triggered.
xfix += Blockly.Dart.injectId(Blockly.Dart.STATEMENT_PREFIX, loop);
}
}
switch (flowType) {
case 'BREAK':
return 'break;\n';
return xfix + 'break;\n';
case 'CONTINUE':
return 'continue;\n';
return xfix + 'continue;\n';
}
throw Error('Unknown flow statement.');
};

View File

@@ -165,11 +165,29 @@ Blockly.JavaScript['controls_forEach'] = function(block) {
Blockly.JavaScript['controls_flow_statements'] = function(block) {
// Flow statements: continue, break.
switch (block.getFieldValue('FLOW')) {
var flowType = block.getFieldValue('FLOW');
var xfix = '';
if (Blockly.JavaScript.STATEMENT_SUFFIX) {
// Inject any statement suffix here since the regular one at the end
// will not get executed if the break/continue is triggered.
xfix += Blockly.JavaScript.injectId(Blockly.JavaScript.STATEMENT_SUFFIX,
block);
}
if (Blockly.JavaScript.STATEMENT_PREFIX && flowType == 'CONTINUE') {
var loop = Blockly.Constants.Loops
.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.getSurroundLoop(block);
if (loop) {
// Inject loop's statement prefix here since the regular one at the end
// of the loop will not get executed if the continue is triggered.
xfix += Blockly.JavaScript.injectId(Blockly.JavaScript.STATEMENT_PREFIX,
loop);
}
}
switch (flowType) {
case 'BREAK':
return 'break;\n';
return xfix + 'break;\n';
case 'CONTINUE':
return 'continue;\n';
return xfix + 'continue;\n';
}
throw Error('Unknown flow statement.');
};

View File

@@ -106,7 +106,6 @@ Blockly.JavaScript['procedures_ifreturn'] = function(block) {
if (Blockly.JavaScript.STATEMENT_SUFFIX) {
// Inject any statement suffix here since the regular one at the end
// will not get executed if the return is triggered.
var id = block.id.replace(/\$/g, '$$$$'); // Issue 251.
code += Blockly.JavaScript.prefixLines(
Blockly.JavaScript.injectId(Blockly.JavaScript.STATEMENT_SUFFIX, block),
Blockly.JavaScript.INDENT);

View File

@@ -49,6 +49,7 @@ Blockly.Lua.CONTINUE_STATEMENT = 'goto continue\n';
*/
Blockly.Lua.addContinueLabel_ = function(branch) {
if (branch.indexOf(Blockly.Lua.CONTINUE_STATEMENT) != -1) {
// False positives are possible (e.g. a string literal), but are harmless.
return branch + Blockly.Lua.INDENT + '::continue::\n';
} else {
return branch;
@@ -156,11 +157,27 @@ Blockly.Lua['controls_forEach'] = function(block) {
Blockly.Lua['controls_flow_statements'] = function(block) {
// Flow statements: continue, break.
switch (block.getFieldValue('FLOW')) {
var flowType = block.getFieldValue('FLOW');
var xfix = '';
if (Blockly.Lua.STATEMENT_SUFFIX) {
// Inject any statement suffix here since the regular one at the end
// will not get executed if the break/continue is triggered.
xfix += Blockly.Lua.injectId(Blockly.Lua.STATEMENT_SUFFIX, block);
}
if (Blockly.Lua.STATEMENT_PREFIX && flowType == 'CONTINUE') {
var loop = Blockly.Constants.Loops
.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.getSurroundLoop(block);
if (loop) {
// Inject loop's statement prefix here since the regular one at the end
// of the loop will not get executed if the continue is triggered.
xfix += Blockly.Lua.injectId(Blockly.Lua.STATEMENT_PREFIX, loop);
}
}
switch (flowType) {
case 'BREAK':
return 'break\n';
return xfix + 'break\n';
case 'CONTINUE':
return Blockly.Lua.CONTINUE_STATEMENT;
return xfix + Blockly.Lua.CONTINUE_STATEMENT;
}
throw Error('Unknown flow statement.');
};

View File

@@ -154,11 +154,27 @@ Blockly.PHP['controls_forEach'] = function(block) {
Blockly.PHP['controls_flow_statements'] = function(block) {
// Flow statements: continue, break.
switch (block.getFieldValue('FLOW')) {
var flowType = block.getFieldValue('FLOW');
var xfix = '';
if (Blockly.PHP.STATEMENT_SUFFIX) {
// Inject any statement suffix here since the regular one at the end
// will not get executed if the break/continue is triggered.
xfix += Blockly.PHP.injectId(Blockly.PHP.STATEMENT_SUFFIX, block);
}
if (Blockly.PHP.STATEMENT_PREFIX && flowType == 'CONTINUE') {
var loop = Blockly.Constants.Loops
.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.getSurroundLoop(block);
if (loop) {
// Inject loop's statement prefix here since the regular one at the end
// of the loop will not get executed if the continue is triggered.
xfix += Blockly.PHP.injectId(Blockly.PHP.STATEMENT_PREFIX, loop);
}
}
switch (flowType) {
case 'BREAK':
return 'break;\n';
return xfix + 'break;\n';
case 'CONTINUE':
return 'continue;\n';
return xfix + 'continue;\n';
}
throw Error('Unknown flow statement.');
};

View File

@@ -127,7 +127,6 @@ Blockly.PHP['procedures_ifreturn'] = function(block) {
if (Blockly.PHP.STATEMENT_SUFFIX) {
// Inject any statement suffix here since the regular one at the end
// will not get executed if the return is triggered.
var id = block.id.replace(/\$/g, '$$$$'); // Issue 251.
code += Blockly.PHP.prefixLines(
Blockly.PHP.injectId(Blockly.PHP.STATEMENT_SUFFIX, block),
Blockly.PHP.INDENT);

View File

@@ -197,11 +197,27 @@ Blockly.Python['controls_forEach'] = function(block) {
Blockly.Python['controls_flow_statements'] = function(block) {
// Flow statements: continue, break.
switch (block.getFieldValue('FLOW')) {
var flowType = block.getFieldValue('FLOW');
var xfix = '';
if (Blockly.Python.STATEMENT_SUFFIX) {
// Inject any statement suffix here since the regular one at the end
// will not get executed if the break/continue is triggered.
xfix += Blockly.Python.injectId(Blockly.Python.STATEMENT_SUFFIX, block);
}
if (Blockly.Python.STATEMENT_PREFIX && flowType == 'CONTINUE') {
var loop = Blockly.Constants.Loops
.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.getSurroundLoop(block);
if (loop) {
// Inject loop's statement prefix here since the regular one at the end
// of the loop will not get executed if the continue is triggered.
xfix += Blockly.Python.injectId(Blockly.Python.STATEMENT_PREFIX, loop);
}
}
switch (flowType) {
case 'BREAK':
return 'break\n';
return xfix + 'break\n';
case 'CONTINUE':
return 'continue\n';
return xfix + 'continue\n';
}
throw Error('Unknown flow statement.');
};