示例#1
0
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
}
示例#4
0
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 (&params),
                                  g_steal_pointer (&cancellable),
                                  ide_langserv_completion_provider_complete_cb,
                                  g_steal_pointer (&state));

  IDE_EXIT;
}
示例#5
0
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;
}