diff --git a/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java b/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java index a02e77fc54cd..3e43258a75f0 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java @@ -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 myConfigurables = new HashMap(); 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 myAlreadyAddedFiles = new ArrayList(); 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 actions = new HashSet(); + final Set settings = new HashSet(); + final HashSet toolWindows = new HashSet(); + + List 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 matches = collectResults(pattern, myFiles, myFileModel); + final List files = new ArrayList(); + + 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 matches = collectResults(pattern, myClasses, myClassModel); + final List classes = new ArrayList(); + 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 files = new ArrayList(); + 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 elements = new ArrayList(); + final Consumer consumer = new Consumer() { + @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 map = new HashMap(); - final AnAction act = myActionManager.getAction(id); - if (act != null) { - map.put(act, myActionsMap.get(act)); - if (checkBoxState) { - final Set 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 words = myIndex.getProcessedWords(pattern); - Set optionDescriptions = null; - final String actionManagerName = myActionManager.getComponentName(); - for (String word : words) { - final Set descriptions = ((SearchableOptionsRegistrarImpl)myIndex).getAcceptableDescriptions(word); - if (descriptions != null) { - for (Iterator 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 currentHits = new HashSet(); - for (Iterator 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 classes = collectResults(pattern, myClasses, myClassModel); - List files = collectResults(pattern, myFiles, myFileModel); - List actions = collectResults(pattern, myActions, myActionModel); - DefaultListModel listModel = new DefaultListModel(); - Set alreadyAddedFiles = new HashSet(); + } - 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 map = new HashMap(); + final AnAction act = myActionManager.getAction(id); + if (act != null) { + map.put(act, myActionsMap.get(act)); + if (checkBoxState) { + final Set 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 words = myIndex.getProcessedWords(pattern); + Set optionDescriptions = null; + final String actionManagerName = myActionManager.getComponentName(); + for (String word : words) { + final Set descriptions = ((SearchableOptionsRegistrarImpl)myIndex).getAcceptableDescriptions(word); + if (descriptions != null) { + for (Iterator 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 addedActions = new HashSet(); - final List actionsAndSettings = new ArrayList(); - 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 currentHits = new HashSet(); + for (Iterator 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() { - @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 consumer = new Consumer() { - @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); + } + } }