src/rules/insertFragmentAtRange/append-table-at-range-start.js
function appendTableAtRangeStart(opts: Options): typeRule {
    return (rootInsert, change, range, fragment, insertOptions, next) => {
        if (fragment.nodes.size === 0) {
            return next(insertOptions);
        }
        const fragmentTable = fragment.nodes.first();

        if (fragmentTable.type !== opts.typeTable) {
            return next(insertOptions);
        }

        const startPosition = EditTablePosition.create({
            node: change.value.document,
            range: range.collapseToStart(),
            opts
        });
        const endPosition = EditTablePosition.create({
            node: change.value.document,
            range: range.collapseToEnd(),
            opts
        });

        if (
            !startPosition.isInTable() ||
            startPosition.isSameTable(endPosition) ||
            !startPosition.isLastRow()
        ) {
            return next(insertOptions);
        }

        const { table, cellIndex, row } = startPosition;

        if (!isSameWidth(table, fragmentTable)) {
            return next(insertOptions);
        }

        const { document } = change.value;
        const { firstNodeAsText } = insertOptions;
        let nextTable = table;

        if (firstNodeAsText) {
            const fragmentRow = fragmentTable.nodes.first();
            const nextRow = pasteSingleRow(row, fragmentRow, cellIndex, -1);
            nextTable = table.updateNode(nextRow);
        }

        const afterRows = firstNodeAsText
            ? fragmentTable.nodes.shift()
            : fragmentTable.nodes;

        nextTable = nextTable.set('nodes', nextTable.nodes.concat(afterRows));
        change.replaceNodeByKey(nextTable.key, nextTable, { normalize: false });

        fragment = fragment.deleteIn(['nodes', 0]);
        const nextBlock = document.getNextBlock(table.key);
        if (!nextBlock) {
            range = range.collapseToEndOf(fragmentTable);
            insertOptions = insertOptions.set('lastNodeAsText', false);
        } else {
            range = range.moveAnchorToStartOf(nextBlock);
            if (startPosition.isSameTable(endPosition)) {
                range = range.moveToAnchor();
            }
        }
        return rootInsert(
            change,
            range,
            fragment,
            insertOptions.set('firstNodeAsText', false)
        );
    };
}
function appendTableAtRangeStart(opts: Options): typeRule {
    return (rootInsert, change, range, fragment, insertOptions, next) => {
        if (fragment.nodes.size === 0) {
            return next(insertOptions);
        }
        const fragmentTable = fragment.nodes.first();

        if (fragmentTable.type !== opts.typeTable) {
            return next(insertOptions);
        }

        const startPosition = EditTablePosition.create({
            node: change.value.document,
            range: range.collapseToStart(),
            opts
        });
        const endPosition = EditTablePosition.create({
            node: change.value.document,
            range: range.collapseToEnd(),
            opts
        });

        if (
            !startPosition.isInTable() ||
            startPosition.isSameTable(endPosition) ||
            !startPosition.isLastRow()
        ) {
            return next(insertOptions);
        }

        const { table, cellIndex, row } = startPosition;

        if (!isSameWidth(table, fragmentTable)) {
            return next(insertOptions);
        }

        const { document } = change.value;
        const { firstNodeAsText } = insertOptions;
        let nextTable = table;

        if (firstNodeAsText) {
            const fragmentRow = fragmentTable.nodes.first();
            const nextRow = pasteSingleRow(row, fragmentRow, cellIndex, -1);
            nextTable = table.updateNode(nextRow);
        }

        const afterRows = firstNodeAsText
            ? fragmentTable.nodes.shift()
            : fragmentTable.nodes;

        nextTable = nextTable.set('nodes', nextTable.nodes.concat(afterRows));
        change.replaceNodeByKey(nextTable.key, nextTable, { normalize: false });

        fragment = fragment.deleteIn(['nodes', 0]);
        const nextBlock = document.getNextBlock(table.key);
        if (!nextBlock) {
            range = range.collapseToEndOf(fragmentTable);
            insertOptions = insertOptions.set('lastNodeAsText', false);
        } else {
            range = range.moveAnchorToStartOf(nextBlock);
            if (startPosition.isSameTable(endPosition)) {
                range = range.moveToAnchor();
            }
        }
        return rootInsert(
            change,
            range,
            fragment,
            insertOptions.set('firstNodeAsText', false)
        );
    };
}
Append at the table end when: 1. range.start is in the end row of a table 2. firstNodeAsText: true 3. fragment.nodes.first is a table with same width