public static void replaceWhiteSpace(
      final String whiteSpace,
      final ASTNode leafElement,
      final IElementType whiteSpaceToken,
      @Nullable final TextRange textRange) {
    final CharTable charTable = SharedImplUtil.findCharTableByTree(leafElement);

    ASTNode treePrev = findPreviousWhiteSpace(leafElement, whiteSpaceToken);
    if (treePrev == null) {
      treePrev = getWsCandidate(leafElement);
    }

    if (treePrev != null
        && treePrev.getText().trim().isEmpty()
        && treePrev.getElementType() != whiteSpaceToken
        && treePrev.getTextLength() > 0
        && !whiteSpace.isEmpty()) {
      LeafElement whiteSpaceElement =
          Factory.createSingleLeafElement(
              treePrev.getElementType(),
              whiteSpace,
              charTable,
              SharedImplUtil.getManagerByTree(leafElement));

      ASTNode treeParent = treePrev.getTreeParent();
      treeParent.replaceChild(treePrev, whiteSpaceElement);
    } else {
      LeafElement whiteSpaceElement =
          Factory.createSingleLeafElement(
              whiteSpaceToken, whiteSpace, charTable, SharedImplUtil.getManagerByTree(leafElement));

      if (treePrev == null) {
        if (!whiteSpace.isEmpty()) {
          addWhiteSpace(leafElement, whiteSpaceElement);
        }
      } else {
        if (!(treePrev.getElementType() == whiteSpaceToken)) {
          if (!whiteSpace.isEmpty()) {
            addWhiteSpace(treePrev, whiteSpaceElement);
          }
        } else {
          if (treePrev.getElementType() == whiteSpaceToken) {
            final CompositeElement treeParent = (CompositeElement) treePrev.getTreeParent();
            if (!whiteSpace.isEmpty()) {
              //          LOG.assertTrue(textRange == null ||
              // treeParent.getTextRange().equals(textRange));
              treeParent.replaceChild(treePrev, whiteSpaceElement);
            } else {
              treeParent.removeChild(treePrev);
            }

            // There is a possible case that more than one PSI element is matched by the target text
            // range.
            // That is the case, for example, for Python's multi-line expression. It may looks like
            // below:
            //     import contextlib,\
            //       math, decimal
            // Here single range contains two blocks: '\' & '\n  '. So, we may want to replace that
            // range to another text, hence,
            // we replace last element located there with it ('\n  ') and want to remove any
            // remaining elements ('\').
            ASTNode removeCandidate = findPreviousWhiteSpace(whiteSpaceElement, whiteSpaceToken);
            while (textRange != null
                && removeCandidate != null
                && removeCandidate.getStartOffset() >= textRange.getStartOffset()) {
              treePrev = findPreviousWhiteSpace(removeCandidate, whiteSpaceToken);
              removeCandidate.getTreeParent().removeChild(removeCandidate);
              removeCandidate = treePrev;
            }
            // treeParent.subtreeChanged();
          }
        }
      }
    }
  }