fixRange = (range) => {
  const { endOffset, commonAncestorContainer: commonContainer } = range;
  let { startOffset, startContainer, endContainer } = range;

  if (!isTextNode(startContainer)) {
    startContainer = textNodeLookup(commonContainer, startContainer, startOffset, "forward");
    if (!startContainer) return null;
    range.setStart(startContainer, 0);
    startOffset = 0;
  }

  // if user started selection from the end of the tag, start could be this tag,
  // so we should move it to more relevant one
  const selectionFromTheEnd = startContainer.wholeText.length === startOffset;
  // we skip ephemeral whitespace-only text nodes, like \n between tags in original html
  const isBasicallyEmpty = (textNode) => /^\s*$/.test(textNode.wholeText);

  if (selectionFromTheEnd || isBasicallyEmpty(startContainer)) {
    do {
      startContainer = textNodeLookup(commonContainer, startContainer, startOffset, "forward-next");
      if (!startContainer) return null;
    } while (isBasicallyEmpty(startContainer));

    range.setStart(startContainer, 0);
    startOffset = 0;
  }

  if (!isTextNode(endContainer)) {
    endContainer = textNodeLookup(commonContainer, endContainer, endOffset, "backward");
    if (!endContainer) return null;

    while (/^\s*$/.test(endContainer.wholeText)) {
      endContainer = textNodeLookup(commonContainer, endContainer, endOffset, "backward-next");
      if (!endContainer) return null;
    }
    // we skip empty whitespace-only text nodes, so we need the found one to be included
    range.setEnd(endContainer, endContainer.length);
  }

  return range;
}
Fix range if it contains non-text nodes and shrink it down to the better fit.
The main goal here is to get the most relevant xpath+offset combination.
i.e. `start` should point to the element, containing first char, not parent,
not root, not some previous element with `startOffset` on the last char.
@param {Range} range