function buildBlockFromDef()

in pxtblocks/blocklyloader.ts [665:854]


        function buildBlockFromDef(def: pxtc.ParsedBlockDef, expanded = false) {
            let anonIndex = 0;
            let firstParam = !expanded && !!comp.thisParameter;

            const inputs = splitInputs(def);
            const imgConv = new ImageConverter()

            if (fn.attributes.shim === "ENUM_GET" || fn.attributes.shim === "KIND_GET") {
                if (comp.parameters.length > 1 || comp.thisParameter) {
                    console.warn(`Enum blocks may only have 1 parameter but ${fn.attributes.blockId} has ${comp.parameters.length}`);
                    return;
                }
            }

            inputs.forEach(inputParts => {
                const fields: NamedField[] = [];
                let inputName: string;
                let inputCheck: string | string[];
                let hasParameter = false;

                inputParts.forEach(part => {
                    if (part.kind !== "param") {
                        const f = newLabel(part);
                        if (f) {
                            fields.push({ field: f });
                        }
                    }
                    else if (fn.attributes.shim === "ENUM_GET") {
                        U.assert(!!fn.attributes.enumName, "Trying to create an ENUM_GET block without a valid enum name")
                        fields.push({
                            name: "MEMBER",
                            field: new pxtblockly.FieldUserEnum(info.enumsByName[fn.attributes.enumName])
                        });
                        return;
                    }
                    else if (fn.attributes.shim === "KIND_GET") {
                        fields.push({
                            name: "MEMBER",
                            field: new pxtblockly.FieldKind(info.kindsByName[fn.attributes.kindNamespace || fn.attributes.blockNamespace || fn.namespace])
                        });
                        return;
                    }
                    else {
                        // find argument
                        let pr = getParameterFromDef(part, comp, firstParam);

                        firstParam = false;
                        if (!pr) {
                            console.error("block " + fn.attributes.blockId + ": unknown parameter " + part.name + (part.ref ? ` (${part.ref})` : ""));
                            return;
                        }

                        if (isHandlerArg(pr)) {
                            inputName = "HANDLER_DRAG_PARAM_" + pr.name;
                            inputCheck = fn.attributes.draggableParameters === "reporter" ? getBlocklyCheckForType(pr.type, info) : "Variable";
                            return;
                        }

                        let typeInfo = U.lookup(info.apis.byQName, pr.type)

                        hasParameter = true;
                        const defName = pr.definitionName;
                        const actName = pr.actualName;

                        let isEnum = typeInfo && typeInfo.kind == pxtc.SymbolKind.Enum
                        let isFixed = typeInfo && !!typeInfo.attributes.fixedInstances && !pr.shadowBlockId;
                        let isConstantShim = !!fn.attributes.constantShim;
                        let isCombined = pr.type == "@combined@"
                        let customField = pr.fieldEditor;
                        let fieldLabel = defName.charAt(0).toUpperCase() + defName.slice(1);
                        let fieldType = pr.type;

                        if (isEnum || isFixed || isConstantShim || isCombined) {
                            let syms: pxtc.SymbolInfo[];

                            if (isEnum) {
                                syms = getEnumDropdownValues(info.apis, pr.type);
                            }
                            else if (isFixed) {
                                syms = getFixedInstanceDropdownValues(info.apis, typeInfo.qName);
                            }
                            else if (isCombined) {
                                syms = fn.combinedProperties.map(p => U.lookup(info.apis.byQName, p))
                            }
                            else {
                                syms = getConstantDropdownValues(info.apis, fn.qName);
                            }

                            if (syms.length == 0) {
                                console.error(`no instances of ${typeInfo.qName} found`)
                            }
                            const dd = syms.map(v => {
                                let k = v.attributes.block || v.attributes.blockId || v.name;
                                let comb = v.attributes.blockCombine
                                if (v.attributes.jresURL && !v.attributes.iconURL && U.startsWith(v.attributes.jresURL, "data:image/x-mkcd-f")) {
                                    v.attributes.iconURL = imgConv.convert(v.attributes.jresURL)
                                }
                                if (!!comb)
                                    k = k.replace(/@set/, "")
                                return [
                                    v.attributes.iconURL || v.attributes.blockImage ? {
                                        src: v.attributes.iconURL || Util.pathJoin(pxt.webConfig.commitCdnUrl, `blocks/${v.namespace.toLowerCase()}/${v.name.toLowerCase()}.png`),
                                        alt: k,
                                        width: 36,
                                        height: 36,
                                        value: v.name
                                    } : k,
                                    v.namespace + "." + v.name
                                ];
                            });
                            // if a value is provided, move it first
                            if (pr.defaultValue) {
                                let shadowValueIndex = -1;
                                dd.some((v, i) => {
                                    if (v[1] === (pr as BlockParameter).defaultValue) {
                                        shadowValueIndex = i;
                                        return true;
                                    }
                                    return false;
                                });
                                if (shadowValueIndex > -1) {
                                    const shadowValue = dd.splice(shadowValueIndex, 1)[0];
                                    dd.unshift(shadowValue);
                                }
                            }

                            if (customField) {
                                let defl = fn.attributes.paramDefl[actName] || "";
                                const options = {
                                    data: dd,
                                    colour: color,
                                    label: fieldLabel,
                                    type: fieldType,
                                    blocksInfo: info
                                } as Blockly.FieldCustomDropdownOptions;
                                Util.jsonMergeFrom(options, fn.attributes.paramFieldEditorOptions && fn.attributes.paramFieldEditorOptions[actName] || {});
                                fields.push(namedField(createFieldEditor(customField, defl, options), defName));
                            }
                            else
                                fields.push(namedField(new Blockly.FieldDropdown(dd), defName));

                        } else if (customField) {
                            const defl = fn.attributes.paramDefl[pr.actualName] || "";
                            const options = {
                                colour: color,
                                label: fieldLabel,
                                type: fieldType,
                                blocksInfo: info
                            } as Blockly.FieldCustomOptions;
                            Util.jsonMergeFrom(options, fn.attributes.paramFieldEditorOptions && fn.attributes.paramFieldEditorOptions[pr.actualName] || {});
                            fields.push(namedField(createFieldEditor(customField, defl, options), pr.definitionName));
                        } else {
                            inputName = defName;
                            if (instance && part.name === "this") {
                                inputCheck = pr.type;
                            } else if (pr.type == "number" && pr.shadowBlockId && pr.shadowBlockId == "value") {
                                inputName = undefined;
                                fields.push(namedField(new Blockly.FieldNumber("0"), defName));
                            } else if (pr.type == "string" && pr.shadowOptions && pr.shadowOptions.toString) {
                                inputCheck = null;
                            } else {
                                inputCheck = getBlocklyCheckForType(pr.type, info);
                            }
                        }
                    }
                });

                let input: Blockly.Input;

                if (inputName) {
                    input = block.appendValueInput(inputName);
                    input.setAlign(Blockly.ALIGN_LEFT);
                }
                else if (expanded) {
                    const prefix = hasParameter ? optionalInputWithFieldPrefix : optionalDummyInputPrefix;
                    input = block.appendDummyInput(prefix + (anonIndex++));
                }
                else {
                    input = block.appendDummyInput();
                }

                if (inputCheck) {
                    input.setCheck(inputCheck);
                }

                fields.forEach(f => input.appendField(f.field, f.name));
            });

            imgConv.logTime()
        }