2 * Copyright 2000-2016 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.ide.util.gotoByName;
18 import com.intellij.ide.DataManager;
19 import com.intellij.ide.SearchTopHitProvider;
20 import com.intellij.ide.actions.ApplyIntentionAction;
21 import com.intellij.ide.ui.OptionsTopHitProvider;
22 import com.intellij.ide.ui.search.ActionFromOptionDescriptorProvider;
23 import com.intellij.ide.ui.search.OptionDescription;
24 import com.intellij.ide.ui.search.SearchableOptionsRegistrar;
25 import com.intellij.ide.ui.search.SearchableOptionsRegistrarImpl;
26 import com.intellij.openapi.actionSystem.*;
27 import com.intellij.openapi.actionSystem.impl.ActionManagerImpl;
28 import com.intellij.openapi.progress.ProgressIndicator;
29 import com.intellij.openapi.progress.ProgressManager;
30 import com.intellij.openapi.project.Project;
31 import com.intellij.openapi.util.text.StringUtil;
32 import com.intellij.util.CollectConsumer;
33 import com.intellij.util.Function;
34 import com.intellij.util.Processor;
35 import com.intellij.util.containers.ContainerUtil;
36 import org.jetbrains.annotations.NotNull;
37 import org.jetbrains.annotations.Nullable;
41 import static com.intellij.ide.util.gotoByName.GotoActionModel.*;
46 @SuppressWarnings("TestOnlyProblems")
47 public class GotoActionItemProvider implements ChooseByNameItemProvider {
48 private final ActionManager myActionManager = ActionManager.getInstance();
49 protected final SearchableOptionsRegistrar myIndex = SearchableOptionsRegistrar.getInstance();
50 private final GotoActionModel myModel;
52 public GotoActionItemProvider(GotoActionModel model) {
58 public List<String> filterNames(@NotNull ChooseByNameBase base, @NotNull String[] names, @NotNull String pattern) {
59 return Collections.emptyList(); // no common prefix insertion in goto action
63 public boolean filterElements(@NotNull final ChooseByNameBase base,
64 @NotNull final String pattern,
66 @NotNull ProgressIndicator cancelled,
67 @NotNull final Processor<Object> consumer) {
68 return filterElements(pattern, everywhere, new Processor<MatchedValue>() {
70 public boolean process(MatchedValue value) {
71 return consumer.process(value);
76 public boolean filterElements(String pattern, boolean everywhere, Processor<MatchedValue> consumer) {
77 DataContext dataContext = DataManager.getInstance().getDataContext(myModel.getContextComponent());
79 if (!processAbbreviations(pattern, consumer, dataContext)) return false;
80 if (!processIntentions(pattern, consumer, dataContext)) return false;
81 if (!processActions(pattern, everywhere, consumer, dataContext)) return false;
82 if (!processTopHits(pattern, consumer, dataContext)) return false;
83 if (!processOptions(pattern, consumer, dataContext)) return false;
88 private boolean processAbbreviations(final String pattern, Processor<MatchedValue> consumer, DataContext context) {
89 List<String> actions = AbbreviationManager.getInstance().findActions(pattern);
90 if (actions.isEmpty()) return true;
91 List<ActionWrapper> wrappers = ContainerUtil.newArrayListWithCapacity(actions.size());
92 for (String actionId : actions) {
93 AnAction action = myActionManager.getAction(actionId);
94 wrappers.add(new ActionWrapper(action, myModel.myActionGroups.get(action), MatchMode.NAME, context));
96 return ContainerUtil.process(ContainerUtil.map(wrappers, new Function<ActionWrapper, MatchedValue>() {
98 public MatchedValue fun(@NotNull ActionWrapper w) {
99 return new MatchedValue(w, pattern) {
102 public String getValueText() {
110 private static boolean processTopHits(String pattern, Processor<MatchedValue> consumer, DataContext dataContext) {
111 Project project = CommonDataKeys.PROJECT.getData(dataContext);
112 final CollectConsumer<Object> collector = new CollectConsumer<Object>();
113 for (SearchTopHitProvider provider : SearchTopHitProvider.EP_NAME.getExtensions()) {
114 if (provider instanceof OptionsTopHitProvider.CoveredByToggleActions) continue;
115 if (provider instanceof OptionsTopHitProvider && !((OptionsTopHitProvider)provider).isEnabled(project)) continue;
116 if (provider instanceof OptionsTopHitProvider && !StringUtil.startsWith(pattern, "#")) {
117 String prefix = "#" + ((OptionsTopHitProvider)provider).getId() + " ";
118 provider.consumeTopHits(prefix + pattern, collector, project);
120 provider.consumeTopHits(pattern, collector, project);
122 final Collection<Object> result = collector.getResult();
123 final List<Comparable> c = new ArrayList<Comparable>();
124 for (Object o : result) {
125 if (o instanceof Comparable) {
126 c.add((Comparable)o);
129 return processItems(pattern, c, consumer);
132 private boolean processOptions(String pattern, Processor<MatchedValue> consumer, DataContext dataContext) {
133 List<Comparable> options = ContainerUtil.newArrayList();
134 final Set<String> words = myIndex.getProcessedWords(pattern);
135 Set<OptionDescription> optionDescriptions = null;
136 final String actionManagerName = myActionManager.getComponentName();
137 for (String word : words) {
138 final Set<OptionDescription> descriptions = ((SearchableOptionsRegistrarImpl)myIndex).getAcceptableDescriptions(word);
139 if (descriptions != null) {
140 for (Iterator<OptionDescription> iterator = descriptions.iterator(); iterator.hasNext(); ) {
141 OptionDescription description = iterator.next();
142 if (actionManagerName.equals(description.getPath())) {
146 if (!descriptions.isEmpty()) {
147 if (optionDescriptions == null) {
148 optionDescriptions = descriptions;
151 optionDescriptions.retainAll(descriptions);
155 optionDescriptions = null;
159 if (optionDescriptions != null && !optionDescriptions.isEmpty()) {
160 Set<String> currentHits = new HashSet<String>();
161 for (Iterator<OptionDescription> iterator = optionDescriptions.iterator(); iterator.hasNext(); ) {
162 OptionDescription description = iterator.next();
163 final String hit = description.getHit();
164 if (hit == null || !currentHits.add(hit.trim())) {
168 for (OptionDescription description : optionDescriptions) {
169 for (ActionFromOptionDescriptorProvider converter : ActionFromOptionDescriptorProvider.EP.getExtensions()) {
170 AnAction action = converter.provide(description);
171 if (action != null) options.add(new ActionWrapper(action, null, MatchMode.NAME, dataContext));
172 options.add(description);
176 return processItems(pattern, options, consumer);
179 private boolean processActions(String pattern, boolean everywhere, Processor<MatchedValue> consumer, DataContext dataContext) {
180 List<AnAction> actions = ContainerUtil.newArrayList();
182 for (String id : ((ActionManagerImpl)myActionManager).getActionIds()) {
183 ProgressManager.checkCanceled();
184 ContainerUtil.addIfNotNull(actions, myActionManager.getAction(id));
187 actions.addAll(myModel.myActionGroups.keySet());
190 List<ActionWrapper> actionWrappers = ContainerUtil.newArrayList();
191 for (AnAction action : actions) {
192 ProgressManager.checkCanceled();
193 MatchMode mode = myModel.actionMatches(pattern, action);
194 if (mode != MatchMode.NONE) {
195 actionWrappers.add(new ActionWrapper(action, myModel.myActionGroups.get(action), mode, dataContext));
198 return processItems(pattern, actionWrappers, consumer);
201 private boolean processIntentions(String pattern, Processor<MatchedValue> consumer, DataContext dataContext) {
202 List<ActionWrapper> intentions = ContainerUtil.newArrayList();
203 for (String intentionText : myModel.myIntentions.keySet()) {
204 final ApplyIntentionAction intentionAction = myModel.myIntentions.get(intentionText);
205 if (myModel.actionMatches(pattern, intentionAction) != MatchMode.NONE) {
206 intentions.add(new ActionWrapper(intentionAction, intentionText, MatchMode.INTENTION, dataContext));
209 return processItems(pattern, intentions, consumer);
212 private static boolean processItems(final String pattern, Collection<? extends Comparable> items, Processor<MatchedValue> consumer) {
213 List<MatchedValue> matched = ContainerUtil.map(items, new Function<Comparable, MatchedValue>() {
215 public MatchedValue fun(Comparable comparable) {
216 return new MatchedValue(comparable, pattern);
219 Collections.sort(matched);
220 return ContainerUtil.process(matched, consumer);