[gnome-builder/wip/libide: 82/153] libide: start on clang service
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/libide: 82/153] libide: start on clang service
- Date: Fri, 13 Feb 2015 20:07:04 +0000 (UTC)
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]