Merge symbol-tree branch:
Apply patch (with some reworking) from Conrad Steenberg (gnocci-man) to show methods as children of classes in the symbol list, and for other tag types to group children by their parents (thanks; #2083110). This works for any filetype that TagManager can parse tag scopes for. Fix not allowing a leading underscore when using scope name prefix. Fix symbol-tree branch bug: missing C++ constructor declaration tags. - Code changes: Use TMTag instead of GeanySymbol so the symbol tree can read the scope without parsing it. Free tag list straight after use, instead of next time the list is generated. Use TMTag pointer tree model column instead of line number. git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@3197 ea778897-0a13-0410-b9d1-a72fbfd435f5
This commit is contained in:
commit
1f6a611580
24
ChangeLog
24
ChangeLog
@ -1,9 +1,31 @@
|
||||
2008-11-10 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
|
||||
|
||||
* src/about.c, src/treeviews.c, src/treeviews.h, src/symbols.c,
|
||||
THANKS:
|
||||
Merge symbol-tree branch:
|
||||
Apply patch (with some reworking) from Conrad Steenberg
|
||||
(gnocci-man) to show methods as children of classes in the symbol
|
||||
list, and for other tag types to group children by their parents
|
||||
(thanks; #2083110).
|
||||
This works for any filetype that TagManager can parse tag scopes
|
||||
for.
|
||||
Fix not allowing a leading underscore when using scope name prefix.
|
||||
Fix symbol-tree branch bug: missing C++ constructor declaration
|
||||
tags.
|
||||
- Code changes:
|
||||
Use TMTag instead of GeanySymbol so the symbol tree can read the
|
||||
scope without parsing it.
|
||||
Free tag list straight after use, instead of next time the list is
|
||||
generated.
|
||||
Use TMTag pointer tree model column instead of line number.
|
||||
|
||||
|
||||
2008-11-08 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
|
||||
|
||||
* src/editor.c, doc/geany.txt, doc/geany.html:
|
||||
Make Ctrl-click go to matching brace if there's no current word.
|
||||
* plugins/vcdiff.c, src/editor.c, src/plugindata.h, src/plugins.c:
|
||||
Make Version Diff plugin set the indent type for diffs based on the
|
||||
Make Version Diff plugin set the indent type for diffs based on the
|
||||
current file's indent type.
|
||||
Add editor_set_indent_type() to the API.
|
||||
Note: uses editor.h plugindata.h include.
|
||||
|
1
THANKS
1
THANKS
@ -51,6 +51,7 @@ Jason Oster <parasytic(at)users(dot)sourceforge(dot)net> - various patches
|
||||
Andrew Rowland <weibullguy(at)charter(dot)net> - R filetype patch
|
||||
Bronisław Białek <after89(at)gmail(dot)com> - CSS parser update
|
||||
Roland Baudin <roland(dot)baudin(at)thalesaleniaspace(dot)com> - Matlab filetype patch
|
||||
Conrad Steenberg <gnocci-man(at)users(dot)sourceforge(dot)net> - symbol tree patch
|
||||
|
||||
Translators:
|
||||
------------
|
||||
|
@ -79,7 +79,7 @@ static const gint prev_translators_len = G_N_ELEMENTS(prev_translators);
|
||||
|
||||
static const gchar *contributors =
|
||||
"Alexander Rodin, Andrew Rowland, Anh Phạm, blackdog, Bo Lorentsen, Bob Doan, Bronisław Białek, Catalin Marinas, "
|
||||
"Christoph Berg, Daniel Richard G., Dave Moore, Dirk Weber, Felipe Pena, François Cami, "
|
||||
"Christoph Berg, Conrad Steenberg, Daniel Richard G., Dave Moore, Dirk Weber, Felipe Pena, François Cami, "
|
||||
"Giuseppe Torelli, Guillaume Hoffmann, Jason Oster, Jean-François Wauthy, Jeff Pohlmeyer, "
|
||||
"John Gabriele, Josef Whiter, Kevin Ellwood, Kristoffer A. Tjernås, Marko Peric, Matti Mårds, "
|
||||
"Peter Strand, Pierre Joye, Rob van der Linde, Robert McGinley, S Jagannathan, Saleem Abdulrasool, "
|
||||
|
608
src/symbols.c
608
src/symbols.c
@ -22,9 +22,9 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tagmanager related convenience functions.
|
||||
* Tagmanager parses tags in the current documents, known as the workspace, plus global tags,
|
||||
* which are lists of tags for each filetype. Global tags are loaded when a document with a
|
||||
* Symbol Tree and TagManager-related convenience functions.
|
||||
* TagManager parses tags for each document, and also adds them to the workspace (session).
|
||||
* Global tags are lists of tags for each filetype, loaded when a document with a
|
||||
* matching filetype is first loaded.
|
||||
*/
|
||||
|
||||
@ -89,6 +89,8 @@ static TagFileInfo tag_file_info[GTF_MAX] =
|
||||
|
||||
static gchar *user_tags_dir;
|
||||
|
||||
static GPtrArray *top_level_iter_names = NULL;
|
||||
|
||||
|
||||
static void html_tags_loaded(void);
|
||||
static void load_user_tags(filetype_id ft_id);
|
||||
@ -370,113 +372,60 @@ void symbols_finalize(void)
|
||||
}
|
||||
|
||||
|
||||
/* small struct to track tag name and type together */
|
||||
typedef struct GeanySymbol
|
||||
{
|
||||
gchar *str;
|
||||
gint type;
|
||||
gint line;
|
||||
} GeanySymbol;
|
||||
|
||||
|
||||
/* sort by name, line */
|
||||
static gint compare_symbol(const GeanySymbol *a, const GeanySymbol *b)
|
||||
/* sort by name, then line */
|
||||
static gint compare_symbol(const TMTag *tag_a, const TMTag *tag_b)
|
||||
{
|
||||
gint ret;
|
||||
|
||||
if (a == NULL || b == NULL) return 0;
|
||||
if (tag_a == NULL || tag_b == NULL)
|
||||
return 0;
|
||||
|
||||
ret = strcmp(a->str, b->str);
|
||||
ret = strcmp(tag_a->name, tag_b->name);
|
||||
if (ret == 0)
|
||||
{
|
||||
return a->line - b->line;
|
||||
return tag_a->atts.entry.line - tag_b->atts.entry.line;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* sort by line only */
|
||||
static gint compare_symbol_lines(const GeanySymbol *a, const GeanySymbol *b)
|
||||
static gint compare_symbol_lines(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
if (a == NULL || b == NULL) return 0;
|
||||
const TMTag *tag_a = TM_TAG(a);
|
||||
const TMTag *tag_b = TM_TAG(b);
|
||||
|
||||
return a->line - b->line;
|
||||
if (a == NULL || b == NULL)
|
||||
return 0;
|
||||
|
||||
return tag_a->atts.entry.line - tag_b->atts.entry.line;
|
||||
}
|
||||
|
||||
|
||||
static const GList *get_tag_list(GeanyDocument *doc, guint tag_types, gint sort_mode)
|
||||
static GList *get_tag_list(GeanyDocument *doc, guint tag_types)
|
||||
{
|
||||
static GList *tag_names = NULL;
|
||||
GList *tag_names = NULL;
|
||||
TMTag *tag;
|
||||
guint i;
|
||||
|
||||
if (doc != NULL && doc->tm_file &&
|
||||
doc->tm_file->tags_array)
|
||||
{
|
||||
TMTag *tag;
|
||||
guint i;
|
||||
GeanySymbol *symbol;
|
||||
gboolean doc_is_utf8 = FALSE;
|
||||
gchar *utf8_name;
|
||||
const gchar *cosep =
|
||||
symbols_get_context_separator(FILETYPE_ID(doc->file_type));
|
||||
g_return_val_if_fail(doc, NULL);
|
||||
|
||||
if (tag_names)
|
||||
{
|
||||
GList *tmp;
|
||||
for (tmp = tag_names; tmp; tmp = g_list_next(tmp))
|
||||
{
|
||||
g_free(((GeanySymbol*)tmp->data)->str);
|
||||
g_free(tmp->data);
|
||||
}
|
||||
g_list_free(tag_names);
|
||||
tag_names = NULL;
|
||||
}
|
||||
|
||||
/* encodings_convert_to_utf8_from_charset() fails with charset "None", so skip conversion
|
||||
* for None at this point completely */
|
||||
if (utils_str_equal(doc->encoding, "UTF-8") ||
|
||||
utils_str_equal(doc->encoding, "None"))
|
||||
doc_is_utf8 = TRUE;
|
||||
|
||||
for (i = 0; i < (doc->tm_file)->tags_array->len; ++i)
|
||||
{
|
||||
tag = TM_TAG((doc->tm_file)->tags_array->pdata[i]);
|
||||
if (tag == NULL)
|
||||
return NULL;
|
||||
|
||||
if (tag->type & tag_types)
|
||||
{
|
||||
if (! doc_is_utf8) utf8_name = encodings_convert_to_utf8_from_charset(tag->name,
|
||||
(gsize)-1, doc->encoding, TRUE);
|
||||
else utf8_name = tag->name;
|
||||
|
||||
if (utf8_name == NULL)
|
||||
continue;
|
||||
|
||||
symbol = g_new0(GeanySymbol, 1);
|
||||
if ((tag->atts.entry.scope != NULL) && isalpha(tag->atts.entry.scope[0]))
|
||||
{
|
||||
symbol->str = g_strconcat(tag->atts.entry.scope, cosep, utf8_name, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
symbol->str = g_strdup(utf8_name);
|
||||
}
|
||||
symbol->type = tag->type;
|
||||
symbol->line = tag->atts.entry.line;
|
||||
tag_names = g_list_prepend(tag_names, symbol);
|
||||
|
||||
if (! doc_is_utf8) g_free(utf8_name);
|
||||
}
|
||||
}
|
||||
if (sort_mode == SYMBOLS_SORT_BY_NAME)
|
||||
tag_names = g_list_sort(tag_names, (GCompareFunc) compare_symbol);
|
||||
else
|
||||
tag_names = g_list_sort(tag_names, (GCompareFunc) compare_symbol_lines);
|
||||
|
||||
return tag_names;
|
||||
}
|
||||
else
|
||||
if (!doc->tm_file || !doc->tm_file->tags_array)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < doc->tm_file->tags_array->len; ++i)
|
||||
{
|
||||
tag = TM_TAG(doc->tm_file->tags_array->pdata[i]);
|
||||
if (tag == NULL)
|
||||
return NULL;
|
||||
|
||||
if (tag->type & tag_types)
|
||||
{
|
||||
tag_names = g_list_append(tag_names, tag);
|
||||
}
|
||||
}
|
||||
tag_names = g_list_sort(tag_names, compare_symbol_lines);
|
||||
return tag_names;
|
||||
}
|
||||
|
||||
|
||||
@ -513,13 +462,8 @@ static void init_tag_iters(void)
|
||||
}
|
||||
|
||||
|
||||
/* Adds symbol list groups in (iter*, title) pairs.
|
||||
* The list must be ended with NULL. */
|
||||
static void G_GNUC_NULL_TERMINATED
|
||||
tag_list_add_groups(GtkTreeStore *tree_store, ...)
|
||||
static GdkPixbuf *get_tag_icon(const gchar *icon_name)
|
||||
{
|
||||
va_list args;
|
||||
GtkTreeIter *iter;
|
||||
static GtkIconTheme *icon_theme = NULL;
|
||||
static gint x, y;
|
||||
|
||||
@ -537,6 +481,19 @@ tag_list_add_groups(GtkTreeStore *tree_store, ...)
|
||||
g_free(path);
|
||||
#endif
|
||||
}
|
||||
return gtk_icon_theme_load_icon(icon_theme, icon_name, x, 0, NULL);
|
||||
}
|
||||
|
||||
|
||||
/* Adds symbol list groups in (iter*, title) pairs.
|
||||
* The list must be ended with NULL. */
|
||||
static void G_GNUC_NULL_TERMINATED
|
||||
tag_list_add_groups(GtkTreeStore *tree_store, ...)
|
||||
{
|
||||
va_list args;
|
||||
GtkTreeIter *iter;
|
||||
|
||||
g_return_if_fail(top_level_iter_names);
|
||||
|
||||
va_start(args, tree_store);
|
||||
for (; iter = va_arg(args, GtkTreeIter*), iter != NULL;)
|
||||
@ -547,30 +504,35 @@ tag_list_add_groups(GtkTreeStore *tree_store, ...)
|
||||
|
||||
if (icon_name)
|
||||
{
|
||||
icon = gtk_icon_theme_load_icon(icon_theme, icon_name, x, 0, NULL);
|
||||
icon = get_tag_icon(icon_name);
|
||||
}
|
||||
|
||||
g_assert(title != NULL);
|
||||
g_ptr_array_add(top_level_iter_names, title);
|
||||
|
||||
gtk_tree_store_append(tree_store, iter, NULL);
|
||||
|
||||
if (G_IS_OBJECT(icon))
|
||||
{
|
||||
gtk_tree_store_set(tree_store, iter, SYMBOLS_COLUMN_ICON, icon,
|
||||
SYMBOLS_COLUMN_NAME, title, -1);
|
||||
gtk_tree_store_set(tree_store, iter, SYMBOLS_COLUMN_ICON, icon, -1);
|
||||
g_object_unref(icon);
|
||||
}
|
||||
else
|
||||
gtk_tree_store_set(tree_store, iter, SYMBOLS_COLUMN_NAME, title, -1);
|
||||
gtk_tree_store_set(tree_store, iter, SYMBOLS_COLUMN_NAME, title, -1);
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
static void init_tag_list(GeanyDocument *doc)
|
||||
static void add_top_level_items(GeanyDocument *doc)
|
||||
{
|
||||
filetype_id ft_id = doc->file_type->id;
|
||||
GtkTreeStore *tag_store = doc->priv->tag_store;
|
||||
|
||||
if (top_level_iter_names == NULL)
|
||||
top_level_iter_names = g_ptr_array_new();
|
||||
else
|
||||
g_ptr_array_set_size(top_level_iter_names, 0);
|
||||
|
||||
init_tag_iters();
|
||||
|
||||
switch (ft_id)
|
||||
@ -857,25 +819,340 @@ static void hide_empty_rows(GtkTreeStore *store)
|
||||
}
|
||||
|
||||
|
||||
static const gchar *get_symbol_name(GeanyDocument *doc, const TMTag *tag,
|
||||
gboolean found_parent)
|
||||
{
|
||||
gchar *utf8_name;
|
||||
const gchar *scope = tag->atts.entry.scope;
|
||||
static GString *buffer = NULL; /* buffer will be small so we can keep it for reuse */
|
||||
gboolean doc_is_utf8 = FALSE;
|
||||
|
||||
/* encodings_convert_to_utf8_from_charset() fails with charset "None", so skip conversion
|
||||
* for None at this point completely */
|
||||
if (utils_str_equal(doc->encoding, "UTF-8") ||
|
||||
utils_str_equal(doc->encoding, "None"))
|
||||
doc_is_utf8 = TRUE;
|
||||
|
||||
if (! doc_is_utf8)
|
||||
utf8_name = encodings_convert_to_utf8_from_charset(tag->name,
|
||||
(gsize)-1, doc->encoding, TRUE);
|
||||
else
|
||||
utf8_name = tag->name;
|
||||
|
||||
if (utf8_name == NULL)
|
||||
return NULL;
|
||||
|
||||
if (!buffer)
|
||||
buffer = g_string_new(NULL);
|
||||
else
|
||||
g_string_truncate(buffer, 0);
|
||||
|
||||
/* check first char of scope is a wordchar */
|
||||
if (!found_parent && scope &&
|
||||
strpbrk(scope, GEANY_WORDCHARS) == scope)
|
||||
{
|
||||
const gchar *sep = symbols_get_context_separator(FILETYPE_ID(doc->file_type));
|
||||
|
||||
g_string_append(buffer, scope);
|
||||
g_string_append(buffer, sep);
|
||||
}
|
||||
g_string_append(buffer, utf8_name);
|
||||
|
||||
if (! doc_is_utf8)
|
||||
g_free(utf8_name);
|
||||
|
||||
g_string_append_printf(buffer, " [%lu]", tag->atts.entry.line);
|
||||
|
||||
return buffer->str;
|
||||
}
|
||||
|
||||
|
||||
/* find the last word in "foo::bar::blah", e.g. "blah" */
|
||||
const gchar *get_parent_name(const TMTag *tag, filetype_id ft_id)
|
||||
{
|
||||
const gchar *scope = tag->atts.entry.scope;
|
||||
const gchar *separator = symbols_get_context_separator(ft_id);
|
||||
const gchar *str, *ptr;
|
||||
|
||||
if (!scope)
|
||||
return NULL;
|
||||
|
||||
str = scope;
|
||||
|
||||
while (1)
|
||||
{
|
||||
ptr = strstr(str, separator);
|
||||
if (ptr)
|
||||
{
|
||||
str = ptr + strlen(separator);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return NZV(str) ? str : NULL;
|
||||
}
|
||||
|
||||
|
||||
static GtkTreeIter *get_tag_type_iter(TMTagType tag_type, filetype_id ft_id)
|
||||
{
|
||||
GtkTreeIter *iter = NULL;
|
||||
|
||||
switch (tag_type)
|
||||
{
|
||||
case tm_tag_prototype_t:
|
||||
case tm_tag_method_t:
|
||||
case tm_tag_function_t:
|
||||
{
|
||||
iter = &tv_iters.tag_function;
|
||||
break;
|
||||
}
|
||||
case tm_tag_macro_t:
|
||||
case tm_tag_macro_with_arg_t:
|
||||
{
|
||||
iter = &tv_iters.tag_macro;
|
||||
break;
|
||||
}
|
||||
case tm_tag_class_t:
|
||||
{
|
||||
iter = &tv_iters.tag_class;
|
||||
break;
|
||||
}
|
||||
case tm_tag_member_t:
|
||||
case tm_tag_field_t:
|
||||
{
|
||||
iter = &tv_iters.tag_member;
|
||||
break;
|
||||
}
|
||||
case tm_tag_typedef_t:
|
||||
case tm_tag_enum_t:
|
||||
{
|
||||
/* TODO separate C-like types here also */
|
||||
if (ft_id == GEANY_FILETYPES_HAXE)
|
||||
{
|
||||
iter = &tv_iters.tag_type;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
}
|
||||
case tm_tag_union_t:
|
||||
case tm_tag_struct_t:
|
||||
case tm_tag_interface_t:
|
||||
{
|
||||
iter = &tv_iters.tag_struct;
|
||||
break;
|
||||
}
|
||||
case tm_tag_variable_t:
|
||||
{
|
||||
iter = &tv_iters.tag_variable;
|
||||
break;
|
||||
}
|
||||
case tm_tag_namespace_t:
|
||||
case tm_tag_package_t:
|
||||
{
|
||||
iter = &tv_iters.tag_namespace;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
iter = &tv_iters.tag_other;
|
||||
}
|
||||
}
|
||||
if (iter->stamp != -1)
|
||||
return iter;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void add_tree_tag(GeanyDocument *doc, const TMTag *tag, GHashTable *parent_hash)
|
||||
{
|
||||
filetype_id ft_id = FILETYPE_ID(doc->file_type);
|
||||
GtkTreeStore *tree_store = doc->priv->tag_store;
|
||||
GtkTreeIter *parent = NULL;
|
||||
|
||||
parent = get_tag_type_iter(tag->type, ft_id);
|
||||
|
||||
if (parent)
|
||||
{
|
||||
const gchar *name;
|
||||
const gchar *parent_name = get_parent_name(tag, ft_id);
|
||||
GtkTreeIter iter;
|
||||
GtkTreeIter *icon_iter = NULL, *child = NULL;
|
||||
GdkPixbuf *icon = NULL;
|
||||
|
||||
child = &iter;
|
||||
icon_iter = (parent != &tv_iters.tag_other) ? parent : &tv_iters.tag_variable;
|
||||
|
||||
gtk_tree_model_get(GTK_TREE_MODEL(tree_store), icon_iter,
|
||||
SYMBOLS_COLUMN_ICON, &icon, -1);
|
||||
|
||||
if (parent_name)
|
||||
{
|
||||
GtkTreeIter *parent_search =
|
||||
(GtkTreeIter *)g_hash_table_lookup(parent_hash, parent_name);
|
||||
|
||||
if (parent_search)
|
||||
parent = parent_search;
|
||||
else
|
||||
parent_name = NULL;
|
||||
}
|
||||
|
||||
/* check if the current tag is a parent of other tags */
|
||||
if (g_hash_table_lookup_extended(parent_hash, tag->name, NULL, NULL) &&
|
||||
!utils_str_equal(tag->name, parent_name)) /* prevent Foo::Foo from making parent = child */
|
||||
{
|
||||
GtkTreeIter *new_iter = g_new0(GtkTreeIter, 1);
|
||||
|
||||
/* set an iter value for the hash key */
|
||||
g_hash_table_insert(parent_hash, tag->name, new_iter);
|
||||
/* instead of ignoring the appended child iter below, use the one in the hash table */
|
||||
child = new_iter;
|
||||
}
|
||||
|
||||
gtk_tree_store_append(tree_store, child, parent);
|
||||
|
||||
name = get_symbol_name(doc, tag, (parent_name != NULL));
|
||||
gtk_tree_store_set(tree_store, child,
|
||||
SYMBOLS_COLUMN_ICON, icon,
|
||||
SYMBOLS_COLUMN_NAME, name,
|
||||
SYMBOLS_COLUMN_TAG, tag, -1);
|
||||
|
||||
if (G_LIKELY(G_IS_OBJECT(icon)))
|
||||
g_object_unref(icon);
|
||||
}
|
||||
else
|
||||
geany_debug("Missing symbol-tree parent iter for type %d!", tag->type);
|
||||
}
|
||||
|
||||
|
||||
static void add_tree_tags(GeanyDocument *doc, const GList *tags)
|
||||
{
|
||||
const GList *item;
|
||||
GHashTable *parent_hash;
|
||||
|
||||
/* Create a hash table "parent_tag_name":(GtkTreeIter*) */
|
||||
parent_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
|
||||
|
||||
/* find and store all parent names in the hash table */
|
||||
for (item = tags; item; item = g_list_next(item))
|
||||
{
|
||||
const TMTag *tag = item->data;
|
||||
const gchar *name = get_parent_name(tag, FILETYPE_ID(doc->file_type));
|
||||
|
||||
if (name)
|
||||
g_hash_table_insert(parent_hash, (gpointer)name, NULL);
|
||||
}
|
||||
for (item = tags; item; item = g_list_next(item))
|
||||
{
|
||||
const TMTag *tag = item->data;
|
||||
|
||||
add_tree_tag(doc, tag, parent_hash);
|
||||
}
|
||||
g_hash_table_destroy(parent_hash);
|
||||
}
|
||||
|
||||
|
||||
/* @param item Must be a (gpointer*) for implementation reasons.
|
||||
* @example gchar *name = *item; (for when the GPtrArray contains char pointers). */
|
||||
#define foreach_ptr_array(item, ptr_array) \
|
||||
for (item = ptr_array->pdata; item < &ptr_array->pdata[ptr_array->len]; item++)
|
||||
|
||||
/* we don't want to sort 1st-level nodes, but we can't return 0 because the tree sort
|
||||
* is not stable, so the order is already lost. */
|
||||
static gint compare_top_level_names(const gchar *a, const gchar *b)
|
||||
{
|
||||
gpointer *item;
|
||||
|
||||
foreach_ptr_array(item, top_level_iter_names)
|
||||
{
|
||||
const gchar *name = *item;
|
||||
|
||||
if (utils_str_equal(name, a))
|
||||
return -1;
|
||||
if (utils_str_equal(name, b))
|
||||
return 1;
|
||||
}
|
||||
g_warning("Couldn't find top level node '%s' or '%s'!", a, b);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static gboolean tag_has_missing_parent(const TMTag *tag, GtkTreeStore *store,
|
||||
GtkTreeIter *iter)
|
||||
{
|
||||
/* if the tag has a parent tag, it should be at depth >= 2 */
|
||||
return NZV(tag->atts.entry.scope) &&
|
||||
gtk_tree_store_iter_depth(store, iter) == 1;
|
||||
}
|
||||
|
||||
|
||||
static gint tree_sort_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b,
|
||||
gpointer user_data)
|
||||
{
|
||||
gboolean sort_by_name = GPOINTER_TO_INT(user_data);
|
||||
const TMTag *tag_a, *tag_b;
|
||||
|
||||
gtk_tree_model_get(model, a, SYMBOLS_COLUMN_TAG, &tag_a, -1);
|
||||
gtk_tree_model_get(model, b, SYMBOLS_COLUMN_TAG, &tag_b, -1);
|
||||
|
||||
/* Check if the iters can be sorted based on tag name and line, not tree item name.
|
||||
* Sort by tree name if the scope was prepended, e.g. 'ScopeNameWithNoTag::TagName'. */
|
||||
if (tag_a && !tag_has_missing_parent(tag_a, GTK_TREE_STORE(model), a) &&
|
||||
tag_b && !tag_has_missing_parent(tag_b, GTK_TREE_STORE(model), b))
|
||||
{
|
||||
return sort_by_name ? compare_symbol(tag_a, tag_b) :
|
||||
compare_symbol_lines(tag_a, tag_b);
|
||||
}
|
||||
else
|
||||
{
|
||||
gchar *astr, *bstr;
|
||||
gint cmp;
|
||||
|
||||
gtk_tree_model_get(model, a, SYMBOLS_COLUMN_NAME, &astr, -1);
|
||||
gtk_tree_model_get(model, b, SYMBOLS_COLUMN_NAME, &bstr, -1);
|
||||
|
||||
/* if a is toplevel, b must be also */
|
||||
if (gtk_tree_store_iter_depth(GTK_TREE_STORE(model), a) == 0)
|
||||
{
|
||||
cmp = compare_top_level_names(astr, bstr);
|
||||
}
|
||||
else
|
||||
{
|
||||
cmp = strcmp(astr, bstr);
|
||||
|
||||
/* sort duplicate 'ScopeName::OverloadedTagName' items by line as well */
|
||||
if (tag_a && tag_b)
|
||||
if (!sort_by_name ||
|
||||
(utils_str_equal(tag_a->name, tag_b->name) &&
|
||||
utils_str_equal(tag_a->atts.entry.scope, tag_b->atts.entry.scope)))
|
||||
cmp = compare_symbol_lines(tag_a, tag_b);
|
||||
}
|
||||
g_free(astr);
|
||||
g_free(bstr);
|
||||
return cmp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void sort_tree(GtkTreeStore *store, gboolean sort_by_name)
|
||||
{
|
||||
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), 1, tree_sort_func,
|
||||
GINT_TO_POINTER(sort_by_name), NULL);
|
||||
|
||||
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), 1, GTK_SORT_ASCENDING);
|
||||
}
|
||||
|
||||
|
||||
gboolean symbols_recreate_tag_list(GeanyDocument *doc, gint sort_mode)
|
||||
{
|
||||
GList *tmp;
|
||||
const GList *tags;
|
||||
GtkTreeIter iter;
|
||||
GList *tags;
|
||||
static gint prev_sort_mode = SYMBOLS_SORT_BY_NAME;
|
||||
filetype_id ft_id;
|
||||
|
||||
g_return_val_if_fail(doc != NULL, FALSE);
|
||||
|
||||
ft_id = FILETYPE_ID(doc->file_type);
|
||||
|
||||
if (sort_mode == SYMBOLS_SORT_USE_PREVIOUS)
|
||||
sort_mode = prev_sort_mode;
|
||||
else
|
||||
prev_sort_mode = sort_mode;
|
||||
|
||||
tags = get_tag_list(doc, tm_tag_max_t, sort_mode);
|
||||
if (doc->tm_file == NULL || tags == NULL)
|
||||
tags = get_tag_list(doc, tm_tag_max_t);
|
||||
if (tags == NULL)
|
||||
return FALSE;
|
||||
|
||||
/* Make sure the model stays with us after the tree view unrefs it */
|
||||
@ -885,100 +1162,21 @@ gboolean symbols_recreate_tag_list(GeanyDocument *doc, gint sort_mode)
|
||||
/* Clear all contents */
|
||||
gtk_tree_store_clear(doc->priv->tag_store);
|
||||
|
||||
init_tag_list(doc);
|
||||
for (tmp = (GList*)tags; tmp; tmp = g_list_next(tmp))
|
||||
{
|
||||
gchar buf[100];
|
||||
const GeanySymbol *symbol = (GeanySymbol*)tmp->data;
|
||||
GtkTreeIter *parent = NULL;
|
||||
GdkPixbuf *icon = NULL;
|
||||
/* add grandparent type iters */
|
||||
add_top_level_items(doc);
|
||||
|
||||
g_snprintf(buf, sizeof(buf), "%s [%d]", symbol->str, symbol->line);
|
||||
add_tree_tags(doc, tags);
|
||||
g_list_free(tags);
|
||||
|
||||
switch (symbol->type)
|
||||
{
|
||||
case tm_tag_prototype_t:
|
||||
case tm_tag_method_t:
|
||||
case tm_tag_function_t:
|
||||
{
|
||||
if (tv_iters.tag_function.stamp == -1) break;
|
||||
parent = &(tv_iters.tag_function);
|
||||
break;
|
||||
}
|
||||
case tm_tag_macro_t:
|
||||
case tm_tag_macro_with_arg_t:
|
||||
{
|
||||
if (tv_iters.tag_macro.stamp == -1) break;
|
||||
parent = &(tv_iters.tag_macro);
|
||||
break;
|
||||
}
|
||||
case tm_tag_class_t:
|
||||
{
|
||||
if (tv_iters.tag_class.stamp == -1) break;
|
||||
parent = &(tv_iters.tag_class);
|
||||
break;
|
||||
}
|
||||
case tm_tag_member_t:
|
||||
case tm_tag_field_t:
|
||||
{
|
||||
if (tv_iters.tag_member.stamp == -1) break;
|
||||
parent = &(tv_iters.tag_member);
|
||||
break;
|
||||
}
|
||||
case tm_tag_typedef_t:
|
||||
case tm_tag_enum_t:
|
||||
{
|
||||
/** TODO separate C-like types here also */
|
||||
if (ft_id == GEANY_FILETYPES_HAXE)
|
||||
{
|
||||
if (tv_iters.tag_type.stamp == -1) break;
|
||||
parent = &(tv_iters.tag_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
case tm_tag_union_t:
|
||||
case tm_tag_struct_t:
|
||||
case tm_tag_interface_t:
|
||||
{
|
||||
if (tv_iters.tag_struct.stamp == -1) break;
|
||||
parent = &(tv_iters.tag_struct);
|
||||
break;
|
||||
}
|
||||
case tm_tag_variable_t:
|
||||
{
|
||||
if (tv_iters.tag_variable.stamp == -1) break;
|
||||
parent = &(tv_iters.tag_variable);
|
||||
break;
|
||||
}
|
||||
case tm_tag_namespace_t:
|
||||
case tm_tag_package_t:
|
||||
{
|
||||
if (tv_iters.tag_namespace.stamp == -1) break;
|
||||
parent = &(tv_iters.tag_namespace);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (tv_iters.tag_other.stamp == -1) break;
|
||||
parent = &(tv_iters.tag_other);
|
||||
}
|
||||
}
|
||||
|
||||
if (parent)
|
||||
{
|
||||
gtk_tree_model_get(GTK_TREE_MODEL(doc->priv->tag_store), parent,
|
||||
SYMBOLS_COLUMN_ICON, &icon, -1);
|
||||
gtk_tree_store_append(doc->priv->tag_store, &iter, parent);
|
||||
gtk_tree_store_set(doc->priv->tag_store, &iter,
|
||||
SYMBOLS_COLUMN_ICON, icon,
|
||||
SYMBOLS_COLUMN_NAME, buf,
|
||||
SYMBOLS_COLUMN_LINE, symbol->line, -1);
|
||||
|
||||
if (G_LIKELY(G_IS_OBJECT(icon)))
|
||||
g_object_unref(icon);
|
||||
}
|
||||
}
|
||||
hide_empty_rows(doc->priv->tag_store);
|
||||
|
||||
if (sort_mode == SYMBOLS_SORT_USE_PREVIOUS)
|
||||
sort_mode = prev_sort_mode;
|
||||
else
|
||||
prev_sort_mode = sort_mode;
|
||||
|
||||
sort_tree(doc->priv->tag_store, sort_mode == SYMBOLS_SORT_BY_NAME);
|
||||
|
||||
/* Re-attach model to view */
|
||||
gtk_tree_view_set_model(GTK_TREE_VIEW(doc->priv->tag_tree),
|
||||
GTK_TREE_MODEL(doc->priv->tag_store));
|
||||
|
@ -190,7 +190,7 @@ void treeviews_update_tag_list(GeanyDocument *doc, gboolean update)
|
||||
if (doc->priv->tag_tree == NULL)
|
||||
{
|
||||
doc->priv->tag_store = gtk_tree_store_new(
|
||||
SYMBOLS_N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT);
|
||||
SYMBOLS_N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER);
|
||||
doc->priv->tag_tree = gtk_tree_view_new();
|
||||
prepare_taglist(doc->priv->tag_tree, doc->priv->tag_store);
|
||||
gtk_widget_show(doc->priv->tag_tree);
|
||||
@ -640,7 +640,13 @@ static gboolean on_taglist_tree_selection_changed(GtkTreeSelection *selection)
|
||||
|
||||
if (gtk_tree_selection_get_selected(selection, &model, &iter))
|
||||
{
|
||||
gtk_tree_model_get(model, &iter, SYMBOLS_COLUMN_LINE, &line, -1);
|
||||
const TMTag *tag;
|
||||
|
||||
gtk_tree_model_get(model, &iter, SYMBOLS_COLUMN_TAG, &tag, -1);
|
||||
if (!tag)
|
||||
return FALSE;
|
||||
|
||||
line = tag->atts.entry.line;
|
||||
if (line > 0)
|
||||
{
|
||||
GeanyDocument *doc = document_get_current();
|
||||
|
@ -41,7 +41,7 @@ enum
|
||||
{
|
||||
SYMBOLS_COLUMN_ICON,
|
||||
SYMBOLS_COLUMN_NAME,
|
||||
SYMBOLS_COLUMN_LINE,
|
||||
SYMBOLS_COLUMN_TAG,
|
||||
SYMBOLS_N_COLUMNS
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user