Fix file names of generated tags files for C, PHP and Python
[geany-mirror.git] / src / tagmanager / tm_tag.c
blobcaa912d6c843b2c0e2ba90c31e6b70066d91ba3e
1 /*
3 * Copyright (c) 2001-2002, Biswapesh Chattopadhyay
5 * This source code is released for free distribution under the terms of the
6 * GNU General Public License.
8 */
10 #include <stdlib.h>
11 #include <string.h>
12 #include <glib-object.h>
14 #include "tm_tag.h"
15 #include "tm_ctags_wrappers.h"
18 #define TAG_NEW(T) ((T) = g_slice_new0(TMTag))
19 #define TAG_FREE(T) g_slice_free(TMTag, (T))
22 #ifdef DEBUG_TAG_REFS
24 static GHashTable *alive_tags = NULL;
26 static void foreach_tags_log(gpointer key, gpointer value, gpointer data)
28 gsize *ref_count = data;
29 const TMTag *tag = value;
31 *ref_count += (gsize) tag->refcount;
32 g_debug("Leaked TMTag (%d refs): %s", tag->refcount, tag->name);
35 static void log_refs_at_exit(void)
37 gsize ref_count = 0;
39 g_hash_table_foreach(alive_tags, foreach_tags_log, &ref_count);
40 g_debug("TMTag references left at exit: %lu", ref_count);
43 static TMTag *log_tag_new(void)
45 TMTag *tag;
47 if (! alive_tags)
49 alive_tags = g_hash_table_new(g_direct_hash, g_direct_equal);
50 atexit(log_refs_at_exit);
52 TAG_NEW(tag);
53 g_hash_table_insert(alive_tags, tag, tag);
55 return tag;
58 static void log_tag_free(TMTag *tag)
60 g_return_if_fail(alive_tags != NULL);
62 if (! g_hash_table_remove(alive_tags, tag)) {
63 g_critical("Freeing invalid TMTag pointer %p", (void *) tag);
64 } else {
65 TAG_FREE(tag);
69 #undef TAG_NEW
70 #undef TAG_FREE
71 #define TAG_NEW(T) ((T) = log_tag_new())
72 #define TAG_FREE(T) log_tag_free(T)
74 #endif /* DEBUG_TAG_REFS */
77 typedef struct
79 guint *sort_attrs;
80 gboolean partial;
81 const GPtrArray *tags_array;
82 gboolean first;
83 } TMSortOptions;
85 /* Gets the GType for a TMTag */
86 GType tm_tag_get_type(void)
88 static GType gtype = 0;
89 if (G_UNLIKELY (gtype == 0))
91 gtype = g_boxed_type_register_static("TMTag", (GBoxedCopyFunc)tm_tag_ref,
92 (GBoxedFreeFunc)tm_tag_unref);
94 return gtype;
98 Creates a new tag structure and returns a pointer to it.
99 @return the new TMTag structure. This should be free()-ed using tm_tag_free()
101 TMTag *tm_tag_new(void)
103 TMTag *tag;
105 TAG_NEW(tag);
106 tag->refcount = 1;
108 return tag;
112 Destroys a TMTag structure, i.e. frees all elements except the tag itself.
113 @param tag The TMTag structure to destroy
114 @see tm_tag_free()
116 static void tm_tag_destroy(TMTag *tag)
118 g_free(tag->name);
119 g_free(tag->arglist);
120 g_free(tag->scope);
121 g_free(tag->inheritance);
122 g_free(tag->var_type);
127 Drops a reference from a TMTag. If the reference count reaches 0, this function
128 destroys all data in the tag and frees the tag structure as well.
129 @param tag Pointer to a TMTag structure
131 void tm_tag_unref(TMTag *tag)
133 /* be NULL-proof because tm_tag_free() was NULL-proof and we indent to be a
134 * drop-in replacment of it */
135 if (NULL != tag && g_atomic_int_dec_and_test(&tag->refcount))
137 tm_tag_destroy(tag);
138 TAG_FREE(tag);
143 Adds a reference to a TMTag.
144 @param tag Pointer to a TMTag structure
145 @return the passed-in TMTag
147 TMTag *tm_tag_ref(TMTag *tag)
149 g_atomic_int_inc(&tag->refcount);
150 return tag;
154 Inbuilt tag comparison function.
156 static gint tm_tag_compare(gconstpointer ptr1, gconstpointer ptr2, gpointer user_data)
158 unsigned int *sort_attr;
159 int returnval = 0;
160 TMTag *t1 = *((TMTag **) ptr1);
161 TMTag *t2 = *((TMTag **) ptr2);
162 TMSortOptions *sort_options = user_data;
164 if ((NULL == t1) || (NULL == t2))
166 g_warning("Found NULL tag");
167 return t2 - t1;
169 if (NULL == sort_options->sort_attrs)
171 if (sort_options->partial)
172 return strncmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""), strlen(FALLBACK(t1->name, "")));
173 else
174 return strcmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""));
177 for (sort_attr = sort_options->sort_attrs; returnval == 0 && *sort_attr != tm_tag_attr_none_t; ++ sort_attr)
179 switch (*sort_attr)
181 case tm_tag_attr_name_t:
182 if (sort_options->partial)
183 returnval = strncmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""), strlen(FALLBACK(t1->name, "")));
184 else
185 returnval = strcmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""));
186 break;
187 case tm_tag_attr_file_t:
188 returnval = t1->file - t2->file;
189 break;
190 case tm_tag_attr_line_t:
191 returnval = t1->line - t2->line;
192 break;
193 case tm_tag_attr_type_t:
194 returnval = t1->type - t2->type;
195 break;
196 case tm_tag_attr_scope_t:
197 returnval = strcmp(FALLBACK(t1->scope, ""), FALLBACK(t2->scope, ""));
198 break;
199 case tm_tag_attr_arglist_t:
200 returnval = strcmp(FALLBACK(t1->arglist, ""), FALLBACK(t2->arglist, ""));
201 if (returnval != 0)
203 int line_diff = (t1->line - t2->line);
205 returnval = line_diff ? line_diff : returnval;
207 break;
208 case tm_tag_attr_vartype_t:
209 returnval = strcmp(FALLBACK(t1->var_type, ""), FALLBACK(t2->var_type, ""));
210 break;
213 return returnval;
216 gboolean tm_tags_equal(const TMTag *a, const TMTag *b)
218 if (a == b)
219 return TRUE;
221 return (a->line == b->line &&
222 a->file == b->file /* ptr comparison */ &&
223 strcmp(FALLBACK(a->name, ""), FALLBACK(b->name, "")) == 0 &&
224 a->type == b->type &&
225 a->local == b->local &&
226 a->pointerOrder == b->pointerOrder &&
227 a->access == b->access &&
228 a->impl == b->impl &&
229 a->lang == b->lang &&
230 strcmp(FALLBACK(a->scope, ""), FALLBACK(b->scope, "")) == 0 &&
231 strcmp(FALLBACK(a->arglist, ""), FALLBACK(b->arglist, "")) == 0 &&
232 strcmp(FALLBACK(a->inheritance, ""), FALLBACK(b->inheritance, "")) == 0 &&
233 strcmp(FALLBACK(a->var_type, ""), FALLBACK(b->var_type, "")) == 0);
237 Removes NULL tag entries from an array of tags. Called after tm_tags_dedup() since
238 this function substitutes duplicate entries with NULL
239 @param tags_array Array of tags to dedup
241 void tm_tags_prune(GPtrArray *tags_array)
243 guint i, count;
245 g_return_if_fail(tags_array);
247 for (i=0, count = 0; i < tags_array->len; ++i)
249 if (NULL != tags_array->pdata[i])
250 tags_array->pdata[count++] = tags_array->pdata[i];
252 tags_array->len = count;
256 Deduplicates an array on tags using the inbuilt comparison function based on
257 the attributes specified. Called by tm_tags_sort() when dedup is TRUE.
258 @param tags_array Array of tags to dedup.
259 @param sort_attributes Attributes the array is sorted on. They will be deduped
260 on the same criteria.
262 void tm_tags_dedup(GPtrArray *tags_array, TMTagAttrType *sort_attributes, gboolean unref_duplicates)
264 TMSortOptions sort_options;
265 guint i;
267 g_return_if_fail(tags_array);
268 if (tags_array->len < 2)
269 return;
271 sort_options.sort_attrs = sort_attributes;
272 sort_options.partial = FALSE;
273 for (i = 1; i < tags_array->len; ++i)
275 if (0 == tm_tag_compare(&(tags_array->pdata[i - 1]), &(tags_array->pdata[i]), &sort_options))
277 if (unref_duplicates)
278 tm_tag_unref(tags_array->pdata[i-1]);
279 tags_array->pdata[i-1] = NULL;
282 tm_tags_prune(tags_array);
286 Sort an array of tags on the specified attribuites using the inbuilt comparison
287 function.
288 @param tags_array The array of tags to be sorted
289 @param sort_attributes Attributes to be sorted on (int array terminated by 0)
290 @param dedup Whether to deduplicate the sorted array
292 void tm_tags_sort(GPtrArray *tags_array, TMTagAttrType *sort_attributes,
293 gboolean dedup, gboolean unref_duplicates)
295 TMSortOptions sort_options;
297 g_return_if_fail(tags_array);
299 sort_options.sort_attrs = sort_attributes;
300 sort_options.partial = FALSE;
301 g_ptr_array_sort_with_data(tags_array, tm_tag_compare, &sort_options);
302 if (dedup)
303 tm_tags_dedup(tags_array, sort_attributes, unref_duplicates);
306 void tm_tags_remove_file_tags(TMSourceFile *source_file, GPtrArray *tags_array)
308 guint i;
310 /* Now we choose between an algorithm with complexity O(tags_array->len) and
311 * O(source_file->tags_array->len * log(tags_array->len)). The latter algorithm
312 * is better when tags_array contains many times more tags than
313 * source_file->tags_array so instead of trying to find the removed tags
314 * linearly, binary search is used. The constant 20 is more or less random
315 * but seems to work well. It's exact value isn't so critical because it's
316 * the extremes where the difference is the biggest: when
317 * source_file->tags_array->len == tags_array->len (single file open) and
318 * source_file->tags_array->len << tags_array->len (the number of tags
319 * from the file is a small fraction of all tags).
321 if (source_file->tags_array->len != 0 &&
322 tags_array->len / source_file->tags_array->len < 20)
324 for (i = 0; i < tags_array->len; i++)
326 TMTag *tag = tags_array->pdata[i];
328 if (tag->file == source_file)
329 tags_array->pdata[i] = NULL;
332 else
334 GPtrArray *to_delete = g_ptr_array_sized_new(source_file->tags_array->len);
336 for (i = 0; i < source_file->tags_array->len; i++)
338 guint j;
339 guint tag_count;
340 TMTag **found;
341 TMTag *tag = source_file->tags_array->pdata[i];
343 found = tm_tags_find(tags_array, tag->name, FALSE, &tag_count);
345 for (j = 0; j < tag_count; j++)
347 if (*found != NULL && (*found)->file == source_file)
349 /* we cannot set the pointer to NULL now because the search wouldn't work */
350 g_ptr_array_add(to_delete, found);
351 /* no break - if there are multiple tags of the same name, we would
352 * always find the first instance and wouldn't remove others; duplicates
353 * in the to_delete list aren't a problem */
355 found++;
359 for (i = 0; i < to_delete->len; i++)
361 TMTag **tag = to_delete->pdata[i];
362 *tag = NULL;
364 g_ptr_array_free(to_delete, TRUE);
367 tm_tags_prune(tags_array);
370 /* Optimized merge sort for merging sorted values from one array to another
371 * where one of the arrays is much smaller than the other.
372 * The merge complexity depends mostly on the size of the small array
373 * and is almost independent of the size of the big array.
374 * In addition, get rid of the duplicates (if both big_array and small_array are duplicate-free). */
375 static GPtrArray *merge(GPtrArray *big_array, GPtrArray *small_array,
376 TMSortOptions *sort_options, gboolean unref_duplicates) {
377 guint i1 = 0; /* index to big_array */
378 guint i2 = 0; /* index to small_array */
379 guint initial_step;
380 guint step;
381 GPtrArray *res_array = g_ptr_array_sized_new(big_array->len + small_array->len);
382 #ifdef TM_DEBUG
383 guint cmpnum = 0;
384 #endif
386 /* swap the arrays if len(small) > len(big) */
387 if (small_array->len > big_array->len)
389 GPtrArray *tmp = small_array;
390 small_array = big_array;
391 big_array = tmp;
394 /* on average, we are merging a value from small_array every
395 * len(big_array) / len(small_array) values - good approximation for fast jump
396 * step size */
397 initial_step = (small_array->len > 0) ? big_array->len / small_array->len : 1;
398 initial_step = initial_step > 4 ? initial_step : 1;
399 step = initial_step;
401 while (i1 < big_array->len && i2 < small_array->len)
403 gpointer val1;
404 gpointer val2 = small_array->pdata[i2];
406 if (step > 4) /* fast path start */
408 guint j1 = (i1 + step < big_array->len) ? i1 + step : big_array->len - 1;
410 val1 = big_array->pdata[j1];
411 #ifdef TM_DEBUG
412 cmpnum++;
413 #endif
414 /* if the value in big_array after making the big step is still smaller
415 * than the value in small_array, we can copy all the values inbetween
416 * into the result without making expensive string comparisons */
417 if (tm_tag_compare(&val1, &val2, sort_options) < 0)
419 while (i1 <= j1)
421 val1 = big_array->pdata[i1];
422 g_ptr_array_add(res_array, val1);
423 i1++;
426 else
428 /* lower the step and try again */
429 step /= 2;
431 } /* fast path end */
432 else
434 gint cmpval;
436 #ifdef TM_DEBUG
437 cmpnum++;
438 #endif
439 val1 = big_array->pdata[i1];
440 cmpval = tm_tag_compare(&val1, &val2, sort_options);
441 if (cmpval < 0)
443 g_ptr_array_add(res_array, val1);
444 i1++;
446 else
448 g_ptr_array_add(res_array, val2);
449 i2++;
450 /* value from small_array gets merged - reset the step size */
451 step = initial_step;
452 if (cmpval == 0)
454 i1++; /* remove the duplicate, keep just the newly merged value */
455 if (unref_duplicates)
456 tm_tag_unref(val1);
462 /* end of one of the arrays reached - copy the rest from the other array */
463 while (i1 < big_array->len)
464 g_ptr_array_add(res_array, big_array->pdata[i1++]);
465 while (i2 < small_array->len)
466 g_ptr_array_add(res_array, small_array->pdata[i2++]);
468 #ifdef TM_DEBUG
469 printf("cmpnums: %u\n", cmpnum);
470 printf("total tags: %u\n", big_array->len);
471 printf("merged tags: %u\n\n", small_array->len);
472 #endif
474 return res_array;
477 GPtrArray *tm_tags_merge(GPtrArray *big_array, GPtrArray *small_array,
478 TMTagAttrType *sort_attributes, gboolean unref_duplicates)
480 GPtrArray *res_array;
481 TMSortOptions sort_options;
483 sort_options.sort_attrs = sort_attributes;
484 sort_options.partial = FALSE;
485 res_array = merge(big_array, small_array, &sort_options, unref_duplicates);
486 return res_array;
490 This function will extract the tags of the specified types from an array of tags.
491 The returned value is a GPtrArray which should be free-d with a call to
492 g_ptr_array_free(array, TRUE). However, do not free the tags themselves since they
493 are not duplicated.
494 @param tags_array The original array of tags
495 @param tag_types - The tag types to extract. Can be a bitmask. For example, passing
496 (tm_tag_typedef_t | tm_tag_struct_t) will extract all typedefs and structures from
497 the original array.
498 @return an array of tags (NULL on failure)
500 GPtrArray *tm_tags_extract(GPtrArray *tags_array, TMTagType tag_types)
502 GPtrArray *new_tags;
503 guint i;
505 g_return_val_if_fail(tags_array, NULL);
507 new_tags = g_ptr_array_new();
508 for (i=0; i < tags_array->len; ++i)
510 if (NULL != tags_array->pdata[i])
512 if (tag_types & (((TMTag *) tags_array->pdata[i])->type))
513 g_ptr_array_add(new_tags, tags_array->pdata[i]);
516 return new_tags;
520 Completely frees an array of tags.
521 @param tags_array Array of tags to be freed.
522 @param free_array Whether the GptrArray is to be freed as well.
524 void tm_tags_array_free(GPtrArray *tags_array, gboolean free_all)
526 if (tags_array)
528 guint i;
529 for (i = 0; i < tags_array->len; ++i)
530 tm_tag_unref(tags_array->pdata[i]);
531 if (free_all)
532 g_ptr_array_free(tags_array, TRUE);
533 else
534 g_ptr_array_set_size(tags_array, 0);
538 /* copy/pasted bsearch() from libc extended with user_data for comparison function
539 * and using glib types */
540 static gpointer binary_search(gpointer key, gpointer base, size_t nmemb,
541 GCompareDataFunc compar, gpointer user_data)
543 gsize l, u, idx;
544 gpointer p;
545 gint comparison;
547 l = 0;
548 u = nmemb;
549 while (l < u)
551 idx = (l + u) / 2;
552 p = (gpointer) (((const gchar *) base) + (idx * sizeof(gpointer)));
553 comparison = (*compar) (key, p, user_data);
554 if (comparison < 0)
555 u = idx;
556 else if (comparison > 0)
557 l = idx + 1;
558 else
559 return (gpointer) p;
562 return NULL;
565 static gint tag_search_cmp(gconstpointer ptr1, gconstpointer ptr2, gpointer user_data)
567 gint res = tm_tag_compare(ptr1, ptr2, user_data);
569 if (res == 0)
571 TMSortOptions *sort_options = user_data;
572 const GPtrArray *tags_array = sort_options->tags_array;
573 TMTag **tag = (TMTag **) ptr2;
575 /* if previous/next (depending on sort options) tag equal, we haven't
576 * found the first/last tag in a sequence of equal tags yet */
577 if (sort_options->first && ptr2 != &tags_array->pdata[0]) {
578 if (tm_tag_compare(ptr1, tag - 1, user_data) == 0)
579 return -1;
581 else if (!sort_options->first && ptr2 != &tags_array->pdata[tags_array->len-1])
583 if (tm_tag_compare(ptr1, tag + 1, user_data) == 0)
584 return 1;
587 return res;
591 Returns a pointer to the position of the first matching tag in a (sorted) tags array.
592 The passed array of tags must be already sorted by name (searched with binary search).
593 @param tags_array Tag array (sorted on name)
594 @param name Name of the tag to locate.
595 @param partial If TRUE, matches the first part of the name instead of doing exact match.
596 @param tagCount Return location of the matched tags.
598 TMTag **tm_tags_find(const GPtrArray *tags_array, const char *name,
599 gboolean partial, guint *tagCount)
601 TMTag *tag, **first;
602 TMSortOptions sort_options;
604 *tagCount = 0;
605 if (!tags_array || !tags_array->len)
606 return NULL;
608 tag = g_new0(TMTag, 1);
609 tag->name = (char *) name;
611 sort_options.sort_attrs = NULL;
612 sort_options.partial = partial;
613 sort_options.tags_array = tags_array;
614 sort_options.first = TRUE;
615 first = (TMTag **)binary_search(&tag, tags_array->pdata, tags_array->len,
616 tag_search_cmp, &sort_options);
618 if (first)
620 TMTag **last;
621 unsigned first_pos;
623 sort_options.first = FALSE;
624 first_pos = first - (TMTag **)tags_array->pdata;
625 /* search between the first element and end */
626 last = (TMTag **)binary_search(&tag, first, tags_array->len - first_pos,
627 tag_search_cmp, &sort_options);
628 *tagCount = last - first + 1;
631 g_free(tag);
632 return (TMTag **) first;
635 /* Returns TMTag which "own" given line
636 @param line Current line in edited file.
637 @param file_tags A GPtrArray of edited file TMTag pointers.
638 @param tag_types the tag types to include in the match
639 @return TMTag pointers to owner tag. */
640 const TMTag *
641 tm_get_current_tag (GPtrArray * file_tags, const gulong line, const TMTagType tag_types)
643 TMTag *matching_tag = NULL;
644 if (file_tags && file_tags->len)
646 guint i;
647 gulong matching_line = 0;
649 for (i = 0; (i < file_tags->len); ++i)
651 TMTag *tag = TM_TAG (file_tags->pdata[i]);
652 if (tag && tag->type & tag_types &&
653 tag->line <= line && tag->line > matching_line)
655 matching_tag = tag;
656 matching_line = tag->line;
660 return matching_tag;
663 const gchar *tm_tag_context_separator(TMParserType lang)
665 switch (lang)
667 case TM_PARSER_C: /* for C++ .h headers or C structs */
668 case TM_PARSER_CPP:
669 case TM_PARSER_GLSL: /* for structs */
670 /*case GEANY_FILETYPES_RUBY:*/ /* not sure what to use atm*/
671 case TM_PARSER_PHP:
672 case TM_PARSER_POWERSHELL:
673 case TM_PARSER_RUST:
674 case TM_PARSER_ZEPHIR:
675 return "::";
677 /* avoid confusion with other possible separators in group/section name */
678 case TM_PARSER_CONF:
679 case TM_PARSER_REST:
680 return ":::";
682 /* no context separator */
683 case TM_PARSER_ASCIIDOC:
684 case TM_PARSER_TXT2TAGS:
685 return "\x03";
687 default:
688 return ".";
692 gboolean tm_tag_is_anon(const TMTag *tag)
694 guint i;
695 char dummy;
697 if (tag->lang == TM_PARSER_C || tag->lang == TM_PARSER_CPP)
698 return sscanf(tag->name, "anon_%*[a-z]_%u%c", &i, &dummy) == 1;
699 else if (tag->lang == TM_PARSER_FORTRAN || tag->lang == TM_PARSER_F77)
700 return sscanf(tag->name, "Structure#%u%c", &i, &dummy) == 1 ||
701 sscanf(tag->name, "Interface#%u%c", &i, &dummy) == 1 ||
702 sscanf(tag->name, "Enum#%u%c", &i, &dummy) == 1;
703 return FALSE;
707 gboolean tm_tag_langs_compatible(TMParserType lang, TMParserType other)
709 if (lang == TM_PARSER_NONE || other == TM_PARSER_NONE)
710 return FALSE;
711 if (lang == other)
712 return TRUE;
713 /* Accept CPP tags for C lang and vice versa */
714 else if (lang == TM_PARSER_C && other == TM_PARSER_CPP)
715 return TRUE;
716 else if (lang == TM_PARSER_CPP && other == TM_PARSER_C)
717 return TRUE;
719 return FALSE;
723 #ifdef TM_DEBUG /* various debugging functions */
726 Returns the type of tag as a string
727 @param tag The tag whose type is required
729 const char *tm_tag_type_name(const TMTag *tag)
731 g_return_val_if_fail(tag, NULL);
733 return tm_ctags_get_kind_name(tm_parser_get_tag_kind(tag->type, tag->lang), tag->lang);
737 Returns the TMTagType given the name of the type. Reverse of tm_tag_type_name.
738 @param tag_name Name of the tag type
740 TMTagType tm_tag_name_type(const char* tag_name, TMParserType lang)
742 g_return_val_if_fail(tag_name, tm_tag_undef_t);
744 return tm_parser_get_tag_type(tm_ctags_get_kind_from_name(tag_name, lang), lang);
747 static const char *tm_tag_impl_name(TMTag *tag)
749 g_return_val_if_fail(tag, NULL);
750 if (TAG_IMPL_VIRTUAL == tag->impl)
751 return "virtual";
752 else
753 return NULL;
756 static const char *tm_tag_access_name(TMTag *tag)
758 g_return_val_if_fail(tag, NULL);
759 if (TAG_ACCESS_PUBLIC == tag->access)
760 return "public";
761 else if (TAG_ACCESS_PROTECTED == tag->access)
762 return "protected";
763 else if (TAG_ACCESS_PRIVATE == tag->access)
764 return "private";
765 else
766 return NULL;
770 Prints information about a tag to the given file pointer.
771 @param tag The tag whose info is required.
772 @param fp The file pointer of the file to print the info to.
774 void tm_tag_print(TMTag *tag, FILE *fp)
776 const char *laccess, *impl, *type;
777 if (!tag || !fp)
778 return;
779 laccess = tm_tag_access_name(tag);
780 impl = tm_tag_impl_name(tag);
781 type = tm_tag_type_name(tag);
782 if (laccess)
783 fprintf(fp, "%s ", laccess);
784 if (impl)
785 fprintf(fp, "%s ", impl);
786 if (type)
787 fprintf(fp, "%s ", type);
788 if (tag->var_type)
789 fprintf(fp, "%s ", tag->var_type);
790 if (tag->scope)
791 fprintf(fp, "%s::", tag->scope);
792 fprintf(fp, "%s", tag->name);
793 if (tag->arglist)
794 fprintf(fp, "%s", tag->arglist);
795 if (tag->inheritance)
796 fprintf(fp, " : from %s", tag->inheritance);
797 if ((tag->file) && (tag->line > 0))
798 fprintf(fp, "[%s:%ld]", tag->file->file_name
799 , tag->line);
800 fprintf(fp, "\n");
804 Prints info about all tags in the array to the given file pointer.
806 void tm_tags_array_print(GPtrArray *tags, FILE *fp)
808 guint i;
809 TMTag *tag;
810 if (!(tags && (tags->len > 0) && fp))
811 return;
812 for (i = 0; i < tags->len; ++i)
814 tag = TM_TAG(tags->pdata[i]);
815 tm_tag_print(tag, fp);
820 Returns the depth of tag scope (useful for finding tag hierarchy
822 gint tm_tag_scope_depth(const TMTag *t)
824 const gchar *context_sep = tm_tag_context_separator(t->lang);
825 gint depth;
826 char *s;
827 if(!(t && t->scope))
828 return 0;
829 for (s = t->scope, depth = 0; s; s = strstr(s, context_sep))
831 ++ depth;
832 ++ s;
834 return depth;
837 #endif /* TM_DEBUG */