Fine-grained write actions in "Extract Method" refactoring for Python

This way it's safer to launch time-consuming dependent tasks
(e.g. searching for code duplicates) synchonously since under
WriteAction they might cause deadlock.
This commit is contained in:
Mikhail Golubev
2016-06-02 12:38:24 +03:00
parent 568b1161f9
commit 7d7788fe12
@@ -20,6 +20,7 @@ import com.intellij.codeInsight.codeFragment.CodeFragment;
import com.intellij.lang.LanguageNamesValidation;
import com.intellij.lang.refactoring.NamesValidator;
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.project.Project;
@@ -107,7 +108,7 @@ public class PyExtractMethodUtil {
final SimpleDuplicatesFinder finder = new SimpleDuplicatesFinder(statement1, statement2, fragment.getOutputVariables(), variableData);
CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> {
CommandProcessor.getInstance().executeCommand(project, () -> {
final RefactoringEventData beforeData = new RefactoringEventData();
beforeData.addElements(new PsiElement[]{statement1, statement2});
project.getMessageBus().syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC)
@@ -127,7 +128,7 @@ public class PyExtractMethodUtil {
if (hasOutputVariables) {
// Generate return modified variables statements
final String outputVariables = StringUtil.join(fragment.getOutputVariables(), ", ");
String newMethodText = builder + "return " + outputVariables;
final String newMethodText = builder + "return " + outputVariables;
builder.append(outputVariables);
final PyFunction function1 = generator.createFromText(languageLevel, PyFunction.class, newMethodText);
@@ -136,15 +137,17 @@ public class PyExtractMethodUtil {
}
// Generate method
PyFunction generatedMethod = generateMethodFromElements(project, methodName, variableData, newMethodElements, flags, isAsync);
generatedMethod = insertGeneratedMethod(statement1, generatedMethod);
final PyFunction generatedMethod = generateMethodFromElements(project, 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);
processParameters(project, generatedMethod, variableData, isMethod, isClassMethod, isStaticMethod);
processGlobalWrites(generatedMethod, fragment);
processNonlocalWrites(generatedMethod, fragment);
WriteAction.run(() -> {
processParameters(project, insertedMethod, variableData, isMethod, isClassMethod, isStaticMethod);
processGlobalWrites(insertedMethod, fragment);
processNonlocalWrites(insertedMethod, fragment);
});
// Generate call element
if (hasOutputVariables) {
@@ -165,29 +168,29 @@ public class PyExtractMethodUtil {
builder.append(methodName).append("(");
builder.append(createCallArgsString(variableData)).append(")");
final PyFunction function1 = generator.createFromText(languageLevel, PyFunction.class, builder.toString());
PsiElement callElement = function1.getStatementList().getStatements()[0];
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, generatedMethod);
final List<SimpleMatch> duplicates = collectDuplicates(finder, statement1, insertedMethod);
// replace statements with call
callElement = replaceElements(elementsRange, callElement);
callElement = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(callElement);
if (callElement != null) {
processDuplicates(duplicates, callElement, editor);
PsiElement insertedCallElement = WriteAction.compute(() -> replaceElements(elementsRange, callElement));
insertedCallElement = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(insertedCallElement);
if (insertedCallElement != null) {
processDuplicates(duplicates, insertedCallElement, editor);
}
// Set editor
setSelectionAndCaret(editor, callElement);
setSelectionAndCaret(editor, insertedCallElement);
final RefactoringEventData afterData = new RefactoringEventData();
afterData.addElement(generatedMethod);
afterData.addElement(insertedMethod);
project.getMessageBus().syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC)
.refactoringDone(getRefactoringId(), afterData);
}), PyBundle.message("refactoring.extract.method"), null);
}, PyBundle.message("refactoring.extract.method"), null);
}
@NotNull
@@ -307,15 +310,15 @@ public class PyExtractMethodUtil {
final SimpleDuplicatesFinder finder = new SimpleDuplicatesFinder(expression, expression, fragment.getOutputVariables(), variableData);
if (fragment.getOutputVariables().isEmpty()) {
CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> {
CommandProcessor.getInstance().executeCommand(project, () -> {
// Generate method
final boolean isAsync = fragment.isAsync();
PyFunction generatedMethod = generateMethodFromExpression(project, methodName, variableData, expression, flags, isAsync);
generatedMethod = insertGeneratedMethod(expression, generatedMethod);
final PyFunction generatedMethod = generateMethodFromExpression(project, methodName, variableData, expression, flags, isAsync);
final PyFunction insertedMethod = WriteAction.compute(() -> insertGeneratedMethod(expression, generatedMethod));
// Process parameters
final boolean isMethod = PyPsiUtils.isMethodContext(expression);
processParameters(project, generatedMethod, variableData, isMethod, isClassMethod, isStaticMethod);
WriteAction.run(() -> processParameters(project, insertedMethod, variableData, isMethod, isClassMethod, isStaticMethod));
// Generating call element
final StringBuilder builder = new StringBuilder();
@@ -341,26 +344,31 @@ public class PyExtractMethodUtil {
final PyFunction function1 = generator.createFromText(LanguageLevel.forElement(expression), PyFunction.class,
builder.toString());
final PyElement generated = function1.getStatementList().getStatements()[0];
PsiElement callElement = null;
final PsiElement callElement;
if (generated instanceof PyReturnStatement) {
callElement = ((PyReturnStatement)generated).getExpression();
}
else if (generated instanceof PyExpressionStatement) {
callElement = ((PyExpressionStatement)generated).getExpression();
}
else {
callElement = null;
}
PyPsiUtils.assertValid(expression);
final List<SimpleMatch> duplicates = collectDuplicates(finder, expression, generatedMethod);
final List<SimpleMatch> duplicates = collectDuplicates(finder, expression, insertedMethod);
// replace statements with call
PsiElement insertedCallElement = null;
if (callElement != null) {
callElement = PyReplaceExpressionUtil.replaceExpression(expression, callElement);
}
if (callElement != null) {
processDuplicates(duplicates, callElement, editor);
insertedCallElement = WriteAction.compute(() -> PyReplaceExpressionUtil.replaceExpression(expression, callElement));
if (insertedCallElement != null) {
processDuplicates(duplicates, insertedCallElement, editor);
}
}
setSelectionAndCaret(editor, insertedCallElement);
// Set editor
setSelectionAndCaret(editor, callElement);
}), PyBundle.message("refactoring.extract.method"), null);
}, PyBundle.message("refactoring.extract.method"), null);
}
}