Le 13/03/2012 01:48, Lex Trotman a écrit :
> Hi All,

Hey!

Here's an initial patch that fixes the issue by better handling of true
duplicate tags.  It's not necessarily the better fix for the issue,
where maybe having the template type info would be better, but it is
more generic since it would handle any duplicate.

It's not much tested, but have fun :)

Cheers,
Colomban


> 
> There is a problem with symbols in some C++ programs which causes some
> class members to appear and disappear or move in the sidebar.
> Needless to say this is *very* distracting.
> 
> The minimal program to demonstrate this is:
> 
> template<bool b>class problem;
> 
> template<> class problem<false> {
> };
> 
> template<> class problem<true> {
>       int i_change;
> };
> 
> 
> The member i_change changes each time the symbols pane is updated,
> correctly showing as part of the class at line 6, then part of the
> class at line 3, then disappearing, then back to the class at line 6
> etc.
> 
> In C++ this program has three class names:
> 
> a) at line 1 declaration of problem<bool>
> b) at line 3 definition of problem<false>
> c) at line 6 definition of problem<true>
> 
> Note that these are distinct classes as if the names include <bool>,
> <true> and <false>.
> 
> But neither symbols.c or the tagmanager parser includes the template
> parameters in the name, instead showing both of the classes at line 3
> and 6 as "problem".
> 
> Discussion with Colomban on IRC indicated that this may be confusing
> the symbol update algorithm.  Forcing a complete re-generation of the
> symbols did indeed stabilise the sidebar.
> 
> I think that the problem is due to update_tree_tags() identifying the
> parent of i_change just by name so it can't tell if line 3 or line 6
> (or possibly line 1) is the parent.
> 
> If  i_change is shown in the symbol tree as part of the class at line
> 6 then it will be in the parent_tags hash with parent of "problem".
> If this is taken as "problem"of line 3 as parent, it will delete it
> from the class at line 6 in pass 1 and as i_change is still in the
> tags list will add it to the class at line 3 since thats what the
> parent_tags hash says is its parent.
> 
> On the next symbol update, since what it thinks is the parent of
> i_change (the class at line 3) does not in fact have i_change as a
> member, it will be deleted from the tree and the tags list, so it
> isn't added again in pass 2.
> 
> On the next symbol update i_change is not in the tree so it isn't in
> the parent_tags hash so it is added where the tags list says is the
> correct place.
> 
> If the order of the definitions at line 3 and 6 are reversed i_change
> just alternates on and off, suggesting that it isn't the first
> definition of the symbol "problem" that is the one found.
> 
> At least I think this is the mechanism?
> 
> Other than the hack to re-create the whole table each time, I'm not
> sure how to cure it without distinguishing the three class names, and
> that means modifying tagmanager/c.c (shudder).
> 
> Cheers
> Lex
> _______________________________________________
> Geany-devel mailing list
> Geany-devel@uvena.de
> https://lists.uvena.de/cgi-bin/mailman/listinfo/geany-devel

diff --git a/src/symbols.c b/src/symbols.c
index ff9c210..a055081 100644
--- a/src/symbols.c
+++ b/src/symbols.c
@@ -1258,21 +1258,105 @@ static gboolean tree_store_remove_row(GtkTreeStore *store, GtkTreeIter *iter)
 }
 
 
-/* updates @table adding @tag->name:@iter if necessary */
+/* adds a new element in the parent table if it's key is known
+ * duplicates are kept */
 static void update_parents_table(GHashTable *table, const TMTag *tag, const gchar *parent_name,
 		const GtkTreeIter *iter)
 {
-	if (g_hash_table_lookup_extended(table, tag->name, NULL, NULL) &&
+	GList **list;
+	if (g_hash_table_lookup_extended(table, tag->name, NULL, (gpointer *) &list) &&
 		! utils_str_equal(parent_name, tag->name) /* prevent Foo::Foo from making parent = child */)
 	{
-		g_hash_table_insert(table, tag->name, g_slice_dup(GtkTreeIter, iter));
+		if (! list)
+		{
+			list = g_slice_alloc(sizeof *list);
+			*list = NULL;
+			g_hash_table_insert(table, tag->name, list);
+		}
+		*list = g_list_prepend(*list, g_slice_dup(GtkTreeIter, iter));
+	}
+}
+
+
+static void free_iter_slice_list(gpointer data)
+{
+	GList **list = data;
+
+	if (list)
+	{
+		GList *node;
+		foreach_list(node, *list)
+			g_slice_free(GtkTreeIter, node->data);
+		g_list_free(*list);
+		g_slice_free1(sizeof *list, list);
 	}
 }
 
 
-static void free_iter_slice(gpointer iter)
+/* inserts a @data in @table on key @tag
+ * previous data is not overwritten if the key is duplicated, but rather the
+ * two values are kept in a list
+ * 
+ * table is: <TMTag>:GSList<GList<TMTag>> */
+static void tags_table_insert(GHashTable *table, TMTag *tag, GList *data)
 {
-	g_slice_free(GtkTreeIter, iter);
+	GList *list = g_hash_table_lookup(table, tag);
+	list = g_list_prepend(list, data);
+	g_hash_table_insert(table, tag, list);
+}
+
+
+/* looks up the entry in @table that matches @tag the better
+ * if there are more than one candidate, the one that is the closest line position to @tag */
+static GList *tags_table_lookup(GHashTable *table, TMTag *tag)
+{
+	GList *data = NULL;
+	GList *node = g_hash_table_lookup(table, tag);
+	if (node)
+	{
+		glong delta;
+		data = node->data;
+
+#define TAG_DELTA(a, b) (TM_TAG(a)->atts.entry.line - TM_TAG(b)->atts.entry.line)
+
+		delta = TAG_DELTA(((GList *) node->data)->data, tag);
+		for (node = node->next; node; node = node->next)
+		{
+			glong d = TAG_DELTA(((GList *) node->data)->data, tag);
+
+			if (d < delta)
+			{
+				data = node->data;
+				delta = d;
+			}
+		}
+
+#undef TAG_DELTA
+
+	}
+	return data;
+}
+
+
+/* removes the element at @tag from @table
+ * @tag must be the exact pointer used at insertion time */
+static void tags_table_remove(GHashTable *table, TMTag *tag)
+{
+	GList *list = g_hash_table_lookup(table, tag);
+	if (list)
+	{
+		GList *node;
+		foreach_list(node, list)
+		{
+			if (((GList *) node->data)->data == tag)
+				break;
+		}
+		list = g_list_delete_link(list, node);
+		if (list)
+			g_hash_table_insert(table, tag, list);
+		else
+			g_hash_table_remove(table, tag);
+	}
 }
 
 
@@ -1306,7 +1390,7 @@ static void update_tree_tags(GeanyDocument *doc, GList **tags)
 
 	/* Build hash tables holding tags and parents */
 	/* parent table holds "tag-name":GtkTreeIter */
-	parents_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free_iter_slice);
+	parents_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free_iter_slice_list);
 	/* tags table is another representation of the @tags list, TMTag:GList<TMTag> */
 	tags_table = g_hash_table_new_full(tag_hash, tag_equal, NULL, NULL);
 	foreach_list(item, *tags)
@@ -1314,7 +1398,7 @@ static void update_tree_tags(GeanyDocument *doc, GList **tags)
 		TMTag *tag = item->data;
 		const gchar *name;
 
-		g_hash_table_insert(tags_table, tag, item);
+		tags_table_insert(tags_table, tag, item);
 
 		name = get_parent_name(tag, doc->file_type->id);
 		if (name)
@@ -1337,7 +1421,7 @@ static void update_tree_tags(GeanyDocument *doc, GList **tags)
 		{
 			GList *found_item;
 
-			found_item = g_hash_table_lookup(tags_table, tag);
+			found_item = tags_table_lookup(tags_table, tag);
 			if (! found_item) /* tag doesn't exist, remove it */
 				cont = tree_store_remove_row(store, &iter);
 			else /* tag still exist, update it */
@@ -1362,7 +1446,7 @@ static void update_tree_tags(GeanyDocument *doc, GList **tags)
 				update_parents_table(parents_table, found, parent_name, &iter);
 
 				/* remove the updated tag from the table and list */
-				g_hash_table_remove(tags_table, found);
+				tags_table_remove(tags_table, found);
 				*tags = g_list_delete_link(*tags, found_item);
 
 				cont = next_iter(model, &iter, TRUE);
@@ -1393,9 +1477,34 @@ static void update_tree_tags(GeanyDocument *doc, GList **tags)
 			parent_name = get_parent_name(tag, doc->file_type->id);
 			if (parent_name)
 			{
-				GtkTreeIter *parent_search;
+				GList **candidates;
+				GtkTreeIter *parent_search = NULL;
+
+				/* walk parent candidates to find the better one
+				 * if there are more than one, take the one that has the closest line number
+				 * after the tags we're searching the parent for */
+				candidates = g_hash_table_lookup(parents_table, parent_name);
+				if (candidates)
+				{
+					GList *node;
+					glong delta = G_MAXLONG;
+					foreach_list(node, *candidates)
+					{
+						TMTag *parent_tag;
+						glong  d;
+
+						gtk_tree_model_get(GTK_TREE_MODEL(store), node->data,
+								SYMBOLS_COLUMN_TAG, &parent_tag, -1);
+
+						d = tag->atts.entry.line - parent_tag->atts.entry.line;
+						if (! parent_search || (d >= 0 && d < delta))
+						{
+							delta = d;
+							parent_search = node->data;
+						}
+					}
+				}
 
-				parent_search = g_hash_table_lookup(parents_table, parent_name);
 				if (parent_search)
 					parent = parent_search;
 				else
_______________________________________________
Geany-devel mailing list
Geany-devel@uvena.de
https://lists.uvena.de/cgi-bin/mailman/listinfo/geany-devel

Reply via email to