| /* |
| * 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.extractMethod; |
| |
| import com.intellij.codeInsight.CodeInsightUtil; |
| import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil; |
| import com.intellij.codeInsight.highlighting.HighlightManager; |
| import com.intellij.openapi.actionSystem.CommonDataKeys; |
| import com.intellij.openapi.actionSystem.DataContext; |
| import com.intellij.openapi.actionSystem.LangDataKeys; |
| import com.intellij.openapi.actionSystem.PlatformDataKeys; |
| import com.intellij.openapi.command.CommandProcessor; |
| import com.intellij.openapi.diagnostic.Logger; |
| 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.fileEditor.FileEditorManager; |
| import com.intellij.openapi.fileEditor.OpenFileDescriptor; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Pass; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.openapi.wm.WindowManager; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.source.PostprocessReformattingAspect; |
| import com.intellij.psi.util.PsiExpressionTrimRenderer; |
| import com.intellij.refactoring.HelpID; |
| import com.intellij.refactoring.IntroduceTargetChooser; |
| import com.intellij.refactoring.RefactoringActionHandler; |
| import com.intellij.refactoring.RefactoringBundle; |
| import com.intellij.refactoring.introduceVariable.IntroduceVariableBase; |
| import com.intellij.refactoring.listeners.RefactoringEventData; |
| import com.intellij.refactoring.listeners.RefactoringEventListener; |
| import com.intellij.refactoring.util.CommonRefactoringUtil; |
| import com.intellij.refactoring.util.RefactoringUtil; |
| import com.intellij.refactoring.util.duplicates.DuplicatesImpl; |
| import com.intellij.util.IncorrectOperationException; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.List; |
| |
| public class ExtractMethodHandler implements RefactoringActionHandler { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.extractMethod.ExtractMethodHandler"); |
| |
| public static final String REFACTORING_NAME = RefactoringBundle.message("extract.method.title"); |
| |
| public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { |
| if (dataContext != null) { |
| final PsiFile file = CommonDataKeys.PSI_FILE.getData(dataContext); |
| final Editor editor = CommonDataKeys.EDITOR.getData(dataContext); |
| if (file != null && editor != null) { |
| invokeOnElements(project, editor, file, elements); |
| } |
| } |
| } |
| |
| public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file, DataContext dataContext) { |
| final Pass<PsiElement[]> callback = new Pass<PsiElement[]>() { |
| public void pass(final PsiElement[] selectedValue) { |
| invokeOnElements(project, editor, file, selectedValue); |
| } |
| }; |
| selectAndPass(project, editor, file, callback); |
| } |
| |
| public static void selectAndPass(@NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile file, @NotNull final Pass<PsiElement[]> callback) { |
| editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); |
| if (!editor.getSelectionModel().hasSelection()) { |
| final int offset = editor.getCaretModel().getOffset(); |
| final List<PsiExpression> expressions = IntroduceVariableBase.collectExpressions(file, editor, offset, true); |
| if (expressions.isEmpty()) { |
| editor.getSelectionModel().selectLineAtCaret(); |
| } |
| else if (expressions.size() == 1) { |
| callback.pass(new PsiElement[]{expressions.get(0)}); |
| return; |
| } |
| else { |
| IntroduceTargetChooser.showChooser(editor, expressions, new Pass<PsiExpression>() { |
| @Override |
| public void pass(PsiExpression psiExpression) { |
| callback.pass(new PsiElement[]{psiExpression}); |
| } |
| }, new PsiExpressionTrimRenderer.RenderFunction()); |
| return; |
| } |
| } |
| |
| int startOffset = editor.getSelectionModel().getSelectionStart(); |
| int endOffset = editor.getSelectionModel().getSelectionEnd(); |
| |
| PsiDocumentManager.getInstance(project).commitAllDocuments(); |
| |
| PsiElement[] elements; |
| PsiExpression expr = CodeInsightUtil.findExpressionInRange(file, startOffset, endOffset); |
| if (expr != null) { |
| elements = new PsiElement[]{expr}; |
| } |
| else { |
| elements = CodeInsightUtil.findStatementsInRange(file, startOffset, endOffset); |
| if (elements.length == 0) { |
| final PsiExpression expression = IntroduceVariableBase.getSelectedExpression(project, file, startOffset, endOffset); |
| if (expression != null && IntroduceVariableBase.getErrorMessage(expression) == null) { |
| final PsiType originalType = RefactoringUtil.getTypeByExpressionWithExpectedType(expression); |
| if (originalType != null) { |
| elements = new PsiElement[]{expression}; |
| } |
| } |
| } |
| } |
| callback.pass(elements); |
| } |
| |
| private static void invokeOnElements(final Project project, final Editor editor, PsiFile file, PsiElement[] elements) { |
| getProcessor(elements, project, file, editor, true, new Pass<ExtractMethodProcessor>(){ |
| @Override |
| public void pass(ExtractMethodProcessor processor) { |
| invokeOnElements(project, editor, processor, true); |
| } |
| }); |
| } |
| |
| private static boolean invokeOnElements(final Project project, final Editor editor, @NotNull final ExtractMethodProcessor processor, final boolean directTypes) { |
| if (!CommonRefactoringUtil.checkReadOnlyStatus(project, processor.getTargetClass().getContainingFile())) return false; |
| if (processor.showDialog(directTypes)) { |
| run(project, editor, processor); |
| DuplicatesImpl.processDuplicates(processor, project, editor); |
| return true; |
| } |
| return false; |
| } |
| |
| public static void run(@NotNull final Project project, final Editor editor, final ExtractMethodProcessor processor) { |
| CommandProcessor.getInstance().executeCommand(project, new Runnable() { |
| public void run() { |
| PostprocessReformattingAspect.getInstance(project).postponeFormattingInside(new Runnable() { |
| public void run() { |
| try { |
| final RefactoringEventData beforeData = new RefactoringEventData(); |
| beforeData.addElements(processor.myElements); |
| project.getMessageBus().syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC).refactoringStarted("refactoring.extract.method", beforeData); |
| |
| processor.doRefactoring(); |
| |
| final RefactoringEventData data = new RefactoringEventData(); |
| data.addElement(processor.getExtractedMethod()); |
| project.getMessageBus().syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC).refactoringDone("refactoring.extract.method", data); |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| } |
| } |
| }); |
| } |
| }, REFACTORING_NAME, null); |
| } |
| |
| @Nullable |
| private static ExtractMethodProcessor getProcessor(final PsiElement[] elements, |
| final Project project, |
| final PsiFile file, |
| final Editor editor, |
| final boolean showErrorMessages, |
| final @Nullable Pass<ExtractMethodProcessor> pass) { |
| if (elements == null || elements.length == 0) { |
| if (showErrorMessages) { |
| String message = RefactoringBundle |
| .getCannotRefactorMessage(RefactoringBundle.message("selected.block.should.represent.a.set.of.statements.or.an.expression")); |
| CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.EXTRACT_METHOD); |
| } |
| return null; |
| } |
| |
| for (PsiElement element : elements) { |
| if (element instanceof PsiStatement && JavaHighlightUtil.isSuperOrThisCall((PsiStatement)element, true, true)) { |
| if (showErrorMessages) { |
| String message = RefactoringBundle |
| .getCannotRefactorMessage(RefactoringBundle.message("selected.block.contains.invocation.of.another.class.constructor")); |
| CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.EXTRACT_METHOD); |
| } |
| return null; |
| } |
| } |
| |
| final ExtractMethodProcessor processor = |
| new ExtractMethodProcessor(project, editor, elements, null, REFACTORING_NAME, "", HelpID.EXTRACT_METHOD); |
| processor.setShowErrorDialogs(showErrorMessages); |
| try { |
| if (!processor.prepare(pass)) return null; |
| } |
| catch (PrepareFailedException e) { |
| if (showErrorMessages) { |
| CommonRefactoringUtil.showErrorHint(project, editor, e.getMessage(), REFACTORING_NAME, HelpID.EXTRACT_METHOD); |
| highlightPrepareError(e, file, editor, project); |
| } |
| return null; |
| } |
| return processor; |
| } |
| |
| public static void highlightPrepareError(PrepareFailedException e, PsiFile file, Editor editor, final Project project) { |
| if (e.getFile() == file) { |
| final TextRange textRange = e.getTextRange(); |
| final HighlightManager highlightManager = HighlightManager.getInstance(project); |
| EditorColorsManager colorsManager = EditorColorsManager.getInstance(); |
| TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES); |
| highlightManager.addRangeHighlight(editor, textRange.getStartOffset(), textRange.getEndOffset(), attributes, true, null); |
| final LogicalPosition logicalPosition = editor.offsetToLogicalPosition(textRange.getStartOffset()); |
| editor.getScrollingModel().scrollTo(logicalPosition, ScrollType.MAKE_VISIBLE); |
| WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting")); |
| } |
| } |
| |
| @Nullable |
| public static ExtractMethodProcessor getProcessor(final Project project, |
| final PsiElement[] elements, |
| final PsiFile file, |
| final boolean openEditor) { |
| return getProcessor(elements, project, file, openEditor ? openEditor(project, file) : null, false, null); |
| } |
| |
| public static boolean invokeOnElements(final Project project, @NotNull final ExtractMethodProcessor processor, final PsiFile file, final boolean directTypes) { |
| return invokeOnElements(project, openEditor(project, file), processor, directTypes); |
| } |
| |
| @Nullable |
| private static Editor openEditor(final Project project, final PsiFile file) { |
| final VirtualFile virtualFile = file.getVirtualFile(); |
| LOG.assertTrue(virtualFile != null); |
| final OpenFileDescriptor fileDescriptor = new OpenFileDescriptor(project, virtualFile); |
| return FileEditorManager.getInstance(project).openTextEditor(fileDescriptor, false); |
| } |
| } |