@Override
  public void doInvoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
    InfoHolder info = InfoHolder.collect(getElementFromEditor(editor, file));
    try {
      String qualifier; // we don't always qualify with module name
      sure(info.myModuleReference); sure(info.myModuleName);
      String[] relative_names = null; // [0] is remaining import path, [1] is imported module name
      if (info.myRelativeLevel > 0) {
        relative_names = getRelativeNames(true, info);
        if (relative_names == null) throw new IncorrectOperationException("failed to get relative names");
        qualifier = relative_names[1];
      }
      else qualifier = info.myModuleName;
      // find all unqualified references that lead to one of our import elements
      final PyImportElement[] ielts = info.myFromImportStatement.getImportElements();
      final PyStarImportElement star_ielt = info.myFromImportStatement.getStarImportElement();
      final Map<PsiReference, PyImportElement> references = new HashMap<>();
      final List<PsiReference> star_references = new ArrayList<>();
      PsiTreeUtil.processElements(file, new PsiElementProcessor() {
        @Override
        public boolean execute(@NotNull PsiElement element) {
          PyPsiUtils.assertValid(element);
          if (element instanceof PyReferenceExpression ref && PsiTreeUtil.getParentOfType(element, PyImportElement.class) == null) {
            if (!ref.isQualified()) {
              ResolveResult[] resolved = ref.getReference().multiResolve(false);
              for (ResolveResult rr : resolved) {
                if (rr.isValidResult()) {
                  if (rr.getElement() == star_ielt) star_references.add(ref.getReference());
                  for (PyImportElement ielt : ielts) {
                    if (rr.getElement() == ielt) references.put(ref.getReference(), ielt);
                  }
                }
              }
            }
          }
          return true;
        }
      });

      // check that at every replacement site our topmost qualifier name is visible
      PyQualifiedExpression top_qualifier;
      PyExpression feeler = info.myModuleReference;
      do {
        sure(feeler instanceof PyQualifiedExpression); // if for some crazy reason module name refers to numbers, etc, no point to continue.
        top_qualifier = (PyQualifiedExpression)feeler;
        feeler = top_qualifier.getQualifier();
      } while (feeler != null);
      String top_name = top_qualifier.getName();
      Collection<PsiReference> possible_targets = references.keySet();
      if (star_references.size() > 0) {
        possible_targets = new ArrayList<>(references.keySet().size() + star_references.size());
        possible_targets.addAll(references.keySet());
        possible_targets.addAll(star_references);
      }
      final Set<PsiElement> ignored = Sets.newHashSet(Arrays.asList(info.myFromImportStatement.getImportElements()));
      if (top_name != null && PythonUiService.getInstance()
        .showConflicts(project, DeclarationConflictChecker.findDefinitions(top_name, possible_targets, ignored),
                       top_name, info.myModuleName)) {
        return; // got conflicts
      }

      // add qualifiers
      PyElementGenerator generator = PyElementGenerator.getInstance(project);
      final LanguageLevel level = LanguageLevel.forElement(file);
      for (Map.Entry<PsiReference, PyImportElement> entry : references.entrySet()) {
        PsiElement referring_elt = entry.getKey().getElement();
        assert referring_elt.isValid(); // else we won't add it
        ASTNode target_node = referring_elt.getNode();
        assert target_node != null; // else it won't be valid
        PyImportElement ielt = entry.getValue();
        if (ielt.getAsNameElement() != null) {
          // we have an alias, replace it with real name
          PyReferenceExpression refex = ielt.getImportReferenceExpression();
          assert refex != null; // else we won't resolve to this ielt
          String real_name = refex.getReferencedName();
          ASTNode new_qualifier = generator.createExpressionFromText(level, real_name).getNode();
          assert new_qualifier != null;
          //ASTNode first_under_target = target_node.getFirstChildNode();
          //if (first_under_target != null) new_qualifier.addChildren(first_under_target, null, null); // save the children if any
          target_node.getTreeParent().replaceChild(target_node, new_qualifier);
          target_node = new_qualifier;
        }
        qualifyTarget(target_node, project, qualifier);
      }
      for (PsiReference reference : star_references) {
        PsiElement referring_elt = reference.getElement();
        assert referring_elt.isValid(); // else we won't add it
        ASTNode target_node = referring_elt.getNode();
        assert target_node != null; // else it won't be valid
        qualifyTarget(target_node, project, qualifier);
      }
      // transform the import statement
      PyStatement new_import;
      if (info.myRelativeLevel == 0) {
        new_import = sure(generator.createFromText(level, PyImportStatement.class, "import " + info.myModuleName));
      }
      else {
        new_import = sure(generator.createFromText(level, PyFromImportStatement.class, "from " + relative_names[0] + " import " + relative_names[1]));
      }
      ASTNode parent = sure(info.myFromImportStatement.getParent().getNode());
      ASTNode old_node = sure(info.myFromImportStatement.getNode());
      parent.replaceChild(old_node, sure(new_import.getNode()));
      //myFromImportStatement.replace(new_import);
    }
    catch (IncorrectOperationException ignored) {
      PythonUiService.getInstance().showBalloonError(project, PyPsiBundle.message("QFIX.action.failed"));
    }
  }
@Override
  public void doInvoke(@NotNull Project project, Editor editor, PsiFile file)