1 /* ide-code-index-entries.c
2  *
3  * Copyright 2017-2019 Christian Hergert <chergert@redhat.com>
4  * Copyright 2017 Anoop Chandu <anoopchandu96@gmail.com>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  * SPDX-License-Identifier: GPL-3.0-or-later
20  */
21 
22 #define G_LOG_DOMAIN "ide-code-index-entries"
23 
24 #include "config.h"
25 
26 #include <libide-threading.h>
27 
28 #include "ide-code-index-entry.h"
29 #include "ide-code-index-entries.h"
30 
G_DEFINE_INTERFACE(IdeCodeIndexEntries,ide_code_index_entries,G_TYPE_OBJECT)31 G_DEFINE_INTERFACE (IdeCodeIndexEntries, ide_code_index_entries, G_TYPE_OBJECT)
32 
33 static void
34 ide_code_index_entries_real_next_entries_async (IdeCodeIndexEntries *self,
35                                                 GCancellable        *cancellable,
36                                                 GAsyncReadyCallback  callback,
37                                                 gpointer             user_data)
38 {
39   g_autoptr(IdeTask) task = NULL;
40   g_autoptr(GPtrArray) ret = NULL;
41   IdeCodeIndexEntry *entry;
42 
43   g_assert (IDE_IS_MAIN_THREAD ());
44   g_assert (IDE_IS_CODE_INDEX_ENTRIES (self));
45   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
46 
47   task = ide_task_new (self, cancellable, callback, user_data);
48   ide_task_set_source_tag (task, ide_code_index_entries_real_next_entries_async);
49   ide_task_set_priority (task, G_PRIORITY_LOW);
50   ide_task_set_kind (task, IDE_TASK_KIND_INDEXER);
51 
52   ret = g_ptr_array_new_with_free_func ((GDestroyNotify)ide_code_index_entry_free);
53 
54   while ((entry = ide_code_index_entries_get_next_entry (self)))
55     g_ptr_array_add (ret, g_steal_pointer (&entry));
56 
57   ide_task_return_pointer (task, g_steal_pointer (&ret), g_ptr_array_unref);
58 }
59 
60 static GPtrArray *
ide_code_index_entries_real_next_entries_finish(IdeCodeIndexEntries * self,GAsyncResult * result,GError ** error)61 ide_code_index_entries_real_next_entries_finish (IdeCodeIndexEntries  *self,
62                                                  GAsyncResult         *result,
63                                                  GError              **error)
64 {
65   GPtrArray *ret;
66 
67   g_assert (IDE_IS_CODE_INDEX_ENTRIES (self));
68   g_assert (IDE_IS_TASK (result));
69 
70   ret = ide_task_propagate_pointer (IDE_TASK (result), error);
71 
72   return IDE_PTR_ARRAY_STEAL_FULL (&ret);
73 }
74 
75 static IdeCodeIndexEntry *
ide_code_index_entries_real_get_next_entry(IdeCodeIndexEntries * self)76 ide_code_index_entries_real_get_next_entry (IdeCodeIndexEntries *self)
77 {
78   return NULL;
79 }
80 
81 static void
ide_code_index_entries_default_init(IdeCodeIndexEntriesInterface * iface)82 ide_code_index_entries_default_init (IdeCodeIndexEntriesInterface *iface)
83 {
84   iface->get_next_entry = ide_code_index_entries_real_get_next_entry;
85   iface->next_entries_async = ide_code_index_entries_real_next_entries_async;
86   iface->next_entries_finish = ide_code_index_entries_real_next_entries_finish;
87 }
88 
89 /**
90  * ide_code_index_entries_get_next_entry:
91  * @self: An #IdeCodeIndexEntries instance.
92  *
93  * This will fetch next entry in index.
94  *
95  * When all of the entries have been exhausted, %NULL should be returned.
96  *
97  * Returns: (nullable) (transfer full): An #IdeCodeIndexEntry.
98  *
99  * Since: 3.32
100  */
101 IdeCodeIndexEntry *
ide_code_index_entries_get_next_entry(IdeCodeIndexEntries * self)102 ide_code_index_entries_get_next_entry (IdeCodeIndexEntries *self)
103 {
104   g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
105   g_return_val_if_fail (IDE_IS_CODE_INDEX_ENTRIES (self), NULL);
106 
107   return IDE_CODE_INDEX_ENTRIES_GET_IFACE (self)->get_next_entry (self);
108 }
109 
110 /**
111  * ide_code_index_entries_get_file:
112  * @self: a #IdeCodeIndexEntries
113  *
114  * The file that was indexed.
115  *
116  * Returns: (transfer full): a #GFile
117  *
118  * Since: 3.32
119  */
120 GFile *
ide_code_index_entries_get_file(IdeCodeIndexEntries * self)121 ide_code_index_entries_get_file (IdeCodeIndexEntries *self)
122 {
123   g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
124   g_return_val_if_fail (IDE_IS_CODE_INDEX_ENTRIES (self), NULL);
125 
126   return IDE_CODE_INDEX_ENTRIES_GET_IFACE (self)->get_file (self);
127 }
128 
129 /**
130  * ide_code_index_entries_next_entries_async:
131  * @self: a #IdeCodeIndexEntries
132  * @cancellable: (nullable): a #GCancellable or %NULL
133  * @callback: a callback to execute upon completion
134  * @user_data: user data for @callback, or %NULL
135  *
136  * Requests the next set of results from the code index asynchronously.
137  * This allows implementations to possibly process data off the main thread
138  * without blocking the main loop.
139  *
140  * Since: 3.32
141  */
142 void
ide_code_index_entries_next_entries_async(IdeCodeIndexEntries * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)143 ide_code_index_entries_next_entries_async (IdeCodeIndexEntries *self,
144                                            GCancellable        *cancellable,
145                                            GAsyncReadyCallback  callback,
146                                            gpointer             user_data)
147 {
148   g_return_if_fail (IDE_IS_MAIN_THREAD ());
149   g_return_if_fail (IDE_IS_CODE_INDEX_ENTRIES (self));
150   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
151 
152   IDE_CODE_INDEX_ENTRIES_GET_IFACE (self)->next_entries_async (self, cancellable, callback, user_data);
153 }
154 
155 /**
156  * ide_code_index_entries_next_entries_finish:
157  * @self: a #IdeCodeIndexEntries
158  * @result: a #GAsyncResult provided to callback
159  * @error: a location for a #GError, or %NULL
160  *
161  * Completes an asynchronous request for the next set of entries from the index.
162  *
163  * Returns: (transfer full) (element-type IdeCodeIndexEntry): a #GPtrArray
164  *   of #IdeCodeIndexEntry.
165  *
166  * Since: 3.32
167  */
168 GPtrArray *
ide_code_index_entries_next_entries_finish(IdeCodeIndexEntries * self,GAsyncResult * result,GError ** error)169 ide_code_index_entries_next_entries_finish (IdeCodeIndexEntries  *self,
170                                             GAsyncResult         *result,
171                                             GError              **error)
172 {
173   g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
174   g_return_val_if_fail (IDE_IS_CODE_INDEX_ENTRIES (self), NULL);
175   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
176 
177   return IDE_CODE_INDEX_ENTRIES_GET_IFACE (self)->next_entries_finish (self, result, error);
178 }
179 
180 static void
ide_code_index_entries_collect_cb(GObject * object,GAsyncResult * result,gpointer user_data)181 ide_code_index_entries_collect_cb (GObject      *object,
182                                    GAsyncResult *result,
183                                    gpointer      user_data)
184 {
185   IdeCodeIndexEntries *self = (IdeCodeIndexEntries *)object;
186   g_autoptr(IdeTask) task = user_data;
187   g_autoptr(GPtrArray) ret = NULL;
188   g_autoptr(GError) error = NULL;
189   GPtrArray *task_data;
190 
191   g_assert (IDE_IS_MAIN_THREAD ());
192   g_assert (IDE_IS_CODE_INDEX_ENTRIES (self));
193   g_assert (G_IS_ASYNC_RESULT (result));
194   g_assert (IDE_IS_TASK (task));
195 
196   if (!(task_data = ide_task_get_task_data (task)))
197     {
198       task_data = g_ptr_array_new_with_free_func ((GDestroyNotify)ide_code_index_entry_free);
199       ide_task_set_task_data (task, task_data, g_ptr_array_unref);
200     }
201 
202   if ((ret = ide_code_index_entries_next_entries_finish (self, result, &error)) && ret->len > 0)
203     {
204       IDE_PTR_ARRAY_SET_FREE_FUNC (ret, NULL);
205 
206       for (guint i = 0; i < ret->len; i++)
207         g_ptr_array_add (task_data, g_ptr_array_index (ret, i));
208 
209       g_ptr_array_remove_range (ret, 0, ret->len);
210 
211       ide_code_index_entries_next_entries_async (self,
212                                                  ide_task_get_cancellable (task),
213                                                  ide_code_index_entries_collect_cb,
214                                                  g_object_ref (task));
215       return;
216     }
217 
218   ide_task_return_pointer (task,
219                            g_ptr_array_ref (task_data),
220                            g_ptr_array_unref);
221 }
222 
223 /**
224  * ide_code_index_entries_collect_async:
225  *
226  * Calls ide_code_index_entries_next_entries_async() repeatedly until all
227  * entries have been retrieved. After that, the async operation will complete.
228  *
229  * Since: 3.32
230  */
231 void
ide_code_index_entries_collect_async(IdeCodeIndexEntries * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)232 ide_code_index_entries_collect_async (IdeCodeIndexEntries *self,
233                                       GCancellable        *cancellable,
234                                       GAsyncReadyCallback  callback,
235                                       gpointer             user_data)
236 {
237   g_autoptr(IdeTask) task = NULL;
238 
239   g_return_if_fail (IDE_IS_CODE_INDEX_ENTRIES (self));
240   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
241 
242   task = ide_task_new (self, cancellable, callback, user_data);
243   ide_task_set_source_tag (task, ide_code_index_entries_collect_async);
244   ide_code_index_entries_next_entries_async (self,
245                                              cancellable,
246                                              ide_code_index_entries_collect_cb,
247                                              g_steal_pointer (&task));
248 }
249 
250 /**
251  * ide_code_index_entries_collect_finish:
252  *
253  * Returns: (transfer full) (element-type IdeCodeIndexEntry): an array of #IdeCodeIndexEntry
254  *   or %NULL and @error is set
255  *
256  * Since: 3.32
257  */
258 GPtrArray *
ide_code_index_entries_collect_finish(IdeCodeIndexEntries * self,GAsyncResult * result,GError ** error)259 ide_code_index_entries_collect_finish (IdeCodeIndexEntries  *self,
260                                        GAsyncResult         *result,
261                                        GError              **error)
262 {
263   g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
264   g_return_val_if_fail (IDE_IS_CODE_INDEX_ENTRIES (self), NULL);
265   g_return_val_if_fail (IDE_IS_TASK (result), NULL);
266 
267   return ide_task_propagate_pointer (IDE_TASK (result), error);
268 }
269