From 3f8d6b99f1bcfb1a6cdd12bc2b376e275b460470 Mon Sep 17 00:00:00 2001 From: TannerGabriel Date: Fri, 8 Aug 2025 17:04:40 +0200 Subject: [PATCH 1/5] Global declaration placement check and allow previous and next statement --- .../src/blocks/lexical-variables.js | 14 ++++++++++++++ block-lexical-variables/src/msg.js | 1 + 2 files changed, 15 insertions(+) diff --git a/block-lexical-variables/src/blocks/lexical-variables.js b/block-lexical-variables/src/blocks/lexical-variables.js index f308ba8..359e056 100644 --- a/block-lexical-variables/src/blocks/lexical-variables.js +++ b/block-lexical-variables/src/blocks/lexical-variables.js @@ -121,7 +121,10 @@ Blockly.Blocks['global_declaration'] = { Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_NAME, FieldFlydown.DISPLAY_BELOW), 'NAME') .appendField(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_TO); + this.setPreviousStatement(true); + this.setNextStatement(true); this.setTooltip(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_TOOLTIP); + this.setOnChange(this._checkPlacement); }, getDeclaredVars: function() { const field = this.getField('NAME'); @@ -135,6 +138,17 @@ Blockly.Blocks['global_declaration'] = { this.setFieldValue(newName, 'NAME'); } }, + _checkPlacement: function() { + const parent = this.getSurroundParent(); + + if (parent) { + this.setWarningText(Blockly.Msg.VARIABLES_GLOBAL_DECLARATION_WARNING); + this.setEnabled(false); + } else { + this.setWarningText(null); + this.setEnabled(true); + } + } }; Blockly.Blocks['simple_local_declaration_statement'] = { diff --git a/block-lexical-variables/src/msg.js b/block-lexical-variables/src/msg.js index cdaa364..7f0265a 100644 --- a/block-lexical-variables/src/msg.js +++ b/block-lexical-variables/src/msg.js @@ -148,3 +148,4 @@ 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['VARIABLES_GLOBAL_DECLARATION_WARNING'] = 'Global declarations must be at the top level.' \ No newline at end of file From 9e0071685f59a4c22b46f52285dc92235cb52817 Mon Sep 17 00:00:00 2001 From: TannerGabriel Date: Sun, 7 Sep 2025 14:31:06 +0200 Subject: [PATCH 2/5] Add initialize_global and global_declaration2 blocks --- .../src/blocks/lexical-variables.js | 103 ++++++++++++++++-- .../src/fields/field_lexical_variable.js | 4 +- .../src/generators/lexical-variables.js | 7 ++ block-lexical-variables/src/msg.js | 7 +- .../test/field_lexical_var.mocha.js | 46 ++++++++ block-lexical-variables/test/index.js | 2 + 6 files changed, 157 insertions(+), 12 deletions(-) diff --git a/block-lexical-variables/src/blocks/lexical-variables.js b/block-lexical-variables/src/blocks/lexical-variables.js index 01d29c9..0c2db4c 100644 --- a/block-lexical-variables/src/blocks/lexical-variables.js +++ b/block-lexical-variables/src/blocks/lexical-variables.js @@ -121,10 +121,7 @@ Blockly.Blocks['global_declaration'] = { Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_NAME, FieldFlydown.DISPLAY_BELOW), 'NAME') .appendField(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_TO); - this.setPreviousStatement(true); - this.setNextStatement(true); this.setTooltip(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_TOOLTIP); - this.setOnChange(this._checkPlacement); }, getDeclaredVars: function() { const field = this.getField('NAME'); @@ -138,21 +135,109 @@ Blockly.Blocks['global_declaration'] = { this.setFieldValue(newName, 'NAME'); } }, +}; + +Blockly.Blocks['global_declaration2'] = { + 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_declaration2', 'initialize_global']); + this.setNextStatement(true, ['global_declaration2']); + 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) { - this.setWarningText(Blockly.Msg.VARIABLES_GLOBAL_DECLARATION_WARNING); - this.setEnabled(false); + if (!parent || parent.type !== 'initialize_global') { + this.setWarningText(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_WARNING); + this.setDisabledReason(true, REASON); } else { - this.setWarningText(null); - this.setEnabled(true); + this.setWarningText(null); + 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) + }, + getDeclaredVarFieldNames: function () { + return ['VAR']; + }, + getScopedInputName: function () { + return 'DO'; + }, + getGlobalNames: function (block) { + const names = [] + let b = this.getInputTargetBlock('DO') + while (b) { + if (b.type === 'global_declaration2' && block !== b) { + names.push(...b.getGlobalNames()) + } + b = b.getNextBlock() + } + return names + }, + _checkChildren: function(e) { + if (this.isInFlyout) return; + if (e && e.type !== Blockly.Events.BLOCK_MOVE && e.type !== Blockly.Events.BLOCK_DRAG) return; + + const REASON = Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_BLOCK_CHECK; + const inStack = new Set(); + + // Enforce on current children and mark them + let b = this.getInputTargetBlock('DO'); + while (b) { + inStack.add(b.id); + if (b.type !== 'global_declaration2') { + b.setWarningText(REASON); + b.setDisabledReason(true, REASON); + b.__disabledByInitGlobal = true; + } else { + b.setWarningText(null); + b.setDisabledReason(false, REASON); + b.__disabledByInitGlobal = false; + } + b = b.getNextBlock(); + } + + // If a block was moved OUT, clear our disable flag/state + if (e && e.blockId) { + const moved = this.workspace.getBlockById(e.blockId); + if (moved && moved.__disabledByInitGlobal && !inStack.has(moved.id)) { + moved.setWarningText(null); + moved.setDisabledReason(false, REASON); + moved.__disabledByInitGlobal = false; + } + } + } +} + 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/fields/field_lexical_variable.js b/block-lexical-variables/src/fields/field_lexical_variable.js index b44f297..956faad 100644 --- a/block-lexical-variables/src/fields/field_lexical_variable.js +++ b/block-lexical-variables/src/fields/field_lexical_variable.js @@ -162,8 +162,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..5105edc 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_declaration2'] = 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 7f0265a..46ba05f 100644 --- a/block-lexical-variables/src/msg.js +++ b/block-lexical-variables/src/msg.js @@ -148,4 +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['VARIABLES_GLOBAL_DECLARATION_WARNING'] = 'Global declarations must be at the top level.' \ No newline at end of file +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_declaration2 blocks are allowed here.'; \ No newline at end of file diff --git a/block-lexical-variables/test/field_lexical_var.mocha.js b/block-lexical-variables/test/field_lexical_var.mocha.js index ce96e51..d40c3a8 100644 --- a/block-lexical-variables/test/field_lexical_var.mocha.js +++ b/block-lexical-variables/test/field_lexical_var.mocha.js @@ -90,6 +90,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_declaration2 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 1c1dc26..80175b4 100644 --- a/block-lexical-variables/test/index.js +++ b/block-lexical-variables/test/index.js @@ -52,6 +52,8 @@ document.addEventListener('DOMContentLoaded', function() { + + From d14241a5e341f30b9c34a3b400fc54b3640a2000 Mon Sep 17 00:00:00 2001 From: TannerGabriel Date: Thu, 11 Sep 2025 09:35:52 +0200 Subject: [PATCH 3/5] Refactor warning handling for global declarations and procedures --- .../src/blocks/lexical-variables.js | 22 +++++++++++-------- .../src/blocks/procedures.js | 4 ++-- block-lexical-variables/src/warningHandler.js | 6 ++--- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/block-lexical-variables/src/blocks/lexical-variables.js b/block-lexical-variables/src/blocks/lexical-variables.js index 0c2db4c..0082d1b 100644 --- a/block-lexical-variables/src/blocks/lexical-variables.js +++ b/block-lexical-variables/src/blocks/lexical-variables.js @@ -163,10 +163,10 @@ Blockly.Blocks['global_declaration2'] = { const parent = this.getSurroundParent(); if (!parent || parent.type !== 'initialize_global') { - this.setWarningText(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_WARNING); + this.setWarningText(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_WARNING, 'global_declaration2'); this.setDisabledReason(true, REASON); } else { - this.setWarningText(null); + this.setWarningText(null, 'global_declaration2'); this.setDisabledReason(false, REASON); } } @@ -184,6 +184,7 @@ Blockly.Blocks['initialize_global'] = { .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']; @@ -214,11 +215,11 @@ Blockly.Blocks['initialize_global'] = { while (b) { inStack.add(b.id); if (b.type !== 'global_declaration2') { - b.setWarningText(REASON); + b.setWarningText(REASON, 'initialize_global'); b.setDisabledReason(true, REASON); b.__disabledByInitGlobal = true; } else { - b.setWarningText(null); + b.setWarningText(null, 'initialize_global'); b.setDisabledReason(false, REASON); b.__disabledByInitGlobal = false; } @@ -227,11 +228,14 @@ Blockly.Blocks['initialize_global'] = { // If a block was moved OUT, clear our disable flag/state if (e && e.blockId) { - const moved = this.workspace.getBlockById(e.blockId); - if (moved && moved.__disabledByInitGlobal && !inStack.has(moved.id)) { - moved.setWarningText(null); - moved.setDisabledReason(false, REASON); - moved.__disabledByInitGlobal = false; + let moved = this.workspace.getBlockById(e.blockId); + while (moved) { + if (moved && moved.__disabledByInitGlobal && !inStack.has(moved.id)) { + moved.setWarningText(null, 'initialize_global'); + moved.setDisabledReason(false, REASON); + moved.__disabledByInitGlobal = false; + } + moved = moved.getNextBlock(); } } } 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/warningHandler.js b/block-lexical-variables/src/warningHandler.js index df3effd..3a73e32 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'); } } From ff46493d5a32df8823d1cdee8e92f779c3e3e022 Mon Sep 17 00:00:00 2001 From: TannerGabriel Date: Fri, 12 Sep 2025 12:41:03 +0200 Subject: [PATCH 4/5] Apply style changes --- .../src/blocks/lexical-variables.js | 75 +++++++++---------- .../src/generators/lexical-variables.js | 2 +- block-lexical-variables/src/msg.js | 2 +- .../test/field_lexical_var.mocha.js | 12 +-- block-lexical-variables/test/index.js | 2 +- 5 files changed, 46 insertions(+), 47 deletions(-) diff --git a/block-lexical-variables/src/blocks/lexical-variables.js b/block-lexical-variables/src/blocks/lexical-variables.js index 0082d1b..a598790 100644 --- a/block-lexical-variables/src/blocks/lexical-variables.js +++ b/block-lexical-variables/src/blocks/lexical-variables.js @@ -137,36 +137,36 @@ Blockly.Blocks['global_declaration'] = { }, }; -Blockly.Blocks['global_declaration2'] = { +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_declaration2', 'initialize_global']); - this.setNextStatement(true, ['global_declaration2']); + .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); + this.setOnChange(this.checkPlacement_); }, getDeclaredVars: Blockly.Blocks.global_declaration.getDeclaredVars, getGlobalNames: Blockly.Blocks.global_declaration.getGlobalNames, renameVar: Blockly.Blocks.global_declaration.renameVar, - _checkPlacement: function() { + 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_declaration2'); + this.setWarningText(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_WARNING, 'global_declaration_entry'); this.setDisabledReason(true, REASON); } else { - this.setWarningText(null, 'global_declaration2'); + this.setWarningText(null, 'global_declaration_entry'); this.setDisabledReason(false, REASON); } } @@ -178,13 +178,12 @@ Blockly.Blocks['initialize_global'] = { init: function () { this.setStyle('variable_blocks'); this.appendDummyInput() - .appendField( - Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_TITLE_INIT) + .appendField(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_TITLE_INIT) this.appendStatementInput('DO') - .appendField(Blockly.Msg.LANG_VARIABLES_GLOBAL_DECLARATION_TO_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)); + this.setOnChange(this.checkChildren_) + queueMicrotask(this.checkChildren_.bind(this)); }, getDeclaredVarFieldNames: function () { return ['VAR']; @@ -194,41 +193,41 @@ Blockly.Blocks['initialize_global'] = { }, getGlobalNames: function (block) { const names = [] - let b = this.getInputTargetBlock('DO') - while (b) { - if (b.type === 'global_declaration2' && block !== b) { - names.push(...b.getGlobalNames()) + let childBlock = this.getInputTargetBlock('DO') + while (childBlock) { + if (childBlock.type === 'global_declaration_entry' && block !== childBlock) { + names.push(...childBlock.getGlobalNames()) } - b = b.getNextBlock() + childBlock = childBlock.getNextBlock() } return names }, - _checkChildren: function(e) { + checkChildren_: function(event) { if (this.isInFlyout) return; - if (e && e.type !== Blockly.Events.BLOCK_MOVE && e.type !== Blockly.Events.BLOCK_DRAG) 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(); - // Enforce on current children and mark them - let b = this.getInputTargetBlock('DO'); - while (b) { - inStack.add(b.id); - if (b.type !== 'global_declaration2') { - b.setWarningText(REASON, 'initialize_global'); - b.setDisabledReason(true, REASON); - b.__disabledByInitGlobal = true; + // 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'); + childBlock.setDisabledReason(true, REASON); + childBlock.__disabledByInitGlobal = true; } else { - b.setWarningText(null, 'initialize_global'); - b.setDisabledReason(false, REASON); - b.__disabledByInitGlobal = false; + childBlock.setWarningText(null, 'initialize_global'); + childBlock.setDisabledReason(false, REASON); + childBlock.__disabledByInitGlobal = false; } - b = b.getNextBlock(); + childBlock = childBlock.getNextBlock(); } // If a block was moved OUT, clear our disable flag/state - if (e && e.blockId) { - let moved = this.workspace.getBlockById(e.blockId); + 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'); diff --git a/block-lexical-variables/src/generators/lexical-variables.js b/block-lexical-variables/src/generators/lexical-variables.js index 5105edc..80caf2d 100644 --- a/block-lexical-variables/src/generators/lexical-variables.js +++ b/block-lexical-variables/src/generators/lexical-variables.js @@ -52,7 +52,7 @@ if (pkg) { return 'var ' + genBasicSetterCode(block, 'NAME', generator); }; - javascriptGenerator.forBlock['global_declaration2'] = javascriptGenerator.forBlock['global_declaration']; + javascriptGenerator.forBlock['global_declaration_entry'] = javascriptGenerator.forBlock['global_declaration']; javascriptGenerator.forBlock['initialize_global'] = function (block, generator) { // Global variable declaration diff --git a/block-lexical-variables/src/msg.js b/block-lexical-variables/src/msg.js index 46ba05f..72e9d6f 100644 --- a/block-lexical-variables/src/msg.js +++ b/block-lexical-variables/src/msg.js @@ -153,4 +153,4 @@ Blockly.Msg['LANG_VARIABLES_GLOBAL_DECLARATION_TITLE_INIT'] = 'initialize global 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_declaration2 blocks are allowed here.'; \ No newline at end of file +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/test/field_lexical_var.mocha.js b/block-lexical-variables/test/field_lexical_var.mocha.js index d40c3a8..e4ad11d 100644 --- a/block-lexical-variables/test/field_lexical_var.mocha.js +++ b/block-lexical-variables/test/field_lexical_var.mocha.js @@ -100,10 +100,10 @@ suite ('FieldLexical', function() { ' ' + ' ' + ' ' + - ' ' + + ' ' + ' a' + ' ' + - ' ' + + ' ' + ' b' + ' ' + ' ' + @@ -115,23 +115,23 @@ suite ('FieldLexical', function() { const vars = FieldLexicalVariable.getGlobalNames(); chai.assert.sameOrderedMembers(vars, ['name', 'name2', 'a', 'b']); }) - test('global_declaration2 disabled if outside of initialize_global', async function () { + 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(); + block.checkPlacement_(); const vars = FieldLexicalVariable.getGlobalNames(); chai.assert.sameOrderedMembers(vars, ['a']); chai.assert.equal(block.isEnabled(), false) diff --git a/block-lexical-variables/test/index.js b/block-lexical-variables/test/index.js index 80175b4..243edde 100644 --- a/block-lexical-variables/test/index.js +++ b/block-lexical-variables/test/index.js @@ -53,7 +53,7 @@ document.addEventListener('DOMContentLoaded', function() { - + From 9900e875998d9bde373bb36dd72581e5ff0f467c Mon Sep 17 00:00:00 2001 From: TannerGabriel Date: Sun, 14 Sep 2025 12:51:52 +0200 Subject: [PATCH 5/5] Only diable if disableInvalidBlocks is set and add blocks to documentation --- block-lexical-variables/README.md | 6 ++++++ .../readme-media/initialize-global-block.png | Bin 0 -> 18991 bytes .../src/blocks/lexical-variables.js | 20 +++++++++++++----- .../test/field_lexical_var.mocha.js | 1 + 4 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 block-lexical-variables/readme-media/initialize-global-block.png 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: ![A picture of a global variable block with getter and setter blocks](readme-media/globalvar-with-flydown.png "Global variable with flydown") +**Block type: 'initialize_global' and 'global_declaration_entry'** - Provide a more structured way to initialize global variables by grouping multiple initializations together. + +![A picture of a global variable initialization block](readme-media/initialize-global-block.png "Initialize global variable") + +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 0000000000000000000000000000000000000000..a2ae7ec5fbe780501726e0ce0da45b0c07144e5d GIT binary patch literal 18991 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%0D`N^!O7^ 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 VQn)