Use language-specific context separator instead of hard-coded "::"
[geany-mirror.git] / tagmanager / src / tm_tag.c
bloba5ddeeeb81094a8446826087c99c97858cb339c8
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 "general.h"
15 #include "entry.h"
16 #include "parse.h"
17 #include "read.h"
18 #define LIBCTAGS_DEFINED
19 #include "tm_tag.h"
20 #include "tm_parser.h"
23 #define TAG_NEW(T) ((T) = g_slice_new0(TMTag))
24 #define TAG_FREE(T) g_slice_free(TMTag, (T))
27 #ifdef DEBUG_TAG_REFS
29 static GHashTable *alive_tags = NULL;
31 static void foreach_tags_log(gpointer key, gpointer value, gpointer data)
33 gsize *ref_count = data;
34 const TMTag *tag = value;
36 *ref_count += (gsize) tag->refcount;
37 g_debug("Leaked TMTag (%d refs): %s", tag->refcount, tag->name);
40 static void log_refs_at_exit(void)
42 gsize ref_count = 0;
44 g_hash_table_foreach(alive_tags, foreach_tags_log, &ref_count);
45 g_debug("TMTag references left at exit: %lu", ref_count);
48 static TMTag *log_tag_new(void)
50 TMTag *tag;
52 if (! alive_tags)
54 alive_tags = g_hash_table_new(g_direct_hash, g_direct_equal);
55 atexit(log_refs_at_exit);
57 TAG_NEW(tag);
58 g_hash_table_insert(alive_tags, tag, tag);
60 return tag;
63 static void log_tag_free(TMTag *tag)
65 g_return_if_fail(alive_tags != NULL);
67 if (! g_hash_table_remove(alive_tags, tag)) {
68 g_critical("Freeing invalid TMTag pointer %p", (void *) tag);
69 } else {
70 TAG_FREE(tag);
74 #undef TAG_NEW
75 #undef TAG_FREE
76 #define TAG_NEW(T) ((T) = log_tag_new())
77 #define TAG_FREE(T) log_tag_free(T)
79 #endif /* DEBUG_TAG_REFS */
82 const TMTagType TM_GLOBAL_TYPE_MASK =
83 tm_tag_class_t | tm_tag_enum_t | tm_tag_interface_t |
84 tm_tag_struct_t | tm_tag_typedef_t | tm_tag_union_t | tm_tag_namespace_t;
87 /* Note: To preserve binary compatibility, it is very important
88 that you only *append* to this list ! */
89 enum
91 TA_NAME = 200,
92 TA_LINE,
93 TA_LOCAL,
94 TA_POS, /* Obsolete */
95 TA_TYPE,
96 TA_ARGLIST,
97 TA_SCOPE,
98 TA_VARTYPE,
99 TA_INHERITS,
100 TA_TIME,
101 TA_ACCESS,
102 TA_IMPL,
103 TA_LANG,
104 TA_INACTIVE, /* Obsolete */
105 TA_POINTER
108 typedef struct
110 guint *sort_attrs;
111 gboolean partial;
112 const GPtrArray *tags_array;
113 gboolean first;
114 } TMSortOptions;
116 static const char *s_tag_type_names[] = {
117 "class", /* classes */
118 "enum", /* enumeration names */
119 "enumerator", /* enumerators (values inside an enumeration) */
120 "externvar", /* external variable declarations */
121 "field", /* fields */
122 "function", /* function definitions */
123 "interface", /* interfaces */
124 "macro", /* macro definitions */
125 "member", /* class, struct, and union members */
126 "method", /* methods */
127 "namespace", /* namespaces */
128 "package", /* packages */
129 "prototype", /* function prototypes */
130 "struct", /* structure names */
131 "typedef", /* typedefs */
132 "union", /* union names */
133 "variable", /* variable definitions */
134 "other" /* Other tag type (non C/C++/Java) */
137 static TMTagType s_tag_types[] = {
138 tm_tag_class_t,
139 tm_tag_enum_t,
140 tm_tag_enumerator_t,
141 tm_tag_externvar_t,
142 tm_tag_field_t,
143 tm_tag_function_t,
144 tm_tag_interface_t,
145 tm_tag_macro_t,
146 tm_tag_member_t,
147 tm_tag_method_t,
148 tm_tag_namespace_t,
149 tm_tag_package_t,
150 tm_tag_prototype_t,
151 tm_tag_struct_t,
152 tm_tag_typedef_t,
153 tm_tag_union_t,
154 tm_tag_variable_t,
155 tm_tag_other_t
158 /* Gets the GType for a TMTag */
159 GType tm_tag_get_type(void)
161 static GType gtype = 0;
162 if (G_UNLIKELY (gtype == 0))
164 gtype = g_boxed_type_register_static("TMTag", (GBoxedCopyFunc)tm_tag_ref,
165 (GBoxedFreeFunc)tm_tag_unref);
167 return gtype;
170 static TMTagType get_tag_type(const char *tag_name)
172 unsigned int i;
173 int cmp;
174 g_return_val_if_fail(tag_name, 0);
175 for (i=0; i < sizeof(s_tag_type_names)/sizeof(char *); ++i)
177 cmp = strcmp(tag_name, s_tag_type_names[i]);
178 if (0 == cmp)
179 return s_tag_types[i];
180 else if (cmp < 0)
181 break;
183 /* other is not checked above as it is last, not sorted alphabetically */
184 if (strcmp(tag_name, "other") == 0)
185 return tm_tag_other_t;
186 #ifdef TM_DEBUG
187 fprintf(stderr, "Unknown tag type %s\n", tag_name);
188 #endif
189 return tm_tag_undef_t;
192 static char get_tag_impl(const char *impl)
194 if ((0 == strcmp("virtual", impl))
195 || (0 == strcmp("pure virtual", impl)))
196 return TAG_IMPL_VIRTUAL;
198 #ifdef TM_DEBUG
199 g_warning("Unknown implementation %s", impl);
200 #endif
201 return TAG_IMPL_UNKNOWN;
204 static char get_tag_access(const char *access)
206 if (0 == strcmp("public", access))
207 return TAG_ACCESS_PUBLIC;
208 else if (0 == strcmp("protected", access))
209 return TAG_ACCESS_PROTECTED;
210 else if (0 == strcmp("private", access))
211 return TAG_ACCESS_PRIVATE;
212 else if (0 == strcmp("friend", access))
213 return TAG_ACCESS_FRIEND;
214 else if (0 == strcmp("default", access))
215 return TAG_ACCESS_DEFAULT;
217 #ifdef TM_DEBUG
218 g_warning("Unknown access type %s", access);
219 #endif
220 return TAG_ACCESS_UNKNOWN;
224 Initializes a TMTag structure with information from a tagEntryInfo struct
225 used by the ctags parsers. Note that the TMTag structure must be malloc()ed
226 before calling this function. This function is called by tm_tag_new() - you
227 should not need to call this directly.
228 @param tag The TMTag structure to initialize
229 @param file Pointer to a TMSourceFile struct (it is assigned to the file member)
230 @param tag_entry Tag information gathered by the ctags parser
231 @return TRUE on success, FALSE on failure
233 static gboolean tm_tag_init(TMTag *tag, TMSourceFile *file, const tagEntryInfo *tag_entry)
235 tag->refcount = 1;
236 if (NULL == tag_entry)
237 return FALSE;
239 /* This is a normal tag entry */
240 if (NULL == tag_entry->name)
241 return FALSE;
242 tag->name = g_strdup(tag_entry->name);
243 tag->type = get_tag_type(tag_entry->kindName);
244 tag->local = tag_entry->isFileScope;
245 tag->pointerOrder = 0; /* backward compatibility (use var_type instead) */
246 tag->line = tag_entry->lineNumber;
247 if (NULL != tag_entry->extensionFields.arglist)
248 tag->arglist = g_strdup(tag_entry->extensionFields.arglist);
249 if ((NULL != tag_entry->extensionFields.scope[1]) &&
250 (isalpha(tag_entry->extensionFields.scope[1][0]) ||
251 tag_entry->extensionFields.scope[1][0] == '_' ||
252 tag_entry->extensionFields.scope[1][0] == '$'))
253 tag->scope = g_strdup(tag_entry->extensionFields.scope[1]);
254 if (tag_entry->extensionFields.inheritance != NULL)
255 tag->inheritance = g_strdup(tag_entry->extensionFields.inheritance);
256 if (tag_entry->extensionFields.varType != NULL)
257 tag->var_type = g_strdup(tag_entry->extensionFields.varType);
258 if (tag_entry->extensionFields.access != NULL)
259 tag->access = get_tag_access(tag_entry->extensionFields.access);
260 if (tag_entry->extensionFields.implementation != NULL)
261 tag->impl = get_tag_impl(tag_entry->extensionFields.implementation);
262 if ((tm_tag_macro_t == tag->type) && (NULL != tag->arglist))
263 tag->type = tm_tag_macro_with_arg_t;
264 tag->file = file;
265 tag->lang = file->lang;
266 return TRUE;
270 Creates a new tag structure from a tagEntryInfo pointer and a TMSOurceFile pointer
271 and returns a pointer to it.
272 @param file - Pointer to the TMSourceFile structure containing the tag
273 @param tag_entry Contains tag information generated by ctags
274 @return the new TMTag structure. This should be free()-ed using tm_tag_free()
276 TMTag *tm_tag_new(TMSourceFile *file, const tagEntryInfo *tag_entry)
278 TMTag *tag;
280 TAG_NEW(tag);
281 if (FALSE == tm_tag_init(tag, file, tag_entry))
283 TAG_FREE(tag);
284 return NULL;
286 return tag;
290 Initializes an already malloc()ed TMTag structure by reading a tag entry
291 line from a file. The structure should be allocated beforehand.
292 @param tag The TMTag structure to populate
293 @param file The TMSourceFile struct (assigned to the file member)
294 @param fp FILE pointer from where the tag line is read
295 @return TRUE on success, FALSE on FAILURE
297 static gboolean tm_tag_init_from_file(TMTag *tag, TMSourceFile *file, FILE *fp)
299 guchar buf[BUFSIZ];
300 guchar *start, *end;
301 gboolean status;
302 guchar changed_char = TA_NAME;
304 tag->refcount = 1;
305 if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf))
306 return FALSE;
307 for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end)
309 while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n'))
310 ++ end;
311 if (('\0' == *end) || ('\n' == *end))
312 status = FALSE;
313 changed_char = *end;
314 *end = '\0';
315 if (NULL == tag->name)
317 if (!isprint(*start))
318 return FALSE;
319 else
320 tag->name = g_strdup((gchar*)start);
322 else
324 switch (*start)
326 case TA_LINE:
327 tag->line = atol((gchar*)start + 1);
328 break;
329 case TA_LOCAL:
330 tag->local = atoi((gchar*)start + 1);
331 break;
332 case TA_TYPE:
333 tag->type = (TMTagType) atoi((gchar*)start + 1);
334 break;
335 case TA_ARGLIST:
336 tag->arglist = g_strdup((gchar*)start + 1);
337 break;
338 case TA_SCOPE:
339 tag->scope = g_strdup((gchar*)start + 1);
340 break;
341 case TA_POINTER:
342 tag->pointerOrder = atoi((gchar*)start + 1);
343 break;
344 case TA_VARTYPE:
345 tag->var_type = g_strdup((gchar*)start + 1);
346 break;
347 case TA_INHERITS:
348 tag->inheritance = g_strdup((gchar*)start + 1);
349 break;
350 case TA_TIME: /* Obsolete */
351 break;
352 case TA_LANG: /* Obsolete */
353 break;
354 case TA_INACTIVE: /* Obsolete */
355 break;
356 case TA_ACCESS:
357 tag->access = (char) *(start + 1);
358 break;
359 case TA_IMPL:
360 tag->impl = (char) *(start + 1);
361 break;
362 default:
363 #ifdef GEANY_DEBUG
364 g_warning("Unknown attribute %s", start + 1);
365 #endif
366 break;
369 *end = changed_char;
371 if (NULL == tag->name)
372 return FALSE;
373 tag->file = file;
374 return TRUE;
377 /* alternative parser for Pascal and LaTeX global tags files with the following format
378 * tagname|return value|arglist|description\n */
379 static gboolean tm_tag_init_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp)
381 guchar buf[BUFSIZ];
382 guchar *start, *end;
383 gboolean status;
384 /*guchar changed_char = TA_NAME;*/
386 tag->refcount = 1;
387 if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf))
388 return FALSE;
390 gchar **fields;
391 guint field_len;
392 for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end)
394 while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n'))
395 ++ end;
396 if (('\0' == *end) || ('\n' == *end))
397 status = FALSE;
398 /*changed_char = *end;*/
399 *end = '\0';
400 if (NULL == tag->name && !isprint(*start))
401 return FALSE;
403 fields = g_strsplit((gchar*)start, "|", -1);
404 field_len = g_strv_length(fields);
406 if (field_len >= 1) tag->name = g_strdup(fields[0]);
407 else tag->name = NULL;
408 if (field_len >= 2 && fields[1] != NULL) tag->var_type = g_strdup(fields[1]);
409 if (field_len >= 3 && fields[2] != NULL) tag->arglist = g_strdup(fields[2]);
410 tag->type = tm_tag_prototype_t;
411 g_strfreev(fields);
415 if (NULL == tag->name)
416 return FALSE;
417 tag->file = file;
418 return TRUE;
422 Same as tm_tag_init_from_file(), but parsing CTags tag file format
423 (http://ctags.sourceforge.net/FORMAT)
425 static gboolean tm_tag_init_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp)
427 gchar buf[BUFSIZ];
428 gchar *p, *tab;
430 tag->refcount = 1;
431 tag->type = tm_tag_function_t; /* default type is function if no kind is specified */
434 if ((NULL == fgets(buf, BUFSIZ, fp)) || ('\0' == *buf))
435 return FALSE;
437 while (strncmp(buf, "!_TAG_", 6) == 0); /* skip !_TAG_ lines */
439 p = buf;
441 /* tag name */
442 if (! (tab = strchr(p, '\t')) || p == tab)
443 return FALSE;
444 tag->name = g_strndup(p, (gsize)(tab - p));
445 p = tab + 1;
447 /* tagfile, unused */
448 if (! (tab = strchr(p, '\t')))
450 g_free(tag->name);
451 tag->name = NULL;
452 return FALSE;
454 p = tab + 1;
455 /* Ex command, unused */
456 if (*p == '/' || *p == '?')
458 gchar c = *p;
459 for (++p; *p && *p != c; p++)
461 if (*p == '\\' && p[1])
462 p++;
465 else /* assume a line */
466 tag->line = atol(p);
467 tab = strstr(p, ";\"");
468 /* read extension fields */
469 if (tab)
471 p = tab + 2;
472 while (*p && *p != '\n' && *p != '\r')
474 gchar *end;
475 const gchar *key, *value = NULL;
477 /* skip leading tabulations */
478 while (*p && *p == '\t') p++;
479 /* find the separator (:) and end (\t) */
480 key = end = p;
481 while (*end && *end != '\t' && *end != '\n' && *end != '\r')
483 if (*end == ':' && ! value)
485 *end = 0; /* terminate the key */
486 value = end + 1;
488 end++;
490 /* move p paste the so we won't stop parsing by setting *end=0 below */
491 p = *end ? end + 1 : end;
492 *end = 0; /* terminate the value (or key if no value) */
494 if (! value || 0 == strcmp(key, "kind")) /* tag kind */
496 const gchar *kind = value ? value : key;
498 if (kind[0] && kind[1])
499 tag->type = get_tag_type(kind);
500 else
502 switch (*kind)
504 case 'c': tag->type = tm_tag_class_t; break;
505 case 'd': tag->type = tm_tag_macro_t; break;
506 case 'e': tag->type = tm_tag_enumerator_t; break;
507 case 'F': tag->type = tm_tag_other_t; break; /* Obsolete */
508 case 'f': tag->type = tm_tag_function_t; break;
509 case 'g': tag->type = tm_tag_enum_t; break;
510 case 'I': tag->type = tm_tag_class_t; break;
511 case 'i': tag->type = tm_tag_interface_t; break;
512 case 'l': tag->type = tm_tag_variable_t; break;
513 case 'M': tag->type = tm_tag_macro_t; break;
514 case 'm': tag->type = tm_tag_member_t; break;
515 case 'n': tag->type = tm_tag_namespace_t; break;
516 case 'P': tag->type = tm_tag_package_t; break;
517 case 'p': tag->type = tm_tag_prototype_t; break;
518 case 's': tag->type = tm_tag_struct_t; break;
519 case 't': tag->type = tm_tag_typedef_t; break;
520 case 'u': tag->type = tm_tag_union_t; break;
521 case 'v': tag->type = tm_tag_variable_t; break;
522 case 'x': tag->type = tm_tag_externvar_t; break;
523 default:
524 #ifdef TM_DEBUG
525 g_warning("Unknown tag kind %c", *kind);
526 #endif
527 tag->type = tm_tag_other_t; break;
531 else if (0 == strcmp(key, "inherits")) /* comma-separated list of classes this class inherits from */
533 g_free(tag->inheritance);
534 tag->inheritance = g_strdup(value);
536 else if (0 == strcmp(key, "implementation")) /* implementation limit */
537 tag->impl = get_tag_impl(value);
538 else if (0 == strcmp(key, "line")) /* line */
539 tag->line = atol(value);
540 else if (0 == strcmp(key, "access")) /* access */
541 tag->access = get_tag_access(value);
542 else if (0 == strcmp(key, "class") ||
543 0 == strcmp(key, "enum") ||
544 0 == strcmp(key, "function") ||
545 0 == strcmp(key, "struct") ||
546 0 == strcmp(key, "union")) /* Name of the class/enum/function/struct/union in which this tag is a member */
548 g_free(tag->scope);
549 tag->scope = g_strdup(value);
551 else if (0 == strcmp(key, "file")) /* static (local) tag */
552 tag->local = TRUE;
553 else if (0 == strcmp(key, "signature")) /* arglist */
555 g_free(tag->arglist);
556 tag->arglist = g_strdup(value);
561 tag->file = file;
562 return TRUE;
566 Same as tm_tag_new() except that the tag attributes are read from file.
567 @param mode langType to use for the tag.
569 TMTag *tm_tag_new_from_file(TMSourceFile *file, FILE *fp, gint mode, TMFileFormat format)
571 TMTag *tag;
572 gboolean result = FALSE;
574 TAG_NEW(tag);
576 switch (format)
578 case TM_FILE_FORMAT_TAGMANAGER:
579 result = tm_tag_init_from_file(tag, file, fp);
580 break;
581 case TM_FILE_FORMAT_PIPE:
582 result = tm_tag_init_from_file_alt(tag, file, fp);
583 break;
584 case TM_FILE_FORMAT_CTAGS:
585 result = tm_tag_init_from_file_ctags(tag, file, fp);
586 break;
589 if (! result)
591 TAG_FREE(tag);
592 return NULL;
594 tag->lang = mode;
595 return tag;
599 Writes tag information to the given FILE *.
600 @param tag The tag information to write.
601 @param file FILE pointer to which the tag information is written.
602 @param attrs Attributes to be written (bitmask).
603 @return TRUE on success, FALSE on failure.
605 gboolean tm_tag_write(TMTag *tag, FILE *fp, TMTagAttrType attrs)
607 fprintf(fp, "%s", tag->name);
608 if (attrs & tm_tag_attr_type_t)
609 fprintf(fp, "%c%d", TA_TYPE, tag->type);
610 if ((attrs & tm_tag_attr_arglist_t) && (NULL != tag->arglist))
611 fprintf(fp, "%c%s", TA_ARGLIST, tag->arglist);
612 if (attrs & tm_tag_attr_line_t)
613 fprintf(fp, "%c%ld", TA_LINE, tag->line);
614 if (attrs & tm_tag_attr_local_t)
615 fprintf(fp, "%c%d", TA_LOCAL, tag->local);
616 if ((attrs & tm_tag_attr_scope_t) && (NULL != tag->scope))
617 fprintf(fp, "%c%s", TA_SCOPE, tag->scope);
618 if ((attrs & tm_tag_attr_inheritance_t) && (NULL != tag->inheritance))
619 fprintf(fp, "%c%s", TA_INHERITS, tag->inheritance);
620 if (attrs & tm_tag_attr_pointer_t)
621 fprintf(fp, "%c%d", TA_POINTER, tag->pointerOrder);
622 if ((attrs & tm_tag_attr_vartype_t) && (NULL != tag->var_type))
623 fprintf(fp, "%c%s", TA_VARTYPE, tag->var_type);
624 if ((attrs & tm_tag_attr_access_t) && (TAG_ACCESS_UNKNOWN != tag->access))
625 fprintf(fp, "%c%c", TA_ACCESS, tag->access);
626 if ((attrs & tm_tag_attr_impl_t) && (TAG_IMPL_UNKNOWN != tag->impl))
627 fprintf(fp, "%c%c", TA_IMPL, tag->impl);
629 if (fprintf(fp, "\n"))
630 return TRUE;
631 else
632 return FALSE;
636 Destroys a TMTag structure, i.e. frees all elements except the tag itself.
637 @param tag The TMTag structure to destroy
638 @see tm_tag_free()
640 static void tm_tag_destroy(TMTag *tag)
642 g_free(tag->name);
643 g_free(tag->arglist);
644 g_free(tag->scope);
645 g_free(tag->inheritance);
646 g_free(tag->var_type);
651 Drops a reference from a TMTag. If the reference count reaches 0, this function
652 destroys all data in the tag and frees the tag structure as well.
653 @param tag Pointer to a TMTag structure
655 void tm_tag_unref(TMTag *tag)
657 /* be NULL-proof because tm_tag_free() was NULL-proof and we indent to be a
658 * drop-in replacment of it */
659 if (NULL != tag && g_atomic_int_dec_and_test(&tag->refcount))
661 tm_tag_destroy(tag);
662 TAG_FREE(tag);
667 Adds a reference to a TMTag.
668 @param tag Pointer to a TMTag structure
669 @return the passed-in TMTag
671 TMTag *tm_tag_ref(TMTag *tag)
673 g_atomic_int_inc(&tag->refcount);
674 return tag;
678 Inbuilt tag comparison function.
680 static gint tm_tag_compare(gconstpointer ptr1, gconstpointer ptr2, gpointer user_data)
682 unsigned int *sort_attr;
683 int returnval = 0;
684 TMTag *t1 = *((TMTag **) ptr1);
685 TMTag *t2 = *((TMTag **) ptr2);
686 TMSortOptions *sort_options = user_data;
688 if ((NULL == t1) || (NULL == t2))
690 g_warning("Found NULL tag");
691 return t2 - t1;
693 if (NULL == sort_options->sort_attrs)
695 if (sort_options->partial)
696 return strncmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""), strlen(FALLBACK(t1->name, "")));
697 else
698 return strcmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""));
701 for (sort_attr = sort_options->sort_attrs; returnval == 0 && *sort_attr != tm_tag_attr_none_t; ++ sort_attr)
703 switch (*sort_attr)
705 case tm_tag_attr_name_t:
706 if (sort_options->partial)
707 returnval = strncmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""), strlen(FALLBACK(t1->name, "")));
708 else
709 returnval = strcmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""));
710 break;
711 case tm_tag_attr_file_t:
712 returnval = t1->file - t2->file;
713 break;
714 case tm_tag_attr_line_t:
715 returnval = t1->line - t2->line;
716 break;
717 case tm_tag_attr_type_t:
718 returnval = t1->type - t2->type;
719 break;
720 case tm_tag_attr_scope_t:
721 returnval = strcmp(FALLBACK(t1->scope, ""), FALLBACK(t2->scope, ""));
722 break;
723 case tm_tag_attr_arglist_t:
724 returnval = strcmp(FALLBACK(t1->arglist, ""), FALLBACK(t2->arglist, ""));
725 if (returnval != 0)
727 int line_diff = (t1->line - t2->line);
729 returnval = line_diff ? line_diff : returnval;
731 break;
732 case tm_tag_attr_vartype_t:
733 returnval = strcmp(FALLBACK(t1->var_type, ""), FALLBACK(t2->var_type, ""));
734 break;
737 return returnval;
740 gboolean tm_tags_equal(const TMTag *a, const TMTag *b)
742 if (a == b)
743 return TRUE;
745 return (a->line == b->line &&
746 a->file == b->file /* ptr comparison */ &&
747 strcmp(FALLBACK(a->name, ""), FALLBACK(b->name, "")) == 0 &&
748 a->type == b->type &&
749 a->local == b->local &&
750 a->pointerOrder == b->pointerOrder &&
751 a->access == b->access &&
752 a->impl == b->impl &&
753 a->lang == b->lang &&
754 strcmp(FALLBACK(a->scope, ""), FALLBACK(b->scope, "")) == 0 &&
755 strcmp(FALLBACK(a->arglist, ""), FALLBACK(b->arglist, "")) == 0 &&
756 strcmp(FALLBACK(a->inheritance, ""), FALLBACK(b->inheritance, "")) == 0 &&
757 strcmp(FALLBACK(a->var_type, ""), FALLBACK(b->var_type, "")) == 0);
761 Removes NULL tag entries from an array of tags. Called after tm_tags_dedup() since
762 this function substitutes duplicate entries with NULL
763 @param tags_array Array of tags to dedup
764 @return TRUE on success, FALSE on failure
766 gboolean tm_tags_prune(GPtrArray *tags_array)
768 guint i, count;
769 for (i=0, count = 0; i < tags_array->len; ++i)
771 if (NULL != tags_array->pdata[i])
772 tags_array->pdata[count++] = tags_array->pdata[i];
774 tags_array->len = count;
775 return TRUE;
779 Deduplicates an array on tags using the inbuilt comparison function based on
780 the attributes specified. Called by tm_tags_sort() when dedup is TRUE.
781 @param tags_array Array of tags to dedup.
782 @param sort_attributes Attributes the array is sorted on. They will be deduped
783 on the same criteria.
784 @return TRUE on success, FALSE on failure
786 gboolean tm_tags_dedup(GPtrArray *tags_array, TMTagAttrType *sort_attributes, gboolean unref_duplicates)
788 TMSortOptions sort_options;
789 guint i;
791 if ((!tags_array) || (!tags_array->len))
792 return TRUE;
793 sort_options.sort_attrs = sort_attributes;
794 sort_options.partial = FALSE;
795 for (i = 1; i < tags_array->len; ++i)
797 if (0 == tm_tag_compare(&(tags_array->pdata[i - 1]), &(tags_array->pdata[i]), &sort_options))
799 if (unref_duplicates)
800 tm_tag_unref(tags_array->pdata[i-1]);
801 tags_array->pdata[i-1] = NULL;
804 tm_tags_prune(tags_array);
805 return TRUE;
809 Sort an array of tags on the specified attribuites using the inbuilt comparison
810 function.
811 @param tags_array The array of tags to be sorted
812 @param sort_attributes Attributes to be sorted on (int array terminated by 0)
813 @param dedup Whether to deduplicate the sorted array
814 @return TRUE on success, FALSE on failure
816 gboolean tm_tags_sort(GPtrArray *tags_array, TMTagAttrType *sort_attributes,
817 gboolean dedup, gboolean unref_duplicates)
819 TMSortOptions sort_options;
821 if ((!tags_array) || (!tags_array->len))
822 return TRUE;
823 sort_options.sort_attrs = sort_attributes;
824 sort_options.partial = FALSE;
825 g_ptr_array_sort_with_data(tags_array, tm_tag_compare, &sort_options);
826 if (dedup)
827 tm_tags_dedup(tags_array, sort_attributes, unref_duplicates);
828 return TRUE;
831 void tm_tags_remove_file_tags(TMSourceFile *source_file, GPtrArray *tags_array)
833 guint i;
835 /* Now we choose between an algorithm with complexity O(tags_array->len) and
836 * O(source_file->tags_array->len * log(tags_array->len)). The latter algorithm
837 * is better when tags_array contains many times more tags than
838 * source_file->tags_array so instead of trying to find the removed tags
839 * linearly, binary search is used. The constant 20 is more or less random
840 * but seems to work well. It's exact value isn't so critical because it's
841 * the extremes where the difference is the biggest: when
842 * source_file->tags_array->len == tags_array->len (single file open) and
843 * source_file->tags_array->len << tags_array->len (the number of tags
844 * from the file is a small fraction of all tags).
846 if (source_file->tags_array->len != 0 &&
847 tags_array->len / source_file->tags_array->len < 20)
849 for (i = 0; i < tags_array->len; i++)
851 TMTag *tag = tags_array->pdata[i];
853 if (tag->file == source_file)
854 tags_array->pdata[i] = NULL;
857 else
859 GPtrArray *to_delete = g_ptr_array_sized_new(source_file->tags_array->len);
861 for (i = 0; i < source_file->tags_array->len; i++)
863 guint j;
864 guint tag_count;
865 TMTag **found;
866 TMTag *tag = source_file->tags_array->pdata[i];
868 found = tm_tags_find(tags_array, tag->name, FALSE, &tag_count);
870 for (j = 0; j < tag_count; j++)
872 if (*found != NULL && (*found)->file == source_file)
874 /* we cannot set the pointer to NULL now because the search wouldn't work */
875 g_ptr_array_add(to_delete, found);
876 /* no break - if there are multiple tags of the same name, we would
877 * always find the first instance and wouldn't remove others; duplicates
878 * in the to_delete list aren't a problem */
880 found++;
884 for (i = 0; i < to_delete->len; i++)
886 TMTag **tag = to_delete->pdata[i];
887 *tag = NULL;
889 g_ptr_array_free(to_delete, TRUE);
892 tm_tags_prune(tags_array);
895 /* Optimized merge sort for merging sorted values from one array to another
896 * where one of the arrays is much smaller than the other.
897 * The merge complexity depends mostly on the size of the small array
898 * and is almost independent of the size of the big array.
899 * In addition, get rid of the duplicates (if both big_array and small_array are duplicate-free). */
900 static GPtrArray *merge(GPtrArray *big_array, GPtrArray *small_array,
901 TMSortOptions *sort_options, gboolean unref_duplicates) {
902 guint i1 = 0; /* index to big_array */
903 guint i2 = 0; /* index to small_array */
904 guint initial_step;
905 guint step;
906 GPtrArray *res_array = g_ptr_array_sized_new(big_array->len + small_array->len);
907 #ifdef TM_DEBUG
908 guint cmpnum = 0;
909 #endif
911 /* swap the arrays if len(small) > len(big) */
912 if (small_array->len > big_array->len)
914 GPtrArray *tmp = small_array;
915 small_array = big_array;
916 big_array = tmp;
919 /* on average, we are merging a value from small_array every
920 * len(big_array) / len(small_array) values - good approximation for fast jump
921 * step size */
922 initial_step = (small_array->len > 0) ? big_array->len / small_array->len : 1;
923 initial_step = initial_step > 4 ? initial_step : 1;
924 step = initial_step;
926 while (i1 < big_array->len && i2 < small_array->len)
928 gpointer val1;
929 gpointer val2 = small_array->pdata[i2];
931 if (step > 4) /* fast path start */
933 guint j1 = (i1 + step < big_array->len) ? i1 + step : big_array->len - 1;
935 val1 = big_array->pdata[j1];
936 #ifdef TM_DEBUG
937 cmpnum++;
938 #endif
939 /* if the value in big_array after making the big step is still smaller
940 * than the value in small_array, we can copy all the values inbetween
941 * into the result without making expensive string comparisons */
942 if (tm_tag_compare(&val1, &val2, sort_options) < 0)
944 while (i1 <= j1)
946 val1 = big_array->pdata[i1];
947 g_ptr_array_add(res_array, val1);
948 i1++;
951 else
953 /* lower the step and try again */
954 step /= 2;
956 } /* fast path end */
957 else
959 gint cmpval;
961 #ifdef TM_DEBUG
962 cmpnum++;
963 #endif
964 val1 = big_array->pdata[i1];
965 cmpval = tm_tag_compare(&val1, &val2, sort_options);
966 if (cmpval < 0)
968 g_ptr_array_add(res_array, val1);
969 i1++;
971 else
973 g_ptr_array_add(res_array, val2);
974 i2++;
975 /* value from small_array gets merged - reset the step size */
976 step = initial_step;
977 if (cmpval == 0)
979 i1++; /* remove the duplicate, keep just the newly merged value */
980 if (unref_duplicates)
981 tm_tag_unref(val1);
987 /* end of one of the arrays reached - copy the rest from the other array */
988 while (i1 < big_array->len)
989 g_ptr_array_add(res_array, big_array->pdata[i1++]);
990 while (i2 < small_array->len)
991 g_ptr_array_add(res_array, small_array->pdata[i2++]);
993 #ifdef TM_DEBUG
994 printf("cmpnums: %u\n", cmpnum);
995 printf("total tags: %u\n", big_array->len);
996 printf("merged tags: %u\n\n", small_array->len);
997 #endif
999 return res_array;
1002 GPtrArray *tm_tags_merge(GPtrArray *big_array, GPtrArray *small_array,
1003 TMTagAttrType *sort_attributes, gboolean unref_duplicates)
1005 GPtrArray *res_array;
1006 TMSortOptions sort_options;
1008 sort_options.sort_attrs = sort_attributes;
1009 sort_options.partial = FALSE;
1010 res_array = merge(big_array, small_array, &sort_options, unref_duplicates);
1011 return res_array;
1015 This function will extract the tags of the specified types from an array of tags.
1016 The returned value is a GPtrArray which should be free-d with a call to
1017 g_ptr_array_free(array, TRUE). However, do not free the tags themselves since they
1018 are not duplicated.
1019 @param tags_array The original array of tags
1020 @param tag_types - The tag types to extract. Can be a bitmask. For example, passing
1021 (tm_tag_typedef_t | tm_tag_struct_t) will extract all typedefs and structures from
1022 the original array.
1023 @return an array of tags (NULL on failure)
1025 GPtrArray *tm_tags_extract(GPtrArray *tags_array, TMTagType tag_types)
1027 GPtrArray *new_tags;
1028 guint i;
1029 if (NULL == tags_array)
1030 return NULL;
1031 new_tags = g_ptr_array_new();
1032 for (i=0; i < tags_array->len; ++i)
1034 if (NULL != tags_array->pdata[i])
1036 if (tag_types & (((TMTag *) tags_array->pdata[i])->type))
1037 g_ptr_array_add(new_tags, tags_array->pdata[i]);
1040 return new_tags;
1044 Completely frees an array of tags.
1045 @param tags_array Array of tags to be freed.
1046 @param free_array Whether the GptrArray is to be freed as well.
1048 void tm_tags_array_free(GPtrArray *tags_array, gboolean free_all)
1050 if (tags_array)
1052 guint i;
1053 for (i = 0; i < tags_array->len; ++i)
1054 tm_tag_unref(tags_array->pdata[i]);
1055 if (free_all)
1056 g_ptr_array_free(tags_array, TRUE);
1057 else
1058 g_ptr_array_set_size(tags_array, 0);
1062 /* copy/pasted bsearch() from libc extended with user_data for comparison function
1063 * and using glib types */
1064 static gpointer binary_search(gpointer key, gpointer base, size_t nmemb,
1065 GCompareDataFunc compar, gpointer user_data)
1067 gsize l, u, idx;
1068 gpointer p;
1069 gint comparison;
1071 l = 0;
1072 u = nmemb;
1073 while (l < u)
1075 idx = (l + u) / 2;
1076 p = (gpointer) (((const gchar *) base) + (idx * sizeof(gpointer)));
1077 comparison = (*compar) (key, p, user_data);
1078 if (comparison < 0)
1079 u = idx;
1080 else if (comparison > 0)
1081 l = idx + 1;
1082 else
1083 return (gpointer) p;
1086 return NULL;
1089 static gint tag_search_cmp(gconstpointer ptr1, gconstpointer ptr2, gpointer user_data)
1091 gint res = tm_tag_compare(ptr1, ptr2, user_data);
1093 if (res == 0)
1095 TMSortOptions *sort_options = user_data;
1096 const GPtrArray *tags_array = sort_options->tags_array;
1097 TMTag **tag = (TMTag **) ptr2;
1099 /* if previous/next (depending on sort options) tag equal, we haven't
1100 * found the first/last tag in a sequence of equal tags yet */
1101 if (sort_options->first && ptr2 != &tags_array->pdata[0]) {
1102 if (tm_tag_compare(ptr1, tag - 1, user_data) == 0)
1103 return -1;
1105 else if (!sort_options->first && ptr2 != &tags_array->pdata[tags_array->len-1])
1107 if (tm_tag_compare(ptr1, tag + 1, user_data) == 0)
1108 return 1;
1111 return res;
1115 Returns a pointer to the position of the first matching tag in a (sorted) tags array.
1116 The passed array of tags must be already sorted by name (searched with binary search).
1117 @param tags_array Tag array (sorted on name)
1118 @param name Name of the tag to locate.
1119 @param partial If TRUE, matches the first part of the name instead of doing exact match.
1120 @param tagCount Return location of the matched tags.
1122 TMTag **tm_tags_find(const GPtrArray *tags_array, const char *name,
1123 gboolean partial, guint *tagCount)
1125 TMTag *tag, **first;
1126 TMSortOptions sort_options;
1128 *tagCount = 0;
1129 if (!tags_array || !tags_array->len)
1130 return NULL;
1132 tag = g_new0(TMTag, 1);
1133 tag->name = (char *) name;
1135 sort_options.sort_attrs = NULL;
1136 sort_options.partial = partial;
1137 sort_options.tags_array = tags_array;
1138 sort_options.first = TRUE;
1139 first = (TMTag **)binary_search(&tag, tags_array->pdata, tags_array->len,
1140 tag_search_cmp, &sort_options);
1142 if (first)
1144 TMTag **last;
1145 unsigned first_pos;
1147 sort_options.first = FALSE;
1148 first_pos = first - (TMTag **)tags_array->pdata;
1149 /* search between the first element and end */
1150 last = (TMTag **)binary_search(&tag, first, tags_array->len - first_pos,
1151 tag_search_cmp, &sort_options);
1152 *tagCount = last - first + 1;
1155 g_free(tag);
1156 return (TMTag **) first;
1159 /* Returns TMTag which "own" given line
1160 @param line Current line in edited file.
1161 @param file_tags A GPtrArray of edited file TMTag pointers.
1162 @param tag_types the tag types to include in the match
1163 @return TMTag pointers to owner tag. */
1164 const TMTag *
1165 tm_get_current_tag (GPtrArray * file_tags, const gulong line, const TMTagType tag_types)
1167 TMTag *matching_tag = NULL;
1168 if (file_tags && file_tags->len)
1170 guint i;
1171 gulong matching_line = 0;
1173 for (i = 0; (i < file_tags->len); ++i)
1175 TMTag *tag = TM_TAG (file_tags->pdata[i]);
1176 if (tag && tag->type & tag_types &&
1177 tag->line <= line && tag->line > matching_line)
1179 matching_tag = tag;
1180 matching_line = tag->line;
1184 return matching_tag;
1187 const gchar *tm_tag_context_separator(langType lang)
1189 switch (lang)
1191 case TM_PARSER_C: /* for C++ .h headers or C structs */
1192 case TM_PARSER_CPP:
1193 case TM_PARSER_GLSL: /* for structs */
1194 /*case GEANY_FILETYPES_RUBY:*/ /* not sure what to use atm*/
1195 case TM_PARSER_PHP:
1196 case TM_PARSER_POWERSHELL:
1197 case TM_PARSER_RUST:
1198 case TM_PARSER_ZEPHIR:
1199 return "::";
1201 /* avoid confusion with other possible separators in group/section name */
1202 case TM_PARSER_CONF:
1203 case TM_PARSER_REST:
1204 return ":::";
1206 /* no context separator */
1207 case TM_PARSER_ASCIIDOC:
1208 case TM_PARSER_TXT2TAGS:
1209 return "\x03";
1211 default:
1212 return ".";
1216 gboolean tm_tag_is_anon(const TMTag *tag)
1218 guint i;
1219 char dummy;
1221 if (tag->lang == TM_PARSER_C || tag->lang == TM_PARSER_CPP)
1222 return sscanf(tag->name, "anon_%*[a-z]_%u%c", &i, &dummy) == 1;
1223 else if (tag->lang == TM_PARSER_FORTRAN || tag->lang == TM_PARSER_F77)
1224 return sscanf(tag->name, "Structure#%u%c", &i, &dummy) == 1 ||
1225 sscanf(tag->name, "Interface#%u%c", &i, &dummy) == 1 ||
1226 sscanf(tag->name, "Enum#%u%c", &i, &dummy) == 1;
1227 return FALSE;
1231 #ifdef TM_DEBUG /* various debugging functions */
1234 Returns the type of tag as a string
1235 @param tag The tag whose type is required
1237 const char *tm_tag_type_name(const TMTag *tag)
1239 g_return_val_if_fail(tag, NULL);
1240 switch(tag->type)
1242 case tm_tag_class_t: return "class";
1243 case tm_tag_enum_t: return "enum";
1244 case tm_tag_enumerator_t: return "enumval";
1245 case tm_tag_field_t: return "field";
1246 case tm_tag_function_t: return "function";
1247 case tm_tag_interface_t: return "interface";
1248 case tm_tag_member_t: return "member";
1249 case tm_tag_method_t: return "method";
1250 case tm_tag_namespace_t: return "namespace";
1251 case tm_tag_package_t: return "package";
1252 case tm_tag_prototype_t: return "prototype";
1253 case tm_tag_struct_t: return "struct";
1254 case tm_tag_typedef_t: return "typedef";
1255 case tm_tag_union_t: return "union";
1256 case tm_tag_variable_t: return "variable";
1257 case tm_tag_externvar_t: return "extern";
1258 case tm_tag_macro_t: return "define";
1259 case tm_tag_macro_with_arg_t: return "macro";
1260 default: return NULL;
1262 return NULL;
1266 Returns the TMTagType given the name of the type. Reverse of tm_tag_type_name.
1267 @param tag_name Name of the tag type
1269 TMTagType tm_tag_name_type(const char* tag_name)
1271 g_return_val_if_fail(tag_name, tm_tag_undef_t);
1273 if (strcmp(tag_name, "class") == 0) return tm_tag_class_t;
1274 else if (strcmp(tag_name, "enum") == 0) return tm_tag_enum_t;
1275 else if (strcmp(tag_name, "enumval") == 0) return tm_tag_enumerator_t;
1276 else if (strcmp(tag_name, "field") == 0) return tm_tag_field_t;
1277 else if (strcmp(tag_name, "function") == 0) return tm_tag_function_t;
1278 else if (strcmp(tag_name, "interface") == 0) return tm_tag_interface_t;
1279 else if (strcmp(tag_name, "member") == 0) return tm_tag_member_t;
1280 else if (strcmp(tag_name, "method") == 0) return tm_tag_method_t;
1281 else if (strcmp(tag_name, "namespace") == 0) return tm_tag_namespace_t;
1282 else if (strcmp(tag_name, "package") == 0) return tm_tag_package_t;
1283 else if (strcmp(tag_name, "prototype") == 0) return tm_tag_prototype_t;
1284 else if (strcmp(tag_name, "struct") == 0) return tm_tag_struct_t;
1285 else if (strcmp(tag_name, "typedef") == 0) return tm_tag_typedef_t;
1286 else if (strcmp(tag_name, "union") == 0) return tm_tag_union_t;
1287 else if (strcmp(tag_name, "variable") == 0) return tm_tag_variable_t;
1288 else if (strcmp(tag_name, "extern") == 0) return tm_tag_externvar_t;
1289 else if (strcmp(tag_name, "define") == 0) return tm_tag_macro_t;
1290 else if (strcmp(tag_name, "macro") == 0) return tm_tag_macro_with_arg_t;
1291 else return tm_tag_undef_t;
1294 static const char *tm_tag_impl_name(TMTag *tag)
1296 g_return_val_if_fail(tag, NULL);
1297 if (TAG_IMPL_VIRTUAL == tag->impl)
1298 return "virtual";
1299 else
1300 return NULL;
1303 static const char *tm_tag_access_name(TMTag *tag)
1305 g_return_val_if_fail(tag, NULL);
1306 if (TAG_ACCESS_PUBLIC == tag->access)
1307 return "public";
1308 else if (TAG_ACCESS_PROTECTED == tag->access)
1309 return "protected";
1310 else if (TAG_ACCESS_PRIVATE == tag->access)
1311 return "private";
1312 else
1313 return NULL;
1317 Prints information about a tag to the given file pointer.
1318 @param tag The tag whose info is required.
1319 @param fp The file pointer of teh file to print the info to.
1321 void tm_tag_print(TMTag *tag, FILE *fp)
1323 const char *laccess, *impl, *type;
1324 if (!tag || !fp)
1325 return;
1326 laccess = tm_tag_access_name(tag);
1327 impl = tm_tag_impl_name(tag);
1328 type = tm_tag_type_name(tag);
1329 if (laccess)
1330 fprintf(fp, "%s ", laccess);
1331 if (impl)
1332 fprintf(fp, "%s ", impl);
1333 if (type)
1334 fprintf(fp, "%s ", type);
1335 if (tag->var_type)
1336 fprintf(fp, "%s ", tag->var_type);
1337 if (tag->scope)
1338 fprintf(fp, "%s::", tag->scope);
1339 fprintf(fp, "%s", tag->name);
1340 if (tag->arglist)
1341 fprintf(fp, "%s", tag->arglist);
1342 if (tag->inheritance)
1343 fprintf(fp, " : from %s", tag->inheritance);
1344 if ((tag->file) && (tag->line > 0))
1345 fprintf(fp, "[%s:%ld]", tag->file->file_name
1346 , tag->line);
1347 fprintf(fp, "\n");
1351 Prints info about all tags in the array to the given file pointer.
1353 void tm_tags_array_print(GPtrArray *tags, FILE *fp)
1355 guint i;
1356 TMTag *tag;
1357 if (!(tags && (tags->len > 0) && fp))
1358 return;
1359 for (i = 0; i < tags->len; ++i)
1361 tag = TM_TAG(tags->pdata[i]);
1362 tm_tag_print(tag, fp);
1367 Returns the depth of tag scope (useful for finding tag hierarchy
1369 gint tm_tag_scope_depth(const TMTag *t)
1371 const gchar *context_sep = tm_tag_context_separator(t->lang);
1372 gint depth;
1373 char *s;
1374 if(!(t && t->scope))
1375 return 0;
1376 for (s = t->scope, depth = 0; s; s = strstr(s, context_sep))
1378 ++ depth;
1379 ++ s;
1381 return depth;
1384 #endif /* TM_DEBUG */