SF.net SVN: geany:[3850] trunk

ntrel at users.sourceforge.net ntrel at xxxxx
Wed Jun 10 12:36:14 UTC 2009


Revision: 3850
          http://geany.svn.sourceforge.net/geany/?rev=3850&view=rev
Author:   ntrel
Date:     2009-06-10 12:36:13 +0000 (Wed, 10 Jun 2009)

Log Message:
-----------
Autocomplete scoped fields like struct members when typing '.' (and
also '->' or '::' in C/C++).
Save all tag types for C/C++ when generating a global tags file, so
we can use autocompletion for structs also.
Merge tm_workspace_find_scope_members(),
tm_workspace_find_namespace_members() (currently not built) from
Anjuta 2.24.1 tagmanager.

Modified Paths:
--------------
    trunk/ChangeLog
    trunk/TODO
    trunk/src/editor.c
    trunk/tagmanager/include/tm_workspace.h
    trunk/tagmanager/tm_workspace.c

Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog	2009-06-09 21:02:46 UTC (rev 3849)
+++ trunk/ChangeLog	2009-06-10 12:36:13 UTC (rev 3850)
@@ -1,3 +1,16 @@
+2009-06-10  Nick Treleaven  <nick(dot)treleaven(at)btinternet(dot)com>
+
+ * src/editor.c, tagmanager/include/tm_workspace.h,
+   tagmanager/tm_workspace.c, TODO:
+   Autocomplete scoped fields like struct members when typing '.' (and
+   also '->' or '::' in C/C++).
+   Save all tag types for C/C++ when generating a global tags file, so
+   we can use autocompletion for structs also.
+   Merge tm_workspace_find_scope_members(),
+   tm_workspace_find_namespace_members() (currently not built) from
+   Anjuta 2.24.1 tagmanager.
+
+
 2009-06-09  Enrico Tröger  <enrico(dot)troeger(at)uvena(dot)de>
 
  * tagmanager/pascal.c:

Modified: trunk/TODO
===================================================================
--- trunk/TODO	2009-06-09 21:02:46 UTC (rev 3849)
+++ trunk/TODO	2009-06-10 12:36:13 UTC (rev 3850)
@@ -41,5 +41,4 @@
 	o (better tags support for popular languages? - this is a moving
 	   target...)
 	o Some kind of support for CTags tags files
-	o Scope resolution for object members
 	o Python plugin interface (different concept from Lua scripting)

Modified: trunk/src/editor.c
===================================================================
--- trunk/src/editor.c	2009-06-09 21:02:46 UTC (rev 3849)
+++ trunk/src/editor.c	2009-06-10 12:36:13 UTC (rev 3850)
@@ -431,6 +431,103 @@
 }
 
 
+static void show_autocomplete(ScintillaObject *sci, gint rootlen, const gchar *words)
+{
+	/* store whether a calltip is showing, so we can reshow it after autocompletion */
+	calltip.set = SSM(sci, SCI_CALLTIPACTIVE, 0, 0);
+	SSM(sci, SCI_AUTOCSHOW, rootlen, (sptr_t) words);
+}
+
+
+static void show_tags_list(GeanyEditor *editor, const GPtrArray *tags, gsize rootlen)
+{
+	ScintillaObject *sci = editor->sci;
+
+	g_return_if_fail(tags);
+
+	if (tags->len > 0)
+	{
+		GString *words = g_string_sized_new(150);
+		guint j;
+
+		for (j = 0; j < tags->len; ++j)
+		{
+			if (j > 0)
+				g_string_append_c(words, '\n');
+
+			if (j == editor_prefs.autocompletion_max_entries)
+			{
+				g_string_append(words, "...");
+				break;
+			}
+			g_string_append(words, ((TMTag *) tags->pdata[j])->name);
+		}
+		show_autocomplete(sci, rootlen, words->str);
+		g_string_free(words, TRUE);
+	}
+}
+
+
+/* do not use with long strings */
+static gboolean match_last_chars(ScintillaObject *sci, gint pos, const gchar *str)
+{
+	gsize len = strlen(str);
+	gchar *buf;
+
+	g_return_val_if_fail(len < 100, FALSE);
+
+	buf = g_alloca(len + 1);
+	sci_get_text_range(sci, pos - len, pos, buf);
+	return strcmp(str, buf) == 0;
+}
+
+
+static void autocomplete_scope(GeanyEditor *editor)
+{
+	ScintillaObject *sci = editor->sci;
+	gint pos = sci_get_current_position(editor->sci);
+	gchar typed = sci_get_char_at(sci, pos - 1);
+	gchar *name;
+	const GPtrArray *tags = NULL;
+	const TMTag *tag;
+	gint ft_id = FILETYPE_ID(editor->document->file_type);
+
+	if (ft_id == GEANY_FILETYPES_C || ft_id == GEANY_FILETYPES_CPP)
+	{
+		if (match_last_chars(sci, pos, "->") || match_last_chars(sci, pos, "::"))
+			pos--;
+		else if (typed != '.')
+			return;
+	}
+	else if (typed != '.')
+		return;
+
+	/* allow for a space between word and operator */
+	if (isspace(sci_get_char_at(sci, pos - 2)))
+		pos--;
+	name = editor_get_word_at_pos(editor, pos - 1, NULL);
+	if (!name)
+		return;
+
+	tags = tm_workspace_find(name, tm_tag_max_t, NULL, FALSE, -1);
+	g_free(name);
+	if (!tags || tags->len == 0)
+		return;
+
+	tag = g_ptr_array_index(tags, 0);
+	name = tag->atts.entry.var_type;
+	if (name)
+	{
+		TMWorkObject *obj = editor->document->tm_file;
+
+		tags = tm_workspace_find_scope_members(obj ? obj->tags_array : NULL,
+			name, TRUE, FALSE);
+		if (tags)
+			show_tags_list(editor, tags, 0);
+	}
+}
+
+
 static void on_char_added(GeanyEditor *editor, SCNotification *nt)
 {
 	ScintillaObject *sci = editor->sci;
@@ -450,6 +547,8 @@
 			break;
 		}
 		case '>':
+			editor_start_auto_complete(editor, pos, FALSE);	/* C/C++ ptr-> scope completion */
+			/* fall through */
 		case '/':
 		{	/* close xml-tags */
 			handle_xml(editor, pos, nt->ch);
@@ -489,6 +588,10 @@
 				close_block(editor, pos - 1);
 			break;
 		}
+		/* scope autocompletion */
+		case '.':
+		case ':':	/* C/C++ class:: syntax */
+		/* tag autocompletion */
 		default:
 			editor_start_auto_complete(editor, pos, FALSE);
 	}
@@ -1520,14 +1623,6 @@
 }
 
 
-static void show_autocomplete(ScintillaObject *sci, gint rootlen, const gchar *words)
-{
-	/* store whether a calltip is showing, so we can reshow it after autocompletion */
-	calltip.set = SSM(sci, SCI_CALLTIPACTIVE, 0, 0);
-	SSM(sci, SCI_AUTOCSHOW, rootlen, (sptr_t) words);
-}
-
-
 static gboolean
 autocomplete_html(ScintillaObject *sci, const gchar *root, gsize rootlen)
 {	/* HTML entities auto completion */
@@ -1566,35 +1661,15 @@
 {
 	TMTagAttrType attrs[] = { tm_tag_attr_name_t, 0 };
 	const GPtrArray *tags;
-	ScintillaObject *sci;
 	GeanyDocument *doc;
 
 	g_return_val_if_fail(editor != NULL && editor->document->file_type != NULL, FALSE);
 
-	sci = editor->sci;
 	doc = editor->document;
 
 	tags = tm_workspace_find(root, tm_tag_max_t, attrs, TRUE, doc->file_type->lang);
-	if (NULL != tags && tags->len > 0)
-	{
-		GString *words = g_string_sized_new(150);
-		guint j;
-
-		for (j = 0; j < tags->len; ++j)
-		{
-			if (j > 0)
-				g_string_append_c(words, '\n');
-
-			if (j == editor_prefs.autocompletion_max_entries)
-			{
-				g_string_append(words, "...");
-				break;
-			}
-			g_string_append(words, ((TMTag *) tags->pdata[j])->name);
-		}
-		show_autocomplete(sci, rootlen, words->str);
-		g_string_free(words, TRUE);
-	}
+	if (tags)
+		show_tags_list(editor, tags, rootlen);
 	return TRUE;
 }
 
@@ -1658,6 +1733,8 @@
 	if (!force && !is_code_style(lexer, style))
 		return FALSE;
 
+	autocomplete_scope(editor);
+
 	linebuf = sci_get_line(sci, line);
 
 	if (ft->id == GEANY_FILETYPES_LATEX)

Modified: trunk/tagmanager/include/tm_workspace.h
===================================================================
--- trunk/tagmanager/include/tm_workspace.h	2009-06-09 21:02:46 UTC (rev 3849)
+++ trunk/tagmanager/include/tm_workspace.h	2009-06-10 12:36:13 UTC (rev 3850)
@@ -139,6 +139,19 @@
 tm_workspace_find_scoped (const char *name, const char *scope, gint type,
 		TMTagAttrType *attrs, gboolean partial, langType lang, gboolean global_search);
 
+/*! Returns all matching members tags found in given struct/union/class name.
+ \param name Name of the struct/union/class.
+ \param file_tags A GPtrArray of edited file TMTag pointers (for search speedup, can be NULL).
+ \return A GPtrArray of TMTag pointers to struct/union/class members */
+const GPtrArray *tm_workspace_find_scope_members(const GPtrArray *file_tags,
+												 const char *scope_name,
+												 gboolean find_global,
+												 gboolean no_definitions);
+
+const GPtrArray *
+tm_workspace_find_namespace_members (const GPtrArray * file_tags, const char *name,
+								 gboolean search_global);
+
 /* Returns TMTag to function which "own" given line
  \param line Current line in edited file.
  \param file_tags A GPtrArray of edited file TMTag pointers.

Modified: trunk/tagmanager/tm_workspace.c
===================================================================
--- trunk/tagmanager/tm_workspace.c	2009-06-09 21:02:46 UTC (rev 3849)
+++ trunk/tagmanager/tm_workspace.c	2009-06-10 12:36:13 UTC (rev 3850)
@@ -257,22 +257,6 @@
 	}
 }
 
-static gint get_global_tag_type_mask(gint lang)
-{
-	switch (lang)
-	{
-		case 0:
-		case 1:
-			/* C/C++ */
-			return tm_tag_class_t | tm_tag_typedef_t | tm_tag_enum_t | tm_tag_enumerator_t |
-				tm_tag_prototype_t |
-				tm_tag_function_t | tm_tag_method_t |	/* for inline functions */
-				tm_tag_macro_t | tm_tag_macro_with_arg_t;
-		default:
-			return tm_tag_max_t;
-	}
-}
-
 gboolean tm_workspace_create_global_tags(const char *config_dir, const char *pre_process,
 	const char **includes, int includes_count, const char *tags_file, int lang)
 {
@@ -415,7 +399,7 @@
 		tm_source_file_free(source_file);
 		return FALSE;
 	}
-	tags_array = tm_tags_extract(source_file->tags_array, get_global_tag_type_mask(lang));
+	tags_array = tm_tags_extract(source_file->tags_array, tm_tag_max_t);
 	if ((NULL == tags_array) || (0 == tags_array->len))
 	{
 		if (tags_array)
@@ -752,6 +736,461 @@
 }
 
 
+static int
+find_scope_members_tags (const GPtrArray * all, GPtrArray * tags,
+						 const langType langJava, const char *name,
+						 const char *filename, gboolean no_definitions)
+{
+	GPtrArray *local = g_ptr_array_new ();
+	unsigned int i;
+	TMTag *tag;
+	size_t len = strlen (name);
+	for (i = 0; (i < all->len); ++i)
+	{
+		tag = TM_TAG (all->pdata[i]);
+		if (no_definitions && filename && tag->atts.entry.file &&
+			0 != strcmp (filename,
+						 tag->atts.entry.file->work_object.short_name))
+		{
+			continue;
+		}
+		if (tag && tag->atts.entry.scope && tag->atts.entry.scope[0] != '\0')
+		{
+			if (0 == strncmp (name, tag->atts.entry.scope, len))
+			{
+				g_ptr_array_add (local, tag);
+			}
+		}
+	}
+	if (local->len > 0)
+	{
+		unsigned int j;
+		TMTag *tag2;
+		char backup = 0;
+		char *s_backup = NULL;
+		char *var_type = NULL;
+		char *scope;
+		for (i = 0; (i < local->len); ++i)
+		{
+			tag = TM_TAG (local->pdata[i]);
+			scope = tag->atts.entry.scope;
+			if (scope && 0 == strcmp (name, scope))
+			{
+				g_ptr_array_add (tags, tag);
+				continue;
+			}
+			s_backup = NULL;
+			j = 0;				/* someone could write better code :P */
+			while (scope)
+			{
+				if (s_backup)
+				{
+					backup = s_backup[0];
+					s_backup[0] = '\0';
+					if (0 == strcmp (name, tag->atts.entry.scope))
+					{
+						j = local->len;
+						s_backup[0] = backup;
+						break;
+					}
+				}
+				if (tag->atts.entry.file
+					&& tag->atts.entry.file->lang == langJava)
+				{
+					scope = strrchr (tag->atts.entry.scope, '.');
+					if (scope)
+						var_type = scope + 1;
+				}
+				else
+				{
+					scope = strrchr (tag->atts.entry.scope, ':');
+					if (scope)
+					{
+						var_type = scope + 1;
+						scope--;
+					}
+				}
+				if (s_backup)
+				{
+					s_backup[0] = backup;
+				}
+				if (scope)
+				{
+					if (s_backup)
+					{
+						backup = s_backup[0];
+						s_backup[0] = '\0';
+					}
+					for (j = 0; (j < local->len); ++j)
+					{
+						if (i == j)
+							continue;
+						tag2 = TM_TAG (local->pdata[j]);
+						if (tag2->atts.entry.var_type &&
+							0 == strcmp (var_type, tag2->atts.entry.var_type))
+						{
+							break;
+						}
+					}
+					if (s_backup)
+						s_backup[0] = backup;
+				}
+				if (j < local->len)
+				{
+					break;
+				}
+				s_backup = scope;
+			}
+			if (j == local->len)
+			{
+				g_ptr_array_add (tags, tag);
+			}
+		}
+	}
+	g_ptr_array_free (local, TRUE);
+	return (int) tags->len;
+}
+
+
+#if 0
+static int
+find_namespace_members_tags (const GPtrArray * all, GPtrArray * tags,
+						 	const langType langJava, const char *name,
+						 	const char *filename)
+{
+	GPtrArray *local = g_ptr_array_new ();
+	unsigned int i;
+	TMTag *tag;
+	size_t len = strlen (name);
+
+	g_return_val_if_fail (all != NULL, 0);
+
+	for (i = 0; (i < all->len); ++i)
+	{
+		tag = TM_TAG (all->pdata[i]);
+		if (filename && tag->atts.entry.file &&
+			0 != strcmp (filename,
+						 tag->atts.entry.file->work_object.short_name))
+		{
+			continue;
+		}
+
+		if (tag && tag->atts.entry.scope && tag->atts.entry.scope[0] != '\0')
+		{
+			if (0 == strncmp (name, tag->atts.entry.scope, len))
+			{
+				g_ptr_array_add (local, tag);
+			}
+		}
+	}
+
+	if (local->len > 0)
+	{
+		char *scope;
+		for (i = 0; (i < local->len); ++i)
+		{
+			tag = TM_TAG (local->pdata[i]);
+			scope = tag->atts.entry.scope;
+
+			/* if we wanna complete something like
+			 * namespace1::
+			 * we'll just return the tags that have "namespace1"
+			 * as their scope. So we won't return classes/members/namespaces
+			 * under, for example, namespace2, where namespace1::namespace2
+			 */
+			if (scope && 0 == strcmp (name, scope))
+			{
+				g_ptr_array_add (tags, tag);
+			}
+		}
+	}
+
+	g_ptr_array_free (local, TRUE);
+	return (int) tags->len;
+}
+
+const GPtrArray *
+tm_workspace_find_namespace_members (const GPtrArray * file_tags, const char *name,
+								 gboolean search_global)
+{
+	static GPtrArray *tags = NULL;
+	GPtrArray *local = NULL;
+	char *new_name = (char *) name;
+	char *filename = NULL;
+	int found = 0, del = 0;
+	static langType langJava = -1;
+	TMTag *tag = NULL;
+
+	g_return_val_if_fail ((theWorkspace && name && name[0] != '\0'), NULL);
+
+	if (!tags)
+		tags = g_ptr_array_new ();
+
+	while (1)
+	{
+		const GPtrArray *tags2;
+		int got = 0, types = (tm_tag_class_t | tm_tag_namespace_t |
+								tm_tag_struct_t | tm_tag_typedef_t |
+								tm_tag_union_t | tm_tag_enum_t);
+
+		if (file_tags)
+		{
+			g_ptr_array_set_size (tags, 0);
+			got = fill_find_tags_array (tags, file_tags,
+										  new_name, NULL, types, FALSE, -1, FALSE);
+		}
+
+
+		if (got)
+		{
+			tags2 = tags;
+		}
+		else
+		{
+			TMTagAttrType attrs[] = {
+				tm_tag_attr_name_t, tm_tag_attr_type_t,
+				tm_tag_attr_none_t
+			};
+			tags2 = tm_workspace_find (new_name, types, attrs, FALSE, -1);
+		}
+
+		if ((tags2) && (tags2->len == 1) && (tag = TM_TAG (tags2->pdata[0])))
+		{
+			if (tag->type == tm_tag_typedef_t && tag->atts.entry.var_type
+				&& tag->atts.entry.var_type[0] != '\0')
+			{
+				new_name = tag->atts.entry.var_type;
+				continue;
+			}
+			filename = (tag->atts.entry.file ?
+						tag->atts.entry.file->work_object.short_name : NULL);
+			if (tag->atts.entry.scope && tag->atts.entry.scope[0] != '\0')
+			{
+				del = 1;
+				if (tag->atts.entry.file &&
+					tag->atts.entry.file->lang == langJava)
+				{
+					new_name = g_strdup_printf ("%s.%s",
+												tag->atts.entry.scope,
+												new_name);
+				}
+				else
+				{
+					new_name = g_strdup_printf ("%s::%s",
+												tag->atts.entry.scope,
+												new_name);
+				}
+			}
+			break;
+		}
+		else
+		{
+			return NULL;
+		}
+	}
+
+	g_ptr_array_set_size (tags, 0);
+
+	if (tag && tag->atts.entry.file)
+	{
+		local = tm_tags_extract (tag->atts.entry.file->work_object.tags_array,
+								 (tm_tag_function_t |
+								  tm_tag_field_t | tm_tag_enumerator_t |
+								  tm_tag_namespace_t | tm_tag_class_t ));
+	}
+	else
+	{
+		local = tm_tags_extract (theWorkspace->work_object.tags_array,
+								 (tm_tag_function_t | tm_tag_prototype_t |
+								  tm_tag_member_t |
+								  tm_tag_field_t | tm_tag_enumerator_t |
+								  tm_tag_namespace_t | tm_tag_class_t ));
+	}
+
+	if (local)
+	{
+		found = find_namespace_members_tags (local, tags,
+										 langJava, new_name, filename);
+		g_ptr_array_free (local, TRUE);
+	}
+
+
+	if (!found && search_global)
+	{
+		GPtrArray *global = tm_tags_extract (theWorkspace->global_tags,
+											 (tm_tag_member_t |
+											  tm_tag_prototype_t |
+											  tm_tag_field_t |
+											  tm_tag_method_t |
+											  tm_tag_function_t |
+											  tm_tag_enumerator_t |
+											  tm_tag_namespace_t |
+											  tm_tag_class_t ));
+
+		if (global)
+		{
+			find_namespace_members_tags (global, tags, langJava,
+									 new_name, filename);
+/*/
+			DEBUG_PRINT ("returning these");
+  		    gint i;
+			for (i=0; i < tags->len; i++) {
+				TMTag *cur_tag;
+
+				cur_tag = (TMTag*)g_ptr_array_index (tags, i);
+				tm_tag_print (cur_tag, stdout );
+			}
+/*/
+			g_ptr_array_free (global, TRUE);
+		}
+	}
+
+
+	if (del)
+	{
+		g_free (new_name);
+	}
+
+	return tags;
+}
+#endif
+
+const GPtrArray *
+tm_workspace_find_scope_members (const GPtrArray * file_tags, const char *name,
+								 gboolean search_global, gboolean no_definitions)
+{
+	static GPtrArray *tags = NULL;
+	GPtrArray *local = NULL;
+	char *new_name = (char *) name;
+	char *filename = NULL;
+	int found = 0, del = 0;
+	static langType langJava = -1;
+	TMTag *tag = NULL;
+
+	/* FIXME */
+	/* langJava = getNamedLanguage ("Java"); */
+
+	g_return_val_if_fail ((theWorkspace && name && name[0] != '\0'), NULL);
+
+	if (!tags)
+		tags = g_ptr_array_new ();
+
+	while (1)
+	{
+		const GPtrArray *tags2;
+		int got = 0, types = (tm_tag_class_t | tm_tag_namespace_t |
+								tm_tag_struct_t | tm_tag_typedef_t |
+								tm_tag_union_t | tm_tag_enum_t);
+
+		if (file_tags)
+		{
+			g_ptr_array_set_size (tags, 0);
+			got = fill_find_tags_array (tags, file_tags,
+										  new_name, NULL, types, FALSE, -1, FALSE);
+		}
+		if (got)
+		{
+			tags2 = tags;
+		}
+		else
+		{
+			TMTagAttrType attrs[] = {
+				tm_tag_attr_name_t, tm_tag_attr_type_t,
+				tm_tag_attr_none_t
+			};
+			tags2 = tm_workspace_find (new_name, types, attrs, FALSE, -1);
+		}
+
+		if ((tags2) && (tags2->len == 1) && (tag = TM_TAG (tags2->pdata[0])))
+		{
+			if (tag->type == tm_tag_typedef_t && tag->atts.entry.var_type
+				&& tag->atts.entry.var_type[0] != '\0')
+			{
+				char *tmp_name;
+				tmp_name = tag->atts.entry.var_type;
+				if (strcmp(tmp_name, new_name) == 0) {
+					new_name = NULL;
+				}
+				else {
+					new_name = tmp_name;
+				}
+				continue;
+			}
+			filename = (tag->atts.entry.file ?
+						tag->atts.entry.file->work_object.short_name : NULL);
+			if (tag->atts.entry.scope && tag->atts.entry.scope[0] != '\0')
+			{
+				del = 1;
+				if (tag->atts.entry.file &&
+					tag->atts.entry.file->lang == langJava)
+				{
+					new_name = g_strdup_printf ("%s.%s",
+												tag->atts.entry.scope,
+												new_name);
+				}
+				else
+				{
+					new_name = g_strdup_printf ("%s::%s",
+												tag->atts.entry.scope,
+												new_name);
+				}
+			}
+			break;
+		}
+		else
+		{
+			return NULL;
+		}
+	}
+
+	g_ptr_array_set_size (tags, 0);
+
+	if (no_definitions && tag && tag->atts.entry.file)
+	{
+		local = tm_tags_extract (tag->atts.entry.file->work_object.tags_array,
+								 (tm_tag_function_t | tm_tag_prototype_t |
+								  tm_tag_member_t | tm_tag_field_t |
+								  tm_tag_method_t | tm_tag_enumerator_t));
+	}
+	else
+	{
+		local = tm_tags_extract (theWorkspace->work_object.tags_array,
+								 (tm_tag_function_t | tm_tag_prototype_t |
+								  tm_tag_member_t | tm_tag_field_t |
+								  tm_tag_method_t | tm_tag_enumerator_t));
+	}
+	if (local)
+	{
+		found = find_scope_members_tags (local, tags, langJava, new_name,
+										 filename, no_definitions);
+		g_ptr_array_free (local, TRUE);
+	}
+	if (!found && search_global)
+	{
+		GPtrArray *global = tm_tags_extract (theWorkspace->global_tags,
+											 (tm_tag_member_t |
+											  tm_tag_prototype_t |
+											  tm_tag_field_t |
+											  tm_tag_method_t |
+											  tm_tag_function_t |
+											  tm_tag_enumerator_t
+											  |tm_tag_struct_t | tm_tag_typedef_t |
+											  tm_tag_union_t | tm_tag_enum_t));
+		if (global)
+		{
+			find_scope_members_tags (global, tags, langJava, new_name,
+									 filename, no_definitions);
+			g_ptr_array_free (global, TRUE);
+		}
+	}
+	if (del)
+	{
+		g_free (new_name);
+	}
+
+	return tags;
+}
+
 const GPtrArray *tm_workspace_get_parents(const gchar *name)
 {
 	static TMTagAttrType type[] = { tm_tag_attr_name_t, tm_tag_attr_none_t };


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.



More information about the Commits mailing list