Package com.jetbrains.lang.dart.ide.refactoring.extract

Source Code of com.jetbrains.lang.dart.ide.refactoring.extract.DartExtractMethodHandler

package com.jetbrains.lang.dart.ide.refactoring.extract;

import com.intellij.lang.ASTNode;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiParserFacade;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.RefactoringActionHandler;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.jetbrains.lang.dart.DartBundle;
import com.jetbrains.lang.dart.DartTokenTypes;
import com.jetbrains.lang.dart.psi.*;
import com.jetbrains.lang.dart.resolve.DartResolver;
import com.jetbrains.lang.dart.util.DartControlFlow;
import com.jetbrains.lang.dart.util.DartElementGenerator;
import com.jetbrains.lang.dart.util.DartRefactoringUtil;
import com.jetbrains.lang.dart.util.DartResolveUtil;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.Set;

public class DartExtractMethodHandler implements RefactoringActionHandler {
  @Override
  public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) {
  }

  @Override
  public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) {
    final SelectionModel selectionModel = editor.getSelectionModel();
    if (!selectionModel.hasSelection()) selectionModel.selectLineAtCaret();

    final PsiElement[] elements =
      DartRefactoringUtil.findStatementsInRange(file, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd());

    if (elements.length == 0 || (elements.length == 1 && elements[0] instanceof DartExpression)) {
      // todo
      CommonRefactoringUtil.showErrorHint(
        project,
        editor,
        RefactoringBundle.getCannotRefactorMessage(DartBundle.message("dart.refactoring.extract.method.from.expression.error")),
        DartBundle.message("dart.refactoring.extract.method.error"),
        null
      );
      return;
    }

    final DartControlFlow controlFlow = DartControlFlow.analyze(elements);

    selectionModel.isBlockSelectionGuarded();

    if (controlFlow.getReturnValues().size() > 1) {
      CommonRefactoringUtil.showErrorHint(
        project,
        editor,
        RefactoringBundle.getCannotRefactorMessage(DartBundle.message("dart.refactoring.multiple.output.values")),
        DartBundle.message("dart.refactoring.extract.method.error"),
        null
      );
      return;
    }

    final Scope scope = findScope(elements);

    controlFlow.filterParams(new Condition<DartComponentName>() {
      @Override
      public boolean value(DartComponentName name) {
        return !scope.containsDeclaration(name);
      }
    });

    doRefactoringInWriteAction(project, editor, elements, controlFlow, scope);
  }

  private static void doRefactoringInWriteAction(Project project,
                                                 final Editor editor,
                                                 final PsiElement[] elements,
                                                 final DartControlFlow controlFlow,
                                                 final Scope scope) {
    CommandProcessor.getInstance().executeCommand(project, new Runnable() {
      public void run() {
        AccessToken l = WriteAction.start();
        try {
          doRefactoring(editor, elements, controlFlow, scope);
        }
        finally {
          l.finish();
        }
      }
    }, DartBundle.message("dart.extract.method"), null);
  }

  private static void doRefactoring(Editor editor, PsiElement[] elements, DartControlFlow controlFlow, Scope scope) {
    final Project project = elements[0].getProject();
    final PsiFile file = elements[0].getContainingFile();
    final PsiElement anchorToAdd = scope.findAnchor(elements);
    final StringBuilder functionBody = new StringBuilder();

    final Set<String> usedNames = DartRefactoringUtil.collectUsedNames(anchorToAdd);
    String functionName = "extracted";
    while (usedNames.contains(functionName)) {
      functionName += "0";
    }

    if (!ApplicationManager.getApplication().isUnitTestMode()) {
      DartExtractDialog extractDialog = new DartExtractDialog(project, functionName, controlFlow);
      extractDialog.show();
      if (!extractDialog.isOK()) {
        return;
      }
      functionName = extractDialog.getFunctionName();
    }

    functionBody.append(controlFlow.getSignature(functionName));
    functionBody.append("{\n");

    final int startOffset = elements[0].getTextOffset();
    final int endOffset = elements[elements.length - 1].getTextRange().getEndOffset();
    functionBody.append(new TextRange(startOffset, endOffset).substring(elements[0].getContainingFile().getText()));

    if (!controlFlow.getReturnValues().isEmpty()) {
      functionBody.append("\nreturn ");
      final DartComponentName componentName = controlFlow.getReturnValues().iterator().next();
      functionBody.append(componentName.getName());
      functionBody.append(";");
    }
    functionBody.append("\n}");
    final String replaceStatementText = controlFlow.getReplaceStatementText(functionName);
    PsiElement replaceStatement = DartElementGenerator.createStatementFromText(project, replaceStatementText);
    final List<DartComponent> dartComponents = DartElementGenerator.createFunctionsFromText(project, functionBody.toString());
    if (replaceStatement == null || dartComponents.isEmpty()) {
      return;
    }
    PsiElement function = dartComponents.iterator().next();
    function = anchorToAdd.getParent().addBefore(function, anchorToAdd);

    final PsiElement newLineNode =
      PsiParserFacade.SERVICE.getInstance(function.getProject()).createWhiteSpaceFromText("\n");

    replaceStatement = elements[0].getParent().addBefore(replaceStatement, elements[0]);
    replaceStatement.getParent().addBefore(newLineNode, replaceStatement);

    function.getParent().addAfter(newLineNode, function);

    final ASTNode nextChild = replaceStatement.getNode().getTreeNext();
    replaceStatement.getParent().getNode().addLeaf(DartTokenTypes.SEMICOLON, ";", nextChild);

    elements[0].getParent().deleteChildRange(elements[0], elements[elements.length - 1]);

    PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument());
    CodeStyleManager.getInstance(project).reformatText(
      file,
      function.getTextRange().getStartOffset(),
      function.getTextRange().getEndOffset()
    );

    editor.getCaretModel().moveToOffset(replaceStatement.getTextOffset());
  }

  private static Scope findScope(PsiElement[] elements) {
    final DartClass dartClass = PsiTreeUtil.getParentOfType(elements[0], DartClass.class);
    return new Scope(dartClass == null ? PsiTreeUtil.getTopmostParentOfType(elements[0], DartExecutionScope.class) : dartClass);
  }

  private static class Scope {

    private final PsiElement myElement;

    public Scope(PsiElement element) {
      myElement = element;
    }

    public boolean containsDeclaration(final DartComponentName declarationName) {
      return DartResolver.resolveSimpleReference(getScopeBody(), declarationName.getText()).contains(declarationName);
    }

    private PsiElement getScopeBody() {
      PsiElement result = myElement instanceof DartClass ? DartResolveUtil.getBody((DartClass)myElement) : myElement;
      assert result != null;
      return result;
    }

    public PsiElement findAnchor(PsiElement[] elements) {
      PsiElement scopeBody = getScopeBody();
      PsiElement result = elements[0];
      while (result.getParent() != null && result.getParent() != scopeBody) {
        result = result.getParent();
      }
      return result;
    }
  }
}
TOP

Related Classes of com.jetbrains.lang.dart.ide.refactoring.extract.DartExtractMethodHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.