function generateNodeNames(node, options) {
        if (RED.workspaces.isLocked()) {
            return
        }
        options = Object.assign({
            renameBlank: true,
            renameClash: true,
            generateHistory: true
        }, options)
        let nodes = node;
        if (node) {
            if (!Array.isArray(node)) {
                nodes = [ node ]
            }
        } else {
            nodes = RED.view.selection().nodes;
        }
        if (nodes && nodes.length > 0) {
            // Generate history event if using the workspace selection,
            // or if the provided node already exists
            const generateHistory = options.generateHistory && (!node || !!RED.nodes.node(node.id))
            const historyEvents = []
            const typeIndex = {}
            let changed = false;
            nodes.forEach(n => {
                const nodeDef = n._def || RED.nodes.getType(n.type)
                if (nodeDef && nodeDef.defaults && nodeDef.defaults.name) {
                    const paletteLabel = RED.utils.getPaletteLabel(n.type, nodeDef)
                    const defaultNodeNameRE = new RegExp('^'+paletteLabel.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')+' (\\d+)$')
                    if (!typeIndex.hasOwnProperty(n.type)) {
                        const existingNodes = RED.nodes.filterNodes({ type: n.type });
                        const existingIds = existingNodes.reduce((ids, node) => {
                            let match = defaultNodeNameRE.exec(node.name);
                            if (match) {
                                const nodeNumber = parseInt(match[1], 10);
                                if (!ids.includes(nodeNumber)) {
                                    ids.push(nodeNumber);
                                }
                            }
                            return ids;
                        }, []).sort((a, b) => a - b);

                        let availableNameNumber = 1;
                        for (let i = 0; i < existingIds.length; i++) {
                            if (existingIds[i] !== availableNameNumber) {
                                break;
                            }
                            availableNameNumber++;
                        }

                        typeIndex[n.type] = availableNameNumber;
                    }
                    if ((options.renameBlank && n.name === '') || (options.renameClash && defaultNodeNameRE.test(n.name))) {
                        if (generateHistory) {
                            historyEvents.push({
                                t:'edit',
                                node: n,
                                changes: { name: n.name },
                                dirty: RED.nodes.dirty(),
                                changed: n.changed
                            })
                        }
                        n.name = paletteLabel+" "+typeIndex[n.type]
                        n.dirty = true
                        typeIndex[n.type]++
                        changed = true
                    }
                }
            })
            if (changed) {
                if (historyEvents.length > 0) {
                    RED.history.push({
                        t: 'multi',
                        events: historyEvents
                    })
                }
                RED.nodes.dirty(true)
                RED.view.redraw()
            }
        }
    }
Generate names for the select nodes.
 - it only sets the name if it is currently blank
 - it uses `<paletteLabel> <N>` - where N is the next available integer that
   doesn't clash with any existing nodes of that type
@param {Object} node The node to set the name of - if not provided, uses current selection
@param {{ renameBlank: boolean, renameClash: boolean, generateHistory: boolean }} options Possible options are `renameBlank`, `renameClash` and `generateHistory`