[gnome-builder/wip/libide] libide: add basic completion provider for C



commit 1412d1a90d9a7a42270d129c8e1d582747f34f64
Author: Christian Hergert <christian hergert me>
Date:   Sat Mar 14 17:52:28 2015 -0700

    libide: add basic completion provider for C
    
    Lots of stuff isn't wired up yet, but I don't want this code to land in
    the bit bucket.
    
    In particular, the display code for the completion window needs an
    incredible amount of work. Some of it will likely require changes in
    GSV so that we can get columns for appropriate layout.
    
    Also, very little effort seems to be going into the result sorting, so
    items do not get shown in a very helpful way.

 libide/Makefile.am                           |    4 +
 libide/clang/ide-clang-completion-item.c     |  163 +++++++++++++++++
 libide/clang/ide-clang-completion-item.h     |   41 +++++
 libide/clang/ide-clang-completion-provider.c |  244 +++++++++++++++++++++++++
 libide/clang/ide-clang-completion-provider.h |   41 +++++
 libide/clang/ide-clang-translation-unit.c    |  245 ++++++++++++++++++++++----
 libide/clang/ide-clang-translation-unit.h    |   20 ++-
 7 files changed, 718 insertions(+), 40 deletions(-)
---
diff --git a/libide/Makefile.am b/libide/Makefile.am
index 5eaa15f..71b0b72 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -184,6 +184,10 @@ libide_1_0_la_SOURCES = \
        libide/autotools/ide-makecache.h \
        libide/c/c-parse-helper.c \
        libide/c/c-parse-helper.h \
+       libide/clang/ide-clang-completion-item.c \
+       libide/clang/ide-clang-completion-item.h \
+       libide/clang/ide-clang-completion-provider.c \
+       libide/clang/ide-clang-completion-provider.h \
        libide/ide-debug.h \
        libide/editorconfig/editorconfig-glib.c \
        libide/editorconfig/editorconfig-glib.h \
diff --git a/libide/clang/ide-clang-completion-item.c b/libide/clang/ide-clang-completion-item.c
new file mode 100644
index 0000000..2e769c4
--- /dev/null
+++ b/libide/clang/ide-clang-completion-item.c
@@ -0,0 +1,163 @@
+/* ide-clang-completion-item.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <clang-c/Index.h>
+#include <glib/gi18n.h>
+#include <gtksourceview/gtksourcecompletionproposal.h>
+
+#include "ide-clang-completion-item.h"
+#include "ide-ref-ptr.h"
+
+struct _IdeClangCompletionItemClass
+{
+  GObjectClass parent_class;
+};
+
+struct _IdeClangCompletionItem
+{
+  GObject    parent_instance;
+
+  IdeRefPtr *results;
+  guint      index;
+};
+
+enum
+{
+  PROP_0,
+  PROP_INDEX,
+  PROP_RESULTS,
+  LAST_PROP
+};
+
+static void completion_proposal_iface_init (GtkSourceCompletionProposalIface *);
+
+G_DEFINE_TYPE_EXTENDED (IdeClangCompletionItem, ide_clang_completion_item, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (GTK_SOURCE_TYPE_COMPLETION_PROPOSAL,
+                                               completion_proposal_iface_init))
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+static void
+ide_clang_completion_item_finalize (GObject *object)
+{
+  IdeClangCompletionItem *self = (IdeClangCompletionItem *)object;
+
+  g_clear_pointer (&self->results, ide_ref_ptr_unref);
+
+  G_OBJECT_CLASS (ide_clang_completion_item_parent_class)->finalize (object);
+}
+
+static void
+ide_clang_completion_item_set_property (GObject      *object,
+                                        guint         prop_id,
+                                        const GValue *value,
+                                        GParamSpec   *pspec)
+{
+  IdeClangCompletionItem *self = IDE_CLANG_COMPLETION_ITEM (object);
+
+  switch (prop_id)
+    {
+    case PROP_INDEX:
+      self->index = g_value_get_uint (value);
+      break;
+
+    case PROP_RESULTS:
+      self->results = g_value_dup_boxed (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_clang_completion_item_class_init (IdeClangCompletionItemClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_clang_completion_item_finalize;
+  object_class->set_property = ide_clang_completion_item_set_property;
+
+  gParamSpecs [PROP_INDEX] =
+    g_param_spec_uint ("index",
+                       "Index",
+                       "The index in the result structure.",
+                       0,
+                       G_MAXUINT,
+                       0,
+                       (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_INDEX, gParamSpecs [PROP_INDEX]);
+
+  gParamSpecs [PROP_RESULTS] =
+    g_param_spec_boxed ("results",
+                        "Results",
+                        "The result set from clang.",
+                        IDE_TYPE_REF_PTR,
+                        (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_RESULTS, gParamSpecs [PROP_RESULTS]);
+}
+
+static void
+ide_clang_completion_item_init (IdeClangCompletionItem *item)
+{
+}
+
+static CXCompletionResult *
+get_completion_result (GtkSourceCompletionProposal *proposal)
+{
+  IdeClangCompletionItem *self = (IdeClangCompletionItem *)proposal;
+  CXCodeCompleteResults *results;
+  CXCompletionResult *result;
+
+  results = ide_ref_ptr_get (self->results);
+  result = &results->Results [self->index];
+
+  return result;
+}
+
+static gchar *
+ide_clang_completion_item_get_label (GtkSourceCompletionProposal *proposal)
+{
+  CXCompletionResult *result = get_completion_result (proposal);
+  GString *str;
+  unsigned num_chunks;
+  unsigned i;
+
+  str = g_string_new (NULL);
+  num_chunks = clang_getNumCompletionChunks (result->CompletionString);
+
+  for (i = 0; i < num_chunks; i++)
+    {
+      CXString cxstr;
+
+      cxstr = clang_getCompletionChunkText (result->CompletionString, i);
+
+      if (str->len)
+        g_string_append_printf (str, " %s", clang_getCString (cxstr));
+      else
+        g_string_append (str, clang_getCString (cxstr));
+    }
+
+  return g_string_free (str, FALSE);
+}
+
+static void
+completion_proposal_iface_init (GtkSourceCompletionProposalIface *iface)
+{
+  iface->get_label = ide_clang_completion_item_get_label;
+}
diff --git a/libide/clang/ide-clang-completion-item.h b/libide/clang/ide-clang-completion-item.h
new file mode 100644
index 0000000..a9fe9ac
--- /dev/null
+++ b/libide/clang/ide-clang-completion-item.h
@@ -0,0 +1,41 @@
+/* ide-clang-completion-item.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_CLANG_COMPLETION_ITEM_H
+#define IDE_CLANG_COMPLETION_ITEM_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_CLANG_COMPLETION_ITEM            (ide_clang_completion_item_get_type())
+#define IDE_CLANG_COMPLETION_ITEM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
IDE_TYPE_CLANG_COMPLETION_ITEM, IdeClangCompletionItem))
+#define IDE_CLANG_COMPLETION_ITEM_CONST(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
IDE_TYPE_CLANG_COMPLETION_ITEM, IdeClangCompletionItem const))
+#define IDE_CLANG_COMPLETION_ITEM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  
IDE_TYPE_CLANG_COMPLETION_ITEM, IdeClangCompletionItemClass))
+#define IDE_IS_CLANG_COMPLETION_ITEM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
IDE_TYPE_CLANG_COMPLETION_ITEM))
+#define IDE_IS_CLANG_COMPLETION_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  
IDE_TYPE_CLANG_COMPLETION_ITEM))
+#define IDE_CLANG_COMPLETION_ITEM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  
IDE_TYPE_CLANG_COMPLETION_ITEM, IdeClangCompletionItemClass))
+
+typedef struct _IdeClangCompletionItem        IdeClangCompletionItem;
+typedef struct _IdeClangCompletionItemClass   IdeClangCompletionItemClass;
+
+GType ide_clang_completion_item_get_type (void);
+
+G_END_DECLS
+
+#endif /* IDE_CLANG_COMPLETION_ITEM_H */
diff --git a/libide/clang/ide-clang-completion-provider.c b/libide/clang/ide-clang-completion-provider.c
new file mode 100644
index 0000000..1b46bdc
--- /dev/null
+++ b/libide/clang/ide-clang-completion-provider.c
@@ -0,0 +1,244 @@
+/* ide-clang-completion-provider.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "ide-buffer.h"
+#include "ide-clang-completion-provider.h"
+#include "ide-clang-service.h"
+#include "ide-clang-translation-unit.h"
+#include "ide-context.h"
+#include "ide-debug.h"
+#include "ide-file.h"
+
+struct _IdeClangCompletionProviderClass
+{
+  GObjectClass parent_class;
+};
+
+struct _IdeClangCompletionProvider
+{
+  GObject parent_instance;
+};
+
+typedef struct
+{
+  GCancellable                *cancellable;
+  GtkSourceCompletionProvider *provider;
+  GtkSourceCompletionContext  *context;
+  GFile                       *file;
+} AddProposalsState;
+
+static void completion_provider_iface_init (GtkSourceCompletionProviderIface *);
+
+G_DEFINE_TYPE_EXTENDED (IdeClangCompletionProvider,
+                        ide_clang_completion_provider,
+                        G_TYPE_OBJECT,
+                        0,
+                        G_IMPLEMENT_INTERFACE (GTK_SOURCE_TYPE_COMPLETION_PROVIDER,
+                                               completion_provider_iface_init))
+
+static void
+add_proposals_state_free (AddProposalsState *state)
+{
+  g_signal_handlers_disconnect_by_func (state->context,
+                                        G_CALLBACK (g_cancellable_cancel),
+                                        state->cancellable);
+
+  g_clear_object (&state->provider);
+  g_clear_object (&state->context);
+  g_clear_object (&state->file);
+  g_clear_object (&state->cancellable);
+  g_free (state);
+}
+
+static void
+ide_clang_completion_provider_class_init (IdeClangCompletionProviderClass *klass)
+{
+}
+
+static void
+ide_clang_completion_provider_init (IdeClangCompletionProvider *provider)
+{
+}
+
+static gchar *
+ide_clang_completion_provider_get_name (GtkSourceCompletionProvider *provider)
+{
+  return g_strdup (_("Clang"));
+}
+
+static void
+ide_clang_completion_provider_complete_cb (GObject      *object,
+                                           GAsyncResult *result,
+                                           gpointer      user_data)
+{
+  IdeClangTranslationUnit *tu = (IdeClangTranslationUnit *)object;
+  AddProposalsState *state = user_data;
+  GError *error = NULL;
+  GList *list;
+
+  list = ide_clang_translation_unit_code_complete_finish (tu, result, &error);
+
+  if (!list && error)
+    {
+      g_warning ("%s", error->message);
+      g_clear_error (&error);
+    }
+
+  if (!g_cancellable_is_cancelled (state->cancellable))
+    gtk_source_completion_context_add_proposals (state->context, state->provider, list, TRUE);
+  g_list_free_full (list, g_object_unref);
+  add_proposals_state_free (state);
+}
+
+static void
+ide_clang_completion_provider_tu_cb (GObject      *object,
+                                     GAsyncResult *result,
+                                     gpointer      user_data)
+{
+  IdeClangService *service = (IdeClangService *)object;
+  g_autoptr(IdeClangTranslationUnit) tu = NULL;
+  AddProposalsState *state = user_data;
+  GError *error = NULL;
+  GtkTextIter iter;
+
+  g_assert (IDE_IS_CLANG_SERVICE (service));
+  g_assert (state);
+  g_assert (IDE_IS_CLANG_COMPLETION_PROVIDER (state->provider));
+  g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (state->context));
+  g_assert (G_IS_FILE (state->file));
+
+  tu = ide_clang_service_get_translation_unit_finish (service, result, &error);
+
+  if (!tu)
+    {
+      g_warning ("%s", error->message);
+      g_clear_error (&error);
+      goto cleanup;
+    }
+
+  if (!gtk_source_completion_context_get_iter (state->context, &iter))
+    goto cleanup;
+
+  ide_clang_translation_unit_code_complete_async (tu,
+                                                  state->file,
+                                                  &iter,
+                                                  NULL,
+                                                  ide_clang_completion_provider_complete_cb,
+                                                  state);
+
+  return;
+
+cleanup:
+  if (!g_cancellable_is_cancelled (state->cancellable))
+    gtk_source_completion_context_add_proposals (state->context, state->provider, NULL, TRUE);
+  add_proposals_state_free (state);
+}
+
+static void
+ide_clang_completion_provider_populate (GtkSourceCompletionProvider *provider,
+                                        GtkSourceCompletionContext  *context)
+{
+  AddProposalsState *state;
+  IdeClangService *service;
+  GtkTextBuffer *buffer;
+  IdeContext *icontext;
+  GtkTextIter iter;
+  IdeFile *file;
+
+  if (!gtk_source_completion_context_get_iter (context, &iter))
+    goto failure;
+
+  buffer = gtk_text_iter_get_buffer (&iter);
+  if (buffer == NULL)
+    goto failure;
+
+  g_assert (IDE_IS_BUFFER (buffer));
+
+  file = ide_buffer_get_file (IDE_BUFFER (buffer));
+  if (file == NULL)
+    goto failure;
+
+  g_assert (IDE_IS_FILE (file));
+
+  icontext = ide_buffer_get_context (IDE_BUFFER (buffer));
+  if (icontext == NULL)
+    goto failure;
+
+  g_assert (IDE_IS_CONTEXT (icontext));
+
+  service = ide_context_get_service_typed (icontext, IDE_TYPE_CLANG_SERVICE);
+  g_assert (IDE_IS_CLANG_SERVICE (service));
+
+  state = g_new0 (AddProposalsState, 1);
+  state->provider = g_object_ref (provider);
+  state->context = g_object_ref (context);
+  state->file = g_object_ref (ide_file_get_file (file));
+  state->cancellable = g_cancellable_new ();
+
+  g_signal_connect_swapped (context,
+                            "cancelled",
+                            G_CALLBACK (g_cancellable_cancel),
+                            state->cancellable);
+
+  ide_clang_service_get_translation_unit_async (service,
+                                                file,
+                                                0,
+                                                NULL,
+                                                ide_clang_completion_provider_tu_cb,
+                                                state);
+
+  return;
+
+failure:
+  gtk_source_completion_context_add_proposals (context, provider, NULL, TRUE);
+}
+
+static gboolean
+ide_clang_completion_provider_get_start_iter (GtkSourceCompletionProvider *provider,
+                                              GtkSourceCompletionContext  *context,
+                                              GtkSourceCompletionProposal *proposal,
+                                              GtkTextIter                 *iter)
+{
+  return FALSE;
+}
+
+static gboolean
+ide_clang_completion_provider_activate_proposal (GtkSourceCompletionProvider *provider,
+                                                 GtkSourceCompletionProposal *proposal,
+                                                 GtkTextIter                 *iter)
+{
+  return FALSE;
+}
+
+static gint
+ide_clang_completion_provider_get_interactive_delay (GtkSourceCompletionProvider *provider)
+{
+  return -1;
+}
+
+static void
+completion_provider_iface_init (GtkSourceCompletionProviderIface *iface)
+{
+  iface->activate_proposal = ide_clang_completion_provider_activate_proposal;
+  iface->get_interactive_delay = ide_clang_completion_provider_get_interactive_delay;
+  iface->get_name = ide_clang_completion_provider_get_name;
+  iface->get_start_iter = ide_clang_completion_provider_get_start_iter;
+  iface->populate = ide_clang_completion_provider_populate;
+}
diff --git a/libide/clang/ide-clang-completion-provider.h b/libide/clang/ide-clang-completion-provider.h
new file mode 100644
index 0000000..31ec58b
--- /dev/null
+++ b/libide/clang/ide-clang-completion-provider.h
@@ -0,0 +1,41 @@
+/* ide-clang-completion-provider.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_CLANG_COMPLETION_PROVIDER_H
+#define IDE_CLANG_COMPLETION_PROVIDER_H
+
+#include <gtksourceview/gtksourcecompletionprovider.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_CLANG_COMPLETION_PROVIDER            (ide_clang_completion_provider_get_type())
+#define IDE_CLANG_COMPLETION_PROVIDER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
IDE_TYPE_CLANG_COMPLETION_PROVIDER, IdeClangCompletionProvider))
+#define IDE_CLANG_COMPLETION_PROVIDER_CONST(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
IDE_TYPE_CLANG_COMPLETION_PROVIDER, IdeClangCompletionProvider const))
+#define IDE_CLANG_COMPLETION_PROVIDER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  
IDE_TYPE_CLANG_COMPLETION_PROVIDER, IdeClangCompletionProviderClass))
+#define IDE_IS_CLANG_COMPLETION_PROVIDER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
IDE_TYPE_CLANG_COMPLETION_PROVIDER))
+#define IDE_IS_CLANG_COMPLETION_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  
IDE_TYPE_CLANG_COMPLETION_PROVIDER))
+#define IDE_CLANG_COMPLETION_PROVIDER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  
IDE_TYPE_CLANG_COMPLETION_PROVIDER, IdeClangCompletionProviderClass))
+
+typedef struct _IdeClangCompletionProvider        IdeClangCompletionProvider;
+typedef struct _IdeClangCompletionProviderClass   IdeClangCompletionProviderClass;
+
+GType ide_clang_completion_provider_get_type (void);
+
+G_END_DECLS
+
+#endif /* IDE_CLANG_COMPLETION_PROVIDER_H */
diff --git a/libide/clang/ide-clang-translation-unit.c b/libide/clang/ide-clang-translation-unit.c
index 29beb37..9770d9a 100644
--- a/libide/clang/ide-clang-translation-unit.c
+++ b/libide/clang/ide-clang-translation-unit.c
@@ -20,23 +20,38 @@
 #include <glib/gi18n.h>
 
 #include "ide-context.h"
+#include "ide-clang-completion-item.h"
 #include "ide-clang-translation-unit.h"
 #include "ide-diagnostic.h"
+#include "ide-diagnostics.h"
 #include "ide-file.h"
 #include "ide-internal.h"
 #include "ide-project.h"
+#include "ide-ref-ptr.h"
 #include "ide-source-location.h"
+#include "ide-unsaved-file.h"
+#include "ide-unsaved-files.h"
 #include "ide-vcs.h"
 
-typedef struct
+struct _IdeClangTranslationUnit
 {
+  IdeObject          parent_instance;
+
   CXTranslationUnit  tu;
   gint64             sequence;
   IdeDiagnostics    *diagnostics;
   GFile             *file;
-} IdeClangTranslationUnitPrivate;
+};
 
-G_DEFINE_TYPE_WITH_PRIVATE (IdeClangTranslationUnit, ide_clang_translation_unit, IDE_TYPE_OBJECT)
+typedef struct
+{
+  GPtrArray *unsaved_files;
+  gchar     *path;
+  guint      line;
+  guint      line_offset;
+} CodeCompleteState;
+
+G_DEFINE_TYPE (IdeClangTranslationUnit, ide_clang_translation_unit, IDE_TYPE_OBJECT)
 
 enum {
   PROP_0,
@@ -47,26 +62,35 @@ enum {
 
 static GParamSpec *gParamSpecs [LAST_PROP];
 
+static void
+code_complete_state_free (gpointer data)
+{
+  CodeCompleteState *state = data;
+
+  if (state)
+    {
+      g_clear_pointer (&state->unsaved_files, g_ptr_array_unref);
+      g_free (state->path);
+      g_free (state);
+    }
+}
+
 GFile *
 ide_clang_translation_unit_get_file (IdeClangTranslationUnit *self)
 {
-  IdeClangTranslationUnitPrivate *priv = ide_clang_translation_unit_get_instance_private (self);
-
   g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);
 
-  return priv->file;
+  return self->file;
 }
 
 static void
 ide_clang_translation_unit_set_file (IdeClangTranslationUnit *self,
                                      GFile                   *file)
 {
-  IdeClangTranslationUnitPrivate *priv = ide_clang_translation_unit_get_instance_private (self);
-
   g_return_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self));
   g_return_if_fail (G_IS_FILE (file));
 
-  if (g_set_object (&priv->file, file))
+  if (g_set_object (&self->file, file))
     g_object_notify_by_pspec (G_OBJECT (self), gParamSpecs [PROP_FILE]);
 }
 
@@ -76,7 +100,6 @@ _ide_clang_translation_unit_new (IdeContext        *context,
                                  GFile             *file,
                                  gint64             sequence)
 {
-  IdeClangTranslationUnitPrivate *priv;
   IdeClangTranslationUnit *ret;
 
   g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
@@ -88,10 +111,8 @@ _ide_clang_translation_unit_new (IdeContext        *context,
                       "context", context,
                       NULL);
 
-  priv = ide_clang_translation_unit_get_instance_private (ret);
-
-  priv->tu = tu;
-  priv->sequence = sequence;
+  ret->tu = tu;
+  ret->sequence = sequence;
 
   return ret;
 }
@@ -241,7 +262,6 @@ create_diagnostic (IdeClangTranslationUnit *self,
                    const gchar             *workpath,
                    CXDiagnostic            *cxdiag)
 {
-  IdeClangTranslationUnitPrivate *priv = ide_clang_translation_unit_get_instance_private (self);
   enum CXDiagnosticSeverity cxseverity;
   IdeDiagnosticSeverity severity;
   IdeDiagnostic *diag;
@@ -259,7 +279,7 @@ create_diagnostic (IdeClangTranslationUnit *self,
   cxloc = clang_getDiagnosticLocation (cxdiag);
   clang_getExpansionLocation (cxloc, &cxfile, NULL, NULL, NULL);
 
-  if (cxfile && !cxfile_equal (cxfile, priv->file))
+  if (cxfile && !cxfile_equal (cxfile, self->file))
     return NULL;
 
   cxseverity = clang_getDiagnosticSeverity (cxdiag);
@@ -298,13 +318,10 @@ create_diagnostic (IdeClangTranslationUnit *self,
 IdeDiagnostics *
 ide_clang_translation_unit_get_diagnostics (IdeClangTranslationUnit *self)
 {
-  IdeClangTranslationUnitPrivate *priv;
 
   g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);
 
-  priv = ide_clang_translation_unit_get_instance_private (self);
-
-  if (!priv->diagnostics)
+  if (!self->diagnostics)
     {
       IdeContext *context;
       IdeProject *project;
@@ -316,7 +333,7 @@ ide_clang_translation_unit_get_diagnostics (IdeClangTranslationUnit *self)
       guint i;
 
       ar = g_ptr_array_new_with_free_func ((GDestroyNotify)ide_diagnostic_unref);
-      count = clang_getNumDiagnostics (priv->tu);
+      count = clang_getNumDiagnostics (self->tu);
 
       /*
        * Acquire the reader lock for the project since we will need to do
@@ -338,7 +355,7 @@ ide_clang_translation_unit_get_diagnostics (IdeClangTranslationUnit *self)
           CXDiagnostic cxdiag;
           IdeDiagnostic *diag;
 
-          cxdiag = clang_getDiagnostic (priv->tu, i);
+          cxdiag = clang_getDiagnostic (self->tu, i);
           diag = create_diagnostic (self, project, workpath, cxdiag);
           if (diag)
             g_ptr_array_add (ar, diag);
@@ -347,31 +364,28 @@ ide_clang_translation_unit_get_diagnostics (IdeClangTranslationUnit *self)
 
       ide_project_reader_unlock (project);
 
-      priv->diagnostics = _ide_diagnostics_new (ar);
+      self->diagnostics = _ide_diagnostics_new (ar);
     }
 
-  return priv->diagnostics;
+  return self->diagnostics;
 }
 
 gint64
 ide_clang_translation_unit_get_sequence (IdeClangTranslationUnit *self)
 {
-  IdeClangTranslationUnitPrivate *priv;
-
   g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), -1);
 
-  priv = ide_clang_translation_unit_get_instance_private (self);
-
-  return priv->sequence;
+  return self->sequence;
 }
 
 static void
 ide_clang_translation_unit_finalize (GObject *object)
 {
   IdeClangTranslationUnit *self = (IdeClangTranslationUnit *)object;
-  IdeClangTranslationUnitPrivate *priv = ide_clang_translation_unit_get_instance_private (self);
 
-  clang_disposeTranslationUnit (priv->tu);
+  clang_disposeTranslationUnit (self->tu);
+  g_clear_pointer (&self->diagnostics, ide_diagnostics_unref);
+  g_clear_object (&self->file);
 
   G_OBJECT_CLASS (ide_clang_translation_unit_parent_class)->finalize (object);
 }
@@ -433,8 +447,7 @@ ide_clang_translation_unit_class_init (IdeClangTranslationUnitClass *klass)
                          _("The file used to build the translation unit."),
                          G_TYPE_FILE,
                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
-  g_object_class_install_property (object_class, PROP_FILE,
-                                   gParamSpecs [PROP_FILE]);
+  g_object_class_install_property (object_class, PROP_FILE, gParamSpecs [PROP_FILE]);
 
   gParamSpecs [PROP_SEQUENCE] =
     g_param_spec_int64 ("sequence",
@@ -451,3 +464,169 @@ static void
 ide_clang_translation_unit_init (IdeClangTranslationUnit *self)
 {
 }
+
+static void
+cleanup_list (gpointer data)
+{
+  g_list_free_full (data, g_object_unref);
+}
+
+static void
+ide_clang_translation_unit_code_complete_worker (GTask        *task,
+                                                 gpointer      source_object,
+                                                 gpointer      task_data,
+                                                 GCancellable *cancellable)
+{
+  IdeClangTranslationUnit *self = source_object;
+  CodeCompleteState *state = task_data;
+  CXCodeCompleteResults *results;
+  g_autoptr(IdeRefPtr) refptr = NULL;
+  struct CXUnsavedFile *ufs;
+  GList *list = NULL;
+  gsize i;
+  gsize j = 0;
+
+  g_assert (IDE_IS_CLANG_TRANSLATION_UNIT (self));
+  g_assert (state);
+  g_assert (state->unsaved_files);
+
+  /*
+   * FIXME: Not thread safe! We should probably add a "Pending" flag or something that is
+   *        similar to g_input_stream_set_pending().
+   */
+
+  if (!state->path)
+    {
+      /* implausable to reach here, anyway */
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_INVALID_FILENAME,
+                               _("clang_codeCompleteAt() only works on local files"));
+      return;
+    }
+
+  ufs = g_new0 (struct CXUnsavedFile, state->unsaved_files->len);
+
+  for (i = 0; i < state->unsaved_files->len; i++)
+    {
+      IdeUnsavedFile *uf;
+      gchar *path;
+      GFile *file;
+
+      uf = g_ptr_array_index (state->unsaved_files, i);
+      file = ide_unsaved_file_get_file (uf);
+      path = g_file_get_path (file);
+
+      /*
+       * NOTE: Some files might not be local, and therefore return a NULL path.
+       *       Also, we will free the path from the (const char *) pointer after
+       *       executing the work.
+       */
+      if (path != NULL)
+        {
+          GBytes *content = ide_unsaved_file_get_content (uf);
+
+          ufs [j].Filename = path;
+          ufs [j].Contents = g_bytes_get_data (content, NULL);
+          ufs [j].Length = g_bytes_get_size (content);
+
+          j++;
+        }
+    }
+
+  results = clang_codeCompleteAt (self->tu,
+                                  state->path,
+                                  state->line + 1,
+                                  state->line_offset + 1,
+                                  ufs, j,
+                                  clang_defaultCodeCompleteOptions ());
+
+  /*
+   * encapsulate in refptr so we don't need to malloc lots of little strings.
+   * we will inflate result strings as necessary.
+   */
+  refptr = ide_ref_ptr_new (results, (GDestroyNotify)clang_disposeCodeCompleteResults);
+
+  for (i = 0; i < results->NumResults; i++)
+    {
+      GtkSourceCompletionProposal *proposal;
+
+      proposal = g_object_new (IDE_TYPE_CLANG_COMPLETION_ITEM,
+                               "results", ide_ref_ptr_ref (refptr),
+                               "index", (guint)i,
+                               NULL);
+      list = g_list_prepend (list, proposal);
+    }
+
+  g_task_return_pointer (task, g_list_reverse (list), cleanup_list);
+
+  /* cleanup malloc'd state */
+  for (i = 0; i < j; i++)
+    g_free ((gchar *)ufs [i].Filename);
+  g_free (ufs);
+}
+
+
+void
+ide_clang_translation_unit_code_complete_async (IdeClangTranslationUnit *self,
+                                                GFile                   *file,
+                                                const GtkTextIter       *location,
+                                                GCancellable            *cancellable,
+                                                GAsyncReadyCallback      callback,
+                                                gpointer                 user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  g_autofree gchar *path = NULL;
+  CodeCompleteState *state;
+  IdeContext *context;
+  IdeUnsavedFiles *unsaved_files;
+
+  g_return_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self));
+  g_return_if_fail (G_IS_FILE (file));
+  g_return_if_fail (location);
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  unsaved_files = ide_context_get_unsaved_files (context);
+
+  task = g_task_new (self, cancellable, callback, user_data);
+
+  state = g_new0 (CodeCompleteState, 1);
+  state->path = g_file_get_path (file);
+  state->line = gtk_text_iter_get_line (location);
+  state->line_offset = gtk_text_iter_get_line_offset (location);
+  state->unsaved_files = ide_unsaved_files_get_unsaved_files (unsaved_files);
+
+  /*
+   * TODO: Technically it is not safe for us to go run this in a thread. We need to ensure
+   *       that only one thread is dealing with this at a time.
+   */
+
+  g_task_set_task_data (task, state, code_complete_state_free);
+  g_task_run_in_thread (task, ide_clang_translation_unit_code_complete_worker);
+}
+
+/**
+ * ide_clang_translation_unit_code_complete_finish:
+ * @self: A #IdeClangTranslationUnit.
+ * @result: A #GAsyncResult
+ * @error: (out) (nullable): A location for a #GError, or %NULL.
+ *
+ * Completes a call to ide_clang_translation_unit_code_complete_async().
+ *
+ * Returns: (transfer full) (element-type GtkSourceCompletionProposal*): A list of
+ *   #GtkSourceCompletionProposal. If this is %NULL, check @error to determine if
+ *   there was an error.
+ */
+GList *
+ide_clang_translation_unit_code_complete_finish (IdeClangTranslationUnit  *self,
+                                                 GAsyncResult             *result,
+                                                 GError                  **error)
+{
+  GTask *task = (GTask *)result;
+
+  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);
+  g_return_val_if_fail (G_IS_TASK (task), NULL);
+
+  return g_task_propagate_pointer (task, error);
+}
diff --git a/libide/clang/ide-clang-translation-unit.h b/libide/clang/ide-clang-translation-unit.h
index e55595c..60e5539 100644
--- a/libide/clang/ide-clang-translation-unit.h
+++ b/libide/clang/ide-clang-translation-unit.h
@@ -21,19 +21,25 @@
 
 #include "ide-object.h"
 
+#include <gtk/gtk.h>
+
 G_BEGIN_DECLS
 
 #define IDE_TYPE_CLANG_TRANSLATION_UNIT (ide_clang_translation_unit_get_type())
 
 G_DECLARE_FINAL_TYPE (IdeClangTranslationUnit, ide_clang_translation_unit, IDE, CLANG_TRANSLATION_UNIT, 
IdeObject)
 
-struct _IdeClangTranslationUnit
-{
-  GObject parent_instance;
-};
-
-gint64          ide_clang_translation_unit_get_sequence    (IdeClangTranslationUnit *self);
-IdeDiagnostics *ide_clang_translation_unit_get_diagnostics (IdeClangTranslationUnit *self);
+gint64          ide_clang_translation_unit_get_sequence         (IdeClangTranslationUnit  *self);
+IdeDiagnostics *ide_clang_translation_unit_get_diagnostics      (IdeClangTranslationUnit  *self);
+void            ide_clang_translation_unit_code_complete_async  (IdeClangTranslationUnit  *self,
+                                                                 GFile                    *file,
+                                                                 const GtkTextIter        *location,
+                                                                 GCancellable             *cancellable,
+                                                                 GAsyncReadyCallback       callback,
+                                                                 gpointer                  user_data);
+GList          *ide_clang_translation_unit_code_complete_finish (IdeClangTranslationUnit  *self,
+                                                                 GAsyncResult             *result,
+                                                                 GError                  **error);
 
 G_END_DECLS
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]