blob: 5e5b620bcd316a8b3db5357fb1028baa4339fb38 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.refactoring.util.duplicates;
import com.intellij.CommonBundle;
import com.intellij.codeInsight.folding.CodeFoldingManager;
import com.intellij.codeInsight.highlighting.HighlightManager;
import com.intellij.find.FindManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.FoldRegion;
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.RangeHighlighter;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.ui.ReplacePromptDialog;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* @author dsl
*/
public class DuplicatesImpl {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.util.duplicates.DuplicatesImpl");
private DuplicatesImpl() {}
public static void invoke(@NotNull final Project project, @NotNull Editor editor, @NotNull MatchProvider provider) {
final List<Match> duplicates = provider.getDuplicates();
int idx = 0;
final Ref<Boolean> showAll = new Ref<Boolean>();
final String confirmDuplicatePrompt = getConfirmationPrompt(provider, duplicates);
for (final Match match : duplicates) {
if (!match.getMatchStart().isValid() || !match.getMatchEnd().isValid()) continue;
if (replaceMatch(project, provider, match, editor, ++idx, duplicates.size(), showAll, confirmDuplicatePrompt, true)) return;
}
}
public static void invoke(final Project project, final MatchProvider provider) {
final List<Match> duplicates = provider.getDuplicates();
int idx = 0;
final Ref<Boolean> showAll = new Ref<Boolean>();
final String confirmDuplicatePrompt = getConfirmationPrompt(provider, duplicates);
for (final Match match : duplicates) {
final PsiFile file = match.getFile();
final VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile == null || !virtualFile.isValid()) return;
if (!CommonRefactoringUtil.checkReadOnlyStatus(project, file)) return;
final Editor editor = FileEditorManager.getInstance(project).openTextEditor(new OpenFileDescriptor(project, virtualFile), false);
LOG.assertTrue(editor != null);
if (!match.getMatchStart().isValid() || !match.getMatchEnd().isValid()) continue;
if (replaceMatch(project, provider, match, editor, ++idx, duplicates.size(), showAll, confirmDuplicatePrompt, false)) return;
}
}
@Nullable
private static String getConfirmationPrompt(MatchProvider provider, List<Match> duplicates) {
String confirmDuplicatePrompt = null;
for (Match duplicate : duplicates) {
confirmDuplicatePrompt = provider.getConfirmDuplicatePrompt(duplicate);
if (confirmDuplicatePrompt != null) {
break;
}
}
return confirmDuplicatePrompt;
}
private static boolean replaceMatch(final Project project,
final MatchProvider provider,
final Match match,
@NotNull final Editor editor,
final int idx,
final int size,
Ref<Boolean> showAll,
final String confirmDuplicatePrompt,
boolean skipPromptWhenOne) {
final ArrayList<RangeHighlighter> highlighters = previewMatch(project, match, editor);
if (!ApplicationManager.getApplication().isUnitTestMode()) {
if ((!skipPromptWhenOne || size > 1) && (showAll.get() == null || !showAll.get())) {
final String prompt = provider.getConfirmDuplicatePrompt(match);
final ReplacePromptDialog promptDialog = new ReplacePromptDialog(false, provider.getReplaceDuplicatesTitle(idx, size), project) {
@Override
protected String getMessage() {
final String message = super.getMessage();
return prompt != null ? message + prompt : message;
}
};
promptDialog.show();
final boolean allChosen = promptDialog.getExitCode() == FindManager.PromptResult.ALL;
showAll.set(allChosen);
if (allChosen && confirmDuplicatePrompt != null && prompt == null) {
if (Messages.showOkCancelDialog(project, "In order to replace all occurrences method signature will be changed. Proceed?", CommonBundle.getWarningTitle(), Messages.getWarningIcon()) !=
Messages.OK) return true;
}
if (promptDialog.getExitCode() == FindManager.PromptResult.SKIP) return false;
if (promptDialog.getExitCode() == FindManager.PromptResult.CANCEL) return true;
}
}
HighlightManager.getInstance(project).removeSegmentHighlighter(editor, highlighters.get(0));
new WriteCommandAction(project, MethodDuplicatesHandler.REFACTORING_NAME, MethodDuplicatesHandler.REFACTORING_NAME) {
@Override
protected void run(Result result) throws Throwable {
try {
provider.processMatch(match);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
}.execute();
return false;
}
public static ArrayList<RangeHighlighter> previewMatch(Project project, Match match, Editor editor) {
final ArrayList<RangeHighlighter> highlighters = new ArrayList<RangeHighlighter>();
highlightMatch(project, editor, match, highlighters);
final TextRange textRange = match.getTextRange();
final LogicalPosition logicalPosition = editor.offsetToLogicalPosition(textRange.getStartOffset());
expandAllRegionsCoveringRange(project, editor, textRange);
editor.getScrollingModel().scrollTo(logicalPosition, ScrollType.MAKE_VISIBLE);
return highlighters;
}
private static void expandAllRegionsCoveringRange(final Project project, Editor editor, final TextRange textRange) {
final FoldRegion[] foldRegions = CodeFoldingManager.getInstance(project).getFoldRegionsAtOffset(editor, textRange.getStartOffset());
boolean anyCollapsed = false;
for (final FoldRegion foldRegion : foldRegions) {
if (!foldRegion.isExpanded()) {
anyCollapsed = true;
break;
}
}
if (anyCollapsed) {
editor.getFoldingModel().runBatchFoldingOperation(new Runnable() {
public void run() {
for (final FoldRegion foldRegion : foldRegions) {
if (!foldRegion.isExpanded()) {
foldRegion.setExpanded(true);
}
}
}
});
}
}
public static void highlightMatch(final Project project, Editor editor, final Match match, final ArrayList<RangeHighlighter> highlighters) {
EditorColorsManager colorsManager = EditorColorsManager.getInstance();
TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
HighlightManager.getInstance(project).addRangeHighlight(editor, match.getTextRange().getStartOffset(), match.getTextRange().getEndOffset(),
attributes, true, highlighters);
}
public static void processDuplicates(@NotNull MatchProvider provider, @NotNull Project project, @NotNull Editor editor) {
boolean hasDuplicates = provider.hasDuplicates();
if (hasDuplicates) {
List<Match> duplicates = provider.getDuplicates();
if (duplicates.size() == 1) {
previewMatch(project, duplicates.get(0), editor);
}
final int answer = ApplicationManager.getApplication().isUnitTestMode() ? Messages.YES : Messages.showYesNoDialog(project,
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()),
"Process Duplicates", Messages.getQuestionIcon());
if (answer == Messages.YES) {
invoke(project, editor, provider);
}
}
}
}