1 /* ide-completion-provider.c
2 *
3 * Copyright 2018-2019 Christian Hergert <chergert@redhat.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: GPL-3.0-or-later
19 */
20
21 #define G_LOG_DOMAIN "ide-completion-provider"
22
23 #include "config.h"
24
25 #include "ide-completion-context.h"
26 #include "ide-completion-private.h"
27 #include "ide-completion-proposal.h"
28 #include "ide-completion-provider.h"
29 #include "ide-completion-list-box-row.h"
30
G_DEFINE_INTERFACE(IdeCompletionProvider,ide_completion_provider,G_TYPE_OBJECT)31 G_DEFINE_INTERFACE (IdeCompletionProvider, ide_completion_provider, G_TYPE_OBJECT)
32
33 static void
34 ide_completion_provider_default_init (IdeCompletionProviderInterface *iface)
35 {
36 }
37
38 /**
39 * ide_completion_provider_get_icon:
40 * @self: an #IdeCompletionProvider
41 *
42 * Gets the #GIcon to represent this provider. This may be used in UI
43 * to allow the user to filter the results to only those of this
44 * completion provider.
45 *
46 * Returns: (transfer full) (nullable): a #GIcon or %NULL.
47 *
48 * Since: 3.32
49 */
50 GIcon *
ide_completion_provider_get_icon(IdeCompletionProvider * self)51 ide_completion_provider_get_icon (IdeCompletionProvider *self)
52 {
53 g_return_val_if_fail (IDE_IS_COMPLETION_PROVIDER (self), NULL);
54
55 if (IDE_COMPLETION_PROVIDER_GET_IFACE (self)->get_icon)
56 return IDE_COMPLETION_PROVIDER_GET_IFACE (self)->get_icon (self);
57
58 return NULL;
59 }
60
61 /**
62 * ide_completion_provider_get_priority:
63 * @self: an #IdeCompletionProvider
64 * @context: an #IdeCompletionContext
65 *
66 * Gets the priority for the completion provider.
67 *
68 * This value is used to group all of the providers proposals together
69 * when displayed, with relation to other providers.
70 *
71 * The @context is provided as some providers may want to lower their
72 * priority based on the position of the completion.
73 *
74 * Returns: an integer specific to the provider
75 *
76 * Since: 3.32
77 */
78 gint
ide_completion_provider_get_priority(IdeCompletionProvider * self,IdeCompletionContext * context)79 ide_completion_provider_get_priority (IdeCompletionProvider *self,
80 IdeCompletionContext *context)
81 {
82 g_return_val_if_fail (IDE_IS_COMPLETION_PROVIDER (self), 0);
83 g_return_val_if_fail (IDE_IS_COMPLETION_CONTEXT (context), 0);
84
85 if (IDE_COMPLETION_PROVIDER_GET_IFACE (self)->get_priority)
86 return IDE_COMPLETION_PROVIDER_GET_IFACE (self)->get_priority (self, context);
87
88 return 0;
89 }
90
91 /**
92 * ide_completion_provider_get_title:
93 * @self: an #IdeCompletionProvider
94 *
95 * Gets the title for the provider. This may be used in UI to give
96 * the user context about the type of results that are displayed.
97 *
98 * Returns: (transfer full) (nullable): a string or %NULL
99 *
100 * Since: 3.32
101 */
102 gchar *
ide_completion_provider_get_title(IdeCompletionProvider * self)103 ide_completion_provider_get_title (IdeCompletionProvider *self)
104 {
105 g_return_val_if_fail (IDE_IS_COMPLETION_PROVIDER (self), NULL);
106
107 if (IDE_COMPLETION_PROVIDER_GET_IFACE (self)->get_title)
108 return IDE_COMPLETION_PROVIDER_GET_IFACE (self)->get_title (self);
109
110 return NULL;
111 }
112
113 /**
114 * ide_completion_provider_populate_async:
115 * @self: an #IdeCompletionProvider
116 * @context: the completion context
117 * @cancellable: (nullable): a #GCancellable, or %NULL
118 * @callback: (nullable) (scope async) (closure user_data): a #GAsyncReadyCallback
119 * or %NULL. Called when the provider has completed loading proposals.
120 * @user_data: closure data for @callback
121 *
122 * Asynchronously requests the provider populate the contents.
123 *
124 * For completion providers that can provide intermediate results immediately,
125 * use ide_completion_context_set_proposals_for_provider() to notify of results
126 * while the async operation is in progress.
127 *
128 * Since: 3.32
129 */
130 void
ide_completion_provider_populate_async(IdeCompletionProvider * self,IdeCompletionContext * context,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)131 ide_completion_provider_populate_async (IdeCompletionProvider *self,
132 IdeCompletionContext *context,
133 GCancellable *cancellable,
134 GAsyncReadyCallback callback,
135 gpointer user_data)
136 {
137 g_return_if_fail (IDE_IS_COMPLETION_PROVIDER (self));
138 g_return_if_fail (IDE_IS_COMPLETION_CONTEXT (context));
139 g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
140
141 IDE_COMPLETION_PROVIDER_GET_IFACE (self)->populate_async (self, context, cancellable, callback, user_data);
142 }
143
144 /**
145 * ide_completion_provider_populate_finish:
146 * @self: an #IdeCompletionProvider
147 * @result: a #GAsyncResult provided to callback
148 * @error: a location for a GError, or %NULL
149 *
150 * Returns: (transfer full): a #GListModel of #IdeCompletionProposal
151 *
152 * Since: 3.32
153 */
154 GListModel *
ide_completion_provider_populate_finish(IdeCompletionProvider * self,GAsyncResult * result,GError ** error)155 ide_completion_provider_populate_finish (IdeCompletionProvider *self,
156 GAsyncResult *result,
157 GError **error)
158 {
159 g_return_val_if_fail (IDE_IS_COMPLETION_PROVIDER (self), NULL);
160 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
161
162 return IDE_COMPLETION_PROVIDER_GET_IFACE (self)->populate_finish (self, result, error);
163 }
164
165 void
ide_completion_provider_activate_poposal(IdeCompletionProvider * self,IdeCompletionContext * context,IdeCompletionProposal * proposal,const GdkEventKey * key)166 ide_completion_provider_activate_poposal (IdeCompletionProvider *self,
167 IdeCompletionContext *context,
168 IdeCompletionProposal *proposal,
169 const GdkEventKey *key)
170 {
171 g_return_if_fail (IDE_IS_COMPLETION_PROVIDER (self));
172 g_return_if_fail (IDE_IS_COMPLETION_CONTEXT (context));
173 g_return_if_fail (IDE_IS_COMPLETION_PROPOSAL (proposal));
174
175 if (IDE_COMPLETION_PROVIDER_GET_IFACE (self)->activate_proposal)
176 IDE_COMPLETION_PROVIDER_GET_IFACE (self)->activate_proposal (self, context, proposal, key);
177 else
178 g_critical ("%s does not implement activate_proposal()!", G_OBJECT_TYPE_NAME (self));
179 }
180
181 /**
182 * ide_completion_provider_refilter:
183 * @self: an #IdeCompletionProvider
184 * @context: an #IdeCompletionContext
185 * @proposals: a #GListModel of results previously provided to the context
186 *
187 * This requests that the completion provider refilter the results based on
188 * changes to the #IdeCompletionContext, such as additional text typed by the
189 * user. If the provider can refine the results, then the provider should do
190 * so and return %TRUE.
191 *
192 * Otherwise, %FALSE is returned and the context will request a new set of
193 * completion results.
194 *
195 * Returns: %TRUE if refiltered; otherwise %FALSE
196 *
197 * Since: 3.32
198 */
199 gboolean
ide_completion_provider_refilter(IdeCompletionProvider * self,IdeCompletionContext * context,GListModel * proposals)200 ide_completion_provider_refilter (IdeCompletionProvider *self,
201 IdeCompletionContext *context,
202 GListModel *proposals)
203 {
204 g_return_val_if_fail (IDE_IS_COMPLETION_PROVIDER (self), FALSE);
205 g_return_val_if_fail (IDE_IS_COMPLETION_CONTEXT (context), FALSE);
206 g_return_val_if_fail (G_IS_LIST_MODEL (proposals), FALSE);
207
208 if (IDE_COMPLETION_PROVIDER_GET_IFACE (self)->refilter)
209 return IDE_COMPLETION_PROVIDER_GET_IFACE (self)->refilter (self, context, proposals);
210
211 return FALSE;
212 }
213
214 /**
215 * ide_completion_provider_is_trigger:
216 * @self: an #IdeCompletionProvider
217 * @iter: the current insertion point
218 * @ch: the character that was just inserted
219 *
220 * Completion providers may want to trigger that the completion window is
221 * displayed upon insertion of a particular character. For example, a C
222 * indenter might want to trigger after -> or . is inserted.
223 *
224 * @ch is set to the character that was just inserted. If you need something
225 * more complex, copy @iter and move it backwards twice to check the character
226 * previous to @ch.
227 *
228 * Returns: %TRUE to request that the completion window is displayed.
229 *
230 * Since: 3.32
231 */
232 gboolean
ide_completion_provider_is_trigger(IdeCompletionProvider * self,const GtkTextIter * iter,gunichar ch)233 ide_completion_provider_is_trigger (IdeCompletionProvider *self,
234 const GtkTextIter *iter,
235 gunichar ch)
236 {
237 g_return_val_if_fail (IDE_IS_COMPLETION_PROVIDER (self), FALSE);
238 g_return_val_if_fail (iter != NULL, FALSE);
239
240 if (IDE_COMPLETION_PROVIDER_GET_IFACE (self)->is_trigger)
241 return IDE_COMPLETION_PROVIDER_GET_IFACE (self)->is_trigger (self, iter, ch);
242
243 return FALSE;
244 }
245
246 /**
247 * ide_completion_provider_key_activates:
248 * @self: a #IdeCompletionProvider
249 * @proposal: an #IdeCompletionProposal created by the provider
250 * @key: the #GdkEventKey for the current keyboard event
251 *
252 * This function is called to ask the provider if the key-press event should
253 * force activation of the proposal. This is useful for languages where you
254 * might want to activate the completion from a language-specific character.
255 *
256 * For example, in C, you might want to use period (.) to activate the
257 * completion and insert either (.) or (->) based on the type.
258 *
259 * Returns: %TRUE if the proposal should be activated.
260 *
261 * Since: 3.32
262 */
263 gboolean
ide_completion_provider_key_activates(IdeCompletionProvider * self,IdeCompletionProposal * proposal,const GdkEventKey * key)264 ide_completion_provider_key_activates (IdeCompletionProvider *self,
265 IdeCompletionProposal *proposal,
266 const GdkEventKey *key)
267 {
268 g_return_val_if_fail (IDE_IS_COMPLETION_PROVIDER (self), FALSE);
269 g_return_val_if_fail (IDE_IS_COMPLETION_PROPOSAL (proposal), FALSE);
270 g_return_val_if_fail (key != NULL, FALSE);
271
272 if (IDE_COMPLETION_PROVIDER_GET_IFACE (self)->key_activates)
273 return IDE_COMPLETION_PROVIDER_GET_IFACE (self)->key_activates (self, proposal, key);
274
275 return FALSE;
276 }
277
278 void
_ide_completion_provider_load(IdeCompletionProvider * self,IdeContext * context)279 _ide_completion_provider_load (IdeCompletionProvider *self,
280 IdeContext *context)
281 {
282 g_return_if_fail (IDE_IS_COMPLETION_PROVIDER (self));
283 g_return_if_fail (IDE_IS_CONTEXT (context));
284
285 if (IDE_COMPLETION_PROVIDER_GET_IFACE (self)->load)
286 IDE_COMPLETION_PROVIDER_GET_IFACE (self)->load (self, context);
287 }
288
289 /**
290 * ide_completion_provider_display_proposal:
291 * @self: a #IdeCompletionProvider
292 * @row: an #IdeCompletionListBoxRow
293 * @context: an #IdeCompletionContext
294 * @typed_text: (nullable): the typed text for the proposal
295 * @proposal: an #IdeCompletionProposal
296 *
297 * Requests that the provider update @row with values from @proposal.
298 *
299 * The design rational about having this operation part of the
300 * #IdeCompletionProvider interface (as opposed to the #IdeCompletionProposal
301 * interface) is that it allows for some optimizations and code simplification
302 * on behalf of completion providers.
303 *
304 * Since: 3.32
305 */
306 void
ide_completion_provider_display_proposal(IdeCompletionProvider * self,IdeCompletionListBoxRow * row,IdeCompletionContext * context,const gchar * typed_text,IdeCompletionProposal * proposal)307 ide_completion_provider_display_proposal (IdeCompletionProvider *self,
308 IdeCompletionListBoxRow *row,
309 IdeCompletionContext *context,
310 const gchar *typed_text,
311 IdeCompletionProposal *proposal)
312 {
313 g_return_if_fail (IDE_IS_COMPLETION_PROVIDER (self));
314 g_return_if_fail (IDE_IS_COMPLETION_LIST_BOX_ROW (row));
315 g_return_if_fail (IDE_IS_COMPLETION_CONTEXT (context));
316 g_return_if_fail (IDE_IS_COMPLETION_PROPOSAL (proposal));
317
318 if (typed_text == NULL)
319 typed_text = "";
320
321 if (IDE_COMPLETION_PROVIDER_GET_IFACE (self)->display_proposal)
322 IDE_COMPLETION_PROVIDER_GET_IFACE (self)->display_proposal (self, row, context, typed_text, proposal);
323 }
324
325 /**
326 * ide_completion_provider_get_comment:
327 * @self: an #IdeCompletionProvider
328 * @proposal: an #IdeCompletionProposal
329 *
330 * If the completion proposal has a comment, the provider should return
331 * a newly allocated string containing it.
332 *
333 * This is displayed at the bottom of the completion window.
334 *
335 * Returns: (transfer full) (nullable): A new string or %NULL
336 *
337 * Since: 3.32
338 */
339 gchar *
ide_completion_provider_get_comment(IdeCompletionProvider * self,IdeCompletionProposal * proposal)340 ide_completion_provider_get_comment (IdeCompletionProvider *self,
341 IdeCompletionProposal *proposal)
342 {
343 g_return_val_if_fail (IDE_IS_COMPLETION_PROVIDER (self), NULL);
344 g_return_val_if_fail (IDE_IS_COMPLETION_PROPOSAL (proposal), NULL);
345
346 if (IDE_COMPLETION_PROVIDER_GET_IFACE (self)->get_comment)
347 return IDE_COMPLETION_PROVIDER_GET_IFACE (self)->get_comment (self, proposal);
348
349 return NULL;
350 }
351