public static List<SmartPsiFileRange> extractFromStatements(@NotNull final Project project,
                                           @NotNull final Editor editor,
                                           @NotNull final PyCodeFragment fragment,
                                           @NotNull final PsiElement statement1,
                                           @NotNull final PsiElement statement2,
                                           Boolean processDuplicates) {
    List<SmartPsiFileRange> pointers = new ArrayList<>();
    if (!fragment.getOutputVariables().isEmpty() && fragment.isReturnInstructionInside()) {
      CommonRefactoringUtil.showErrorHint(project, editor,
                                          PyPsiBundle.message("refactoring.extract.method.error.local.variable.modifications.and.returns"),
                                          RefactoringBundle.message("error.title"), "refactoring.extractMethod");
      return pointers;
    }

    final PyFunction function = PsiTreeUtil.getParentOfType(statement1, PyFunction.class);
    final PyUtil.MethodFlags flags = function == null ? null : PyUtil.MethodFlags.of(function);
    final boolean isClassMethod = flags != null && flags.isClassMethod();
    final boolean isStaticMethod = flags != null && flags.isStaticMethod();

    // collect statements
    final List<PsiElement> elementsRange = PsiTreeUtil.getElementsOfRange(statement1, statement2);
    if (elementsRange.isEmpty()) {
      CommonRefactoringUtil.showErrorHint(project, editor,
                                          PyPsiBundle.message("refactoring.extract.method.error.empty.fragment"),
                                          RefactoringBundle.message("extract.method.title"), "refactoring.extractMethod");
      return pointers;
    }

    final Pair<String, AbstractVariableData[]> data = getNameAndVariableData(project, fragment, statement1, isClassMethod, isStaticMethod);
    if (data.first == null || data.second == null) {
      return pointers;
    }

    PsiFile file = statement1.getContainingFile();

    final String methodName = data.first;
    final AbstractVariableData[] variableData = data.second;

    final SimpleDuplicatesFinder finder = new SimpleDuplicatesFinder(statement1, statement2, fragment.getOutputVariables(), variableData);

    CommandProcessor.getInstance().executeCommand(project, () ->  {
      final RefactoringEventData beforeData = new RefactoringEventData();
      beforeData.addElements(new PsiElement[]{statement1, statement2});
      project.getMessageBus().syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC)
        .refactoringStarted(getRefactoringId(), beforeData);

      final StringBuilder builder = new StringBuilder();
      final boolean isAsync = fragment.isAsync();
      if (isAsync) {
        builder.append("async ");
      }
      builder.append("def f():\n    ");
      final List<PsiElement> newMethodElements = new ArrayList<>(elementsRange);
      final boolean hasOutputVariables = !fragment.getOutputVariables().isEmpty();

      final PyElementGenerator generator = PyElementGenerator.getInstance(project);
      final LanguageLevel languageLevel = LanguageLevel.forElement(statement1);
      if (hasOutputVariables) {
        // Generate return modified variables statements
        final String outputVariables = StringUtil.join(fragment.getOutputVariables(), ", ");
        final String newMethodText = builder + "return " + outputVariables;
        builder.append(outputVariables);

        final PyFunction function1 = generator.createFromText(languageLevel, PyFunction.class, newMethodText);
        final PsiElement returnStatement = function1.getStatementList().getStatements()[0];
        newMethodElements.add(returnStatement);
      }

      // Generate method
      final PyFunction generatedMethod = generateMethodFromElements(methodName, variableData, newMethodElements, flags, isAsync);
      final PyFunction insertedMethod = WriteAction.compute(() -> insertGeneratedMethod(statement1, generatedMethod));

      // Process parameters
      final PsiElement firstElement = elementsRange.get(0);
      final boolean isMethod = PyPsiUtils.isMethodContext(firstElement);
      WriteAction.run(() -> {
        processParameters(project, insertedMethod, variableData, isMethod, isClassMethod, isStaticMethod);
        processGlobalWrites(insertedMethod, fragment);
        processNonlocalWrites(insertedMethod, fragment);
      });

      // Generate call element
      if (hasOutputVariables) {
        builder.append(" = ");
      }
      else if (fragment.isReturnInstructionInside()) {
        builder.append("return ");
      }
      if (isAsync) {
        builder.append("await ");
      }
      else if (fragment.isYieldInside()) {
        builder.append("yield from ");
      }
      if (isMethod) {
        appendSelf(firstElement, builder, isStaticMethod);
      }
      builder.append(methodName).append("(");
      builder.append(createCallArgsString(variableData)).append(")");
      final PyFunction function1 = generator.createFromText(languageLevel, PyFunction.class, builder.toString());
      final PsiElement callElement = function1.getStatementList().getStatements()[0];

      // Both statements are used in finder, so should be valid at this moment
      PyPsiUtils.assertValid(statement1);
      PyPsiUtils.assertValid(statement2);
      final List<SimpleMatch> duplicates = collectDuplicates(finder, statement1, insertedMethod);

      // replace statements with call
      PsiElement insertedCallElement = WriteAction.compute(() -> replaceElements(elementsRange, callElement));
      insertedCallElement = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(insertedCallElement);

      SmartPointerManager pointerManager = SmartPointerManager.getInstance(project);
      if (processDuplicates) {
        pointers.addAll(ContainerUtil.map(duplicates, p -> pointerManager.createSmartPsiFileRangePointer(file, p.getStartElement().getTextRange())));
      }

      if (insertedCallElement != null) {
        pointers.add(0, pointerManager.createSmartPsiFileRangePointer(file, insertedMethod.getNameIdentifier().getTextRange()));
        pointers.add(pointerManager.createSmartPsiFileRangePointer(file, insertedCallElement.getTextRange()));
        if (processDuplicates) {
          processDuplicates(duplicates, insertedCallElement, editor);
        }
      }

      // Set editor
      setSelectionAndCaret(editor, insertedCallElement);

      final RefactoringEventData afterData = new RefactoringEventData();
      afterData.addElement(insertedMethod);
      project.getMessageBus().syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC)
        .refactoringDone(getRefactoringId(), afterData);
    }, PyPsiBundle.message("refactoring.extract.method"), null);
    return pointers;
  }
public static List<SmartPsiFileRange> extractFromStatements(@NotNull final Project project,
                                           @NotNull final Editor editor,
                                           @NotNull final PyCodeFragment fragment,
                                           @NotNull final PsiElement statement1,
                                           @NotNull final PsiElement statement2,
                                           Boolean processDuplicates)