formatText = (transaction, parent, currPos, length, attributes) => {
  const doc = transaction.doc;
  const ownClientId = doc.clientID;
  minimizeAttributeChanges(currPos, attributes);
  const negatedAttributes = insertAttributes(transaction, parent, currPos, attributes); // iterate until first non-format or null is found
  // delete all formats with attributes[format.key] != null

  while (length > 0 && currPos.right !== null) {
    if (!currPos.right.deleted) {
      switch (currPos.right.content.constructor) {
        case ContentFormat:
          {
            const {
              key,
              value
            } =
            /** @type {ContentFormat} */
            currPos.right.content;
            const attr = attributes[key];

            if (attr !== undefined) {
              if (equalAttrs(attr, value)) {
                negatedAttributes.delete(key);
              } else {
                negatedAttributes.set(key, value);
              }

              currPos.right.delete(transaction);
            }

            break;
          }

        default:
          if (length < currPos.right.length) {
            getItemCleanStart(transaction, createID(currPos.right.id.client, currPos.right.id.clock + length));
          }

          length -= currPos.right.length;
          break;
      }
    }

    currPos.forward();
  } // Quill just assumes that the editor starts with a newline and that it always
  // ends with a newline. We only insert that newline when a new newline is
  // inserted - i.e when length is bigger than type.length


  if (length > 0) {
    let newlines = '';

    for (; length > 0; length--) {
      newlines += '\n';
    }

    currPos.right = new Item(createID(ownClientId, getState(doc.store, ownClientId)), currPos.left, currPos.left && currPos.left.lastId, currPos.right, currPos.right && currPos.right.id, parent, null, new ContentString(newlines));
    currPos.right.integrate(transaction, 0);
    currPos.forward();
  }

  insertNegatedAttributes(transaction, parent, currPos, negatedAttributes);
}
@param {Transaction} transaction
@param {AbstractType<any>} parent
@param {ItemTextListPosition} currPos
@param {number} length
@param {Object<string,any>} attributes

@private
@function