static gboolean add_in_idle (GscProviderDevhelp *devhelp) { guint idx = 0; GList *ret = NULL; gboolean finished; /* Don't complete empty string (when word == NULL) */ if (devhelp->priv->word == NULL) { gtk_source_completion_context_add_proposals (devhelp->priv->context, GTK_SOURCE_COMPLETION_PROVIDER (devhelp), NULL, TRUE); population_finished (devhelp); return FALSE; } if (devhelp->priv->populate_iter == NULL) { devhelp->priv->populate_iter = find_first_proposal (devhelp); } while (idx < PROCESS_BATCH && devhelp->priv->populate_iter) { GtkSourceCompletionProposal *proposal; proposal = GTK_SOURCE_COMPLETION_PROPOSAL (g_sequence_get (devhelp->priv->populate_iter)); ret = g_list_prepend (ret, proposal); devhelp->priv->populate_iter = find_next_proposal (devhelp, devhelp->priv->populate_iter); ++idx; } ret = g_list_reverse (ret); finished = devhelp->priv->populate_iter == NULL; gtk_source_completion_context_add_proposals (devhelp->priv->context, GTK_SOURCE_COMPLETION_PROVIDER (devhelp), ret, finished); if (finished) { population_finished (devhelp); } return !finished; }
static void get_methods_cb (GObject *object, GAsyncResult *result, gpointer user_data) { GVariant *matches; gpointer *closure; GError *error = NULL; GList *list = NULL; closure = (gpointer *)user_data; if (!gb_dbus_typelib_call_get_methods_finish(GB_DBUS_TYPELIB(object), &matches, result, &error)) { if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_warning("%s", error->message); } goto cleanup; } if (matches) { GVariantIter *viter; const gchar *text; const gchar *markup; gdouble score; g_variant_get(matches, "a(sd)", &viter); while (g_variant_iter_loop(viter, "(sd)", &text, &score)) { GtkSourceCompletionItem *item; item = g_object_new(GB_TYPE_SOURCE_TYPELIB_COMPLETION_ITEM, "icon", gMethodPixbuf, "search-term", (gchar *)closure[2], "text", text, NULL); if (!item) { g_error("Failed to create item."); } list = g_list_prepend(list, item); } list = g_list_reverse(list); g_variant_unref(matches); } gtk_source_completion_context_add_proposals(closure[1], closure[0], list, TRUE); g_list_foreach(list, (GFunc)g_object_unref, NULL); g_list_free(list); cleanup: g_object_unref(closure[0]); g_object_unref(closure[1]); g_free(closure[2]); g_free(closure); }
static void provider_populate (GtkSourceCompletionProvider *provider, GtkSourceCompletionContext *context) { GbDBusTypelib *proxy; GCancellable *cancellable; GtkTextIter iter; GError *error = NULL; GVariant *matches = NULL; gpointer *closure; //gchar **words = NULL; gchar *word; GList *list = NULL; gint len; gint i; proxy = get_proxy(GB_SOURCE_TYPELIB_COMPLETION_PROVIDER(provider)); if (!proxy) { g_warning("No dbus proxy."); return; } gtk_source_completion_context_get_iter(context, &iter); word = get_word(provider, &iter); len = strlen(word); closure = g_new0(gpointer, 2); closure[0] = g_object_ref(provider); closure[1] = g_object_ref(context); closure[2] = g_strdup(word); cancellable = g_cancellable_new(); g_signal_connect_object(context, "cancelled", G_CALLBACK(g_cancellable_cancel), cancellable, G_CONNECT_SWAPPED); gb_dbus_typelib_call_get_methods(proxy, word, cancellable, get_methods_cb, closure); g_object_unref(cancellable); #if 0 if (!gb_dbus_typelib_call_get_objects_sync(proxy, word, &words, NULL, &error)) { g_warning("%s\n", error->message); g_error_free(error); } if (words) { for (i = 0; words[i]; i++) { GtkSourceCompletionItem *item; item = gtk_source_completion_item_new(words[i], words[i], gClassPixbuf, NULL); list = g_list_prepend(list, item); } } list = g_list_reverse(list); gtk_source_completion_context_add_proposals(context, provider, list, TRUE); g_strfreev(words); words = NULL; g_free(word); #endif }
static void ide_langserv_completion_provider_populate (GtkSourceCompletionProvider *provider, GtkSourceCompletionContext *context) { IdeLangservCompletionProvider *self = (IdeLangservCompletionProvider *)provider; IdeLangservCompletionProviderPrivate *priv = ide_langserv_completion_provider_get_instance_private (self); g_autoptr(GVariant) params = NULL; g_autoptr(GCancellable) cancellable = NULL; g_autoptr(CompletionState) state = NULL; g_autofree gchar *uri = NULL; GtkTextIter iter; IdeBuffer *buffer; gint line; gint column; IDE_ENTRY; g_assert (IDE_IS_LANGSERV_COMPLETION_PROVIDER (self)); g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context)); if (priv->client == NULL) { IDE_TRACE_MSG ("No client set, cannot provide proposals"); gtk_source_completion_context_add_proposals (context, provider, NULL, TRUE); IDE_EXIT; } gtk_source_completion_context_get_iter (context, &iter); buffer = IDE_BUFFER (gtk_text_iter_get_buffer (&iter)); uri = ide_buffer_get_uri (buffer); line = gtk_text_iter_get_line (&iter); column = gtk_text_iter_get_line_offset (&iter); params = JSONRPC_MESSAGE_NEW ( "textDocument", "{", "uri", JSONRPC_MESSAGE_PUT_STRING (uri), "}", "position", "{", "line", JSONRPC_MESSAGE_PUT_INT32 (line), "character", JSONRPC_MESSAGE_PUT_INT32 (column), "}" ); cancellable = g_cancellable_new (); g_signal_connect_data (context, "cancelled", G_CALLBACK (g_cancellable_cancel), g_object_ref (cancellable), (GClosureNotify)g_object_unref, G_CONNECT_SWAPPED); state = completion_state_new (self, context); ide_langserv_client_call_async (priv->client, "textDocument/completion", g_steal_pointer (¶ms), g_steal_pointer (&cancellable), ide_langserv_completion_provider_complete_cb, g_steal_pointer (&state)); IDE_EXIT; }
static void ide_langserv_completion_provider_complete_cb (GObject *object, GAsyncResult *result, gpointer user_data) { IdeLangservClient *client = (IdeLangservClient *)object; g_autoptr(CompletionState) state = user_data; g_autoptr(GVariant) return_value = NULL; g_autoptr(GError) error = NULL; GVariant *node; GList *list = NULL; GVariantIter iter; IDE_ENTRY; g_assert (IDE_IS_LANGSERV_CLIENT (client)); g_assert (G_IS_ASYNC_RESULT (result)); g_assert (state != NULL); g_assert (IDE_IS_LANGSERV_COMPLETION_PROVIDER (state->self)); g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (state->context)); if (!ide_langserv_client_call_finish (client, result, &return_value, &error)) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_message ("%s", error->message); IDE_GOTO (failure); } /* * TODO: We will want to make a much more optimized version of this using * the other completion result work we've done. */ g_variant_iter_init (&iter, return_value); while (g_variant_iter_loop (&iter, "v", &node)) { g_autoptr(GtkSourceCompletionItem) item = NULL; g_autofree gchar *full_label = NULL; const gchar *label; const gchar *detail; const gchar *icon_name = NULL; gboolean success; gint64 kind = 0; success = JSONRPC_MESSAGE_PARSE (node, "label", JSONRPC_MESSAGE_GET_STRING (&label), "detail", JSONRPC_MESSAGE_GET_STRING (&detail) ); if (!success) { IDE_TRACE_MSG ("Failed to extract completion item from node"); continue; } /* Optional kind field */ JSONRPC_MESSAGE_PARSE (node, "kind", JSONRPC_MESSAGE_GET_INT64 (&kind)); kind = ide_langserv_decode_completion_kind (kind); if (kind != IDE_SYMBOL_NONE) icon_name = ide_symbol_kind_get_icon_name (kind); if (label != NULL && detail != NULL) full_label = g_strdup_printf ("%s : %s", label, detail); else full_label = g_strdup (label); //item = gtk_source_completion_item_new (full_label, label, NULL, NULL); item = g_object_new (GTK_SOURCE_TYPE_COMPLETION_ITEM, "icon-name", icon_name, "label", full_label, "text", label, NULL); list = g_list_prepend (list, g_steal_pointer (&item)); } failure: gtk_source_completion_context_add_proposals (state->context, GTK_SOURCE_COMPLETION_PROVIDER (state->self), list, TRUE); g_list_free_full (list, g_object_unref); IDE_EXIT; }
static void ide_ctags_completion_provider_populate (GtkSourceCompletionProvider *provider, GtkSourceCompletionContext *context) { IdeCtagsCompletionProvider *self = (IdeCtagsCompletionProvider *)provider; const gchar * const *allowed; g_autofree gchar *casefold = NULL; gint word_len; guint i; guint j; IDE_ENTRY; g_assert (IDE_IS_CTAGS_COMPLETION_PROVIDER (self)); g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context)); g_clear_pointer (&self->current_word, g_free); self->current_word = ide_completion_provider_context_current_word (context); allowed = get_allowed_suffixes (context); if (self->results != NULL) { if (ide_completion_results_replay (self->results, self->current_word)) { ide_completion_results_present (self->results, provider, context); IDE_EXIT; } g_clear_pointer (&self->results, g_object_unref); } word_len = strlen (self->current_word); if (word_len < self->minimum_word_size) IDE_GOTO (word_too_small); casefold = g_utf8_casefold (self->current_word, -1); self->results = ide_completion_results_new (self->current_word); for (i = 0; i < self->indexes->len; i++) { g_autofree gchar *copy = g_strdup (self->current_word); IdeCtagsIndex *index = g_ptr_array_index (self->indexes, i); const IdeCtagsIndexEntry *entries = NULL; const gchar *last_name = NULL; guint tmp_len = word_len; gsize n_entries = 0; while (entries == NULL && *copy) { if (!(entries = ide_ctags_index_lookup_prefix (index, copy, &n_entries))) copy [--tmp_len] = '\0'; } if ((entries == NULL) || (n_entries == 0)) continue; for (j = 0; j < n_entries; j++) { const IdeCtagsIndexEntry *entry = &entries [j]; IdeCtagsCompletionItem *item; if (ide_str_equal0 (entry->name, last_name)) continue; last_name = entry->name; if (!ide_ctags_is_allowed (entry, allowed)) continue; item = ide_ctags_completion_item_new (self, entry); if (!ide_completion_item_match (IDE_COMPLETION_ITEM (item), self->current_word, casefold)) { g_object_unref (item); continue; } ide_completion_results_take_proposal (self->results, IDE_COMPLETION_ITEM (item)); } } ide_completion_results_present (self->results, provider, context); IDE_EXIT; word_too_small: gtk_source_completion_context_add_proposals (context, provider, NULL, TRUE); IDE_EXIT; }
static void ide_ctags_completion_provider_populate (GtkSourceCompletionProvider *provider, GtkSourceCompletionContext *context) { IdeCtagsCompletionProvider *self = (IdeCtagsCompletionProvider *)provider; g_autofree gchar *word = NULL; const IdeCtagsIndexEntry *entries; const gchar * const *allowed; g_autoptr(GPtrArray) ar = NULL; IdeCtagsIndexEntry *last = NULL; GtkSourceBuffer *buffer; gsize n_entries; GtkTextIter iter; GList *list = NULL; gsize i; gsize j; IDE_ENTRY; g_assert (IDE_IS_CTAGS_COMPLETION_PROVIDER (self)); g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context)); if (self->indexes->len == 0) IDE_GOTO (failure); if (!g_settings_get_boolean (self->settings, "ctags-autocompletion")) IDE_GOTO (failure); if (!gtk_source_completion_context_get_iter (context, &iter)) IDE_GOTO (failure); buffer = GTK_SOURCE_BUFFER (gtk_text_iter_get_buffer (&iter)); allowed = get_allowed_suffixes (buffer); word = get_word_to_cursor (&iter); if (ide_str_empty0 (word) || strlen (word) < self->minimum_word_size) IDE_GOTO (failure); if (strlen (word) < 3) IDE_GOTO (failure); ar = g_ptr_array_new (); IDE_TRACE_MSG ("Searching for %s", word); for (j = 0; j < self->indexes->len; j++) { IdeCtagsIndex *index = g_ptr_array_index (self->indexes, j); entries = ide_ctags_index_lookup_prefix (index, word, &n_entries); if ((entries == NULL) || (n_entries == 0)) continue; for (i = 0; i < n_entries; i++) { const IdeCtagsIndexEntry *entry = &entries [i]; if (is_allowed (entry, allowed)) g_ptr_array_add (ar, (gpointer)entry); } } g_ptr_array_sort (ar, sort_wrapper); for (i = ar->len; i > 0; i--) { GtkSourceCompletionProposal *item; IdeCtagsIndexEntry *entry = g_ptr_array_index (ar, i - 1); /* * NOTE: * * We walk backwards in this ptrarray so that we can use g_list_prepend() for O(1) access. * I think everyone agrees that using GList for passing completion data around was not * a great choice, but it is what we have to work with. */ /* * Ignore this item if the previous one looks really similar. * We take the first item instead of the last since the first item (when walking backwards) * tends to be more likely to be the one we care about (based on lexicographical * ordering. For example, something in "gtk-2.0" is less useful than "gtk-3.0". * * This is done here instead of during our initial object creation so that * we can merge items between different indexes. It often happens that the * same headers are included in multiple tags files. */ if ((last != NULL) && too_similar (entry, last)) continue; /* * NOTE: * * Autocompletion is very performance sensitive code. The smallest amount of * extra work has a very negative impact on interactivity. We are trying to * avoid a couple things here based on how completion works. * * 1) Avoiding referencing or copying things. * Since the provider will always outlive the completion item, we use * borrowed references for as much as we can. * 2) We delay the work of looking up icons until they are requested. * No sense in doing that work before hand. */ item = ide_ctags_completion_item_new (entry, self, context); list = g_list_prepend (list, item); last = entry; } failure: gtk_source_completion_context_add_proposals (context, provider, list, TRUE); g_list_free_full (list, g_object_unref); IDE_EXIT; }