TM: Don't allow passing NULL to tm_workspace API
[geany-mirror.git] / tagmanager / src / tm_workspace.c
blob5edf1e4f399854967b8d9e4b283700839f0daedd
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 /**
11 * @file tm_workspace.h
12 The TMWorkspace structure is meant to be used as a singleton to store application
13 wide tag information.
15 The workspace is intended to contain a list of global tags
16 and a set of individual source files.
19 #include "general.h"
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <ctype.h>
25 #include <sys/types.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #ifdef HAVE_GLOB_H
29 # include <glob.h>
30 #endif
31 #include <glib/gstdio.h>
33 #include "tm_workspace.h"
34 #include "tm_tag.h"
37 /* when changing, always keep the three sort criteria below in sync */
38 static TMTagAttrType workspace_tags_sort_attrs[] =
40 tm_tag_attr_name_t, tm_tag_attr_file_t, tm_tag_attr_line_t,
41 tm_tag_attr_type_t, tm_tag_attr_scope_t, tm_tag_attr_arglist_t, 0
44 /* for file tags the file is always identical, don't use for sorting */
45 static TMTagAttrType file_tags_sort_attrs[] =
47 tm_tag_attr_name_t, tm_tag_attr_line_t,
48 tm_tag_attr_type_t, tm_tag_attr_scope_t, tm_tag_attr_arglist_t, 0
51 /* global tags don't have file/line information */
52 static TMTagAttrType global_tags_sort_attrs[] =
54 tm_tag_attr_name_t,
55 tm_tag_attr_type_t, tm_tag_attr_scope_t, tm_tag_attr_arglist_t, 0
59 static TMWorkspace *theWorkspace = NULL;
62 static gboolean tm_create_workspace(void)
64 theWorkspace = g_new(TMWorkspace, 1);
65 theWorkspace->tags_array = g_ptr_array_new();
67 theWorkspace->global_tags = g_ptr_array_new();
68 theWorkspace->source_files = g_ptr_array_new();
69 theWorkspace->typename_array = g_ptr_array_new();
70 return TRUE;
74 /* Frees the workspace structure and all child source files. Use only when
75 exiting from the main program.
77 void tm_workspace_free(void)
79 guint i;
81 #ifdef TM_DEBUG
82 g_message("Workspace destroyed");
83 #endif
85 for (i=0; i < theWorkspace->source_files->len; ++i)
86 tm_source_file_free(theWorkspace->source_files->pdata[i]);
87 g_ptr_array_free(theWorkspace->source_files, TRUE);
88 tm_tags_array_free(theWorkspace->global_tags, TRUE);
89 g_ptr_array_free(theWorkspace->tags_array, TRUE);
90 g_ptr_array_free(theWorkspace->typename_array, TRUE);
91 g_free(theWorkspace);
92 theWorkspace = NULL;
96 /* Since TMWorkspace is a singleton, you should not create multiple
97 workspaces, but get a pointer to the workspace whenever required. The first
98 time a pointer is requested, or a source file is added to the workspace,
99 a workspace is created. Subsequent calls to the function will return the
100 created workspace.
102 const TMWorkspace *tm_get_workspace(void)
104 if (NULL == theWorkspace)
105 tm_create_workspace();
106 return theWorkspace;
110 static void tm_workspace_merge_tags(GPtrArray **big_array, GPtrArray *small_array)
112 GPtrArray *new_tags = tm_tags_merge(*big_array, small_array, workspace_tags_sort_attrs, FALSE);
113 /* tags owned by TMSourceFile - free just the pointer array */
114 g_ptr_array_free(*big_array, TRUE);
115 *big_array = new_tags;
119 static void update_source_file(TMSourceFile *source_file, guchar* text_buf,
120 gsize buf_size, gboolean use_buffer, gboolean update_workspace)
122 #ifdef TM_DEBUG
123 g_message("Source file updating based on source file %s", source_file->file_name);
124 #endif
126 if (update_workspace)
128 /* tm_source_file_parse() deletes the tag objects - remove the tags from
129 * workspace while they exist and can be scanned */
130 tm_tags_remove_file_tags(source_file, theWorkspace->tags_array);
131 tm_tags_remove_file_tags(source_file, theWorkspace->typename_array);
133 tm_source_file_parse(source_file, text_buf, buf_size, use_buffer);
134 tm_tags_sort(source_file->tags_array, file_tags_sort_attrs, FALSE, TRUE);
135 if (update_workspace)
137 GPtrArray *sf_typedefs;
139 #ifdef TM_DEBUG
140 g_message("Updating workspace from source file");
141 #endif
142 tm_workspace_merge_tags(&theWorkspace->tags_array, source_file->tags_array);
144 sf_typedefs = tm_tags_extract(source_file->tags_array, TM_GLOBAL_TYPE_MASK);
145 tm_workspace_merge_tags(&theWorkspace->typename_array, sf_typedefs);
146 g_ptr_array_free(sf_typedefs, TRUE);
148 #ifdef TM_DEBUG
149 else
150 g_message("Skipping workspace update because update_workspace is %s",
151 update_workspace?"TRUE":"FALSE");
153 #endif
157 /** Adds a source file to the workspace, parses it and updates the workspace tags.
158 @param source_file The source file to add to the workspace.
160 void tm_workspace_add_source_file(TMSourceFile *source_file)
162 g_return_if_fail(source_file != NULL);
164 g_ptr_array_add(theWorkspace->source_files, source_file);
165 update_source_file(source_file, NULL, 0, FALSE, TRUE);
169 void tm_workspace_add_source_file_noupdate(TMSourceFile *source_file)
171 g_return_if_fail(source_file != NULL);
173 g_ptr_array_add(theWorkspace->source_files, source_file);
177 /* Updates the source file by reparsing the text-buffer passed as parameter.
178 Ctags will use a parsing based on buffer instead of on files.
179 You should call this function when you don't want a previous saving of the file
180 you're editing. It's useful for a "real-time" updating of the tags.
181 The tags array and the tags themselves are destroyed and re-created, hence any
182 other tag arrays pointing to these tags should be rebuilt as well. All sorting
183 information is also lost. The language parameter is automatically detected
184 the first time the file is parsed if it is set to LANG_AUTO.
185 @param source_file The source file to update with a buffer.
186 @param text_buf A text buffer. The user should take care of allocate and free it after
187 the use here.
188 @param buf_size The size of text_buf.
190 void tm_workspace_update_source_file_buffer(TMSourceFile *source_file, guchar* text_buf,
191 gsize buf_size)
193 update_source_file(source_file, text_buf, buf_size, TRUE, TRUE);
197 /** Removes a source file from the workspace if it exists. This function also removes
198 the tags belonging to this file from the workspace. To completely free the TMSourceFile
199 pointer call tm_source_file_free() on it.
200 @param source_file Pointer to the source file to be removed.
202 void tm_workspace_remove_source_file(TMSourceFile *source_file)
204 guint i;
206 g_return_if_fail(source_file != NULL);
208 for (i=0; i < theWorkspace->source_files->len; ++i)
210 if (theWorkspace->source_files->pdata[i] == source_file)
212 tm_tags_remove_file_tags(source_file, theWorkspace->tags_array);
213 tm_tags_remove_file_tags(source_file, theWorkspace->typename_array);
214 g_ptr_array_remove_index_fast(theWorkspace->source_files, i);
215 return;
221 /* Recreates workspace tag array from all member TMSourceFile objects. Use if you
222 want to globally refresh the workspace. This function does not call tm_source_file_update()
223 which should be called before this function on source files which need to be
224 reparsed.
226 static void tm_workspace_update(void)
228 guint i, j;
229 TMSourceFile *source_file;
231 #ifdef TM_DEBUG
232 g_message("Recreating workspace tags array");
233 #endif
235 g_ptr_array_set_size(theWorkspace->tags_array, 0);
237 #ifdef TM_DEBUG
238 g_message("Total %d objects", theWorkspace->source_files->len);
239 #endif
240 for (i=0; i < theWorkspace->source_files->len; ++i)
242 source_file = theWorkspace->source_files->pdata[i];
243 #ifdef TM_DEBUG
244 g_message("Adding tags of %s", source_file->file_name);
245 #endif
246 if (source_file->tags_array->len > 0)
248 for (j = 0; j < source_file->tags_array->len; ++j)
250 g_ptr_array_add(theWorkspace->tags_array,
251 source_file->tags_array->pdata[j]);
255 #ifdef TM_DEBUG
256 g_message("Total: %d tags", theWorkspace->tags_array->len);
257 #endif
258 tm_tags_sort(theWorkspace->tags_array, workspace_tags_sort_attrs, TRUE, FALSE);
260 theWorkspace->typename_array = tm_tags_extract(theWorkspace->tags_array, TM_GLOBAL_TYPE_MASK);
264 /** Adds multiple source files to the workspace and updates the workspace tag arrays.
265 This is more efficient than calling tm_workspace_add_source_file() and
266 tm_workspace_update_source_file() separately for each of the files.
267 @param source_files The source files to be added to the workspace.
269 void tm_workspace_add_source_files(GPtrArray *source_files)
271 guint i;
273 g_return_if_fail(source_files != NULL);
275 for (i = 0; i < source_files->len; i++)
277 TMSourceFile *source_file = source_files->pdata[i];
279 tm_workspace_add_source_file_noupdate(source_file);
280 update_source_file(source_file, NULL, 0, FALSE, FALSE);
283 tm_workspace_update();
287 /** Removes multiple source files from the workspace and updates the workspace tag
288 arrays. This is more efficient than calling tm_workspace_remove_source_file()
289 separately for each of the files. To completely free the TMSourceFile pointers
290 call tm_source_file_free() on each of them.
291 @param source_files The source files to be removed from the workspace.
293 void tm_workspace_remove_source_files(GPtrArray *source_files)
295 guint i, j;
297 g_return_if_fail(source_files != NULL);
299 //TODO: sort both arrays by pointer value and remove in single pass
300 for (i = 0; i < source_files->len; i++)
302 TMSourceFile *source_file = source_files->pdata[i];
304 for (j = 0; j < theWorkspace->source_files->len; j++)
306 if (theWorkspace->source_files->pdata[j] == source_file)
308 g_ptr_array_remove_index_fast(theWorkspace->source_files, j);
309 break;
314 tm_workspace_update();
318 /* Loads the global tag list from the specified file. The global tag list should
319 have been first created using tm_workspace_create_global_tags().
320 @param tags_file The file containing global tags.
321 @return TRUE on success, FALSE on failure.
322 @see tm_workspace_create_global_tags()
324 gboolean tm_workspace_load_global_tags(const char *tags_file, gint mode)
326 guchar buf[BUFSIZ];
327 FILE *fp;
328 GPtrArray *file_tags, *new_tags;
329 TMTag *tag;
330 TMFileFormat format = TM_FILE_FORMAT_TAGMANAGER;
332 if (NULL == (fp = g_fopen(tags_file, "r")))
333 return FALSE;
334 if ((NULL == fgets((gchar*) buf, BUFSIZ, fp)) || ('\0' == *buf))
336 fclose(fp);
337 return FALSE; /* early out on error */
339 else
340 { /* We read the first line for the format specification. */
341 if (buf[0] == '#' && strstr((gchar*) buf, "format=pipe") != NULL)
342 format = TM_FILE_FORMAT_PIPE;
343 else if (buf[0] == '#' && strstr((gchar*) buf, "format=tagmanager") != NULL)
344 format = TM_FILE_FORMAT_TAGMANAGER;
345 else if (buf[0] == '#' && strstr((gchar*) buf, "format=ctags") != NULL)
346 format = TM_FILE_FORMAT_CTAGS;
347 else if (strncmp((gchar*) buf, "!_TAG_", 6) == 0)
348 format = TM_FILE_FORMAT_CTAGS;
349 else
350 { /* We didn't find a valid format specification, so we try to auto-detect the format
351 * by counting the pipe characters on the first line and asumme pipe format when
352 * we find more than one pipe on the line. */
353 guint i, pipe_cnt = 0, tab_cnt = 0;
354 for (i = 0; i < BUFSIZ && buf[i] != '\0' && pipe_cnt < 2; i++)
356 if (buf[i] == '|')
357 pipe_cnt++;
358 else if (buf[i] == '\t')
359 tab_cnt++;
361 if (pipe_cnt > 1)
362 format = TM_FILE_FORMAT_PIPE;
363 else if (tab_cnt > 1)
364 format = TM_FILE_FORMAT_CTAGS;
366 rewind(fp); /* reset the file pointer, to start reading again from the beginning */
369 file_tags = g_ptr_array_new();
370 while (NULL != (tag = tm_tag_new_from_file(NULL, fp, mode, format)))
371 g_ptr_array_add(file_tags, tag);
372 fclose(fp);
374 tm_tags_sort(file_tags, global_tags_sort_attrs, TRUE, TRUE);
376 /* reorder the whole array, because tm_tags_find expects a sorted array */
377 new_tags = tm_tags_merge(theWorkspace->global_tags,
378 file_tags, global_tags_sort_attrs, TRUE);
379 g_ptr_array_free(theWorkspace->global_tags, TRUE);
380 g_ptr_array_free(file_tags, TRUE);
381 theWorkspace->global_tags = new_tags;
383 return TRUE;
387 static guint tm_file_inode_hash(gconstpointer key)
389 struct stat file_stat;
390 const char *filename = (const char*)key;
391 if (g_stat(filename, &file_stat) == 0)
393 #ifdef TM_DEBUG
394 g_message ("Hash for '%s' is '%d'\n", filename, file_stat.st_ino);
395 #endif
396 return g_direct_hash ((gpointer)(gulong)file_stat.st_ino);
397 } else {
398 return 0;
403 static void tm_move_entries_to_g_list(gpointer key, gpointer value, gpointer user_data)
405 GList **pp_list = (GList**)user_data;
407 if (user_data == NULL)
408 return;
410 *pp_list = g_list_prepend(*pp_list, value);
414 static void write_includes_file(FILE *fp, GList *includes_files)
416 GList *node;
418 node = includes_files;
419 while (node)
421 char *str = g_strdup_printf("#include \"%s\"\n", (char*)node->data);
422 size_t str_len = strlen(str);
424 fwrite(str, str_len, 1, fp);
425 g_free(str);
426 node = g_list_next(node);
431 static void append_to_temp_file(FILE *fp, GList *file_list)
433 GList *node;
435 node = file_list;
436 while (node)
438 const char *fname = node->data;
439 char *contents;
440 size_t length;
441 GError *err = NULL;
443 if (! g_file_get_contents(fname, &contents, &length, &err))
445 fprintf(stderr, "Unable to read file: %s\n", err->message);
446 g_error_free(err);
448 else
450 fwrite(contents, length, 1, fp);
451 fwrite("\n", 1, 1, fp); /* in case file doesn't end in newline (e.g. windows). */
452 g_free(contents);
454 node = g_list_next (node);
459 static gchar *create_temp_file(const gchar *tpl)
461 gchar *name;
462 gint fd;
464 fd = g_file_open_tmp(tpl, &name, NULL);
465 if (fd < 0)
466 name = NULL;
467 else
468 close(fd);
470 return name;
474 /* Creates a list of global tags. Ideally, this should be created once during
475 installations so that all users can use the same file. Thsi is because a full
476 scale global tag list can occupy several megabytes of disk space.
477 @param pre_process The pre-processing command. This is executed via system(),
478 so you can pass stuff like 'gcc -E -dD -P `gnome-config --cflags gnome`'.
479 @param includes Include files to process. Wildcards such as '/usr/include/a*.h'
480 are allowed.
481 @param tags_file The file where the tags will be stored.
482 @param lang The language to use for the tags file.
483 @return TRUE on success, FALSE on failure.
485 gboolean tm_workspace_create_global_tags(const char *pre_process, const char **includes,
486 int includes_count, const char *tags_file, int lang)
488 #ifdef HAVE_GLOB_H
489 glob_t globbuf;
490 size_t idx_glob;
491 #endif
492 int idx_inc;
493 char *command;
494 guint i;
495 FILE *fp;
496 TMSourceFile *source_file;
497 GPtrArray *tags_array;
498 GHashTable *includes_files_hash;
499 GList *includes_files = NULL;
500 gchar *temp_file = create_temp_file("tmp_XXXXXX.cpp");
501 gchar *temp_file2 = create_temp_file("tmp_XXXXXX.cpp");
503 if (NULL == temp_file || NULL == temp_file2 ||
504 NULL == (fp = g_fopen(temp_file, "w")))
506 g_free(temp_file);
507 g_free(temp_file2);
508 return FALSE;
511 includes_files_hash = g_hash_table_new_full (tm_file_inode_hash,
512 g_direct_equal,
513 NULL, g_free);
515 #ifdef HAVE_GLOB_H
516 globbuf.gl_offs = 0;
518 if (includes[0][0] == '"') /* leading \" char for glob matching */
519 for(idx_inc = 0; idx_inc < includes_count; idx_inc++)
521 size_t dirty_len = strlen(includes[idx_inc]);
522 char *clean_path = g_malloc(dirty_len - 1);
524 strncpy(clean_path, includes[idx_inc] + 1, dirty_len - 1);
525 clean_path[dirty_len - 2] = 0;
527 #ifdef TM_DEBUG
528 g_message ("[o][%s]\n", clean_path);
529 #endif
530 glob(clean_path, 0, NULL, &globbuf);
532 #ifdef TM_DEBUG
533 g_message ("matches: %d\n", globbuf.gl_pathc);
534 #endif
536 for(idx_glob = 0; idx_glob < globbuf.gl_pathc; idx_glob++)
538 #ifdef TM_DEBUG
539 g_message (">>> %s\n", globbuf.gl_pathv[idx_glob]);
540 #endif
541 if (!g_hash_table_lookup(includes_files_hash,
542 globbuf.gl_pathv[idx_glob]))
544 char* file_name_copy = strdup(globbuf.gl_pathv[idx_glob]);
545 g_hash_table_insert(includes_files_hash, file_name_copy,
546 file_name_copy);
547 #ifdef TM_DEBUG
548 g_message ("Added ...\n");
549 #endif
552 globfree(&globbuf);
553 g_free(clean_path);
555 else
556 #endif
557 /* no glob support or globbing not wanted */
558 for(idx_inc = 0; idx_inc < includes_count; idx_inc++)
560 if (!g_hash_table_lookup(includes_files_hash,
561 includes[idx_inc]))
563 char* file_name_copy = strdup(includes[idx_inc]);
564 g_hash_table_insert(includes_files_hash, file_name_copy,
565 file_name_copy);
569 /* Checks for duplicate file entries which would case trouble */
570 g_hash_table_foreach(includes_files_hash, tm_move_entries_to_g_list,
571 &includes_files);
573 includes_files = g_list_reverse (includes_files);
575 #ifdef TM_DEBUG
576 g_message ("writing out files to %s\n", temp_file);
577 #endif
578 if (pre_process != NULL)
579 write_includes_file(fp, includes_files);
580 else
581 append_to_temp_file(fp, includes_files);
583 g_list_free (includes_files);
584 g_hash_table_destroy(includes_files_hash);
585 includes_files_hash = NULL;
586 includes_files = NULL;
587 fclose(fp);
589 if (pre_process != NULL)
591 gint ret;
592 gchar *tmp_errfile = create_temp_file("tmp_XXXXXX");
593 gchar *errors = NULL;
594 command = g_strdup_printf("%s %s >%s 2>%s",
595 pre_process, temp_file, temp_file2, tmp_errfile);
596 #ifdef TM_DEBUG
597 g_message("Executing: %s", command);
598 #endif
599 ret = system(command);
600 g_free(command);
601 g_unlink(temp_file);
602 g_free(temp_file);
603 g_file_get_contents(tmp_errfile, &errors, NULL, NULL);
604 if (errors && *errors)
605 g_printerr("%s", errors);
606 g_free(errors);
607 g_unlink(tmp_errfile);
608 g_free(tmp_errfile);
609 if (ret == -1)
611 g_unlink(temp_file2);
612 return FALSE;
615 else
617 /* no pre-processing needed, so temp_file2 = temp_file */
618 g_unlink(temp_file2);
619 g_free(temp_file2);
620 temp_file2 = temp_file;
621 temp_file = NULL;
623 source_file = tm_source_file_new(temp_file2, tm_source_file_get_lang_name(lang));
624 update_source_file(source_file, NULL, 0, FALSE, FALSE);
625 if (NULL == source_file)
627 g_unlink(temp_file2);
628 return FALSE;
630 g_unlink(temp_file2);
631 g_free(temp_file2);
632 if (0 == source_file->tags_array->len)
634 tm_source_file_free(source_file);
635 return FALSE;
637 tags_array = tm_tags_extract(source_file->tags_array, tm_tag_max_t);
638 if ((NULL == tags_array) || (0 == tags_array->len))
640 if (tags_array)
641 g_ptr_array_free(tags_array, TRUE);
642 tm_source_file_free(source_file);
643 return FALSE;
645 if (FALSE == tm_tags_sort(tags_array, global_tags_sort_attrs, TRUE, FALSE))
647 tm_source_file_free(source_file);
648 return FALSE;
650 if (NULL == (fp = g_fopen(tags_file, "w")))
652 tm_source_file_free(source_file);
653 return FALSE;
655 fprintf(fp, "# format=tagmanager\n");
656 for (i = 0; i < tags_array->len; ++i)
658 tm_tag_write(TM_TAG(tags_array->pdata[i]), fp, tm_tag_attr_type_t
659 | tm_tag_attr_scope_t | tm_tag_attr_arglist_t | tm_tag_attr_vartype_t
660 | tm_tag_attr_pointer_t);
662 fclose(fp);
663 tm_source_file_free(source_file);
664 g_ptr_array_free(tags_array, TRUE);
665 return TRUE;
669 /* Returns all matching tags found in the workspace.
670 @param name The name of the tag to find.
671 @param type The tag types to return (TMTagType). Can be a bitmask.
672 @param attrs The attributes to sort and dedup on (0 terminated integer array).
673 @param partial Whether partial match is allowed.
674 @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
675 -1 for all
676 @return Array of matching tags. Do not free() it since it is a static member.
678 const GPtrArray *tm_workspace_find(const char *name, TMTagType type, TMTagAttrType *attrs,
679 gboolean partial, langType lang)
681 static GPtrArray *tags = NULL;
682 TMTag **matches[2];
683 size_t len;
684 guint tagCount[2]={0,0}, tagIter;
685 gint tags_lang;
687 if (!name)
688 return NULL;
689 len = strlen(name);
690 if (!len)
691 return NULL;
692 if (tags)
693 g_ptr_array_set_size(tags, 0);
694 else
695 tags = g_ptr_array_new();
697 matches[0] = tm_tags_find(theWorkspace->tags_array, name, partial, TRUE,
698 &tagCount[0]);
699 matches[1] = tm_tags_find(theWorkspace->global_tags, name, partial, TRUE, &tagCount[1]);
701 /* file tags */
702 if (matches[0] && *matches[0])
704 tags_lang = (*matches[0])->lang;
706 for (tagIter=0;tagIter<tagCount[0];++tagIter)
708 if ((type & (*matches[0])->type) && (lang == -1 || tags_lang == lang))
709 g_ptr_array_add(tags, *matches[0]);
710 if (partial)
712 if (0 != strncmp((*matches[0])->name, name, len))
713 break;
715 else
717 if (0 != strcmp((*matches[0])->name, name))
718 break;
720 ++ matches[0];
724 /* global tags */
725 if (matches[1] && *matches[1])
727 int tags_lang_alt = 0;
728 tags_lang = (*matches[1])->lang;
729 /* tags_lang_alt is used to load C global tags only once for C and C++
730 * lang = 1 is C++, lang = 0 is C
731 * if we have lang 0, than accept also lang 1 for C++ */
732 if (tags_lang == 0) /* C or C++ */
733 tags_lang_alt = 1;
734 else
735 tags_lang_alt = tags_lang; /* otherwise just ignore it */
737 for (tagIter=0;tagIter<tagCount[1];++tagIter)
739 if ((type & (*matches[1])->type) && (lang == -1 ||
740 tags_lang == lang || tags_lang_alt == lang))
741 g_ptr_array_add(tags, *matches[1]);
743 if (partial)
745 if (0 != strncmp((*matches[1])->name, name, len))
746 break;
748 else
750 if (0 != strcmp((*matches[1])->name, name))
751 break;
753 ++ matches[1];
757 if (attrs)
758 tm_tags_sort(tags, attrs, TRUE, FALSE);
759 return tags;
763 static gboolean match_langs(gint lang, const TMTag *tag)
765 if (tag->file)
766 { /* workspace tag */
767 if (lang == tag->file->lang)
768 return TRUE;
770 else
771 { /* global tag */
772 if (lang == tag->lang)
773 return TRUE;
775 return FALSE;
779 /* scope can be NULL.
780 * lang can be -1 */
781 static guint
782 fill_find_tags_array (GPtrArray *dst, const GPtrArray *src,
783 const char *name, const char *scope, TMTagType type, gboolean partial,
784 gint lang, gboolean first)
786 TMTag **match;
787 guint tagIter, count;
789 if ((!src) || (!dst) || (!name) || (!*name))
790 return 0;
792 match = tm_tags_find (src, name, partial, TRUE, &count);
793 if (count && match && *match)
795 for (tagIter = 0; tagIter < count; ++tagIter)
797 if (! scope || (match[tagIter]->scope &&
798 0 == strcmp(match[tagIter]->scope, scope)))
800 if (type & match[tagIter]->type)
801 if (lang == -1 || match_langs(lang, match[tagIter]))
803 g_ptr_array_add (dst, match[tagIter]);
804 if (first)
805 break;
810 return dst->len;
814 /* Returns all matching tags found in the workspace. Adapted from tm_workspace_find, Anjuta 2.02
815 @param name The name of the tag to find.
816 @param scope The scope name of the tag to find, or NULL.
817 @param type The tag types to return (TMTagType). Can be a bitmask.
818 @param attrs The attributes to sort and dedup on (0 terminated integer array).
819 @param partial Whether partial match is allowed.
820 @param lang Specifies the language(see the table in parsers.h) of the tags to be found,
821 -1 for all
822 @return Array of matching tags. Do not free() it since it is a static member.
824 const GPtrArray *
825 tm_workspace_find_scoped (const char *name, const char *scope, TMTagType type,
826 TMTagAttrType *attrs, gboolean partial, langType lang, gboolean global_search)
828 static GPtrArray *tags = NULL;
830 if (tags)
831 g_ptr_array_set_size (tags, 0);
832 else
833 tags = g_ptr_array_new ();
835 fill_find_tags_array (tags, theWorkspace->tags_array,
836 name, scope, type, partial, lang, FALSE);
837 if (global_search)
839 /* for a scoped tag, I think we always want the same language */
840 fill_find_tags_array (tags, theWorkspace->global_tags,
841 name, scope, type, partial, lang, FALSE);
843 if (attrs)
844 tm_tags_sort (tags, attrs, TRUE, FALSE);
845 return tags;
849 static int
850 find_scope_members_tags (const GPtrArray * all, GPtrArray * tags,
851 const langType langJava, const char *name,
852 const char *filename, gboolean no_definitions)
854 GPtrArray *local = g_ptr_array_new ();
855 unsigned int i;
856 TMTag *tag;
857 size_t len = strlen (name);
858 for (i = 0; (i < all->len); ++i)
860 tag = TM_TAG (all->pdata[i]);
861 if (no_definitions && filename && tag->file &&
862 0 != strcmp (filename,
863 tag->file->short_name))
865 continue;
867 if (tag && tag->scope && tag->scope[0] != '\0')
869 if (0 == strncmp (name, tag->scope, len))
871 g_ptr_array_add (local, tag);
875 if (local->len > 0)
877 unsigned int j;
878 TMTag *tag2;
879 char backup = 0;
880 char *s_backup = NULL;
881 char *var_type = NULL;
882 char *scope;
883 for (i = 0; (i < local->len); ++i)
885 tag = TM_TAG (local->pdata[i]);
886 scope = tag->scope;
887 if (scope && 0 == strcmp (name, scope))
889 g_ptr_array_add (tags, tag);
890 continue;
892 s_backup = NULL;
893 j = 0; /* someone could write better code :P */
894 while (scope)
896 if (s_backup)
898 backup = s_backup[0];
899 s_backup[0] = '\0';
900 if (0 == strcmp (name, tag->scope))
902 j = local->len;
903 s_backup[0] = backup;
904 break;
907 if (tag->file
908 && tag->file->lang == langJava)
910 scope = strrchr (tag->scope, '.');
911 if (scope)
912 var_type = scope + 1;
914 else
916 scope = strrchr (tag->scope, ':');
917 if (scope)
919 var_type = scope + 1;
920 scope--;
923 if (s_backup)
925 s_backup[0] = backup;
927 if (scope)
929 if (s_backup)
931 backup = s_backup[0];
932 s_backup[0] = '\0';
934 for (j = 0; (j < local->len); ++j)
936 if (i == j)
937 continue;
938 tag2 = TM_TAG (local->pdata[j]);
939 if (tag2->var_type &&
940 0 == strcmp (var_type, tag2->var_type))
942 break;
945 if (s_backup)
946 s_backup[0] = backup;
948 if (j < local->len)
950 break;
952 s_backup = scope;
954 if (j == local->len)
956 g_ptr_array_add (tags, tag);
960 g_ptr_array_free (local, TRUE);
961 return (int) tags->len;
965 /* Returns all matching members tags found in given struct/union/class name.
966 @param name Name of the struct/union/class.
967 @param file_tags A GPtrArray of edited file TMTag pointers (for search speedup, can be NULL).
968 @return A GPtrArray of TMTag pointers to struct/union/class members */
969 const GPtrArray *
970 tm_workspace_find_scope_members (const GPtrArray * file_tags, const char *name,
971 gboolean search_global, gboolean no_definitions)
973 static GPtrArray *tags = NULL;
974 GPtrArray *local = NULL;
975 char *new_name = (char *) name;
976 char *filename = NULL;
977 int found = 0, del = 0;
978 static langType langJava = -1;
979 TMTag *tag = NULL;
981 /* FIXME */
982 /* langJava = getNamedLanguage ("Java"); */
984 g_return_val_if_fail ((theWorkspace && name && name[0] != '\0'), NULL);
986 if (!tags)
987 tags = g_ptr_array_new ();
989 while (1)
991 const GPtrArray *tags2;
992 guint got = 0;
993 TMTagType types = (tm_tag_class_t | tm_tag_namespace_t |
994 tm_tag_struct_t | tm_tag_typedef_t |
995 tm_tag_union_t | tm_tag_enum_t);
997 if (file_tags)
999 g_ptr_array_set_size (tags, 0);
1000 got = fill_find_tags_array (tags, file_tags,
1001 new_name, NULL, types, FALSE, -1, FALSE);
1003 if (got)
1005 tags2 = tags;
1007 else
1009 TMTagAttrType attrs[] = {
1010 tm_tag_attr_name_t, tm_tag_attr_type_t,
1011 tm_tag_attr_none_t
1013 tags2 = tm_workspace_find (new_name, types, attrs, FALSE, -1);
1016 if ((tags2) && (tags2->len == 1) && (tag = TM_TAG (tags2->pdata[0])))
1018 if (tag->type == tm_tag_typedef_t && tag->var_type
1019 && tag->var_type[0] != '\0')
1021 char *tmp_name;
1022 tmp_name = tag->var_type;
1023 if (strcmp(tmp_name, new_name) == 0) {
1024 new_name = NULL;
1026 else {
1027 new_name = tmp_name;
1029 continue;
1031 filename = (tag->file ?
1032 tag->file->short_name : NULL);
1033 if (tag->scope && tag->scope[0] != '\0')
1035 del = 1;
1036 if (tag->file &&
1037 tag->file->lang == langJava)
1039 new_name = g_strdup_printf ("%s.%s",
1040 tag->scope,
1041 new_name);
1043 else
1045 new_name = g_strdup_printf ("%s::%s",
1046 tag->scope,
1047 new_name);
1050 break;
1052 else
1054 return NULL;
1058 g_ptr_array_set_size (tags, 0);
1060 if (no_definitions && tag && tag->file)
1062 local = tm_tags_extract (tag->file->tags_array,
1063 (tm_tag_function_t | tm_tag_prototype_t |
1064 tm_tag_member_t | tm_tag_field_t |
1065 tm_tag_method_t | tm_tag_enumerator_t));
1067 else
1069 local = tm_tags_extract (theWorkspace->tags_array,
1070 (tm_tag_function_t | tm_tag_prototype_t |
1071 tm_tag_member_t | tm_tag_field_t |
1072 tm_tag_method_t | tm_tag_enumerator_t));
1074 if (local)
1076 found = find_scope_members_tags (local, tags, langJava, new_name,
1077 filename, no_definitions);
1078 g_ptr_array_free (local, TRUE);
1080 if (!found && search_global)
1082 GPtrArray *global = tm_tags_extract (theWorkspace->global_tags,
1083 (tm_tag_member_t |
1084 tm_tag_prototype_t |
1085 tm_tag_field_t |
1086 tm_tag_method_t |
1087 tm_tag_function_t |
1088 tm_tag_enumerator_t
1089 |tm_tag_struct_t | tm_tag_typedef_t |
1090 tm_tag_union_t | tm_tag_enum_t));
1091 if (global)
1093 find_scope_members_tags (global, tags, langJava, new_name,
1094 filename, no_definitions);
1095 g_ptr_array_free (global, TRUE);
1098 if (del)
1100 g_free (new_name);
1103 return tags;
1107 #ifdef TM_DEBUG
1109 /* Dumps the workspace tree - useful for debugging */
1110 void tm_workspace_dump(void)
1112 guint i;
1114 #ifdef TM_DEBUG
1115 g_message("Dumping TagManager workspace tree..");
1116 #endif
1117 for (i=0; i < theWorkspace->source_files->len; ++i)
1119 TMSourceFile *source_file = theWorkspace->source_files->pdata[i];
1120 fprintf(stderr, "%s", source_file->file_name);
1123 #endif /* TM_DEBUG */
1126 #if 0
1128 static int
1129 find_namespace_members_tags (const GPtrArray * all, GPtrArray * tags,
1130 const langType langJava, const char *name,
1131 const char *filename)
1133 GPtrArray *local = g_ptr_array_new ();
1134 unsigned int i;
1135 TMTag *tag;
1136 size_t len = strlen (name);
1138 g_return_val_if_fail (all != NULL, 0);
1140 for (i = 0; (i < all->len); ++i)
1142 tag = TM_TAG (all->pdata[i]);
1143 if (filename && tag->file &&
1144 0 != strcmp (filename,
1145 tag->file->short_name))
1147 continue;
1150 if (tag && tag->scope && tag->scope[0] != '\0')
1152 if (0 == strncmp (name, tag->scope, len))
1154 g_ptr_array_add (local, tag);
1159 if (local->len > 0)
1161 char *scope;
1162 for (i = 0; (i < local->len); ++i)
1164 tag = TM_TAG (local->pdata[i]);
1165 scope = tag->scope;
1167 /* if we wanna complete something like
1168 * namespace1::
1169 * we'll just return the tags that have "namespace1"
1170 * as their scope. So we won't return classes/members/namespaces
1171 * under, for example, namespace2, where namespace1::namespace2
1173 if (scope && 0 == strcmp (name, scope))
1175 g_ptr_array_add (tags, tag);
1180 g_ptr_array_free (local, TRUE);
1181 return (int) tags->len;
1184 static const GPtrArray *
1185 tm_workspace_find_namespace_members (const GPtrArray * file_tags, const char *name,
1186 gboolean search_global)
1188 static GPtrArray *tags = NULL;
1189 GPtrArray *local = NULL;
1190 char *new_name = (char *) name;
1191 char *filename = NULL;
1192 int found = 0, del = 0;
1193 static langType langJava = -1;
1194 TMTag *tag = NULL;
1196 g_return_val_if_fail (name && name[0] != '\0', NULL);
1198 if (!tags)
1199 tags = g_ptr_array_new ();
1201 while (1)
1203 const GPtrArray *tags2;
1204 guint got = 0;
1205 TMTagType types = (tm_tag_class_t
1206 tm_tag_struct_t | tm_tag_typedef_t |
1207 tm_tag_union_t | tm_tag_enum_t);
1209 if (file_tags)
1211 g_ptr_array_set_size (tags, 0);
1212 got = fill_find_tags_array (tags, file_tags,
1213 new_name, NULL, types, FALSE, -1, FALSE);
1217 if (got)
1219 tags2 = tags;
1221 else
1223 TMTagAttrType attrs[] = {
1224 tm_tag_attr_name_t, tm_tag_attr_type_t,
1225 tm_tag_attr_none_t
1227 tags2 = tm_workspace_find (new_name, types, attrs, FALSE, -1);
1230 if ((tags2) && (tags2->len == 1) && (tag = TM_TAG (tags2->pdata[0])))
1232 if (tag->type == tm_tag_typedef_t && tag->var_type
1233 && tag->var_type[0] != '\0')
1235 new_name = tag->var_type;
1236 continue;
1238 filename = (tag->file ?
1239 tag->file->short_name : NULL);
1240 if (tag->scope && tag->scope[0] != '\0')
1242 del = 1;
1243 if (tag->file &&
1244 tag->file->lang == langJava)
1246 new_name = g_strdup_printf ("%s.%s",
1247 tag->scope,
1248 new_name);
1250 else
1252 new_name = g_strdup_printf ("%s::%s",
1253 tag->scope,
1254 new_name);
1257 break;
1259 else
1261 return NULL;
1265 g_ptr_array_set_size (tags, 0);
1267 if (tag && tag->file)
1269 local = tm_tags_extract (tag->file->tags_array,
1270 (tm_tag_function_t |
1271 tm_tag_field_t | tm_tag_enumerator_t |
1272 tm_tag_namespace_t | tm_tag_class_t ));
1274 else
1276 local = tm_tags_extract (theWorkspace->tags_array,
1277 (tm_tag_function_t | tm_tag_prototype_t |
1278 tm_tag_member_t |
1279 tm_tag_field_t | tm_tag_enumerator_t |
1280 tm_tag_namespace_t | tm_tag_class_t ));
1283 if (local)
1285 found = find_namespace_members_tags (local, tags,
1286 langJava, new_name, filename);
1287 g_ptr_array_free (local, TRUE);
1291 if (!found && search_global)
1293 GPtrArray *global = tm_tags_extract (theWorkspace->global_tags,
1294 (tm_tag_member_t |
1295 tm_tag_prototype_t |
1296 tm_tag_field_t |
1297 tm_tag_method_t |
1298 tm_tag_function_t |
1299 tm_tag_enumerator_t |
1300 tm_tag_namespace_t |
1301 tm_tag_class_t ));
1303 if (global)
1305 find_namespace_members_tags (global, tags, langJava,
1306 new_name, filename);
1308 DEBUG_PRINT ("returning these");
1309 gint i;
1310 for (i=0; i < tags->len; i++) {
1311 TMTag *cur_tag;
1313 cur_tag = (TMTag*)g_ptr_array_index (tags, i);
1314 tm_tag_print (cur_tag, stdout );
1317 g_ptr_array_free (global, TRUE);
1322 if (del)
1324 g_free (new_name);
1327 return tags;
1331 /* Returns a list of parent classes for the given class name
1332 @param name Name of the class
1333 @return A GPtrArray of TMTag pointers (includes the TMTag for the class) */
1334 static const GPtrArray *tm_workspace_get_parents(const gchar *name)
1336 static TMTagAttrType type[] = { tm_tag_attr_name_t, tm_tag_attr_none_t };
1337 static GPtrArray *parents = NULL;
1338 const GPtrArray *matches;
1339 guint i = 0;
1340 guint j;
1341 gchar **klasses;
1342 gchar **klass;
1343 TMTag *tag;
1345 g_return_val_if_fail(name && isalpha(*name),NULL);
1347 if (NULL == parents)
1348 parents = g_ptr_array_new();
1349 else
1350 g_ptr_array_set_size(parents, 0);
1351 matches = tm_workspace_find(name, tm_tag_class_t, type, FALSE, -1);
1352 if ((NULL == matches) || (0 == matches->len))
1353 return NULL;
1354 g_ptr_array_add(parents, matches->pdata[0]);
1355 while (i < parents->len)
1357 tag = TM_TAG(parents->pdata[i]);
1358 if ((NULL != tag->inheritance) && (isalpha(tag->inheritance[0])))
1360 klasses = g_strsplit(tag->inheritance, ",", 10);
1361 for (klass = klasses; (NULL != *klass); ++ klass)
1363 for (j=0; j < parents->len; ++j)
1365 if (0 == strcmp(*klass, TM_TAG(parents->pdata[j])->name))
1366 break;
1368 if (parents->len == j)
1370 matches = tm_workspace_find(*klass, tm_tag_class_t, type, FALSE, -1);
1371 if ((NULL != matches) && (0 < matches->len))
1372 g_ptr_array_add(parents, matches->pdata[0]);
1375 g_strfreev(klasses);
1377 ++ i;
1379 return parents;
1382 #endif