Hi there
Thanks for geany, I use it every day :-)
It would be nice to have methods in the symbol browser show up as children of the classes that they belong to.
E.g. for Python the display looks like this:
> Classes
& ClassOne
& ClassTwo
> Methods
& ClassOne.__init__
& ClassOne.addFoo
& ClassTwo.__init__
& ClassTwo.getBar
> Functions
...
It would be easier to read if the above were arranged as
> Classes
& ClassOne
* __init__
* addFoo
& ClassTwo
* __init__
* getBar
> Functions
...
Maybe it could be made cofigurable ?
Thanks!
Conrad
I vote for this as well. One of the reasons are long class names for example. I did some mockup of this, and it can be seen here:
http://img220.imageshack.us/img220/1491/geanysymbolbrowserzy5.png
I'd like Geany to do this also. But I probably won't work on it for some time, as there are other things to do.
Attached is a patch to do the above - please test.
File Added: class_tree.diff
Patch to place class members/methods under class name
Patch against svn3011
Attached is a new patch for svn 3011
File Added: class_tree2.diff
modified class_tree2.diff to use geany coding conventions
gnocci-man: Thanks for the patch. I've cleaned it up to use Geany coding conventions (described in HACKING), please see class-tree_nmt.diff.
Your work is good so far, but we can't apply it yet because it doesn't handle some nested elements well, and many filetypes support nested classes (e.g. D, Python). This also applies for C++: a member function of a class in a namespace is not parsed correctly:
namespace Name
{
class A
{
char member_of_A(int val);
};
}
member_of_A is incorrectly shown as a function called 'A'.
File Added: class-tree_nmt.diff
Thanks for looking at the patch.
Can you point me to the Python and C++ test files you use?
> Can you point me to the Python and C++ test files you use?
For C++, just try the namespace example. You can also replace the namespace keyword with class to see a nested class example (e.g. for D but probably works for the C++ parser too).
For python:
class OuterClass():
class NestedClass():
nestedclassmember = "a value"
def method_of_inner():
"""This is a method of NestedClass."""
j = 3
Neither of NestedClass's children are shown in the right place, and they are each named 'NestedClass'.
Just noticed another minor bug with the patch: with the Diff filetype, a file such as 'src/Makefile.am' is just shown as 'am' in the symbol list.
New patch against 3028:
- Fixed multi-level hierarchies
- Fixed .diff issue
File Added: class-tree3.diff
New patch against 3031
- Works around C++ nested namespace tag bug.
File upload seems to be disabled, so I'll paste it here:
Index: src/symbols.c
===================================================================
--- src/symbols.c (revision 3031)
+++ src/symbols.c (working copy)
@@ -841,17 +841,23 @@
}
+
+
gboolean symbols_recreate_tag_list(GeanyDocument *doc, gint sort_mode)
{
- GList *tmp;
+ GList *tmp, *skipped = NULL;
const GList *tags;
GtkTreeIter iter;
static gint prev_sort_mode = SYMBOLS_SORT_BY_NAME;
filetype_id ft_id;
+ gint num_parents = 0, do_skipped = FALSE, num_skipped=0;
+ GHashTable *parent_hash;
+ const gchar *separator;
g_return_val_if_fail(doc != NULL, FALSE);
ft_id = FILETYPE_ID(doc->file_type);
+ separator=symbols_get_context_separator(ft_id);
if (sort_mode == SYMBOLS_SORT_USE_PREVIOUS)
sort_mode = prev_sort_mode;
@@ -868,17 +874,22 @@
gtk_tree_view_set_model(GTK_TREE_VIEW(doc->priv->tag_tree), NULL);
/* Clear all contents */
gtk_tree_store_clear(doc->priv->tag_store);
+ /* Create a hash table to keep track of parents */
+ parent_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
+ g_free);
init_tag_list(doc);
- for (tmp = (GList*)tags; tmp; tmp = g_list_next(tmp))
+
+ for (tmp = (GList*)tags; tmp && num_skipped<20;)
{
- gchar buf[100];
+ gchar buf[100] = "", buf2[100] = "";
const GeanySymbol *symbol = (GeanySymbol*)tmp->data;
- GtkTreeIter *parent = NULL;
+ GtkTreeIter *parent = NULL, *parent_search = NULL, *parent_search_short = NULL;
+ GtkTreeIter *parent_icon = NULL, *child = NULL;
GdkPixbuf *icon = NULL;
+ gint add_parent = FALSE, add_child = FALSE, skip = FALSE;
+ gchar *final_name = symbol->str, *child_name = NULL;
- g_snprintf(buf, sizeof(buf), "%s [%d]", symbol->str, symbol->line);
-
switch (symbol->type)
{
case tm_tag_prototype_t:
@@ -887,6 +898,9 @@
{
if (tv_iters.tag_function.stamp == -1) break;
parent = &(tv_iters.tag_function);
+ parent_icon = &(tv_iters.tag_function);
+ if (ft_id!=GEANY_FILETYPES_DIFF)
+ add_child = TRUE;
break;
}
case tm_tag_macro_t:
@@ -900,6 +914,9 @@
{
if (tv_iters.tag_class.stamp == -1) break;
parent = &(tv_iters.tag_class);
+ parent_icon = &(tv_iters.tag_class);
+ add_parent = TRUE;
+ add_child = TRUE;
break;
}
case tm_tag_member_t:
@@ -907,6 +924,9 @@
{
if (tv_iters.tag_member.stamp == -1) break;
parent = &(tv_iters.tag_member);
+ parent_icon = &(tv_iters.tag_member);
+ add_parent = TRUE;
+ add_child = TRUE;
break;
}
case tm_tag_typedef_t:
@@ -926,12 +946,17 @@
{
if (tv_iters.tag_struct.stamp == -1) break;
parent = &(tv_iters.tag_struct);
+ parent_icon = &(tv_iters.tag_struct);
+ add_parent = TRUE;
+ add_child = TRUE;
break;
}
case tm_tag_variable_t:
{
if (tv_iters.tag_variable.stamp == -1) break;
parent = &(tv_iters.tag_variable);
+ parent_icon = &(tv_iters.tag_variable);
+ add_child = TRUE;
break;
}
case tm_tag_namespace_t:
@@ -939,30 +964,111 @@
{
if (tv_iters.tag_namespace.stamp == -1) break;
parent = &(tv_iters.tag_namespace);
+ parent_icon = &(tv_iters.tag_namespace);
+ add_child = TRUE;
+ add_parent = TRUE;
break;
}
default:
{
if (tv_iters.tag_other.stamp == -1) break;
parent = &(tv_iters.tag_other);
+ parent_icon = &(tv_iters.tag_variable);
+ add_child = TRUE;
}
}
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);
+ { /* Split the string to obtain parent child names */
+ gchar **name_elems = g_strsplit (symbol->str, separator, 10);
+ gchar *child_name = NULL;
+ gchar *parent_name = buf2;
+ gchar *end_ptr = parent_name;
+ gint ne; /* number of name_elements */
+ buf2[0]='\0';
+ parent_search_short=NULL;
+ for (ne=0; name_elems[ne]; ne++){
+ child_name=name_elems[ne];
+ if (name_elems[ne+1]){
+ if (ne>0)
+ end_ptr=g_stpcpy (end_ptr, separator);
+ end_ptr=g_stpcpy (end_ptr, name_elems[ne]);
+ parent_search_short =
+ (GtkTreeIter *)g_hash_table_lookup(parent_hash, (gpointer)name_elems[ne]);
+ }
+ }
+
+ child = &iter;
+ if (!parent_icon)
+ parent_icon = parent;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(doc->priv->tag_store), parent_icon,
+ SYMBOLS_COLUMN_ICON, &icon, -1);
+ if (add_child)
+ {
+ if (parent_name)
+ {
+ parent_search = (GtkTreeIter *)g_hash_table_lookup(parent_hash, (gpointer)parent_name);
+ if (parent_search)
+ parent = parent_search;
+ else if (parent_search_short)
+ parent = parent_search_short;
+
+ if (child_name)
+ final_name = child_name;
+ else
+ final_name = symbol->str;
+ }
+ if (ne>1 && !parent_search && !parent_search_short){ /* Want to add child but parent not found */
+ GList *newstart;
+ tmp = g_list_previous(tmp);
+ newstart = g_list_remove(tmp, (gconstpointer) symbol);
+ newstart = g_list_append(tmp, (gpointer) symbol);
+ skip = TRUE;
+ num_skipped++;
+ }
+ }
+
+ if (!skip || do_skipped){
+ num_skipped=0;
+ if (add_parent)
+ {
+ GtkTreeIter *new_iter = g_new0(GtkTreeIter, 1);
+ g_hash_table_insert(parent_hash, symbol->str, new_iter);
+ child = new_iter;
+ num_parents++;
+ }
+
+ g_snprintf(buf, sizeof(buf), "%s [%d]", final_name, symbol->line);
+ gtk_tree_store_append(doc->priv->tag_store, child, parent);
+ gtk_tree_store_set(doc->priv->tag_store, child,
+ SYMBOLS_COLUMN_ICON, icon,
+ SYMBOLS_COLUMN_NAME, buf,
+ SYMBOLS_COLUMN_LINE, symbol->line, -1);
+ }
+ else{ /* Put in skipped list */
+/*
+ skipped = g_list_append(skipped, (gpointer)symbol);
+*/
+ }
+
if (G_LIKELY(G_IS_OBJECT(icon)))
g_object_unref(icon);
+ g_strfreev(name_elems);
}
+
+ if (! do_skipped && !g_list_next(tmp)){ /* Start adding skipped items */
+ do_skipped = TRUE;
+ tmp = g_list_first(skipped);
+ }
+ else
+ tmp = g_list_next(tmp);
}
+
hide_empty_rows(doc->priv->tag_store);
+ g_hash_table_destroy(parent_hash);
+
/* Re-attach model to view */
gtk_tree_view_set_model(GTK_TREE_VIEW(doc->priv->tag_tree),
GTK_TREE_MODEL(doc->priv->tag_store));
Attached patch against 3031
- Works around C++ nested namespace tag bug.
File Added: class-tree4.diff
Class tree patch to work around C++ tags bug
Thanks for the new patch class-tree4.diff.
I've applied it in a branch with minor changes here:
https://geany.svn.sourceforge.net/svnroot/geany/branches/symbol-tree
The bugs seem to fixed, thanks.
I'm going to refactor some of symbols_recreate_tag_list(), so it's easier to understand.
I have some ideas to make the code more straightforward and robust:
1. Replace the GeanySymbol list with a TMTag list. This avoids having to parse the parent name, avoiding possible bugs.
2. Sort the tags by appearance before adding to the tree model. This ensures that parent items are added before children, and we can remove all the complex 'skipped' code. The model can then be sorted after all entries have been added (for the 'sort by name' case).
3. Instead of looking for children for certain tags (e.g. class, etc), just see if the TMTag struct has a parent field. This should be much more flexible for filetypes that overload e.g. the tm_tag_class_t fields to mean something else.
I'm happy to work on these changes over time. If you want to discuss them, the geany-devel list might be better than here.
Finished the code changes; now merged into SVN trunk. Thanks again for the original patch, gnocci-man.