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.
12 #include <glib-object.h>
18 #define LIBCTAGS_DEFINED
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))
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)
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)
54 alive_tags
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
55 atexit(log_refs_at_exit
);
58 g_hash_table_insert(alive_tags
, tag
, 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
);
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 ! */
94 TA_POS
, /* Obsolete */
104 TA_INACTIVE
, /* Obsolete */
112 const GPtrArray
*tags_array
;
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
[] = {
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
);
170 static TMTagType
get_tag_type(const char *tag_name
)
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
]);
179 return s_tag_types
[i
];
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
;
187 fprintf(stderr
, "Unknown tag type %s\n", tag_name
);
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
;
199 g_warning("Unknown implementation %s", impl
);
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
;
218 g_warning("Unknown access type %s", access
);
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
)
236 if (NULL
== tag_entry
)
239 /* This is a normal tag entry */
240 if (NULL
== tag_entry
->name
)
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
;
265 tag
->lang
= file
->lang
;
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
)
281 if (FALSE
== tm_tag_init(tag
, file
, tag_entry
))
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
)
302 guchar changed_char
= TA_NAME
;
305 if ((NULL
== fgets((gchar
*)buf
, BUFSIZ
, fp
)) || ('\0' == *buf
))
307 for (start
= end
= buf
, status
= TRUE
; (TRUE
== status
); start
= end
, ++ end
)
309 while ((*end
< TA_NAME
) && (*end
!= '\0') && (*end
!= '\n'))
311 if (('\0' == *end
) || ('\n' == *end
))
315 if (NULL
== tag
->name
)
317 if (!isprint(*start
))
320 tag
->name
= g_strdup((gchar
*)start
);
327 tag
->line
= atol((gchar
*)start
+ 1);
330 tag
->local
= atoi((gchar
*)start
+ 1);
333 tag
->type
= (TMTagType
) atoi((gchar
*)start
+ 1);
336 tag
->arglist
= g_strdup((gchar
*)start
+ 1);
339 tag
->scope
= g_strdup((gchar
*)start
+ 1);
342 tag
->pointerOrder
= atoi((gchar
*)start
+ 1);
345 tag
->var_type
= g_strdup((gchar
*)start
+ 1);
348 tag
->inheritance
= g_strdup((gchar
*)start
+ 1);
350 case TA_TIME
: /* Obsolete */
352 case TA_LANG
: /* Obsolete */
354 case TA_INACTIVE
: /* Obsolete */
357 tag
->access
= (char) *(start
+ 1);
360 tag
->impl
= (char) *(start
+ 1);
364 g_warning("Unknown attribute %s", start
+ 1);
371 if (NULL
== tag
->name
)
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
)
384 /*guchar changed_char = TA_NAME;*/
387 if ((NULL
== fgets((gchar
*)buf
, BUFSIZ
, fp
)) || ('\0' == *buf
))
392 for (start
= end
= buf
, status
= TRUE
; (TRUE
== status
); start
= end
, ++ end
)
394 while ((*end
< TA_NAME
) && (*end
!= '\0') && (*end
!= '\n'))
396 if (('\0' == *end
) || ('\n' == *end
))
398 /*changed_char = *end;*/
400 if (NULL
== tag
->name
&& !isprint(*start
))
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
;
415 if (NULL
== tag
->name
)
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
)
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
))
437 while (strncmp(buf
, "!_TAG_", 6) == 0); /* skip !_TAG_ lines */
442 if (! (tab
= strchr(p
, '\t')) || p
== tab
)
444 tag
->name
= g_strndup(p
, (gsize
)(tab
- p
));
447 /* tagfile, unused */
448 if (! (tab
= strchr(p
, '\t')))
455 /* Ex command, unused */
456 if (*p
== '/' || *p
== '?')
459 for (++p
; *p
&& *p
!= c
; p
++)
461 if (*p
== '\\' && p
[1])
465 else /* assume a line */
467 tab
= strstr(p
, ";\"");
468 /* read extension fields */
472 while (*p
&& *p
!= '\n' && *p
!= '\r')
475 const gchar
*key
, *value
= NULL
;
477 /* skip leading tabulations */
478 while (*p
&& *p
== '\t') p
++;
479 /* find the separator (:) and end (\t) */
481 while (*end
&& *end
!= '\t' && *end
!= '\n' && *end
!= '\r')
483 if (*end
== ':' && ! value
)
485 *end
= 0; /* terminate the key */
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
);
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;
525 g_warning("Unknown tag kind %c", *kind
);
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 */
549 tag
->scope
= g_strdup(value
);
551 else if (0 == strcmp(key
, "file")) /* static (local) tag */
553 else if (0 == strcmp(key
, "signature")) /* arglist */
555 g_free(tag
->arglist
);
556 tag
->arglist
= g_strdup(value
);
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
)
572 gboolean result
= FALSE
;
578 case TM_FILE_FORMAT_TAGMANAGER
:
579 result
= tm_tag_init_from_file(tag
, file
, fp
);
581 case TM_FILE_FORMAT_PIPE
:
582 result
= tm_tag_init_from_file_alt(tag
, file
, fp
);
584 case TM_FILE_FORMAT_CTAGS
:
585 result
= tm_tag_init_from_file_ctags(tag
, file
, fp
);
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"))
636 Destroys a TMTag structure, i.e. frees all elements except the tag itself.
637 @param tag The TMTag structure to destroy
640 static void tm_tag_destroy(TMTag
*tag
)
643 g_free(tag
->arglist
);
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
))
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
);
678 Inbuilt tag comparison function.
680 static gint
tm_tag_compare(gconstpointer ptr1
, gconstpointer ptr2
, gpointer user_data
)
682 unsigned int *sort_attr
;
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");
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
, "")));
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
)
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
, "")));
709 returnval
= strcmp(FALLBACK(t1
->name
, ""), FALLBACK(t2
->name
, ""));
711 case tm_tag_attr_file_t
:
712 returnval
= t1
->file
- t2
->file
;
714 case tm_tag_attr_line_t
:
715 returnval
= t1
->line
- t2
->line
;
717 case tm_tag_attr_type_t
:
718 returnval
= t1
->type
- t2
->type
;
720 case tm_tag_attr_scope_t
:
721 returnval
= strcmp(FALLBACK(t1
->scope
, ""), FALLBACK(t2
->scope
, ""));
723 case tm_tag_attr_arglist_t
:
724 returnval
= strcmp(FALLBACK(t1
->arglist
, ""), FALLBACK(t2
->arglist
, ""));
727 int line_diff
= (t1
->line
- t2
->line
);
729 returnval
= line_diff
? line_diff
: returnval
;
732 case tm_tag_attr_vartype_t
:
733 returnval
= strcmp(FALLBACK(t1
->var_type
, ""), FALLBACK(t2
->var_type
, ""));
740 gboolean
tm_tags_equal(const TMTag
*a
, const TMTag
*b
)
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
)
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
;
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
;
791 if ((!tags_array
) || (!tags_array
->len
))
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
);
809 Sort an array of tags on the specified attribuites using the inbuilt comparison
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
))
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
);
827 tm_tags_dedup(tags_array
, sort_attributes
, unref_duplicates
);
831 void tm_tags_remove_file_tags(TMSourceFile
*source_file
, GPtrArray
*tags_array
)
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
;
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
++)
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 */
884 for (i
= 0; i
< to_delete
->len
; i
++)
886 TMTag
**tag
= to_delete
->pdata
[i
];
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 */
906 GPtrArray
*res_array
= g_ptr_array_sized_new(big_array
->len
+ small_array
->len
);
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
;
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
922 initial_step
= (small_array
->len
> 0) ? big_array
->len
/ small_array
->len
: 1;
923 initial_step
= initial_step
> 4 ? initial_step
: 1;
926 while (i1
< big_array
->len
&& i2
< small_array
->len
)
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
];
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)
946 val1
= big_array
->pdata
[i1
];
947 g_ptr_array_add(res_array
, val1
);
953 /* lower the step and try again */
956 } /* fast path end */
964 val1
= big_array
->pdata
[i1
];
965 cmpval
= tm_tag_compare(&val1
, &val2
, sort_options
);
968 g_ptr_array_add(res_array
, val1
);
973 g_ptr_array_add(res_array
, val2
);
975 /* value from small_array gets merged - reset the step size */
979 i1
++; /* remove the duplicate, keep just the newly merged value */
980 if (unref_duplicates
)
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
++]);
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
);
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
);
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
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
1023 @return an array of tags (NULL on failure)
1025 GPtrArray
*tm_tags_extract(GPtrArray
*tags_array
, TMTagType tag_types
)
1027 GPtrArray
*new_tags
;
1029 if (NULL
== tags_array
)
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
]);
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
)
1053 for (i
= 0; i
< tags_array
->len
; ++i
)
1054 tm_tag_unref(tags_array
->pdata
[i
]);
1056 g_ptr_array_free(tags_array
, TRUE
);
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
)
1076 p
= (gpointer
) (((const gchar
*) base
) + (idx
* sizeof(gpointer
)));
1077 comparison
= (*compar
) (key
, p
, user_data
);
1080 else if (comparison
> 0)
1083 return (gpointer
) p
;
1089 static gint
tag_search_cmp(gconstpointer ptr1
, gconstpointer ptr2
, gpointer user_data
)
1091 gint res
= tm_tag_compare(ptr1
, ptr2
, user_data
);
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)
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)
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
;
1129 if (!tags_array
|| !tags_array
->len
)
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
);
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;
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. */
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
)
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
)
1180 matching_line
= tag
->line
;
1184 return matching_tag
;
1187 const gchar
*tm_tag_context_separator(langType lang
)
1191 case TM_PARSER_C
: /* for C++ .h headers or C structs */
1193 case TM_PARSER_GLSL
: /* for structs */
1194 /*case GEANY_FILETYPES_RUBY:*/ /* not sure what to use atm*/
1196 case TM_PARSER_POWERSHELL
:
1197 case TM_PARSER_RUST
:
1198 case TM_PARSER_ZEPHIR
:
1201 /* avoid confusion with other possible separators in group/section name */
1202 case TM_PARSER_CONF
:
1203 case TM_PARSER_REST
:
1206 /* no context separator */
1207 case TM_PARSER_ASCIIDOC
:
1208 case TM_PARSER_TXT2TAGS
:
1216 gboolean
tm_tag_is_anon(const TMTag
*tag
)
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;
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
);
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
;
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
)
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
)
1308 else if (TAG_ACCESS_PROTECTED
== tag
->access
)
1310 else if (TAG_ACCESS_PRIVATE
== tag
->access
)
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
;
1326 laccess
= tm_tag_access_name(tag
);
1327 impl
= tm_tag_impl_name(tag
);
1328 type
= tm_tag_type_name(tag
);
1330 fprintf(fp
, "%s ", laccess
);
1332 fprintf(fp
, "%s ", impl
);
1334 fprintf(fp
, "%s ", type
);
1336 fprintf(fp
, "%s ", tag
->var_type
);
1338 fprintf(fp
, "%s::", tag
->scope
);
1339 fprintf(fp
, "%s", tag
->name
);
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
1351 Prints info about all tags in the array to the given file pointer.
1353 void tm_tags_array_print(GPtrArray
*tags
, FILE *fp
)
1357 if (!(tags
&& (tags
->len
> 0) && fp
))
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
);
1374 if(!(t
&& t
->scope
))
1376 for (s
= t
->scope
, depth
= 0; s
; s
= strstr(s
, context_sep
))
1384 #endif /* TM_DEBUG */