blob: e70bbb81ff481acd372b37479ad9c795a105b81a [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.codeInsight.unwrap;
import com.intellij.codeInsight.CodeInsightActionHandler;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.highlighting.HighlightManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.markup.HighlighterLayer;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.*;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.tree.RecursiveTreeElementWalkingVisitor;
import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.ui.components.JBList;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.util.ArrayList;
import java.util.List;
public class UnwrapHandler implements CodeInsightActionHandler {
public static final int HIGHLIGHTER_LEVEL = HighlighterLayer.SELECTION + 1;
@Override
public boolean startInWriteAction() {
return true;
}
@Override
public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
if (!CodeInsightUtilBase.prepareEditorForWrite(editor)) return;
List<AnAction> options = collectOptions(project, editor, file);
selectOption(options, editor, file);
}
private List<AnAction> collectOptions(Project project, Editor editor, PsiFile file) {
List<AnAction> result = new ArrayList<AnAction>();
UnwrapDescriptor d = getUnwrapDescription(file);
for (Pair<PsiElement, Unwrapper> each : d.collectUnwrappers(project, editor, file)) {
result.add(createUnwrapAction(each.getSecond(), each.getFirst(), editor, project));
}
return result;
}
private static UnwrapDescriptor getUnwrapDescription(PsiFile file) {
return LanguageUnwrappers.INSTANCE.forLanguage(file.getLanguage());
}
private AnAction createUnwrapAction(Unwrapper u, PsiElement el, Editor ed, Project p) {
return new MyUnwrapAction(p, ed, u, el);
}
protected void selectOption(List<AnAction> options, Editor editor, PsiFile file) {
if (options.isEmpty()) return;
if (!getUnwrapDescription(file).showOptionsDialog() ||
ApplicationManager.getApplication().isUnitTestMode()
) {
options.get(0).actionPerformed(null);
return;
}
showPopup(options, editor);
}
private void showPopup(final List<AnAction> options, Editor editor) {
final ScopeHighlighter highlighter = new ScopeHighlighter(editor);
DefaultListModel m = new DefaultListModel();
for (AnAction a : options) {
m.addElement(((MyUnwrapAction)a).getName());
}
final JList list = new JBList(m);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setVisibleRowCount(options.size());
list.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
int index = list.getSelectedIndex();
if (index < 0) return;
MyUnwrapAction a = (MyUnwrapAction)options.get(index);
List<PsiElement> toExtract = new ArrayList<PsiElement>();
PsiElement wholeRange = a.collectAffectedElements(toExtract);
highlighter.highlight(wholeRange, toExtract);
}
});
PopupChooserBuilder builder = JBPopupFactory.getInstance().createListPopupBuilder(list);
builder
.setTitle(CodeInsightBundle.message("unwrap.popup.title"))
.setMovable(false)
.setResizable(false)
.setRequestFocus(true)
.setItemChoosenCallback(new Runnable() {
@Override
public void run() {
MyUnwrapAction a = (MyUnwrapAction)options.get(list.getSelectedIndex());
a.actionPerformed(null);
}
})
.addListener(new JBPopupAdapter() {
@Override
public void onClosed(LightweightWindowEvent event) {
highlighter.dropHighlight();
}
});
JBPopup popup = builder.createPopup();
popup.showInBestPositionFor(editor);
}
public static TextAttributes getTestAttributesForExtract() {
EditorColorsManager manager = EditorColorsManager.getInstance();
return manager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
}
private static class MyUnwrapAction extends AnAction {
private static final Key<Integer> CARET_POS_KEY = new Key<Integer>("UNWRAP_HANDLER_CARET_POSITION");
private final Project myProject;
private final Editor myEditor;
private final Unwrapper myUnwrapper;
private final PsiElement myElement;
public MyUnwrapAction(Project project, Editor editor, Unwrapper unwrapper, PsiElement element) {
super(unwrapper.getDescription(element));
myProject = project;
myEditor = editor;
myUnwrapper = unwrapper;
myElement = element;
}
@Override
public void actionPerformed(AnActionEvent e) {
final PsiFile file = myElement.getContainingFile();
if (!FileModificationService.getInstance().prepareFileForWrite(file)) return;
CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
try {
UnwrapDescriptor d = getUnwrapDescription(file);
if (d.shouldTryToRestoreCaretPosition()) saveCaretPosition(file);
int scrollOffset = myEditor.getScrollingModel().getVerticalScrollOffset();
List<PsiElement> extractedElements = myUnwrapper.unwrap(myEditor, myElement);
if (d.shouldTryToRestoreCaretPosition()) restoreCaretPosition(file);
myEditor.getScrollingModel().scrollVertically(scrollOffset);
highlightExtractedElements(extractedElements);
}
catch (IncorrectOperationException ex) {
throw new RuntimeException(ex);
}
}
});
}
}, null, myEditor.getDocument());
}
private void saveCaretPosition(PsiFile file) {
int offset = myEditor.getCaretModel().getOffset();
PsiElement el = file.findElementAt(offset);
int innerOffset = offset - el.getTextOffset();
el.putCopyableUserData(CARET_POS_KEY, innerOffset);
}
private void restoreCaretPosition(final PsiFile file) {
((TreeElement)file.getNode()).acceptTree(new RecursiveTreeElementWalkingVisitor() {
@Override
protected void visitNode(TreeElement element) {
PsiElement el = element.getPsi();
Integer offset = el.getCopyableUserData(CARET_POS_KEY);
// continue;
if (offset != null) {
myEditor.getCaretModel().moveToOffset(el.getTextOffset() + offset);
el.putCopyableUserData(CARET_POS_KEY, null);
return;
}
super.visitNode(element);
}
});
}
private void highlightExtractedElements(final List<PsiElement> extractedElements) {
for (PsiElement each : extractedElements) {
HighlightManager.getInstance(myProject).addRangeHighlight(
myEditor,
each.getTextOffset(),
each.getTextOffset() + each.getTextLength(),
getTestAttributesForExtract(),
false,
true,
null);
}
}
public String getName() {
return myUnwrapper.getDescription(myElement);
}
public PsiElement collectAffectedElements(List<PsiElement> toExtract) {
return myUnwrapper.collectAffectedElements(myElement, toExtract);
}
}
}