[gnome-builder/wip/libide: 82/153] libide: start on clang service



commit 035a0d350f6699a8ce351e75bc45db73eebd1764
Author: Christian Hergert <christian hergert me>
Date:   Wed Feb 11 17:06:11 2015 -0800

    libide: start on clang service
    
    Lot's to do here, but this will let us experiment with having things in
    process. In particular, we can start using this for faster highlighting,
    diagnostics, and most importantly, autocompletion.

 libide/Makefile.am                        |    7 +-
 libide/clang/ide-clang-private.h          |   37 ++++
 libide/clang/ide-clang-service.c          |  326 +++++++++++++++++++++++++++++
 libide/clang/ide-clang-service.h          |   12 +
 libide/clang/ide-clang-translation-unit.c |  131 ++++++++++++
 libide/clang/ide-clang-translation-unit.h |   39 ++++
 6 files changed, 551 insertions(+), 1 deletions(-)
---
diff --git a/libide/Makefile.am b/libide/Makefile.am
index e3910fe..7ce5e50 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -18,6 +18,8 @@ libide_la_SOURCES = \
        libide/clang/ide-clang-service.h \
        libide/clang/ide-clang-symbol-resolver.c \
        libide/clang/ide-clang-symbol-resolver.h \
+       libide/clang/ide-clang-translation-unit.c \
+       libide/clang/ide-clang-translation-unit.h \
        libide/directory/ide-directory-build-system.c \
        libide/directory/ide-directory-build-system.h \
        libide/directory/ide-directory-vcs.c \
@@ -113,4 +115,7 @@ libide_la_CFLAGS = \
        $(LIBIDE_CFLAGS)
 
 libide_la_LIBADD = \
-       $(LIBIDE_LIBS)
+       $(CLANG_LDFLAGS) \
+       $(LIBIDE_LIBS) \
+       -lclang \
+       $(NULL)
diff --git a/libide/clang/ide-clang-private.h b/libide/clang/ide-clang-private.h
new file mode 100644
index 0000000..cdc50b0
--- /dev/null
+++ b/libide/clang/ide-clang-private.h
@@ -0,0 +1,37 @@
+/* ide-clang-private.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_PRIVATE_H
+#define IDE_CLANG_PRIVATE_H
+
+#include <clang-c/Index.h>
+
+#include "ide-types.h"
+
+#include "ide-clang-service.h"
+#include "ide-clang-translation-unit.h"
+
+G_BEGIN_DECLS
+
+IdeClangTranslationUnit *_ide_clang_translation_unit_new (IdeContext        *context,
+                                                          CXTranslationUnit  tu,
+                                                          gint64             sequence);
+
+G_END_DECLS
+
+#endif /* IDE_CLANG_PRIVATE_H */
diff --git a/libide/clang/ide-clang-service.c b/libide/clang/ide-clang-service.c
index e69de29..8c9f2c0 100644
--- a/libide/clang/ide-clang-service.c
+++ b/libide/clang/ide-clang-service.c
@@ -0,0 +1,326 @@
+/* ide-clang-service.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 2.1 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 "ide-clang-private.h"
+#include "ide-clang-service.h"
+#include "ide-context.h"
+#include "ide-file.h"
+#include "ide-unsaved-file.h"
+#include "ide-unsaved-files.h"
+
+typedef struct
+{
+  GHashTable   *cached_units;
+  GRWLock       cached_rwlock;
+  CXIndex       index;
+  GCancellable *cancellable;
+} IdeClangServicePrivate;
+
+typedef struct
+{
+  IdeFile    *file;
+  CXIndex     index;
+  gchar      *source_filename;
+  gchar     **command_line_args;
+  GPtrArray  *unsaved_files;
+  gint64      sequence;
+  guint       options;
+} ParseRequest;
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeClangService, ide_clang_service,
+                            IDE_TYPE_SERVICE)
+
+enum {
+  PROP_0,
+  LAST_PROP
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+static void
+parse_request_free (gpointer data)
+{
+  ParseRequest *request = data;
+
+  g_free (request->source_filename);
+  g_strfreev (request->command_line_args);
+  g_ptr_array_unref (request->unsaved_files);
+  g_clear_object (&request->file);
+  g_slice_free (ParseRequest, request);
+}
+
+static void
+ide_clang_service_parse_worker (GTask        *task,
+                                gpointer      source_object,
+                                gpointer      task_data,
+                                GCancellable *cancellable)
+{
+  IdeClangServicePrivate *priv;
+  IdeClangTranslationUnit *ret;
+  CXTranslationUnit tu;
+  ParseRequest *request = task_data;
+  IdeContext *context;
+  struct CXUnsavedFile *unsaved_files;
+  GArray *ar;
+  gsize i;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (IDE_IS_CLANG_SERVICE (source_object));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  priv = ide_clang_service_get_instance_private (source_object);
+
+  ar = g_array_new (FALSE, FALSE, sizeof (struct CXUnsavedFile));
+
+  for (i = 0; i < request->unsaved_files->len; i++)
+    {
+      IdeUnsavedFile *iuf = g_ptr_array_index (request->unsaved_files, i);
+      struct CXUnsavedFile uf;
+      GBytes *content;
+      GFile *file;
+
+      file = ide_unsaved_file_get_file (iuf);
+      content = ide_unsaved_file_get_content (iuf);
+
+      uf.Filename = g_file_get_path (ide_unsaved_file_get_file (iuf));
+      uf.Contents = g_bytes_get_data (content, NULL);
+      uf.Length = g_bytes_get_size (content);
+
+      g_array_append_val (ar, uf);
+    }
+
+  tu = clang_parseTranslationUnit (request->index,
+                                   request->source_filename,
+                                   (const gchar * const *)request->command_line_args,
+                                   g_strv_length (request->command_line_args),
+                                   (struct CXUnsavedFile *)ar->data,
+                                   ar->len,
+                                   request->options);
+
+  if (!tu)
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_FAILED,
+                               _("Failed to create translation unit."));
+      goto cleanup;
+    }
+
+  context = ide_object_get_context (source_object);
+  ret = _ide_clang_translation_unit_new (context, tu, request->sequence);
+
+  g_rw_lock_writer_lock (&priv->cached_rwlock);
+  g_hash_table_replace (priv->cached_units,
+                        g_object_ref (request->file),
+                        g_object_ref (ret));
+  g_rw_lock_writer_unlock (&priv->cached_rwlock);
+
+cleanup:
+  g_array_unref (ar);
+}
+
+/**
+ * ide_clang_service_get_translation_unit_async:
+ * @min_sequence: The minimum change sequence number to reuse a cached unit.
+ *
+ * This function is used to asynchronously retrieve the translation unit for
+ * a particular file.
+ *
+ * If the translation unit is up to date, then no parsing will occur and the
+ * existing translation unit will be used.
+ *
+ * If the translation unit is out of date, then the source file(s) will be
+ * parsed via clang_parseTranslationUnit() asynchronously.
+ */
+void
+ide_clang_service_get_translation_unit_async (IdeClangService     *self,
+                                              IdeFile             *file,
+                                              gint64               min_sequence,
+                                              GCancellable        *cancellable,
+                                              GAsyncReadyCallback  callback,
+                                              gpointer             user_data)
+{
+  IdeClangServicePrivate *priv = ide_clang_service_get_instance_private (self);
+  g_autoptr(IdeClangTranslationUnit) cached = NULL;
+  IdeUnsavedFiles *unsaved_files;
+  IdeContext *context;
+  g_autoptr(GTask) task = NULL;
+  ParseRequest *request;
+  const gchar *path;
+  GFile *gfile;
+
+  g_return_if_fail (IDE_IS_CLANG_SERVICE (self));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  context = ide_object_get_context (IDE_OBJECT (self));
+  unsaved_files = ide_context_get_unsaved_files (context);
+
+  g_rw_lock_reader_lock (&priv->cached_rwlock);
+  cached = g_hash_table_lookup (priv->cached_units, file);
+  if (cached)
+    g_object_ref (cached);
+  g_rw_lock_reader_unlock (&priv->cached_rwlock);
+
+  if (min_sequence <= 0)
+    min_sequence = ide_unsaved_files_get_sequence (unsaved_files);
+
+  if (cached)
+    {
+      if (ide_clang_translation_unit_get_sequence (cached) >= min_sequence)
+        {
+          g_task_return_pointer (task, g_object_ref (cached), g_object_unref);
+          return;
+        }
+    }
+
+  gfile = ide_file_get_file (file);
+
+  if (!gfile || !(path = g_file_get_path (gfile)))
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_NOT_SUPPORTED,
+                               _("File must be saved locally to parse."));
+      return;
+    }
+
+  request = g_slice_new0 (ParseRequest);
+  request->file = g_object_ref (file);
+  request->index = priv->index;
+  request->source_filename = g_strdup (path);
+  request->command_line_args = NULL; /* TODO: Get from build system */
+  request->unsaved_files = ide_unsaved_files_get_unsaved_files (unsaved_files);
+  request->sequence = ide_unsaved_files_get_sequence (unsaved_files);
+  request->options = 0;
+
+  g_task_set_task_data (task, request, parse_request_free);
+  g_task_run_in_thread (task, ide_clang_service_parse_worker);
+}
+
+IdeClangTranslationUnit *
+ide_clang_service_get_translation_unit_finish (IdeClangService  *self,
+                                               GAsyncResult     *result,
+                                               GError          **error)
+{
+  GTask *task = (GTask *)result;
+
+  g_return_val_if_fail (IDE_IS_CLANG_SERVICE (self), NULL);
+
+  return g_task_propagate_pointer (task, error);
+}
+
+static void
+ide_clang_service_start (IdeService *service)
+{
+  IdeClangService *self = (IdeClangService *)service;
+  IdeClangServicePrivate *priv = ide_clang_service_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_CLANG_SERVICE (self));
+  g_return_if_fail (!priv->index);
+
+  g_clear_object (&priv->cancellable);
+  priv->cancellable = g_cancellable_new ();
+
+  priv->index = clang_createIndex (0, 0);
+  clang_CXIndex_setGlobalOptions (priv->index,
+                                  CXGlobalOpt_ThreadBackgroundPriorityForAll);
+
+  IDE_SERVICE_CLASS (ide_clang_service_parent_class)->start (service);
+}
+
+static void
+ide_clang_service_stop (IdeService *service)
+{
+  IdeClangService *self = (IdeClangService *)service;
+  IdeClangServicePrivate *priv = ide_clang_service_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_CLANG_SERVICE (self));
+  g_return_if_fail (!priv->index);
+
+  g_cancellable_cancel (priv->cancellable);
+
+  IDE_SERVICE_CLASS (ide_clang_service_parent_class)->start (service);
+}
+
+static void
+ide_clang_service_dispose (GObject *object)
+{
+  IdeClangService *self = (IdeClangService *)object;
+  IdeClangServicePrivate *priv = ide_clang_service_get_instance_private (self);
+
+  g_clear_pointer (&priv->index, clang_disposeIndex);
+  g_clear_object (&priv->cancellable);
+
+  G_OBJECT_CLASS (ide_clang_service_parent_class)->dispose (object);
+}
+
+static void
+ide_clang_service_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+  IdeClangService *self = IDE_CLANG_SERVICE (object);
+
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_clang_service_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+  IdeClangService *self = IDE_CLANG_SERVICE (object);
+
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_clang_service_class_init (IdeClangServiceClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = ide_clang_service_dispose;
+  object_class->get_property = ide_clang_service_get_property;
+  object_class->set_property = ide_clang_service_set_property;
+}
+
+static void
+ide_clang_service_init (IdeClangService *self)
+{
+  IdeClangServicePrivate *priv = ide_clang_service_get_instance_private (self);
+
+  g_rw_lock_init (&priv->cached_rwlock);
+
+  priv->cached_units = g_hash_table_new_full ((GHashFunc)ide_file_hash,
+                                              (GEqualFunc)ide_file_equal,
+                                              g_object_unref,
+                                              g_object_unref);
+}
diff --git a/libide/clang/ide-clang-service.h b/libide/clang/ide-clang-service.h
index 38068aa..4d47f4c 100644
--- a/libide/clang/ide-clang-service.h
+++ b/libide/clang/ide-clang-service.h
@@ -21,6 +21,8 @@
 
 #include "ide-service.h"
 
+#include "ide-clang-translation-unit.h"
+
 G_BEGIN_DECLS
 
 #define IDE_TYPE_CLANG_SERVICE (ide_clang_service_get_type())
@@ -32,6 +34,16 @@ struct _IdeClangServiceClass
   IdeServiceClass parent;
 };
 
+void                     ide_clang_service_get_translation_unit_async  (IdeClangService      *self,
+                                                                        IdeFile              *file,
+                                                                        gint64                min_sequence,
+                                                                        GCancellable         *cancellable,
+                                                                        GAsyncReadyCallback   callback,
+                                                                        gpointer              user_data);
+IdeClangTranslationUnit *ide_clang_service_get_translation_unit_finish (IdeClangService      *self,
+                                                                        GAsyncResult         *result,
+                                                                        GError              **error);
+
 G_END_DECLS
 
 #endif /* IDE_CLANG_SERVICE_H */
diff --git a/libide/clang/ide-clang-translation-unit.c b/libide/clang/ide-clang-translation-unit.c
new file mode 100644
index 0000000..44f2f4c
--- /dev/null
+++ b/libide/clang/ide-clang-translation-unit.c
@@ -0,0 +1,131 @@
+/* ide-clang-translation-unit.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 "ide-context.h"
+#include "ide-clang-translation-unit.h"
+
+typedef struct
+{
+  CXTranslationUnit tu;
+  gint64            sequence;
+} IdeClangTranslationUnitPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeClangTranslationUnit,
+                            ide_clang_translation_unit,
+                            IDE_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_SEQUENCE,
+  LAST_PROP
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+IdeClangTranslationUnit *
+_ide_clang_translation_unit_new (IdeContext        *context,
+                                 CXTranslationUnit  tu,
+                                 gint64             sequence)
+{
+  IdeClangTranslationUnitPrivate *priv;
+  IdeClangTranslationUnit *ret;
+
+  g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
+  g_return_val_if_fail (tu, NULL);
+
+  ret = g_object_new (IDE_TYPE_CLANG_TRANSLATION_UNIT,
+                      "context", context,
+                      NULL);
+
+  priv = ide_clang_translation_unit_get_instance_private (ret);
+
+  priv->tu = tu;
+  priv->sequence = sequence;
+
+  return ret;
+}
+
+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;
+}
+
+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);
+
+  G_OBJECT_CLASS (ide_clang_translation_unit_parent_class)->finalize (object);
+}
+
+static void
+ide_clang_translation_unit_get_property (GObject    *object,
+                                         guint       prop_id,
+                                         GValue     *value,
+                                         GParamSpec *pspec)
+{
+  IdeClangTranslationUnit *self = IDE_CLANG_TRANSLATION_UNIT (object);
+
+  switch (prop_id)
+    {
+    case PROP_SEQUENCE:
+      g_value_set_int64 (value, ide_clang_translation_unit_get_sequence (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_clang_translation_unit_class_init (IdeClangTranslationUnitClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_clang_translation_unit_finalize;
+  object_class->get_property = ide_clang_translation_unit_get_property;
+
+  gParamSpecs [PROP_SEQUENCE] =
+    g_param_spec_int64 ("sequence",
+                        _("Sequence"),
+                        _("The sequence number when created."),
+                        G_MININT64,
+                        G_MAXINT64,
+                        0,
+                        (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_SEQUENCE,
+                                   gParamSpecs [PROP_SEQUENCE]);
+}
+
+static void
+ide_clang_translation_unit_init (IdeClangTranslationUnit *self)
+{
+}
diff --git a/libide/clang/ide-clang-translation-unit.h b/libide/clang/ide-clang-translation-unit.h
new file mode 100644
index 0000000..73a1834
--- /dev/null
+++ b/libide/clang/ide-clang-translation-unit.h
@@ -0,0 +1,39 @@
+/* ide-clang-translation-unit.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_TRANSLATION_UNIT_H
+#define IDE_CLANG_TRANSLATION_UNIT_H
+
+#include "ide-object.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);
+
+G_END_DECLS
+
+#endif /* IDE_CLANG_TRANSLATION_UNIT_H */


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