summaryrefslogtreecommitdiff
path: root/platform/lang-api/src/com/intellij/ide/actions/searcheverywhere/SearchEverywhereContributor.java
blob: 50636c73f563fc4748adba8f7bb4eb76015e809f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.actions.searcheverywhere;

import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.DataKey;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.PossiblyDumbAware;
import com.intellij.util.Processor;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.event.InputEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Contributor which provides and processes items for <i>Search Everywhere</i> dialog.
 *
 * @author Konstantin Bulenkov
 * @author Mikhail Sokolov
 */
public interface SearchEverywhereContributor<Item> extends PossiblyDumbAware, Disposable {

  ExtensionPointName<SearchEverywhereContributorFactory<?>> EP_NAME = ExtensionPointName.create("com.intellij.searchEverywhereContributor");

  /**
   * Unique ID of provider. Usually {@link Class#getSimpleName()} of the implementing class is used.
   */
  @NotNull
  String getSearchProviderId();

  /**
   * Display name for the group defined by this contributor. This name is shown in:
   * <ul>
   *   <li>Contributor tab if there is separate tab for this contributor</li>
   *   <li>Group separator in results list when splitting by groups is enabled in results</li>
   * </ui>
   */
  @NotNull
  @Nls
  String getGroupName();

  /**
   * Full group name for contributor. Used in filters, empty results placeholders, etc.
   * Usually equals to {@code getGroupName}.
   */
  @NotNull
  @Nls
  default String getFullGroupName() {
    return getGroupName();
  }

  /**
   * <p>Defines weight for sorting contributors (<b>not elements</b>).
   * This weight is used for example for ordering groups in results list when splitting by groups is enabled.</p>
   *
   * <p>Please do not use this method to set found items weights. For this purposes look at {@link SearchEverywhereContributor#getElementPriority(Object, String)}
   * and {@link WeightedSearchEverywhereContributor#fetchWeightedElements(String, ProgressIndicator, Processor)} methods.</p>
   */
  int getSortWeight();

  /**
   * Defines if results found by this contributor can be shown in <i>Find</i> toolwindow.
   */
  boolean showInFindResults();

  /**
   * <p>Defines if separate tab should be shown for this contributor in <i>Search Everywhere</i> dialog.</p>
   * <p>Please do not override this method unless absolutely necessary. Too many separate tabs make the <i>Search Everywhere</i>
   * dialog unusable.</p>
   *
   * @deprecated method is not used since 2022.1
   */
  @Deprecated(since = "2022.1")
  default boolean isShownInSeparateTab() {
    return false;
  }

  /**
   * Return priority of found elements. Priority is used to sort items in results list.
   *
   * @deprecated method is left for backward compatibility only. If you want to consider elements weight in your search contributor
   * please use {@link WeightedSearchEverywhereContributor#fetchWeightedElements(String, ProgressIndicator, Processor)} method for fetching
   * this elements.
   */
  @Deprecated
  default int getElementPriority(@NotNull Item element, @NotNull String searchPattern) {
    return 0;
  }

  /**
   * <p>Returns list of commands supported by this contributor.</p>
   * <p>Usually commands are used for additional elements filtering, but you can implement any behavior for your commands.
   * {@link SearchEverywhereCommandInfo} doesn't contain any behavior details. All commands should be processed in
   * {@link SearchEverywhereContributor#fetchElements(String, ProgressIndicator, Processor)} method.</p>
   */
  @NotNull
  default List<SearchEverywhereCommandInfo> getSupportedCommands() {
    return Collections.emptyList();
  }

  /**
   * Return an advertisement text which can be shown in a right part of search field.
   */
  @Nullable
  @Nls
  default String getAdvertisement() { return null; }

  @NotNull
  default List<AnAction> getActions(@NotNull Runnable onChanged) {
    return Collections.emptyList();
  }

  /**
   * <p>Performs searching process. All found items will be passed to consumer.</p>
   * <p>Searching is performed until any of following events happens:
   * <ul>
   *   <li>all items which match {@code pattern} are found</li>
   *   <li>{@code progressIndicator} is cancelled</li>
   *   <li>{@code consumer} returns {@code false} for any item</li>
   * </ul></p>
   * @param pattern searching pattern used for matching
   * @param progressIndicator {@link ProgressIndicator} which can be used for tracking or cancelling searching process
   * @param consumer items {@link Processor} which will receive any found item. When false is returned by consumer this contributor stops
   *                 searching process
   */
  void fetchElements(@NotNull String pattern,
                     @NotNull ProgressIndicator progressIndicator,
                     @NotNull Processor<? super Item> consumer);

  /**
   * <p>Search for pattern matches with elements limit. Found items will be returned as {@link ContributorSearchResult} structure.</p>
   * <p>Searching is performed until any of following events happens:
   * <ul>
   *   <li>all items which match {@code pattern} are found</li>
   *   <li>{@code progressIndicator} is cancelled</li>
   *   <li>{@code elementsLimit} is reached</li>
   * </ul></p>
   *
   * @param pattern searching pattern used for matching
   * @param progressIndicator {@link ProgressIndicator} which can be used for tracking or cancelling searching process
   * @param elementsLimit maximal found items number
   */
  @NotNull
  default ContributorSearchResult<Item> search(@NotNull String pattern,
                                               @NotNull ProgressIndicator progressIndicator,
                                               int elementsLimit) {
    ContributorSearchResult.Builder<Item> builder = ContributorSearchResult.builder();
    fetchElements(pattern, progressIndicator, element -> {
      if (elementsLimit < 0 || builder.itemsCount() < elementsLimit) {
        builder.addItem(element);
        return true;
      }
      else {
        builder.setHasMore(true);
        return false;
      }
    });

    return builder.build();
  }

  /**
   * <p>Search for all pattern matches. Found items will be returned as {@link List}.</p>
   * <p>Searching is performed until any of following events happens:
   * <ul>
   *   <li>all items which match {@code pattern} are found</li>
   *   <li>{@code progressIndicator} is cancelled</li>
   * </ul></p>
   *
   * @param pattern searching pattern used for matching
   * @param progressIndicator {@link ProgressIndicator} which can be used for tracking or cancelling searching process
   */
  @NotNull
  default List<Item> search(@NotNull String pattern,
                            @NotNull ProgressIndicator progressIndicator) {
    List<Item> res = new ArrayList<>();
    fetchElements(pattern, progressIndicator, o -> res.add(o));
    return res;
  }

  /**
   * <p>Process selected item. Method called when user choose item from results list.</p>
   * <p>Returned result defines if dialog should be closed after processing element.</p>
   *
   * @param selected item chosen by user
   * @param modifiers keyboard modifiers (see {@link InputEvent#getModifiers()})
   * @param searchText text from search field
   *
   * @return {@code true} if dialog should be closed after element processing. {@code false} to let dialog be shown after processing
   */
  boolean processSelectedItem(@NotNull Item selected, int modifiers, @NotNull String searchText);

  /**
   * Creates {@link ListCellRenderer} for found items.
   */
  @NotNull
  ListCellRenderer<? super Item> getElementsRenderer();

  /**
   * Get context data for selected element.
   * @param element selected item
   * @param dataId {@link DataKey} ID
   *
   * @see DataKey
   * @see DataContext
   */
  @Nullable
  Object getDataForItem(@NotNull Item element, @NotNull String dataId);

  /**
   * Filter out special symbols from pattern before search.
   */
  @NotNull
  default String filterControlSymbols(@NotNull String pattern) {
    return pattern;
  }

  /**
   * <p>Defines if multi-selection should be supported in results list for items found by this contributor.</p>
   * <p>For example few classes can be simultaneously open from <i>Search Everywhere</i> (in different tabs). So classes contributor
   * supports multi-selection, but actions contributor doesn't since only one action can be performed at same time.</p>
   */
  default boolean isMultiSelectionSupported() {
    return false;
  }

  /**
   * Defines if this contributor allowed to call while indexing in process.
   */
  @Override
  default boolean isDumbAware() {
    return true;
  }

  /**
   * Defines if contributor should try to perform search with empty search pattern.
   */
  default boolean isEmptyPatternSupported() {
    return false;
  }

  @Override
  default void dispose() {}
}