Sometimes your block-code generator needs to reference the code of its inner block multiple times.
For example, if you have a block that gets the last element of a list, you need to access the list code multiple times:
// Incorrect block-code generator.
javascriptGenerator.forBlock['last_element'] = function(block, generator) {
const listCode = generator.valueToCode(block, 'LIST', Order.MEMBER);
// listCode gets referenced twice.
const code = `${listCode}[${listCode}.length - 1]`;
return [code, Order.MEMBER];
}
But this can cause problems if the resulting value of the inner block's code is inconsistent, or it has side effects. For example, if the inner code is actually a function call, this particular code can end up with an out of range condition:
randomList()[randomList().length - 1]
Using utility functions lets you make sure that inner blocks' code is only evaluated once.
Utility functions
A utility function is a developer-defined function included as part of the generated code string. You can use them to make sure that inner-block code is only evaluated once, and then the value can be referenced multiple times.
import {javascriptGenerator, Order} from 'blockly/javascript';
// Correct block-code generator.
javascriptGenerator.forBlock['last_element'] = function(block, generator) {
const listCode = generator.valueToCode(block, 'LIST', Order.NONE);
const functionName = generator.provideFunction_(
'list_lastElement',
[
`function ${generator.FUNCTION_NAME_PLACEHOLDER_}(list) {`,
` return list[list.length - 1];`,
`}`
]
);
// listCode only gets evaluated once.
const code = `${functionName}(${listCode})`;
return [code, Order.FUNCTION_CALL];
}
Provide the function
You can define utility functions inside block-code generators using
provideFunction_
. It takes in the name you want for your utility function, and
an array of code strings defining what the function does. It returns the
resulting name of your utility function, after (possibly) modifying it to not
conflict with user-defined functions.
provideFunction_
also dedupes utility function definitions, so that each
utility function only exists once, even if the block type that defines it exists
multiple times.
Update precedences
When you define a utility function, you should also update the precedences (which define how parentheses are inserted) included in the block-code generator.
The precedence is always based on the code string returned by the block-code
generator. It does not care about operators inside utility functions. So in the
previous example the valueToCode
call was changed to Order.NONE
and the
return tuple was changed to Order.FUNCTION_CALL
.