1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
2 /* gtksourcelanguage.c
3 *
4 * Copyright (C) 2003 - Paolo Maggi <paolo.maggi@polito.it>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Library General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28 #ifdef G_OS_WIN32
29 #include <io.h>
30 #endif
31
32 #include <string.h>
33 #include <fcntl.h>
34
35 #include <libxml/xmlreader.h>
36 #include <glib/gstdio.h>
37 #include "gtksourceview-i18n.h"
38 #include "gtksourcelanguage-private.h"
39 #include "gtksourcelanguage.h"
40 #include "gtksourceview-marshal.h"
41
42 /**
43 * SECTION:language
44 * @Short_description: Object representing a syntax highlighted language
45 * @Title: GtkSourceLanguage
46 * @See_also: #GtkSourceLanguageManager
47 *
48 * #GtkSourceLanguage encapsulates syntax and highlighting styles for a
49 * particular language. Use #GtkSourceLanguageManager to obtain a
50 * #GtkSourceLanguage instance, and gtk_source_buffer_set_language() to apply it
51 * to a #GtkSourceBuffer.
52 */
53
54 #define DEFAULT_SECTION _("Others")
55
56 /* Properties */
57 enum {
58 PROP_0,
59 PROP_ID,
60 PROP_NAME,
61 PROP_SECTION,
62 PROP_HIDDEN
63 };
64
65 G_DEFINE_TYPE (GtkSourceLanguage, gtk_source_language, G_TYPE_OBJECT)
66
67 static GtkSourceLanguage *process_language_node (xmlTextReaderPtr reader,
68 const gchar *filename);
69 static gboolean force_styles (GtkSourceLanguage *language);
70
71 GtkSourceLanguage *
_gtk_source_language_new_from_file(const gchar * filename,GtkSourceLanguageManager * lm)72 _gtk_source_language_new_from_file (const gchar *filename,
73 GtkSourceLanguageManager *lm)
74 {
75 GtkSourceLanguage *lang = NULL;
76 xmlTextReaderPtr reader = NULL;
77 gint ret;
78 gint fd;
79
80 g_return_val_if_fail (filename != NULL, NULL);
81 g_return_val_if_fail (lm != NULL, NULL);
82
83 /*
84 * Use fd instead of filename so that it's utf8 safe on w32.
85 */
86 fd = g_open (filename, O_RDONLY, 0);
87 if (fd != -1)
88 reader = xmlReaderForFd (fd, filename, NULL, 0);
89
90 if (reader != NULL)
91 {
92 ret = xmlTextReaderRead (reader);
93
94 while (ret == 1)
95 {
96 if (xmlTextReaderNodeType (reader) == 1)
97 {
98 xmlChar *name;
99
100 name = xmlTextReaderName (reader);
101
102 if (xmlStrcmp (name, BAD_CAST "language") == 0)
103 {
104 lang = process_language_node (reader, filename);
105 ret = 0;
106 }
107
108 xmlFree (name);
109 }
110
111 if (ret == 1)
112 ret = xmlTextReaderRead (reader);
113 }
114
115 xmlFreeTextReader (reader);
116 close (fd);
117
118 if (ret != 0)
119 {
120 g_warning("Failed to parse '%s'", filename);
121 return NULL;
122 }
123 }
124 else
125 {
126 g_warning("Unable to open '%s'", filename);
127
128 }
129
130 if (lang != NULL)
131 {
132 lang->priv->language_manager = lm;
133 g_object_add_weak_pointer (G_OBJECT (lm),
134 (gpointer) &lang->priv->language_manager);
135 }
136
137 return lang;
138 }
139
140 static void
gtk_source_language_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)141 gtk_source_language_get_property (GObject *object,
142 guint prop_id,
143 GValue *value,
144 GParamSpec *pspec)
145 {
146 GtkSourceLanguage *language;
147
148 g_return_if_fail (GTK_IS_SOURCE_LANGUAGE (object));
149
150 language = GTK_SOURCE_LANGUAGE (object);
151
152 switch (prop_id)
153 {
154 case PROP_ID:
155 g_value_set_string (value, language->priv->id);
156 break;
157
158 case PROP_NAME:
159 g_value_set_string (value, language->priv->name);
160 break;
161
162 case PROP_SECTION:
163 g_value_set_string (value, language->priv->section);
164 break;
165
166 case PROP_HIDDEN:
167 g_value_set_boolean (value, language->priv->hidden);
168 break;
169
170 default:
171 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
172 break;
173 }
174 }
175
176 static void
gtk_source_language_dispose(GObject * object)177 gtk_source_language_dispose (GObject *object)
178 {
179 GtkSourceLanguage *lang;
180
181 lang = GTK_SOURCE_LANGUAGE (object);
182
183 if (lang->priv->language_manager != NULL)
184 {
185 g_object_remove_weak_pointer (G_OBJECT (lang->priv->language_manager),
186 (gpointer) &lang->priv->language_manager);
187 lang->priv->language_manager = NULL;
188 }
189
190 G_OBJECT_CLASS (gtk_source_language_parent_class)->dispose (object);
191 }
192
193 static void
gtk_source_language_finalize(GObject * object)194 gtk_source_language_finalize (GObject *object)
195 {
196 GtkSourceLanguage *lang;
197
198 lang = GTK_SOURCE_LANGUAGE (object);
199
200 if (lang->priv->ctx_data != NULL)
201 g_critical ("context data not freed in gtk_source_language_finalize");
202
203 g_free (lang->priv->lang_file_name);
204 g_free (lang->priv->translation_domain);
205 g_free (lang->priv->name);
206 g_free (lang->priv->section);
207 g_free (lang->priv->id);
208 g_hash_table_destroy (lang->priv->properties);
209
210 g_hash_table_destroy (lang->priv->styles);
211
212 G_OBJECT_CLASS (gtk_source_language_parent_class)->finalize (object);
213 }
214
215 static void
gtk_source_language_class_init(GtkSourceLanguageClass * klass)216 gtk_source_language_class_init (GtkSourceLanguageClass *klass)
217 {
218 GObjectClass *object_class = G_OBJECT_CLASS (klass);
219
220 object_class->get_property = gtk_source_language_get_property;
221 object_class->dispose = gtk_source_language_dispose;
222 object_class->finalize = gtk_source_language_finalize;
223
224 g_object_class_install_property (object_class,
225 PROP_ID,
226 g_param_spec_string ("id",
227 _("Language id"),
228 _("Language id"),
229 NULL,
230 G_PARAM_READABLE));
231
232 g_object_class_install_property (object_class,
233 PROP_NAME,
234 g_param_spec_string ("name",
235 _("Language name"),
236 _("Language name"),
237 NULL,
238 G_PARAM_READABLE));
239
240 g_object_class_install_property (object_class,
241 PROP_SECTION,
242 g_param_spec_string ("section",
243 _("Language section"),
244 _("Language section"),
245 NULL,
246 G_PARAM_READABLE));
247
248 g_object_class_install_property (object_class,
249 PROP_HIDDEN,
250 g_param_spec_boolean ("hidden",
251 _("Hidden"),
252 _("Whether the language should be hidden from the user"),
253 FALSE,
254 G_PARAM_READABLE));
255
256 g_type_class_add_private (object_class, sizeof(GtkSourceLanguagePrivate));
257 }
258
259 static void
gtk_source_language_init(GtkSourceLanguage * lang)260 gtk_source_language_init (GtkSourceLanguage *lang)
261 {
262 lang->priv = G_TYPE_INSTANCE_GET_PRIVATE (lang, GTK_TYPE_SOURCE_LANGUAGE,
263 GtkSourceLanguagePrivate);
264 lang->priv->styles = g_hash_table_new_full (g_str_hash,
265 g_str_equal,
266 g_free,
267 (GDestroyNotify)_gtk_source_style_info_free);
268 lang->priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
269 }
270
271 static gboolean
string_to_bool(const gchar * string)272 string_to_bool (const gchar *string)
273 {
274 if (!g_ascii_strcasecmp (string, "yes") ||
275 !g_ascii_strcasecmp (string, "true") ||
276 !g_ascii_strcasecmp (string, "1"))
277 return TRUE;
278 else if (!g_ascii_strcasecmp (string, "no") ||
279 !g_ascii_strcasecmp (string, "false") ||
280 !g_ascii_strcasecmp (string, "0"))
281 return FALSE;
282 else
283 g_return_val_if_reached (FALSE);
284 }
285
286 static void
process_properties(xmlTextReaderPtr reader,GtkSourceLanguage * language)287 process_properties (xmlTextReaderPtr reader,
288 GtkSourceLanguage *language)
289 {
290 xmlNodePtr child;
291 xmlNodePtr node = NULL;
292
293 while (node == NULL && xmlTextReaderRead (reader) == 1)
294 {
295 xmlChar *name;
296
297 if (xmlTextReaderNodeType (reader) != 1)
298 continue;
299
300 name = xmlTextReaderName (reader);
301
302 if (xmlStrcmp (name, BAD_CAST "metadata") != 0)
303 {
304 xmlFree (name);
305 continue;
306 }
307
308 xmlFree (name);
309
310 node = xmlTextReaderExpand (reader);
311
312 if (node == NULL)
313 return;
314 }
315
316 if (node == NULL)
317 return;
318
319 for (child = node->children; child != NULL; child = child->next)
320 {
321 xmlChar *name;
322 xmlChar *content;
323
324 if (child->type != XML_ELEMENT_NODE ||
325 xmlStrcmp (child->name, BAD_CAST "property") != 0)
326 continue;
327
328 name = xmlGetProp (child, BAD_CAST "name");
329 content = xmlNodeGetContent (child);
330
331 if (name != NULL && content != NULL)
332 g_hash_table_insert (language->priv->properties,
333 g_strdup ((gchar *) name),
334 g_strdup ((gchar *) content));
335
336 xmlFree (name);
337 xmlFree (content);
338 }
339 }
340
341 static GtkSourceLanguage *
process_language_node(xmlTextReaderPtr reader,const gchar * filename)342 process_language_node (xmlTextReaderPtr reader, const gchar *filename)
343 {
344 xmlChar *version;
345 xmlChar *tmp;
346 xmlChar *untranslated_name;
347 GtkSourceLanguage *lang;
348
349 lang = g_object_new (GTK_TYPE_SOURCE_LANGUAGE, NULL);
350
351 lang->priv->lang_file_name = g_strdup (filename);
352
353 tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "translation-domain");
354 lang->priv->translation_domain = g_strdup ((gchar*) tmp);
355 xmlFree (tmp);
356
357 tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "hidden");
358 if (tmp != NULL)
359 lang->priv->hidden = string_to_bool ((gchar*) tmp);
360 else
361 lang->priv->hidden = FALSE;
362 xmlFree (tmp);
363
364 tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "mimetypes");
365 if (tmp != NULL)
366 g_hash_table_insert (lang->priv->properties,
367 g_strdup ("mimetypes"),
368 g_strdup ((char*) tmp));
369 xmlFree (tmp);
370
371 tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "globs");
372 if (tmp != NULL)
373 g_hash_table_insert (lang->priv->properties,
374 g_strdup ("globs"),
375 g_strdup ((char*) tmp));
376 xmlFree (tmp);
377
378 tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "_name");
379 if (tmp == NULL)
380 {
381 tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "name");
382
383 if (tmp == NULL)
384 {
385 g_warning ("Impossible to get language name from file '%s'",
386 filename);
387 g_object_unref (lang);
388 return NULL;
389 }
390
391 lang->priv->name = g_strdup ((char*) tmp);
392 untranslated_name = tmp;
393 }
394 else
395 {
396 lang->priv->name = _gtk_source_language_translate_string (lang, (gchar*) tmp);
397 untranslated_name = tmp;
398 }
399
400 tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "id");
401 if (tmp != NULL)
402 {
403 lang->priv->id = g_ascii_strdown ((gchar*) tmp, -1);
404 }
405 else
406 {
407 lang->priv->id = g_ascii_strdown ((gchar*) untranslated_name, -1);
408 }
409 xmlFree (tmp);
410 xmlFree (untranslated_name);
411
412 tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "_section");
413 if (tmp == NULL)
414 {
415 tmp = xmlTextReaderGetAttribute (reader, BAD_CAST "section");
416
417 if (tmp == NULL)
418 lang->priv->section = g_strdup (DEFAULT_SECTION);
419 else
420 lang->priv->section = g_strdup ((gchar *) tmp);
421
422 xmlFree (tmp);
423 }
424 else
425 {
426 lang->priv->section = _gtk_source_language_translate_string (lang, (gchar*) tmp);
427 xmlFree (tmp);
428 }
429
430 version = xmlTextReaderGetAttribute (reader, BAD_CAST "version");
431
432 if (version == NULL)
433 {
434 g_warning ("Impossible to get version number from file '%s'",
435 filename);
436 g_object_unref (lang);
437 return NULL;
438 }
439
440 if (xmlStrcmp (version , BAD_CAST "1.0") == 0)
441 {
442 lang->priv->version = GTK_SOURCE_LANGUAGE_VERSION_1_0;
443 }
444 else if (xmlStrcmp (version, BAD_CAST "2.0") == 0)
445 {
446 lang->priv->version = GTK_SOURCE_LANGUAGE_VERSION_2_0;
447 }
448 else
449 {
450 g_warning ("Unsupported language spec version '%s' in file '%s'",
451 (gchar*) version, filename);
452 xmlFree (version);
453 g_object_unref (lang);
454 return NULL;
455 }
456
457 xmlFree (version);
458
459 if (lang->priv->version == GTK_SOURCE_LANGUAGE_VERSION_2_0)
460 process_properties (reader, lang);
461
462 return lang;
463 }
464
465 gchar *
_gtk_source_language_translate_string(GtkSourceLanguage * language,const gchar * string)466 _gtk_source_language_translate_string (GtkSourceLanguage *language,
467 const gchar *string)
468 {
469 g_return_val_if_fail (string != NULL, NULL);
470 return GD_(language->priv->translation_domain, string);
471 }
472
473 /**
474 * gtk_source_language_get_id:
475 * @language: a #GtkSourceLanguage.
476 *
477 * Returns the ID of the language. The ID is not locale-dependent.
478 *
479 * Returns: the ID of @language.
480 * The returned string is owned by @language and should not be freed
481 * or modified.
482 **/
483 const gchar *
gtk_source_language_get_id(GtkSourceLanguage * language)484 gtk_source_language_get_id (GtkSourceLanguage *language)
485 {
486 g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
487 g_return_val_if_fail (language->priv->id != NULL, NULL);
488
489 return language->priv->id;
490 }
491
492 /**
493 * gtk_source_language_get_name:
494 * @language: a #GtkSourceLanguage.
495 *
496 * Returns the localized name of the language.
497 *
498 * Returns: the name of @language.
499 * The returned string is owned by @language and should not be freed
500 * or modified.
501 **/
502 const gchar *
gtk_source_language_get_name(GtkSourceLanguage * language)503 gtk_source_language_get_name (GtkSourceLanguage *language)
504 {
505 g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
506 g_return_val_if_fail (language->priv->name != NULL, NULL);
507
508 return language->priv->name;
509 }
510
511 /**
512 * gtk_source_language_get_section:
513 * @language: a #GtkSourceLanguage.
514 *
515 * Returns the localized section of the language.
516 * Each language belong to a section (ex. HTML belogs to the
517 * Markup section).
518 *
519 * Returns: the section of @language.
520 * The returned string is owned by @language and should not be freed
521 * or modified.
522 **/
523 const gchar *
gtk_source_language_get_section(GtkSourceLanguage * language)524 gtk_source_language_get_section (GtkSourceLanguage *language)
525 {
526 g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
527 g_return_val_if_fail (language->priv->section != NULL, NULL);
528
529 return language->priv->section;
530 }
531
532 /**
533 * gtk_source_language_get_hidden:
534 * @language: a #GtkSourceLanguage
535 *
536 * Returns whether the language should be hidden from the user.
537 *
538 * Returns: TRUE if the language should be hidden, FALSE otherwise.
539 */
540 gboolean
gtk_source_language_get_hidden(GtkSourceLanguage * language)541 gtk_source_language_get_hidden (GtkSourceLanguage *language)
542 {
543 g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), FALSE);
544
545 return language->priv->hidden;
546 }
547
548 /**
549 * gtk_source_language_get_metadata:
550 * @language: a #GtkSourceLanguage.
551 * @name: metadata property name.
552 *
553 * Returns: value of property @name stored in the metadata of @language
554 * or %NULL if language doesn't contain that metadata property.
555 * The returned string is owned by @language and should not be freed
556 * or modified.
557 **/
558 const gchar *
gtk_source_language_get_metadata(GtkSourceLanguage * language,const gchar * name)559 gtk_source_language_get_metadata (GtkSourceLanguage *language,
560 const gchar *name)
561 {
562 g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
563 g_return_val_if_fail (name != NULL, NULL);
564
565 return g_hash_table_lookup (language->priv->properties, name);
566 }
567
568 /**
569 * gtk_source_language_get_mime_types:
570 * @language: a #GtkSourceLanguage.
571 *
572 * Returns the mime types associated to this language. This is just
573 * an utility wrapper around gtk_source_language_get_metadata() to
574 * retrieve the "mimetypes" metadata property and split it into an
575 * array.
576 *
577 * Returns: a newly-allocated %NULL terminated array containing
578 * the mime types or %NULL if no mime types are found.
579 * The returned array must be freed with g_strfreev().
580 **/
581 gchar **
gtk_source_language_get_mime_types(GtkSourceLanguage * language)582 gtk_source_language_get_mime_types (GtkSourceLanguage *language)
583 {
584 const gchar *mimetypes;
585
586 g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
587
588 mimetypes = gtk_source_language_get_metadata (language, "mimetypes");
589 if (mimetypes == NULL)
590 return NULL;
591
592 return g_strsplit (mimetypes, ";", 0);
593 }
594
595 /**
596 * gtk_source_language_get_globs:
597 * @language: a #GtkSourceLanguage.
598 *
599 * Returns the globs associated to this language. This is just
600 * an utility wrapper around gtk_source_language_get_metadata() to
601 * retrieve the "globs" metadata property and split it into an array.
602 *
603 * Returns: a newly-allocated %NULL terminated array containing
604 * the globs or %NULL if no globs are found.
605 * The returned array must be freed with g_strfreev().
606 **/
607 gchar **
gtk_source_language_get_globs(GtkSourceLanguage * language)608 gtk_source_language_get_globs (GtkSourceLanguage *language)
609 {
610 const gchar *globs;
611
612 g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
613
614 globs = gtk_source_language_get_metadata (language, "globs");
615 if (globs == NULL)
616 return NULL;
617
618 return g_strsplit (globs, ";", 0);
619 }
620
621 /**
622 * _gtk_source_language_get_language_manager:
623 * @language: a #GtkSourceLanguage.
624 *
625 * Returns: #GtkSourceLanguageManager for @language.
626 **/
627 GtkSourceLanguageManager *
_gtk_source_language_get_language_manager(GtkSourceLanguage * language)628 _gtk_source_language_get_language_manager (GtkSourceLanguage *language)
629 {
630 g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
631 g_return_val_if_fail (language->priv->id != NULL, NULL);
632
633 return language->priv->language_manager;
634 }
635
636 /* Highlighting engine creation ------------------------------------------ */
637
638 static void
copy_style_info(const char * style_id,GtkSourceStyleInfo * info,GHashTable * dest)639 copy_style_info (const char *style_id,
640 GtkSourceStyleInfo *info,
641 GHashTable *dest)
642 {
643 g_hash_table_insert (dest, g_strdup (style_id),
644 _gtk_source_style_info_copy (info));
645 }
646
647 void
_gtk_source_language_define_language_styles(GtkSourceLanguage * lang)648 _gtk_source_language_define_language_styles (GtkSourceLanguage *lang)
649 {
650 static const gchar *alias[][2] = {
651 {"Base-N Integer", "def:base-n-integer"},
652 {"Character", "def:character"},
653 {"Comment", "def:comment"},
654 {"Function", "def:function"},
655 {"Decimal", "def:decimal"},
656 {"Floating Point", "def:floating-point"},
657 {"Keyword", "def:keyword"},
658 {"Preprocessor", "def:preprocessor"},
659 {"String", "def:string"},
660 {"Specials", "def:specials"},
661 {"Data Type", "def:type"},
662 {NULL, NULL}};
663
664 gint i = 0;
665 GtkSourceLanguageManager *lm;
666 GtkSourceLanguage *def_lang;
667
668 while (alias[i][0] != NULL)
669 {
670 GtkSourceStyleInfo *info;
671
672 info = _gtk_source_style_info_new (alias[i][0], alias[i][1]);
673
674 g_hash_table_insert (lang->priv->styles,
675 g_strdup (alias[i][0]),
676 info);
677
678 ++i;
679 }
680
681 /* We translate String to def:string, but def:string is mapped-to
682 * def:constant in def.lang, so we got to take style mappings from def.lang */
683
684 lm = _gtk_source_language_get_language_manager (lang);
685 def_lang = gtk_source_language_manager_get_language (lm, "def");
686
687 if (def_lang != NULL)
688 {
689 force_styles (def_lang);
690 g_hash_table_foreach (def_lang->priv->styles,
691 (GHFunc) copy_style_info,
692 lang->priv->styles);
693 }
694 }
695
696 /* returns new reference, which _must_ be unref'ed */
697 static GtkSourceContextData *
gtk_source_language_parse_file(GtkSourceLanguage * language)698 gtk_source_language_parse_file (GtkSourceLanguage *language)
699 {
700 if (language->priv->ctx_data == NULL)
701 {
702 gboolean success = FALSE;
703 GtkSourceContextData *ctx_data;
704
705 if (language->priv->language_manager == NULL)
706 {
707 g_critical ("_gtk_source_language_create_engine() is called after "
708 "language manager was finalized");
709 }
710 else
711 {
712 ctx_data = _gtk_source_context_data_new (language);
713
714 switch (language->priv->version)
715 {
716 case GTK_SOURCE_LANGUAGE_VERSION_1_0:
717 success = _gtk_source_language_file_parse_version1 (language, ctx_data);
718 break;
719
720 case GTK_SOURCE_LANGUAGE_VERSION_2_0:
721 success = _gtk_source_language_file_parse_version2 (language, ctx_data);
722 break;
723
724 default:
725 g_assert_not_reached ();
726 }
727
728 if (!success)
729 _gtk_source_context_data_unref (ctx_data);
730 else
731 language->priv->ctx_data = ctx_data;
732 }
733 }
734 else
735 {
736 _gtk_source_context_data_ref (language->priv->ctx_data);
737 }
738
739 return language->priv->ctx_data;
740 }
741
742 GtkSourceEngine *
_gtk_source_language_create_engine(GtkSourceLanguage * language)743 _gtk_source_language_create_engine (GtkSourceLanguage *language)
744 {
745 GtkSourceContextEngine *ce = NULL;
746 GtkSourceContextData *ctx_data;
747
748 ctx_data = gtk_source_language_parse_file (language);
749
750 if (ctx_data != NULL)
751 {
752 ce = _gtk_source_context_engine_new (ctx_data);
753 _gtk_source_context_data_unref (ctx_data);
754 }
755
756 return ce ? GTK_SOURCE_ENGINE (ce) : NULL;
757 }
758
759 typedef struct _AddStyleIdData AddStyleIdData;
760
761 struct _AddStyleIdData
762 {
763 gchar *language_id;
764 GPtrArray *ids_array;
765 };
766
767 static void
add_style_id(gchar * id,G_GNUC_UNUSED gpointer value,AddStyleIdData * data)768 add_style_id (gchar *id, G_GNUC_UNUSED gpointer value, AddStyleIdData *data)
769 {
770 if (g_str_has_prefix (id, data->language_id))
771 g_ptr_array_add (data->ids_array, g_strdup (id));
772 }
773
774 static gchar **
get_style_ids(GtkSourceLanguage * language)775 get_style_ids (GtkSourceLanguage *language)
776 {
777 GPtrArray *ids_array;
778 AddStyleIdData data;
779
780 g_return_val_if_fail (language->priv->styles != NULL, NULL);
781
782 ids_array = g_ptr_array_new ();
783
784 data.language_id = g_strdup_printf ("%s:", language->priv->id);
785 data.ids_array = ids_array;
786
787 g_hash_table_foreach (language->priv->styles,
788 (GHFunc) add_style_id,
789 &data);
790
791 g_free (data.language_id);
792
793 if (ids_array->len == 0)
794 {
795 /* No style defined in this language */
796 g_ptr_array_free (ids_array, TRUE);
797
798 return NULL;
799 }
800 else
801 {
802 /* Terminate the array with NULL */
803 g_ptr_array_add (ids_array, NULL);
804
805 return (gchar **)g_ptr_array_free (ids_array, FALSE);
806 }
807 }
808
809 static gboolean
force_styles(GtkSourceLanguage * language)810 force_styles (GtkSourceLanguage *language)
811 {
812 /* To be sure to have the list of styles we need to parse lang file
813 * as if we were to create an engine. In the future we can improve
814 * this by parsing styles only.
815 */
816 if (!language->priv->styles_loaded && language->priv->ctx_data == NULL)
817 {
818 GtkSourceContextData *ctx_data;
819
820 ctx_data = gtk_source_language_parse_file (language);
821 if (ctx_data == NULL)
822 return FALSE;
823
824 language->priv->styles_loaded = TRUE;
825 _gtk_source_context_data_unref (ctx_data);
826 }
827
828 return TRUE;
829 }
830
831 /**
832 * gtk_source_language_get_style_ids:
833 * @language: a #GtkSourceLanguage
834 *
835 * Returns the ids of the styles defined by this @language.
836 *
837 * Returns: a %NULL terminated array containing
838 * ids of the styles defined by this @language or %NULL if no style is
839 * defined. The returned array must be freed with g_strfreev().
840 */
841 gchar **
gtk_source_language_get_style_ids(GtkSourceLanguage * language)842 gtk_source_language_get_style_ids (GtkSourceLanguage *language)
843 {
844 g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
845 g_return_val_if_fail (language->priv->id != NULL, NULL);
846
847 if (!force_styles (language))
848 return NULL;
849
850 return get_style_ids (language);
851 }
852
853 static GtkSourceStyleInfo *
get_style_info(GtkSourceLanguage * language,const char * style_id)854 get_style_info (GtkSourceLanguage *language, const char *style_id)
855 {
856 GtkSourceStyleInfo *info;
857
858 if (!force_styles (language))
859 return NULL;
860
861 g_return_val_if_fail (language->priv->styles != NULL, NULL);
862
863 info = g_hash_table_lookup (language->priv->styles, style_id);
864
865 return info;
866 }
867
868 /**
869 * gtk_source_language_get_style_name:
870 * @language: a #GtkSourceLanguage
871 * @style_id: a style ID
872 *
873 * Returns the name of the style with ID @style_id defined by this @language.
874 *
875 * Returns: the name of the style with ID @style_id defined by this @language or
876 * %NULL if the style has no name or there is no style with ID @style_id defined
877 * by this @language. The returned string is owned by the @language and must
878 * not be modified.
879 */
880 const gchar *
gtk_source_language_get_style_name(GtkSourceLanguage * language,const gchar * style_id)881 gtk_source_language_get_style_name (GtkSourceLanguage *language,
882 const gchar *style_id)
883 {
884 GtkSourceStyleInfo *info;
885
886 g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
887 g_return_val_if_fail (language->priv->id != NULL, NULL);
888 g_return_val_if_fail (style_id != NULL, NULL);
889
890 info = get_style_info (language, style_id);
891 if (info == NULL)
892 return NULL;
893
894 return info->name;
895 }
896
897 /* Utility functions for GtkSourceStyleInfo */
898
899 GtkSourceStyleInfo *
_gtk_source_style_info_new(const gchar * name,const gchar * map_to)900 _gtk_source_style_info_new (const gchar *name, const gchar *map_to)
901 {
902 GtkSourceStyleInfo *info = g_new0 (GtkSourceStyleInfo, 1);
903
904 info->name = g_strdup (name);
905 info->map_to = g_strdup (map_to);
906
907 return info;
908 }
909
910 GtkSourceStyleInfo *
_gtk_source_style_info_copy(GtkSourceStyleInfo * info)911 _gtk_source_style_info_copy (GtkSourceStyleInfo *info)
912 {
913 g_return_val_if_fail (info != NULL, NULL);
914 return _gtk_source_style_info_new (info->name, info->map_to);
915 }
916
917 void
_gtk_source_style_info_free(GtkSourceStyleInfo * info)918 _gtk_source_style_info_free (GtkSourceStyleInfo *info)
919 {
920 if (info == NULL)
921 return;
922
923 g_free (info->name);
924 g_free (info->map_to);
925
926 g_free (info);
927 }
928
929