mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
completely refacrored
This commit is contained in:
@@ -33,6 +33,7 @@ import com.intellij.openapi.actionSystem.impl.ActionManagerImpl;
|
||||
import com.intellij.openapi.application.AccessToken;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||
import com.intellij.openapi.fileEditor.impl.EditorHistoryManager;
|
||||
import com.intellij.openapi.keymap.KeymapUtil;
|
||||
import com.intellij.openapi.options.Configurable;
|
||||
import com.intellij.openapi.options.SearchableConfigurable;
|
||||
@@ -42,9 +43,9 @@ import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.popup.ComponentPopupBuilder;
|
||||
import com.intellij.openapi.ui.popup.JBPopup;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
|
||||
import com.intellij.openapi.util.ActionCallback;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
@@ -60,6 +61,7 @@ import com.intellij.psi.codeStyle.NameUtil;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.ui.*;
|
||||
import com.intellij.ui.border.CustomLineBorder;
|
||||
import com.intellij.ui.components.JBList;
|
||||
import com.intellij.ui.components.JBScrollPane;
|
||||
import com.intellij.ui.components.OnOffButton;
|
||||
import com.intellij.util.Alarm;
|
||||
@@ -80,6 +82,7 @@ import java.awt.event.FocusAdapter;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -98,14 +101,30 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
private String[] myActions;
|
||||
private Component myFocusComponent;
|
||||
private JBPopup myPopup;
|
||||
private int myLeftWidth;
|
||||
private SearchListModel myListModel = new SearchListModel();
|
||||
private TitleIndexes myTitleIndexes;
|
||||
private Map<String, String> myConfigurables = new HashMap<String, String>();
|
||||
|
||||
private Alarm myAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, ApplicationManager.getApplication());
|
||||
private JList myList = new JList(); //don't use JBList here!!! todo[kb]
|
||||
private JBList myList = new JBList(myListModel) {
|
||||
{
|
||||
setOpaque(false);
|
||||
}
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
g.setColor(getTitlePanelBackground());
|
||||
g.fillRect(0, 0, myLeftWidth - 1, getHeight());
|
||||
g.setColor(getSeparatorColor());
|
||||
g.drawLine(myLeftWidth-1, 0, myLeftWidth-1, getHeight());
|
||||
super.paintComponent(g);
|
||||
}
|
||||
}; //don't use JBList here!!! todo[kb]
|
||||
private AnActionEvent myActionEvent;
|
||||
private Component myContextComponent;
|
||||
private CalcThread myCalcThread;
|
||||
private static AtomicBoolean ourShiftCanBeUsed = new AtomicBoolean(false);
|
||||
private ArrayList<VirtualFile> myAlreadyAddedFiles = new ArrayList<VirtualFile>();
|
||||
|
||||
static {
|
||||
IdeEventQueue.getInstance().addPostprocessor(new IdeEventQueue.EventDispatcher() {
|
||||
@@ -117,7 +136,6 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
return false;
|
||||
}
|
||||
}, null);
|
||||
|
||||
}
|
||||
|
||||
private int myTopHitsCount;
|
||||
@@ -159,21 +177,11 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
myAlarm.addRequest(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (StringUtil.isEmpty(pattern.trim())) {
|
||||
//noinspection SSBasedInspection
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (myPopup != null && myPopup.isVisible()) {
|
||||
myPopup.cancel();
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
if (field != null && field.getTextEditor().hasFocus()) {
|
||||
rebuildList(pattern);
|
||||
}
|
||||
rebuildList(pattern);
|
||||
}
|
||||
}, Registry.intValue("ide.goto.rebuild.delay"));
|
||||
}, /*Registry.intValue("ide.goto.rebuild.delay")*/30);
|
||||
}
|
||||
});
|
||||
editor.addFocusListener(new FocusAdapter() {
|
||||
@@ -181,7 +189,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
public void focusGained(FocusEvent e) {
|
||||
field.setText("");
|
||||
field.getTextEditor().setForeground(UIUtil.getLabelForeground());
|
||||
|
||||
myTitleIndexes = new TitleIndexes();
|
||||
editor.setColumns(25);
|
||||
myFocusComponent = e.getOppositeComponent();
|
||||
//noinspection SSBasedInspection
|
||||
@@ -193,6 +201,11 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
parent.repaint();
|
||||
}
|
||||
});
|
||||
if (myPopup != null && myPopup.isVisible()) {
|
||||
myPopup.cancel();
|
||||
myPopup = null;
|
||||
}
|
||||
rebuildList("");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -225,13 +238,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
myCalcThread = null;
|
||||
}
|
||||
myAlarm.cancelAllRequests();
|
||||
myList.setModel(new DefaultListModel());
|
||||
myClassModel = null;
|
||||
myFileModel = null;
|
||||
myActionModel = null;
|
||||
myClasses = null;
|
||||
myFiles = null;
|
||||
myActions = null;
|
||||
myListModel.clear();
|
||||
|
||||
//noinspection SSBasedInspection
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@@ -254,31 +261,35 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
}
|
||||
AccessToken token = ApplicationManager.getApplication().acquireReadActionLock();
|
||||
try {
|
||||
if (value instanceof PsiElement) {
|
||||
NavigationUtil.activateFileWithPsiElement((PsiElement)value, true);
|
||||
return;
|
||||
} else if (isVirtualFile(value)) {
|
||||
OpenFileDescriptor navigatable = new OpenFileDescriptor(project, (VirtualFile)value);
|
||||
if (navigatable.canNavigate()) {
|
||||
navigatable.navigate(true);
|
||||
if (value instanceof PsiElement) {
|
||||
NavigationUtil.activateFileWithPsiElement((PsiElement)value, true);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
focusManager.requestDefaultFocus(true);
|
||||
IdeFocusManager.getInstance(project).doWhenFocusSettlesDown(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (value instanceof BooleanOptionDescription) {
|
||||
final BooleanOptionDescription option = (BooleanOptionDescription)value;
|
||||
option.setOptionState(!option.isOptionEnabled());
|
||||
} else {
|
||||
GotoActionAction.openOptionOrPerformAction(value, pattern, project, myContextComponent ,myActionEvent);
|
||||
}
|
||||
else if (isVirtualFile(value)) {
|
||||
OpenFileDescriptor navigatable = new OpenFileDescriptor(project, (VirtualFile)value);
|
||||
if (navigatable.canNavigate()) {
|
||||
navigatable.navigate(true);
|
||||
return;
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
else {
|
||||
focusManager.requestDefaultFocus(true);
|
||||
IdeFocusManager.getInstance(project).doWhenFocusSettlesDown(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (value instanceof BooleanOptionDescription) {
|
||||
final BooleanOptionDescription option = (BooleanOptionDescription)value;
|
||||
option.setOptionState(!option.isOptionEnabled());
|
||||
}
|
||||
else {
|
||||
GotoActionAction.openOptionOrPerformAction(value, pattern, project, myContextComponent, myActionEvent);
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
finally {
|
||||
token.finish();
|
||||
}
|
||||
focusManager.requestDefaultFocus(true);
|
||||
@@ -353,11 +364,6 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
return new Dimension(myLeftWidth, super.getPreferredSize().height);
|
||||
}
|
||||
};
|
||||
private int myLeftWidth;
|
||||
|
||||
public void setLeftWidth(int width) {
|
||||
myLeftWidth = width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
@@ -386,7 +392,8 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
final OnOffButton button = new OnOffButton();
|
||||
button.setSelected(((BooleanOptionDescription)value).isOptionEnabled());
|
||||
rightComponent = button;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
rightComponent = myLocation.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||
}
|
||||
panel.add(rightComponent, BorderLayout.EAST);
|
||||
@@ -394,55 +401,15 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
}
|
||||
|
||||
cmp.setBackground(isSelected ? UIUtil.getListSelectionBackground() : getRightBackground());
|
||||
String title = getTitle(index, value, index == 0 ? null : list.getModel().getElementAt(index -1));
|
||||
String title = myTitleIndexes.getTitle(index);
|
||||
myTitle.setText(title == null ? "" : title);
|
||||
myLeftPanel.removeAll();
|
||||
myLeftPanel.setBackground(new JBColor(Gray._242, JBColor.background()));
|
||||
myLeftPanel.setBackground(getTitlePanelBackground());
|
||||
myMainPanel.removeAll();
|
||||
myLeftPanel.add(myTitle, BorderLayout.EAST);
|
||||
myMainPanel.add(myLeftPanel, BorderLayout.WEST);
|
||||
myMainPanel.add(cmp, BorderLayout.CENTER);
|
||||
return myMainPanel;
|
||||
}
|
||||
|
||||
private Color getRightBackground() {
|
||||
return UIUtil.isUnderAquaLookAndFeel() ? Gray._236 : UIUtil.getListBackground();
|
||||
}
|
||||
|
||||
|
||||
private String getTitle(int index, Object value, Object prevValue) {
|
||||
if (index == 0 && myTopHitsCount > 0) {
|
||||
return myTopHitsCount == 1 ? "Top Hit" : "Top Hits";
|
||||
}
|
||||
if (index < myTopHitsCount) {
|
||||
return null;
|
||||
}
|
||||
if (index == myTopHitsCount) {
|
||||
prevValue = null;
|
||||
}
|
||||
String gotoClass = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("GotoClass"));
|
||||
gotoClass = StringUtil.isEmpty(gotoClass) ? "Classes" : "Classes (" + gotoClass + ")";
|
||||
String gotoFile = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("GotoFile"));
|
||||
gotoFile = StringUtil.isEmpty(gotoFile) ? "Files" : "Files (" + gotoFile + ")";
|
||||
String gotoAction = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("GotoAction"));
|
||||
gotoAction = StringUtil.isEmpty(gotoAction) ? "Actions" : "Actions (" + gotoAction + ")";
|
||||
String gotoSettings = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("ShowSettings"));
|
||||
gotoSettings = StringUtil.isEmpty(gotoAction) ? "Preferences" : "Preferences (" + gotoSettings + ")";
|
||||
String toolWindows = "Tool Windows";
|
||||
if (prevValue == null) { // firstElement
|
||||
if (value instanceof PsiElement)return gotoClass;
|
||||
if (isVirtualFile(value))return gotoFile;
|
||||
if (isToolWindowAction(value)) return toolWindows;
|
||||
if (isSetting(value)) return gotoSettings;
|
||||
if (isActionValue(value))return gotoAction;
|
||||
return gotoSettings;
|
||||
} else {
|
||||
if (!isVirtualFile(prevValue) && isVirtualFile(value)) return gotoFile;
|
||||
if (!isSetting(prevValue) && isSetting(value)) return gotoSettings;
|
||||
if (!isToolWindowAction(prevValue) && isToolWindowAction(value)) return toolWindows;
|
||||
if ((!isActionValue(prevValue) || isToolWindowAction(prevValue)) && isActionValue(value)) return gotoAction;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -457,7 +424,8 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
}
|
||||
else if (isVirtualFile(value)) {
|
||||
append(((VirtualFile)value).getName());
|
||||
} else if (isActionValue(value)) {
|
||||
}
|
||||
else if (isActionValue(value)) {
|
||||
final Map.Entry actionWithParentGroup = value instanceof Map.Entry ? (Map.Entry)value : null;
|
||||
final AnAction anAction = actionWithParentGroup == null ? (AnAction)value : (AnAction)actionWithParentGroup.getKey();
|
||||
final Presentation templatePresentation = anAction.getTemplatePresentation();
|
||||
@@ -514,7 +482,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
while (index < model.getSize()) {
|
||||
Object el = model.getElementAt(index);
|
||||
Object prev = index == 0 ? null : model.getElementAt(index - 1);
|
||||
String title = getTitle(index, el, prev);
|
||||
String title = myTitleIndexes.getTitle(index);
|
||||
if (title != null) {
|
||||
myTitle.setText(title);
|
||||
myLeftWidth = Math.max(myLeftWidth, myTitle.getPreferredSize().width);
|
||||
@@ -525,7 +493,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
myLeftWidth += 10;
|
||||
myTitle.setForeground(Gray._122);
|
||||
myTitle.setAlignmentY(BOTTOM_ALIGNMENT);
|
||||
myLeftPanel.setBorder(new CompoundBorder(new CustomLineBorder(new JBColor(Gray._206, Gray._75), 0,0,0,1), new EmptyBorder(0,0,0,5)));
|
||||
myLeftPanel.setBorder(new CompoundBorder(new CustomLineBorder(getSeparatorColor(), 0, 0, 0, 1), new EmptyBorder(0, 0, 0, 5)));
|
||||
}
|
||||
|
||||
private Font getTitleFont() {
|
||||
@@ -533,6 +501,18 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
}
|
||||
}
|
||||
|
||||
private static JBColor getTitlePanelBackground() {
|
||||
return new JBColor(Gray._242, JBColor.background());
|
||||
}
|
||||
|
||||
private static Color getRightBackground() {
|
||||
return UIUtil.isUnderAquaLookAndFeel() ? Gray._236 : UIUtil.getListBackground();
|
||||
}
|
||||
|
||||
private static JBColor getSeparatorColor() {
|
||||
return new JBColor(Gray._206, Gray._75);
|
||||
}
|
||||
|
||||
private static boolean isActionValue(Object o) {
|
||||
return o instanceof Map.Entry || o instanceof AnAction;
|
||||
}
|
||||
@@ -545,6 +525,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
return o instanceof VirtualFile;
|
||||
}
|
||||
|
||||
@SuppressWarnings("SSBasedInspection")
|
||||
private class CalcThread implements Runnable {
|
||||
private final Project project;
|
||||
private final String pattern;
|
||||
@@ -557,84 +538,241 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
//noinspection SSBasedInspection
|
||||
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myTitleIndexes.clear();
|
||||
myListModel.clear();
|
||||
myAlreadyAddedFiles.clear();
|
||||
}
|
||||
});
|
||||
if (pattern.trim().length() == 0) {
|
||||
buildModelFromRecentFiles();
|
||||
updatePopup();
|
||||
return;
|
||||
}
|
||||
|
||||
checkModelsUpToDate();
|
||||
buildTopHit(pattern);
|
||||
buildRecentFiles(pattern);
|
||||
updatePopup();
|
||||
AccessToken readLock = ApplicationManager.getApplication().acquireReadActionLock();
|
||||
try {
|
||||
buildClasses(pattern);
|
||||
} finally {readLock.finish();}
|
||||
updatePopup();
|
||||
readLock = ApplicationManager.getApplication().acquireReadActionLock();
|
||||
try {
|
||||
buildFiles(pattern);
|
||||
} finally {readLock.finish();}
|
||||
buildActionsAndSettings(pattern);
|
||||
updatePopup();
|
||||
}
|
||||
|
||||
private void buildActionsAndSettings(String pattern) {
|
||||
final Set<AnAction> actions = new HashSet<AnAction>();
|
||||
final Set<Object> settings = new HashSet<Object>();
|
||||
final HashSet<AnAction> toolWindows = new HashSet<AnAction>();
|
||||
|
||||
List<MatchResult> matches = collectResults(pattern, myActions, myActionModel);
|
||||
|
||||
for (MatchResult o : matches) {
|
||||
//if (actionsCount > 15) break;
|
||||
myProgressIndicator.checkCanceled();
|
||||
Object[] objects = myActionModel.getElementsByName(o.elementName, true, pattern);
|
||||
for (Object object : objects) {
|
||||
myProgressIndicator.checkCanceled();
|
||||
if (isSetting(object)) {
|
||||
settings.add(object);
|
||||
}
|
||||
else if (isToolWindowAction(object)) {
|
||||
toolWindows.add((AnAction)((Map.Entry)object).getKey());
|
||||
}
|
||||
else if (isActionValue(object)) {
|
||||
actions.add((AnAction)((Map.Entry)object).getKey());
|
||||
}
|
||||
if (actions.size() + settings.size() + toolWindows.size() > 15) break;
|
||||
}
|
||||
}
|
||||
|
||||
myProgressIndicator.checkCanceled();
|
||||
|
||||
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (myProgressIndicator.isCanceled()) return;
|
||||
if (toolWindows.size() > 0) {
|
||||
myTitleIndexes.toolWindows = myListModel.size();
|
||||
for (Object toolWindow : toolWindows) {
|
||||
myListModel.addElement(toolWindow);
|
||||
}
|
||||
}
|
||||
if (actions.size() > 0) {
|
||||
myTitleIndexes.actions = myListModel.size();
|
||||
for (Object action : actions) {
|
||||
myListModel.addElement(action);
|
||||
}
|
||||
}
|
||||
if (settings.size() > 0) {
|
||||
myTitleIndexes.settings = myListModel.size();
|
||||
for (Object setting : settings) {
|
||||
myListModel.addElement(setting);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void buildFiles(String pattern) {
|
||||
int filesCounter = 0;
|
||||
List<MatchResult> matches = collectResults(pattern, myFiles, myFileModel);
|
||||
final List<VirtualFile> files = new ArrayList<VirtualFile>();
|
||||
|
||||
for (MatchResult o : matches) {
|
||||
if (filesCounter > 15) break;
|
||||
Object[] objects = myFileModel.getElementsByName(o.elementName, false, pattern, myProgressIndicator);
|
||||
for (Object object : objects) {
|
||||
if (!myListModel.contains(object)) {
|
||||
if (object instanceof PsiFile) {
|
||||
object = ((PsiFile)object).getVirtualFile();
|
||||
}
|
||||
if (object instanceof VirtualFile &&
|
||||
!myAlreadyAddedFiles.contains((VirtualFile)object) &&
|
||||
!((VirtualFile)object).isDirectory()) {
|
||||
files.add((VirtualFile)object);
|
||||
filesCounter++;
|
||||
}
|
||||
}
|
||||
if (filesCounter > 15) break;
|
||||
}
|
||||
}
|
||||
|
||||
myProgressIndicator.checkCanceled();
|
||||
|
||||
if (files.size() > 0) {
|
||||
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!myProgressIndicator.isCanceled()) {
|
||||
myTitleIndexes.files = myListModel.size();
|
||||
for (Object file : files) {
|
||||
myListModel.addElement(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void buildClasses(String pattern) {
|
||||
int clsCounter = 0;
|
||||
List<MatchResult> matches = collectResults(pattern, myClasses, myClassModel);
|
||||
final List<Object> classes = new ArrayList<Object>();
|
||||
for (MatchResult matchResult : matches) {
|
||||
if (clsCounter > 15) break;
|
||||
|
||||
Object[] objects = myClassModel.getElementsByName(matchResult.elementName, false, pattern, myProgressIndicator);
|
||||
for (Object object : objects) {
|
||||
if (!myListModel.contains(object)) {
|
||||
if (object instanceof PsiElement) {
|
||||
VirtualFile file = PsiUtilCore.getVirtualFile((PsiElement)object);
|
||||
if (file != null) {
|
||||
if (myAlreadyAddedFiles.contains(file)) {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
myAlreadyAddedFiles.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
classes.add(object);
|
||||
clsCounter++;
|
||||
|
||||
if (clsCounter > 15) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
myProgressIndicator.checkCanceled();
|
||||
|
||||
if (classes.size() > 0) {
|
||||
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!myProgressIndicator.isCanceled()) {
|
||||
myTitleIndexes.classes = myListModel.size();
|
||||
for (Object cls : classes) {
|
||||
myListModel.addElement(cls);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void buildRecentFiles(String pattern) {
|
||||
final MinusculeMatcher matcher = new MinusculeMatcher(pattern, NameUtil.MatchingCaseSensitivity.NONE);
|
||||
final ArrayList<VirtualFile> files = new ArrayList<VirtualFile>();
|
||||
for (VirtualFile file : ArrayUtil.reverseArray(EditorHistoryManager.getInstance(project).getFiles())) {
|
||||
if (StringUtil.isEmptyOrSpaces(pattern) || matcher.matches(file.getName())) {
|
||||
files.add(file);
|
||||
}
|
||||
if (files.size() > 15) break;
|
||||
}
|
||||
|
||||
if (files.size() > 0) {
|
||||
myAlreadyAddedFiles.addAll(files);
|
||||
|
||||
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!myProgressIndicator.isCanceled()) {
|
||||
myTitleIndexes.recentFiles = myListModel.size();
|
||||
for (Object file : files) {
|
||||
myListModel.addElement(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void buildTopHit(String pattern) {
|
||||
final List<Object> elements = new ArrayList<Object>();
|
||||
final Consumer<Object> consumer = new Consumer<Object>() {
|
||||
@Override
|
||||
public void consume(Object o) {
|
||||
if (isSetting(o) || isVirtualFile(o) || isActionValue(o) || o instanceof PsiElement) {
|
||||
elements.add(o);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (SearchTopHitProvider provider : SearchTopHitProvider.EP_NAME.getExtensions()) {
|
||||
myProgressIndicator.checkCanceled();
|
||||
provider.consumeTopHits(pattern, consumer);
|
||||
}
|
||||
if (elements.size() > 0) {
|
||||
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!myProgressIndicator.isCanceled()) {
|
||||
myTitleIndexes.topHit = myListModel.size();
|
||||
for (Object element : elements) {
|
||||
myListModel.addElement(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void checkModelsUpToDate() {
|
||||
if (myClassModel == null) {
|
||||
myClassModel = new GotoClassModel2(project);
|
||||
myFileModel = new GotoFileModel(project);
|
||||
myActionModel = new GotoActionModel(project, myFocusComponent) {
|
||||
@Override
|
||||
public boolean matches(@NotNull String name, @NotNull String pattern) {
|
||||
final AnAction anAction = ActionManager.getInstance().getAction(name);
|
||||
if (anAction == null) return true;
|
||||
return NameUtil.buildMatcher("*" + pattern, NameUtil.MatchingCaseSensitivity.NONE).matches(
|
||||
anAction.getTemplatePresentation().getText());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object[] getElementsByName(String id, boolean checkBoxState, String pattern) {
|
||||
final HashMap<AnAction, String> map = new HashMap<AnAction, String>();
|
||||
final AnAction act = myActionManager.getAction(id);
|
||||
if (act != null) {
|
||||
map.put(act, myActionsMap.get(act));
|
||||
if (checkBoxState) {
|
||||
final Set<String> ids = ((ActionManagerImpl)myActionManager).getActionIds();
|
||||
for (AnAction action : map.keySet()) { //do not add already included actions
|
||||
ids.remove(getActionId(action));
|
||||
}
|
||||
if (ids.contains(id)) {
|
||||
final AnAction anAction = myActionManager.getAction(id);
|
||||
map.put(anAction, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
Object[] objects = map.entrySet().toArray(new Map.Entry[map.size()]);
|
||||
if (Comparing.strEqual(id, SETTINGS_KEY)) {
|
||||
final Set<String> words = myIndex.getProcessedWords(pattern);
|
||||
Set<OptionDescription> optionDescriptions = null;
|
||||
final String actionManagerName = myActionManager.getComponentName();
|
||||
for (String word : words) {
|
||||
final Set<OptionDescription> descriptions = ((SearchableOptionsRegistrarImpl)myIndex).getAcceptableDescriptions(word);
|
||||
if (descriptions != null) {
|
||||
for (Iterator<OptionDescription> iterator = descriptions.iterator(); iterator.hasNext(); ) {
|
||||
OptionDescription description = iterator.next();
|
||||
if (actionManagerName.equals(description.getPath())) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
if (!descriptions.isEmpty()) {
|
||||
if (optionDescriptions == null) {
|
||||
optionDescriptions = descriptions;
|
||||
}
|
||||
else {
|
||||
optionDescriptions.retainAll(descriptions);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
optionDescriptions = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (optionDescriptions != null && !optionDescriptions.isEmpty()) {
|
||||
Set<String> currentHits = new HashSet<String>();
|
||||
for (Iterator<OptionDescription> iterator = optionDescriptions.iterator(); iterator.hasNext(); ) {
|
||||
OptionDescription description = iterator.next();
|
||||
final String hit = description.getHit();
|
||||
if (hit == null || !currentHits.add(hit.trim())) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
final Object[] descriptions = optionDescriptions.toArray();
|
||||
Arrays.sort(descriptions);
|
||||
objects = ArrayUtil.mergeArrays(objects, descriptions);
|
||||
}
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
};
|
||||
myActionModel = createActionModel();
|
||||
myClasses = myClassModel.getNames(false);
|
||||
myFiles = myFileModel.getNames(false);
|
||||
myActions = myActionModel.getNames(true);
|
||||
@@ -642,152 +780,100 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
fillConfigurablesIds(null, new IdeConfigurablesGroup().getConfigurables());
|
||||
fillConfigurablesIds(null, new ProjectConfigurablesGroup(project).getConfigurables());
|
||||
}
|
||||
int clsCounter = 0;
|
||||
int filesCounter = 0;
|
||||
int actionsCount = 0;
|
||||
final AccessToken token = ApplicationManager.getApplication().acquireReadActionLock();
|
||||
try {
|
||||
List<MatchResult> classes = collectResults(pattern, myClasses, myClassModel);
|
||||
List<MatchResult> files = collectResults(pattern, myFiles, myFileModel);
|
||||
List<MatchResult> actions = collectResults(pattern, myActions, myActionModel);
|
||||
DefaultListModel listModel = new DefaultListModel();
|
||||
Set<VirtualFile> alreadyAddedFiles = new HashSet<VirtualFile>();
|
||||
}
|
||||
|
||||
for (MatchResult o : classes) {
|
||||
if (clsCounter > 15) break;
|
||||
private void buildModelFromRecentFiles() {
|
||||
buildRecentFiles("");
|
||||
}
|
||||
|
||||
Object[] objects = myClassModel.getElementsByName(o.elementName, false, pattern, myProgressIndicator);
|
||||
for (Object object : objects) {
|
||||
if (!listModel.contains(object)) {
|
||||
listModel.addElement(object);
|
||||
clsCounter++;
|
||||
if (object instanceof PsiElement) {
|
||||
VirtualFile file = PsiUtilCore.getVirtualFile((PsiElement)object);
|
||||
if (file != null) {
|
||||
alreadyAddedFiles.add(file);
|
||||
private GotoActionModel createActionModel() {
|
||||
return new GotoActionModel(project, myFocusComponent) {
|
||||
@Override
|
||||
public boolean matches(@NotNull String name, @NotNull String pattern) {
|
||||
final AnAction anAction = ActionManager.getInstance().getAction(name);
|
||||
if (anAction == null) return true;
|
||||
return NameUtil.buildMatcher("*" + pattern, NameUtil.MatchingCaseSensitivity.NONE).matches(
|
||||
anAction.getTemplatePresentation().getText());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object[] getElementsByName(String id, boolean checkBoxState, String pattern) {
|
||||
final HashMap<AnAction, String> map = new HashMap<AnAction, String>();
|
||||
final AnAction act = myActionManager.getAction(id);
|
||||
if (act != null) {
|
||||
map.put(act, myActionsMap.get(act));
|
||||
if (checkBoxState) {
|
||||
final Set<String> ids = ((ActionManagerImpl)myActionManager).getActionIds();
|
||||
for (AnAction action : map.keySet()) { //do not add already included actions
|
||||
ids.remove(getActionId(action));
|
||||
}
|
||||
if (ids.contains(id)) {
|
||||
final AnAction anAction = myActionManager.getAction(id);
|
||||
map.put(anAction, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
Object[] objects = map.entrySet().toArray(new Map.Entry[map.size()]);
|
||||
if (Comparing.strEqual(id, SETTINGS_KEY)) {
|
||||
final Set<String> words = myIndex.getProcessedWords(pattern);
|
||||
Set<OptionDescription> optionDescriptions = null;
|
||||
final String actionManagerName = myActionManager.getComponentName();
|
||||
for (String word : words) {
|
||||
final Set<OptionDescription> descriptions = ((SearchableOptionsRegistrarImpl)myIndex).getAcceptableDescriptions(word);
|
||||
if (descriptions != null) {
|
||||
for (Iterator<OptionDescription> iterator = descriptions.iterator(); iterator.hasNext(); ) {
|
||||
OptionDescription description = iterator.next();
|
||||
if (actionManagerName.equals(description.getPath())) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
if (!descriptions.isEmpty()) {
|
||||
if (optionDescriptions == null) {
|
||||
optionDescriptions = descriptions;
|
||||
}
|
||||
else {
|
||||
optionDescriptions.retainAll(descriptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (clsCounter > 15) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (MatchResult o : files) {
|
||||
if (filesCounter > 15) break;
|
||||
Object[] objects = myFileModel.getElementsByName(o.elementName, false, pattern, myProgressIndicator);
|
||||
for (Object object : objects) {
|
||||
if (!listModel.contains(object)) {
|
||||
if (object instanceof PsiFile) {
|
||||
object = ((PsiFile)object).getVirtualFile();
|
||||
}
|
||||
if (object instanceof VirtualFile && !alreadyAddedFiles.contains((VirtualFile)object) && !((VirtualFile)object).isDirectory()) {
|
||||
listModel.addElement(object);
|
||||
filesCounter++;
|
||||
else {
|
||||
optionDescriptions = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (filesCounter > 15) break;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<AnAction> addedActions = new HashSet<AnAction>();
|
||||
final List<Object> actionsAndSettings = new ArrayList<Object>();
|
||||
for (MatchResult o : actions) {
|
||||
//if (actionsCount > 15) break;
|
||||
myProgressIndicator.checkCanceled();
|
||||
Object[] objects = myActionModel.getElementsByName(o.elementName, true, pattern);
|
||||
for (Object object : objects) {
|
||||
myProgressIndicator.checkCanceled();
|
||||
if (isActionValue(object)) {
|
||||
final AnAction action = (AnAction)((Map.Entry)object).getKey();
|
||||
if (!addedActions.add(action)) continue;
|
||||
if (optionDescriptions != null && !optionDescriptions.isEmpty()) {
|
||||
Set<String> currentHits = new HashSet<String>();
|
||||
for (Iterator<OptionDescription> iterator = optionDescriptions.iterator(); iterator.hasNext(); ) {
|
||||
OptionDescription description = iterator.next();
|
||||
final String hit = description.getHit();
|
||||
if (hit == null || !currentHits.add(hit.trim())) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
final Object[] descriptions = optionDescriptions.toArray();
|
||||
Arrays.sort(descriptions);
|
||||
objects = ArrayUtil.mergeArrays(objects, descriptions);
|
||||
}
|
||||
actionsAndSettings.add(object);
|
||||
actionsCount++;
|
||||
//if (actionsCount > 15) break;
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
|
||||
Collections.sort(actionsAndSettings, new Comparator<Object>() {
|
||||
@Override
|
||||
public int compare(Object o1, Object o2) {
|
||||
final boolean b1 = isSetting(o1);
|
||||
final boolean b2 = isSetting(o2);
|
||||
final boolean t1 = isToolWindowAction(o1);
|
||||
final boolean t2 = isToolWindowAction(o2);
|
||||
return t1 == t2 ? b1 == b2 ? 0 : b1 ? 1 : -1 : t1 ? -1 : 1;
|
||||
}
|
||||
});
|
||||
|
||||
for (Object actionOrSetting : actionsAndSettings) {
|
||||
listModel.addElement(actionOrSetting);
|
||||
}
|
||||
|
||||
updateModel(listModel);
|
||||
}
|
||||
finally {
|
||||
token.finish();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("SSBasedInspection")
|
||||
private void updateModel(final DefaultListModel listModel) {
|
||||
private void updatePopup() {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myProgressIndicator.checkCanceled();
|
||||
final DataContext dataContext = DataManager.getInstance().getDataContext(myContextComponent);
|
||||
int settings = 0;
|
||||
int actions = 0;
|
||||
final DefaultListModel model = new DefaultListModel();
|
||||
myListModel.update();
|
||||
myList.revalidate();
|
||||
myList.repaint();
|
||||
|
||||
final String pattern = field.getText();
|
||||
final Consumer<Object> consumer = new Consumer<Object>() {
|
||||
@Override
|
||||
public void consume(Object o) {
|
||||
if (isSetting(o) || isVirtualFile(o) || isActionValue(o) || o instanceof PsiElement) {
|
||||
model.addElement(o);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (SearchTopHitProvider provider : SearchTopHitProvider.EP_NAME.getExtensions()) {
|
||||
provider.consumeTopHits(pattern, consumer);
|
||||
}
|
||||
|
||||
myTopHitsCount = model.size();
|
||||
|
||||
for (Object o : listModel.toArray()) {
|
||||
if (model.contains(o)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isSetting(o)) {
|
||||
if (settings < 15) {
|
||||
settings++;
|
||||
model.addElement(o);
|
||||
}
|
||||
} else if (isActionValue(o)) {
|
||||
if (actions < 15) {
|
||||
final AnAction action = (AnAction)((Map.Entry)o).getKey();
|
||||
|
||||
if (model.contains(action)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final AnActionEvent event = GotoActionModel.updateActionBeforeShow(action, dataContext);
|
||||
if (event.getPresentation().isEnabledAndVisible()) {
|
||||
actions++;
|
||||
model.addElement(action);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
model.addElement(o);
|
||||
}
|
||||
}
|
||||
myList.setModel(model);
|
||||
myRenderer.recalculateWidth();
|
||||
if (myPopup == null || !myPopup.isVisible()) {
|
||||
final ActionCallback callback = ListDelegationUtil.installKeyboardDelegation(field.getTextEditor(), myList);
|
||||
final PopupChooserBuilder builder = JBPopupFactory.getInstance().createListPopupBuilder(myList);
|
||||
final ComponentPopupBuilder builder = JBPopupFactory.getInstance().createComponentPopupBuilder(new JBScrollPane(myList), null);
|
||||
myPopup = builder.setRequestFocus(false).createPopup();
|
||||
Disposer.register(myPopup, new Disposable() {
|
||||
@Override
|
||||
@@ -802,26 +888,31 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
myPopup.cancel();
|
||||
}
|
||||
}, myPopup);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
myList.revalidate();
|
||||
myList.repaint();
|
||||
}
|
||||
ListScrollingUtil.ensureSelectionExists(myList);
|
||||
if (myList.getModel().getSize() == 0) {
|
||||
myPopup.cancel();
|
||||
} else {
|
||||
//rebuildList("");
|
||||
}
|
||||
else {
|
||||
final Dimension size = myList.getPreferredSize();
|
||||
Dimension sz = new Dimension(Math.max(field.getWidth(), size.width), size.height);
|
||||
if (sz.width > 800 || sz.height > 800) {
|
||||
final int extra = new JBScrollPane().getVerticalScrollBar().getWidth();
|
||||
sz = new Dimension(Math.min(800, Math.max(field.getWidth(), size.width + extra)), Math.min(800, size.height + extra));
|
||||
sz.width += 16;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
sz.height++;
|
||||
sz.height++;
|
||||
sz.width++;
|
||||
sz.width++;
|
||||
}
|
||||
sz.width = 600;
|
||||
sz.height = 800;
|
||||
myPopup.setSize(sz);
|
||||
final Point screen = field.getLocationOnScreen();
|
||||
final int x = screen.x + field.getWidth() - myPopup.getSize().width;
|
||||
@@ -909,4 +1000,83 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class TitleIndexes {
|
||||
int topHit;
|
||||
int recentFiles;
|
||||
int classes;
|
||||
int files;
|
||||
int actions;
|
||||
int settings;
|
||||
int toolWindows;
|
||||
|
||||
final String gotoClassTitle;
|
||||
final String gotoFileTitle;
|
||||
final String gotoActionTitle;
|
||||
final String gotoSettingsTitle;
|
||||
final String gotoRecentFilesTitle;
|
||||
static final String toolWindowsTitle = "Tool Windows";
|
||||
|
||||
TitleIndexes() {
|
||||
String gotoClass = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("GotoClass"));
|
||||
gotoClassTitle = StringUtil.isEmpty(gotoClass) ? "Classes" : "Classes (" + gotoClass + ")";
|
||||
String gotoFile = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("GotoFile"));
|
||||
gotoFileTitle = StringUtil.isEmpty(gotoFile) ? "Files" : "Files (" + gotoFile + ")";
|
||||
String gotoAction = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("GotoAction"));
|
||||
gotoActionTitle = StringUtil.isEmpty(gotoAction) ? "Actions" : "Actions (" + gotoAction + ")";
|
||||
String gotoSettings = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("ShowSettings"));
|
||||
gotoSettingsTitle = StringUtil.isEmpty(gotoAction) ? "Preferences" : "Preferences (" + gotoSettings + ")";
|
||||
String gotoRecentFiles = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("RecentFiles"));
|
||||
gotoRecentFilesTitle = StringUtil.isEmpty(gotoRecentFiles) ? "Recent Files" : "Recent Files (" + gotoRecentFiles + ")";
|
||||
}
|
||||
|
||||
String getTitle(int index) {
|
||||
if (index == topHit) return index == 0 ? "Top Hit" : "Top Hits";
|
||||
if (index == recentFiles) return gotoRecentFilesTitle;
|
||||
if (index == classes) return gotoClassTitle;
|
||||
if (index == files) return gotoFileTitle;
|
||||
if (index == toolWindows) return toolWindowsTitle;
|
||||
if (index == actions) return gotoActionTitle;
|
||||
if (index == settings) return gotoSettingsTitle;
|
||||
return null;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
topHit = -1;
|
||||
recentFiles = -1;
|
||||
classes = -1;
|
||||
files = -1;
|
||||
actions = -1;
|
||||
settings = -1;
|
||||
toolWindows = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SearchListModel extends DefaultListModel {
|
||||
Vector myDelegate;
|
||||
|
||||
private SearchListModel() {
|
||||
super();
|
||||
try {
|
||||
final Field field = DefaultListModel.class.getDeclaredField("delegate");
|
||||
field.setAccessible(true);
|
||||
myDelegate = (Vector)field.get(this);
|
||||
}
|
||||
catch (NoSuchFieldException e) {
|
||||
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addElement(Object obj) {
|
||||
myDelegate.add(obj);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
fireContentsChanged(this, 0, getSize() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user