Fix the current scope shown in the statusbar
[geany-mirror.git] / src / symbols.c
blob5ee8c8514062fcbded75c565d3fff2c76eef239b
1 /*
2 * symbols.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2006-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
6 * Copyright 2011-2012 Colomban Wendling <ban(at)herbesfolles(dot)org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 /**
24 * @file symbols.h
25 * Tag-related functions.
26 **/
29 * Symbol Tree and TagManager-related convenience functions.
30 * TagManager parses tags for each document, and also adds them to the workspace (session).
31 * Global tags are lists of tags for each filetype, loaded when a document with a
32 * matching filetype is first loaded.
35 #ifdef HAVE_CONFIG_H
36 # include "config.h"
37 #endif
39 #include "symbols.h"
41 #include "app.h"
42 #include "callbacks.h" /* FIXME: for ignore_callback */
43 #include "documentprivate.h"
44 #include "editor.h"
45 #include "encodings.h"
46 #include "filetypesprivate.h"
47 #include "geanyobject.h"
48 #include "highlighting.h"
49 #include "main.h"
50 #include "navqueue.h"
51 #include "sciwrappers.h"
52 #include "sidebar.h"
53 #include "support.h"
54 #include "tm_parser.h"
55 #include "tm_tag.h"
56 #include "ui_utils.h"
57 #include "utils.h"
59 #include "SciLexer.h"
61 #include "gtkcompat.h"
63 #include <ctype.h>
64 #include <string.h>
65 #include <stdlib.h>
68 typedef struct
70 gint found_line; /* return: the nearest line found */
71 gint line; /* input: the line to look for */
72 gboolean lower /* input: search only for lines with lower number than @line */;
73 } TreeSearchData;
76 static GPtrArray *top_level_iter_names = NULL;
78 enum
80 ICON_CLASS,
81 ICON_MACRO,
82 ICON_MEMBER,
83 ICON_METHOD,
84 ICON_NAMESPACE,
85 ICON_OTHER,
86 ICON_STRUCT,
87 ICON_VAR,
88 ICON_NONE,
89 N_ICONS = ICON_NONE
92 static struct
94 const gchar *icon_name;
95 GdkPixbuf *pixbuf;
97 symbols_icons[N_ICONS] = {
98 [ICON_CLASS] = { "classviewer-class", NULL },
99 [ICON_MACRO] = { "classviewer-macro", NULL },
100 [ICON_MEMBER] = { "classviewer-member", NULL },
101 [ICON_METHOD] = { "classviewer-method", NULL },
102 [ICON_NAMESPACE] = { "classviewer-namespace", NULL },
103 [ICON_OTHER] = { "classviewer-other", NULL },
104 [ICON_STRUCT] = { "classviewer-struct", NULL },
105 [ICON_VAR] = { "classviewer-var", NULL },
108 static struct
110 GtkWidget *expand_all;
111 GtkWidget *collapse_all;
112 GtkWidget *sort_by_name;
113 GtkWidget *sort_by_appearance;
114 GtkWidget *find_usage;
115 GtkWidget *find_doc_usage;
116 GtkWidget *find_in_files;
118 symbol_menu;
120 static void load_user_tags(GeanyFiletypeID ft_id);
122 /* get the tags_ignore list, exported by tagmanager's options.c */
123 extern gchar **c_tags_ignore;
125 /* ignore certain tokens when parsing C-like syntax.
126 * Also works for reloading. */
127 static void load_c_ignore_tags(void)
129 gchar *path = g_build_filename(app->configdir, "ignore.tags", NULL);
130 gchar *content;
132 if (g_file_get_contents(path, &content, NULL, NULL))
134 /* historically we ignore the glib _DECLS for tag generation */
135 SETPTR(content, g_strconcat("G_BEGIN_DECLS G_END_DECLS\n", content, NULL));
137 g_strfreev(c_tags_ignore);
138 c_tags_ignore = g_strsplit_set(content, " \n\r", -1);
139 g_free(content);
141 g_free(path);
145 void symbols_reload_config_files(void)
147 load_c_ignore_tags();
151 static gsize get_tag_count(void)
153 GPtrArray *tags = tm_get_workspace()->global_tags;
154 gsize count = tags ? tags->len : 0;
156 return count;
160 /* wrapper for tm_workspace_load_global_tags().
161 * note that the tag count only counts new global tags added - if a tag has the same name,
162 * currently it replaces the existing tag, so loading a file twice will say 0 tags the 2nd time. */
163 static gboolean symbols_load_global_tags(const gchar *tags_file, GeanyFiletype *ft)
165 gboolean result;
166 gsize old_tag_count = get_tag_count();
168 result = tm_workspace_load_global_tags(tags_file, ft->lang);
169 if (result)
171 geany_debug("Loaded %s (%s), %u symbol(s).", tags_file, ft->name,
172 (guint) (get_tag_count() - old_tag_count));
174 return result;
178 /* Ensure that the global tags file(s) for the file_type_idx filetype is loaded.
179 * This provides autocompletion, calltips, etc. */
180 void symbols_global_tags_loaded(guint file_type_idx)
182 /* load ignore list for C/C++ parser */
183 if ((file_type_idx == GEANY_FILETYPES_C || file_type_idx == GEANY_FILETYPES_CPP) &&
184 c_tags_ignore == NULL)
186 load_c_ignore_tags();
189 if (cl_options.ignore_global_tags || app->tm_workspace == NULL)
190 return;
192 /* load config in case of custom filetypes */
193 filetypes_load_config(file_type_idx, FALSE);
195 load_user_tags(file_type_idx);
197 switch (file_type_idx)
199 case GEANY_FILETYPES_CPP:
200 symbols_global_tags_loaded(GEANY_FILETYPES_C); /* load C global tags */
201 break;
202 case GEANY_FILETYPES_PHP:
203 symbols_global_tags_loaded(GEANY_FILETYPES_HTML); /* load HTML global tags */
204 break;
209 GString *symbols_find_typenames_as_string(TMParserType lang, gboolean global)
211 guint j;
212 TMTag *tag;
213 GString *s = NULL;
214 GPtrArray *typedefs;
215 TMParserType tag_lang;
217 if (global)
218 typedefs = app->tm_workspace->global_typename_array;
219 else
220 typedefs = app->tm_workspace->typename_array;
222 if ((typedefs) && (typedefs->len > 0))
224 const gchar *last_name = "";
226 s = g_string_sized_new(typedefs->len * 10);
227 for (j = 0; j < typedefs->len; ++j)
229 tag = TM_TAG(typedefs->pdata[j]);
230 tag_lang = tag->lang;
232 if (tag->name && tm_tag_langs_compatible(lang, tag_lang) &&
233 strcmp(tag->name, last_name) != 0)
235 if (j != 0)
236 g_string_append_c(s, ' ');
237 g_string_append(s, tag->name);
238 last_name = tag->name;
242 return s;
246 /** Gets the context separator used by the tag manager for a particular file
247 * type.
248 * @param ft_id File type identifier.
249 * @return The context separator string.
251 * Returns non-printing sequence "\x03" ie ETX (end of text) for filetypes
252 * without a context separator.
254 * @since 0.19
256 GEANY_API_SYMBOL
257 const gchar *symbols_get_context_separator(gint ft_id)
259 return tm_tag_context_separator(filetypes[ft_id]->lang);
263 /* sort by name, then line */
264 static gint compare_symbol(const TMTag *tag_a, const TMTag *tag_b)
266 gint ret;
268 if (tag_a == NULL || tag_b == NULL)
269 return 0;
271 if (tag_a->name == NULL)
272 return -(tag_a->name != tag_b->name);
274 if (tag_b->name == NULL)
275 return tag_a->name != tag_b->name;
277 ret = strcmp(tag_a->name, tag_b->name);
278 if (ret == 0)
280 return tag_a->line - tag_b->line;
282 return ret;
286 /* sort by line, then scope */
287 static gint compare_symbol_lines(gconstpointer a, gconstpointer b)
289 const TMTag *tag_a = TM_TAG(a);
290 const TMTag *tag_b = TM_TAG(b);
291 gint ret;
293 if (a == NULL || b == NULL)
294 return 0;
296 ret = tag_a->line - tag_b->line;
297 if (ret == 0)
299 if (tag_a->scope == NULL)
300 return -(tag_a->scope != tag_b->scope);
301 if (tag_b->scope == NULL)
302 return tag_a->scope != tag_b->scope;
303 else
304 return strcmp(tag_a->scope, tag_b->scope);
306 return ret;
310 static GList *get_tag_list(GeanyDocument *doc, TMTagType tag_types)
312 GList *tag_names = NULL;
313 guint i;
315 g_return_val_if_fail(doc, NULL);
317 if (! doc->tm_file || ! doc->tm_file->tags_array)
318 return NULL;
320 for (i = 0; i < doc->tm_file->tags_array->len; ++i)
322 TMTag *tag = TM_TAG(doc->tm_file->tags_array->pdata[i]);
324 if (G_UNLIKELY(tag == NULL))
325 return NULL;
327 if (tag->type & tag_types)
329 tag_names = g_list_prepend(tag_names, tag);
332 tag_names = g_list_sort(tag_names, compare_symbol_lines);
333 return tag_names;
337 /* amount of types in the symbol list (currently max. 8 are used) */
338 #define MAX_SYMBOL_TYPES (sizeof(tv_iters) / sizeof(GtkTreeIter))
340 struct TreeviewSymbols
342 GtkTreeIter tag_function;
343 GtkTreeIter tag_class;
344 GtkTreeIter tag_macro;
345 GtkTreeIter tag_member;
346 GtkTreeIter tag_variable;
347 GtkTreeIter tag_externvar;
348 GtkTreeIter tag_namespace;
349 GtkTreeIter tag_struct;
350 GtkTreeIter tag_interface;
351 GtkTreeIter tag_type;
352 GtkTreeIter tag_other;
353 } tv_iters;
356 static void init_tag_iters(void)
358 /* init all GtkTreeIters with -1 to make them invalid to avoid crashes when switching between
359 * filetypes(e.g. config file to Python crashes Geany without this) */
360 tv_iters.tag_function.stamp = -1;
361 tv_iters.tag_class.stamp = -1;
362 tv_iters.tag_member.stamp = -1;
363 tv_iters.tag_macro.stamp = -1;
364 tv_iters.tag_variable.stamp = -1;
365 tv_iters.tag_externvar.stamp = -1;
366 tv_iters.tag_namespace.stamp = -1;
367 tv_iters.tag_struct.stamp = -1;
368 tv_iters.tag_interface.stamp = -1;
369 tv_iters.tag_type.stamp = -1;
370 tv_iters.tag_other.stamp = -1;
374 static GdkPixbuf *get_tag_icon(const gchar *icon_name)
376 static GtkIconTheme *icon_theme = NULL;
377 static gint x = -1;
379 if (G_UNLIKELY(x < 0))
381 gint dummy;
382 icon_theme = gtk_icon_theme_get_default();
383 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &x, &dummy);
385 return gtk_icon_theme_load_icon(icon_theme, icon_name, x, 0, NULL);
389 static gboolean find_toplevel_iter(GtkTreeStore *store, GtkTreeIter *iter, const gchar *title)
391 GtkTreeModel *model = GTK_TREE_MODEL(store);
393 if (!gtk_tree_model_get_iter_first(model, iter))
394 return FALSE;
397 gchar *candidate;
399 gtk_tree_model_get(model, iter, SYMBOLS_COLUMN_NAME, &candidate, -1);
400 /* FIXME: what if 2 different items have the same name?
401 * this should never happen, but might be caused by a typo in a translation */
402 if (utils_str_equal(candidate, title))
404 g_free(candidate);
405 return TRUE;
407 else
408 g_free(candidate);
410 while (gtk_tree_model_iter_next(model, iter));
412 return FALSE;
416 /* Adds symbol list groups in (iter*, title) pairs.
417 * The list must be ended with NULL. */
418 static void G_GNUC_NULL_TERMINATED
419 tag_list_add_groups(GtkTreeStore *tree_store, ...)
421 va_list args;
422 GtkTreeIter *iter;
424 g_return_if_fail(top_level_iter_names);
426 va_start(args, tree_store);
427 for (; iter = va_arg(args, GtkTreeIter*), iter != NULL;)
429 gchar *title = va_arg(args, gchar*);
430 guint icon_id = va_arg(args, guint);
431 GdkPixbuf *icon = NULL;
433 if (icon_id < N_ICONS)
434 icon = symbols_icons[icon_id].pixbuf;
436 g_assert(title != NULL);
437 g_ptr_array_add(top_level_iter_names, title);
439 if (!find_toplevel_iter(tree_store, iter, title))
440 gtk_tree_store_append(tree_store, iter, NULL);
442 if (icon)
443 gtk_tree_store_set(tree_store, iter, SYMBOLS_COLUMN_ICON, icon, -1);
444 gtk_tree_store_set(tree_store, iter, SYMBOLS_COLUMN_NAME, title, -1);
446 va_end(args);
450 static void add_top_level_items(GeanyDocument *doc)
452 GeanyFiletypeID ft_id = doc->file_type->id;
453 GtkTreeStore *tag_store = doc->priv->tag_store;
455 if (top_level_iter_names == NULL)
456 top_level_iter_names = g_ptr_array_new();
457 else
458 g_ptr_array_set_size(top_level_iter_names, 0);
460 init_tag_iters();
462 switch (ft_id)
464 case GEANY_FILETYPES_DIFF:
466 tag_list_add_groups(tag_store,
467 &(tv_iters.tag_function), _("Files"), ICON_NONE, NULL);
468 break;
470 case GEANY_FILETYPES_DOCBOOK:
472 tag_list_add_groups(tag_store,
473 &(tv_iters.tag_function), _("Chapter"), ICON_NONE,
474 &(tv_iters.tag_class), _("Section"), ICON_NONE,
475 &(tv_iters.tag_member), _("Sect1"), ICON_NONE,
476 &(tv_iters.tag_macro), _("Sect2"), ICON_NONE,
477 &(tv_iters.tag_variable), _("Sect3"), ICON_NONE,
478 &(tv_iters.tag_struct), _("Appendix"), ICON_NONE,
479 &(tv_iters.tag_other), _("Other"), ICON_NONE,
480 NULL);
481 break;
483 case GEANY_FILETYPES_HASKELL:
484 tag_list_add_groups(tag_store,
485 &tv_iters.tag_namespace, _("Module"), ICON_NONE,
486 &tv_iters.tag_type, _("Types"), ICON_NONE,
487 &tv_iters.tag_macro, _("Type constructors"), ICON_NONE,
488 &tv_iters.tag_function, _("Functions"), ICON_METHOD,
489 NULL);
490 break;
491 case GEANY_FILETYPES_COBOL:
492 tag_list_add_groups(tag_store,
493 &tv_iters.tag_class, _("Program"), ICON_CLASS,
494 &tv_iters.tag_function, _("File"), ICON_METHOD,
495 &tv_iters.tag_namespace, _("Sections"), ICON_NAMESPACE,
496 &tv_iters.tag_macro, _("Paragraph"), ICON_OTHER,
497 &tv_iters.tag_struct, _("Group"), ICON_STRUCT,
498 &tv_iters.tag_variable, _("Data"), ICON_VAR,
499 NULL);
500 break;
501 case GEANY_FILETYPES_CONF:
502 tag_list_add_groups(tag_store,
503 &tv_iters.tag_namespace, _("Sections"), ICON_OTHER,
504 &tv_iters.tag_macro, _("Keys"), ICON_VAR,
505 NULL);
506 break;
507 case GEANY_FILETYPES_NSIS:
508 tag_list_add_groups(tag_store,
509 &tv_iters.tag_namespace, _("Sections"), ICON_OTHER,
510 &tv_iters.tag_function, _("Functions"), ICON_METHOD,
511 &(tv_iters.tag_variable), _("Variables"), ICON_VAR,
512 NULL);
513 break;
514 case GEANY_FILETYPES_LATEX:
516 tag_list_add_groups(tag_store,
517 &(tv_iters.tag_function), _("Command"), ICON_NONE,
518 &(tv_iters.tag_class), _("Environment"), ICON_NONE,
519 &(tv_iters.tag_member), _("Section"), ICON_NONE,
520 &(tv_iters.tag_macro), _("Subsection"), ICON_NONE,
521 &(tv_iters.tag_variable), _("Subsubsection"), ICON_NONE,
522 &(tv_iters.tag_struct), _("Label"), ICON_NONE,
523 &(tv_iters.tag_namespace), _("Chapter"), ICON_NONE,
524 &(tv_iters.tag_other), _("Other"), ICON_NONE,
525 NULL);
526 break;
528 case GEANY_FILETYPES_MATLAB:
530 tag_list_add_groups(tag_store,
531 &(tv_iters.tag_function), _("Functions"), ICON_METHOD,
532 &(tv_iters.tag_struct), _("Structures"), ICON_STRUCT,
533 NULL);
534 break;
536 case GEANY_FILETYPES_ABAQUS:
538 tag_list_add_groups(tag_store,
539 &(tv_iters.tag_class), _("Parts"), ICON_NONE,
540 &(tv_iters.tag_member), _("Assembly"), ICON_NONE,
541 &(tv_iters.tag_namespace), _("Steps"), ICON_NONE,
542 NULL);
543 break;
545 case GEANY_FILETYPES_R:
547 tag_list_add_groups(tag_store,
548 &(tv_iters.tag_function), _("Functions"), ICON_METHOD,
549 &(tv_iters.tag_other), _("Other"), ICON_NONE,
550 NULL);
551 break;
553 case GEANY_FILETYPES_RUST:
555 tag_list_add_groups(tag_store,
556 &(tv_iters.tag_namespace), _("Modules"), ICON_NAMESPACE,
557 &(tv_iters.tag_struct), _("Structures"), ICON_STRUCT,
558 &(tv_iters.tag_interface), _("Traits"), ICON_CLASS,
559 &(tv_iters.tag_class), _("Implementations"), ICON_CLASS,
560 &(tv_iters.tag_function), _("Functions"), ICON_METHOD,
561 &(tv_iters.tag_type), _("Typedefs / Enums"), ICON_STRUCT,
562 &(tv_iters.tag_variable), _("Variables"), ICON_VAR,
563 &(tv_iters.tag_macro), _("Macros"), ICON_MACRO,
564 &(tv_iters.tag_member), _("Methods"), ICON_MEMBER,
565 &(tv_iters.tag_other), _("Other"), ICON_OTHER,
566 NULL);
567 break;
569 case GEANY_FILETYPES_GO:
571 tag_list_add_groups(tag_store,
572 &(tv_iters.tag_namespace), _("Package"), ICON_NAMESPACE,
573 &(tv_iters.tag_function), _("Functions"), ICON_METHOD,
574 &(tv_iters.tag_interface), _("Interfaces"), ICON_STRUCT,
575 &(tv_iters.tag_struct), _("Structs"), ICON_STRUCT,
576 &(tv_iters.tag_type), _("Types"), ICON_STRUCT,
577 &(tv_iters.tag_macro), _("Constants"), ICON_MACRO,
578 &(tv_iters.tag_variable), _("Variables"), ICON_VAR,
579 &(tv_iters.tag_member), _("Members"), ICON_MEMBER,
580 &(tv_iters.tag_other), _("Other"), ICON_OTHER,
581 NULL);
582 break;
584 case GEANY_FILETYPES_PERL:
586 tag_list_add_groups(tag_store,
587 &(tv_iters.tag_namespace), _("Package"), ICON_NAMESPACE,
588 &(tv_iters.tag_function), _("Functions"), ICON_METHOD,
589 &(tv_iters.tag_macro), _("Labels"), ICON_NONE,
590 &(tv_iters.tag_type), _("Constants"), ICON_NONE,
591 &(tv_iters.tag_other), _("Other"), ICON_OTHER,
592 NULL);
593 break;
595 case GEANY_FILETYPES_PHP:
596 case GEANY_FILETYPES_ZEPHIR:
598 tag_list_add_groups(tag_store,
599 &(tv_iters.tag_namespace), _("Namespaces"), ICON_NAMESPACE,
600 &(tv_iters.tag_interface), _("Interfaces"), ICON_STRUCT,
601 &(tv_iters.tag_class), _("Classes"), ICON_CLASS,
602 &(tv_iters.tag_function), _("Functions"), ICON_METHOD,
603 &(tv_iters.tag_macro), _("Constants"), ICON_MACRO,
604 &(tv_iters.tag_variable), _("Variables"), ICON_VAR,
605 &(tv_iters.tag_struct), _("Traits"), ICON_STRUCT,
606 NULL);
607 break;
609 case GEANY_FILETYPES_HTML:
611 tag_list_add_groups(tag_store,
612 &(tv_iters.tag_function), _("Functions"), ICON_NONE,
613 &(tv_iters.tag_member), _("Anchors"), ICON_NONE,
614 &(tv_iters.tag_namespace), _("H1 Headings"), ICON_NONE,
615 &(tv_iters.tag_class), _("H2 Headings"), ICON_NONE,
616 &(tv_iters.tag_variable), _("H3 Headings"), ICON_NONE,
617 NULL);
618 break;
620 case GEANY_FILETYPES_CSS:
622 tag_list_add_groups(tag_store,
623 &(tv_iters.tag_class), _("Classes"), ICON_CLASS,
624 &(tv_iters.tag_variable), _("ID Selectors"), ICON_VAR,
625 &(tv_iters.tag_struct), _("Type Selectors"), ICON_STRUCT, NULL);
626 break;
628 case GEANY_FILETYPES_REST:
629 case GEANY_FILETYPES_TXT2TAGS:
630 case GEANY_FILETYPES_ABC:
632 tag_list_add_groups(tag_store,
633 &(tv_iters.tag_namespace), _("Chapter"), ICON_NONE,
634 &(tv_iters.tag_member), _("Section"), ICON_NONE,
635 &(tv_iters.tag_macro), _("Subsection"), ICON_NONE,
636 &(tv_iters.tag_variable), _("Subsubsection"), ICON_NONE,
637 NULL);
638 break;
640 case GEANY_FILETYPES_ASCIIDOC:
642 tag_list_add_groups(tag_store,
643 &(tv_iters.tag_namespace), _("Document"), ICON_NONE,
644 &(tv_iters.tag_member), _("Section Level 1"), ICON_NONE,
645 &(tv_iters.tag_macro), _("Section Level 2"), ICON_NONE,
646 &(tv_iters.tag_variable), _("Section Level 3"), ICON_NONE,
647 &(tv_iters.tag_struct), _("Section Level 4"), ICON_NONE,
648 NULL);
649 break;
651 case GEANY_FILETYPES_RUBY:
653 tag_list_add_groups(tag_store,
654 &(tv_iters.tag_namespace), _("Modules"), ICON_NAMESPACE,
655 &(tv_iters.tag_class), _("Classes"), ICON_CLASS,
656 &(tv_iters.tag_member), _("Singletons"), ICON_STRUCT,
657 &(tv_iters.tag_function), _("Methods"), ICON_METHOD,
658 NULL);
659 break;
661 case GEANY_FILETYPES_TCL:
663 tag_list_add_groups(tag_store,
664 &(tv_iters.tag_namespace), _("Namespaces"), ICON_NAMESPACE,
665 &(tv_iters.tag_class), _("Classes"), ICON_CLASS,
666 &(tv_iters.tag_member), _("Methods"), ICON_METHOD,
667 &(tv_iters.tag_function), _("Procedures"), ICON_OTHER,
668 NULL);
669 break;
671 case GEANY_FILETYPES_PYTHON:
673 tag_list_add_groups(tag_store,
674 &(tv_iters.tag_class), _("Classes"), ICON_CLASS,
675 &(tv_iters.tag_member), _("Methods"), ICON_MACRO,
676 &(tv_iters.tag_function), _("Functions"), ICON_METHOD,
677 &(tv_iters.tag_variable), _("Variables"), ICON_VAR,
678 &(tv_iters.tag_externvar), _("Imports"), ICON_NAMESPACE,
679 NULL);
680 break;
682 case GEANY_FILETYPES_VHDL:
684 tag_list_add_groups(tag_store,
685 &(tv_iters.tag_namespace), _("Package"), ICON_NAMESPACE,
686 &(tv_iters.tag_class), _("Entities"), ICON_CLASS,
687 &(tv_iters.tag_struct), _("Architectures"), ICON_STRUCT,
688 &(tv_iters.tag_type), _("Types"), ICON_OTHER,
689 &(tv_iters.tag_function), _("Functions / Procedures"), ICON_METHOD,
690 &(tv_iters.tag_variable), _("Variables / Signals"), ICON_VAR,
691 &(tv_iters.tag_member), _("Processes / Blocks / Components"), ICON_MEMBER,
692 &(tv_iters.tag_other), _("Other"), ICON_OTHER,
693 NULL);
694 break;
696 case GEANY_FILETYPES_VERILOG:
698 tag_list_add_groups(tag_store,
699 &(tv_iters.tag_type), _("Events"), ICON_MACRO,
700 &(tv_iters.tag_class), _("Modules"), ICON_CLASS,
701 &(tv_iters.tag_function), _("Functions / Tasks"), ICON_METHOD,
702 &(tv_iters.tag_variable), _("Variables"), ICON_VAR,
703 &(tv_iters.tag_other), _("Other"), ICON_OTHER,
704 NULL);
705 break;
707 case GEANY_FILETYPES_JAVA:
709 tag_list_add_groups(tag_store,
710 &(tv_iters.tag_namespace), _("Package"), ICON_NAMESPACE,
711 &(tv_iters.tag_interface), _("Interfaces"), ICON_STRUCT,
712 &(tv_iters.tag_class), _("Classes"), ICON_CLASS,
713 &(tv_iters.tag_function), _("Methods"), ICON_METHOD,
714 &(tv_iters.tag_member), _("Members"), ICON_MEMBER,
715 &(tv_iters.tag_type), _("Enums"), ICON_STRUCT,
716 &(tv_iters.tag_other), _("Other"), ICON_OTHER,
717 NULL);
718 break;
720 case GEANY_FILETYPES_AS:
722 tag_list_add_groups(tag_store,
723 &(tv_iters.tag_namespace), _("Package"), ICON_NAMESPACE,
724 &(tv_iters.tag_interface), _("Interfaces"), ICON_STRUCT,
725 &(tv_iters.tag_class), _("Classes"), ICON_CLASS,
726 &(tv_iters.tag_function), _("Functions"), ICON_METHOD,
727 &(tv_iters.tag_member), _("Properties"), ICON_MEMBER,
728 &(tv_iters.tag_variable), _("Variables"), ICON_VAR,
729 &(tv_iters.tag_macro), _("Constants"), ICON_MACRO,
730 &(tv_iters.tag_other), _("Other"), ICON_OTHER,
731 NULL);
732 break;
734 case GEANY_FILETYPES_HAXE:
736 tag_list_add_groups(tag_store,
737 &(tv_iters.tag_interface), _("Interfaces"), ICON_STRUCT,
738 &(tv_iters.tag_class), _("Classes"), ICON_CLASS,
739 &(tv_iters.tag_function), _("Methods"), ICON_METHOD,
740 &(tv_iters.tag_type), _("Types"), ICON_MACRO,
741 &(tv_iters.tag_variable), _("Variables"), ICON_VAR,
742 &(tv_iters.tag_other), _("Other"), ICON_OTHER,
743 NULL);
744 break;
746 case GEANY_FILETYPES_BASIC:
748 tag_list_add_groups(tag_store,
749 &(tv_iters.tag_function), _("Functions"), ICON_METHOD,
750 &(tv_iters.tag_variable), _("Variables"), ICON_VAR,
751 &(tv_iters.tag_macro), _("Constants"), ICON_MACRO,
752 &(tv_iters.tag_struct), _("Types"), ICON_NAMESPACE,
753 &(tv_iters.tag_namespace), _("Labels"), ICON_MEMBER,
754 &(tv_iters.tag_other), _("Other"), ICON_OTHER,
755 NULL);
756 break;
758 case GEANY_FILETYPES_F77:
759 case GEANY_FILETYPES_FORTRAN:
761 tag_list_add_groups(tag_store,
762 &(tv_iters.tag_namespace), _("Module"), ICON_CLASS,
763 &(tv_iters.tag_struct), _("Programs"), ICON_CLASS,
764 &(tv_iters.tag_interface), _("Interfaces"), ICON_STRUCT,
765 &(tv_iters.tag_function), _("Functions / Subroutines"), ICON_METHOD,
766 &(tv_iters.tag_variable), _("Variables"), ICON_VAR,
767 &(tv_iters.tag_class), _("Types"), ICON_CLASS,
768 &(tv_iters.tag_member), _("Components"), ICON_MEMBER,
769 &(tv_iters.tag_macro), _("Blocks"), ICON_MEMBER,
770 &(tv_iters.tag_type), _("Enums"), ICON_STRUCT,
771 &(tv_iters.tag_other), _("Other"), ICON_OTHER,
772 NULL);
773 break;
775 case GEANY_FILETYPES_ASM:
777 tag_list_add_groups(tag_store,
778 &(tv_iters.tag_namespace), _("Labels"), ICON_NAMESPACE,
779 &(tv_iters.tag_function), _("Macros"), ICON_METHOD,
780 &(tv_iters.tag_macro), _("Defines"), ICON_MACRO,
781 &(tv_iters.tag_struct), _("Types"), ICON_STRUCT,
782 NULL);
783 break;
785 case GEANY_FILETYPES_MAKE:
786 tag_list_add_groups(tag_store,
787 &tv_iters.tag_function, _("Targets"), ICON_METHOD,
788 &tv_iters.tag_macro, _("Macros"), ICON_MACRO,
789 NULL);
790 break;
791 case GEANY_FILETYPES_SQL:
793 tag_list_add_groups(tag_store,
794 &(tv_iters.tag_function), _("Functions"), ICON_METHOD,
795 &(tv_iters.tag_namespace), _("Procedures"), ICON_NAMESPACE,
796 &(tv_iters.tag_struct), _("Indexes"), ICON_STRUCT,
797 &(tv_iters.tag_class), _("Tables"), ICON_CLASS,
798 &(tv_iters.tag_macro), _("Triggers"), ICON_MACRO,
799 &(tv_iters.tag_member), _("Views"), ICON_VAR,
800 &(tv_iters.tag_other), _("Other"), ICON_OTHER,
801 &(tv_iters.tag_variable), _("Variables"), ICON_VAR,
802 NULL);
803 break;
805 case GEANY_FILETYPES_D:
806 default:
808 if (ft_id == GEANY_FILETYPES_D)
809 tag_list_add_groups(tag_store,
810 &(tv_iters.tag_namespace), _("Module"), ICON_NONE, NULL);
811 else
812 tag_list_add_groups(tag_store,
813 &(tv_iters.tag_namespace), _("Namespaces"), ICON_NAMESPACE, NULL);
815 tag_list_add_groups(tag_store,
816 &(tv_iters.tag_class), _("Classes"), ICON_CLASS,
817 &(tv_iters.tag_interface), _("Interfaces"), ICON_STRUCT,
818 &(tv_iters.tag_function), _("Functions"), ICON_METHOD,
819 &(tv_iters.tag_member), _("Members"), ICON_MEMBER,
820 &(tv_iters.tag_struct), _("Structs"), ICON_STRUCT,
821 &(tv_iters.tag_type), _("Typedefs / Enums"), ICON_STRUCT,
822 NULL);
824 if (ft_id != GEANY_FILETYPES_D)
826 tag_list_add_groups(tag_store,
827 &(tv_iters.tag_macro), _("Macros"), ICON_MACRO, NULL);
829 tag_list_add_groups(tag_store,
830 &(tv_iters.tag_variable), _("Variables"), ICON_VAR,
831 &(tv_iters.tag_externvar), _("Extern Variables"), ICON_VAR,
832 &(tv_iters.tag_other), _("Other"), ICON_OTHER, NULL);
838 /* removes toplevel items that have no children */
839 static void hide_empty_rows(GtkTreeStore *store)
841 GtkTreeIter iter;
842 gboolean cont = TRUE;
844 if (! gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter))
845 return; /* stop when first iter is invalid, i.e. no elements */
847 while (cont)
849 if (! gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store), &iter))
850 cont = gtk_tree_store_remove(store, &iter);
851 else
852 cont = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
857 static const gchar *get_symbol_name(GeanyDocument *doc, const TMTag *tag, gboolean found_parent)
859 gchar *utf8_name;
860 const gchar *scope = tag->scope;
861 static GString *buffer = NULL; /* buffer will be small so we can keep it for reuse */
862 gboolean doc_is_utf8 = FALSE;
864 /* encodings_convert_to_utf8_from_charset() fails with charset "None", so skip conversion
865 * for None at this point completely */
866 if (utils_str_equal(doc->encoding, "UTF-8") ||
867 utils_str_equal(doc->encoding, "None"))
868 doc_is_utf8 = TRUE;
869 else /* normally the tags will always be in UTF-8 since we parse from our buffer, but a
870 * plugin might have called tm_source_file_update(), so check to be sure */
871 doc_is_utf8 = g_utf8_validate(tag->name, -1, NULL);
873 if (! doc_is_utf8)
874 utf8_name = encodings_convert_to_utf8_from_charset(tag->name,
875 -1, doc->encoding, TRUE);
876 else
877 utf8_name = tag->name;
879 if (utf8_name == NULL)
880 return NULL;
882 if (! buffer)
883 buffer = g_string_new(NULL);
884 else
885 g_string_truncate(buffer, 0);
887 /* check first char of scope is a wordchar */
888 if (!found_parent && scope &&
889 strpbrk(scope, GEANY_WORDCHARS) == scope)
891 const gchar *sep = symbols_get_context_separator(doc->file_type->id);
893 g_string_append(buffer, scope);
894 g_string_append(buffer, sep);
896 g_string_append(buffer, utf8_name);
898 if (! doc_is_utf8)
899 g_free(utf8_name);
901 g_string_append_printf(buffer, " [%lu]", tag->line);
903 return buffer->str;
907 static gchar *get_symbol_tooltip(GeanyDocument *doc, const TMTag *tag)
909 gchar *utf8_name = editor_get_calltip_text(doc->editor, tag);
911 /* encodings_convert_to_utf8_from_charset() fails with charset "None", so skip conversion
912 * for None at this point completely */
913 if (utf8_name != NULL &&
914 ! utils_str_equal(doc->encoding, "UTF-8") &&
915 ! utils_str_equal(doc->encoding, "None"))
917 SETPTR(utf8_name,
918 encodings_convert_to_utf8_from_charset(utf8_name, -1, doc->encoding, TRUE));
921 return utf8_name;
925 /* find the last word in "foo::bar::blah", e.g. "blah" */
926 static const gchar *get_parent_name(const TMTag *tag, GeanyFiletypeID ft_id)
928 const gchar *scope = tag->scope;
929 const gchar *separator = symbols_get_context_separator(ft_id);
930 const gchar *str, *ptr;
932 if (!scope)
933 return NULL;
935 str = scope;
937 while (1)
939 ptr = strstr(str, separator);
940 if (ptr)
942 str = ptr + strlen(separator);
944 else
945 break;
948 return !EMPTY(str) ? str : NULL;
952 static GtkTreeIter *get_tag_type_iter(TMTagType tag_type)
954 GtkTreeIter *iter = NULL;
956 switch (tag_type)
958 case tm_tag_prototype_t:
959 case tm_tag_method_t:
960 case tm_tag_function_t:
962 iter = &tv_iters.tag_function;
963 break;
965 case tm_tag_externvar_t:
967 iter = &tv_iters.tag_externvar;
968 break;
970 case tm_tag_macro_t:
971 case tm_tag_macro_with_arg_t:
973 iter = &tv_iters.tag_macro;
974 break;
976 case tm_tag_class_t:
978 iter = &tv_iters.tag_class;
979 break;
981 case tm_tag_member_t:
982 case tm_tag_field_t:
984 iter = &tv_iters.tag_member;
985 break;
987 case tm_tag_typedef_t:
988 case tm_tag_enum_t:
990 iter = &tv_iters.tag_type;
991 break;
993 case tm_tag_union_t:
994 case tm_tag_struct_t:
996 iter = &tv_iters.tag_struct;
997 break;
999 case tm_tag_interface_t:
1000 iter = &tv_iters.tag_interface;
1001 break;
1002 case tm_tag_variable_t:
1004 iter = &tv_iters.tag_variable;
1005 break;
1007 case tm_tag_namespace_t:
1008 case tm_tag_package_t:
1010 iter = &tv_iters.tag_namespace;
1011 break;
1013 default:
1015 iter = &tv_iters.tag_other;
1018 if (G_LIKELY(iter->stamp != -1))
1019 return iter;
1020 else
1021 return NULL;
1025 static GdkPixbuf *get_child_icon(GtkTreeStore *tree_store, GtkTreeIter *parent)
1027 GdkPixbuf *icon = NULL;
1029 if (parent == &tv_iters.tag_other)
1031 return g_object_ref(symbols_icons[ICON_VAR].pixbuf);
1033 /* copy parent icon */
1034 gtk_tree_model_get(GTK_TREE_MODEL(tree_store), parent,
1035 SYMBOLS_COLUMN_ICON, &icon, -1);
1036 return icon;
1040 static gboolean tag_equal(gconstpointer v1, gconstpointer v2)
1042 const TMTag *t1 = v1;
1043 const TMTag *t2 = v2;
1045 return (t1->type == t2->type && strcmp(t1->name, t2->name) == 0 &&
1046 utils_str_equal(t1->scope, t2->scope) &&
1047 /* include arglist in match to support e.g. C++ overloading */
1048 utils_str_equal(t1->arglist, t2->arglist));
1052 /* inspired from g_str_hash() */
1053 static guint tag_hash(gconstpointer v)
1055 const TMTag *tag = v;
1056 const gchar *p;
1057 guint32 h = 5381;
1059 h = (h << 5) + h + tag->type;
1060 for (p = tag->name; *p != '\0'; p++)
1061 h = (h << 5) + h + *p;
1062 if (tag->scope)
1064 for (p = tag->scope; *p != '\0'; p++)
1065 h = (h << 5) + h + *p;
1067 /* for e.g. C++ overloading */
1068 if (tag->arglist)
1070 for (p = tag->arglist; *p != '\0'; p++)
1071 h = (h << 5) + h + *p;
1074 return h;
1078 /* like gtk_tree_view_expand_to_path() but with an iter */
1079 static void tree_view_expand_to_iter(GtkTreeView *view, GtkTreeIter *iter)
1081 GtkTreeModel *model = gtk_tree_view_get_model(view);
1082 GtkTreePath *path = gtk_tree_model_get_path(model, iter);
1084 gtk_tree_view_expand_to_path(view, path);
1085 gtk_tree_path_free(path);
1089 /* like gtk_tree_store_remove() but finds the next iter at any level */
1090 static gboolean tree_store_remove_row(GtkTreeStore *store, GtkTreeIter *iter)
1092 GtkTreeIter parent;
1093 gboolean has_parent;
1094 gboolean cont;
1096 has_parent = gtk_tree_model_iter_parent(GTK_TREE_MODEL(store), &parent, iter);
1097 cont = gtk_tree_store_remove(store, iter);
1098 /* if there is no next at this level but there is a parent iter, continue from it */
1099 if (! cont && has_parent)
1101 *iter = parent;
1102 cont = ui_tree_model_iter_any_next(GTK_TREE_MODEL(store), iter, FALSE);
1105 return cont;
1109 static gint tree_search_func(gconstpointer key, gpointer user_data)
1111 TreeSearchData *data = user_data;
1112 gint parent_line = GPOINTER_TO_INT(key);
1113 gboolean new_nearest;
1115 if (data->found_line == -1)
1116 data->found_line = parent_line; /* initial value */
1118 new_nearest = ABS(data->line - parent_line) < ABS(data->line - data->found_line);
1120 if (parent_line > data->line)
1122 if (new_nearest && !data->lower)
1123 data->found_line = parent_line;
1124 return -1;
1127 if (new_nearest)
1128 data->found_line = parent_line;
1130 if (parent_line < data->line)
1131 return 1;
1133 return 0;
1137 static gint tree_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
1139 return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
1143 static void parents_table_tree_value_free(gpointer data)
1145 g_slice_free(GtkTreeIter, data);
1149 /* adds a new element in the parent table if its key is known. */
1150 static void update_parents_table(GHashTable *table, const TMTag *tag, const gchar *parent_name,
1151 const GtkTreeIter *iter)
1153 GTree *tree;
1154 if (g_hash_table_lookup_extended(table, tag->name, NULL, (gpointer *) &tree) &&
1155 ! utils_str_equal(parent_name, tag->name) /* prevent Foo::Foo from making parent = child */)
1157 if (!tree)
1159 tree = g_tree_new_full(tree_cmp, NULL, NULL, parents_table_tree_value_free);
1160 g_hash_table_insert(table, tag->name, tree);
1163 g_tree_insert(tree, GINT_TO_POINTER(tag->line), g_slice_dup(GtkTreeIter, iter));
1168 static GtkTreeIter *parents_table_lookup(GHashTable *table, const gchar *name, guint line)
1170 GtkTreeIter *parent_search = NULL;
1171 GTree *tree;
1173 tree = g_hash_table_lookup(table, name);
1174 if (tree)
1176 TreeSearchData user_data = {-1, line, TRUE};
1178 /* search parent candidates for the one with the nearest
1179 * line number which is lower than the tag's line number */
1180 g_tree_search(tree, (GCompareFunc)tree_search_func, &user_data);
1181 parent_search = g_tree_lookup(tree, GINT_TO_POINTER(user_data.found_line));
1184 return parent_search;
1188 static void parents_table_value_free(gpointer data)
1190 GTree *tree = data;
1191 if (tree)
1192 g_tree_destroy(tree);
1196 /* inserts a @data in @table on key @tag.
1197 * previous data is not overwritten if the key is duplicated, but rather the
1198 * two values are kept in a list
1200 * table is: GHashTable<TMTag, GTree<line_num, GList<GList<TMTag>>>> */
1201 static void tags_table_insert(GHashTable *table, TMTag *tag, GList *data)
1203 GTree *tree = g_hash_table_lookup(table, tag);
1204 if (!tree)
1206 tree = g_tree_new_full(tree_cmp, NULL, NULL, NULL);
1207 g_hash_table_insert(table, tag, tree);
1209 GList *list = g_tree_lookup(tree, GINT_TO_POINTER(tag->line));
1210 list = g_list_prepend(list, data);
1211 g_tree_insert(tree, GINT_TO_POINTER(tag->line), list);
1215 /* looks up the entry in @table that best matches @tag.
1216 * if there is more than one candidate, the one that has closest line position to @tag is chosen */
1217 static GList *tags_table_lookup(GHashTable *table, TMTag *tag)
1219 TreeSearchData user_data = {-1, tag->line, FALSE};
1220 GTree *tree = g_hash_table_lookup(table, tag);
1222 if (tree)
1224 GList *list;
1226 g_tree_search(tree, (GCompareFunc)tree_search_func, &user_data);
1227 list = g_tree_lookup(tree, GINT_TO_POINTER(user_data.found_line));
1228 /* return the first value in the list - we don't care which of the
1229 * tags with identical names defined on the same line we get */
1230 if (list)
1231 return list->data;
1233 return NULL;
1237 /* removes the element at @tag from @table.
1238 * @tag must be the exact pointer used at insertion time */
1239 static void tags_table_remove(GHashTable *table, TMTag *tag)
1241 GTree *tree = g_hash_table_lookup(table, tag);
1242 if (tree)
1244 GList *list = g_tree_lookup(tree, GINT_TO_POINTER(tag->line));
1245 if (list)
1247 GList *node;
1248 /* should always be the first element as we returned the first one in
1249 * tags_table_lookup() */
1250 foreach_list(node, list)
1252 if (((GList *) node->data)->data == tag)
1253 break;
1255 list = g_list_delete_link(list, node);
1256 if (!list)
1257 g_tree_remove(tree, GINT_TO_POINTER(tag->line));
1258 else
1259 g_tree_insert(tree, GINT_TO_POINTER(tag->line), list);
1265 static gboolean tags_table_tree_value_free(gpointer key, gpointer value, gpointer data)
1267 GList *list = value;
1268 g_list_free(list);
1269 return FALSE;
1273 static void tags_table_value_free(gpointer data)
1275 GTree *tree = data;
1276 if (tree)
1278 /* free any leftover elements. note that we can't register a value_free_func when
1279 * creating the tree because we only want to free it when destroying the tree,
1280 * not when inserting a duplicate (we handle this manually) */
1281 g_tree_foreach(tree, tags_table_tree_value_free, NULL);
1282 g_tree_destroy(tree);
1288 * Updates the tag tree for a document with the tags in *list.
1289 * @param doc a document
1290 * @param tags a pointer to a GList* holding the tags to add/update. This
1291 * list may be updated, removing updated elements.
1293 * The update is done in two passes:
1294 * 1) walking the current tree, update tags that still exist and remove the
1295 * obsolescent ones;
1296 * 2) walking the remaining (non updated) tags, adds them in the list.
1298 * For better performances, we use 2 hash tables:
1299 * - one containing all the tags for lookup in the first pass (actually stores a
1300 * reference in the tags list for removing it efficiently), avoiding list search
1301 * on each tag;
1302 * - the other holding "tag-name":row references for tags having children, used to
1303 * lookup for a parent in both passes, avoiding tree traversal.
1305 static void update_tree_tags(GeanyDocument *doc, GList **tags)
1307 GtkTreeStore *store = doc->priv->tag_store;
1308 GtkTreeModel *model = GTK_TREE_MODEL(store);
1309 GHashTable *parents_table;
1310 GHashTable *tags_table;
1311 GtkTreeIter iter;
1312 gboolean cont;
1313 GList *item;
1315 /* Build hash tables holding tags and parents */
1316 /* parent table is GHashTable<tag_name, GTree<line_num, GtkTreeIter>> */
1317 parents_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, parents_table_value_free);
1318 /* tags table is another representation of the @tags list,
1319 * GHashTable<TMTag, GTree<line_num, GList<GList<TMTag>>>> */
1320 tags_table = g_hash_table_new_full(tag_hash, tag_equal, NULL, tags_table_value_free);
1321 foreach_list(item, *tags)
1323 TMTag *tag = item->data;
1324 const gchar *name;
1326 tags_table_insert(tags_table, tag, item);
1328 name = get_parent_name(tag, doc->file_type->id);
1329 if (name)
1330 g_hash_table_insert(parents_table, (gpointer) name, NULL);
1333 /* First pass, update existing rows or delete them.
1334 * It is OK to delete them since we walk top down so we would remove
1335 * parents before checking for their children, thus never implicitly
1336 * deleting an updated child */
1337 cont = gtk_tree_model_get_iter_first(model, &iter);
1338 while (cont)
1340 TMTag *tag;
1342 gtk_tree_model_get(model, &iter, SYMBOLS_COLUMN_TAG, &tag, -1);
1343 if (! tag) /* most probably a toplevel, skip it */
1344 cont = ui_tree_model_iter_any_next(model, &iter, TRUE);
1345 else
1347 GList *found_item;
1349 found_item = tags_table_lookup(tags_table, tag);
1350 if (! found_item) /* tag doesn't exist, remove it */
1351 cont = tree_store_remove_row(store, &iter);
1352 else /* tag still exist, update it */
1354 const gchar *parent_name;
1355 TMTag *found = found_item->data;
1357 parent_name = get_parent_name(found, doc->file_type->id);
1358 /* if parent is unknown, ignore it */
1359 if (parent_name && ! g_hash_table_lookup(parents_table, parent_name))
1360 parent_name = NULL;
1362 if (!tm_tags_equal(tag, found))
1364 const gchar *name;
1365 gchar *tooltip;
1367 /* only update fields that (can) have changed (name that holds line
1368 * number, tooltip, and the tag itself) */
1369 name = get_symbol_name(doc, found, parent_name != NULL);
1370 tooltip = get_symbol_tooltip(doc, found);
1371 gtk_tree_store_set(store, &iter,
1372 SYMBOLS_COLUMN_NAME, name,
1373 SYMBOLS_COLUMN_TOOLTIP, tooltip,
1374 SYMBOLS_COLUMN_TAG, found,
1375 -1);
1376 g_free(tooltip);
1379 update_parents_table(parents_table, found, parent_name, &iter);
1381 /* remove the updated tag from the table and list */
1382 tags_table_remove(tags_table, found);
1383 *tags = g_list_delete_link(*tags, found_item);
1385 cont = ui_tree_model_iter_any_next(model, &iter, TRUE);
1388 tm_tag_unref(tag);
1392 /* Second pass, now we have a tree cleaned up from invalid rows,
1393 * we simply add new ones */
1394 foreach_list (item, *tags)
1396 TMTag *tag = item->data;
1397 GtkTreeIter *parent;
1399 parent = get_tag_type_iter(tag->type);
1400 if (G_UNLIKELY(! parent))
1401 geany_debug("Missing symbol-tree parent iter for type %d!", tag->type);
1402 else
1404 gboolean expand;
1405 const gchar *name;
1406 const gchar *parent_name;
1407 gchar *tooltip;
1408 GdkPixbuf *icon = get_child_icon(store, parent);
1410 parent_name = get_parent_name(tag, doc->file_type->id);
1411 if (parent_name)
1413 GtkTreeIter *parent_search = parents_table_lookup(parents_table, parent_name, tag->line);
1415 if (parent_search)
1416 parent = parent_search;
1417 else
1418 parent_name = NULL;
1421 /* only expand to the iter if the parent was empty, otherwise we let the
1422 * folding as it was before (already expanded, or closed by the user) */
1423 expand = ! gtk_tree_model_iter_has_child(model, parent);
1425 /* insert the new element */
1426 name = get_symbol_name(doc, tag, parent_name != NULL);
1427 tooltip = get_symbol_tooltip(doc, tag);
1428 gtk_tree_store_insert_with_values(store, &iter, parent, 0,
1429 SYMBOLS_COLUMN_NAME, name,
1430 SYMBOLS_COLUMN_TOOLTIP, tooltip,
1431 SYMBOLS_COLUMN_ICON, icon,
1432 SYMBOLS_COLUMN_TAG, tag,
1433 -1);
1434 g_free(tooltip);
1435 if (G_LIKELY(icon))
1436 g_object_unref(icon);
1438 update_parents_table(parents_table, tag, parent_name, &iter);
1440 if (expand)
1441 tree_view_expand_to_iter(GTK_TREE_VIEW(doc->priv->tag_tree), &iter);
1445 g_hash_table_destroy(parents_table);
1446 g_hash_table_destroy(tags_table);
1450 /* we don't want to sort 1st-level nodes, but we can't return 0 because the tree sort
1451 * is not stable, so the order is already lost. */
1452 static gint compare_top_level_names(const gchar *a, const gchar *b)
1454 guint i;
1455 const gchar *name;
1457 /* This should never happen as it would mean that two or more top
1458 * level items have the same name but it can happen by typos in the translations. */
1459 if (utils_str_equal(a, b))
1460 return 1;
1462 foreach_ptr_array(name, i, top_level_iter_names)
1464 if (utils_str_equal(name, a))
1465 return -1;
1466 if (utils_str_equal(name, b))
1467 return 1;
1469 g_warning("Couldn't find top level node '%s' or '%s'!", a, b);
1470 return 0;
1474 static gboolean tag_has_missing_parent(const TMTag *tag, GtkTreeStore *store,
1475 GtkTreeIter *iter)
1477 /* if the tag has a parent tag, it should be at depth >= 2 */
1478 return !EMPTY(tag->scope) &&
1479 gtk_tree_store_iter_depth(store, iter) == 1;
1483 static gint tree_sort_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b,
1484 gpointer user_data)
1486 gboolean sort_by_name = GPOINTER_TO_INT(user_data);
1487 TMTag *tag_a, *tag_b;
1488 gint cmp;
1490 gtk_tree_model_get(model, a, SYMBOLS_COLUMN_TAG, &tag_a, -1);
1491 gtk_tree_model_get(model, b, SYMBOLS_COLUMN_TAG, &tag_b, -1);
1493 /* Check if the iters can be sorted based on tag name and line, not tree item name.
1494 * Sort by tree name if the scope was prepended, e.g. 'ScopeNameWithNoTag::TagName'. */
1495 if (tag_a && !tag_has_missing_parent(tag_a, GTK_TREE_STORE(model), a) &&
1496 tag_b && !tag_has_missing_parent(tag_b, GTK_TREE_STORE(model), b))
1498 cmp = sort_by_name ? compare_symbol(tag_a, tag_b) :
1499 compare_symbol_lines(tag_a, tag_b);
1501 else
1503 gchar *astr, *bstr;
1505 gtk_tree_model_get(model, a, SYMBOLS_COLUMN_NAME, &astr, -1);
1506 gtk_tree_model_get(model, b, SYMBOLS_COLUMN_NAME, &bstr, -1);
1508 /* if a is toplevel, b must be also */
1509 if (gtk_tree_store_iter_depth(GTK_TREE_STORE(model), a) == 0)
1511 cmp = compare_top_level_names(astr, bstr);
1513 else
1515 /* this is what g_strcmp0() does */
1516 if (! astr)
1517 cmp = -(astr != bstr);
1518 else if (! bstr)
1519 cmp = astr != bstr;
1520 else
1522 cmp = strcmp(astr, bstr);
1524 /* sort duplicate 'ScopeName::OverloadedTagName' items by line as well */
1525 if (tag_a && tag_b)
1526 if (!sort_by_name ||
1527 (utils_str_equal(tag_a->name, tag_b->name) &&
1528 utils_str_equal(tag_a->scope, tag_b->scope)))
1529 cmp = compare_symbol_lines(tag_a, tag_b);
1532 g_free(astr);
1533 g_free(bstr);
1535 tm_tag_unref(tag_a);
1536 tm_tag_unref(tag_b);
1538 return cmp;
1542 static void sort_tree(GtkTreeStore *store, gboolean sort_by_name)
1544 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), SYMBOLS_COLUMN_NAME, tree_sort_func,
1545 GINT_TO_POINTER(sort_by_name), NULL);
1547 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), SYMBOLS_COLUMN_NAME, GTK_SORT_ASCENDING);
1551 gboolean symbols_recreate_tag_list(GeanyDocument *doc, gint sort_mode)
1553 GList *tags;
1555 g_return_val_if_fail(DOC_VALID(doc), FALSE);
1557 tags = get_tag_list(doc, tm_tag_max_t);
1558 if (tags == NULL)
1559 return FALSE;
1561 /* FIXME: Not sure why we detached the model here? */
1563 /* disable sorting during update because the code doesn't support correctly
1564 * models that are currently being built */
1565 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(doc->priv->tag_store), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, 0);
1567 /* add grandparent type iters */
1568 add_top_level_items(doc);
1570 update_tree_tags(doc, &tags);
1571 g_list_free(tags);
1573 hide_empty_rows(doc->priv->tag_store);
1575 if (sort_mode == SYMBOLS_SORT_USE_PREVIOUS)
1576 sort_mode = doc->priv->symbol_list_sort_mode;
1578 sort_tree(doc->priv->tag_store, sort_mode == SYMBOLS_SORT_BY_NAME);
1579 doc->priv->symbol_list_sort_mode = sort_mode;
1581 return TRUE;
1585 /* Detects a global tags filetype from the *.lang.* language extension.
1586 * Returns NULL if there was no matching TM language. */
1587 static GeanyFiletype *detect_global_tags_filetype(const gchar *utf8_filename)
1589 gchar *tags_ext;
1590 gchar *shortname = utils_strdupa(utf8_filename);
1591 GeanyFiletype *ft = NULL;
1593 tags_ext = g_strrstr(shortname, ".tags");
1594 if (tags_ext)
1596 *tags_ext = '\0'; /* remove .tags extension */
1597 ft = filetypes_detect_from_extension(shortname);
1598 if (ft->id != GEANY_FILETYPES_NONE)
1599 return ft;
1601 return NULL;
1605 /* Adapted from anjuta-2.0.2/global-tags/tm_global_tags.c, thanks.
1606 * Needs full paths for filenames, except for C/C++ tag files, when CFLAGS includes
1607 * the relevant path.
1608 * Example:
1609 * CFLAGS=-I/home/user/libname-1.x geany -g libname.d.tags libname.h */
1610 int symbols_generate_global_tags(int argc, char **argv, gboolean want_preprocess)
1612 /* -E pre-process, -dD output user macros, -p prof info (?) */
1613 const char pre_process[] = "gcc -E -dD -p -I.";
1615 if (argc > 2)
1617 /* Create global taglist */
1618 int status;
1619 char *command;
1620 const char *tags_file = argv[1];
1621 char *utf8_fname;
1622 GeanyFiletype *ft;
1624 utf8_fname = utils_get_utf8_from_locale(tags_file);
1625 ft = detect_global_tags_filetype(utf8_fname);
1626 g_free(utf8_fname);
1628 if (ft == NULL)
1630 g_printerr(_("Unknown filetype extension for \"%s\".\n"), tags_file);
1631 return 1;
1633 /* load config in case of custom filetypes */
1634 filetypes_load_config(ft->id, FALSE);
1636 /* load ignore list for C/C++ parser */
1637 if (ft->id == GEANY_FILETYPES_C || ft->id == GEANY_FILETYPES_CPP)
1638 load_c_ignore_tags();
1640 if (want_preprocess && (ft->id == GEANY_FILETYPES_C || ft->id == GEANY_FILETYPES_CPP))
1642 const gchar *cflags = getenv("CFLAGS");
1643 command = g_strdup_printf("%s %s", pre_process, FALLBACK(cflags, ""));
1645 else
1646 command = NULL; /* don't preprocess */
1648 geany_debug("Generating %s tags file.", ft->name);
1649 tm_get_workspace();
1650 status = tm_workspace_create_global_tags(command, (const char **) (argv + 2),
1651 argc - 2, tags_file, ft->lang);
1652 g_free(command);
1653 symbols_finalize(); /* free c_tags_ignore data */
1654 if (! status)
1656 g_printerr(_("Failed to create tags file, perhaps because no symbols "
1657 "were found.\n"));
1658 return 1;
1661 else
1663 g_printerr(_("Usage: %s -g <Tags File> <File list>\n\n"), argv[0]);
1664 g_printerr(_("Example:\n"
1665 "CFLAGS=`pkg-config gtk+-2.0 --cflags` %s -g gtk2.c.tags"
1666 " /usr/include/gtk-2.0/gtk/gtk.h\n"), argv[0]);
1667 return 1;
1669 return 0;
1673 void symbols_show_load_tags_dialog(void)
1675 GtkWidget *dialog;
1676 GtkFileFilter *filter;
1678 dialog = gtk_file_chooser_dialog_new(_("Load Tags File"), GTK_WINDOW(main_widgets.window),
1679 GTK_FILE_CHOOSER_ACTION_OPEN,
1680 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1681 GTK_STOCK_OPEN, GTK_RESPONSE_OK,
1682 NULL);
1683 gtk_widget_set_name(dialog, "GeanyDialog");
1684 filter = gtk_file_filter_new();
1685 gtk_file_filter_set_name(filter, _("Geany tags file (*.*.tags)"));
1686 gtk_file_filter_add_pattern(filter, "*.*.tags");
1687 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
1689 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
1691 GSList *flist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1692 GSList *item;
1694 for (item = flist; item != NULL; item = g_slist_next(item))
1696 gchar *fname = item->data;
1697 gchar *utf8_fname;
1698 GeanyFiletype *ft;
1700 utf8_fname = utils_get_utf8_from_locale(fname);
1701 ft = detect_global_tags_filetype(utf8_fname);
1703 if (ft != NULL && symbols_load_global_tags(fname, ft))
1704 /* For translators: the first wildcard is the filetype, the second the filename */
1705 ui_set_statusbar(TRUE, _("Loaded %s tags file '%s'."),
1706 filetypes_get_display_name(ft), utf8_fname);
1707 else
1708 ui_set_statusbar(TRUE, _("Could not load tags file '%s'."), utf8_fname);
1710 g_free(utf8_fname);
1711 g_free(fname);
1713 g_slist_free(flist);
1715 gtk_widget_destroy(dialog);
1719 static void init_user_tags(void)
1721 GSList *file_list = NULL, *list = NULL;
1722 const GSList *node;
1723 gchar *dir;
1725 dir = g_build_filename(app->configdir, GEANY_TAGS_SUBDIR, NULL);
1726 /* create the user tags dir for next time if it doesn't exist */
1727 if (! g_file_test(dir, G_FILE_TEST_IS_DIR))
1728 utils_mkdir(dir, FALSE);
1729 file_list = utils_get_file_list_full(dir, TRUE, FALSE, NULL);
1731 SETPTR(dir, g_build_filename(app->datadir, GEANY_TAGS_SUBDIR, NULL));
1732 list = utils_get_file_list_full(dir, TRUE, FALSE, NULL);
1733 g_free(dir);
1735 file_list = g_slist_concat(file_list, list);
1737 /* populate the filetype-specific tag files lists */
1738 for (node = file_list; node != NULL; node = node->next)
1740 gchar *fname = node->data;
1741 gchar *utf8_fname = utils_get_utf8_from_locale(fname);
1742 GeanyFiletype *ft = detect_global_tags_filetype(utf8_fname);
1744 g_free(utf8_fname);
1746 if (FILETYPE_ID(ft) != GEANY_FILETYPES_NONE)
1747 ft->priv->tag_files = g_slist_prepend(ft->priv->tag_files, fname);
1748 else
1750 geany_debug("Unknown filetype for file '%s'.", fname);
1751 g_free(fname);
1755 /* don't need to delete list contents because they are now stored in
1756 * ft->priv->tag_files */
1757 g_slist_free(file_list);
1761 static void load_user_tags(GeanyFiletypeID ft_id)
1763 static guchar *tags_loaded = NULL;
1764 static gboolean init_tags = FALSE;
1765 const GSList *node;
1766 GeanyFiletype *ft = filetypes[ft_id];
1768 g_return_if_fail(ft_id > 0);
1770 if (!tags_loaded)
1771 tags_loaded = g_new0(guchar, filetypes_array->len);
1772 if (tags_loaded[ft_id])
1773 return;
1774 tags_loaded[ft_id] = TRUE; /* prevent reloading */
1776 if (!init_tags)
1778 init_user_tags();
1779 init_tags = TRUE;
1782 for (node = ft->priv->tag_files; node != NULL; node = g_slist_next(node))
1784 const gchar *fname = node->data;
1786 symbols_load_global_tags(fname, ft);
1791 static void on_goto_popup_item_activate(GtkMenuItem *item, TMTag *tag)
1793 GeanyDocument *new_doc, *old_doc;
1795 g_return_if_fail(tag);
1797 old_doc = document_get_current();
1798 new_doc = document_open_file(tag->file->file_name, FALSE, NULL, NULL);
1800 if (new_doc)
1801 navqueue_goto_line(old_doc, new_doc, tag->line);
1805 /* FIXME: use the same icons as in the symbols tree defined in add_top_level_items() */
1806 static guint get_tag_class(const TMTag *tag)
1808 switch (tag->type)
1810 case tm_tag_prototype_t:
1811 case tm_tag_method_t:
1812 case tm_tag_function_t:
1813 return ICON_METHOD;
1814 case tm_tag_variable_t:
1815 case tm_tag_externvar_t:
1816 return ICON_VAR;
1817 case tm_tag_macro_t:
1818 case tm_tag_macro_with_arg_t:
1819 return ICON_MACRO;
1820 case tm_tag_class_t:
1821 return ICON_CLASS;
1822 case tm_tag_member_t:
1823 case tm_tag_field_t:
1824 return ICON_MEMBER;
1825 case tm_tag_typedef_t:
1826 case tm_tag_enum_t:
1827 case tm_tag_union_t:
1828 case tm_tag_struct_t:
1829 return ICON_STRUCT;
1830 case tm_tag_namespace_t:
1831 case tm_tag_package_t:
1832 return ICON_NAMESPACE;
1833 default:
1834 break;
1836 return ICON_STRUCT;
1840 /* positions a popup at the caret from the ScintillaObject in @p data */
1841 static void goto_popup_position_func(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
1843 gint line_height;
1844 GdkScreen *screen = gtk_widget_get_screen(GTK_WIDGET(menu));
1845 gint monitor_num;
1846 GdkRectangle monitor;
1847 GtkRequisition req;
1848 GdkEventButton *event_button = g_object_get_data(G_OBJECT(menu), "geany-button-event");
1850 if (event_button)
1852 /* if we got a mouse click, popup at that position */
1853 *x = (gint) event_button->x_root;
1854 *y = (gint) event_button->y_root;
1855 line_height = 0; /* we don't want to offset below the line or anything */
1857 else /* keyboard positioning */
1859 ScintillaObject *sci = data;
1860 GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(sci));
1861 gint pos = sci_get_current_position(sci);
1862 gint line = sci_get_line_from_position(sci, pos);
1863 gint pos_x = scintilla_send_message(sci, SCI_POINTXFROMPOSITION, 0, pos);
1864 gint pos_y = scintilla_send_message(sci, SCI_POINTYFROMPOSITION, 0, pos);
1866 line_height = scintilla_send_message(sci, SCI_TEXTHEIGHT, line, 0);
1868 gdk_window_get_origin(window, x, y);
1869 *x += pos_x;
1870 *y += pos_y;
1873 monitor_num = gdk_screen_get_monitor_at_point(screen, *x, *y);
1875 #if GTK_CHECK_VERSION(3, 0, 0)
1876 gtk_widget_get_preferred_size(GTK_WIDGET(menu), NULL, &req);
1877 #else
1878 gtk_widget_size_request(GTK_WIDGET(menu), &req);
1879 #endif
1881 #if GTK_CHECK_VERSION(3, 4, 0)
1882 gdk_screen_get_monitor_workarea(screen, monitor_num, &monitor);
1883 #else
1884 gdk_screen_get_monitor_geometry(screen, monitor_num, &monitor);
1885 #endif
1887 /* put on one size of the X position, but within the monitor */
1888 if (gtk_widget_get_direction(GTK_WIDGET(menu)) == GTK_TEXT_DIR_RTL)
1890 if (*x - req.width - 1 >= monitor.x)
1891 *x -= req.width + 1;
1892 else if (*x + req.width > monitor.x + monitor.width)
1893 *x = monitor.x;
1894 else
1895 *x += 1;
1897 else
1899 if (*x + req.width + 1 <= monitor.x + monitor.width)
1900 *x = MAX(monitor.x, *x + 1);
1901 else if (*x - req.width - 1 >= monitor.x)
1902 *x -= req.width + 1;
1903 else
1904 *x = monitor.x + MAX(0, monitor.width - req.width);
1907 /* try to put, in order:
1908 * 1. below the Y position, under the line
1909 * 2. above the Y position
1910 * 3. within the monitor */
1911 if (*y + line_height + req.height <= monitor.y + monitor.height)
1912 *y = MAX(monitor.y, *y + line_height);
1913 else if (*y - req.height >= monitor.y)
1914 *y = *y - req.height;
1915 else
1916 *y = monitor.y + MAX(0, monitor.height - req.height);
1918 *push_in = FALSE;
1922 static void show_goto_popup(GeanyDocument *doc, GPtrArray *tags, gboolean have_best)
1924 GtkWidget *first = NULL;
1925 GtkWidget *menu;
1926 GdkEvent *event;
1927 GdkEventButton *button_event = NULL;
1928 TMTag *tmtag;
1929 guint i;
1931 menu = gtk_menu_new();
1933 foreach_ptr_array(tmtag, i, tags)
1935 GtkWidget *item;
1936 GtkWidget *label;
1937 GtkWidget *image;
1938 gchar *fname = g_path_get_basename(tmtag->file->file_name);
1939 gchar *text;
1941 if (! first && have_best)
1942 /* For translators: it's the filename and line number of a symbol in the goto-symbol popup menu */
1943 text = g_markup_printf_escaped(_("<b>%s: %lu</b>"), fname, tmtag->line);
1944 else
1945 /* For translators: it's the filename and line number of a symbol in the goto-symbol popup menu */
1946 text = g_markup_printf_escaped(_("%s: %lu"), fname, tmtag->line);
1948 image = gtk_image_new_from_pixbuf(symbols_icons[get_tag_class(tmtag)].pixbuf);
1949 label = g_object_new(GTK_TYPE_LABEL, "label", text, "use-markup", TRUE, "xalign", 0.0, NULL);
1950 item = g_object_new(GTK_TYPE_IMAGE_MENU_ITEM, "image", image, "child", label, NULL);
1951 g_signal_connect_data(item, "activate", G_CALLBACK(on_goto_popup_item_activate),
1952 tm_tag_ref(tmtag), (GClosureNotify) tm_tag_unref, 0);
1953 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1955 if (! first)
1956 first = item;
1958 g_free(text);
1959 g_free(fname);
1962 gtk_widget_show_all(menu);
1964 if (first) /* always select the first item for better keyboard navigation */
1965 g_signal_connect(menu, "realize", G_CALLBACK(gtk_menu_shell_select_item), first);
1967 event = gtk_get_current_event();
1968 if (event && event->type == GDK_BUTTON_PRESS)
1969 button_event = (GdkEventButton *) event;
1970 else
1971 gdk_event_free(event);
1973 g_object_set_data_full(G_OBJECT(menu), "geany-button-event", button_event,
1974 button_event ? (GDestroyNotify) gdk_event_free : NULL);
1975 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, goto_popup_position_func, doc->editor->sci,
1976 button_event ? button_event->button : 0, gtk_get_current_event_time ());
1980 static gint compare_tags_by_name_line(gconstpointer ptr1, gconstpointer ptr2)
1982 gint res;
1983 TMTag *t1 = *((TMTag **) ptr1);
1984 TMTag *t2 = *((TMTag **) ptr2);
1986 res = g_strcmp0(t1->file->short_name, t2->file->short_name);
1987 if (res != 0)
1988 return res;
1989 return t1->line - t2->line;
1993 static TMTag *find_best_goto_tag(GeanyDocument *doc, GPtrArray *tags)
1995 TMTag *tag;
1996 guint i;
1998 /* first check if we have a tag in the current file */
1999 foreach_ptr_array(tag, i, tags)
2001 if (g_strcmp0(doc->real_path, tag->file->file_name) == 0)
2002 return tag;
2005 /* next check if we have a tag for some of the open documents */
2006 foreach_ptr_array(tag, i, tags)
2008 guint j;
2010 foreach_document(j)
2012 if (g_strcmp0(documents[j]->real_path, tag->file->file_name) == 0)
2013 return tag;
2017 /* next check if we have a tag for a file inside the current document's directory */
2018 foreach_ptr_array(tag, i, tags)
2020 gchar *dir = g_path_get_dirname(doc->real_path);
2022 if (g_str_has_prefix(tag->file->file_name, dir))
2024 g_free(dir);
2025 return tag;
2027 g_free(dir);
2030 return NULL;
2034 static GPtrArray *filter_tags(GPtrArray *tags, TMTag *current_tag, gboolean definition)
2036 const TMTagType forward_types = tm_tag_prototype_t | tm_tag_externvar_t;
2037 TMTag *tmtag, *last_tag = NULL;
2038 GPtrArray *filtered_tags = g_ptr_array_new();
2039 guint i;
2041 foreach_ptr_array(tmtag, i, tags)
2043 if ((definition && !(tmtag->type & forward_types)) ||
2044 (!definition && (tmtag->type & forward_types)))
2046 /* If there are typedefs of e.g. a struct such as
2047 * "typedef struct Foo {} Foo;", filter out the typedef unless
2048 * cursor is at the struct name. */
2049 if (last_tag != NULL && last_tag->file == tmtag->file &&
2050 last_tag->type != tm_tag_typedef_t && tmtag->type == tm_tag_typedef_t)
2052 if (last_tag == current_tag)
2053 g_ptr_array_add(filtered_tags, tmtag);
2055 else if (tmtag != current_tag)
2056 g_ptr_array_add(filtered_tags, tmtag);
2058 last_tag = tmtag;
2062 return filtered_tags;
2066 static gboolean goto_tag(const gchar *name, gboolean definition)
2068 const TMTagType forward_types = tm_tag_prototype_t | tm_tag_externvar_t;
2069 TMTag *tmtag, *current_tag = NULL;
2070 GeanyDocument *old_doc = document_get_current();
2071 gboolean found = FALSE;
2072 const GPtrArray *all_tags;
2073 GPtrArray *tags, *filtered_tags;
2074 guint i;
2075 guint current_line = sci_get_current_line(old_doc->editor->sci) + 1;
2077 all_tags = tm_workspace_find(name, NULL, tm_tag_max_t, NULL, old_doc->file_type->lang);
2079 /* get rid of global tags and find tag at current line */
2080 tags = g_ptr_array_new();
2081 foreach_ptr_array(tmtag, i, all_tags)
2083 if (tmtag->file)
2085 g_ptr_array_add(tags, tmtag);
2086 if (tmtag->file == old_doc->tm_file && tmtag->line == current_line)
2087 current_tag = tmtag;
2091 if (current_tag)
2092 /* swap definition/declaration search */
2093 definition = current_tag->type & forward_types;
2095 filtered_tags = filter_tags(tags, current_tag, definition);
2096 if (filtered_tags->len == 0)
2098 /* if we didn't find anything, try again with the opposite type */
2099 g_ptr_array_free(filtered_tags, TRUE);
2100 filtered_tags = filter_tags(tags, current_tag, !definition);
2102 g_ptr_array_free(tags, TRUE);
2103 tags = filtered_tags;
2105 if (tags->len == 1)
2107 GeanyDocument *new_doc;
2109 tmtag = tags->pdata[0];
2110 new_doc = document_find_by_real_path(tmtag->file->file_name);
2112 if (!new_doc)
2113 /* not found in opened document, should open */
2114 new_doc = document_open_file(tmtag->file->file_name, FALSE, NULL, NULL);
2116 navqueue_goto_line(old_doc, new_doc, tmtag->line);
2118 else if (tags->len > 1)
2120 GPtrArray *tag_list;
2121 TMTag *tag, *best_tag;
2123 g_ptr_array_sort(tags, compare_tags_by_name_line);
2124 best_tag = find_best_goto_tag(old_doc, tags);
2126 tag_list = g_ptr_array_new();
2127 if (best_tag)
2128 g_ptr_array_add(tag_list, best_tag);
2129 foreach_ptr_array(tag, i, tags)
2131 if (tag != best_tag)
2132 g_ptr_array_add(tag_list, tag);
2134 show_goto_popup(old_doc, tag_list, best_tag != NULL);
2136 g_ptr_array_free(tag_list, TRUE);
2139 found = tags->len > 0;
2140 g_ptr_array_free(tags, TRUE);
2142 return found;
2146 gboolean symbols_goto_tag(const gchar *name, gboolean definition)
2148 if (goto_tag(name, definition))
2149 return TRUE;
2151 /* if we are here, there was no match and we are beeping ;-) */
2152 utils_beep();
2154 if (!definition)
2155 ui_set_statusbar(FALSE, _("Forward declaration \"%s\" not found."), name);
2156 else
2157 ui_set_statusbar(FALSE, _("Definition of \"%s\" not found."), name);
2158 return FALSE;
2162 /* This could perhaps be improved to check for #if, class etc. */
2163 static gint get_function_fold_number(GeanyDocument *doc)
2165 /* for Java the functions are always one fold level above the class scope */
2166 if (doc->file_type->id == GEANY_FILETYPES_JAVA)
2167 return SC_FOLDLEVELBASE + 1;
2168 else
2169 return SC_FOLDLEVELBASE;
2173 /* Should be used only with get_current_tag_cached.
2174 * tag_types caching might trigger recomputation too often but this isn't used differently often
2175 * enough to be an issue for now */
2176 static gboolean current_tag_changed(GeanyDocument *doc, gint cur_line, gint fold_level, guint tag_types)
2178 static gint old_line = -2;
2179 static GeanyDocument *old_doc = NULL;
2180 static gint old_fold_num = -1;
2181 static guint old_tag_types = 0;
2182 const gint fold_num = fold_level & SC_FOLDLEVELNUMBERMASK;
2183 gboolean ret;
2185 /* check if the cached line and file index have changed since last time: */
2186 if (doc == NULL || doc != old_doc || old_tag_types != tag_types)
2187 ret = TRUE;
2188 else if (cur_line == old_line)
2189 ret = FALSE;
2190 else
2192 /* if the line has only changed by 1 */
2193 if (abs(cur_line - old_line) == 1)
2195 /* It's the same function if the fold number hasn't changed */
2196 ret = (fold_num != old_fold_num);
2198 else ret = TRUE;
2201 /* record current line and file index for next time */
2202 old_line = cur_line;
2203 old_doc = doc;
2204 old_fold_num = fold_num;
2205 old_tag_types = tag_types;
2206 return ret;
2210 /* Parse the function name up to 2 lines before tag_line.
2211 * C++ like syntax should be parsed by parse_cpp_function_at_line, otherwise the return
2212 * type or argument names can be confused with the function name. */
2213 static gchar *parse_function_at_line(ScintillaObject *sci, gint tag_line)
2215 gint start, end, max_pos;
2216 gint fn_style;
2218 switch (sci_get_lexer(sci))
2220 case SCLEX_RUBY: fn_style = SCE_RB_DEFNAME; break;
2221 case SCLEX_PYTHON: fn_style = SCE_P_DEFNAME; break;
2222 default: fn_style = SCE_C_IDENTIFIER; /* several lexers use SCE_C_IDENTIFIER */
2224 start = sci_get_position_from_line(sci, tag_line - 2);
2225 max_pos = sci_get_position_from_line(sci, tag_line + 1);
2226 while (start < max_pos && sci_get_style_at(sci, start) != fn_style)
2227 start++;
2229 end = start;
2230 while (end < max_pos && sci_get_style_at(sci, end) == fn_style)
2231 end++;
2233 if (start == end)
2234 return NULL;
2235 return sci_get_contents_range(sci, start, end);
2239 /* Parse the function name */
2240 static gchar *parse_cpp_function_at_line(ScintillaObject *sci, gint tag_line)
2242 gint start, end, first_pos, max_pos;
2243 gint tmp;
2244 gchar c;
2246 first_pos = end = sci_get_position_from_line(sci, tag_line);
2247 max_pos = sci_get_position_from_line(sci, tag_line + 1);
2248 tmp = 0;
2249 /* goto the begin of function body */
2250 while (end < max_pos &&
2251 (tmp = sci_get_char_at(sci, end)) != '{' &&
2252 tmp != 0) end++;
2253 if (tmp == 0) end --;
2255 /* go back to the end of function identifier */
2256 while (end > 0 && end > first_pos - 500 &&
2257 (tmp = sci_get_char_at(sci, end)) != '(' &&
2258 tmp != 0) end--;
2259 end--;
2260 if (end < 0) end = 0;
2262 /* skip whitespaces between identifier and ( */
2263 while (end > 0 && isspace(sci_get_char_at(sci, end))) end--;
2265 start = end;
2266 /* Use tmp to find SCE_C_IDENTIFIER or SCE_C_GLOBALCLASS chars */
2267 while (start >= 0 && ((tmp = sci_get_style_at(sci, start)) == SCE_C_IDENTIFIER
2268 || tmp == SCE_C_GLOBALCLASS
2269 || (c = sci_get_char_at(sci, start)) == '~'
2270 || c == ':'))
2271 start--;
2272 if (start != 0 && start < end) start++; /* correct for last non-matching char */
2274 if (start == end) return NULL;
2275 return sci_get_contents_range(sci, start, end + 1);
2279 /* gets the fold header after or on @line, but skipping folds created because of parentheses */
2280 static gint get_fold_header_after(ScintillaObject *sci, gint line)
2282 const gint line_count = sci_get_line_count(sci);
2284 for (; line < line_count; line++)
2286 if (sci_get_fold_level(sci, line) & SC_FOLDLEVELHEADERFLAG)
2288 const gint last_child = scintilla_send_message(sci, SCI_GETLASTCHILD, line, -1);
2289 const gint line_end = sci_get_line_end_position(sci, line);
2290 const gint lexer = sci_get_lexer(sci);
2291 gint parenthesis_match_line = -1;
2293 /* now find any unbalanced open parenthesis on the line and see where the matching
2294 * brace would be, mimicking what folding on () does */
2295 for (gint pos = sci_get_position_from_line(sci, line); pos < line_end; pos++)
2297 if (highlighting_is_code_style(lexer, sci_get_style_at(sci, pos)) &&
2298 sci_get_char_at(sci, pos) == '(')
2300 const gint matching = sci_find_matching_brace(sci, pos);
2302 if (matching >= 0)
2304 parenthesis_match_line = sci_get_line_from_position(sci, matching);
2305 if (parenthesis_match_line != line)
2306 break; /* match is on a different line, we found a possible fold */
2307 else
2308 pos = matching; /* just skip the range and continue searching */
2313 /* if the matching parenthesis matches the fold level, skip it and continue.
2314 * it matches if it either spans the same lines, or spans one more but the next one is
2315 * a fold header (in which case the last child of the fold is one less to let the
2316 * header be at the parent level) */
2317 if ((parenthesis_match_line == last_child) ||
2318 (parenthesis_match_line == last_child + 1 &&
2319 sci_get_fold_level(sci, parenthesis_match_line) & SC_FOLDLEVELHEADERFLAG))
2320 line = last_child;
2321 else
2322 return line;
2326 return -1;
2330 static gint get_current_tag_name(GeanyDocument *doc, gchar **tagname, TMTagType tag_types)
2332 gint line;
2333 gint parent;
2335 line = sci_get_current_line(doc->editor->sci);
2336 parent = sci_get_fold_parent(doc->editor->sci, line);
2337 /* if we're inside a fold level and we have up-to-date tags, get the function from TM */
2338 if (parent >= 0 && doc->tm_file != NULL && doc->tm_file->tags_array != NULL &&
2339 (! doc->changed || editor_prefs.autocompletion_update_freq > 0))
2341 const TMTag *tag = tm_get_current_tag(doc->tm_file->tags_array, parent + 1, tag_types);
2343 if (tag)
2345 gint tag_line = tag->line - 1;
2346 gint last_child = line + 1;
2348 /* if it may be a false positive because we're inside a fold level not inside anything
2349 * we match, e.g. a #if in C or C++, we check we're inside the fold level that start
2350 * right after the tag we got from TM.
2351 * Additionally, we perform parentheses matching on the initial line not to get confused
2352 * by folding on () in case the parameter list spans multiple lines */
2353 if (abs(tag_line - parent) > 1)
2355 const gint tag_fold = get_fold_header_after(doc->editor->sci, tag_line);
2356 if (tag_fold >= 0)
2357 last_child = scintilla_send_message(doc->editor->sci, SCI_GETLASTCHILD, tag_fold, -1);
2360 if (line <= last_child)
2362 if (tag->scope)
2363 *tagname = g_strconcat(tag->scope,
2364 symbols_get_context_separator(doc->file_type->id), tag->name, NULL);
2365 else
2366 *tagname = g_strdup(tag->name);
2368 return tag_line;
2372 /* for the poor guy with a modified document and without real time tag parsing, we fallback
2373 * to dirty and inaccurate hand-parsing */
2374 else if (parent >= 0 && doc->file_type != NULL && doc->file_type->id != GEANY_FILETYPES_NONE)
2376 const gint fn_fold = get_function_fold_number(doc);
2377 gint tag_line = parent;
2378 gint fold_level = sci_get_fold_level(doc->editor->sci, tag_line);
2380 /* find the top level fold point */
2381 while (tag_line >= 0 && (fold_level & SC_FOLDLEVELNUMBERMASK) != fn_fold)
2383 tag_line = sci_get_fold_parent(doc->editor->sci, tag_line);
2384 fold_level = sci_get_fold_level(doc->editor->sci, tag_line);
2387 if (tag_line >= 0)
2389 gchar *cur_tag;
2391 if (sci_get_lexer(doc->editor->sci) == SCLEX_CPP)
2392 cur_tag = parse_cpp_function_at_line(doc->editor->sci, tag_line);
2393 else
2394 cur_tag = parse_function_at_line(doc->editor->sci, tag_line);
2396 if (cur_tag != NULL)
2398 *tagname = cur_tag;
2399 return tag_line;
2404 *tagname = g_strdup(_("unknown"));
2405 return -1;
2409 static gint get_current_tag_name_cached(GeanyDocument *doc, const gchar **tagname, TMTagType tag_types)
2411 static gint tag_line = -1;
2412 static gchar *cur_tag = NULL;
2414 g_return_val_if_fail(doc == NULL || doc->is_valid, -1);
2416 if (doc == NULL) /* reset current function */
2418 current_tag_changed(NULL, -1, -1, 0);
2419 g_free(cur_tag);
2420 cur_tag = g_strdup(_("unknown"));
2421 if (tagname != NULL)
2422 *tagname = cur_tag;
2423 tag_line = -1;
2425 else
2427 gint line = sci_get_current_line(doc->editor->sci);
2428 gint fold_level = sci_get_fold_level(doc->editor->sci, line);
2430 if (current_tag_changed(doc, line, fold_level, tag_types))
2432 g_free(cur_tag);
2433 tag_line = get_current_tag_name(doc, &cur_tag, tag_types);
2435 *tagname = cur_tag;
2438 return tag_line;
2442 /* Sets *tagname to point at the current function or tag name.
2443 * If doc is NULL, reset the cached current tag data to ensure it will be reparsed on the next
2444 * call to this function.
2445 * Returns: line number of the current tag, or -1 if unknown. */
2446 gint symbols_get_current_function(GeanyDocument *doc, const gchar **tagname)
2448 return get_current_tag_name_cached(doc, tagname, tm_tag_function_t | tm_tag_method_t);
2452 /* same as symbols_get_current_function() but finds class, namespaces and more */
2453 gint symbols_get_current_scope(GeanyDocument *doc, const gchar **tagname)
2455 TMTagType tag_types = (tm_tag_function_t | tm_tag_method_t | tm_tag_class_t |
2456 tm_tag_struct_t | tm_tag_enum_t | tm_tag_union_t);
2458 /* Python parser reports imports as namespaces which confuses the scope detection */
2459 if (doc && doc->file_type->lang != filetypes[GEANY_FILETYPES_PYTHON]->lang)
2460 tag_types |= tm_tag_namespace_t;
2462 return get_current_tag_name_cached(doc, tagname, tag_types);
2466 static void on_symbol_tree_sort_clicked(GtkMenuItem *menuitem, gpointer user_data)
2468 gint sort_mode = GPOINTER_TO_INT(user_data);
2469 GeanyDocument *doc = document_get_current();
2471 if (ignore_callback)
2472 return;
2474 if (doc != NULL)
2475 doc->has_tags = symbols_recreate_tag_list(doc, sort_mode);
2479 static void on_symbol_tree_menu_show(GtkWidget *widget,
2480 gpointer user_data)
2482 GeanyDocument *doc = document_get_current();
2483 gboolean enable;
2485 enable = doc && doc->has_tags;
2486 gtk_widget_set_sensitive(symbol_menu.sort_by_name, enable);
2487 gtk_widget_set_sensitive(symbol_menu.sort_by_appearance, enable);
2488 gtk_widget_set_sensitive(symbol_menu.expand_all, enable);
2489 gtk_widget_set_sensitive(symbol_menu.collapse_all, enable);
2490 gtk_widget_set_sensitive(symbol_menu.find_usage, enable);
2491 gtk_widget_set_sensitive(symbol_menu.find_doc_usage, enable);
2493 if (! doc)
2494 return;
2496 ignore_callback = TRUE;
2498 if (doc->priv->symbol_list_sort_mode == SYMBOLS_SORT_BY_NAME)
2499 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(symbol_menu.sort_by_name), TRUE);
2500 else
2501 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(symbol_menu.sort_by_appearance), TRUE);
2503 ignore_callback = FALSE;
2507 static void on_expand_collapse(GtkWidget *widget, gpointer user_data)
2509 gboolean expand = GPOINTER_TO_INT(user_data);
2510 GeanyDocument *doc = document_get_current();
2512 if (! doc)
2513 return;
2515 g_return_if_fail(doc->priv->tag_tree);
2517 if (expand)
2518 gtk_tree_view_expand_all(GTK_TREE_VIEW(doc->priv->tag_tree));
2519 else
2520 gtk_tree_view_collapse_all(GTK_TREE_VIEW(doc->priv->tag_tree));
2524 static void on_find_usage(GtkWidget *widget, G_GNUC_UNUSED gpointer unused)
2526 GtkTreeIter iter;
2527 GtkTreeSelection *selection;
2528 GtkTreeModel *model;
2529 GeanyDocument *doc;
2530 TMTag *tag = NULL;
2532 doc = document_get_current();
2533 if (!doc)
2534 return;
2536 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(doc->priv->tag_tree));
2537 if (gtk_tree_selection_get_selected(selection, &model, &iter))
2538 gtk_tree_model_get(model, &iter, SYMBOLS_COLUMN_TAG, &tag, -1);
2539 if (tag)
2541 if (widget == symbol_menu.find_in_files)
2542 search_show_find_in_files_dialog_full(tag->name, NULL);
2543 else
2544 search_find_usage(tag->name, tag->name, GEANY_FIND_WHOLEWORD | GEANY_FIND_MATCHCASE,
2545 widget == symbol_menu.find_usage);
2547 tm_tag_unref(tag);
2552 static void create_taglist_popup_menu(void)
2554 GtkWidget *item, *menu;
2556 tv.popup_taglist = menu = gtk_menu_new();
2558 symbol_menu.expand_all = item = ui_image_menu_item_new(GTK_STOCK_ADD, _("_Expand All"));
2559 gtk_widget_show(item);
2560 gtk_container_add(GTK_CONTAINER(menu), item);
2561 g_signal_connect(item, "activate", G_CALLBACK(on_expand_collapse), GINT_TO_POINTER(TRUE));
2563 symbol_menu.collapse_all = item = ui_image_menu_item_new(GTK_STOCK_REMOVE, _("_Collapse All"));
2564 gtk_widget_show(item);
2565 gtk_container_add(GTK_CONTAINER(menu), item);
2566 g_signal_connect(item, "activate", G_CALLBACK(on_expand_collapse), GINT_TO_POINTER(FALSE));
2568 item = gtk_separator_menu_item_new();
2569 gtk_widget_show(item);
2570 gtk_container_add(GTK_CONTAINER(menu), item);
2572 symbol_menu.sort_by_name = item = gtk_radio_menu_item_new_with_mnemonic(NULL,
2573 _("Sort by _Name"));
2574 gtk_widget_show(item);
2575 gtk_container_add(GTK_CONTAINER(menu), item);
2576 g_signal_connect(item, "activate", G_CALLBACK(on_symbol_tree_sort_clicked),
2577 GINT_TO_POINTER(SYMBOLS_SORT_BY_NAME));
2579 symbol_menu.sort_by_appearance = item = gtk_radio_menu_item_new_with_mnemonic_from_widget(
2580 GTK_RADIO_MENU_ITEM(item), _("Sort by _Appearance"));
2581 gtk_widget_show(item);
2582 gtk_container_add(GTK_CONTAINER(menu), item);
2583 g_signal_connect(item, "activate", G_CALLBACK(on_symbol_tree_sort_clicked),
2584 GINT_TO_POINTER(SYMBOLS_SORT_BY_APPEARANCE));
2586 item = gtk_separator_menu_item_new();
2587 gtk_widget_show(item);
2588 gtk_container_add(GTK_CONTAINER(menu), item);
2590 symbol_menu.find_usage = item = ui_image_menu_item_new(GTK_STOCK_FIND, _("Find _Usage"));
2591 gtk_widget_show(item);
2592 gtk_container_add(GTK_CONTAINER(menu), item);
2593 g_signal_connect(item, "activate", G_CALLBACK(on_find_usage), symbol_menu.find_usage);
2595 symbol_menu.find_doc_usage = item = ui_image_menu_item_new(GTK_STOCK_FIND, _("Find _Document Usage"));
2596 gtk_widget_show(item);
2597 gtk_container_add(GTK_CONTAINER(menu), item);
2598 g_signal_connect(item, "activate", G_CALLBACK(on_find_usage), symbol_menu.find_doc_usage);
2600 symbol_menu.find_in_files = item = ui_image_menu_item_new(GTK_STOCK_FIND, _("Find in F_iles..."));
2601 gtk_widget_show(item);
2602 gtk_container_add(GTK_CONTAINER(menu), item);
2603 g_signal_connect(item, "activate", G_CALLBACK(on_find_usage), NULL);
2605 g_signal_connect(menu, "show", G_CALLBACK(on_symbol_tree_menu_show), NULL);
2607 sidebar_add_common_menu_items(GTK_MENU(menu));
2611 static void on_document_save(G_GNUC_UNUSED GObject *object, GeanyDocument *doc)
2613 gchar *f;
2615 g_return_if_fail(!EMPTY(doc->real_path));
2617 f = g_build_filename(app->configdir, "ignore.tags", NULL);
2618 if (utils_str_equal(doc->real_path, f))
2619 load_c_ignore_tags();
2621 g_free(f);
2625 void symbols_init(void)
2627 gchar *f;
2628 guint i;
2630 create_taglist_popup_menu();
2632 f = g_build_filename(app->configdir, "ignore.tags", NULL);
2633 ui_add_config_file_menu_item(f, NULL, NULL);
2634 g_free(f);
2636 g_signal_connect(geany_object, "document-save", G_CALLBACK(on_document_save), NULL);
2638 for (i = 0; i < G_N_ELEMENTS(symbols_icons); i++)
2639 symbols_icons[i].pixbuf = get_tag_icon(symbols_icons[i].icon_name);
2643 void symbols_finalize(void)
2645 guint i;
2647 g_strfreev(c_tags_ignore);
2649 for (i = 0; i < G_N_ELEMENTS(symbols_icons); i++)
2651 if (symbols_icons[i].pixbuf)
2652 g_object_unref(symbols_icons[i].pixbuf);