blob: 7750d91ade5e251a0eb906cfa9e9823c43dbe978 [file] [log] [blame]
/*
* Copyright 2000-2009 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.actions;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.codeInsight.lookup.Lookup;
import com.intellij.codeInsight.lookup.LookupEx;
import com.intellij.codeInsight.lookup.LookupManager;
import com.intellij.codeInsight.lookup.impl.LookupImpl;
import com.intellij.ide.IdeEventQueue;
import com.intellij.lang.Language;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.command.UndoConfirmationPolicy;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actionSystem.DocCommandGroupId;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.refactoring.RefactoringActionHandler;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.rename.inplace.InplaceRefactoring;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public abstract class BaseRefactoringAction extends AnAction {
private final Condition<Language> myLanguageCondition = new Condition<Language>() {
@Override
public boolean value(Language language) {
return isAvailableForLanguage(language);
}
};
protected abstract boolean isAvailableInEditorOnly();
protected abstract boolean isEnabledOnElements(@NotNull PsiElement[] elements);
protected boolean isAvailableOnElementInEditorAndFile(@NotNull PsiElement element, @NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext context) {
return true;
}
protected boolean hasAvailableHandler(@NotNull DataContext dataContext) {
return getHandler(dataContext) != null;
}
@Nullable
protected abstract RefactoringActionHandler getHandler(@NotNull DataContext dataContext);
@Override
public final void actionPerformed(AnActionEvent e) {
DataContext dataContext = e.getDataContext();
final Project project = e.getData(CommonDataKeys.PROJECT);
if (project == null) return;
PsiDocumentManager.getInstance(project).commitAllDocuments();
final Editor editor = e.getData(CommonDataKeys.EDITOR);
final PsiElement[] elements = getPsiElementArray(dataContext);
int eventCount = IdeEventQueue.getInstance().getEventCount();
RefactoringActionHandler handler;
try {
handler = getHandler(dataContext);
}
catch (ProcessCanceledException ignored) {
return;
}
if (handler == null) {
CommonRefactoringUtil.showErrorHint(project, editor, RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message(
"error.wrong.caret.position.symbol.to.refactor")), RefactoringBundle.getCannotRefactorMessage(null), null);
return;
}
if (!InplaceRefactoring.canStartAnotherRefactoring(editor, project, handler, elements)) {
InplaceRefactoring.unableToStartWarning(project, editor);
return;
}
if (InplaceRefactoring.getActiveInplaceRenamer(editor) == null) {
final LookupEx lookup = LookupManager.getActiveLookup(editor);
if (lookup instanceof LookupImpl) {
Runnable command = new Runnable() {
@Override
public void run() {
((LookupImpl)lookup).finishLookup(Lookup.NORMAL_SELECT_CHAR);
}
};
assert editor != null;
Document doc = editor.getDocument();
DocCommandGroupId group = DocCommandGroupId.noneGroupId(doc);
CommandProcessor.getInstance().executeCommand(editor.getProject(), command, "Completion", group, UndoConfirmationPolicy.DEFAULT, doc);
}
}
IdeEventQueue.getInstance().setEventCount(eventCount);
if (editor != null) {
final PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
if (file == null) return;
DaemonCodeAnalyzer.getInstance(project).autoImportReferenceAtCursor(editor, file);
handler.invoke(project, editor, file, dataContext);
}
else {
handler.invoke(project, elements, dataContext);
}
}
protected boolean isEnabledOnDataContext(DataContext dataContext) {
return false;
}
@Override
public void update(AnActionEvent e) {
Presentation presentation = e.getPresentation();
presentation.setVisible(true);
presentation.setEnabled(true);
DataContext dataContext = e.getDataContext();
Project project = e.getData(CommonDataKeys.PROJECT);
if (project == null || isHidden()) {
hideAction(e);
return;
}
Editor editor = e.getData(CommonDataKeys.EDITOR);
PsiFile file = e.getData(CommonDataKeys.PSI_FILE);
if (file != null) {
if (file instanceof PsiCompiledElement || !isAvailableForFile(file)) {
hideAction(e);
return;
}
}
if (editor == null) {
if (isAvailableInEditorOnly()) {
hideAction(e);
return;
}
final PsiElement[] elements = getPsiElementArray(dataContext);
final boolean isEnabled = isEnabledOnDataContext(dataContext) || elements.length != 0 && isEnabledOnElements(elements);
if (!isEnabled) {
disableAction(e);
}
}
else {
PsiElement element = e.getData(CommonDataKeys.PSI_ELEMENT);
Language[] languages = e.getData(LangDataKeys.CONTEXT_LANGUAGES);
if (element == null || !isAvailableForLanguage(element.getLanguage())) {
if (file == null) {
hideAction(e);
return;
}
element = getElementAtCaret(editor, file);
}
if (element == null || element instanceof SyntheticElement || languages == null) {
hideAction(e);
return;
}
boolean isVisible = ContainerUtil.find(languages, myLanguageCondition) != null;
if (isVisible) {
boolean isEnabled = isAvailableOnElementInEditorAndFile(element, editor, file, dataContext);
if (!isEnabled) {
disableAction(e);
}
}
else {
hideAction(e);
}
}
}
private static void hideAction(AnActionEvent e) {
e.getPresentation().setVisible(false);
disableAction(e);
}
protected boolean isHidden() {
return false;
}
public static PsiElement getElementAtCaret(final Editor editor, final PsiFile file) {
final int offset = fixCaretOffset(editor);
PsiElement element = file.findElementAt(offset);
if (element == null && offset == file.getTextLength()) {
element = file.findElementAt(offset - 1);
}
if (element instanceof PsiWhiteSpace) {
element = file.findElementAt(element.getTextRange().getStartOffset() - 1);
}
return element;
}
private static int fixCaretOffset(final Editor editor) {
final int caret = editor.getCaretModel().getOffset();
if (editor.getSelectionModel().hasSelection() && !editor.getSelectionModel().hasBlockSelection()) {
if (caret == editor.getSelectionModel().getSelectionEnd()) {
return Math.max(editor.getSelectionModel().getSelectionStart(), editor.getSelectionModel().getSelectionEnd() - 1);
}
}
return caret;
}
private static void disableAction(final AnActionEvent e) {
e.getPresentation().setEnabled(false);
}
protected boolean isAvailableForLanguage(Language language) {
return language.isKindOf(StdFileTypes.JAVA.getLanguage());
}
protected boolean isAvailableForFile(PsiFile file) {
return true;
}
@NotNull
public static PsiElement[] getPsiElementArray(DataContext dataContext) {
PsiElement[] psiElements = LangDataKeys.PSI_ELEMENT_ARRAY.getData(dataContext);
if (psiElements == null || psiElements.length == 0) {
PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
if (element != null) {
psiElements = new PsiElement[]{element};
}
}
if (psiElements == null) return PsiElement.EMPTY_ARRAY;
List<PsiElement> filtered = null;
for (PsiElement element : psiElements) {
if (element instanceof SyntheticElement) {
if (filtered == null) filtered = new ArrayList<PsiElement>(Arrays.asList(element));
filtered.remove(element);
}
}
return filtered == null ? psiElements : PsiUtilCore.toPsiElementArray(filtered);
}
}