Improve API docs related to keybindings configuration file
[geany-mirror.git] / tagmanager / src / tm_tag.c
blobff559ee1e587e766a279474847fa987003a93f3a
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 (0 != tag_entry->extensionFields.scope[1][0]))
251 tag->scope = g_strdup(tag_entry->extensionFields.scope[1]);
252 if (tag_entry->extensionFields.inheritance != NULL)
253 tag->inheritance = g_strdup(tag_entry->extensionFields.inheritance);
254 if (tag_entry->extensionFields.varType != NULL)
255 tag->var_type = g_strdup(tag_entry->extensionFields.varType);
256 if (tag_entry->extensionFields.access != NULL)
257 tag->access = get_tag_access(tag_entry->extensionFields.access);
258 if (tag_entry->extensionFields.implementation != NULL)
259 tag->impl = get_tag_impl(tag_entry->extensionFields.implementation);
260 if ((tm_tag_macro_t == tag->type) && (NULL != tag->arglist))
261 tag->type = tm_tag_macro_with_arg_t;
262 tag->file = file;
263 tag->lang = file->lang;
264 return TRUE;
268 Creates a new tag structure from a tagEntryInfo pointer and a TMSOurceFile pointer
269 and returns a pointer to it.
270 @param file - Pointer to the TMSourceFile structure containing the tag
271 @param tag_entry Contains tag information generated by ctags
272 @return the new TMTag structure. This should be free()-ed using tm_tag_free()
274 TMTag *tm_tag_new(TMSourceFile *file, const tagEntryInfo *tag_entry)
276 TMTag *tag;
278 TAG_NEW(tag);
279 if (FALSE == tm_tag_init(tag, file, tag_entry))
281 TAG_FREE(tag);
282 return NULL;
284 return tag;
288 Initializes an already malloc()ed TMTag structure by reading a tag entry
289 line from a file. The structure should be allocated beforehand.
290 @param tag The TMTag structure to populate
291 @param file The TMSourceFile struct (assigned to the file member)
292 @param fp FILE pointer from where the tag line is read
293 @return TRUE on success, FALSE on FAILURE
295 static gboolean tm_tag_init_from_file(TMTag *tag, TMSourceFile *file, FILE *fp)
297 guchar buf[BUFSIZ];
298 guchar *start, *end;
299 gboolean status;
300 guchar changed_char = TA_NAME;
302 tag->refcount = 1;
303 if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf))
304 return FALSE;
305 for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end)
307 while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n'))
308 ++ end;
309 if (('\0' == *end) || ('\n' == *end))
310 status = FALSE;
311 changed_char = *end;
312 *end = '\0';
313 if (NULL == tag->name)
315 if (!isprint(*start))
316 return FALSE;
317 else
318 tag->name = g_strdup((gchar*)start);
320 else
322 switch (*start)
324 case TA_LINE:
325 tag->line = atol((gchar*)start + 1);
326 break;
327 case TA_LOCAL:
328 tag->local = atoi((gchar*)start + 1);
329 break;
330 case TA_TYPE:
331 tag->type = (TMTagType) atoi((gchar*)start + 1);
332 break;
333 case TA_ARGLIST:
334 tag->arglist = g_strdup((gchar*)start + 1);
335 break;
336 case TA_SCOPE:
337 tag->scope = g_strdup((gchar*)start + 1);
338 break;
339 case TA_POINTER:
340 tag->pointerOrder = atoi((gchar*)start + 1);
341 break;
342 case TA_VARTYPE:
343 tag->var_type = g_strdup((gchar*)start + 1);
344 break;
345 case TA_INHERITS:
346 tag->inheritance = g_strdup((gchar*)start + 1);
347 break;
348 case TA_TIME: /* Obsolete */
349 break;
350 case TA_LANG: /* Obsolete */
351 break;
352 case TA_INACTIVE: /* Obsolete */
353 break;
354 case TA_ACCESS:
355 tag->access = (char) *(start + 1);
356 break;
357 case TA_IMPL:
358 tag->impl = (char) *(start + 1);
359 break;
360 default:
361 #ifdef GEANY_DEBUG
362 g_warning("Unknown attribute %s", start + 1);
363 #endif
364 break;
367 *end = changed_char;
369 if (NULL == tag->name)
370 return FALSE;
371 tag->file = file;
372 return TRUE;
375 /* alternative parser for Pascal and LaTeX global tags files with the following format
376 * tagname|return value|arglist|description\n */
377 static gboolean tm_tag_init_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp)
379 guchar buf[BUFSIZ];
380 guchar *start, *end;
381 gboolean status;
382 /*guchar changed_char = TA_NAME;*/
384 tag->refcount = 1;
385 if ((NULL == fgets((gchar*)buf, BUFSIZ, fp)) || ('\0' == *buf))
386 return FALSE;
388 gchar **fields;
389 guint field_len;
390 for (start = end = buf, status = TRUE; (TRUE == status); start = end, ++ end)
392 while ((*end < TA_NAME) && (*end != '\0') && (*end != '\n'))
393 ++ end;
394 if (('\0' == *end) || ('\n' == *end))
395 status = FALSE;
396 /*changed_char = *end;*/
397 *end = '\0';
398 if (NULL == tag->name && !isprint(*start))
399 return FALSE;
401 fields = g_strsplit((gchar*)start, "|", -1);
402 field_len = g_strv_length(fields);
404 if (field_len >= 1) tag->name = g_strdup(fields[0]);
405 else tag->name = NULL;
406 if (field_len >= 2 && fields[1] != NULL) tag->var_type = g_strdup(fields[1]);
407 if (field_len >= 3 && fields[2] != NULL) tag->arglist = g_strdup(fields[2]);
408 tag->type = tm_tag_prototype_t;
409 g_strfreev(fields);
413 if (NULL == tag->name)
414 return FALSE;
415 tag->file = file;
416 return TRUE;
420 Same as tm_tag_init_from_file(), but parsing CTags tag file format
421 (http://ctags.sourceforge.net/FORMAT)
423 static gboolean tm_tag_init_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp)
425 gchar buf[BUFSIZ];
426 gchar *p, *tab;
428 tag->refcount = 1;
429 tag->type = tm_tag_function_t; /* default type is function if no kind is specified */
432 if ((NULL == fgets(buf, BUFSIZ, fp)) || ('\0' == *buf))
433 return FALSE;
435 while (strncmp(buf, "!_TAG_", 6) == 0); /* skip !_TAG_ lines */
437 p = buf;
439 /* tag name */
440 if (! (tab = strchr(p, '\t')) || p == tab)
441 return FALSE;
442 tag->name = g_strndup(p, (gsize)(tab - p));
443 p = tab + 1;
445 /* tagfile, unused */
446 if (! (tab = strchr(p, '\t')))
448 g_free(tag->name);
449 tag->name = NULL;
450 return FALSE;
452 p = tab + 1;
453 /* Ex command, unused */
454 if (*p == '/' || *p == '?')
456 gchar c = *p;
457 for (++p; *p && *p != c; p++)
459 if (*p == '\\' && p[1])
460 p++;
463 else /* assume a line */
464 tag->line = atol(p);
465 tab = strstr(p, ";\"");
466 /* read extension fields */
467 if (tab)
469 p = tab + 2;
470 while (*p && *p != '\n' && *p != '\r')
472 gchar *end;
473 const gchar *key, *value = NULL;
475 /* skip leading tabulations */
476 while (*p && *p == '\t') p++;
477 /* find the separator (:) and end (\t) */
478 key = end = p;
479 while (*end && *end != '\t' && *end != '\n' && *end != '\r')
481 if (*end == ':' && ! value)
483 *end = 0; /* terminate the key */
484 value = end + 1;
486 end++;
488 /* move p paste the so we won't stop parsing by setting *end=0 below */
489 p = *end ? end + 1 : end;
490 *end = 0; /* terminate the value (or key if no value) */
492 if (! value || 0 == strcmp(key, "kind")) /* tag kind */
494 const gchar *kind = value ? value : key;
496 if (kind[0] && kind[1])
497 tag->type = get_tag_type(kind);
498 else
500 switch (*kind)
502 case 'c': tag->type = tm_tag_class_t; break;
503 case 'd': tag->type = tm_tag_macro_t; break;
504 case 'e': tag->type = tm_tag_enumerator_t; break;
505 case 'F': tag->type = tm_tag_other_t; break; /* Obsolete */
506 case 'f': tag->type = tm_tag_function_t; break;
507 case 'g': tag->type = tm_tag_enum_t; break;
508 case 'I': tag->type = tm_tag_class_t; break;
509 case 'i': tag->type = tm_tag_interface_t; break;
510 case 'l': tag->type = tm_tag_variable_t; break;
511 case 'M': tag->type = tm_tag_macro_t; break;
512 case 'm': tag->type = tm_tag_member_t; break;
513 case 'n': tag->type = tm_tag_namespace_t; break;
514 case 'P': tag->type = tm_tag_package_t; break;
515 case 'p': tag->type = tm_tag_prototype_t; break;
516 case 's': tag->type = tm_tag_struct_t; break;
517 case 't': tag->type = tm_tag_typedef_t; break;
518 case 'u': tag->type = tm_tag_union_t; break;
519 case 'v': tag->type = tm_tag_variable_t; break;
520 case 'x': tag->type = tm_tag_externvar_t; break;
521 default:
522 #ifdef TM_DEBUG
523 g_warning("Unknown tag kind %c", *kind);
524 #endif
525 tag->type = tm_tag_other_t; break;
529 else if (0 == strcmp(key, "inherits")) /* comma-separated list of classes this class inherits from */
531 g_free(tag->inheritance);
532 tag->inheritance = g_strdup(value);
534 else if (0 == strcmp(key, "implementation")) /* implementation limit */
535 tag->impl = get_tag_impl(value);
536 else if (0 == strcmp(key, "line")) /* line */
537 tag->line = atol(value);
538 else if (0 == strcmp(key, "access")) /* access */
539 tag->access = get_tag_access(value);
540 else if (0 == strcmp(key, "class") ||
541 0 == strcmp(key, "enum") ||
542 0 == strcmp(key, "function") ||
543 0 == strcmp(key, "struct") ||
544 0 == strcmp(key, "union")) /* Name of the class/enum/function/struct/union in which this tag is a member */
546 g_free(tag->scope);
547 tag->scope = g_strdup(value);
549 else if (0 == strcmp(key, "file")) /* static (local) tag */
550 tag->local = TRUE;
551 else if (0 == strcmp(key, "signature")) /* arglist */
553 g_free(tag->arglist);
554 tag->arglist = g_strdup(value);
559 tag->file = file;
560 return TRUE;
564 Same as tm_tag_new() except that the tag attributes are read from file.
565 @param mode langType to use for the tag.
567 TMTag *tm_tag_new_from_file(TMSourceFile *file, FILE *fp, gint mode, TMFileFormat format)
569 TMTag *tag;
570 gboolean result = FALSE;
572 TAG_NEW(tag);
574 switch (format)
576 case TM_FILE_FORMAT_TAGMANAGER:
577 result = tm_tag_init_from_file(tag, file, fp);
578 break;
579 case TM_FILE_FORMAT_PIPE:
580 result = tm_tag_init_from_file_alt(tag, file, fp);
581 break;
582 case TM_FILE_FORMAT_CTAGS:
583 result = tm_tag_init_from_file_ctags(tag, file, fp);
584 break;
587 if (! result)
589 TAG_FREE(tag);
590 return NULL;
592 tag->lang = mode;
593 return tag;
597 Writes tag information to the given FILE *.
598 @param tag The tag information to write.
599 @param file FILE pointer to which the tag information is written.
600 @param attrs Attributes to be written (bitmask).
601 @return TRUE on success, FALSE on failure.
603 gboolean tm_tag_write(TMTag *tag, FILE *fp, TMTagAttrType attrs)
605 fprintf(fp, "%s", tag->name);
606 if (attrs & tm_tag_attr_type_t)
607 fprintf(fp, "%c%d", TA_TYPE, tag->type);
608 if ((attrs & tm_tag_attr_arglist_t) && (NULL != tag->arglist))
609 fprintf(fp, "%c%s", TA_ARGLIST, tag->arglist);
610 if (attrs & tm_tag_attr_line_t)
611 fprintf(fp, "%c%ld", TA_LINE, tag->line);
612 if (attrs & tm_tag_attr_local_t)
613 fprintf(fp, "%c%d", TA_LOCAL, tag->local);
614 if ((attrs & tm_tag_attr_scope_t) && (NULL != tag->scope))
615 fprintf(fp, "%c%s", TA_SCOPE, tag->scope);
616 if ((attrs & tm_tag_attr_inheritance_t) && (NULL != tag->inheritance))
617 fprintf(fp, "%c%s", TA_INHERITS, tag->inheritance);
618 if (attrs & tm_tag_attr_pointer_t)
619 fprintf(fp, "%c%d", TA_POINTER, tag->pointerOrder);
620 if ((attrs & tm_tag_attr_vartype_t) && (NULL != tag->var_type))
621 fprintf(fp, "%c%s", TA_VARTYPE, tag->var_type);
622 if ((attrs & tm_tag_attr_access_t) && (TAG_ACCESS_UNKNOWN != tag->access))
623 fprintf(fp, "%c%c", TA_ACCESS, tag->access);
624 if ((attrs & tm_tag_attr_impl_t) && (TAG_IMPL_UNKNOWN != tag->impl))
625 fprintf(fp, "%c%c", TA_IMPL, tag->impl);
627 if (fprintf(fp, "\n"))
628 return TRUE;
629 else
630 return FALSE;
634 Destroys a TMTag structure, i.e. frees all elements except the tag itself.
635 @param tag The TMTag structure to destroy
636 @see tm_tag_free()
638 static void tm_tag_destroy(TMTag *tag)
640 g_free(tag->name);
641 g_free(tag->arglist);
642 g_free(tag->scope);
643 g_free(tag->inheritance);
644 g_free(tag->var_type);
649 Drops a reference from a TMTag. If the reference count reaches 0, this function
650 destroys all data in the tag and frees the tag structure as well.
651 @param tag Pointer to a TMTag structure
653 void tm_tag_unref(TMTag *tag)
655 /* be NULL-proof because tm_tag_free() was NULL-proof and we indent to be a
656 * drop-in replacment of it */
657 if (NULL != tag && g_atomic_int_dec_and_test(&tag->refcount))
659 tm_tag_destroy(tag);
660 TAG_FREE(tag);
665 Adds a reference to a TMTag.
666 @param tag Pointer to a TMTag structure
667 @return the passed-in TMTag
669 TMTag *tm_tag_ref(TMTag *tag)
671 g_atomic_int_inc(&tag->refcount);
672 return tag;
676 Inbuilt tag comparison function.
678 static gint tm_tag_compare(gconstpointer ptr1, gconstpointer ptr2, gpointer user_data)
680 unsigned int *sort_attr;
681 int returnval = 0;
682 TMTag *t1 = *((TMTag **) ptr1);
683 TMTag *t2 = *((TMTag **) ptr2);
684 TMSortOptions *sort_options = user_data;
686 if ((NULL == t1) || (NULL == t2))
688 g_warning("Found NULL tag");
689 return t2 - t1;
691 if (NULL == sort_options->sort_attrs)
693 if (sort_options->partial)
694 return strncmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""), strlen(FALLBACK(t1->name, "")));
695 else
696 return strcmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""));
699 for (sort_attr = sort_options->sort_attrs; returnval == 0 && *sort_attr != tm_tag_attr_none_t; ++ sort_attr)
701 switch (*sort_attr)
703 case tm_tag_attr_name_t:
704 if (sort_options->partial)
705 returnval = strncmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""), strlen(FALLBACK(t1->name, "")));
706 else
707 returnval = strcmp(FALLBACK(t1->name, ""), FALLBACK(t2->name, ""));
708 break;
709 case tm_tag_attr_file_t:
710 returnval = t1->file - t2->file;
711 break;
712 case tm_tag_attr_line_t:
713 returnval = t1->line - t2->line;
714 break;
715 case tm_tag_attr_type_t:
716 returnval = t1->type - t2->type;
717 break;
718 case tm_tag_attr_scope_t:
719 returnval = strcmp(FALLBACK(t1->scope, ""), FALLBACK(t2->scope, ""));
720 break;
721 case tm_tag_attr_arglist_t:
722 returnval = strcmp(FALLBACK(t1->arglist, ""), FALLBACK(t2->arglist, ""));
723 if (returnval != 0)
725 int line_diff = (t1->line - t2->line);
727 returnval = line_diff ? line_diff : returnval;
729 break;
730 case tm_tag_attr_vartype_t:
731 returnval = strcmp(FALLBACK(t1->var_type, ""), FALLBACK(t2->var_type, ""));
732 break;
735 return returnval;
738 gboolean tm_tags_equal(const TMTag *a, const TMTag *b)
740 if (a == b)
741 return TRUE;
743 return (a->line == b->line &&
744 a->file == b->file /* ptr comparison */ &&
745 strcmp(FALLBACK(a->name, ""), FALLBACK(b->name, "")) == 0 &&
746 a->type == b->type &&
747 a->local == b->local &&
748 a->pointerOrder == b->pointerOrder &&
749 a->access == b->access &&
750 a->impl == b->impl &&
751 a->lang == b->lang &&
752 strcmp(FALLBACK(a->scope, ""), FALLBACK(b->scope, "")) == 0 &&
753 strcmp(FALLBACK(a->arglist, ""), FALLBACK(b->arglist, "")) == 0 &&
754 strcmp(FALLBACK(a->inheritance, ""), FALLBACK(b->inheritance, "")) == 0 &&
755 strcmp(FALLBACK(a->var_type, ""), FALLBACK(b->var_type, "")) == 0);
759 Removes NULL tag entries from an array of tags. Called after tm_tags_dedup() since
760 this function substitutes duplicate entries with NULL
761 @param tags_array Array of tags to dedup
762 @return TRUE on success, FALSE on failure
764 gboolean tm_tags_prune(GPtrArray *tags_array)
766 guint i, count;
767 for (i=0, count = 0; i < tags_array->len; ++i)
769 if (NULL != tags_array->pdata[i])
770 tags_array->pdata[count++] = tags_array->pdata[i];
772 tags_array->len = count;
773 return TRUE;
777 Deduplicates an array on tags using the inbuilt comparison function based on
778 the attributes specified. Called by tm_tags_sort() when dedup is TRUE.
779 @param tags_array Array of tags to dedup.
780 @param sort_attributes Attributes the array is sorted on. They will be deduped
781 on the same criteria.
782 @return TRUE on success, FALSE on failure
784 gboolean tm_tags_dedup(GPtrArray *tags_array, TMTagAttrType *sort_attributes, gboolean unref_duplicates)
786 TMSortOptions sort_options;
787 guint i;
789 if ((!tags_array) || (!tags_array->len))
790 return TRUE;
791 sort_options.sort_attrs = sort_attributes;
792 sort_options.partial = FALSE;
793 for (i = 1; i < tags_array->len; ++i)
795 if (0 == tm_tag_compare(&(tags_array->pdata[i - 1]), &(tags_array->pdata[i]), &sort_options))
797 if (unref_duplicates)
798 tm_tag_unref(tags_array->pdata[i-1]);
799 tags_array->pdata[i-1] = NULL;
802 tm_tags_prune(tags_array);
803 return TRUE;
807 Sort an array of tags on the specified attribuites using the inbuilt comparison
808 function.
809 @param tags_array The array of tags to be sorted
810 @param sort_attributes Attributes to be sorted on (int array terminated by 0)
811 @param dedup Whether to deduplicate the sorted array
812 @return TRUE on success, FALSE on failure
814 gboolean tm_tags_sort(GPtrArray *tags_array, TMTagAttrType *sort_attributes,
815 gboolean dedup, gboolean unref_duplicates)
817 TMSortOptions sort_options;
819 if ((!tags_array) || (!tags_array->len))
820 return TRUE;
821 sort_options.sort_attrs = sort_attributes;
822 sort_options.partial = FALSE;
823 g_ptr_array_sort_with_data(tags_array, tm_tag_compare, &sort_options);
824 if (dedup)
825 tm_tags_dedup(tags_array, sort_attributes, unref_duplicates);
826 return TRUE;
829 void tm_tags_remove_file_tags(TMSourceFile *source_file, GPtrArray *tags_array)
831 guint i;
833 /* Now we choose between an algorithm with complexity O(tags_array->len) and
834 * O(source_file->tags_array->len * log(tags_array->len)). The latter algorithm
835 * is better when tags_array contains many times more tags than
836 * source_file->tags_array so instead of trying to find the removed tags
837 * linearly, binary search is used. The constant 20 is more or less random
838 * but seems to work well. It's exact value isn't so critical because it's
839 * the extremes where the difference is the biggest: when
840 * source_file->tags_array->len == tags_array->len (single file open) and
841 * source_file->tags_array->len << tags_array->len (the number of tags
842 * from the file is a small fraction of all tags).
844 if (source_file->tags_array->len != 0 &&
845 tags_array->len / source_file->tags_array->len < 20)
847 for (i = 0; i < tags_array->len; i++)
849 TMTag *tag = tags_array->pdata[i];
851 if (tag->file == source_file)
852 tags_array->pdata[i] = NULL;
855 else
857 GPtrArray *to_delete = g_ptr_array_sized_new(source_file->tags_array->len);
859 for (i = 0; i < source_file->tags_array->len; i++)
861 guint j;
862 guint tag_count;
863 TMTag **found;
864 TMTag *tag = source_file->tags_array->pdata[i];
866 found = tm_tags_find(tags_array, tag->name, FALSE, &tag_count);
868 for (j = 0; j < tag_count; j++)
870 if (*found != NULL && (*found)->file == source_file)
872 /* we cannot set the pointer to NULL now because the search wouldn't work */
873 g_ptr_array_add(to_delete, found);
874 /* no break - if there are multiple tags of the same name, we would
875 * always find the first instance and wouldn't remove others; duplicates
876 * in the to_delete list aren't a problem */
878 found++;
882 for (i = 0; i < to_delete->len; i++)
884 TMTag **tag = to_delete->pdata[i];
885 *tag = NULL;
887 g_ptr_array_free(to_delete, TRUE);
890 tm_tags_prune(tags_array);
893 /* Optimized merge sort for merging sorted values from one array to another
894 * where one of the arrays is much smaller than the other.
895 * The merge complexity depends mostly on the size of the small array
896 * and is almost independent of the size of the big array.
897 * In addition, get rid of the duplicates (if both big_array and small_array are duplicate-free). */
898 static GPtrArray *merge(GPtrArray *big_array, GPtrArray *small_array,
899 TMSortOptions *sort_options, gboolean unref_duplicates) {
900 guint i1 = 0; /* index to big_array */
901 guint i2 = 0; /* index to small_array */
902 guint initial_step;
903 guint step;
904 GPtrArray *res_array = g_ptr_array_sized_new(big_array->len + small_array->len);
905 #ifdef TM_DEBUG
906 guint cmpnum = 0;
907 #endif
909 /* swap the arrays if len(small) > len(big) */
910 if (small_array->len > big_array->len)
912 GPtrArray *tmp = small_array;
913 small_array = big_array;
914 big_array = tmp;
917 /* on average, we are merging a value from small_array every
918 * len(big_array) / len(small_array) values - good approximation for fast jump
919 * step size */
920 initial_step = (small_array->len > 0) ? big_array->len / small_array->len : 1;
921 initial_step = initial_step > 4 ? initial_step : 1;
922 step = initial_step;
924 while (i1 < big_array->len && i2 < small_array->len)
926 gpointer val1;
927 gpointer val2 = small_array->pdata[i2];
929 if (step > 4) /* fast path start */
931 guint j1 = (i1 + step < big_array->len) ? i1 + step : big_array->len - 1;
933 val1 = big_array->pdata[j1];
934 #ifdef TM_DEBUG
935 cmpnum++;
936 #endif
937 /* if the value in big_array after making the big step is still smaller
938 * than the value in small_array, we can copy all the values inbetween
939 * into the result without making expensive string comparisons */
940 if (tm_tag_compare(&val1, &val2, sort_options) < 0)
942 while (i1 <= j1)
944 val1 = big_array->pdata[i1];
945 g_ptr_array_add(res_array, val1);
946 i1++;
949 else
951 /* lower the step and try again */
952 step /= 2;
954 } /* fast path end */
955 else
957 gint cmpval;
959 #ifdef TM_DEBUG
960 cmpnum++;
961 #endif
962 val1 = big_array->pdata[i1];
963 cmpval = tm_tag_compare(&val1, &val2, sort_options);
964 if (cmpval < 0)
966 g_ptr_array_add(res_array, val1);
967 i1++;
969 else
971 g_ptr_array_add(res_array, val2);
972 i2++;
973 /* value from small_array gets merged - reset the step size */
974 step = initial_step;
975 if (cmpval == 0)
977 i1++; /* remove the duplicate, keep just the newly merged value */
978 if (unref_duplicates)
979 tm_tag_unref(val1);
985 /* end of one of the arrays reached - copy the rest from the other array */
986 while (i1 < big_array->len)
987 g_ptr_array_add(res_array, big_array->pdata[i1++]);
988 while (i2 < small_array->len)
989 g_ptr_array_add(res_array, small_array->pdata[i2++]);
991 #ifdef TM_DEBUG
992 printf("cmpnums: %u\n", cmpnum);
993 printf("total tags: %u\n", big_array->len);
994 printf("merged tags: %u\n\n", small_array->len);
995 #endif
997 return res_array;
1000 GPtrArray *tm_tags_merge(GPtrArray *big_array, GPtrArray *small_array,
1001 TMTagAttrType *sort_attributes, gboolean unref_duplicates)
1003 GPtrArray *res_array;
1004 TMSortOptions sort_options;
1006 sort_options.sort_attrs = sort_attributes;
1007 sort_options.partial = FALSE;
1008 res_array = merge(big_array, small_array, &sort_options, unref_duplicates);
1009 return res_array;
1013 This function will extract the tags of the specified types from an array of tags.
1014 The returned value is a GPtrArray which should be free-d with a call to
1015 g_ptr_array_free(array, TRUE). However, do not free the tags themselves since they
1016 are not duplicated.
1017 @param tags_array The original array of tags
1018 @param tag_types - The tag types to extract. Can be a bitmask. For example, passing
1019 (tm_tag_typedef_t | tm_tag_struct_t) will extract all typedefs and structures from
1020 the original array.
1021 @return an array of tags (NULL on failure)
1023 GPtrArray *tm_tags_extract(GPtrArray *tags_array, TMTagType tag_types)
1025 GPtrArray *new_tags;
1026 guint i;
1027 if (NULL == tags_array)
1028 return NULL;
1029 new_tags = g_ptr_array_new();
1030 for (i=0; i < tags_array->len; ++i)
1032 if (NULL != tags_array->pdata[i])
1034 if (tag_types & (((TMTag *) tags_array->pdata[i])->type))
1035 g_ptr_array_add(new_tags, tags_array->pdata[i]);
1038 return new_tags;
1042 Completely frees an array of tags.
1043 @param tags_array Array of tags to be freed.
1044 @param free_array Whether the GptrArray is to be freed as well.
1046 void tm_tags_array_free(GPtrArray *tags_array, gboolean free_all)
1048 if (tags_array)
1050 guint i;
1051 for (i = 0; i < tags_array->len; ++i)
1052 tm_tag_unref(tags_array->pdata[i]);
1053 if (free_all)
1054 g_ptr_array_free(tags_array, TRUE);
1055 else
1056 g_ptr_array_set_size(tags_array, 0);
1060 /* copy/pasted bsearch() from libc extended with user_data for comparison function
1061 * and using glib types */
1062 static gpointer binary_search(gpointer key, gpointer base, size_t nmemb,
1063 GCompareDataFunc compar, gpointer user_data)
1065 gsize l, u, idx;
1066 gpointer p;
1067 gint comparison;
1069 l = 0;
1070 u = nmemb;
1071 while (l < u)
1073 idx = (l + u) / 2;
1074 p = (gpointer) (((const gchar *) base) + (idx * sizeof(gpointer)));
1075 comparison = (*compar) (key, p, user_data);
1076 if (comparison < 0)
1077 u = idx;
1078 else if (comparison > 0)
1079 l = idx + 1;
1080 else
1081 return (gpointer) p;
1084 return NULL;
1087 static gint tag_search_cmp(gconstpointer ptr1, gconstpointer ptr2, gpointer user_data)
1089 gint res = tm_tag_compare(ptr1, ptr2, user_data);
1091 if (res == 0)
1093 TMSortOptions *sort_options = user_data;
1094 const GPtrArray *tags_array = sort_options->tags_array;
1095 TMTag **tag = (TMTag **) ptr2;
1097 /* if previous/next (depending on sort options) tag equal, we haven't
1098 * found the first/last tag in a sequence of equal tags yet */
1099 if (sort_options->first && ptr2 != &tags_array->pdata[0]) {
1100 if (tm_tag_compare(ptr1, tag - 1, user_data) == 0)
1101 return -1;
1103 else if (!sort_options->first && ptr2 != &tags_array->pdata[tags_array->len-1])
1105 if (tm_tag_compare(ptr1, tag + 1, user_data) == 0)
1106 return 1;
1109 return res;
1113 Returns a pointer to the position of the first matching tag in a (sorted) tags array.
1114 The passed array of tags must be already sorted by name (searched with binary search).
1115 @param tags_array Tag array (sorted on name)
1116 @param name Name of the tag to locate.
1117 @param partial If TRUE, matches the first part of the name instead of doing exact match.
1118 @param tagCount Return location of the matched tags.
1120 TMTag **tm_tags_find(const GPtrArray *tags_array, const char *name,
1121 gboolean partial, guint *tagCount)
1123 TMTag *tag, **first;
1124 TMSortOptions sort_options;
1126 *tagCount = 0;
1127 if (!tags_array || !tags_array->len)
1128 return NULL;
1130 tag = g_new0(TMTag, 1);
1131 tag->name = (char *) name;
1133 sort_options.sort_attrs = NULL;
1134 sort_options.partial = partial;
1135 sort_options.tags_array = tags_array;
1136 sort_options.first = TRUE;
1137 first = (TMTag **)binary_search(&tag, tags_array->pdata, tags_array->len,
1138 tag_search_cmp, &sort_options);
1140 if (first)
1142 TMTag **last;
1143 unsigned first_pos;
1145 sort_options.first = FALSE;
1146 first_pos = first - (TMTag **)tags_array->pdata;
1147 /* search between the first element and end */
1148 last = (TMTag **)binary_search(&tag, first, tags_array->len - first_pos,
1149 tag_search_cmp, &sort_options);
1150 *tagCount = last - first + 1;
1153 g_free(tag);
1154 return (TMTag **) first;
1157 /* Returns TMTag which "own" given line
1158 @param line Current line in edited file.
1159 @param file_tags A GPtrArray of edited file TMTag pointers.
1160 @param tag_types the tag types to include in the match
1161 @return TMTag pointers to owner tag. */
1162 const TMTag *
1163 tm_get_current_tag (GPtrArray * file_tags, const gulong line, const TMTagType tag_types)
1165 TMTag *matching_tag = NULL;
1166 if (file_tags && file_tags->len)
1168 guint i;
1169 gulong matching_line = 0;
1171 for (i = 0; (i < file_tags->len); ++i)
1173 TMTag *tag = TM_TAG (file_tags->pdata[i]);
1174 if (tag && tag->type & tag_types &&
1175 tag->line <= line && tag->line > matching_line)
1177 matching_tag = tag;
1178 matching_line = tag->line;
1182 return matching_tag;
1185 const gchar *tm_tag_context_separator(langType lang)
1187 switch (lang)
1189 case TM_PARSER_C: /* for C++ .h headers or C structs */
1190 case TM_PARSER_CPP:
1191 case TM_PARSER_GLSL: /* for structs */
1192 /*case GEANY_FILETYPES_RUBY:*/ /* not sure what to use atm*/
1193 case TM_PARSER_PHP:
1194 case TM_PARSER_POWERSHELL:
1195 case TM_PARSER_RUST:
1196 case TM_PARSER_ZEPHIR:
1197 return "::";
1199 /* avoid confusion with other possible separators in group/section name */
1200 case TM_PARSER_CONF:
1201 case TM_PARSER_REST:
1202 return ":::";
1204 /* no context separator */
1205 case TM_PARSER_ASCIIDOC:
1206 case TM_PARSER_TXT2TAGS:
1207 return "\x03";
1209 default:
1210 return ".";
1214 gboolean tm_tag_is_anon(const TMTag *tag)
1216 guint i;
1217 char dummy;
1219 if (tag->lang == TM_PARSER_C || tag->lang == TM_PARSER_CPP)
1220 return sscanf(tag->name, "anon_%*[a-z]_%u%c", &i, &dummy) == 1;
1221 else if (tag->lang == TM_PARSER_FORTRAN || tag->lang == TM_PARSER_F77)
1222 return sscanf(tag->name, "Structure#%u%c", &i, &dummy) == 1 ||
1223 sscanf(tag->name, "Interface#%u%c", &i, &dummy) == 1 ||
1224 sscanf(tag->name, "Enum#%u%c", &i, &dummy) == 1;
1225 return FALSE;
1229 #ifdef TM_DEBUG /* various debugging functions */
1232 Returns the type of tag as a string
1233 @param tag The tag whose type is required
1235 const char *tm_tag_type_name(const TMTag *tag)
1237 g_return_val_if_fail(tag, NULL);
1238 switch(tag->type)
1240 case tm_tag_class_t: return "class";
1241 case tm_tag_enum_t: return "enum";
1242 case tm_tag_enumerator_t: return "enumval";
1243 case tm_tag_field_t: return "field";
1244 case tm_tag_function_t: return "function";
1245 case tm_tag_interface_t: return "interface";
1246 case tm_tag_member_t: return "member";
1247 case tm_tag_method_t: return "method";
1248 case tm_tag_namespace_t: return "namespace";
1249 case tm_tag_package_t: return "package";
1250 case tm_tag_prototype_t: return "prototype";
1251 case tm_tag_struct_t: return "struct";
1252 case tm_tag_typedef_t: return "typedef";
1253 case tm_tag_union_t: return "union";
1254 case tm_tag_variable_t: return "variable";
1255 case tm_tag_externvar_t: return "extern";
1256 case tm_tag_macro_t: return "define";
1257 case tm_tag_macro_with_arg_t: return "macro";
1258 default: return NULL;
1260 return NULL;
1264 Returns the TMTagType given the name of the type. Reverse of tm_tag_type_name.
1265 @param tag_name Name of the tag type
1267 TMTagType tm_tag_name_type(const char* tag_name)
1269 g_return_val_if_fail(tag_name, tm_tag_undef_t);
1271 if (strcmp(tag_name, "class") == 0) return tm_tag_class_t;
1272 else if (strcmp(tag_name, "enum") == 0) return tm_tag_enum_t;
1273 else if (strcmp(tag_name, "enumval") == 0) return tm_tag_enumerator_t;
1274 else if (strcmp(tag_name, "field") == 0) return tm_tag_field_t;
1275 else if (strcmp(tag_name, "function") == 0) return tm_tag_function_t;
1276 else if (strcmp(tag_name, "interface") == 0) return tm_tag_interface_t;
1277 else if (strcmp(tag_name, "member") == 0) return tm_tag_member_t;
1278 else if (strcmp(tag_name, "method") == 0) return tm_tag_method_t;
1279 else if (strcmp(tag_name, "namespace") == 0) return tm_tag_namespace_t;
1280 else if (strcmp(tag_name, "package") == 0) return tm_tag_package_t;
1281 else if (strcmp(tag_name, "prototype") == 0) return tm_tag_prototype_t;
1282 else if (strcmp(tag_name, "struct") == 0) return tm_tag_struct_t;
1283 else if (strcmp(tag_name, "typedef") == 0) return tm_tag_typedef_t;
1284 else if (strcmp(tag_name, "union") == 0) return tm_tag_union_t;
1285 else if (strcmp(tag_name, "variable") == 0) return tm_tag_variable_t;
1286 else if (strcmp(tag_name, "extern") == 0) return tm_tag_externvar_t;
1287 else if (strcmp(tag_name, "define") == 0) return tm_tag_macro_t;
1288 else if (strcmp(tag_name, "macro") == 0) return tm_tag_macro_with_arg_t;
1289 else return tm_tag_undef_t;
1292 static const char *tm_tag_impl_name(TMTag *tag)
1294 g_return_val_if_fail(tag, NULL);
1295 if (TAG_IMPL_VIRTUAL == tag->impl)
1296 return "virtual";
1297 else
1298 return NULL;
1301 static const char *tm_tag_access_name(TMTag *tag)
1303 g_return_val_if_fail(tag, NULL);
1304 if (TAG_ACCESS_PUBLIC == tag->access)
1305 return "public";
1306 else if (TAG_ACCESS_PROTECTED == tag->access)
1307 return "protected";
1308 else if (TAG_ACCESS_PRIVATE == tag->access)
1309 return "private";
1310 else
1311 return NULL;
1315 Prints information about a tag to the given file pointer.
1316 @param tag The tag whose info is required.
1317 @param fp The file pointer of the file to print the info to.
1319 void tm_tag_print(TMTag *tag, FILE *fp)
1321 const char *laccess, *impl, *type;
1322 if (!tag || !fp)
1323 return;
1324 laccess = tm_tag_access_name(tag);
1325 impl = tm_tag_impl_name(tag);
1326 type = tm_tag_type_name(tag);
1327 if (laccess)
1328 fprintf(fp, "%s ", laccess);
1329 if (impl)
1330 fprintf(fp, "%s ", impl);
1331 if (type)
1332 fprintf(fp, "%s ", type);
1333 if (tag->var_type)
1334 fprintf(fp, "%s ", tag->var_type);
1335 if (tag->scope)
1336 fprintf(fp, "%s::", tag->scope);
1337 fprintf(fp, "%s", tag->name);
1338 if (tag->arglist)
1339 fprintf(fp, "%s", tag->arglist);
1340 if (tag->inheritance)
1341 fprintf(fp, " : from %s", tag->inheritance);
1342 if ((tag->file) && (tag->line > 0))
1343 fprintf(fp, "[%s:%ld]", tag->file->file_name
1344 , tag->line);
1345 fprintf(fp, "\n");
1349 Prints info about all tags in the array to the given file pointer.
1351 void tm_tags_array_print(GPtrArray *tags, FILE *fp)
1353 guint i;
1354 TMTag *tag;
1355 if (!(tags && (tags->len > 0) && fp))
1356 return;
1357 for (i = 0; i < tags->len; ++i)
1359 tag = TM_TAG(tags->pdata[i]);
1360 tm_tag_print(tag, fp);
1365 Returns the depth of tag scope (useful for finding tag hierarchy
1367 gint tm_tag_scope_depth(const TMTag *t)
1369 const gchar *context_sep = tm_tag_context_separator(t->lang);
1370 gint depth;
1371 char *s;
1372 if(!(t && t->scope))
1373 return 0;
1374 for (s = t->scope, depth = 0; s; s = strstr(s, context_sep))
1376 ++ depth;
1377 ++ s;
1379 return depth;
1382 #endif /* TM_DEBUG */