[gnome-builder/wip/chergert/templates] wip: templates
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/chergert/templates] wip: templates
- Date: Mon, 4 Jan 2016 12:35:54 +0000 (UTC)
commit 6057ac8c37dad30cab5431bcaed7505df63a22dd
Author: Christian Hergert <christian hergert me>
Date: Mon Jan 4 04:35:33 2016 -0800
wip: templates
start on some template ideas. this uses mako for the moment, via the C API.
I'm not sure that is what I want to do long term, but figured I'd do a
code drop so people can see what I'm working on.
configure.ac | 7 +
libide/Makefile.am | 7 +
libide/ide.h | 3 +
libide/template/example.tmpl | 193 ++++++++
libide/template/ide-project-template.c | 133 ++++++
libide/template/ide-project-template.h | 67 +++
libide/template/ide-template-mako.c | 258 +++++++++++
libide/template/ide-template-private.h | 51 +++
libide/template/ide-template-state.c | 160 +++++++
libide/template/ide-template-state.h | 49 ++
libide/template/ide-template.c | 473 ++++++++++++++++++++
libide/template/ide-template.h | 59 +++
plugins/Makefile.am | 2 +
plugins/create-project/Makefile.am | 46 ++
plugins/create-project/configure.ac | 12 +
plugins/create-project/create-project.plugin | 10 +
plugins/create-project/gbp-create-project-plugin.c | 30 ++
plugins/create-project/gbp-create-project-tool.c | 358 +++++++++++++++
plugins/create-project/gbp-create-project-tool.h | 32 ++
.../gbp-create-project.gresource.xml | 5 +
plugins/library-template/Makefile.am | 15 +
plugins/library-template/configure.ac | 11 +
plugins/library-template/library-template.plugin | 9 +
.../library-template/library_template/__init__.py | 63 +++
24 files changed, 2053 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 137dd8f..53b5a39 100644
--- a/configure.ac
+++ b/configure.ac
@@ -227,6 +227,7 @@ m4_include([plugins/build-tools/configure.ac])
m4_include([plugins/c-pack/configure.ac])
m4_include([plugins/clang/configure.ac])
m4_include([plugins/command-bar/configure.ac])
+m4_include([plugins/create-project/configure.ac])
m4_include([plugins/ctags/configure.ac])
m4_include([plugins/devhelp/configure.ac])
m4_include([plugins/file-search/configure.ac])
@@ -237,6 +238,7 @@ m4_include([plugins/gnome-code-assistance/configure.ac])
m4_include([plugins/html-completion/configure.ac])
m4_include([plugins/html-preview/configure.ac])
m4_include([plugins/jedi/configure.ac])
+m4_include([plugins/library-template/configure.ac])
m4_include([plugins/mingw/configure.ac])
m4_include([plugins/project-tree/configure.ac])
m4_include([plugins/python-gi-imports-completion/configure.ac])
@@ -491,6 +493,7 @@ echo " GNOME Code Assistance ................ : ${enable_gnome_code_assistance_
echo " HTML Autocompletion .................. : ${enable_html_completion_plugin}"
echo " HTML and Markdown Preview ............ : ${enable_html_preview_plugin}"
echo " MinGW ................................ : ${enable_mingw_plugin}"
+echo " Project Creation ..................... : ${enable_create_project_plugin}"
echo " Project Tree ......................... : ${enable_project_tree_plugin}"
echo " Python GObject Introspection ......... : ${enable_python_gi_imports_completion_plugin}"
echo " Python Jedi Autocompletion ........... : ${enable_jedi_plugin}"
@@ -503,4 +506,8 @@ echo " Terminal ............................. : ${enable_terminal_plugin}"
echo " Vala Language Pack ................... : ${enable_vala_pack_plugin}"
echo " XML Language Pack .................... : ${enable_xml_pack_plugin}"
echo ""
+echo " Templates"
+echo ""
+echo " Library .............................. : ${enable_library_template_plugin}"
+echo ""
diff --git a/libide/Makefile.am b/libide/Makefile.am
index b78ed7b..1e21c19 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -235,6 +235,13 @@ libide_1_0_la_public_sources = \
ide.h \
local/ide-local-device.c \
local/ide-local-device.h \
+ template/ide-template.c \
+ template/ide-template.h \
+ template/ide-template-mako.c \
+ template/ide-template-state.c \
+ template/ide-template-state.h \
+ template/ide-project-template.c \
+ template/ide-project-template.h \
util/ide-file-manager.c \
util/ide-file-manager.h \
$(NULL)
diff --git a/libide/ide.h b/libide/ide.h
index df2eb80..d4425e3 100644
--- a/libide/ide.h
+++ b/libide/ide.h
@@ -126,6 +126,9 @@ G_BEGIN_DECLS
#include "doap/ide-doap.h"
#include "local/ide-local-device.h"
#include "search/ide-omni-search-row.h"
+#include "template/ide-project-template.h"
+#include "template/ide-template-state.h"
+#include "template/ide-template.h"
#include "util/ide-file-manager.h"
#include "util/ide-gtk.h"
#include "util/ide-line-reader.h"
diff --git a/libide/template/example.tmpl b/libide/template/example.tmpl
new file mode 100644
index 0000000..f8befaa
--- /dev/null
+++ b/libide/template/example.tmpl
@@ -0,0 +1,193 @@
+% define NAME name|strup|delimit("-","_")
+% include "configure.ac.basic"
+
+AC_PREREQ([2.69])
+
+
+dnl ***********************************************************************
+dnl Versioning Information
+dnl ***********************************************************************
+m4_define([major_version],[3])
+m4_define([minor_version],[19])
+m4_define([micro_version],[0])
+m4_define([version],[major_version.minor_version.micro_version])
+m4_define([interface_age],[0])
+m4_define([bugreport_url], [{{bugreport_url}}])
+m4_define([debug_default],
+ [m4_if(m4_eval(minor_version % 2), [1], [yes], [minimum])])
+
+
+dnl ***********************************************************************
+dnl Initialize Autoconf
+dnl ***********************************************************************
+AC_INIT([{{name}}],[version],[bugreport_url])
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_SRCDIR([configure.ac])
+AC_CONFIG_MACRO_DIR([build-aux])
+AC_CONFIG_AUX_DIR([build-aux])
+AC_SUBST(ACLOCAL_AMFLAGS, "-I build-aux")
+AC_CANONICAL_HOST
+
+
+dnl ***********************************************************************
+dnl Initialize Automake
+dnl ***********************************************************************
+AM_SILENT_RULES([yes])
+AM_INIT_AUTOMAKE([1.11 foreign subdir-objects tar-ustar no-dist-gzip dist-xz])
+AM_MAINTAINER_MODE([enable])
+
+
+dnl ***********************************************************************
+dnl Internationalization
+dnl ***********************************************************************
+IT_PROG_INTLTOOL([0.50.1])
+GETTEXT_PACKAGE=AC_PACKAGE_TARNAME
+AC_SUBST(GETTEXT_PACKAGE)
+AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"], [GETTEXT package name])
+AM_GLIB_GNU_GETTEXT
+
+
+dnl ***********************************************************************
+dnl Check for Required Programs
+dnl ***********************************************************************
+AC_PROG_CC
+##if cplusplus_enabled
+AC_PROG_CXX
+##endif
+AC_PROG_INSTALL
+PKG_PROG_PKG_CONFIG([0.22])
+AC_PATH_PROG([GLIB_GENMARSHAL], [glib-genmarshal])
+AC_PATH_PROG([GLIB_MKENUMS], [glib-mkenums])
+AC_PATH_PROG([GLIB_COMPILE_RESOURCES], [glib-compile-resources])
+GOBJECT_INTROSPECTION_CHECK([1.42.0])
+GLIB_GSETTINGS
+##if vala_enabled
+VAPIGEN_CHECK
+##endif
+##if appstream_enabled
+APPSTREAM_XML
+##endif
+
+
+##if std_version
+dnl ***********************************************************************
+dnl Ensure {{std_version}} is Supported
+dnl ***********************************************************************
+AX_CHECK_COMPILE_FLAG([-std={{std_version}}],
+ [CFLAGS="$CFLAGS -std={{std_version}}"],
+ [AC_MSG_ERROR([C compiler cannot compile {{std_version}} code])])
+##endif
+
+
+##if cplusplus_enabled
+##if std_version == "c11"
+dnl ***********************************************************************
+dnl Ensure C++11 is Supported
+dnl ***********************************************************************
+AX_CXX_COMPILE_STDCXX_11([noext], [mandatory])
+##endif
+##endif
+
+
+dnl ***********************************************************************
+dnl Setup Debug and Tracing Support
+dnl ***********************************************************************
+AC_ARG_ENABLE(debug,
+ AS_HELP_STRING([--enable-debug=@<:@no/minimum/yes@:>@],
+ [turn on debugging @<:@default=debug_default@:>@]),
+ ,
+ enable_debug=debug_default)
+AS_CASE(["$enable_debug"],
+ [yes],[
+ DEBUG_CFLAGS="$DEBUG_CFLAGS -O0"
+ DEBUG_CFLAGS="$DEBUG_CFLAGS -g"
+ ],
+ [minimum],[
+ DEBUG_CFLAGS="$DEBUG_CFLAGS -DG_DISABLE_CAST_CHECKS"
+ ],
+ [no],[
+ DEBUG_CFLAGS="$DEBUG_CFLAGS -DG_DISABLE_ASSERT"
+ DEBUG_CFLAGS="$DEBUG_CFLAGS -DG_DISABLE_CHECKS"
+ DEBUG_CFLAGS="$DEBUG_CFLAGS -DG_DISABLE_CAST_CHECKS"
+ ],
+ [])
+AC_SUBST(DEBUG_CFLAGS)
+
+
+dnl ***********************************************************************
+dnl Check for Required Packages
+dnl ***********************************************************************
+##if glib_min_version
+m4_define([glib_required_version], [{{glib_min_version}}])
+##else
+m4_define([glib_required_version], [2.47.0])
+##endif
+
+PKG_CHECK_MODULES({{NAME}}, [glib_required_version])
+
+
+dnl ***********************************************************************
+dnl Initialize Libtool
+dnl ***********************************************************************
+LT_PREREQ([2.2])
+LT_INIT
+
+
+dnl ***********************************************************************
+dnl Additional C Compiler Flags
+dnl ***********************************************************************
+AX_CHECK_COMPILE_FLAG([-Werror=unknown-warning-option], [
+ ax_compiler_flags_test="-Werror=unknown-warning-option"
+],[
+ ax_compiler_flags_test=""
+])
+AX_APPEND_COMPILE_FLAGS([ \
+ -Wall \
+ -Wcast-align \
+ -Wdeclaration-after-statement \
+ -Wempty-body \
+ -Werror=format-security \
+ -Werror=format=2 \
+ -Wextra \
+ -Wformat \
+ -Wformat-nonliteral \
+ -Wformat-security \
+ -Winit-self \
+ -Wmisleading-indentation \
+ -Wmissing-include-dirs \
+ -Wshift-negative-value \
+ -Wnested-externs \
+ -Wno-missing-field-initializers \
+ -Wno-sign-compare \
+ -Wno-strict-aliasing \
+ -Wno-uninitialized \
+ -Wno-unused-parameter \
+ -Wpointer-arith \
+ -Wredundant-decls \
+ -Wreturn-type \
+ -Wshadow \
+ -Wswitch-default \
+ -Wswitch-enum \
+ -Wundef \
+ -Wuninitialized \
+], [], [$ax_compiler_flags_test])
+AC_C_CONST
+
+
+dnl ***********************************************************************
+dnl Process .in Files
+dnl ***********************************************************************
+AC_CONFIG_FILES([
+ Makefile
+ build-aux/Makefile
+])
+AC_OUTPUT
+
+
+echo ""
+echo " ${PACKAGE} - ${VERSION}"
+echo ""
+echo " Developer Options"
+echo ""
+echo " Enable Debug ......................... : ${enable_debug}"
+echo ""
diff --git a/libide/template/ide-project-template.c b/libide/template/ide-project-template.c
new file mode 100644
index 0000000..ede6cde
--- /dev/null
+++ b/libide/template/ide-project-template.c
@@ -0,0 +1,133 @@
+/* ide-project-template.c
+ *
+ * Copyright (C) 2015 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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 "ide-project-template.h"
+
+G_DEFINE_INTERFACE (IdeProjectTemplate, ide_project_template, G_TYPE_OBJECT)
+
+static void
+ide_project_template_default_init (IdeProjectTemplateInterface *iface)
+{
+}
+
+gchar *
+ide_project_template_get_id (IdeProjectTemplate *self)
+{
+ g_return_val_if_fail (IDE_IS_PROJECT_TEMPLATE (self), NULL);
+
+ return IDE_PROJECT_TEMPLATE_GET_IFACE (self)->get_id (self);
+}
+
+gchar *
+ide_project_template_get_name (IdeProjectTemplate *self)
+{
+ g_return_val_if_fail (IDE_IS_PROJECT_TEMPLATE (self), NULL);
+
+ return IDE_PROJECT_TEMPLATE_GET_IFACE (self)->get_name (self);
+}
+
+gchar *
+ide_project_template_get_description (IdeProjectTemplate *self)
+{
+ g_return_val_if_fail (IDE_IS_PROJECT_TEMPLATE (self), NULL);
+
+ return IDE_PROJECT_TEMPLATE_GET_IFACE (self)->get_description (self);
+}
+
+/**
+ * ide_project_template_get_widget:
+ * @self: An #IdeProjectTemplate
+ *
+ * Get's the configuration widget for the template if there is one.
+ *
+ * Returns: (transfer none): A #GtkWidget.
+ */
+GtkWidget *
+ide_project_template_get_widget (IdeProjectTemplate *self)
+{
+ g_return_val_if_fail (IDE_IS_PROJECT_TEMPLATE (self), NULL);
+
+ return IDE_PROJECT_TEMPLATE_GET_IFACE (self)->get_widget (self);
+}
+
+/**
+ * ide_project_template_get_languages:
+ * @self: an #IdeProjectTemplate
+ *
+ * Gets the list of languages that this template can support when generating
+ * the project.
+ *
+ * Returns: (transfer full): A newly allocated, NULL terminated list of
+ * supported languages.
+ */
+gchar **
+ide_project_template_get_languages (IdeProjectTemplate *self)
+{
+ g_return_val_if_fail (IDE_IS_PROJECT_TEMPLATE (self), NULL);
+
+ return IDE_PROJECT_TEMPLATE_GET_IFACE (self)->get_languages (self);
+}
+
+gchar *
+ide_project_template_get_icon_name (IdeProjectTemplate *self)
+{
+ g_return_val_if_fail (IDE_IS_PROJECT_TEMPLATE (self), NULL);
+
+ return IDE_PROJECT_TEMPLATE_GET_IFACE (self)->get_icon_name (self);
+}
+
+/**
+ * ide_project_template_expand_async:
+ * @self: an #IdeProjectTemplate
+ * @params: (element-type utf8 GLib.Variant): A hashtable of template parameters.
+ * @cancellable: (nullable): A #GCancellable or %NULL.
+ * @callback: the callback for the asynchronous operation.
+ * @user_data: user data for @callback.
+ *
+ * Asynchronously requests expansion of the template.
+ *
+ * This may involve creating files and directories on disk as well as
+ * expanding files based on the contents of @params.
+ *
+ * It is expected that this method is only called once on an #IdeProjectTemplate.
+ */
+void
+ide_project_template_expand_async (IdeProjectTemplate *self,
+ GHashTable *params,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_PROJECT_TEMPLATE (self));
+ g_return_if_fail (params != NULL);
+ g_return_if_fail (g_hash_table_contains (params, "name"));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ IDE_PROJECT_TEMPLATE_GET_IFACE (self)->expand_async (self, params, cancellable, callback, user_data);
+}
+
+gboolean
+ide_project_template_expand_finish (IdeProjectTemplate *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_PROJECT_TEMPLATE (self), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+ return IDE_PROJECT_TEMPLATE_GET_IFACE (self)->expand_finish (self, result, error);
+}
diff --git a/libide/template/ide-project-template.h b/libide/template/ide-project-template.h
new file mode 100644
index 0000000..031370d
--- /dev/null
+++ b/libide/template/ide-project-template.h
@@ -0,0 +1,67 @@
+/* ide-project-template.h
+ *
+ * Copyright (C) 2015 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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_PROJECT_TEMPLATE_H
+#define IDE_PROJECT_TEMPLATE_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_PROJECT_TEMPLATE (ide_project_template_get_type())
+
+G_DECLARE_INTERFACE (IdeProjectTemplate, ide_project_template, IDE, PROJECT_TEMPLATE, GObject)
+
+struct _IdeProjectTemplateInterface
+{
+ GTypeInterface parent;
+
+ gchar *(*get_id) (IdeProjectTemplate *self);
+ gchar *(*get_name) (IdeProjectTemplate *self);
+ gchar *(*get_description) (IdeProjectTemplate *self);
+ GtkWidget *(*get_widget) (IdeProjectTemplate *self);
+ gchar **(*get_languages) (IdeProjectTemplate *self);
+ gchar *(*get_icon_name) (IdeProjectTemplate *self);
+ void (*expand_async) (IdeProjectTemplate *self,
+ GHashTable *params,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*expand_finish) (IdeProjectTemplate *self,
+ GAsyncResult *result,
+ GError **error);
+};
+
+gchar *ide_project_template_get_id (IdeProjectTemplate *self);
+gchar *ide_project_template_get_name (IdeProjectTemplate *self);
+gchar *ide_project_template_get_description (IdeProjectTemplate *self);
+GtkWidget *ide_project_template_get_widget (IdeProjectTemplate *self);
+gchar **ide_project_template_get_languages (IdeProjectTemplate *self);
+gchar *ide_project_template_get_icon_name (IdeProjectTemplate *self);
+void ide_project_template_expand_async (IdeProjectTemplate *self,
+ GHashTable *params,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ide_project_template_expand_finish (IdeProjectTemplate *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* IDE_PROJECT_TEMPLATE_H */
diff --git a/libide/template/ide-template-mako.c b/libide/template/ide-template-mako.c
new file mode 100644
index 0000000..be3eb62
--- /dev/null
+++ b/libide/template/ide-template-mako.c
@@ -0,0 +1,258 @@
+/* ide-template-parse.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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 <Python.h>
+#include "frameobject.h"
+
+#include "ide-template.h"
+#include "ide-template-private.h"
+
+static void
+set_pyerror (GError **error)
+{
+ PyObject *pyerror = PyErr_Occurred ();
+
+ if (pyerror != NULL)
+ {
+ PyObject *repr = PyObject_Str (pyerror);
+
+ if (repr != NULL && PyUnicode_Check (repr))
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "%s",
+ PyUnicode_AsUTF8AndSize (repr, NULL));
+ }
+
+ Py_XDECREF (repr);
+ }
+
+ if (error && !*error)
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "An unknown error occurred when parsing the template");
+ }
+}
+
+gboolean
+ide_template_parse (IdeTemplate *self,
+ GBytes *data,
+ GError **error)
+{
+ IdeTemplatePrivate *priv;
+ PyGILState_STATE gstate;
+ PyObject *module = NULL;
+ PyObject *klass = NULL;
+ PyObject *instance = NULL;
+ PyObject *args = NULL;
+ PyObject *kwargs = NULL;
+ const gchar *buf;
+ gboolean ret = FALSE;
+ gsize len;
+
+ g_return_val_if_fail (IDE_IS_TEMPLATE (self), FALSE);
+ g_return_val_if_fail (data != NULL, FALSE);
+
+ priv = IDE_TEMPLATE_GET_PRIVATE (self);
+
+ buf = g_bytes_get_data (data, &len);
+
+ if (len >= G_MAXINT)
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ "File is too large to map into template");
+ return FALSE;
+ }
+
+ gstate = PyGILState_Ensure ();
+
+ if (NULL == (module = PyImport_ImportModule ("mako.template")) ||
+ !PyModule_Check (module) ||
+ NULL == (klass = PyObject_GetAttrString (module, "Template")) ||
+ !PyType_Check (klass) ||
+ NULL == (args = PyTuple_New (0)) ||
+ !PyTuple_Check (args) ||
+ NULL == (kwargs = Py_BuildValue ("{s:s#}", "text", buf, (int)len)) ||
+ !PyDict_Check (kwargs) ||
+ !PyCallable_Check (klass) ||
+ NULL == (instance = PyObject_Call (klass, args, kwargs)))
+ goto cleanup;
+
+ g_assert (PyType_Ready ((PyTypeObject *)klass) == 0);
+ g_assert (PyObject_IsInstance (instance, klass));
+
+cleanup:
+
+ if (instance != NULL)
+ {
+ priv->pytemplate = instance;
+ instance = NULL;
+ ret = TRUE;
+ }
+ else
+ {
+ set_pyerror (error);
+ }
+
+ Py_XDECREF (args);
+ Py_XDECREF (kwargs);
+ Py_XDECREF (instance);
+ Py_XDECREF (klass);
+ Py_XDECREF (module);
+
+ PyGILState_Release (gstate);
+
+ return ret;
+}
+
+static PyObject *
+g_variant_to_PyObject (GVariant *variant)
+{
+ if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING))
+ {
+ const gchar *str;
+ gsize len;
+
+ str = g_variant_get_string (variant, &len);
+ return PyUnicode_FromStringAndSize (str, len);
+ }
+
+ return NULL;
+}
+
+static PyObject *
+g_value_to_PyObject (const GValue *value)
+{
+ if (G_VALUE_HOLDS_STRING (value))
+ return PyUnicode_FromString (g_value_get_string (value) ?: "");
+
+ if (G_VALUE_HOLDS_VARIANT (value))
+ return g_variant_to_PyObject (g_value_get_variant (value));
+
+ return NULL;
+}
+
+static void
+state_foreach_cb (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ PyObject *dict = user_data;
+ PyObject *pyvalue = NULL;
+ const gchar *var_name = key;
+ const GValue *var_value = value;
+
+ pyvalue = g_value_to_PyObject (var_value);
+
+ if (pyvalue != NULL)
+ {
+ PyDict_SetItemString (dict, var_name, pyvalue);
+ Py_DECREF (pyvalue);
+ }
+}
+
+static PyObject *
+state_to_pydict (IdeTemplateState *state)
+{
+ PyObject *dict;
+
+ g_assert (IDE_IS_TEMPLATE_STATE (state));
+
+ dict = PyDict_New ();
+
+ ide_template_state_foreach (state, state_foreach_cb, dict);
+
+ return dict;
+}
+
+gboolean
+ide_template_expand (IdeTemplate *self,
+ IdeTemplateState *state,
+ GOutputStream *stream,
+ GError **error)
+{
+ IdeTemplatePrivate *priv;
+ PyGILState_STATE gstate;
+ PyObject *kwargs = NULL;
+ PyObject *result = NULL;
+ PyObject *method = NULL;
+ PyObject *args = NULL;
+ const char *str;
+ Py_ssize_t len;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (IDE_IS_TEMPLATE (self), FALSE);
+ g_return_val_if_fail (IDE_IS_TEMPLATE_STATE (state), FALSE);
+ g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE);
+
+ priv = IDE_TEMPLATE_GET_PRIVATE (self);
+
+ if (priv->pytemplate == NULL)
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_INITIALIZED,
+ "IdeTemplate has not been compiled");
+ return FALSE;
+ }
+
+ gstate = PyGILState_Ensure ();
+
+ kwargs = state_to_pydict (state);
+ g_assert (kwargs != NULL);
+ g_assert (PyDict_Check (kwargs));
+
+ method = PyObject_GetAttrString (priv->pytemplate, "render");
+ g_assert (method != NULL);
+ g_assert (PyCallable_Check (method));
+
+ //args = Py_BuildValue ("(O)", priv->pytemplate);
+ args = PyTuple_New (0);
+ g_assert (args != NULL);
+ g_assert (PyTuple_Check (args));
+
+ result = PyObject_Call (method, args, kwargs);
+
+ if (result == NULL)
+ {
+ set_pyerror (error);
+ goto cleanup;
+ }
+
+ g_assert (PyUnicode_Check (result));
+
+ str = PyUnicode_AsUTF8AndSize (result, &len);
+
+ if (g_output_stream_write_all (stream, str, len, NULL, NULL, error))
+ ret = TRUE;
+
+cleanup:
+ Py_XDECREF (args);
+ Py_XDECREF (kwargs);
+ Py_XDECREF (method);
+ Py_XDECREF (result);
+
+ PyGILState_Release (gstate);
+
+ return ret;
+}
diff --git a/libide/template/ide-template-private.h b/libide/template/ide-template-private.h
new file mode 100644
index 0000000..c0040a7
--- /dev/null
+++ b/libide/template/ide-template-private.h
@@ -0,0 +1,51 @@
+/* ide-template-private.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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_TEMPLATE_PRIVATE_H
+#define IDE_TEMPLATE_PRIVATE_H
+
+#include <Python.h>
+
+#include "ide-template.h"
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+ gchar *path;
+ GBytes *data;
+ GError *compile_error;
+ PyObject *pytemplate;
+ guint compiled : 1;
+ guint compile_failed : 1;
+} IdeTemplatePrivate;
+
+gboolean ide_template_parse (IdeTemplate *self,
+ GBytes *input,
+ GError **error);
+gboolean ide_template_expand (IdeTemplate *self,
+ IdeTemplateState *state,
+ GOutputStream *stream,
+ GError **error);
+
+#define IDE_TEMPLATE_GET_PRIVATE(self) \
+ (G_TYPE_INSTANCE_GET_PRIVATE(self, IDE_TYPE_TEMPLATE, IdeTemplatePrivate))
+
+G_END_DECLS
+
+#endif /* IDE_TEMPLATE_PRIVATE_H */
diff --git a/libide/template/ide-template-state.c b/libide/template/ide-template-state.c
new file mode 100644
index 0000000..19a4f44
--- /dev/null
+++ b/libide/template/ide-template-state.c
@@ -0,0 +1,160 @@
+/* ide-template-state.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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 "ide-template-state.h"
+
+struct _IdeTemplateState
+{
+ GObject parent_instance;
+ GHashTable *state;
+};
+
+G_DEFINE_TYPE (IdeTemplateState, ide_template_state, G_TYPE_OBJECT)
+
+static void
+_g_value_free (gpointer data)
+{
+ GValue *value = data;
+
+ if (value != NULL && value->g_type)
+ {
+ g_value_unset (value);
+ g_slice_free (GValue, value);
+ }
+}
+
+static void
+ide_template_state_finalize (GObject *object)
+{
+ IdeTemplateState *self = (IdeTemplateState *)object;
+
+ g_clear_pointer (&self->state, g_hash_table_unref);
+
+ G_OBJECT_CLASS (ide_template_state_parent_class)->finalize (object);
+}
+
+static void
+ide_template_state_class_init (IdeTemplateStateClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ide_template_state_finalize;
+}
+
+static void
+ide_template_state_init (IdeTemplateState *self)
+{
+ self->state = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _g_value_free);
+}
+
+void
+ide_template_state_add_value (IdeTemplateState *self,
+ const gchar *key,
+ const GValue *value)
+{
+ GValue *val;
+
+ g_return_if_fail (IDE_IS_TEMPLATE_STATE (self));
+ g_return_if_fail (key != NULL);
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_VALUE_TYPE (value));
+ g_value_copy (value, val);
+
+ g_hash_table_insert (self->state, g_strdup (key), val);
+}
+
+void
+ide_template_state_add_string (IdeTemplateState *self,
+ const gchar *key,
+ const gchar *value)
+{
+ GValue *val;
+
+ g_return_if_fail (IDE_IS_TEMPLATE_STATE (self));
+ g_return_if_fail (key != NULL);
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_STRING);
+ g_value_set_string (val, value);
+
+ g_hash_table_insert (self->state, g_strdup (key), val);
+}
+
+void
+ide_template_state_add_variant (IdeTemplateState *self,
+ const gchar *key,
+ GVariant *value)
+{
+ GValue *val;
+
+ g_return_if_fail (IDE_IS_TEMPLATE_STATE (self));
+ g_return_if_fail (key != NULL);
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_VARIANT);
+ g_value_set_variant (val, value);
+
+ g_hash_table_insert (self->state, g_strdup (key), val);
+}
+
+const GValue *
+ide_template_state_lookup (IdeTemplateState *self,
+ const gchar *key)
+{
+ g_return_val_if_fail (IDE_IS_TEMPLATE_STATE (self), NULL);
+ g_return_val_if_fail (key != NULL, NULL);
+
+ return g_hash_table_lookup (self->state, key);
+}
+
+IdeTemplateState *
+ide_template_state_new (void)
+{
+ return g_object_new (IDE_TYPE_TEMPLATE_STATE, NULL);
+}
+
+/**
+ * ide_template_state_foreach:
+ * @self: an #IdeTemplateState
+ * @callback: (scope call): A callback to execute for every key/value pair
+ * @callback_data: user data for @calback
+ *
+ * Calls @callback for every key/value pair in @self.
+ *
+ * The key is a UTF-8 string which should not be modified or freed.
+ * The value is a #GValue which should not be modified or freed.
+ */
+void
+ide_template_state_foreach (IdeTemplateState *self,
+ GHFunc callback,
+ gpointer callback_data)
+{
+ g_return_if_fail (IDE_IS_TEMPLATE_STATE (self));
+ g_return_if_fail (callback != NULL);
+
+ g_hash_table_foreach (self->state, callback, callback_data);
+}
+
+gsize
+ide_template_state_size (IdeTemplateState *self)
+{
+ g_return_val_if_fail (IDE_IS_TEMPLATE_STATE (self), 0);
+
+ return g_hash_table_size (self->state);
+}
diff --git a/libide/template/ide-template-state.h b/libide/template/ide-template-state.h
new file mode 100644
index 0000000..2e93291
--- /dev/null
+++ b/libide/template/ide-template-state.h
@@ -0,0 +1,49 @@
+/* ide-template-state.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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_TEMPLATE_STATE_H
+#define IDE_TEMPLATE_STATE_H
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_TEMPLATE_STATE (ide_template_state_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeTemplateState, ide_template_state, IDE, TEMPLATE_STATE, GObject)
+
+IdeTemplateState *ide_template_state_new (void);
+void ide_template_state_add_value (IdeTemplateState *self,
+ const gchar *key,
+ const GValue *value);
+void ide_template_state_add_string (IdeTemplateState *self,
+ const gchar *key,
+ const gchar *value);
+void ide_template_state_add_variant (IdeTemplateState *self,
+ const gchar *key,
+ GVariant *value);
+const GValue *ide_template_state_lookup (IdeTemplateState *self,
+ const gchar *key);
+void ide_template_state_foreach (IdeTemplateState *self,
+ GHFunc callback,
+ gpointer callback_data);
+gsize ide_template_state_size (IdeTemplateState *self);
+
+G_END_DECLS
+
+#endif /* IDE_TEMPLATE_STATE_H */
diff --git a/libide/template/ide-template.c b/libide/template/ide-template.c
new file mode 100644
index 0000000..54d0a23
--- /dev/null
+++ b/libide/template/ide-template.c
@@ -0,0 +1,473 @@
+/* ide-template.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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 "ide-template.h"
+#include "ide-template-private.h"
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeTemplate, ide_template, G_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+ PROP_PATH,
+ LAST_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+
+static void
+ide_template_set_path (IdeTemplate *self,
+ const gchar *path)
+{
+ IdeTemplatePrivate *priv = ide_template_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_TEMPLATE (self));
+ g_return_if_fail (path != NULL);
+
+ if (0 != g_strcmp0 (path, priv->path))
+ {
+ g_free (priv->path);
+ priv->path = g_strdup (path);
+ }
+}
+
+static void
+ide_template_finalize (GObject *object)
+{
+ IdeTemplate *self = (IdeTemplate *)object;
+ IdeTemplatePrivate *priv = ide_template_get_instance_private (self);
+
+ g_clear_pointer (&priv->path, g_free);
+ g_clear_pointer (&priv->compile_error, g_error_free);
+
+ G_OBJECT_CLASS (ide_template_parent_class)->finalize (object);
+}
+
+static void
+ide_template_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeTemplate *self = IDE_TEMPLATE (object);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ g_value_set_string (value, ide_template_get_path (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_template_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeTemplate *self = IDE_TEMPLATE (object);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ ide_template_set_path (self, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_template_class_init (IdeTemplateClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ide_template_finalize;
+ object_class->get_property = ide_template_get_property;
+ object_class->set_property = ide_template_set_property;
+
+ properties [PROP_PATH] =
+ g_param_spec_string ("path",
+ "Path",
+ "Path",
+ NULL,
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_EXPLICIT_NOTIFY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+ide_template_init (IdeTemplate *self)
+{
+}
+
+static void
+ide_template_compile_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ IdeTemplate *self = source_object;
+ IdeTemplatePrivate *priv = ide_template_get_instance_private (self);
+ g_autoptr(GBytes) bytes = NULL;
+ GError *error = NULL;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (IDE_IS_TEMPLATE (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ if (priv->path == NULL)
+ {
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_FILENAME,
+ "No template path was specified.");
+ return;
+ }
+
+ if (g_str_has_prefix (priv->path, "resource://"))
+ {
+ bytes = g_resources_lookup_data (priv->path, G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
+
+ if (bytes == NULL)
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+ }
+ else
+ {
+ gchar *contents = NULL;
+ gsize length = 0;
+
+ if (!g_file_get_contents (priv->path, &contents, &length, &error))
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ bytes = g_bytes_new_take (contents, length);
+ }
+
+ if (!ide_template_parse (self, bytes, &error))
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ priv->compiled = TRUE;
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_template_compile_async (IdeTemplate *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ IdeTemplatePrivate *priv = ide_template_get_instance_private (self);
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (IDE_IS_TEMPLATE (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ if (priv->compiled)
+ {
+ if (priv->compile_failed)
+ g_task_return_error (task, g_error_copy (priv->compile_error));
+ else
+ g_task_return_boolean (task, TRUE);
+ return;
+ }
+
+ g_task_run_in_thread (task, ide_template_compile_worker);
+}
+
+static gboolean
+ide_template_compile_finish (IdeTemplate *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ IdeTemplatePrivate *priv = ide_template_get_instance_private (self);
+ GError *local_error = NULL;
+
+ g_assert (IDE_IS_TEMPLATE (self));
+ g_assert (G_IS_TASK (result));
+
+ priv->compiled = TRUE;
+
+ if (!g_task_propagate_boolean (G_TASK (result), &local_error))
+ {
+ priv->compile_failed = TRUE;
+ priv->compile_error = g_error_copy (local_error);
+ g_propagate_error (error, local_error);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+ide_template_expand_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ IdeTemplate *self = source_object;
+ IdeTemplateState *state;
+ GOutputStream *stream;
+ GError *error = NULL;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (IDE_IS_TEMPLATE (self));
+
+ state = g_object_get_data (G_OBJECT (task), "STATE");
+ stream = g_object_get_data (G_OBJECT (task), "STREAM");
+
+ g_assert (IDE_IS_TEMPLATE_STATE (state));
+ g_assert (G_IS_OUTPUT_STREAM (stream));
+
+ if (!ide_template_expand (self, state, stream, &error))
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_template_expand_compile_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeTemplate *self = (IdeTemplate *)object;
+ g_autoptr(GTask) task = user_data;
+ GError *error = NULL;
+
+ g_assert (IDE_IS_TEMPLATE (self));
+ g_assert (G_IS_TASK (task));
+
+ if (!ide_template_compile_finish (self, result, &error))
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ g_task_run_in_thread (task, ide_template_expand_worker);
+}
+
+void
+ide_template_expand_async (IdeTemplate *self,
+ IdeTemplateState *state,
+ GOutputStream *stream,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ IdeTemplatePrivate *priv = ide_template_get_instance_private (self);
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (IDE_IS_TEMPLATE (self));
+ g_return_if_fail (!state || IDE_IS_TEMPLATE_STATE (state));
+ g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ if (priv->compile_failed)
+ {
+ g_task_return_error (task, g_error_copy (priv->compile_error));
+ return;
+ }
+
+ g_object_set_data_full (G_OBJECT (task), "STATE", g_object_ref (state), g_object_unref);
+ g_object_set_data_full (G_OBJECT (task), "STREAM", g_object_ref (stream), g_object_unref);
+
+ if (priv->pytemplate == NULL)
+ {
+ ide_template_compile_async (self,
+ cancellable,
+ ide_template_expand_compile_cb,
+ g_object_ref (task));
+ }
+ else
+ {
+ g_task_run_in_thread (task, ide_template_expand_worker);
+ }
+}
+
+gboolean
+ide_template_expand_finish (IdeTemplate *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_TEMPLATE (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+ide_template_expand_file_expand_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeTemplate *self = (IdeTemplate *)object;
+ g_autoptr(GTask) task = user_data;
+ GError *error = NULL;
+
+ g_assert (IDE_IS_TEMPLATE (self));
+
+ if (!ide_template_expand_finish (self, result, &error))
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_template_expand_file_replace_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GFile *file = (GFile *)object;
+ g_autoptr(GTask) task = user_data;
+ g_autoptr(GFileOutputStream) stream = NULL;
+ IdeTemplate *self;
+ IdeTemplateState *state;
+ GError *error = NULL;
+
+ g_assert (G_IS_FILE (file));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (G_IS_TASK (task));
+
+ stream = g_file_replace_finish (file, result, &error);
+
+ if (stream == NULL)
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ self = g_task_get_source_object (task);
+ g_assert (IDE_IS_TEMPLATE (self));
+
+ state = g_task_get_task_data (task);
+ g_assert (!state || IDE_IS_TEMPLATE_STATE (state));
+
+ ide_template_expand_async (self,
+ state,
+ G_OUTPUT_STREAM (stream),
+ g_task_get_cancellable (task),
+ ide_template_expand_file_expand_cb,
+ g_object_ref (task));
+}
+
+/**
+ * ide_template_expand_file_async:
+ * @self: an #IdeTemplate.
+ * @state: (nullable): an #IdeTemplateState or %NULL
+ * @file: a #GFile
+ * @cancellable: (nullable): a #GCancellable or %NULL
+ * @callback: the callback to execute upon completion
+ * @user_data: user data for @callback
+ *
+ * This function works like ide_template_expand_async() except that the
+ * contents are written into @file, which is also replaced asynchronously.
+ *
+ * Call ide_template_expand_file_finish() from @callback to check for any
+ * errors expanding the template.
+ */
+void
+ide_template_expand_file_async (IdeTemplate *self,
+ IdeTemplateState *state,
+ GFile *file,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (IDE_IS_TEMPLATE (self));
+ g_return_if_fail (!state || IDE_IS_TEMPLATE_STATE (state));
+ g_return_if_fail (G_IS_FILE (file));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ if (state != NULL)
+ g_task_set_task_data (task, g_object_ref (state), g_object_unref);
+
+ g_file_replace_async (file,
+ NULL,
+ FALSE,
+ G_FILE_CREATE_REPLACE_DESTINATION,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ ide_template_expand_file_replace_cb,
+ g_object_ref (task));
+}
+
+gboolean
+ide_template_expand_file_finish (IdeTemplate *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_TEMPLATE (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+IdeTemplate *
+ide_template_new (const gchar *path)
+{
+ return g_object_new (IDE_TYPE_TEMPLATE,
+ "path", path,
+ NULL);
+}
+
+/**
+ * ide_template_get_path:
+ * @self: an #IdeTemplate
+ *
+ * Gets the path of the template. If the template is a resource, the path
+ * begins with resource:///.
+ */
+const gchar *
+ide_template_get_path (IdeTemplate *self)
+{
+ IdeTemplatePrivate *priv = ide_template_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_TEMPLATE (self), NULL);
+
+ return priv->path;
+}
diff --git a/libide/template/ide-template.h b/libide/template/ide-template.h
new file mode 100644
index 0000000..b6be07f
--- /dev/null
+++ b/libide/template/ide-template.h
@@ -0,0 +1,59 @@
+/* ide-template.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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_TEMPLATE_H
+#define IDE_TEMPLATE_H
+
+#include <gio/gio.h>
+
+#include "ide-template-state.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_TEMPLATE (ide_template_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeTemplate, ide_template, IDE, TEMPLATE, GObject)
+
+struct _IdeTemplateClass
+{
+ GObjectClass parent;
+};
+
+const gchar *ide_template_get_path (IdeTemplate *self);
+void ide_template_expand_async (IdeTemplate *self,
+ IdeTemplateState *state,
+ GOutputStream *stream,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ide_template_expand_finish (IdeTemplate *self,
+ GAsyncResult *result,
+ GError **error);
+void ide_template_expand_file_async (IdeTemplate *self,
+ IdeTemplateState *state,
+ GFile *file,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ide_template_expand_file_finish (IdeTemplate *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* IDE_TEMPLATE_H */
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index c0fc119..e602acb 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -4,6 +4,7 @@ SUBDIRS = \
clang \
command-bar \
c-pack \
+ create-project \
ctags \
devhelp \
file-search \
@@ -14,6 +15,7 @@ SUBDIRS = \
html-completion \
html-preview \
jedi \
+ library-template \
project-tree \
python-gi-imports-completion \
mingw \
diff --git a/plugins/create-project/Makefile.am b/plugins/create-project/Makefile.am
new file mode 100644
index 0000000..f840f31
--- /dev/null
+++ b/plugins/create-project/Makefile.am
@@ -0,0 +1,46 @@
+if ENABLE_CREATE_PROJECT_PLUGIN
+
+DISTCLEANFILES =
+BUILT_SOURCES =
+CLEANFILES =
+EXTRA_DIST = $(plugin_DATA)
+
+plugindir = $(libdir)/gnome-builder/plugins
+plugin_LTLIBRARIES = libcreate-project-plugin.la
+dist_plugin_DATA = create-project.plugin
+
+libcreate_project_plugin_la_SOURCES = \
+ gbp-create-project-plugin.c \
+ gbp-create-project-tool.c \
+ gbp-create-project-tool.h \
+ $(NULL)
+
+nodist_libcreate_project_plugin_la_SOURCES = \
+ gbp-create-project-resources.c \
+ gbp-create-project-resources.h
+
+libcreate_project_plugin_la_CFLAGS = \
+ $(LIBIDE_CFLAGS) \
+ $(OPTIMIZE_CFLAGS) \
+ -I$(top_srcdir)/libide \
+ -I$(top_srcdir)/contrib/egg \
+ $(NULL)
+
+libcreate_project_plugin_la_LDFLAGS = \
+ $(OPTIMIZE_LDFLAGS) \
+ -avoid-version \
+ -module \
+ -export-regex peas_register_types \
+ $(NULL)
+
+glib_resources_c = gbp-create-project-resources.c
+glib_resources_h = gbp-create-project-resources.h
+glib_resources_xml = gbp-create-project.gresource.xml
+glib_resources_namespace = gbp_create_project
+include $(top_srcdir)/build/autotools/Makefile.am.gresources
+
+include $(top_srcdir)/plugins/Makefile.plugin
+
+endif
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/create-project/configure.ac b/plugins/create-project/configure.ac
new file mode 100644
index 0000000..6491ff2
--- /dev/null
+++ b/plugins/create-project/configure.ac
@@ -0,0 +1,12 @@
+# --enable-create-project-plugin=yes/no
+AC_ARG_ENABLE([create-project-plugin],
+ [AS_HELP_STRING([--enable-create-project-plugin=@<:@yes/no@:>@],
+ [Build with support for creating projects.])],
+ [enable_create_project_plugin=$enableval],
+ [enable_create_project_plugin=yes])
+
+# for if ENABLE_CREATE_PROJECT_PLUGIN in Makefile.am
+AM_CONDITIONAL(ENABLE_CREATE_PROJECT_PLUGIN, test x$enable_create_project_plugin != xno)
+
+# Ensure our makefile is generated by autoconf
+AC_CONFIG_FILES([plugins/create-project/Makefile])
diff --git a/plugins/create-project/create-project.plugin b/plugins/create-project/create-project.plugin
new file mode 100644
index 0000000..afc340d
--- /dev/null
+++ b/plugins/create-project/create-project.plugin
@@ -0,0 +1,10 @@
+[Plugin]
+Module=create-project-plugin
+Name=Create Project
+Description=Create projects with Builder
+Authors=Christian Hergert <christian hergert me>
+Copyright=Copyright © 2015 Christian Hergert
+Builtin=true
+Hidden=true
+X-Tool-Name=create-project
+X-Tool-Description=Create a new project
diff --git a/plugins/create-project/gbp-create-project-plugin.c
b/plugins/create-project/gbp-create-project-plugin.c
new file mode 100644
index 0000000..70e02bb
--- /dev/null
+++ b/plugins/create-project/gbp-create-project-plugin.c
@@ -0,0 +1,30 @@
+/* gbp-create-project-plugin.c
+ *
+ * Copyright (C) 2015 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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 <ide.h>
+#include <libpeas/peas.h>
+
+#include "gbp-create-project-tool.h"
+
+void
+peas_register_types (PeasObjectModule *module)
+{
+ peas_object_module_register_extension_type (module,
+ IDE_TYPE_APPLICATION_TOOL,
+ GBP_TYPE_CREATE_PROJECT_TOOL);
+}
diff --git a/plugins/create-project/gbp-create-project-tool.c
b/plugins/create-project/gbp-create-project-tool.c
new file mode 100644
index 0000000..6b71322
--- /dev/null
+++ b/plugins/create-project/gbp-create-project-tool.c
@@ -0,0 +1,358 @@
+/* gbp-create-project-tool.c
+ *
+ * Copyright (C) 2015 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <ctype.h>
+#include <glib/gi18n.h>
+#include <libpeas/peas.h>
+
+#include "gbp-create-project-tool.h"
+
+struct _GbpCreateProjectTool
+{
+ GObject parent_instance;
+ gboolean list_templates;
+ gchar **args;
+ gchar *template;
+ PeasExtensionSet *templates;
+};
+
+static void application_tool_iface_init (IdeApplicationToolInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GbpCreateProjectTool, gbp_create_project_tool, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_APPLICATION_TOOL,
+ application_tool_iface_init))
+
+static void
+gbp_create_project_tool_constructed (GObject *object)
+{
+ GbpCreateProjectTool *self = (GbpCreateProjectTool *)object;
+
+ self->templates = peas_extension_set_new (peas_engine_get_default (),
+ IDE_TYPE_PROJECT_TEMPLATE,
+ NULL);
+
+ G_OBJECT_CLASS (gbp_create_project_tool_parent_class)->constructed (object);
+}
+
+static void
+gbp_create_project_tool_finalize (GObject *object)
+{
+ GbpCreateProjectTool *self = (GbpCreateProjectTool *)object;
+
+ g_clear_object (&self->templates);
+ g_clear_pointer (&self->args, g_strfreev);
+ g_clear_pointer (&self->template, g_free);
+
+ G_OBJECT_CLASS (gbp_create_project_tool_parent_class)->finalize (object);
+}
+
+static void
+gbp_create_project_tool_class_init (GbpCreateProjectToolClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = gbp_create_project_tool_constructed;
+ object_class->finalize = gbp_create_project_tool_finalize;
+}
+
+static void
+gbp_create_project_tool_init (GbpCreateProjectTool *self)
+{
+}
+
+static void
+print_template (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeProjectTemplate *template = (IdeProjectTemplate *)exten;
+ GbpCreateProjectTool *self = user_data;
+ gchar *id;
+
+ g_assert (IDE_IS_PROJECT_TEMPLATE (template));
+ g_assert (GBP_IS_CREATE_PROJECT_TOOL (self));
+
+ id = ide_project_template_get_id (template);
+ g_print (" %s\n", id);
+ g_free (id);
+}
+
+static void
+gbp_create_project_tool_list_templates (GbpCreateProjectTool *self)
+{
+ g_assert (GBP_IS_CREATE_PROJECT_TOOL (self));
+
+ g_print ("\n");
+ peas_extension_set_foreach (self->templates, print_template, self);
+ g_print ("\n");
+}
+
+static gboolean
+gbp_create_project_tool_parse (GbpCreateProjectTool *self,
+ GError **error)
+{
+ g_autoptr(GOptionContext) context = NULL;
+ GOptionEntry entries[] = {
+ { "list-templates", 'l', 0, G_OPTION_ARG_NONE, &self->list_templates,
+ N_("List available templates") },
+ { "template", 't', 0, G_OPTION_ARG_STRING, &self->template,
+ N_("Project template to generate") },
+ { NULL }
+ };
+
+ g_assert (GBP_IS_CREATE_PROJECT_TOOL (self));
+
+ context = g_option_context_new (_("create-project [OPTION...] PROJECT_NAME"));
+ g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+
+ if (!g_option_context_parse_strv (context, &self->args, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+typedef struct
+{
+ const gchar *id;
+ IdeProjectTemplate *result;
+} TemplateLookup;
+
+static void
+find_template_cb (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ TemplateLookup *lookup = user_data;
+ g_autofree gchar *id = NULL;
+
+ if (lookup->result != NULL)
+ return;
+
+ id = ide_project_template_get_id (IDE_PROJECT_TEMPLATE (exten));
+ if (ide_str_equal0 (id, lookup->id))
+ lookup->result = IDE_PROJECT_TEMPLATE (exten);
+}
+
+static IdeProjectTemplate *
+find_template (GbpCreateProjectTool *self)
+{
+ TemplateLookup lookup;
+
+ g_assert (GBP_IS_CREATE_PROJECT_TOOL (self));
+ g_assert (self->template != NULL);
+
+ lookup.id = self->template;
+ lookup.result = NULL;
+
+ peas_extension_set_foreach (self->templates, find_template_cb, &lookup);
+
+ return lookup.result;
+}
+
+static gboolean
+validate_name (GbpCreateProjectTool *self,
+ const gchar *name,
+ GError **error)
+{
+ for (; *name; name = g_utf8_next_char (name))
+ {
+ gunichar ch = g_utf8_get_char (name);
+
+ switch (ch)
+ {
+ default:
+ if (isascii (ch))
+ continue;
+ /* Fall through */
+ case '=':
+ case ':':
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ _("Filename must be ascii and may not contain : or ="));
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+extract_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeProjectTemplate *template = (IdeProjectTemplate *)object;
+ g_autoptr(GTask) task = user_data;
+ GError *error = NULL;
+
+ g_assert (IDE_IS_PROJECT_TEMPLATE (template));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (G_IS_TASK (task));
+
+ if (!ide_project_template_expand_finish (template, result, &error))
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ g_task_return_int (task, 0);
+}
+
+static gboolean
+extract_params (GbpCreateProjectTool *self,
+ GHashTable *params,
+ GError **error)
+{
+ gint i;
+
+ g_assert (GBP_IS_CREATE_PROJECT_TOOL (self));
+ g_assert (params != NULL);
+
+ if (self->args && g_strv_length (self->args) > 2)
+ {
+ for (i = 2; self->args [i]; i++)
+ {
+ const gchar *arg = self->args [i];
+ const gchar *eq;
+
+ if ((eq = strchr (arg, '=')) != NULL)
+ {
+ g_autofree gchar *value = NULL;
+ gchar *key;
+ GVariant *var;
+
+ key = g_strndup (arg, (eq - arg));
+ value = g_strdup (eq + 1);
+
+ var = g_variant_parse (NULL, value, NULL, NULL, NULL);
+ if (var == NULL)
+ var = g_variant_new_string (value);
+
+ g_hash_table_insert (params, key, g_variant_ref_sink (var));
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+gbp_create_project_tool_run_async (IdeApplicationTool *tool,
+ const gchar * const *arguments,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GbpCreateProjectTool *self = (GbpCreateProjectTool *)tool;
+ IdeProjectTemplate *template;
+ g_autoptr(GTask) task = NULL;
+ g_autoptr(GHashTable) params = NULL;
+ const gchar *name;
+ GError *error = NULL;
+
+ g_assert (GBP_IS_CREATE_PROJECT_TOOL (self));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ /* pretend that "create-project" is argv[0] */
+ self->args = g_strdupv ((gchar **)&arguments[1]);
+
+ if (!gbp_create_project_tool_parse (self, &error))
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ if (self->list_templates)
+ {
+ gbp_create_project_tool_list_templates (self);
+ g_task_return_int (task, 0);
+ return;
+ }
+
+ if (!self->args || g_strv_length (self->args) < 2)
+ {
+ g_printerr (_("Please specify a project name.\n"));
+ g_task_return_int (task, 1);
+ return;
+ }
+
+ name = self->args [1];
+
+ if (!validate_name (self, name, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ g_task_return_error (task, error);
+ return;
+ }
+
+ if (!self->template || !(template = find_template (self)))
+ {
+ g_printerr (_("Please specify a project template.\n"));
+ gbp_create_project_tool_list_templates (self);
+ g_task_return_int (task, 1);
+ return;
+ }
+
+ params = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify)g_variant_unref);
+
+ if (!extract_params (self, params, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ g_task_return_error (task, error);
+ return;
+ }
+
+ g_hash_table_insert (params,
+ g_strdup ("name"),
+ g_variant_ref_sink (g_variant_new_string (name)));
+
+ ide_project_template_expand_async (template,
+ params,
+ NULL,
+ extract_cb,
+ g_object_ref (task));
+}
+
+static gint
+gbp_create_project_tool_run_finish (IdeApplicationTool *tool,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_assert (GBP_IS_CREATE_PROJECT_TOOL (tool));
+ g_assert (G_IS_TASK (result));
+
+ return g_task_propagate_int (G_TASK (result), error);
+}
+
+static void
+application_tool_iface_init (IdeApplicationToolInterface *iface)
+{
+ iface->run_async = gbp_create_project_tool_run_async;
+ iface->run_finish = gbp_create_project_tool_run_finish;
+}
diff --git a/plugins/create-project/gbp-create-project-tool.h
b/plugins/create-project/gbp-create-project-tool.h
new file mode 100644
index 0000000..1fa888e
--- /dev/null
+++ b/plugins/create-project/gbp-create-project-tool.h
@@ -0,0 +1,32 @@
+/* gbp-create-project-tool.h
+ *
+ * Copyright (C) 2015 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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 GBP_CREATE_PROJECT_TOOL_H
+#define GBP_CREATE_PROJECT_TOOL_H
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_CREATE_PROJECT_TOOL (gbp_create_project_tool_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpCreateProjectTool, gbp_create_project_tool, GBP, CREATE_PROJECT_TOOL, GObject)
+
+G_END_DECLS
+
+#endif /* GBP_CREATE_PROJECT_TOOL_H */
diff --git a/plugins/create-project/gbp-create-project.gresource.xml
b/plugins/create-project/gbp-create-project.gresource.xml
new file mode 100644
index 0000000..7c5c913
--- /dev/null
+++ b/plugins/create-project/gbp-create-project.gresource.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/builder/plugins/create-project-plugin">
+ </gresource>
+</gresources>
diff --git a/plugins/library-template/Makefile.am b/plugins/library-template/Makefile.am
new file mode 100644
index 0000000..bff686c
--- /dev/null
+++ b/plugins/library-template/Makefile.am
@@ -0,0 +1,15 @@
+if ENABLE_LIBRARY_TEMPLATE_PLUGIN
+
+EXTRA_DIST = $(plugin_DATA)
+
+plugindir = $(libdir)/gnome-builder/plugins
+dist_plugin_DATA = library-template.plugin
+
+moduledir = $(libdir)/gnome-builder/plugins/library_template
+dist_module_DATA = library_template/__init__.py
+
+endif
+
+GITIGNOREFILES = library_template/__pycache__
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/library-template/configure.ac b/plugins/library-template/configure.ac
new file mode 100644
index 0000000..82ef9a4
--- /dev/null
+++ b/plugins/library-template/configure.ac
@@ -0,0 +1,11 @@
+AC_ARG_ENABLE([library-template-plugin],
+ [AS_HELP_STRING([--enable-library-template-plugin=@<:@auto/yes/no@:>@],
+ [Build with support for creating library projects.])],
+ [enable_library_template_plugin=$enableval],
+ [enable_library_template_plugin=yes])
+
+# for if ENABLE_LIBRARY_TEMPLATE_PLUGIN in Makefile.am
+AM_CONDITIONAL(ENABLE_LIBRARY_TEMPLATE_PLUGIN, test x$enable_library_template_plugin = xyes)
+
+# Ensure our makefile is generated by autoconf
+AC_CONFIG_FILES([plugins/library-template/Makefile])
diff --git a/plugins/library-template/library-template.plugin
b/plugins/library-template/library-template.plugin
new file mode 100644
index 0000000..9908d0d
--- /dev/null
+++ b/plugins/library-template/library-template.plugin
@@ -0,0 +1,9 @@
+[Plugin]
+Module=library_template
+Loader=python3
+Name=Library Template
+Description=Provides templates for creating libraries
+Authors=Christian Hergert <christian hergert me>
+Copyright=Copyright © 2015 Christian Hergert
+Builtin=true
+Hidden=true
diff --git a/plugins/library-template/library_template/__init__.py
b/plugins/library-template/library_template/__init__.py
new file mode 100644
index 0000000..39c842b
--- /dev/null
+++ b/plugins/library-template/library_template/__init__.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python3
+
+#
+# library_template.py
+#
+# Copyright (C) 2015 Christian Hergert <chris dronelabs com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 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/>.
+#
+
+from gi.repository import Gio
+from gi.repository import GObject
+from gi.repository import Ide
+
+class LibraryProjectTemplate(GObject.Object, Ide.ProjectTemplate):
+ def do_get_id(self):
+ return 'library'
+
+ def do_get_icon_name(self):
+ return 'template-library-symbolic'
+
+ def do_get_name(self):
+ return _("Library")
+
+ def do_get_description(self):
+ return _("Create a project containing a shared library")
+
+ def do_get_languages(self):
+ return ['C']
+
+ def do_get_widget(self):
+ return None
+
+ def do_expand_async(self, params, cancellable, callback, user_data):
+ task = Gio.Task.new(self, cancellable, callback)
+
+ """
+ outFile = Gio.File.new_for_path("/home/christian/test.tmpl.out")
+
+ state = Ide.TemplateState()
+ for k,v in params.items():
+ state.add_variant(k, v)
+
+ tmpl = Ide.Template(path="/home/christian/test.tmpl")
+ tmpl.expand_file_async(state, outFile, cancellable, lambda *_: task.return_boolean(True))
+ """
+
+ task.return_boolean(True)
+
+ def do_expand_finish(self, result):
+ return result.propagate_boolean()
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]