Blame gtksourceview/gtksourcelanguage.c

Packit a7d494
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
Packit a7d494
/* gtksourcelanguage.c
Packit a7d494
 * This file is part of GtkSourceView
Packit a7d494
 *
Packit a7d494
 * Copyright (C) 2003 - Paolo Maggi <paolo.maggi@polito.it>
Packit a7d494
 *
Packit a7d494
 * GtkSourceView is free software; you can redistribute it and/or
Packit a7d494
 * modify it under the terms of the GNU Lesser General Public
Packit a7d494
 * License as published by the Free Software Foundation; either
Packit a7d494
 * version 2.1 of the License, or (at your option) any later version.
Packit a7d494
 *
Packit a7d494
 * GtkSourceView is distributed in the hope that it will be useful,
Packit a7d494
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit a7d494
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit a7d494
 * Lesser General Public License for more details.
Packit a7d494
 *
Packit a7d494
 * You should have received a copy of the GNU Lesser General Public
Packit a7d494
 * License along with this library; if not, write to the Free Software
Packit a7d494
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
Packit a7d494
 */
Packit a7d494
Packit a7d494
#ifdef HAVE_CONFIG_H
Packit a7d494
#include <config.h>
Packit a7d494
#endif
Packit a7d494
Packit a7d494
#ifdef HAVE_UNISTD_H
Packit a7d494
#include <unistd.h>
Packit a7d494
#endif
Packit a7d494
Packit a7d494
#include <string.h>
Packit a7d494
#include <fcntl.h>
Packit a7d494
Packit a7d494
#include <libxml/xmlreader.h>
Packit a7d494
#include <glib/gstdio.h>
Packit a7d494
#include "gtksourceview-i18n.h"
Packit a7d494
#include "gtksourcelanguage-private.h"
Packit a7d494
#include "gtksourcelanguage.h"
Packit a7d494
Packit a7d494
#ifdef G_OS_WIN32
Packit a7d494
#include <io.h>
Packit a7d494
#endif
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * SECTION:language
Packit a7d494
 * @Short_description: Represents a syntax highlighted language
Packit a7d494
 * @Title: GtkSourceLanguage
Packit a7d494
 * @See_also: #GtkSourceLanguageManager
Packit a7d494
 *
Packit a7d494
 * A #GtkSourceLanguage represents a programming or markup language, affecting
Packit a7d494
 * syntax highlighting and [context classes][context-classes].
Packit a7d494
 *
Packit a7d494
 * Use #GtkSourceLanguageManager to obtain a #GtkSourceLanguage instance, and
Packit a7d494
 * gtk_source_buffer_set_language() to apply it to a #GtkSourceBuffer.
Packit a7d494
 */
Packit a7d494
Packit a7d494
#define DEFAULT_SECTION _("Others")
Packit a7d494
Packit a7d494
enum {
Packit a7d494
	PROP_0,
Packit a7d494
	PROP_ID,
Packit a7d494
	PROP_NAME,
Packit a7d494
	PROP_SECTION,
Packit a7d494
	PROP_HIDDEN
Packit a7d494
};
Packit a7d494
Packit a7d494
G_DEFINE_TYPE_WITH_PRIVATE (GtkSourceLanguage, gtk_source_language, G_TYPE_OBJECT)
Packit a7d494
Packit a7d494
static GtkSourceLanguage *process_language_node (xmlTextReaderPtr	 reader,
Packit a7d494
						 const gchar		*filename);
Packit a7d494
static gboolean		  force_styles		(GtkSourceLanguage	*language);
Packit a7d494
Packit a7d494
GtkSourceLanguage *
Packit a7d494
_gtk_source_language_new_from_file (const gchar              *filename,
Packit a7d494
				    GtkSourceLanguageManager *lm)
Packit a7d494
{
Packit a7d494
	GtkSourceLanguage *lang = NULL;
Packit a7d494
	xmlTextReaderPtr reader = NULL;
Packit a7d494
	gint ret;
Packit a7d494
	gint fd;
Packit a7d494
Packit a7d494
	g_return_val_if_fail (filename != NULL, NULL);
Packit a7d494
	g_return_val_if_fail (lm != NULL, NULL);
Packit a7d494
Packit a7d494
	/*
Packit a7d494
	 * Use fd instead of filename so that it's utf8 safe on w32.
Packit a7d494
	 */
Packit a7d494
	fd = g_open (filename, O_RDONLY, 0);
Packit a7d494
	if (fd != -1)
Packit a7d494
		reader = xmlReaderForFd (fd, filename, NULL, 0);
Packit a7d494
Packit a7d494
	if (reader != NULL)
Packit a7d494
	{
Packit a7d494
        	ret = xmlTextReaderRead (reader);
Packit a7d494
Packit a7d494
        	while (ret == 1)
Packit a7d494
		{
Packit a7d494
			if (xmlTextReaderNodeType (reader) == 1)
Packit a7d494
			{
Packit a7d494
				xmlChar *name;
Packit a7d494
Packit a7d494
				name = xmlTextReaderName (reader);
Packit a7d494
Packit a7d494
				if (xmlStrcmp (name, BAD_CAST "language") == 0)
Packit a7d494
				{
Packit a7d494
					lang = process_language_node (reader, filename);
Packit a7d494
					ret = 0;
Packit a7d494
				}
Packit a7d494
Packit a7d494
				xmlFree (name);
Packit a7d494
			}
Packit a7d494
Packit a7d494
			if (ret == 1)
Packit a7d494
				ret = xmlTextReaderRead (reader);
Packit a7d494
		}
Packit a7d494
Packit a7d494
		xmlFreeTextReader (reader);
Packit a7d494
		close (fd);
Packit a7d494
Packit a7d494
		if (ret != 0)
Packit a7d494
		{
Packit a7d494
	            g_warning("Failed to parse '%s'", filename);
Packit a7d494
		    return NULL;
Packit a7d494
		}
Packit a7d494
        }
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		g_warning ("Unable to open '%s'", filename);
Packit a7d494
Packit a7d494
		if (fd != -1)
Packit a7d494
		{
Packit a7d494
			close (fd);
Packit a7d494
		}
Packit a7d494
    	}
Packit a7d494
Packit a7d494
	if (lang != NULL)
Packit a7d494
	{
Packit a7d494
		lang->priv->language_manager = lm;
Packit a7d494
		g_object_add_weak_pointer (G_OBJECT (lm),
Packit a7d494
					   (gpointer) &lang->priv->language_manager);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return lang;
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_language_get_property (GObject    *object,
Packit a7d494
				  guint       prop_id,
Packit a7d494
				  GValue     *value,
Packit a7d494
				  GParamSpec *pspec)
Packit a7d494
{
Packit a7d494
	GtkSourceLanguage *language;
Packit a7d494
Packit a7d494
	g_return_if_fail (GTK_SOURCE_IS_LANGUAGE (object));
Packit a7d494
Packit a7d494
	language = GTK_SOURCE_LANGUAGE (object);
Packit a7d494
Packit a7d494
	switch (prop_id)
Packit a7d494
	{
Packit a7d494
		case PROP_ID:
Packit a7d494
			g_value_set_string (value, language->priv->id);
Packit a7d494
			break;
Packit a7d494
Packit a7d494
		case PROP_NAME:
Packit a7d494
			g_value_set_string (value, language->priv->name);
Packit a7d494
			break;
Packit a7d494
Packit a7d494
		case PROP_SECTION:
Packit a7d494
			g_value_set_string (value, language->priv->section);
Packit a7d494
			break;
Packit a7d494
Packit a7d494
		case PROP_HIDDEN:
Packit a7d494
			g_value_set_boolean (value, language->priv->hidden);
Packit a7d494
			break;
Packit a7d494
Packit a7d494
		default:
Packit a7d494
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit a7d494
			break;
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_language_dispose (GObject *object)
Packit a7d494
{
Packit a7d494
	GtkSourceLanguage *lang;
Packit a7d494
Packit a7d494
	lang = GTK_SOURCE_LANGUAGE (object);
Packit a7d494
Packit a7d494
	if (lang->priv->language_manager != NULL)
Packit a7d494
	{
Packit a7d494
		g_object_remove_weak_pointer (G_OBJECT (lang->priv->language_manager),
Packit a7d494
					      (gpointer) &lang->priv->language_manager);
Packit a7d494
		lang->priv->language_manager = NULL;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	G_OBJECT_CLASS (gtk_source_language_parent_class)->dispose (object);
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_language_finalize (GObject *object)
Packit a7d494
{
Packit a7d494
	GtkSourceLanguage *lang;
Packit a7d494
Packit a7d494
	lang = GTK_SOURCE_LANGUAGE (object);
Packit a7d494
Packit a7d494
	if (lang->priv->ctx_data != NULL)
Packit a7d494
		g_critical ("context data not freed in gtk_source_language_finalize");
Packit a7d494
Packit a7d494
	g_free (lang->priv->lang_file_name);
Packit a7d494
	g_free (lang->priv->translation_domain);
Packit a7d494
	g_free (lang->priv->name);
Packit a7d494
	g_free (lang->priv->section);
Packit a7d494
	g_free (lang->priv->id);
Packit a7d494
	g_hash_table_destroy (lang->priv->properties);
Packit a7d494
Packit a7d494
	g_hash_table_destroy (lang->priv->styles);
Packit a7d494
Packit a7d494
	G_OBJECT_CLASS (gtk_source_language_parent_class)->finalize (object);
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_language_class_init (GtkSourceLanguageClass *klass)
Packit a7d494
{
Packit a7d494
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
Packit a7d494
Packit a7d494
	object_class->get_property = gtk_source_language_get_property;
Packit a7d494
	object_class->dispose = gtk_source_language_dispose;
Packit a7d494
	object_class->finalize = gtk_source_language_finalize;
Packit a7d494
Packit a7d494
	g_object_class_install_property (object_class,
Packit a7d494
					 PROP_ID,
Packit a7d494
					 g_param_spec_string ("id",
Packit a7d494
						 	      "Language id",
Packit a7d494
							      "Language id",
Packit a7d494
							      NULL,
Packit a7d494
							      G_PARAM_READABLE |
Packit a7d494
							      G_PARAM_STATIC_STRINGS));
Packit a7d494
Packit a7d494
	g_object_class_install_property (object_class,
Packit a7d494
					 PROP_NAME,
Packit a7d494
					 g_param_spec_string ("name",
Packit a7d494
						 	      "Language name",
Packit a7d494
							      "Language name",
Packit a7d494
							      NULL,
Packit a7d494
							      G_PARAM_READABLE |
Packit a7d494
							      G_PARAM_STATIC_STRINGS));
Packit a7d494
Packit a7d494
	g_object_class_install_property (object_class,
Packit a7d494
					 PROP_SECTION,
Packit a7d494
					 g_param_spec_string ("section",
Packit a7d494
						 	      "Language section",
Packit a7d494
							      "Language section",
Packit a7d494
							      NULL,
Packit a7d494
							      G_PARAM_READABLE |
Packit a7d494
							      G_PARAM_STATIC_STRINGS));
Packit a7d494
Packit a7d494
	g_object_class_install_property (object_class,
Packit a7d494
					 PROP_HIDDEN,
Packit a7d494
					 g_param_spec_boolean ("hidden",
Packit a7d494
							       "Hidden",
Packit a7d494
							       "Whether the language should be hidden from the user",
Packit a7d494
							       FALSE,
Packit a7d494
							       G_PARAM_READABLE |
Packit a7d494
							       G_PARAM_STATIC_STRINGS));
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_language_init (GtkSourceLanguage *lang)
Packit a7d494
{
Packit a7d494
	lang->priv = gtk_source_language_get_instance_private (lang);
Packit a7d494
Packit a7d494
	lang->priv->styles = g_hash_table_new_full (g_str_hash,
Packit a7d494
						    g_str_equal,
Packit a7d494
						    g_free,
Packit a7d494
						    (GDestroyNotify)_gtk_source_style_info_free);
Packit a7d494
	lang->priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
Packit a7d494
}
Packit a7d494
Packit a7d494
static gboolean
Packit a7d494
string_to_bool (const gchar *string)
Packit a7d494
{
Packit a7d494
	if (!g_ascii_strcasecmp (string, "yes") ||
Packit a7d494
	    !g_ascii_strcasecmp (string, "true") ||
Packit a7d494
	    !g_ascii_strcasecmp (string, "1"))
Packit a7d494
		return TRUE;
Packit a7d494
	else if (!g_ascii_strcasecmp (string, "no") ||
Packit a7d494
		 !g_ascii_strcasecmp (string, "false") ||
Packit a7d494
		 !g_ascii_strcasecmp (string, "0"))
Packit a7d494
		return FALSE;
Packit a7d494
	else
Packit a7d494
		g_return_val_if_reached (FALSE);
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
process_properties (xmlTextReaderPtr   reader,
Packit a7d494
		    GtkSourceLanguage *language)
Packit a7d494
{
Packit a7d494
	xmlNodePtr child;
Packit a7d494
	xmlNodePtr node = NULL;
Packit a7d494
Packit a7d494
	while (node == NULL && xmlTextReaderRead (reader) == 1)
Packit a7d494
	{
Packit a7d494
		xmlChar *name;
Packit a7d494
Packit a7d494
		if (xmlTextReaderNodeType (reader) != 1)
Packit a7d494
			continue;
Packit a7d494
Packit a7d494
		name = xmlTextReaderName (reader);
Packit a7d494
Packit a7d494
		if (xmlStrcmp (name, BAD_CAST "metadata") != 0)
Packit a7d494
		{
Packit a7d494
			xmlFree (name);
Packit a7d494
			continue;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		xmlFree (name);
Packit a7d494
Packit a7d494
		node = xmlTextReaderExpand (reader);
Packit a7d494
Packit a7d494
		if (node == NULL)
Packit a7d494
			return;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (node == NULL)
Packit a7d494
		return;
Packit a7d494
Packit a7d494
	for (child = node->children; child != NULL; child = child->next)
Packit a7d494
	{
Packit a7d494
		xmlChar *name;
Packit a7d494
		xmlChar *content;
Packit a7d494
Packit a7d494
		if (child->type != XML_ELEMENT_NODE ||
Packit a7d494
		    xmlStrcmp (child->name, BAD_CAST "property") != 0)
Packit a7d494
			continue;
Packit a7d494
Packit a7d494
		name = xmlGetProp (child, BAD_CAST "name");
Packit a7d494
		content = xmlNodeGetContent (child);
Packit a7d494
Packit a7d494
		if (name != NULL && content != NULL)
Packit a7d494
			g_hash_table_insert (language->priv->properties,
Packit a7d494
					     g_strdup ((gchar *) name),
Packit a7d494
					     g_strdup ((gchar *) content));
Packit a7d494
Packit a7d494
		xmlFree (name);
Packit a7d494
		xmlFree (content);
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
static GtkSourceLanguage *
Packit a7d494
process_language_node (xmlTextReaderPtr reader, const gchar *filename)
Packit a7d494
{
Packit a7d494
	xmlChar *version;
Packit a7d494
	xmlChar *tmp;
Packit a7d494
	xmlChar *untranslated_name;
Packit a7d494
	GtkSourceLanguage *lang;
Packit a7d494
Packit a7d494
	lang = g_object_new (GTK_SOURCE_TYPE_LANGUAGE, NULL);
Packit a7d494
Packit a7d494
	lang->priv->lang_file_name = g_strdup (filename);
Packit a7d494
Packit a7d494
	tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "translation-domain");
Packit a7d494
	lang->priv->translation_domain = g_strdup ((gchar*) tmp);
Packit a7d494
	xmlFree (tmp);
Packit a7d494
Packit a7d494
	tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "hidden");
Packit a7d494
	if (tmp != NULL)
Packit a7d494
		lang->priv->hidden = string_to_bool ((gchar*) tmp);
Packit a7d494
	else
Packit a7d494
		lang->priv->hidden = FALSE;
Packit a7d494
	xmlFree (tmp);
Packit a7d494
Packit a7d494
	tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "mimetypes");
Packit a7d494
	if (tmp != NULL)
Packit a7d494
		g_hash_table_insert (lang->priv->properties,
Packit a7d494
				     g_strdup ("mimetypes"),
Packit a7d494
				     g_strdup ((char*) tmp));
Packit a7d494
	xmlFree (tmp);
Packit a7d494
Packit a7d494
	tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "globs");
Packit a7d494
	if (tmp != NULL)
Packit a7d494
		g_hash_table_insert (lang->priv->properties,
Packit a7d494
				     g_strdup ("globs"),
Packit a7d494
				     g_strdup ((char*) tmp));
Packit a7d494
	xmlFree (tmp);
Packit a7d494
Packit a7d494
	tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "_name");
Packit a7d494
	if (tmp == NULL)
Packit a7d494
	{
Packit a7d494
		tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "name");
Packit a7d494
Packit a7d494
		if (tmp == NULL)
Packit a7d494
		{
Packit a7d494
			g_warning ("Impossible to get language name from file '%s'",
Packit a7d494
				   filename);
Packit a7d494
			g_object_unref (lang);
Packit a7d494
			return NULL;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		lang->priv->name = g_strdup ((char*) tmp);
Packit a7d494
		untranslated_name = tmp;
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		lang->priv->name = _gtk_source_language_translate_string (lang, (gchar*) tmp);
Packit a7d494
		untranslated_name = tmp;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "id");
Packit a7d494
	if (tmp != NULL)
Packit a7d494
	{
Packit a7d494
		lang->priv->id = g_ascii_strdown ((gchar*) tmp, -1);
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		lang->priv->id = g_ascii_strdown ((gchar*) untranslated_name, -1);
Packit a7d494
	}
Packit a7d494
	xmlFree (tmp);
Packit a7d494
	xmlFree (untranslated_name);
Packit a7d494
Packit a7d494
	tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "_section");
Packit a7d494
	if (tmp == NULL)
Packit a7d494
	{
Packit a7d494
		tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "section");
Packit a7d494
Packit a7d494
		if (tmp == NULL)
Packit a7d494
			lang->priv->section = g_strdup (DEFAULT_SECTION);
Packit a7d494
		else
Packit a7d494
			lang->priv->section = g_strdup ((gchar *) tmp);
Packit a7d494
Packit a7d494
		xmlFree (tmp);
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		lang->priv->section = _gtk_source_language_translate_string (lang, (gchar*) tmp);
Packit a7d494
		xmlFree (tmp);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	version = xmlTextReaderGetAttribute (reader, BAD_CAST "version");
Packit a7d494
Packit a7d494
	if (version == NULL)
Packit a7d494
	{
Packit a7d494
		g_warning ("Impossible to get version number from file '%s'",
Packit a7d494
			   filename);
Packit a7d494
		g_object_unref (lang);
Packit a7d494
		return NULL;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (xmlStrcmp (version , BAD_CAST "1.0") == 0)
Packit a7d494
	{
Packit a7d494
		lang->priv->version = GTK_SOURCE_LANGUAGE_VERSION_1_0;
Packit a7d494
	}
Packit a7d494
	else if (xmlStrcmp (version, BAD_CAST "2.0") == 0)
Packit a7d494
	{
Packit a7d494
		lang->priv->version = GTK_SOURCE_LANGUAGE_VERSION_2_0;
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		g_warning ("Unsupported language spec version '%s' in file '%s'",
Packit a7d494
			   (gchar*) version, filename);
Packit a7d494
		xmlFree (version);
Packit a7d494
		g_object_unref (lang);
Packit a7d494
		return NULL;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	xmlFree (version);
Packit a7d494
Packit a7d494
	if (lang->priv->version == GTK_SOURCE_LANGUAGE_VERSION_2_0)
Packit a7d494
		process_properties (reader, lang);
Packit a7d494
Packit a7d494
	return lang;
Packit a7d494
}
Packit a7d494
Packit a7d494
gchar *
Packit a7d494
_gtk_source_language_translate_string (GtkSourceLanguage *language,
Packit a7d494
				       const gchar       *string)
Packit a7d494
{
Packit a7d494
	g_return_val_if_fail (string != NULL, NULL);
Packit a7d494
	return GD_(language->priv->translation_domain, string);
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_language_get_id:
Packit a7d494
 * @language: a #GtkSourceLanguage.
Packit a7d494
 *
Packit a7d494
 * Returns the ID of the language. The ID is not locale-dependent.
Packit a7d494
 * The returned string is owned by @language and should not be freed
Packit a7d494
 * or modified.
Packit a7d494
 *
Packit a7d494
 * Returns: the ID of @language.
Packit a7d494
 **/
Packit a7d494
const gchar *
Packit a7d494
gtk_source_language_get_id (GtkSourceLanguage *language)
Packit a7d494
{
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_LANGUAGE (language), NULL);
Packit a7d494
	g_return_val_if_fail (language->priv->id != NULL, NULL);
Packit a7d494
Packit a7d494
	return language->priv->id;
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_language_get_name:
Packit a7d494
 * @language: a #GtkSourceLanguage.
Packit a7d494
 *
Packit a7d494
 * Returns the localized name of the language.
Packit a7d494
 * The returned string is owned by @language and should not be freed
Packit a7d494
 * or modified.
Packit a7d494
 *
Packit a7d494
 * Returns: the name of @language.
Packit a7d494
 **/
Packit a7d494
const gchar *
Packit a7d494
gtk_source_language_get_name (GtkSourceLanguage *language)
Packit a7d494
{
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_LANGUAGE (language), NULL);
Packit a7d494
	g_return_val_if_fail (language->priv->name != NULL, NULL);
Packit a7d494
Packit a7d494
	return language->priv->name;
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_language_get_section:
Packit a7d494
 * @language: a #GtkSourceLanguage.
Packit a7d494
 *
Packit a7d494
 * Returns the localized section of the language.
Packit a7d494
 * Each language belong to a section (ex. HTML belogs to the
Packit a7d494
 * Markup section).
Packit a7d494
 * The returned string is owned by @language and should not be freed
Packit a7d494
 * or modified.
Packit a7d494
 *
Packit a7d494
 * Returns: the section of @language.
Packit a7d494
 **/
Packit a7d494
const gchar *
Packit a7d494
gtk_source_language_get_section	(GtkSourceLanguage *language)
Packit a7d494
{
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_LANGUAGE (language), NULL);
Packit a7d494
	g_return_val_if_fail (language->priv->section != NULL, NULL);
Packit a7d494
Packit a7d494
	return language->priv->section;
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_language_get_hidden:
Packit a7d494
 * @language: a #GtkSourceLanguage
Packit a7d494
 *
Packit a7d494
 * Returns whether the language should be hidden from the user.
Packit a7d494
 *
Packit a7d494
 * Returns: %TRUE if the language should be hidden, %FALSE otherwise.
Packit a7d494
 */
Packit a7d494
gboolean
Packit a7d494
gtk_source_language_get_hidden (GtkSourceLanguage *language)
Packit a7d494
{
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_LANGUAGE (language), FALSE);
Packit a7d494
Packit a7d494
	return language->priv->hidden;
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_language_get_metadata:
Packit a7d494
 * @language: a #GtkSourceLanguage.
Packit a7d494
 * @name: metadata property name.
Packit a7d494
 *
Packit a7d494
 * Returns: (nullable) (transfer none): value of property @name stored in
Packit a7d494
 * the metadata of @language or %NULL if language does not contain the
Packit a7d494
 * specified metadata property.
Packit a7d494
 * The returned string is owned by @language and should not be freed
Packit a7d494
 * or modified.
Packit a7d494
 **/
Packit a7d494
const gchar *
Packit a7d494
gtk_source_language_get_metadata (GtkSourceLanguage *language,
Packit a7d494
				  const gchar       *name)
Packit a7d494
{
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_LANGUAGE (language), NULL);
Packit a7d494
	g_return_val_if_fail (name != NULL, NULL);
Packit a7d494
Packit a7d494
	return g_hash_table_lookup (language->priv->properties, name);
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_language_get_mime_types:
Packit a7d494
 * @language: a #GtkSourceLanguage.
Packit a7d494
 *
Packit a7d494
 * Returns the mime types associated to this language. This is just
Packit a7d494
 * an utility wrapper around gtk_source_language_get_metadata() to
Packit a7d494
 * retrieve the "mimetypes" metadata property and split it into an
Packit a7d494
 * array.
Packit a7d494
 *
Packit a7d494
 * Returns: (nullable) (array zero-terminated=1) (transfer full):
Packit a7d494
 * a newly-allocated %NULL terminated array containing the mime types
Packit a7d494
 * or %NULL if no mime types are found.
Packit a7d494
 * The returned array must be freed with g_strfreev().
Packit a7d494
 **/
Packit a7d494
gchar **
Packit a7d494
gtk_source_language_get_mime_types (GtkSourceLanguage *language)
Packit a7d494
{
Packit a7d494
	const gchar *mimetypes;
Packit a7d494
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_LANGUAGE (language), NULL);
Packit a7d494
Packit a7d494
	mimetypes = gtk_source_language_get_metadata (language, "mimetypes");
Packit a7d494
	if (mimetypes == NULL)
Packit a7d494
		return NULL;
Packit a7d494
Packit a7d494
	return g_strsplit (mimetypes, ";", 0);
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_language_get_globs:
Packit a7d494
 * @language: a #GtkSourceLanguage.
Packit a7d494
 *
Packit a7d494
 * Returns the globs associated to this language. This is just
Packit a7d494
 * an utility wrapper around gtk_source_language_get_metadata() to
Packit a7d494
 * retrieve the "globs" metadata property and split it into an array.
Packit a7d494
 *
Packit a7d494
 * Returns: (nullable) (array zero-terminated=1) (transfer full):
Packit a7d494
 * a newly-allocated %NULL terminated array containing the globs or %NULL
Packit a7d494
 * if no globs are found.
Packit a7d494
 * The returned array must be freed with g_strfreev().
Packit a7d494
 **/
Packit a7d494
gchar **
Packit a7d494
gtk_source_language_get_globs (GtkSourceLanguage *language)
Packit a7d494
{
Packit a7d494
	const gchar *globs;
Packit a7d494
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_LANGUAGE (language), NULL);
Packit a7d494
Packit a7d494
	globs = gtk_source_language_get_metadata (language, "globs");
Packit a7d494
	if (globs == NULL)
Packit a7d494
		return NULL;
Packit a7d494
Packit a7d494
	return g_strsplit (globs, ";", 0);
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * _gtk_source_language_get_language_manager:
Packit a7d494
 * @language: a #GtkSourceLanguage.
Packit a7d494
 *
Packit a7d494
 * Returns: (transfer none): the #GtkSourceLanguageManager for @language.
Packit a7d494
 **/
Packit a7d494
GtkSourceLanguageManager *
Packit a7d494
_gtk_source_language_get_language_manager (GtkSourceLanguage *language)
Packit a7d494
{
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_LANGUAGE (language), NULL);
Packit a7d494
	g_return_val_if_fail (language->priv->id != NULL, NULL);
Packit a7d494
Packit a7d494
	return language->priv->language_manager;
Packit a7d494
}
Packit a7d494
Packit a7d494
/* Highlighting engine creation ------------------------------------------ */
Packit a7d494
Packit a7d494
static void
Packit a7d494
copy_style_info (const char         *style_id,
Packit a7d494
		 GtkSourceStyleInfo *info,
Packit a7d494
		 GHashTable         *dest)
Packit a7d494
{
Packit a7d494
	g_hash_table_insert (dest, g_strdup (style_id),
Packit a7d494
			     _gtk_source_style_info_copy (info));
Packit a7d494
}
Packit a7d494
Packit a7d494
void
Packit a7d494
_gtk_source_language_define_language_styles (GtkSourceLanguage *lang)
Packit a7d494
{
Packit a7d494
	static const gchar *alias[][2] = {
Packit a7d494
		{"Base-N Integer", "def:base-n-integer"},
Packit a7d494
		{"Character", "def:character"},
Packit a7d494
		{"Comment", "def:comment"},
Packit a7d494
		{"Function", "def:function"},
Packit a7d494
		{"Decimal", "def:decimal"},
Packit a7d494
		{"Floating Point", "def:floating-point"},
Packit a7d494
		{"Keyword", "def:keyword"},
Packit a7d494
		{"Preprocessor", "def:preprocessor"},
Packit a7d494
		{"String", "def:string"},
Packit a7d494
		{"Specials", "def:specials"},
Packit a7d494
		{"Data Type", "def:type"},
Packit a7d494
		{NULL, NULL}};
Packit a7d494
Packit a7d494
	gint i = 0;
Packit a7d494
	GtkSourceLanguageManager *lm;
Packit a7d494
	GtkSourceLanguage *def_lang;
Packit a7d494
Packit a7d494
	while (alias[i][0] != NULL)
Packit a7d494
	{
Packit a7d494
		GtkSourceStyleInfo *info;
Packit a7d494
Packit a7d494
		info = _gtk_source_style_info_new (alias[i][0], alias[i][1]);
Packit a7d494
Packit a7d494
		g_hash_table_insert (lang->priv->styles,
Packit a7d494
				     g_strdup (alias[i][0]),
Packit a7d494
				     info);
Packit a7d494
Packit a7d494
		++i;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	/* We translate String to def:string, but def:string is mapped-to
Packit a7d494
	 * def:constant in def.lang, so we got to take style mappings from def.lang */
Packit a7d494
Packit a7d494
	lm = _gtk_source_language_get_language_manager (lang);
Packit a7d494
	def_lang = gtk_source_language_manager_get_language (lm, "def");
Packit a7d494
Packit a7d494
	if (def_lang != NULL)
Packit a7d494
	{
Packit a7d494
		force_styles (def_lang);
Packit a7d494
		g_hash_table_foreach (def_lang->priv->styles,
Packit a7d494
				      (GHFunc) copy_style_info,
Packit a7d494
				      lang->priv->styles);
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
/* returns new reference, which _must_ be unref'ed */
Packit a7d494
static GtkSourceContextData *
Packit a7d494
gtk_source_language_parse_file (GtkSourceLanguage *language)
Packit a7d494
{
Packit a7d494
	if (language->priv->ctx_data == NULL)
Packit a7d494
	{
Packit a7d494
		gboolean success = FALSE;
Packit a7d494
		GtkSourceContextData *ctx_data;
Packit a7d494
Packit a7d494
		if (language->priv->language_manager == NULL)
Packit a7d494
		{
Packit a7d494
			g_critical ("_gtk_source_language_create_engine() is called after "
Packit a7d494
				    "language manager was finalized");
Packit a7d494
		}
Packit a7d494
		else
Packit a7d494
		{
Packit a7d494
			ctx_data = _gtk_source_context_data_new	(language);
Packit a7d494
Packit a7d494
			switch (language->priv->version)
Packit a7d494
			{
Packit a7d494
				case GTK_SOURCE_LANGUAGE_VERSION_1_0:
Packit a7d494
					success = _gtk_source_language_file_parse_version1 (language, ctx_data);
Packit a7d494
					break;
Packit a7d494
Packit a7d494
				case GTK_SOURCE_LANGUAGE_VERSION_2_0:
Packit a7d494
					success = _gtk_source_language_file_parse_version2 (language, ctx_data);
Packit a7d494
					break;
Packit a7d494
Packit a7d494
				default:
Packit a7d494
					g_assert_not_reached ();
Packit a7d494
			}
Packit a7d494
Packit a7d494
			if (!success)
Packit a7d494
				_gtk_source_context_data_unref (ctx_data);
Packit a7d494
			else
Packit a7d494
				language->priv->ctx_data = ctx_data;
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		_gtk_source_context_data_ref (language->priv->ctx_data);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return language->priv->ctx_data;
Packit a7d494
}
Packit a7d494
Packit a7d494
GtkSourceEngine *
Packit a7d494
_gtk_source_language_create_engine (GtkSourceLanguage *language)
Packit a7d494
{
Packit a7d494
	GtkSourceContextEngine *ce = NULL;
Packit a7d494
	GtkSourceContextData *ctx_data;
Packit a7d494
Packit a7d494
	ctx_data = gtk_source_language_parse_file (language);
Packit a7d494
Packit a7d494
	if (ctx_data != NULL)
Packit a7d494
	{
Packit a7d494
		ce = _gtk_source_context_engine_new (ctx_data);
Packit a7d494
		_gtk_source_context_data_unref (ctx_data);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return ce ? GTK_SOURCE_ENGINE (ce) : NULL;
Packit a7d494
}
Packit a7d494
Packit a7d494
typedef struct _AddStyleIdData AddStyleIdData;
Packit a7d494
Packit a7d494
struct _AddStyleIdData
Packit a7d494
{
Packit a7d494
	gchar             *language_id;
Packit a7d494
	GPtrArray         *ids_array;
Packit a7d494
};
Packit a7d494
Packit a7d494
static void
Packit a7d494
add_style_id (gchar *id, G_GNUC_UNUSED gpointer value, AddStyleIdData *data)
Packit a7d494
{
Packit a7d494
	if (g_str_has_prefix (id, data->language_id))
Packit a7d494
		g_ptr_array_add (data->ids_array, g_strdup (id));
Packit a7d494
}
Packit a7d494
Packit a7d494
static gchar **
Packit a7d494
get_style_ids (GtkSourceLanguage *language)
Packit a7d494
{
Packit a7d494
	GPtrArray *ids_array;
Packit a7d494
	AddStyleIdData data;
Packit a7d494
Packit a7d494
	g_return_val_if_fail (language->priv->styles != NULL, NULL);
Packit a7d494
Packit a7d494
	ids_array = g_ptr_array_new ();
Packit a7d494
Packit a7d494
	data.language_id = g_strdup_printf ("%s:", language->priv->id);
Packit a7d494
	data.ids_array = ids_array;
Packit a7d494
Packit a7d494
	g_hash_table_foreach (language->priv->styles,
Packit a7d494
			      (GHFunc) add_style_id,
Packit a7d494
			      &data);
Packit a7d494
Packit a7d494
	g_free (data.language_id);
Packit a7d494
Packit a7d494
	if (ids_array->len == 0)
Packit a7d494
	{
Packit a7d494
		/* No style defined in this language */
Packit a7d494
		g_ptr_array_free (ids_array, TRUE);
Packit a7d494
Packit a7d494
		return NULL;
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		/* Terminate the array with NULL */
Packit a7d494
		g_ptr_array_add (ids_array, NULL);
Packit a7d494
Packit a7d494
		return (gchar **)g_ptr_array_free (ids_array, FALSE);
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
static gboolean
Packit a7d494
force_styles (GtkSourceLanguage *language)
Packit a7d494
{
Packit a7d494
	/* To be sure to have the list of styles we need to parse lang file
Packit a7d494
	 * as if we were to create an engine. In the future we can improve
Packit a7d494
	 * this by parsing styles only.
Packit a7d494
	 */
Packit a7d494
	if (!language->priv->styles_loaded && language->priv->ctx_data == NULL)
Packit a7d494
	{
Packit a7d494
		GtkSourceContextData *ctx_data;
Packit a7d494
Packit a7d494
		ctx_data = gtk_source_language_parse_file (language);
Packit a7d494
		if (ctx_data == NULL)
Packit a7d494
			return FALSE;
Packit a7d494
Packit a7d494
		language->priv->styles_loaded = TRUE;
Packit a7d494
		_gtk_source_context_data_unref (ctx_data);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return TRUE;
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_language_get_style_ids:
Packit a7d494
 * @language: a #GtkSourceLanguage.
Packit a7d494
 *
Packit a7d494
 * Returns the ids of the styles defined by this @language.
Packit a7d494
 *
Packit a7d494
 * Returns: (nullable) (array zero-terminated=1) (transfer full):
Packit a7d494
 * a newly-allocated %NULL terminated array containing ids of the
Packit a7d494
 * styles defined by this @language or %NULL if no style is defined.
Packit a7d494
 * The returned array must be freed with g_strfreev().
Packit a7d494
*/
Packit a7d494
gchar **
Packit a7d494
gtk_source_language_get_style_ids (GtkSourceLanguage *language)
Packit a7d494
{
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_LANGUAGE (language), NULL);
Packit a7d494
	g_return_val_if_fail (language->priv->id != NULL, NULL);
Packit a7d494
Packit a7d494
	if (!force_styles (language))
Packit a7d494
		return NULL;
Packit a7d494
Packit a7d494
	return get_style_ids (language);
Packit a7d494
}
Packit a7d494
Packit a7d494
static GtkSourceStyleInfo *
Packit a7d494
get_style_info (GtkSourceLanguage *language, const char *style_id)
Packit a7d494
{
Packit a7d494
	GtkSourceStyleInfo *info;
Packit a7d494
Packit a7d494
	if (!force_styles (language))
Packit a7d494
		return NULL;
Packit a7d494
Packit a7d494
	g_return_val_if_fail (language->priv->styles != NULL, NULL);
Packit a7d494
Packit a7d494
	info = g_hash_table_lookup (language->priv->styles, style_id);
Packit a7d494
Packit a7d494
	return info;
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_language_get_style_name:
Packit a7d494
 * @language: a #GtkSourceLanguage.
Packit a7d494
 * @style_id: a style ID.
Packit a7d494
 *
Packit a7d494
 * Returns the name of the style with ID @style_id defined by this @language.
Packit a7d494
 *
Packit a7d494
 * Returns: (nullable) (transfer none): the name of the style with ID @style_id
Packit a7d494
 * defined by this @language or %NULL if the style has no name or there is no
Packit a7d494
 * style with ID @style_id defined by this @language.
Packit a7d494
 * The returned string is owned by the @language and must not be modified.
Packit a7d494
 */
Packit a7d494
const gchar *
Packit a7d494
gtk_source_language_get_style_name (GtkSourceLanguage *language,
Packit a7d494
				    const gchar       *style_id)
Packit a7d494
{
Packit a7d494
	GtkSourceStyleInfo *info;
Packit a7d494
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_LANGUAGE (language), NULL);
Packit a7d494
	g_return_val_if_fail (language->priv->id != NULL, NULL);
Packit a7d494
	g_return_val_if_fail (style_id != NULL, NULL);
Packit a7d494
Packit a7d494
	info = get_style_info (language, style_id);
Packit a7d494
Packit a7d494
	return info ? info->name : NULL;
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_language_get_style_fallback:
Packit a7d494
 * @language: a #GtkSourceLanguage.
Packit a7d494
 * @style_id: a style ID.
Packit a7d494
 *
Packit a7d494
 * Returns the ID of the style to use if the specified @style_id
Packit a7d494
 * is not present in the current style scheme.
Packit a7d494
 *
Packit a7d494
 * Returns: (nullable) (transfer none): the ID of the style to use if the
Packit a7d494
 * specified @style_id is not present in the current style scheme or %NULL
Packit a7d494
 * if the style has no fallback defined.
Packit a7d494
 * The returned string is owned by the @language and must not be modified.
Packit a7d494
 *
Packit a7d494
 * Since: 3.4
Packit a7d494
 */
Packit a7d494
const gchar *
Packit a7d494
gtk_source_language_get_style_fallback (GtkSourceLanguage *language,
Packit a7d494
					const gchar       *style_id)
Packit a7d494
{
Packit a7d494
	GtkSourceStyleInfo *info;
Packit a7d494
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_LANGUAGE (language), NULL);
Packit a7d494
	g_return_val_if_fail (language->priv->id != NULL, NULL);
Packit a7d494
	g_return_val_if_fail (style_id != NULL, NULL);
Packit a7d494
Packit a7d494
	info = get_style_info (language, style_id);
Packit a7d494
Packit a7d494
	return info ? info->map_to : NULL;
Packit a7d494
}
Packit a7d494
Packit a7d494
/* Utility functions for GtkSourceStyleInfo */
Packit a7d494
Packit a7d494
GtkSourceStyleInfo *
Packit a7d494
_gtk_source_style_info_new (const gchar *name, const gchar *map_to)
Packit a7d494
{
Packit a7d494
	GtkSourceStyleInfo *info = g_new0 (GtkSourceStyleInfo, 1);
Packit a7d494
Packit a7d494
	info->name = g_strdup (name);
Packit a7d494
	info->map_to = g_strdup (map_to);
Packit a7d494
Packit a7d494
	return info;
Packit a7d494
}
Packit a7d494
Packit a7d494
GtkSourceStyleInfo *
Packit a7d494
_gtk_source_style_info_copy (GtkSourceStyleInfo *info)
Packit a7d494
{
Packit a7d494
	g_return_val_if_fail (info != NULL, NULL);
Packit a7d494
	return _gtk_source_style_info_new (info->name, info->map_to);
Packit a7d494
}
Packit a7d494
Packit a7d494
void
Packit a7d494
_gtk_source_style_info_free (GtkSourceStyleInfo *info)
Packit a7d494
{
Packit a7d494
	if (info == NULL)
Packit a7d494
		return;
Packit a7d494
Packit a7d494
	g_free (info->name);
Packit a7d494
	g_free (info->map_to);
Packit a7d494
Packit a7d494
	g_free (info);
Packit a7d494
}
Packit a7d494