[gtksourceview/wip/chergert/snippets] wip on snippet parsing



commit d6285fc633411a48183b3798be88a942009e920b
Author: Christian Hergert <chergert redhat com>
Date:   Tue Jan 28 10:49:47 2020 -0800

    wip on snippet parsing

 data/meson.build                        |  11 +-
 data/snippets/licenses.snippets         | 342 ++++++++++++++++++++++++++++++++
 gtksourceview/gtksourcesnippetbundle.c  | 311 ++++++++++++++++++++++++++++-
 gtksourceview/gtksourcesnippetmanager.c |   1 +
 gtksourceview/gtksourcesnippetmanager.h |   2 +-
 tests/meson.build                       |   8 +-
 tests/test-snippets.c                   |  47 +++++
 7 files changed, 714 insertions(+), 8 deletions(-)
---
diff --git a/data/meson.build b/data/meson.build
index 45387d48..98d5e739 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -1,7 +1,6 @@
 install_subdir('language-specs',
     install_dir: pkgdatadir,
-  exclude_files: [ 'Makefile.am',
-                   'check-language.sh',
+  exclude_files: [ 'check-language.sh',
                    'lang_v1_to_v2.xslt',
                    'language-specs.its',
                    'language-specs.pot',
@@ -10,10 +9,14 @@ install_subdir('language-specs',
                    'update-pot.sh' ]
 )
 
+install_subdir('snippets',
+    install_dir: pkgdatadir,
+  exclude_files: [ ],
+)
+
 install_subdir('styles',
     install_dir: pkgdatadir,
-  exclude_files: [ 'Makefile.am',
-                   'check-style.sh',
+  exclude_files: [ 'check-style.sh',
                    'styles.its',
                    'styles.pot',
                    'testdark.xml',
diff --git a/data/snippets/licenses.snippets b/data/snippets/licenses.snippets
new file mode 100644
index 00000000..57c8bb78
--- /dev/null
+++ b/data/snippets/licenses.snippets
@@ -0,0 +1,342 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ This file is part of GtkSourceView
+
+ Copyright (C) 2020 Christian Hergert <chergert redhat com>
+
+ GtkSourceView 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.
+
+ GtkSourceView 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 Lesser General Public License
+ along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+ SPDX-License-Identifier: LGPL-2.1-or-later
+
+-->
+<snippets _group="Licenses">
+  <snippet _name="GPLv3 or later" trigger="gpl3" _description="File header with GPLv3+ license">
+    <text languages="python;python3;"><![CDATA[# ${1:$TM_FILENAME}
+#
+# Copyright $CURRENT_YEAR ${2:$FULLNAME} <${3:$EMAIL}>
+#
+# 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/>.
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+$0]]></text>
+    <text languages="c;chdr;cpp;cpphdr;css;js;java;"><![CDATA[/*
+ * ${1:$TM_FILENAME}
+ *
+ * Copyright $CURRENT_YEAR ${2:$FULLNAME} <${3:$EMAIL}>
+ *
+ * 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+$0]]></text>
+    <text languages="c-sharp;rust;"><![CDATA[// ${1:$TM_FILENAME}
+//
+// Copyright $CURRENT_YEAR ${2:$USER_NAME} <${3:$USER_EMAIL}>
+//
+// 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/>.
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+$0]]></text>
+  </snippet>
+  <snippet _name="LGPLv3 or later" trigger="lgpl3" _description="File header with LGPLv3 or later license">
+    <text languages="python;python3;"><![CDATA[# ${1:$TM_FILENAME}
+#
+# Copyright $CURRENT_YEAR ${2:$FULLNAME} <${3:$EMAIL}>
+#
+# 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 Lesser General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+$0]]></text>
+    <text languages="c;chdr;cpp;cpphdr;css;js;java;"><![CDATA[/*
+ * ${1:$TM_FILENAME}
+ *
+ * Copyright $CURRENT_YEAR ${2:$FULLNAME} <${3:$EMAIL}>
+ *
+ * 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 Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ */
+$0]]></text>
+    <text languages="c-sharp;rust;"><![CDATA[// ${1:$TM_FILENAME}
+//
+// Copyright $CURRENT_YEAR ${2:$USER_NAME} <${3:$USER_EMAIL}>
+//
+// 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 Lesser General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+$0]]></text>
+  </snippet>
+  <snippet _name="LGPLv2.1 or later" trigger="lgpl2" _description="File header with LGPL 2.1 or later 
license">
+    <text languages="python;python3;"><![CDATA[# ${1:$TM_FILENAME}
+#
+# Copyright $CURRENT_YEAR ${2:$FULLNAME} <${3:$EMAIL}>
+#
+# This program 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 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, see <http://www.gnu.org/licenses/>.
+#
+# SPDX-License-Identifier: LGPL-2.1-or-later
+$0]]></text>
+    <text languages="c;chdr;cpp;cpphdr;css;js;java;"><![CDATA[/*
+ * ${1:$TM_FILENAME}
+ *
+ * Copyright $CURRENT_YEAR ${2:$FULLNAME} <${3:$EMAIL}>
+ *
+ * This program 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 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+$0]]></text>
+    <text languages="c-sharp;rust;"><![CDATA[// ${1:$TM_FILENAME}
+//
+// Copyright $CURRENT_YEAR ${2:$USER_NAME} <${3:$USER_EMAIL}>
+//
+// This program 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 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library; if not, see <http://www.gnu.org/licenses/>.
+//
+// SPDX-License-Identifier: LGPL-2.1-or-later
+$0]]></text>
+  </snippet>
+  <snippet _name="Apache 2.0" trigger="apache2" _description="File header with Apache 2.0 license">
+    <text languages="python;python3;"><![CDATA[# ${1:$TM_FILENAME}
+#
+# Copyright $CURRENT_YEAR ${2:$FULLNAME} <${3:$EMAIL}>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+$0]]></text>
+    <text languages="c;chdr;cpp;cpphdr;css;js;java;"><![CDATA[/*
+ * ${1:$TM_FILENAME}
+ *
+ * Copyright $CURRENT_YEAR ${2:$FULLNAME} <${3:$EMAIL}>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+$0]]></text>
+    <text languages="c-sharp;rust;"><![CDATA[// ${1:$TM_FILENAME}
+//
+// Copyright $CURRENT_YEAR ${2:$USER_NAME} <${3:$USER_EMAIL}>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// SPDX-License-Identifier: Apache-2.0
+$0]]></text>
+  </snippet>
+  <snippet _name="MIT" trigger="mit" _description="File header with MIT license">
+    <text languages="python;python3;"><![CDATA[# ${1:$TM_FILENAME}
+#
+# Copyright $CURRENT_YEAR ${2:$FULLNAME} <${3:$EMAIL}>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+#
+# SPDX-License-Identifier: MIT
+$0]]></text>
+    <text languages="c;chdr;cpp;cpphdr;css;js;java;"><![CDATA[/*
+ * ${1:$TM_FILENAME}
+ *
+ * Copyright $CURRENT_YEAR ${2:$FULLNAME} <${3:$EMAIL}>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+$0]]></text>
+    <text languages="c-sharp;rust;"><![CDATA[// ${1:$TM_FILENAME}
+//
+// Copyright $CURRENT_YEAR ${2:$USER_NAME} <${3:$USER_EMAIL}>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+// SPDX-License-Identifier: MIT
+$0]]></text>
+  </snippet>
+</snippets>
diff --git a/gtksourceview/gtksourcesnippetbundle.c b/gtksourceview/gtksourcesnippetbundle.c
index 24f875cb..408f369a 100644
--- a/gtksourceview/gtksourcesnippetbundle.c
+++ b/gtksourceview/gtksourcesnippetbundle.c
@@ -20,17 +20,37 @@
 #include "config.h"
 
 #include "gtksourcesnippetbundle-private.h"
+#include "gtksourcesnippetmanager.h"
 
 struct _GtkSourceSnippetBundle
 {
-       GObject parent_instance;
+       GObject  parent_instance;
 };
 
+typedef struct
+{
+       GtkSourceSnippetBundle  *self;
+       gchar                   *group;
+       gchar                   *name;
+       gchar                   *description;
+       gchar                   *trigger;
+       gchar                  **languages;
+} ParseState;
+
 G_DEFINE_TYPE (GtkSourceSnippetBundle, _gtk_source_snippet_bundle, G_TYPE_OBJECT)
 
+static void
+gtk_source_snippet_bundle_finalize (GObject *object)
+{
+       G_OBJECT_CLASS (_gtk_source_snippet_bundle_parent_class)->finalize (object);
+}
+
 static void
 _gtk_source_snippet_bundle_class_init (GtkSourceSnippetBundleClass *klass)
 {
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->finalize = gtk_source_snippet_bundle_finalize;
 }
 
 static void
@@ -38,9 +58,296 @@ _gtk_source_snippet_bundle_init (GtkSourceSnippetBundle *self)
 {
 }
 
+static void
+text_and_cdata (GMarkupParseContext  *context,
+                const gchar          *text,
+                gsize                 text_len,
+                gpointer              user_data,
+                GError              **error)
+{
+       ParseState *state = user_data;
+       gchar *copy;
+
+       g_assert (state != NULL);
+       g_assert (GTK_SOURCE_IS_SNIPPET_BUNDLE (state->self));
+
+       copy = g_strndup (text, text_len);
+       g_print ("%s", copy);
+       g_free (copy);
+}
+
+static const GMarkupParser text_parser = {
+       .text = text_and_cdata,
+};
+
+static void
+elements_start_element (GMarkupParseContext  *context,
+                        const gchar          *element_name,
+                        const gchar         **attribute_names,
+                        const gchar         **attribute_values,
+                        gpointer              user_data,
+                        GError              **error)
+{
+       ParseState *state = user_data;
+
+       g_assert (state != NULL);
+       g_assert (GTK_SOURCE_IS_SNIPPET_BUNDLE (state->self));
+       g_assert (element_name != NULL);
+
+       if (g_strcmp0 (element_name, "text") == 0)
+       {
+               const gchar *languages = NULL;
+
+               if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+                                                 G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, 
"languages", &languages,
+                                                 G_MARKUP_COLLECT_INVALID))
+                       return;
+
+               if (languages != NULL && languages[0] != 0)
+               {
+                       gchar **strv = g_strsplit (languages, ";", 0);
+
+                       g_strfreev (state->languages);
+                       state->languages = g_steal_pointer (&strv);
+               }
+
+               g_markup_parse_context_push (context, &text_parser, state);
+       }
+       else
+       {
+               g_set_error (error,
+                            G_MARKUP_ERROR,
+                            G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                            "Element %s not supported",
+                            element_name);
+       }
+}
+
+static void
+elements_end_element (GMarkupParseContext  *context,
+                      const gchar          *element_name,
+                      gpointer              user_data,
+                      GError              **error)
+{
+       ParseState *state = user_data;
+
+       g_assert (state != NULL);
+       g_assert (GTK_SOURCE_IS_SNIPPET_BUNDLE (state->self));
+       g_assert (element_name != NULL);
+
+       g_clear_pointer (&state->languages, g_strfreev);
+
+       g_markup_parse_context_pop (context);
+}
+
+static const GMarkupParser elements_parser = {
+       .start_element = elements_start_element,
+       .end_element = elements_end_element,
+};
+
+static void
+snippet_start_element (GMarkupParseContext  *context,
+                       const gchar          *element_name,
+                       const gchar         **attribute_names,
+                       const gchar         **attribute_values,
+                       gpointer              user_data,
+                       GError              **error)
+{
+       ParseState *state = user_data;
+       const gchar *_name = NULL;
+       const gchar *_description = NULL;
+       const gchar *trigger = NULL;
+
+       g_assert (state != NULL);
+       g_assert (GTK_SOURCE_IS_SNIPPET_BUNDLE (state->self));
+       g_assert (element_name != NULL);
+
+       if (g_strcmp0 (element_name, "snippet") != 0)
+       {
+               g_set_error (error,
+                            G_MARKUP_ERROR,
+                            G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                            "Element %s not supported",
+                            element_name);
+               return;
+       }
+
+       if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+                                         G_MARKUP_COLLECT_STRING, "trigger", &trigger,
+                                         G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "_name", 
&_name,
+                                         G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, 
"_description", &_description,
+                                         G_MARKUP_COLLECT_INVALID))
+               return;
+
+       if (_name != NULL)
+       {
+               const gchar *name = g_dgettext (GETTEXT_PACKAGE, _name);
+
+               if (g_strcmp0 (state->name, name) != 0)
+               {
+                       g_free (state->name);
+                       state->name = g_strdup (name);
+               }
+       }
+
+       if (_description != NULL)
+       {
+               const gchar *description = g_dgettext (GETTEXT_PACKAGE, _description);
+
+               if (g_strcmp0 (state->description, description) != 0)
+               {
+                       g_free (state->description);
+                       state->description = g_strdup (description);
+               }
+       }
+
+       if (g_strcmp0 (state->trigger, trigger) != 0)
+       {
+               g_free (state->trigger);
+               state->trigger = g_strdup (trigger);
+       }
+
+       g_markup_parse_context_push (context, &elements_parser, state);
+}
+
+static void
+snippet_end_element (GMarkupParseContext  *context,
+                     const gchar          *element_name,
+                     gpointer              user_data,
+                     GError              **error)
+{
+       ParseState *state = user_data;
+
+       g_assert (state != NULL);
+       g_assert (GTK_SOURCE_IS_SNIPPET_BUNDLE (state->self));
+       g_assert (element_name != NULL);
+
+       g_clear_pointer (&state->trigger, g_free);
+       g_clear_pointer (&state->name, g_free);
+
+       g_markup_parse_context_pop (context);
+}
+
+static const GMarkupParser snippet_parser = {
+       .start_element = snippet_start_element,
+       .end_element = snippet_end_element,
+};
+
+static void
+snippets_start_element (GMarkupParseContext  *context,
+                        const gchar          *element_name,
+                        const gchar         **attribute_names,
+                        const gchar         **attribute_values,
+                        gpointer              user_data,
+                        GError              **error)
+{
+       ParseState *state = user_data;
+       const gchar *_group = NULL;
+
+       g_assert (state != NULL);
+       g_assert (GTK_SOURCE_IS_SNIPPET_BUNDLE (state->self));
+       g_assert (element_name != NULL);
+
+       if (g_strcmp0 (element_name, "snippets") != 0)
+       {
+               g_set_error (error,
+                            G_MARKUP_ERROR,
+                            G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                            "Element %s not supported",
+                            element_name);
+               return;
+       }
+
+       if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+                                         G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "_group", 
&_group,
+                                         G_MARKUP_COLLECT_INVALID))
+               return;
+
+       if (_group != NULL)
+       {
+               g_free (state->group);
+               state->group = g_strdup (g_dgettext (GETTEXT_PACKAGE, _group));
+       }
+
+       g_markup_parse_context_push (context, &snippet_parser, state);
+}
+
+static void
+snippets_end_element (GMarkupParseContext  *context,
+                      const gchar          *element_name,
+                      gpointer              user_data,
+                      GError              **error)
+{
+       ParseState *state = user_data;
+
+       g_assert (state != NULL);
+       g_assert (GTK_SOURCE_IS_SNIPPET_BUNDLE (state->self));
+       g_assert (element_name != NULL);
+
+       g_clear_pointer (&state->group, g_free);
+
+       g_markup_parse_context_pop (context);
+}
+
+static const GMarkupParser snippets_parser = {
+       .start_element = snippets_start_element,
+       .end_element = snippets_end_element,
+};
+
+static gboolean
+gtk_source_snippet_bundle_parse (GtkSourceSnippetBundle *self,
+                                 const gchar            *path)
+{
+       gchar *contents = NULL;
+       gsize length = 0;
+       gboolean ret = FALSE;
+
+       g_assert (GTK_SOURCE_IS_SNIPPET_BUNDLE (self));
+       g_assert (path != NULL);
+
+       if (g_file_get_contents (path, &contents, &length, NULL))
+       {
+               GMarkupParseContext *context;
+               ParseState state = { .self = self };
+
+               context = g_markup_parse_context_new (&snippets_parser,
+                                                     (G_MARKUP_TREAT_CDATA_AS_TEXT |
+                                                      G_MARKUP_PREFIX_ERROR_POSITION),
+                                                     &state, NULL);
+
+               ret = g_markup_parse_context_parse (context, contents, length, NULL);
+
+               g_clear_pointer (&state.description, g_free);
+               g_clear_pointer (&state.languages, g_strfreev);
+               g_clear_pointer (&state.name, g_free);
+               g_clear_pointer (&state.trigger, g_free);
+               g_clear_pointer (&state.group, g_free);
+
+               g_markup_parse_context_free (context);
+               g_free (contents);
+       }
+
+       return ret;
+}
+
 GtkSourceSnippetBundle *
 _gtk_source_snippet_bundle_new_from_file (const gchar             *path,
                                           GtkSourceSnippetManager *manager)
 {
-       return NULL;
+       GtkSourceSnippetBundle *self;
+
+       g_return_val_if_fail (path != NULL, NULL);
+       g_return_val_if_fail (GTK_SOURCE_IS_SNIPPET_MANAGER (manager), NULL);
+
+       self = g_object_new (GTK_SOURCE_TYPE_SNIPPET_BUNDLE, NULL);
+
+       if (gtk_source_snippet_bundle_parse (self, path))
+       {
+               return g_steal_pointer (&self);
+       }
+       else
+       {
+               g_object_unref (self);
+               return NULL;
+       }
 }
diff --git a/gtksourceview/gtksourcesnippetmanager.c b/gtksourceview/gtksourcesnippetmanager.c
index 5e695d51..b1e5c7d3 100644
--- a/gtksourceview/gtksourcesnippetmanager.c
+++ b/gtksourceview/gtksourcesnippetmanager.c
@@ -146,6 +146,7 @@ gtk_source_snippet_manager_class_init (GtkSourceSnippetManagerClass *klass)
 static void
 gtk_source_snippet_manager_init (GtkSourceSnippetManager *self)
 {
+       self->bundles = g_ptr_array_new_with_free_func (g_object_unref);
 }
 
 /**
diff --git a/gtksourceview/gtksourcesnippetmanager.h b/gtksourceview/gtksourcesnippetmanager.h
index 9d922a36..ee9b4a95 100644
--- a/gtksourceview/gtksourcesnippetmanager.h
+++ b/gtksourceview/gtksourcesnippetmanager.h
@@ -23,7 +23,7 @@
 #error "Only <gtksourceview/gtksource.h> can be included directly."
 #endif
 
-#include <glib-object.h>
+#include <gio/gio.h>
 
 #include "gtksourcetypes.h"
 
diff --git a/tests/meson.build b/tests/meson.build
index a33e8228..4dddf302 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -8,6 +8,7 @@ tests_sources = {
                     'int2str': ['test-int2str.c'],
                      'search': ['test-search.c'],
         'search-performances': ['test-search-performances.c'],
+                   'snippets': ['test-snippets.c'],
               'space-drawing': ['test-space-drawing.c'],
                      'widget': ['test-widget.c'],
 }
@@ -18,6 +19,11 @@ tests_resources = {
       'widget': 'test-widget.gresource.xml',
 }
 
+tests_deps = [gtksource_dep]
+if cc.get_id() == 'msvc'
+  tests_deps += [core_dep]
+endif
+
 foreach test_name, test_sources: tests_sources
   if tests_resources.has_key(test_name)
     test_sources += gnome.compile_resources(
@@ -30,6 +36,6 @@ foreach test_name, test_sources: tests_sources
   # well as the static core lib
   executable('test-@0@'.format(test_name), test_sources,
           c_args: tests_c_args,
-    dependencies: cc.get_id() == 'msvc' ? [gtksource_dep, core_dep] : [gtksource_dep],
+    dependencies: tests_deps,
   )
 endforeach
diff --git a/tests/test-snippets.c b/tests/test-snippets.c
new file mode 100644
index 00000000..2d1e26be
--- /dev/null
+++ b/tests/test-snippets.c
@@ -0,0 +1,47 @@
+/*
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * GtkSourceView 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.
+ *
+ * GtkSourceView 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 Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtksourceview/gtksource.h>
+#include <gtksourceview/gtksourceinit.h>
+
+gint
+main (gint argc,
+      gchar *argv[])
+{
+       GtkSourceSnippetManager *mgr;
+       GListModel *model;
+       static const gchar *search_path[] = {
+               TOP_SRCDIR"/data/snippets",
+               NULL
+       };
+
+       gtk_source_init ();
+
+       mgr = gtk_source_snippet_manager_get_default ();
+       gtk_source_snippet_manager_set_search_path (mgr, search_path);
+
+       model = gtk_source_snippet_manager_get_snippets (mgr, "c");
+       g_clear_object (&model);
+
+       gtk_source_finalize ();
+
+       return 0;
+}


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