From 7d7788fe12f3e6ecbd372d2b36887d09d0c5fa1c Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Wed, 1 Jun 2016 17:27:02 +0300 Subject: [PATCH] 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. --- .../extractmethod/PyExtractMethodUtil.java | 66 +++++++++++-------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java index 0adf0708bac3..ab38386321bf 100644 --- a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java +++ b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java @@ -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 duplicates = collectDuplicates(finder, statement1, generatedMethod); + final List 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 duplicates = collectDuplicates(finder, expression, generatedMethod); + final List 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); } }