diff --git a/block-lexical-variables/src/blocks/controls.js b/block-lexical-variables/src/blocks/controls.js
index edbf7c6..0535164 100644
--- a/block-lexical-variables/src/blocks/controls.js
+++ b/block-lexical-variables/src/blocks/controls.js
@@ -61,7 +61,7 @@ Blockly.Blocks['controls_forRange'] = {
.appendField(Blockly.Msg.LANG_CONTROLS_FORRANGE_INPUT_ITEM)
.appendField(new FieldParameterFlydown(
Blockly.Msg.LANG_CONTROLS_FORRANGE_INPUT_VAR, true,
- FieldFlydown.DISPLAY_BELOW), 'VAR')
+ FieldFlydown.DISPLAY_BELOW, undefined, Blockly?.types_?.loopType), 'VAR')
.appendField(Blockly.Msg.LANG_CONTROLS_FORRANGE_INPUT_START)
.setAlign(Blockly.inputs.Align.RIGHT);
this.appendValueInput('TO')
@@ -88,6 +88,9 @@ Blockly.Blocks['controls_forRange'] = {
getScopedInputName: function () {
return 'DO';
},
+ getVariableType: function () {
+ return Blockly?.types_?.loopType
+ }
};
// Alias controls_for to controls_forRange We need this because
@@ -114,7 +117,7 @@ Blockly.Blocks['controls_forEach'] = {
.appendField(Blockly.Msg.LANG_CONTROLS_FOREACH_INPUT_ITEM)
.appendField(new FieldParameterFlydown(
Blockly.Msg.LANG_CONTROLS_FOREACH_INPUT_VAR,
- true, FieldFlydown.DISPLAY_BELOW), 'VAR')
+ true, FieldFlydown.DISPLAY_BELOW, undefined, Blockly?.types_?.loopType), 'VAR')
.appendField(Blockly.Msg.LANG_CONTROLS_FOREACH_INPUT_INLIST)
.setAlign(Blockly.inputs.Align.RIGHT);
this.appendStatementInput('DO')
@@ -130,6 +133,9 @@ Blockly.Blocks['controls_forEach'] = {
getScopedInputName: function () {
return 'DO';
},
+ getVariableType: function () {
+ return Blockly?.types_?.loopType
+ }
};
Blockly.Blocks['controls_do_then_return'] = {
@@ -148,4 +154,3 @@ Blockly.Blocks['controls_do_then_return'] = {
this.setTooltip(Blockly.Msg.LANG_CONTROLS_DO_THEN_RETURN_TOOLTIP);
},
};
-
diff --git a/block-lexical-variables/src/blocks/lexical-variables.js b/block-lexical-variables/src/blocks/lexical-variables.js
index f308ba8..e6e08b7 100644
--- a/block-lexical-variables/src/blocks/lexical-variables.js
+++ b/block-lexical-variables/src/blocks/lexical-variables.js
@@ -104,6 +104,7 @@ import * as Shared from '../shared.js';
import {NameSet} from "../nameSet.js";
import {Substitution} from '../substitution.js'
import {lexicalVariableScopeMixin} from "../mixins.js";
+import {dataTypesEnabled} from "../shared.js";
delete Blockly.Blocks['global_declaration'];
/**
@@ -115,13 +116,30 @@ Blockly.Blocks['global_declaration'] = {
helpUrl: Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_HELPURL,
init: function() {
this.setStyle('variable_blocks');
- this.appendValueInput('VALUE')
- .appendField(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_TITLE_INIT)
- .appendField(new FieldGlobalFlydown(
- Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_NAME,
- FieldFlydown.DISPLAY_BELOW), 'NAME')
- .appendField(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_TO);
+ this.fieldGlobalFlydown_ = new FieldGlobalFlydown(
+ Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_NAME,
+ FieldFlydown.DISPLAY_BELOW)
+ const valueField = this.appendValueInput('VALUE')
+ .appendField(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_TITLE_INIT);
+ if (dataTypesEnabled()) {
+ valueField.appendField(new Blockly.FieldDropdown(Blockly.types_.dataTypes), 'TYPE');
+ }
+ valueField.appendField(this.fieldGlobalFlydown_, 'NAME')
+ .appendField(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_TO);
this.setTooltip(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_TOOLTIP);
+
+ if (dataTypesEnabled()) {
+ this.setOnChange(function (e) {
+ if (!this.workspace || this.workspace.isFlyout || this.isInFlyout) return;
+
+ const type = this.getVariableType();
+ this.getInput('VALUE').setCheck(type ? [type] : null);
+
+ if (e.type === 'change' && e.name === 'TYPE') {
+ LexicalVariable.changeGlobalVariableType(this.getFieldValue('NAME'), type, type)
+ }
+ })
+ }
},
getDeclaredVars: function() {
const field = this.getField('NAME');
@@ -130,6 +148,9 @@ Blockly.Blocks['global_declaration'] = {
getGlobalNames: function() {
return this.getDeclaredVars();
},
+ getVariableType: function() {
+ return this.getFieldValue('TYPE');
+ },
renameVar: function(oldName, newName) {
if (Blockly.Names.equals(oldName, this.getFieldValue('NAME'))) {
this.setFieldValue(newName, 'NAME');
@@ -146,8 +167,13 @@ Blockly.Blocks['simple_local_declaration_statement'] = {
this.setStyle('variable_blocks');
const declInput = this.appendValueInput('DECL');
declInput.appendField(
- Blockly.Msg.LANG_VARIABLES_LOCAL_DECLARATION_TITLE_INIT)
- .appendField(new FieldParameterFlydown(Blockly.Msg.LANG_VARIABLES_LOCAL_DECLARATION_DEFAULT_NAME, true), 'VAR')
+ Blockly.Msg.LANG_VARIABLES_LOCAL_DECLARATION_TITLE_INIT);
+
+ if (dataTypesEnabled()) {
+ declInput.appendField(new Blockly.FieldDropdown(Blockly.types_.dataTypes), 'TYPE')
+ }
+
+ declInput.appendField(new FieldParameterFlydown(Blockly.Msg.LANG_VARIABLES_LOCAL_DECLARATION_DEFAULT_NAME, true), 'VAR')
.appendField(Blockly.Msg.LANG_VARIABLES_LOCAL_DECLARATION_INPUT_TO)
.setAlign(Blockly.inputs.Align.RIGHT);
this.appendStatementInput('DO')
@@ -156,10 +182,31 @@ Blockly.Blocks['simple_local_declaration_statement'] = {
this.setNextStatement(true);
this.setTooltip(Blockly.Msg.LANG_VARIABLES_LOCAL_DECLARATION_TOOLTIP);
this.mixin(lexicalVariableScopeMixin);
+
+ if (dataTypesEnabled()) {
+ this.setOnChange(function (e) {
+ if (!this.workspace || this.workspace.isFlyout || this.isInFlyout) return;
+
+ const newType = this.getVariableType();
+
+ if (e.type === 'change' && e.name === 'TYPE') {
+ LexicalVariable.changeVariableType(this, this.getFieldValue('VAR'), newType, newType)
+ }
+
+ const varField = this.getField('VAR');
+ this.getInput('DECL').setCheck(newType ? [newType] : null);
+
+ if (!varField) return;
+ varField.setVariableType(newType);
+ });
+ }
},
getDeclaredVarFieldNames: function () {
return ['VAR'];
},
+ getVariableType: function() {
+ return this.getFieldValue('TYPE');
+ },
getScopedInputName: function () {
return 'DO';
},
@@ -223,9 +270,10 @@ Blockly.Blocks['local_declaration_statement'] = {
withLexicalVarsAndPrefix: function(child, proc) {
if (this.getInputTargetBlock(this.bodyInputName) == child) {
const localNames = this.declaredNames();
+ const paramTypes = this.getVariableTypes();
// not arguments_ instance var
for (let i = 0; i < localNames.length; i++) {
- proc(localNames[i], this.lexicalVarPrefix);
+ proc(localNames[i], this.lexicalVarPrefix, '', paramTypes[i]);
}
}
},
@@ -235,6 +283,7 @@ Blockly.Blocks['local_declaration_statement'] = {
this.setStyle('variable_blocks');
this.localNames_ =
[Blockly.Msg.LANG_VARIABLES_LOCAL_DECLARATION_DEFAULT_NAME];
+ if (dataTypesEnabled()) this.localTypes_ = [Blockly.types_.defaultType];
const declInput = this.appendValueInput('DECL0');
declInput.appendField(
Blockly.Msg.LANG_VARIABLES_LOCAL_DECLARATION_TITLE_INIT)
@@ -255,6 +304,7 @@ Blockly.Blocks['local_declaration_statement'] = {
for (let i = 0; i < this.localNames_.length; i++) {
const parameter = Blockly.utils.xml.createElement('localname');
parameter.setAttribute('name', this.localNames_[i]);
+ if (dataTypesEnabled()) parameter.setAttribute('type', this.localTypes_[i]);
container.appendChild(parameter);
}
return container;
@@ -266,16 +316,19 @@ Blockly.Blocks['local_declaration_statement'] = {
if (children.length > 0) { // Ensure xml element is nonempty
// Else we'll overwrite initial list with "name" for new block
this.localNames_ = [];
- for (let i = 0, childNode; childNode = children[i]; i++) {
+ this.localTypes_ = [];
+
+ for (let i = 0, childNode; childNode = children[i]; i++) {
if (childNode.nodeName.toLowerCase() == 'localname') {
this.localNames_.push(childNode.getAttribute('name'));
+ if (dataTypesEnabled()) this.localTypes_.push(childNode.getAttribute('type') || Blockly.types_.defaultType);
}
}
}
- this.updateDeclarationInputs_(this.localNames_); // add declarations; inits
+ this.updateDeclarationInputs_(this.localNames_, this.localTypes_); // add declarations; inits
// are undefined
},
- updateDeclarationInputs_: function(names, inits) {
+ updateDeclarationInputs_: function(names, types, inits) {
// Modify this block to replace existing initializers by new declaration
// inputs created from names and inits. If inits is undefined, treat all
// initial expressions as undefined. Keep existing body at end of input
@@ -309,6 +362,9 @@ Blockly.Blocks['local_declaration_statement'] = {
// mutator
this.inputList = [];
this.localNames_ = names;
+ if (types) {
+ this.localTypes_ = types;
+ }
for (let i = 0; i < names.length; i++) {
const declInput = this.appendValueInput('DECL' + i);
@@ -325,6 +381,12 @@ Blockly.Blocks['local_declaration_statement'] = {
.appendField(this.parameterFlydown(i), 'VAR' + i)
.appendField(Blockly.Msg.LANG_VARIABLES_LOCAL_DECLARATION_INPUT_TO)
.setAlign(Blockly.inputs.Align.RIGHT);
+
+ if (dataTypesEnabled()) {
+ const t = this.localTypes_[i];
+ declInput.setCheck(t ? [t] : null);
+ }
+
if (inits && inits[i]) { // If there is an initializer, connect it
declInput.connection.connect(inits[i]);
}
@@ -345,6 +407,10 @@ Blockly.Blocks['local_declaration_statement'] = {
// Return a new local variable parameter flydown
parameterFlydown: function(paramIndex) {
const initialParamName = this.localNames_[paramIndex];
+ let initialParamType = '';
+ if (initialParamType.length > paramIndex) {
+ initialParamType = this.localTypes_[paramIndex];
+ }
const localDecl = this; // Here, "this" is the local decl block. Name it to
// use in function below
const localParameterChangeHandler = function(newParamName) {
@@ -381,26 +447,23 @@ Blockly.Blocks['local_declaration_statement'] = {
return new FieldParameterFlydown(initialParamName,
true, // name is editable
FieldFlydown.DISPLAY_RIGHT,
- localParameterChangeHandler);
+ localParameterChangeHandler, initialParamType);
},
decompose: function(workspace) {
- // Create "mutator" editor populated with name blocks with local variable
- // names
- const containerBlock = workspace.newBlock('local_mutatorcontainer');
- containerBlock.initSvg();
- containerBlock.setDefBlock(this);
- let connection = containerBlock.getInput('STACK').connection;
- for (let i = 0; i < this.localNames_.length; i++) {
- const localName = this.getFieldValue('VAR' + i);
- const nameBlock = workspace.newBlock('local_mutatorarg');
- nameBlock.initSvg();
- nameBlock.setFieldValue(localName, 'NAME');
- // Store the old location.
- nameBlock.oldLocation = i;
- connection.connect(nameBlock.previousConnection);
- connection = nameBlock.nextConnection;
- }
- return containerBlock;
+ const container = workspace.newBlock('local_mutatorcontainer');
+ container.initSvg();
+ container.setDefBlock(this);
+ let conn = container.getInput('STACK').connection;
+ for (let i = 0; i < this.localNames_.length; i++) {
+ const arg = workspace.newBlock('local_mutatorarg');
+ arg.initSvg();
+ arg.setFieldValue(this.localNames_[i], 'NAME');
+ arg.setFieldValue(this.localTypes_[i], 'TYPE');
+ arg.oldLocation = i;
+ conn.connect(arg.previousConnection);
+ conn = arg.nextConnection;
+ }
+ return container;
},
compose: function(containerBlock) {
// [lyn, 10/27/13] Modified this so that doesn't rebuild block if names
@@ -408,10 +471,12 @@ Blockly.Blocks['local_declaration_statement'] = {
// localParameterChangeHandler within parameterFlydown.
const newLocalNames = [];
+ const newLocalTypes = [];
const initializers = [];
let mutatorarg = containerBlock.getInputTargetBlock('STACK');
while (mutatorarg) {
newLocalNames.push(mutatorarg.getFieldValue('NAME'));
+ newLocalTypes.push(mutatorarg.getFieldValue('TYPE'));
initializers.push(mutatorarg.valueConnection_); // pushes undefined if
// doesn't exist
mutatorarg =
@@ -420,12 +485,12 @@ Blockly.Blocks['local_declaration_statement'] = {
// Reconstruct inputs only if local list has changed
if (!LexicalVariable.stringListsEqual(this.localNames_,
- newLocalNames)) {
+ newLocalNames) || !LexicalVariable.stringListsEqual(this.localTypes_, newLocalTypes)) {
// Switch off rendering while the block is rebuilt.
// var savedRendered = this.rendered;
// this.rendered = false;
- this.updateDeclarationInputs_(newLocalNames, initializers);
+ this.updateDeclarationInputs_(newLocalNames, newLocalTypes, initializers);
// Restore rendering and show the changes.
// this.rendered = savedRendered;
@@ -494,7 +559,7 @@ Blockly.Blocks['local_declaration_statement'] = {
if (!LexicalVariable.stringListsEqual(renamedLocalNames,
localNames)) {
const initializerConnections = this.initializerConnections();
- this.updateDeclarationInputs_(renamedLocalNames, initializerConnections);
+ this.updateDeclarationInputs_(renamedLocalNames, this.localTypes_, initializerConnections);
// Update the mutator's variables if the mutator is open.
if (this.mutator && this.mutator.isVisible()) {
const blocks = this.mutator.workspace_.getAllBlocks();
@@ -583,6 +648,19 @@ Blockly.Blocks['local_declaration_statement'] = {
}
return result;
},
+ getVariableTypes: function() {
+ // When the mutator is open, reflect the state inside it (live types).
+ if (this.mutator && this.mutator.getSize() && this.mutator.rootBlock) {
+ const types = [];
+ let arg = this.mutator.rootBlock.getInputTargetBlock('STACK');
+ while (arg) {
+ types.push(arg.getFieldValue('TYPE') || 'any');
+ arg = arg.nextConnection && arg.nextConnection.targetBlock();
+ }
+ return types;
+ }
+ return this.localTypes_;
+ },
};
@@ -657,6 +735,7 @@ Blockly.Blocks['local_declaration_expression'] = {
renameBound: Blockly.Blocks.local_declaration_statement.renameBound,
renameFree: Blockly.Blocks.local_declaration_statement.renameFree,
freeVariables: Blockly.Blocks.local_declaration_statement.freeVariables,
+ getVariableTypes: Blockly.Blocks.local_declaration_statement.getVariableTypes,
};
Blockly.Blocks['local_mutatorcontainer'] = {
@@ -704,18 +783,35 @@ Blockly.Blocks['local_mutatorarg'] = {
// Let the theme determine the color.
// this.setColour(Blockly.VARIABLE_CATEGORY_HUE);
this.setStyle('variable_blocks');
- this.appendDummyInput()
- .appendField(Blockly.Msg.LANG_VARIABLES_LOCAL_MUTATOR_ARG_TITLE_NAME)
- .appendField(new Blockly.FieldTextInput(
- Blockly.Msg.LANG_VARIABLES_LOCAL_MUTATOR_ARG_DEFAULT_VARIABLE,
- LexicalVariable.renameParam),
- 'NAME');
+
+ const editor = new Blockly.FieldTextInput(
+ Blockly.Msg.LANG_VARIABLES_LOCAL_MUTATOR_ARG_DEFAULT_VARIABLE,
+ LexicalVariable.renameParam)
+
+ const input = this.appendDummyInput()
+ .appendField('type');
+
+ if (dataTypesEnabled()) {
+ input.appendField(new Blockly.FieldDropdown(Blockly.types_.dataTypes), 'TYPE')
+ }
+
+ input.appendField(Blockly.Msg.LANG_VARIABLES_LOCAL_MUTATOR_ARG_TITLE_NAME)
+ .appendField(editor, 'NAME');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setTooltip('');
this.contextMenu = false;
this.lexicalVarPrefix = Shared.localNamePrefix;
this.mustNotRenameCapturables = true;
+
+ if (dataTypesEnabled()) {
+ this.setOnChange(function (e) {
+ const newType = this.getFieldValue('TYPE');
+ if (e.type === 'change' && e.name === 'TYPE') {
+ LexicalVariable.changeVariableType(this, this.getFieldValue('NAME'), newType, newType)
+ }
+ })
+ }
},
getContainerBlock: function() {
let parent = this.getParent();
@@ -741,7 +837,6 @@ Blockly.Blocks['local_mutatorarg'] = {
const container = this.getContainerBlock();
return (container && container.declaredNames()) || [];
},
-
// [lyn, 11/24/12] Check for situation in which mutator arg has been removed
// from stack,
onchange: function() {
diff --git a/block-lexical-variables/src/blocks/procedures.js b/block-lexical-variables/src/blocks/procedures.js
index 7c87e63..2b1ba8c 100644
--- a/block-lexical-variables/src/blocks/procedures.js
+++ b/block-lexical-variables/src/blocks/procedures.js
@@ -92,6 +92,7 @@ import * as Utilities from '../utilities.js';
import * as Shared from '../shared.js';
import {Substitution} from '../substitution.js'
import '../msg.js';
+import {dataTypesEnabled} from "../shared.js";
Blockly.Blocks['procedures_defnoreturn'] = {
// Define a procedure with no return value.
@@ -113,6 +114,7 @@ Blockly.Blocks['procedures_defnoreturn'] = {
// List of declared local variable names; has one
// ("name") initially
this.arguments_ = [];
+ this.argumentTypes_ = [];
// Other methods guarantee the invariant that this variable contains
// the list of names declared in the local declaration block.
this.warnings = [{name: 'checkEmptySockets', sockets: ['STACK']}];
@@ -125,9 +127,10 @@ Blockly.Blocks['procedures_defnoreturn'] = {
},
withLexicalVarsAndPrefix: function(_, proc) {
const params = this.declaredNames();
+ const paramTypes = this.getParameterTypes();
// not arguments_ instance var
for (let i = 0; i < params.length; i++) {
- proc(params[i], this.lexicalVarPrefix);
+ proc(params[i], this.lexicalVarPrefix, '', paramTypes[i]);
}
},
onchange: function() {
@@ -135,15 +138,18 @@ Blockly.Blocks['procedures_defnoreturn'] = {
// with paramFlydown fields
this.arguments_ = this.declaredNames();
},
- updateParams_: function(opt_params) {
+ updateParams_: function(opt_names, opt_types) {
// make rendered block reflect the parameter names currently in
// this.arguments_
- // [lyn, 11/17/13] Added optional opt_params argument:
+ // [lyn, 11/17/13] Added optional opt_names argument:
// If its falsey (null or undefined), use the existing this.arguments_
- // list Otherwise, replace this.arguments_ by opt_params In either case,
+ // list Otherwise, replace this.arguments_ by opt_names In either case,
// make rendered block reflect the parameter names in this.arguments_
- if (opt_params) {
- this.arguments_ = opt_params;
+ if (opt_names) {
+ this.arguments_ = opt_names;
+ }
+ if (opt_types) {
+ this.argumentTypes_ = opt_types;
}
// Check for duplicated arguments.
// [lyn 10/10/13] Note that in blocks edited within AI2, duplicate
@@ -268,11 +274,11 @@ Blockly.Blocks['procedures_defnoreturn'] = {
// Return a new procedure parameter flydown
parameterFlydown: function(paramIndex) {
const initialParamName = this.arguments_[paramIndex];
+ const initialParamType = this.argumentTypes_[paramIndex];
// Here, "this" is the proc decl block. Name it to
// use in function below
const procDecl = this;
const procedureParameterChangeHandler = function(newParamName) {
- // console.log("enter procedureParameterChangeHandler");
// Extra work that needs to be done when procedure param name is changed,
@@ -349,7 +355,7 @@ Blockly.Blocks['procedures_defnoreturn'] = {
// [lyn, 10/27/13] flydown location depends on parameter orientation
this.horizontalParameters ? FieldFlydown.DISPLAY_BELOW :
FieldFlydown.DISPLAY_RIGHT,
- procedureParameterChangeHandler);
+ procedureParameterChangeHandler, initialParamType);
},
setParameterOrientation: function(isHorizontal) {
const params = this.getParameters();
@@ -357,9 +363,10 @@ Blockly.Blocks['procedures_defnoreturn'] = {
this.horizontalParameters = isHorizontal;
this.updateParams_();
if (Blockly.Events.isEnabled()) {
- Blockly.Events.fire(new Blockly.Events.BlockChange(this, 'parameter_orientation', null,
- !this.horizontalParameters,
- this.horizontalParameters));
+ // Trigger a Blockly UI change event
+ Blockly.Events.fire(new Blockly.Events.Ui(this, 'parameter_orientation',
+ (!this.horizontalParameters).toString(),
+ this.horizontalParameters.toString()));
}
}
},
@@ -374,21 +381,26 @@ Blockly.Blocks['procedures_defnoreturn'] = {
for (let x = 0; x < this.arguments_.length; x++) {
const parameter = Blockly.utils.xml.createElement('arg');
parameter.setAttribute('name', this.arguments_[x]);
+ parameter.setAttribute('type', this.argumentTypes_[x]);
container.appendChild(parameter);
}
return container;
},
domToMutation: function(xmlElement) {
- const params = [];
+ const names = [];
+ const types = [];
const children = Utilities.getChildren(xmlElement);
for (let x = 0, childNode; childNode = children[x]; x++) {
if (childNode.nodeName.toLowerCase() == 'arg') {
- params.push(childNode.getAttribute('name'));
+ names.push(childNode.getAttribute('name'));
+ if (dataTypesEnabled()) {
+ types.push(childNode.getAttribute('type') || Blockly?.types_?.defaultType);
+ }
}
}
this.horizontalParameters =
xmlElement.getAttribute('vertical_parameters') !== 'true';
- this.updateParams_(params);
+ this.updateParams_(names, types);
},
decompose: function(workspace) {
const containerBlock = workspace.newBlock('procedures_mutatorcontainer');
@@ -403,6 +415,7 @@ Blockly.Blocks['procedures_defnoreturn'] = {
this.paramIds_.push(paramBlock.id); // [lyn, 10/26/13] Added
paramBlock.initSvg();
paramBlock.setFieldValue(this.arguments_[x], 'NAME');
+ paramBlock.setFieldValue(this.argumentTypes_[x], 'TYPE');
// Store the old location.
paramBlock.oldLocation = x;
connection.connect(paramBlock.previousConnection);
@@ -414,11 +427,13 @@ Blockly.Blocks['procedures_defnoreturn'] = {
return containerBlock;
},
compose: function(containerBlock) {
- const params = [];
+ const names = []
+ const types = []
this.paramIds_ = [];
let paramBlock = containerBlock.getInputTargetBlock('STACK');
while (paramBlock) {
- params.push(paramBlock.getFieldValue('NAME'));
+ names.push(paramBlock.getFieldValue('NAME'));
+ types.push(paramBlock.getVariableType());
this.paramIds_.push(paramBlock.id);
paramBlock = paramBlock.nextConnection &&
paramBlock.nextConnection.targetBlock();
@@ -431,9 +446,10 @@ Blockly.Blocks['procedures_defnoreturn'] = {
// );
// [lyn, 11/24/12] Note: update params updates param list in proc
// declaration, but renameParam updates procedure body appropriately.
- if (!LexicalVariable.stringListsEqual(params, this.arguments_)) {
+ if (!LexicalVariable.stringListsEqual(names, this.arguments_) ||
+ !LexicalVariable.stringListsEqual(types, this.argumentTypes_)) {
// Only need updates if param list has changed
- this.updateParams_(params);
+ this.updateParams_(names, types);
Blockly.Procedures.mutateCallers(this);
}
// console.log("exit procedures_defnoreturn compose()");
@@ -551,6 +567,9 @@ Blockly.Blocks['procedures_defnoreturn'] = {
getParameters: function() {
return this.arguments_;
},
+ getParameterTypes: function() {
+ return this.argumentTypes_;
+ }
};
// [lyn, 01/15/2013] Edited to remove STACK (no longer necessary with
@@ -574,9 +593,22 @@ Blockly.Blocks['procedures_defreturn'] = {
Blockly.Msg['LANG_PROCEDURES_DEFRETURN_PROCEDURE'], this);
this.createHeader(legalName);
this.horizontalParameters = true; // horizontal by default
- this.appendInputFromRegistry('indented_input', 'RETURN')
- .setAlign(Blockly.inputs.Align.RIGHT)
- .appendField(Blockly.Msg['LANG_PROCEDURES_DEFRETURN_RETURN']);
+ const returnInput = this.appendInputFromRegistry('indented_input', 'RETURN')
+ .setAlign(Blockly.inputs.Align.RIGHT);
+ if (dataTypesEnabled()) {
+ returnInput.appendField(new Blockly.FieldDropdown(Blockly.types_.dataTypes), 'RETURN_TYPE')
+ }
+ returnInput.appendField(Blockly.Msg['LANG_PROCEDURES_DEFRETURN_RETURN']);
+ returnInput.setCheck(this.getReturnType() ? [this.getReturnType()] : null);
+
+ const retTypeField = this.getField('RETURN_TYPE');
+ if (retTypeField) {
+ retTypeField.setValidator((newType) => {
+ const inp = this.getInput('RETURN');
+ if (inp) inp.setCheck(newType ? [newType] : null);
+ return newType;
+ });
+ }
this.setMutator(new Blockly.icons.MutatorIcon(['procedures_mutatorarg'], this));
this.setTooltip(Blockly.Msg['LANG_PROCEDURES_DEFRETURN_TOOLTIP']);
this.arguments_ = [];
@@ -612,6 +644,10 @@ Blockly.Blocks['procedures_defreturn'] = {
blocksInScope: Blockly.Blocks.procedures_defnoreturn.blocksInScope,
customContextMenu: Blockly.Blocks.procedures_defnoreturn.customContextMenu,
getParameters: Blockly.Blocks.procedures_defnoreturn.getParameters,
+ getParameterTypes: Blockly.Blocks.procedures_defnoreturn.getParameterTypes,
+ getReturnType: function() {
+ return this.getFieldValue('RETURN_TYPE');
+ }
};
Blockly.Blocks['procedures_mutatorcontainer'] = {
@@ -654,33 +690,30 @@ Blockly.Blocks['procedures_mutatorcontainer'] = {
Blockly.Blocks['procedures_mutatorarg'] = {
// Procedure argument (for mutator dialog).
init: function() {
- // var mutatorarg = this;
- // var mutatorargChangeHandler = function(newName) {
- // var proc = mutatorarg.getProcBlock();
- // var procArguments = proc ? proc.arguments_ : [];
- // console.log("mutatorargChangeHandler: newName = " + newName
- // + " and proc argumnets = [" + procArguments.join(',') +
- // "]"); return Blockly.LexicalVariable.renameParam.call(this,newName); }
// Let the theme determine the color.
// this.setColour(Blockly.PROCEDURE_CATEGORY_HUE);
this.setStyle('procedure_blocks');
const editor = new Blockly.FieldTextInput('x',
- LexicalVariable.renameParam);
+ LexicalVariable.renameParam);
// 2017 Blockly's text input change breaks our renaming behavior.
// The following is a version we've defined.
- editor.onHtmlInputChange_ = function(e) {
+ editor.onHtmlInputChange_ = function (e) {
const oldValue = this.getValue();
FieldFlydown.prototype.onHtmlInputChange_.call(this, e);
const newValue = this.getValue();
if (newValue && oldValue !== newValue && Blockly.Events.isEnabled()) {
Blockly.Events.fire(
- new Blockly.Events.BlockChange(this.sourceBlock_, 'field', this.name,
- oldValue, newValue));
+ new Blockly.Events.BlockChange(this.sourceBlock_, 'field', this.name,
+ oldValue, newValue));
}
};
- this.appendDummyInput()
- .appendField(Blockly.Msg['LANG_PROCEDURES_MUTATORARG_TITLE'])
- .appendField(editor, 'NAME');
+ const input = this.appendDummyInput()
+ if (dataTypesEnabled()) {
+ input.appendField('type:')
+ .appendField(new Blockly.FieldDropdown(Blockly.types_.dataTypes), 'TYPE')
+ }
+ input.appendField(Blockly.Msg['LANG_PROCEDURES_MUTATORARG_TITLE'])
+ .appendField(editor, 'NAME');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setTooltip(Blockly.Msg['LANG_PROCEDURES_MUTATORARG_TOOLTIP']);
@@ -688,6 +721,9 @@ Blockly.Blocks['procedures_mutatorarg'] = {
this.lexicalVarPrefix = Shared.procedureParameterPrefix;
this.mustNotRenameCapturables = true;
},
+ getVariableType: function() {
+ return this.getFieldValue('TYPE');
+ },
// [lyn, 11/24/12] Return the container this mutator arg is in, or null if
// it's not in one. Dynamically calculate this by walking up chain, because
// mutator arg might or might not be in container stack.
@@ -794,6 +830,7 @@ Blockly.Blocks['procedures_callnoreturn'] = {
this.setNextStatement(true);
this.setTooltip(Blockly.Msg['LANG_PROCEDURES_CALLNORETURN_TOOLTIP']);
this.arguments_ = [];
+ this.argumentTypes_ = [];
this.quarkConnections_ = null;
this.quarkArguments_ = null;
this.errors = [
@@ -889,6 +926,21 @@ Blockly.Blocks['procedures_callnoreturn'] = {
this.quarkArguments_ = [];
}
}
+
+ if (dataTypesEnabled()) {
+ let argTypes;
+ const defBlock = Blockly.Procedures.getDefinition(
+ this.getFieldValue('PROCNAME'), this.workspace);
+ if (defBlock && defBlock.argumentTypes_) {
+ argTypes = defBlock.argumentTypes_.slice();
+ } else if (this.argumentTypes_ && this.argumentTypes_.length === paramNames.length) {
+ argTypes = this.argumentTypes_.slice();
+ } else {
+ argTypes = paramNames.map(_ => Blockly?.types_?.defaultType);
+ }
+ this.argumentTypes_ = argTypes;
+ }
+
// Switch off rendering while the block is rebuilt.
const savedRendered = this.rendered;
this.rendered = false;
@@ -909,6 +961,12 @@ Blockly.Blocks['procedures_callnoreturn'] = {
input = this.appendValueInput('ARG' + x)
.setAlign(Blockly.inputs.Align.RIGHT)
.appendField(this.arguments_[x]);
+
+ if (dataTypesEnabled()) {
+ const t = this.argumentTypes_[x];
+ input.setCheck(t ? [t] : null);
+ }
+
if (this.quarkArguments_) {
// Reconnect any child blocks.
const quarkName = this.quarkArguments_[x];
@@ -952,6 +1010,9 @@ Blockly.Blocks['procedures_callnoreturn'] = {
const parameter = Blockly.utils.xml.createElement('arg');
parameter.setAttribute('name',
this.getInput('ARG' + x).fieldRow[0].getText());
+ if (dataTypesEnabled()) {
+ parameter.setAttribute('type', this.argumentTypes_[x] || Blockly?.types_?.defaultType);
+ }
container.appendChild(parameter);
}
return container;
@@ -963,10 +1024,14 @@ Blockly.Blocks['procedures_callnoreturn'] = {
// [lyn, 10/27/13] Significantly cleaned up this code. Always take arg
// names from xmlElement. Do not attempt to find definition.
this.arguments_ = [];
+ this.argumentTypes_ = [];
const children = Utilities.getChildren(xmlElement);
for (let x = 0, childNode; childNode = children[x]; x++) {
if (childNode.nodeName.toLowerCase() == 'arg') {
this.arguments_.push(childNode.getAttribute('name'));
+ if (dataTypesEnabled()) {
+ this.argumentTypes_.push(childNode.getAttribute('type') || Blockly?.types_?.defaultType);
+ }
}
}
this.setProcedureParameters(this.arguments_, null, true);
@@ -1005,6 +1070,9 @@ Blockly.Blocks['procedures_callnoreturn'] = {
}
this.setFieldValue('none', 'PROCNAME');
},
+ getParameterTypes: function() {
+ return this.argumentTypes_;
+ }
};
@@ -1059,4 +1127,64 @@ Blockly.Blocks['procedures_callreturn'] = {
Blockly.Blocks.procedures_callnoreturn.procCustomContextMenu,
removeProcedureValue:
Blockly.Blocks.procedures_callnoreturn.removeProcedureValue,
+ getParameterTypes: Blockly.Blocks.procedures_callnoreturn.getParameterTypes
};
+
+Blockly.Blocks['procedures_early_return'] = {
+ category: 'Procedures',
+ helpUrl: '',
+ init: function() {
+ this.setStyle('procedure_blocks');
+ this.appendDummyInput()
+ .appendField(Blockly.Msg['LANG_PROCEDURES_EARLY_RETURN_TOOLTIP']);
+ this.appendValueInput('RETURN_VALUE');
+ this.setPreviousStatement(true);
+ this.setNextStatement(true);
+ this.setTooltip(Blockly.Msg['LANG_PROCEDURES_EARLY_RETURN_TOOLTIP']);
+ this.errors = [
+ {func: ErrorCheckers.checkIsInDefinition},
+ ];
+
+ this.setOnChange(this._checkPlacement);
+ },
+ _checkPlacement: function(e) {
+ if (!this.workspace || this.workspace.isFlyout || this.isInFlyout) {
+ return;
+ }
+
+ let block = this.getSurroundParent();
+ let legal = false;
+ while (block) {
+ if (block.type === 'procedures_defreturn' ||
+ block.type === 'procedures_defnoreturn') {
+ legal = true;
+ break;
+ }
+ block = block.getSurroundParent();
+ }
+
+ this.setEnabled(legal);
+
+ if (!legal) {
+ this.setWarningText('Early return only allowed inside a function definition');
+ } else {
+ this.setWarningText(null);
+ }
+ },
+ getReturnType: function() {
+ let parent = this.getSurroundParent();
+
+ // Find the parent procedure block to determine the return type
+ while (parent) {
+ if (parent.type === 'procedures_defreturn') {
+ return parent.getReturnType();
+ }
+ if (parent.type === 'procedures_defnoreturn') {
+ return 'void';
+ }
+ parent = parent.getSurroundParent();
+ }
+
+ return '';
+ }
+}
\ No newline at end of file
diff --git a/block-lexical-variables/src/blocks/variable-get-set.js b/block-lexical-variables/src/blocks/variable-get-set.js
index 5c54bea..dded78f 100644
--- a/block-lexical-variables/src/blocks/variable-get-set.js
+++ b/block-lexical-variables/src/blocks/variable-get-set.js
@@ -10,6 +10,7 @@ import {
} from '../fields/field_lexical_variable.js';
import * as Shared from '../shared.js';
import {NameSet} from "../nameSet.js";
+import {dataTypesEnabled} from "../shared.js";
/**
* Prototype bindings for a variable getter block.
@@ -24,7 +25,12 @@ Blockly.Blocks['lexical_variable_get'] = {
this.appendDummyInput()
.appendField(Blockly.Msg.LANG_VARIABLES_GET_TITLE_GET)
.appendField(this.fieldVar_, 'VAR');
- this.setOutput(true, null);
+ const type = this.getVariableType();
+ if (dataTypesEnabled()) {
+ this.setOutput(true, type ? [type] : null);
+ } else {
+ this.setOutput(true, null);
+ }
this.setTooltip(Blockly.Msg.LANG_VARIABLES_GET_TOOLTIP);
this.errors = [
{func: ErrorCheckers.checkIsInDefinition},
@@ -35,6 +41,10 @@ Blockly.Blocks['lexical_variable_get'] = {
];
this.setOnChange(function(changeEvent) {
this.workspace.getWarningHandler().checkErrors(this);
+ if (dataTypesEnabled()) {
+ const type = this.getVariableType();
+ this.setOutput(true, type ? [type] : null);
+ }
});
},
referenceResults: function(name, prefix, env) {
@@ -85,6 +95,9 @@ Blockly.Blocks['lexical_variable_get'] = {
getDeclaredVars: function() {
return [this.getFieldValue('VAR')];
},
+ getDeclaredVariableType: function() {
+ return [this.getFieldValue('TYPE')];
+ },
renameLexicalVar: function(oldName, newName, oldTranslatedName,
newTranslatedName) {
if (oldTranslatedName === undefined) {
@@ -129,6 +142,14 @@ Blockly.Blocks['lexical_variable_get'] = {
return new NameSet();
}
},
+ getVariableType: function() {
+ return this.fieldVar_.getVariableType();
+ },
+ changeVariableType: function() {
+ this.fieldVar_.getOptions(false);
+ this.fieldVar_.setValue(this.getFieldValue('VAR')); // Reselect to update the selected option
+ this.fieldVar_.forceRerender();
+ }
};
/**
@@ -156,8 +177,17 @@ Blockly.Blocks['lexical_variable_set'] = {
dropDowns: ['VAR'],
},
];
+
+ if (dataTypesEnabled()) {
+ const type = this.getVariableType();
+ this.getInput('VALUE').setCheck(type ? [type] : null);
+ }
this.setOnChange(function(changeEvent) {
this.workspace.getWarningHandler().checkErrors(this);
+ if (dataTypesEnabled()) {
+ const type = this.getVariableType();
+ this.getInput('VALUE').setCheck(type ? [type] : null);
+ }
});
},
referenceResults: Blockly.Blocks.lexical_variable_get.referenceResults,
@@ -198,4 +228,8 @@ Blockly.Blocks['lexical_variable_set'] = {
}
return result;
},
+ getVariableType: function() {
+ return this.fieldVar_.getVariableType();
+ },
+ changeVariableType: Blockly.Blocks.lexical_variable_get.changeVariableType,
};
diff --git a/block-lexical-variables/src/core.js b/block-lexical-variables/src/core.js
index 1a58dad..369fd22 100644
--- a/block-lexical-variables/src/core.js
+++ b/block-lexical-variables/src/core.js
@@ -31,7 +31,9 @@ export class LexicalVariablesPlugin {
/**
* @param workspace
*/
- static init(workspace) {
+ static init(workspace, options) {
+ Blockly.types_ = options.types;
+
// TODO(ewpatton): We need to make sure this is reentrant.
const rendererName = workspace.getRenderer().getClassName();
const themeName = workspace.getTheme().getClassName();
diff --git a/block-lexical-variables/src/fields/field_lexical_variable.js b/block-lexical-variables/src/fields/field_lexical_variable.js
index b44f297..7ed8532 100644
--- a/block-lexical-variables/src/fields/field_lexical_variable.js
+++ b/block-lexical-variables/src/fields/field_lexical_variable.js
@@ -163,7 +163,11 @@ FieldLexicalVariable.getGlobalNames = function(optExcludedBlock) {
const block = blocks[i];
if ((block.getGlobalNames) &&
(block != optExcludedBlock)) {
- globals.push(...block.getGlobalNames());
+ const names = block.getGlobalNames();
+ const type = block.getVariableType();
+ names.forEach(name => {
+ globals.push([name, name, type])
+ })
}
}
}
@@ -206,7 +210,7 @@ FieldLexicalVariable.getNamesInScope = function(block) {
// [lyn, 11/24/12] Sort and remove duplicates from namespaces
globalNames = LexicalVariable.sortAndRemoveDuplicates(globalNames);
globalNames = globalNames.map(function(name) {
- return [Shared.prefixGlobalMenuName(name), 'global ' + name];
+ return [Shared.prefixGlobalMenuName(name[0]), 'global ' + name[1], name[2]];
});
const allLexicalNames = FieldLexicalVariable.getLexicalNamesInScope(
block);
@@ -242,8 +246,9 @@ FieldLexicalVariable.getLexicalNamesInScope = function(block) {
* @param list
* @param prefix
* @param {string=} translated The translated name of the variable, if any
+ * @param type
*/
- function rememberName(codeName, list, prefix, translated) {
+ function rememberName(codeName, list, prefix, translated, type) {
const name = translated || codeName;
let fullName;
if (!Shared.usePrefixInCode) { // Only a single namespace
@@ -257,7 +262,7 @@ FieldLexicalVariable.getLexicalNamesInScope = function(block) {
// note: correctly handles case where some prefixes are the same
fullName = (Shared.possiblyPrefixMenuNameWith(prefix))(name);
}
- list.push([fullName, codeName]);
+ list.push([fullName, codeName, type]);
}
child = block;
@@ -266,8 +271,8 @@ FieldLexicalVariable.getLexicalNamesInScope = function(block) {
if (parent) {
while (parent) {
if (parent.withLexicalVarsAndPrefix) {
- parent.withLexicalVarsAndPrefix(child, (lexVar, prefix, translated) => {
- rememberName(lexVar, allLexicalNames, prefix, translated);
+ parent.withLexicalVarsAndPrefix(child, (lexVar, prefix, translated, type) => {
+ rememberName(lexVar, allLexicalNames, prefix, translated, type);
});
}
child = parent;
@@ -290,9 +295,9 @@ FieldLexicalVariable.dropdownCreate = function() {
if (variableList.length > 0) {
return variableList;
} else if (this.translatedName) {
- return [[this.translatedName, this.varname]];
+ return [[this.translatedName, this.varname, this.getVariableType?.() || '']];
} else {
- return [[' ', ' ']];
+ return [[' ', ' ', '']];
}
};
@@ -348,13 +353,27 @@ FieldLexicalVariable.prototype.doValueUpdate_ = function(newValue) {
}
this.value_ = newValue;
- // Note that we are asking getOptions to add newValue to the list of available
- // options. We do that essentially to force callers up the chain to accept
- // newValue as an option. This could potentially cause trouble, but it seems
- // to be ok for our use case. It is ugly, though, since it bypasses an aspect
- // of the normal dropdown validation.
- const options =
- this.getOptions(true, [[genLocalizedValue(newValue), newValue]]);
+
+ // Try to fetch the parameter type from the flydown
+ let extraOption;
+ try {
+ const topWs = this.sourceBlock_ && this.sourceBlock_.workspace && this.sourceBlock_.workspace.getTopWorkspace
+ ? this.sourceBlock_.workspace.getTopWorkspace()
+ : null;
+ const flydown = topWs && typeof topWs.getFlydown === 'function' ? topWs.getFlydown() : null;
+ const openerField = flydown && flydown.field_ ? flydown.field_ : null;
+ const vartype = openerField && typeof openerField.getVariableType === 'function'
+ ? openerField.getVariableType()
+ : undefined;
+
+ if (vartype) {
+ extraOption = [[genLocalizedValue(newValue), newValue, vartype]];
+ }
+ } catch (e) {
+ // Fall back silently if flydown not available.
+ }
+
+ const options = this.getOptions(false, extraOption || [[genLocalizedValue(newValue), newValue]]);
for (let i = 0, option; (option = options[i]); i++) {
if (option[1] == this.value_) {
this.selectedOption = option;
@@ -591,6 +610,10 @@ FieldLexicalVariable.fromJson = function(options) {
return new FieldLexicalVariable(name);
};
+FieldLexicalVariable.prototype.getVariableType = function () {
+ return this.selectedOption[2];
+}
+
Blockly.fieldRegistry.register('field_lexical_variable',
FieldLexicalVariable);
@@ -613,7 +636,7 @@ LexicalVariable.renameGlobal = function(newName) {
const globals = FieldLexicalVariable.getGlobalNames(this.sourceBlock_);
// this.sourceBlock excludes block being renamed from consideration
// Potentially rename declaration against other occurrences
- newName = FieldLexicalVariable.nameNotIn(newName, globals);
+ newName = FieldLexicalVariable.nameNotIn(newName, globals.map((element) => element[0]));
if (this.sourceBlock_.rendered) {
// Rename getters and setters
if (Blockly.common.getMainWorkspace()) {
@@ -1129,3 +1152,45 @@ LexicalVariable.stringListsEqual = function(strings1, strings2) {
}
return true; // get here iff lists are equal
};
+
+LexicalVariable.changeVariableType = function(block, name, oldType, newType) {
+ let inScopeBlocks = [];
+ if (block.blocksInScope) {
+ inScopeBlocks = block.blocksInScope();
+ }
+
+ const referenceResults = inScopeBlocks.map(function(blk) {
+ return LexicalVariable.referenceResult(blk, name,
+ '', []);
+ });
+
+ let blockToChangeType = []
+ let capturables = [];
+ for (let r = 0; r < referenceResults.length; r++) {
+ blockToChangeType = blockToChangeType.concat(referenceResults[r][0]);
+ capturables = capturables.concat(referenceResults[r][1]);
+ }
+
+ // Rename getters and setters
+ for (let i = 0; i < blockToChangeType.length; i++) {
+ const block = blockToChangeType[i];
+ if (block.changeVariableType) {
+ block.changeVariableType(newType);
+ }
+
+ }
+}
+
+LexicalVariable.changeGlobalVariableType = function(name, oldType, newType) {
+ if (Blockly.common.getMainWorkspace()) {
+ const blocks = Blockly.common.getMainWorkspace().getAllBlocks();
+ for (let i = 0; i < blocks.length; i++) {
+ const block = blocks[i];
+ if (block.type === 'lexical_variable_set' || block.type === 'lexical_variable_get') {
+ if (block.getDeclaredVars().includes(`global ${name}`)) {
+ block.changeVariableType(newType)
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/block-lexical-variables/src/fields/field_parameter_flydown.js b/block-lexical-variables/src/fields/field_parameter_flydown.js
index 9be3e8d..0eee6bd 100644
--- a/block-lexical-variables/src/fields/field_parameter_flydown.js
+++ b/block-lexical-variables/src/fields/field_parameter_flydown.js
@@ -37,7 +37,7 @@ import '../blocks/variable-get-set.js';
// [lyn, 10/26/13] Added opt_additionalChangeHandler to handle propagation of
// renaming of proc decl params
export class FieldParameterFlydown extends FieldFlydown {
- constructor(name, isEditable, opt_displayLocation, opt_additionalChangeHandler) {
+ constructor(name, isEditable, opt_displayLocation, opt_additionalChangeHandler, type) {
const changeHandler = function(text) {
if (!FieldParameterFlydown.changeHandlerEnabled) {
return text;
@@ -54,10 +54,17 @@ export class FieldParameterFlydown extends FieldFlydown {
};
super(name, isEditable, opt_displayLocation, changeHandler);
+ this.type_ = type;
};
referencesVariables() {
return true;
};
+ getVariableType() {
+ return this.type_;
+ }
+ setVariableType(type) {
+ this.type_ = type;
+ }
}
FieldParameterFlydown.prototype.fieldCSSClassName =
@@ -176,5 +183,3 @@ FieldParameterFlydown.fromJson = function(options) {
Blockly.fieldRegistry.register('field_parameter_flydown',
FieldParameterFlydown);
-
-
diff --git a/block-lexical-variables/src/generators/procedures.js b/block-lexical-variables/src/generators/procedures.js
index 4b7d10a..967865d 100644
--- a/block-lexical-variables/src/generators/procedures.js
+++ b/block-lexical-variables/src/generators/procedures.js
@@ -26,4 +26,9 @@ if (pkg) {
const code = funcName + '(' + args.join(', ') + ')';
return [code, Order.FUNCTION_CALL];
};
+
+ javascriptGenerator.forBlock['procedures_early_return'] = function (block, generator) {
+ const returnValue = generator.valueToCode(block, 'RETURN_VALUE', Order.NONE) || 'null';
+ return `return ${returnValue};\n`;
+ }
}
diff --git a/block-lexical-variables/src/mixins.js b/block-lexical-variables/src/mixins.js
index 93389b2..fc3ee8f 100644
--- a/block-lexical-variables/src/mixins.js
+++ b/block-lexical-variables/src/mixins.js
@@ -41,7 +41,8 @@ export const coreLexicalVariableScopeMixin = {
if (child && thisBlock.getInputTargetBlock(thisBlock.getScopedInputName()) === child) {
thisBlock.getDeclaredVarFieldNamesAndPrefixes().forEach(([fieldName, prefix]) => {
const lexVar = thisBlock.getFieldValue(fieldName);
- proc(lexVar, prefix);
+ if (thisBlock && thisBlock.getVariableType) proc(lexVar, prefix, '', thisBlock.getVariableType());
+ else proc(lexVar, prefix);
});
}
},
diff --git a/block-lexical-variables/src/msg.js b/block-lexical-variables/src/msg.js
index cdaa364..1e9632d 100644
--- a/block-lexical-variables/src/msg.js
+++ b/block-lexical-variables/src/msg.js
@@ -148,3 +148,5 @@ Blockly.Msg['LANG_CONTROLS_DO_THEN_RETURN_COLLAPSED_TEXT'] = 'do/result';
Blockly.Msg['LANG_CONTROLS_DO_THEN_RETURN_TITLE'] = 'do result';
Blockly.Msg['PROCEDURES_DEFNORETURN_PROCEDURE'] = Blockly.Msg['LANG_PROCEDURES_DEFNORETURN_PROCEDURE'];
Blockly.Msg['PROCEDURES_DEFRETURN_PROCEDURE'] = Blockly.Msg['LANG_PROCEDURES_DEFRETURN_PROCEDURE'];
+Blockly.Msg['LANG_PROCEDURES_EARLY_RETURN_TOOLTIP'] = 'return';
+Blockly.Msg['LANG_PROCEDURES_EARLY_RETURN_TITLE'] = 'early return';
\ No newline at end of file
diff --git a/block-lexical-variables/src/shared.js b/block-lexical-variables/src/shared.js
index 70399c1..a7af242 100644
--- a/block-lexical-variables/src/shared.js
+++ b/block-lexical-variables/src/shared.js
@@ -116,3 +116,6 @@ export const possiblyPrefixGeneratedVarName = function(prefix) {
};
};
+export const dataTypesEnabled = function () {
+ return Blockly.types_ && Blockly.types_.enableDataTypes;
+}
diff --git a/block-lexical-variables/test/index.js b/block-lexical-variables/test/index.js
index 1c1dc26..706d1d4 100644
--- a/block-lexical-variables/test/index.js
+++ b/block-lexical-variables/test/index.js
@@ -39,7 +39,31 @@ const allBlocks = [
*/
function createWorkspace(blocklyDiv, options) {
const workspace = Blockly.inject(blocklyDiv, options);
- LexicalVariablesPlugin.init(workspace);
+ LexicalVariablesPlugin.init(workspace, {
+ types: {
+ enableDataTypes: true,
+ dataTypes: [
+ ['int', 'int'],
+ ['float', 'float'],
+ ['boolean', 'boolean'],
+ ['char*', 'String'],
+ ],
+ loopType: 'int',
+ defaultType: 'int',
+ }
+ });
+
+workspace.registerToolboxCategoryCallback(
+ 'CUSTOM_PROCEDURE',
+ function(workspace) {
+ const xmlList = Blockly.Procedures.flyoutCategory(workspace);
+ const earlyReturn = document.createElement('block');
+ earlyReturn.setAttribute('type', 'procedures_early_return');
+ xmlList.push(earlyReturn);
+ return xmlList;
+ }
+);
+
return workspace;
}
@@ -60,7 +84,7 @@ document.addEventListener('DOMContentLoaded', function() {
`,
collapse: true,