blob: 7070b065c959b378274b9ad2ea35c527cc360344 [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.ide.actions;
import com.intellij.codeInsight.navigation.NavigationUtil;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.ide.structureView.StructureView;
import com.intellij.ide.structureView.StructureViewBuilder;
import com.intellij.ide.structureView.StructureViewTreeElement;
import com.intellij.ide.util.EditSourceUtil;
import com.intellij.ide.util.gotoByName.*;
import com.intellij.ide.util.treeView.smartTree.TreeElement;
import com.intellij.lang.Language;
import com.intellij.lang.LanguageStructureViewBuilder;
import com.intellij.lang.PsiStructureViewFactory;
import com.intellij.navigation.AnonymousElementProvider;
import com.intellij.navigation.ChooseByNameRegistry;
import com.intellij.navigation.ItemPresentation;
import com.intellij.navigation.NavigationItem;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.playback.commands.ActionCommand;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.Navigatable;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.codeStyle.MinusculeMatcher;
import com.intellij.psi.codeStyle.NameUtil;
import com.intellij.psi.util.PsiUtilCore;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class GotoClassAction extends GotoActionBase implements DumbAware {
@Override
public void actionPerformed(final AnActionEvent e) {
final Project project = e.getData(CommonDataKeys.PROJECT);
assert project != null;
if (!DumbService.getInstance(project).isDumb()) {
super.actionPerformed(e);
}
else {
DumbService.getInstance(project)
.showDumbModeNotification("Goto Class action is not available until indices are built, using Goto File instead");
ActionManager.getInstance()
.tryToExecute(ActionManager.getInstance().getAction(GotoFileAction.ID), ActionCommand.getInputEvent(GotoFileAction.ID),
e.getData(PlatformDataKeys.CONTEXT_COMPONENT), e.getPlace(), true);
}
}
@Override
public void gotoActionPerformed(AnActionEvent e) {
final Project project = e.getData(CommonDataKeys.PROJECT);
assert project != null;
PsiDocumentManager.getInstance(project).commitAllDocuments();
FeatureUsageTracker.getInstance().triggerFeatureUsed("navigation.popup.class");
final GotoClassModel2 model = new GotoClassModel2(project);
showNavigationPopup(e, model, new GotoActionCallback<Language>() {
@Override
protected ChooseByNameFilter<Language> createFilter(@NotNull ChooseByNamePopup popup) {
return new ChooseByNameLanguageFilter(popup, model, GotoClassSymbolConfiguration.getInstance(project), project);
}
@Override
public void elementChosen(ChooseByNamePopup popup, Object element) {
AccessToken token = ReadAction.start();
try {
if (element instanceof PsiElement) {
final PsiElement psiElement = getElement(((PsiElement)element), popup);
final VirtualFile file = PsiUtilCore.getVirtualFile(psiElement);
if (popup.getLinePosition() != -1 && file != null) {
Navigatable n = new OpenFileDescriptor(project, file, popup.getLinePosition(), popup.getColumnPosition()).setUseCurrentWindow(
popup.isOpenInCurrentWindowRequested());
if (n.canNavigate()) {
n.navigate(true);
return;
}
}
if (psiElement != null && file != null && popup.getMemberPattern() != null) {
NavigationUtil.activateFileWithPsiElement(psiElement, !popup.isOpenInCurrentWindowRequested());
Navigatable member = findMember(popup.getMemberPattern(), psiElement, file);
if (member != null) {
member.navigate(true);
}
}
NavigationUtil.activateFileWithPsiElement(psiElement, !popup.isOpenInCurrentWindowRequested());
}
else {
EditSourceUtil.navigate(((NavigationItem)element), true, popup.isOpenInCurrentWindowRequested());
}
}
finally {
token.finish();
}
}
}, "Classes matching pattern", true);
}
@Nullable private static Navigatable findMember(String pattern, PsiElement psiElement, VirtualFile file) {
final PsiStructureViewFactory factory = LanguageStructureViewBuilder.INSTANCE.forLanguage(psiElement.getLanguage());
final StructureViewBuilder builder = factory == null ? null : factory.getStructureViewBuilder(psiElement.getContainingFile());
final FileEditor[] editors = FileEditorManager.getInstance(psiElement.getProject()).getEditors(file);
if (builder == null || editors.length == 0) {
return null;
}
final StructureView view = builder.createStructureView(editors[0], psiElement.getProject());
try {
final StructureViewTreeElement element = findElement(view.getTreeModel().getRoot(), psiElement, 4);
if (element == null) {
return null;
}
final MinusculeMatcher matcher = new MinusculeMatcher(pattern, NameUtil.MatchingCaseSensitivity.NONE);
int max = Integer.MIN_VALUE;
Object target = null;
for (TreeElement treeElement : element.getChildren()) {
if (treeElement instanceof StructureViewTreeElement) {
final ItemPresentation presentation = treeElement.getPresentation();
String presentableText = presentation == null ? null : presentation.getPresentableText();
if (presentableText != null) {
final int degree = matcher.matchingDegree(presentableText);
if (degree > max) {
max = degree;
target = ((StructureViewTreeElement)treeElement).getValue();
}
}
}
}
return target instanceof Navigatable ? (Navigatable)target : null;
}
finally {
Disposer.dispose(view);
}
}
@Nullable
private static StructureViewTreeElement findElement(StructureViewTreeElement node, PsiElement element, int hopes) {
final Object value = node.getValue();
if (value instanceof PsiElement) {
if (((PsiElement)value).isEquivalentTo(element)) return node;
if (hopes != 0) {
for (TreeElement child : node.getChildren()) {
if (child instanceof StructureViewTreeElement) {
final StructureViewTreeElement e = findElement((StructureViewTreeElement)child, element, hopes - 1);
if (e != null) {
return e;
}
}
}
}
}
return null;
}
private static PsiElement getElement(PsiElement element, ChooseByNamePopup popup) {
final String path = popup.getPathToAnonymous();
if (path != null) {
final String[] classes = path.split("\\$");
List<Integer> indexes = new ArrayList<Integer>();
for (String cls : classes) {
if (cls.isEmpty()) continue;
try {
indexes.add(Integer.parseInt(cls) - 1);
}
catch (Exception e) {
return element;
}
}
PsiElement current = element;
for (int index : indexes) {
final PsiElement[] anonymousClasses = getAnonymousClasses(current);
if (anonymousClasses.length > index) {
current = anonymousClasses[index];
}
else {
return current;
}
}
return current;
}
return element;
}
static PsiElement[] getAnonymousClasses(PsiElement element) {
for (AnonymousElementProvider provider : Extensions.getExtensions(AnonymousElementProvider.EP_NAME)) {
final PsiElement[] elements = provider.getAnonymousElements(element);
if (elements != null && elements.length > 0) {
return elements;
}
}
return PsiElement.EMPTY_ARRAY;
}
@Override
protected boolean hasContributors(DataContext dataContext) {
return ChooseByNameRegistry.getInstance().getClassModelContributors().length > 0;
}
}