diff --git a/python/src/com/jetbrains/python/refactoring/extractmethod/PyDuplicatesFinder.java b/python/src/com/jetbrains/python/refactoring/extractmethod/PyDuplicatesFinder.java deleted file mode 100644 index 07d0ec7634b3..000000000000 --- a/python/src/com/jetbrains/python/refactoring/extractmethod/PyDuplicatesFinder.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.jetbrains.python.refactoring.extractmethod; - -import com.intellij.codeInsight.PsiEquivalenceUtil; -import com.intellij.openapi.util.Pair; -import com.intellij.psi.PsiComment; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiWhiteSpace; -import com.intellij.psi.util.PsiTreeUtil; -import com.jetbrains.python.psi.PyFunction; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; - -/** - * User : ktisha - */ -public class PyDuplicatesFinder { - private final ArrayList myPattern; - - public PyDuplicatesFinder(@NotNull final PsiElement statement1, @NotNull final PsiElement statement2) { - myPattern = new ArrayList(); - PsiElement sibling = statement1; - - do { - myPattern.add(sibling); - if (sibling == statement2) break; - sibling = PsiTreeUtil.skipSiblingsForward(sibling, PsiWhiteSpace.class, PsiComment.class); - } while (sibling != null); - } - - public List> findDuplicates(@Nullable final List scope, @NotNull final PyFunction generatedMethod) { - final ArrayList> result = new ArrayList>(); - if (scope != null) { - for (PsiElement element : scope) { - findPatternOccurrences(result, element, generatedMethod); - } - } - return result; - } - - private void findPatternOccurrences(@NotNull final List> array, @NotNull final PsiElement scope, - @NotNull final PyFunction generatedMethod) { - if (scope == generatedMethod) return; - final PsiElement[] children = scope.getChildren(); - for (PsiElement child : children) { - final Pair match = isDuplicateFragment(child); - if (match != null) { - array.add(match); - continue; - } - findPatternOccurrences(array, child, generatedMethod); - } - } - - @Nullable - private Pair isDuplicateFragment(@NotNull final PsiElement candidate) { - for (PsiElement pattern : myPattern) { - if (PsiTreeUtil.isAncestor(pattern, candidate, false)) return null; - } - PsiElement sibling = candidate; - final ArrayList candidates = new ArrayList(); - for (int i = 0; i != myPattern.size(); ++i) { - if (sibling == null) return null; - - candidates.add(sibling); - sibling = PsiTreeUtil.skipSiblingsForward(sibling, PsiWhiteSpace.class, PsiComment.class); - } - if (myPattern.size() != candidates.size()) return null; - if (candidates.size() <= 0) return null; - final Pair match = new Pair(candidates.get(0), candidates.get(candidates.size() - 1)); - for (int i = 0; i < myPattern.size(); i++) { - if (!matchPattern(myPattern.get(i), candidates.get(i))) return null; - } - return match; - } - - private static boolean matchPattern(@Nullable final PsiElement pattern, - @Nullable final PsiElement candidate) { - if (pattern == null || candidate == null) return pattern == candidate; - final PsiElement[] children1 = PsiEquivalenceUtil.getFilteredChildren(pattern, null, true); - final PsiElement[] children2 = PsiEquivalenceUtil.getFilteredChildren(candidate, null, true); - if (children1.length != children2.length) return false; - - for (int i = 0; i < children1.length; i++) { - PsiElement child1 = children1[i]; - PsiElement child2 = children2[i]; - if (!matchPattern(child1, child2)) return false; - } - - if (children1.length == 0) { - if (!pattern.textMatches(candidate)) return false; - } - - return true; - } - -} diff --git a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java index 9304bc4f091b..fa26ff502adb 100644 --- a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java +++ b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java @@ -2,21 +2,12 @@ package com.jetbrains.python.refactoring.extractmethod; import com.intellij.codeInsight.CodeInsightUtilCore; import com.intellij.codeInsight.codeFragment.CodeFragment; -import com.intellij.codeInsight.highlighting.HighlightManager; -import com.intellij.find.FindManager; import com.intellij.lang.LanguageNamesValidation; import com.intellij.lang.refactoring.NamesValidator; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.LogicalPosition; -import com.intellij.openapi.editor.ScrollType; -import com.intellij.openapi.editor.colors.EditorColors; -import com.intellij.openapi.editor.colors.EditorColorsManager; -import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; @@ -26,20 +17,18 @@ import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.impl.source.codeStyle.CodeEditUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.RefactoringBundle; -import com.intellij.refactoring.extractMethod.AbstractExtractMethodDialog; -import com.intellij.refactoring.extractMethod.AbstractVariableData; -import com.intellij.refactoring.extractMethod.ExtractMethodDecorator; -import com.intellij.refactoring.extractMethod.ExtractMethodValidator; +import com.intellij.refactoring.extractMethod.*; import com.intellij.refactoring.listeners.RefactoringElementListenerComposite; import com.intellij.refactoring.rename.RenameUtil; import com.intellij.refactoring.util.CommonRefactoringUtil; -import com.intellij.ui.ReplacePromptDialog; import com.intellij.usageView.UsageInfo; +import com.intellij.util.Consumer; import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.hash.HashMap; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; +import com.jetbrains.python.PythonFileType; import com.jetbrains.python.PythonLanguage; import com.jetbrains.python.codeInsight.codeFragment.PyCodeFragment; import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache; @@ -98,7 +87,7 @@ public class PyExtractMethodUtil { final String methodName = data.first; final AbstractVariableData[] variableData = data.second; - final PyDuplicatesFinder finder = new PyDuplicatesFinder(statement1, statement2); + final SimpleDuplicatesFinder finder = new SimpleDuplicatesFinder(statement1, statement2); if (fragment.getOutputVariables().isEmpty()) { CommandProcessor.getInstance().executeCommand(project, new Runnable() { @@ -200,7 +189,7 @@ public class PyExtractMethodUtil { private static void processDuplicates(@NotNull final PsiElement callElement, @NotNull final PyFunction generatedMethod, - @NotNull final PyDuplicatesFinder finder, + @NotNull final SimpleDuplicatesFinder finder, @NotNull final Editor editor) { ScopeOwner owner = ScopeUtil.getScopeOwner(callElement); if (owner instanceof PsiFile) return; @@ -215,57 +204,19 @@ public class PyExtractMethodUtil { } } } - final List> duplicates = finder.findDuplicates(scope, generatedMethod); - - if (duplicates.size() > 0) { - final String message = RefactoringBundle.message("0.has.detected.1.code.fragments.in.this.file.that.can.be.replaced.with.a.call.to.extracted.method", - ApplicationNamesInfo.getInstance().getProductName(), duplicates.size()); - final boolean isUnittest = ApplicationManager.getApplication().isUnitTestMode(); - final int exitCode = !isUnittest ? Messages.showYesNoDialog(callElement.getProject(), message, - RefactoringBundle.message("refactoring.extract.method.dialog.title"), Messages.getInformationIcon()) : - DialogWrapper.OK_EXIT_CODE; - if (exitCode == DialogWrapper.OK_EXIT_CODE) { - boolean replaceAll = false; - for (Pair match : duplicates) { - final List elementsRange = PyPsiUtils.collectElements(match.getFirst(), match.getSecond()); - if (!replaceAll) { - highlightInEditor(callElement.getProject(), match, editor); - - int promptResult = FindManager.PromptResult.ALL; - if (!isUnittest) { - ReplacePromptDialog promptDialog = new ReplacePromptDialog(false, RefactoringBundle.message("replace.fragment"), callElement.getProject()); - promptDialog.show(); - promptResult = promptDialog.getExitCode(); - } - if (promptResult == FindManager.PromptResult.SKIP) continue; - if (promptResult == FindManager.PromptResult.CANCEL) break; - - if (promptResult == FindManager.PromptResult.OK) { - replaceElements(elementsRange, callElement); - } - else if (promptResult == FindManager.PromptResult.ALL) { - replaceElements(elementsRange, callElement); - replaceAll = true; - } - } - else { - replaceElements(elementsRange, callElement); - } - } - } - } - } - - private static void highlightInEditor(@NotNull final Project project, @NotNull final Pair pair, - @NotNull final Editor editor) { - final HighlightManager highlightManager = HighlightManager.getInstance(project); - final EditorColorsManager colorsManager = EditorColorsManager.getInstance(); - final TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES); - final int startOffset = pair.getFirst().getTextRange().getStartOffset(); - final int endOffset = pair.getSecond().getTextRange().getEndOffset(); - highlightManager.addRangeHighlight(editor, startOffset, endOffset, attributes, true, null); - final LogicalPosition logicalPosition = editor.offsetToLogicalPosition(startOffset); - editor.getScrollingModel().scrollTo(logicalPosition, ScrollType.MAKE_VISIBLE); + ExtractMethodHelper.processDuplicates(callElement, generatedMethod, scope, finder, editor, + new Function, List>() { + @Override + public List fun(Pair pair) { + return PyPsiUtils.collectElements(pair.first, pair.second); + } + }, + new Consumer, PsiElement>>() { + @Override + public void consume(Pair, PsiElement> pair) { + replaceElements(pair.first, pair.second); + } + }); } private static void processGlobalWrites(@NotNull final PyFunction function, @NotNull final PyCodeFragment fragment) { @@ -637,7 +588,8 @@ public class PyExtractMethodUtil { } }; - final AbstractExtractMethodDialog dialog = new AbstractExtractMethodDialog(project, "method_name", fragment, validator, decorator) { + final AbstractExtractMethodDialog dialog = new AbstractExtractMethodDialog(project, "method_name", fragment, validator, decorator, + PythonFileType.INSTANCE) { @Override protected String getHelpId() { return "python.reference.extractMethod";