function appendUpdate(edits, index, elemId, opId, value, firstUpdate) {
  let insert = false
  if (firstUpdate) {
    // Pop all edits for the same index off the end of the edits array. This sequence may begin with
    // either an insert or an update. If it's an insert, we remember that fact, and use it below.
    while (!insert && edits.length > 0) {
      const lastEdit = edits[edits.length - 1]
      if ((lastEdit.action === 'insert' || lastEdit.action === 'update') && lastEdit.index === index) {
        edits.pop()
        insert = (lastEdit.action === 'insert')
      } else if (lastEdit.action === 'multi-insert' && lastEdit.index + lastEdit.values.length - 1 === index) {
        lastEdit.values.pop()
        insert = true
      } else {
        break
      }
    }
  }

  // If we popped an insert edit off the edits array, we need to turn the new update into an insert
  // in order to ensure the list element still gets inserted (just with a new value).
  if (insert) {
    appendEdit(edits, {action: 'insert', index, elemId, opId, value})
  } else {
    appendEdit(edits, {action: 'update', index, opId, value})
  }
}
`edits` is an array of (SingleInsertEdit | MultiInsertEdit | UpdateEdit | RemoveEdit) list edits
for a patch. This function appends an UpdateEdit to this array. A conflict is represented by
having several consecutive edits with the same index, and this can be realised by calling
`appendUpdate` several times for the same list element. On the first such call, `firstUpdate`
must be true.

It is possible that coincidentally the previous edit (potentially arising from a different
change) is for the same index. If this is the case, to avoid accidentally treating consecutive
updates for the same index as a conflict, we remove the previous edit for the same index. This is
safe because the previous edit is overwritten by the new edit being appended, and we know that
it's for the same list elements because there are no intervening insertions/deletions that could
have changed the indexes.