diff --git a/block-lexical-variables/README.md b/block-lexical-variables/README.md
index 5333e3f..3c8ff2a 100644
--- a/block-lexical-variables/README.md
+++ b/block-lexical-variables/README.md
@@ -71,6 +71,12 @@ and while hovering over the variable name:

+**Block type: 'initialize_global' and 'global_declaration_entry'** - Provide a more structured way to initialize global variables by grouping multiple initializations together.
+
+
+
+Note: It is recommended not to mix the two different ways of initializing global variables.
+
## Variable/Parameter setters and getters
### Setter
**Block type: 'lexical_variable_set'** - Note that despite the block type name, the
diff --git a/block-lexical-variables/readme-media/initialize-global-block.png b/block-lexical-variables/readme-media/initialize-global-block.png
new file mode 100644
index 0000000..a2ae7ec
Binary files /dev/null and b/block-lexical-variables/readme-media/initialize-global-block.png differ
diff --git a/block-lexical-variables/src/blocks/lexical-variables.js b/block-lexical-variables/src/blocks/lexical-variables.js
index 747b79b..dfa6904 100644
--- a/block-lexical-variables/src/blocks/lexical-variables.js
+++ b/block-lexical-variables/src/blocks/lexical-variables.js
@@ -137,8 +137,120 @@ Blockly.Blocks['global_declaration'] = {
},
};
+Blockly.Blocks['global_declaration_entry'] = {
+ category: 'Variables',
+ 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.setPreviousStatement(true, ['global_declaration_entry', 'initialize_global']);
+ this.setNextStatement(true, ['global_declaration_entry']);
+ this.setTooltip(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_TOOLTIP);
+ this.setOnChange(this.checkPlacement_);
+ },
+ getDeclaredVars: Blockly.Blocks.global_declaration.getDeclaredVars,
+ getGlobalNames: Blockly.Blocks.global_declaration.getGlobalNames,
+ renameVar: Blockly.Blocks.global_declaration.renameVar,
+ checkPlacement_: function() {
+ if (this.isInFlyout) return;
+
+ const REASON = Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_WARNING;
+ const parent = this.getSurroundParent();
+
+ if (!parent || parent.type !== 'initialize_global') {
+ this.setWarningText(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_WARNING, 'global_declaration_entry');
+ if (this.workspace.disableInvalidBlocks) {
+ this.setDisabledReason(true, REASON);
+ }
+ } else {
+ this.setWarningText(null, 'global_declaration_entry');
+ if (this.workspace.disableInvalidBlocks) {
+ this.setDisabledReason(false, REASON);
+ }
+ }
+ }
+};
+
+Blockly.Blocks['initialize_global'] = {
+ category: 'Variables',
+ helpUrl: Blockly.Msg.LANG_VARIABLES_LOCAL_DECLARATION_HELPURL,
+ init: function () {
+ this.setStyle('variable_blocks');
+ this.appendDummyInput()
+ .appendField(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_TITLE_INIT)
+ this.appendStatementInput('DO')
+ .appendField(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_TO_DO);
+ this.setTooltip(Blockly.Msg.LANG_VARIABLES_LOCAL_DECLARATION_TOOLTIP);
+ this.setOnChange(this.checkChildren_)
+ queueMicrotask(this.checkChildren_.bind(this));
+ },
+ getDeclaredVarFieldNames: function () {
+ return ['VAR'];
+ },
+ getScopedInputName: function () {
+ return 'DO';
+ },
+ getGlobalNames: function (block) {
+ const names = []
+ let childBlock = this.getInputTargetBlock('DO')
+ while (childBlock) {
+ if (childBlock.type === 'global_declaration_entry' && block !== childBlock) {
+ names.push(...childBlock.getGlobalNames())
+ }
+ childBlock = childBlock.getNextBlock()
+ }
+ return names
+ },
+ checkChildren_: function(event) {
+ if (this.isInFlyout) return;
+ if (event && event.type !== Blockly.Events.BLOCK_MOVE && event.type !== Blockly.Events.BLOCK_DRAG) return;
+
+ const REASON = Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_BLOCK_CHECK;
+ const inStack = new Set();
+
+ // Validate child blocks are global_declaration_entry types and mark them
+ let childBlock = this.getInputTargetBlock('DO');
+ while (childBlock) {
+ inStack.add(childBlock.id);
+ if (childBlock.type !== 'global_declaration_entry') {
+ childBlock.setWarningText(REASON, 'initialize_global');
+ if (this.workspace.disableInvalidBlocks) {
+ childBlock.setDisabledReason(true, REASON);
+ }
+ childBlock.__disabledByInitGlobal = true;
+ } else {
+ childBlock.setWarningText(null, 'initialize_global');
+ if (this.workspace.disableInvalidBlocks) {
+ childBlock.setDisabledReason(false, REASON);
+ }
+ childBlock.__disabledByInitGlobal = false;
+ }
+ childBlock = childBlock.getNextBlock();
+ }
+
+ // If a block was moved OUT, clear our disable flag/state
+ if (event && event.blockId) {
+ let moved = this.workspace.getBlockById(event.blockId);
+ while (moved) {
+ if (moved && moved.__disabledByInitGlobal && !inStack.has(moved.id)) {
+ moved.setWarningText(null, 'initialize_global');
+ if (this.workspace.disableInvalidBlocks) {
+ moved.setDisabledReason(false, REASON);
+ }
+ moved.__disabledByInitGlobal = false;
+ }
+ moved = moved.getNextBlock();
+ }
+ }
+ }
+}
+
Blockly.Blocks['simple_local_declaration_statement'] = {
- // For each loop.
category: 'Variables',
helpUrl: Blockly.Msg.LANG_VARIABLES_LOCAL_DECLARATION_HELPURL,
init: function () {
diff --git a/block-lexical-variables/src/blocks/procedures.js b/block-lexical-variables/src/blocks/procedures.js
index 765cad7..1819368 100644
--- a/block-lexical-variables/src/blocks/procedures.js
+++ b/block-lexical-variables/src/blocks/procedures.js
@@ -160,9 +160,9 @@ Blockly.Blocks['procedures_defnoreturn'] = {
hash['arg_' + this.arguments_[x].toLowerCase()] = true;
}
if (badArg) {
- this.setWarningText(Blockly.Msg['LANG_PROCEDURES_DEF_DUPLICATE_WARNING']);
+ this.setWarningText(Blockly.Msg['LANG_PROCEDURES_DEF_DUPLICATE_WARNING'], 'procedures_defnoreturn');
} else {
- this.setWarningText(null);
+ this.setWarningText(null, 'procedures_defnoreturn');
}
const procName = this.getFieldValue('NAME');
diff --git a/block-lexical-variables/src/fields/field_lexical_variable.js b/block-lexical-variables/src/fields/field_lexical_variable.js
index 261715c..77714fe 100644
--- a/block-lexical-variables/src/fields/field_lexical_variable.js
+++ b/block-lexical-variables/src/fields/field_lexical_variable.js
@@ -164,8 +164,8 @@ FieldLexicalVariable.getGlobalNames = function(optExcludedBlock) {
for (let i = 0; i < blocks.length; i++) {
const block = blocks[i];
if ((block.getGlobalNames) &&
- (block != optExcludedBlock)) {
- globals.push(...block.getGlobalNames());
+ (block != optExcludedBlock) && block.isEnabled()) {
+ globals.push(...block.getGlobalNames(optExcludedBlock));
}
}
}
diff --git a/block-lexical-variables/src/generators/lexical-variables.js b/block-lexical-variables/src/generators/lexical-variables.js
index 7b72387..80caf2d 100644
--- a/block-lexical-variables/src/generators/lexical-variables.js
+++ b/block-lexical-variables/src/generators/lexical-variables.js
@@ -52,6 +52,13 @@ if (pkg) {
return 'var ' + genBasicSetterCode(block, 'NAME', generator);
};
+ javascriptGenerator.forBlock['global_declaration_entry'] = javascriptGenerator.forBlock['global_declaration'];
+
+ javascriptGenerator.forBlock['initialize_global'] = function (block, generator) {
+ // Global variable declaration
+ return generator.statementToCode(block, 'DO');
+ };
+
function generateDeclarations(block, generator) {
let code = '{\n let ';
for (let i = 0; block.getFieldValue('VAR' + i); i++) {
diff --git a/block-lexical-variables/src/msg.js b/block-lexical-variables/src/msg.js
index cdaa364..72e9d6f 100644
--- a/block-lexical-variables/src/msg.js
+++ b/block-lexical-variables/src/msg.js
@@ -148,3 +148,9 @@ 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_VARIABLES_GLOBAL_DECLARATION_WARNING'] = 'Global declarations must be inside of a initialize global block.'
+Blockly.Msg['LANG_VARIABLES_GLOBAL_DECLARATION_TITLE_INIT'] = 'initialize global variables';
+Blockly.Msg['LANG_VARIABLES_GLOBAL_DECLARATION_TO_DO'] = 'to';
+Blockly.Msg['LANG_VARIABLES_GLOBAL_DECLARATION_TOOLTIP'] =
+ 'Allows you to create global variables that are accessible everywhere';
+Blockly.Msg['LANG_VARIABLES_GLOBAL_DECLARATION_BLOCK_CHECK'] = 'Only global_declaration_entry blocks are allowed here.';
\ No newline at end of file
diff --git a/block-lexical-variables/src/warningHandler.js b/block-lexical-variables/src/warningHandler.js
index 38c9add..be6d585 100644
--- a/block-lexical-variables/src/warningHandler.js
+++ b/block-lexical-variables/src/warningHandler.js
@@ -74,17 +74,17 @@ export default class WarningHandler {
}
// remove the warning icon, if there is one
- block.setWarningText(null);
+ block.setWarningText(null, 'warningHandler');
if (block.hasWarning) {
block.hasWarning = false;
}
};
setError(block, message) {
- block.setWarningText(message);
+ block.setWarningText(message, 'warningHandler');
}
setWarning(block, message) {
- block.setWarningText(message);
+ block.setWarningText(message, 'warningHandler');
}
}
diff --git a/block-lexical-variables/test/field_lexical_var.mocha.js b/block-lexical-variables/test/field_lexical_var.mocha.js
index ce96e51..0429d44 100644
--- a/block-lexical-variables/test/field_lexical_var.mocha.js
+++ b/block-lexical-variables/test/field_lexical_var.mocha.js
@@ -39,6 +39,7 @@ import {NameSet} from "../src/nameSet";
suite ('FieldLexical', function() {
setup(function() {
this.workspace = new Blockly.Workspace();
+ this.workspace.disableInvalidBlocks = true;
Blockly.common.setMainWorkspace(this.workspace);
this.createBlock = function (type) {
@@ -90,6 +91,52 @@ suite ('FieldLexical', function() {
const vars = FieldLexicalVariable.getGlobalNames();
chai.assert.sameOrderedMembers(vars, ['global', 'global2', 'global3']);
});
+ test('initialize globals mixed', function () {
+ const xml = Blockly.utils.xml.textToDom('' +
+ ' ' +
+ ' name' +
+ ' ' +
+ ' ' +
+ ' name2' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' a' +
+ ' ' +
+ ' ' +
+ ' b' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '');
+ Blockly.Xml.domToWorkspace(xml, this.workspace);
+ const vars = FieldLexicalVariable.getGlobalNames();
+ chai.assert.sameOrderedMembers(vars, ['name', 'name2', 'a', 'b']);
+ })
+ test('global_declaration_entry disabled if outside of initialize_global', async function () {
+ const xml = Blockly.utils.xml.textToDom('' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' a' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' b' +
+ ' ' +
+ '');
+ Blockly.Xml.domToWorkspace(xml, this.workspace);
+ const block = this.workspace.getBlockById('b')
+ // Trigger placement check manually since it's only automatically called after BlockCreate
+ block.checkPlacement_();
+ const vars = FieldLexicalVariable.getGlobalNames();
+ chai.assert.sameOrderedMembers(vars, ['a']);
+ chai.assert.equal(block.isEnabled(), false)
+ })
});
suite('getLexicalNamesInScope', function() {
setup(function() {
diff --git a/block-lexical-variables/test/index.js b/block-lexical-variables/test/index.js
index 95d4cd8..d0d93cb 100644
--- a/block-lexical-variables/test/index.js
+++ b/block-lexical-variables/test/index.js
@@ -52,6 +52,8 @@ document.addEventListener('DOMContentLoaded', function() {
+
+