Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions block-lexical-variables/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
114 changes: 113 additions & 1 deletion block-lexical-variables/src/blocks/lexical-variables.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () {
Expand Down
4 changes: 2 additions & 2 deletions block-lexical-variables/src/blocks/procedures.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
4 changes: 2 additions & 2 deletions block-lexical-variables/src/fields/field_lexical_variable.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions block-lexical-variables/src/generators/lexical-variables.js
Original file line number Diff line number Diff line change
Expand Up @@ -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++) {
Expand Down
6 changes: 6 additions & 0 deletions block-lexical-variables/src/msg.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.';
6 changes: 3 additions & 3 deletions block-lexical-variables/src/warningHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
}

Expand Down
47 changes: 47 additions & 0 deletions block-lexical-variables/test/field_lexical_var.mocha.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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('<xml>' +
' <block type="global_declaration">' +
' <field name="NAME">name</field>' +
' </block>' +
' <block type="global_declaration">' +
' <field name="NAME">name2</field>' +
' </block>' +
' <block type="initialize_global">' +
' <statement name="DO">' +
' <block type="global_declaration_entry">' +
' <field name="NAME">a</field>' +
' <next>' +
' <block type="global_declaration_entry">' +
' <field name="NAME">b</field>' +
' </block>' +
' </next>' +
' </block>' +
' </statement>' +
' </block>' +
'</xml>');
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('<xml>' +
' <block type="initialize_global">' +
' <statement name="DO">' +
' <block type="global_declaration_entry">' +
' <field name="NAME">a</field>' +
' </block>' +
' </statement>' +
' </block>' +
' <block id="b" type="global_declaration_entry">' +
' <field name="NAME">b</field>' +
' </block>' +
'</xml>');
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() {
Expand Down
2 changes: 2 additions & 0 deletions block-lexical-variables/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ document.addEventListener('DOMContentLoaded', function() {
</category>
<sep></sep>
<category id="catVariables" colour="330" name="Variables">
<block type="initialize_global"></block>
<block type="global_declaration_entry"></block>
<block type="global_declaration"></block>
<block type="simple_local_declaration_statement"></block>
<block type="local_declaration_statement"></block>
Expand Down