[geany/geany] d453fe: Add support for loading Vi and CTags tag files

Colomban Wendling git-noreply at xxxxx
Wed Apr 3 20:10:33 UTC 2013


Branch:      refs/heads/master
Author:      Colomban Wendling <ban at herbesfolles.org>
Committer:   Colomban Wendling <ban at herbesfolles.org>
Date:        Wed, 03 Apr 2013 20:10:33 UTC
Commit:      d453fe33fe4139a7d90682cc60ac067ddc222b14
             https://github.com/geany/geany/commit/d453fe33fe4139a7d90682cc60ac067ddc222b14

Log Message:
-----------
Add support for loading Vi and CTags tag files

This allows to load tag files in the CTags format, which is compatible
with Vi format.

References:
 * http://ctags.sourceforge.net/FORMAT
 * http://ctags.sourceforge.net/ctags.html#TAG%20FILE%20FORMAT


Modified Paths:
--------------
    doc/geany.txt
    tagmanager/src/tm_tag.c
    tagmanager/src/tm_tag.h
    tagmanager/src/tm_workspace.c

Modified: doc/geany.txt
23 files changed, 17 insertions(+), 6 deletions(-)
===================================================================
@@ -1601,16 +1601,17 @@ corresponding filetype is first used. Currently these are for:
 Global tags file format
 ```````````````````````
 
-Global tags files can have two different formats:
+Global tags files can have three different formats:
 
 * Tagmanager format
 * Pipe-separated format
+* CTags format
 
 The first line of global tags files should be a comment, introduced
-by ``#`` followed by a space and a string like ``format=pipe``
-or ``format=tagmanager`` respectively, these are case-sensitive.
-This helps Geany to read the file properly. If this line
-is missing, Geany tries to auto-detect the used format but this
+by ``#`` followed by a space and a string like ``format=pipe``,
+``format=ctags`` or ``format=tagmanager`` respectively, these are
+case-sensitive.  This helps Geany to read the file properly. If this
+line is missing, Geany tries to auto-detect the used format but this
 might fail.
 
 
@@ -1618,7 +1619,8 @@ The Tagmanager format is a bit more complex and is used for files
 created by the ``geany -g`` command. There is one tag per line.
 Different tag attributes like the return value or the argument list
 are separated with different characters indicating the type of the
-following argument.
+following argument.  This is the more complete and recommended tag
+format.
 
 Pipe-separated format
 *********************
@@ -1641,6 +1643,15 @@ You can easily write your own global tag files using this format.
 Just save them in your tags directory, as described earlier in the
 section `Global tags`_.
 
+CTags format
+************
+This is the format that ctags generates, and that is used by Vim.
+This format is compatible with the format historically used by Vi.
+
+The format is described at http://ctags.sourceforge.net/FORMAT, but
+for the full list of existing extensions please refer to ctags.
+However, note that Geany may actually only honor a subset of the
+existing extensions.
 
 Generating a global tags file
 `````````````````````````````


Modified: tagmanager/src/tm_tag.c
226 files changed, 189 insertions(+), 37 deletions(-)
===================================================================
@@ -177,6 +177,37 @@ static int get_tag_type(const char *tag_name)
 	return tm_tag_undef_t;
 }
 
+static char get_tag_impl(const char *impl)
+{
+	if ((0 == strcmp("virtual", impl))
+	 || (0 == strcmp("pure virtual", impl)))
+		return TAG_IMPL_VIRTUAL;
+
+#ifdef TM_DEBUG
+		g_warning("Unknown implementation %s", impl);
+#endif
+	return TAG_IMPL_UNKNOWN;
+}
+
+static char get_tag_access(const char *access)
+{
+	if (0 == strcmp("public", access))
+		return TAG_ACCESS_PUBLIC;
+	else if (0 == strcmp("protected", access))
+		return TAG_ACCESS_PROTECTED;
+	else if (0 == strcmp("private", access))
+		return TAG_ACCESS_PRIVATE;
+	else if (0 == strcmp("friend", access))
+		return TAG_ACCESS_FRIEND;
+	else if (0 == strcmp("default", access))
+		return TAG_ACCESS_DEFAULT;
+
+#ifdef TM_DEBUG
+	g_warning("Unknown access type %s", access);
+#endif
+	return TAG_ACCESS_UNKNOWN;
+}
+
 gboolean tm_tag_init(TMTag *tag, TMSourceFile *file, const tagEntryInfo *tag_entry)
 {
 	tag->refcount = 1;
@@ -217,38 +248,9 @@ gboolean tm_tag_init(TMTag *tag, TMSourceFile *file, const tagEntryInfo *tag_ent
 		if (tag_entry->extensionFields.varType != NULL)
 			tag->atts.entry.var_type = g_strdup(tag_entry->extensionFields.varType);
 		if (tag_entry->extensionFields.access != NULL)
-		{
-			if (0 == strcmp("public", tag_entry->extensionFields.access))
-				tag->atts.entry.access = TAG_ACCESS_PUBLIC;
-			else if (0 == strcmp("protected", tag_entry->extensionFields.access))
-				tag->atts.entry.access = TAG_ACCESS_PROTECTED;
-			else if (0 == strcmp("private", tag_entry->extensionFields.access))
-				tag->atts.entry.access = TAG_ACCESS_PRIVATE;
-			else if (0 == strcmp("friend", tag_entry->extensionFields.access))
-				tag->atts.entry.access = TAG_ACCESS_FRIEND;
-			else if (0 == strcmp("default", tag_entry->extensionFields.access))
-				tag->atts.entry.access = TAG_ACCESS_DEFAULT;
-			else
-			{
-#ifdef TM_DEBUG
-				g_warning("Unknown access type %s", tag_entry->extensionFields.access);
-#endif
-				tag->atts.entry.access = TAG_ACCESS_UNKNOWN;
-			}
-		}
+			tag->atts.entry.access = get_tag_access(tag_entry->extensionFields.access);
 		if (tag_entry->extensionFields.implementation != NULL)
-		{
-			if ((0 == strcmp("virtual", tag_entry->extensionFields.implementation))
-			  || (0 == strcmp("pure virtual", tag_entry->extensionFields.implementation)))
-				tag->atts.entry.impl = TAG_IMPL_VIRTUAL;
-			else
-			{
-#ifdef TM_DEBUG
-				g_warning("Unknown implementation %s", tag_entry->extensionFields.implementation);
-#endif
-				tag->atts.entry.impl = TAG_IMPL_UNKNOWN;
-			}
-		}
+			tag->atts.entry.impl = get_tag_impl(tag_entry->extensionFields.implementation);
 		if ((tm_tag_macro_t == tag->type) && (NULL != tag->atts.entry.arglist))
 			tag->type = tm_tag_macro_with_arg_t;
 		tag->atts.entry.file = file;
@@ -416,17 +418,167 @@ gboolean tm_tag_init_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp)
 	return TRUE;
 }
 
-TMTag *tm_tag_new_from_file(TMSourceFile *file, FILE *fp, gint mode, gboolean format_pipe)
+/* Reads ctags format (http://ctags.sourceforge.net/FORMAT) */
+gboolean tm_tag_init_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp)
+{
+	gchar buf[BUFSIZ];
+	gchar *p, *tab;
+
+	tag->refcount = 1;
+	tag->type = tm_tag_function_t; /* default type is function if no kind is specified */
+	do
+	{
+		if ((NULL == fgets(buf, BUFSIZ, fp)) || ('\0' == *buf))
+			return FALSE;
+	}
+	while (strncmp(buf, "!_TAG_", 6) == 0); /* skip !_TAG_ lines */
+
+	p = buf;
+
+	/* tag name */
+	if (! (tab = strchr(p, '\t')) || p == tab)
+		return FALSE;
+	tag->name = g_strndup(p, (gsize)(tab - p));
+	p = tab + 1;
+
+	/* tagfile, unused */
+	if (! (tab = strchr(p, '\t')))
+	{
+		g_free(tag->name);
+		tag->name = NULL;
+		return FALSE;
+	}
+	p = tab + 1;
+	/* Ex command, unused */
+	if (*p == '/' || *p == '?')
+	{
+		gchar c = *p;
+		for (++p; *p && *p != c; p++)
+		{
+			if (*p == '\\' && p[1])
+				p++;
+		}
+	}
+	else /* assume a line */
+		tag->atts.entry.line = atol(p);
+	tab = strstr(p, ";\"");
+	/* read extension fields */
+	if (tab)
+	{
+		p = tab + 2;
+		while (*p && *p != '\n' && *p != '\r')
+		{
+			gchar *end;
+			const gchar *key, *value = NULL;
+
+			/* skip leading tabulations */
+			while (*p && *p == '\t') p++;
+			/* find the separator (:) and end (\t) */
+			key = end = p;
+			while (*end && *end != '\t' && *end != '\n' && *end != '\r')
+			{
+				if (*end == ':' && ! value)
+				{
+					*end = 0; /* terminate the key */
+					value = end + 1;
+				}
+				end++;
+			}
+			/* move p paste the so we won't stop parsing by setting *end=0 below */
+			p = *end ? end + 1 : end;
+			*end = 0; /* terminate the value (or key if no value) */
+
+			if (! value || 0 == strcmp(key, "kind")) /* tag kind */
+			{
+				const gchar *kind = value ? value : key;
+
+				if (kind[0] && kind[1])
+					tag->type = get_tag_type(kind);
+				else
+				{
+					switch (*kind)
+					{
+						case 'c': tag->type = tm_tag_class_t; break;
+						case 'd': tag->type = tm_tag_macro_t; break;
+						case 'e': tag->type = tm_tag_enumerator_t; break;
+						case 'F': tag->type = tm_tag_file_t; break;
+						case 'f': tag->type = tm_tag_function_t; break;
+						case 'g': tag->type = tm_tag_enum_t; break;
+						case 'I': tag->type = tm_tag_class_t; break;
+						case 'i': tag->type = tm_tag_interface_t; break;
+						case 'l': tag->type = tm_tag_variable_t; break;
+						case 'M': tag->type = tm_tag_macro_t; break;
+						case 'm': tag->type = tm_tag_member_t; break;
+						case 'n': tag->type = tm_tag_namespace_t; break;
+						case 'P': tag->type = tm_tag_package_t; break;
+						case 'p': tag->type = tm_tag_prototype_t; break;
+						case 's': tag->type = tm_tag_struct_t; break;
+						case 't': tag->type = tm_tag_typedef_t; break;
+						case 'u': tag->type = tm_tag_union_t; break;
+						case 'v': tag->type = tm_tag_variable_t; break;
+						case 'x': tag->type = tm_tag_externvar_t; break;
+						default:
+#ifdef TM_DEBUG
+							g_warning("Unknown tag kind %c", *kind);
+#endif
+							tag->type = tm_tag_other_t; break;
+					}
+				}
+			}
+			else if (0 == strcmp(key, "inherits")) /* comma-separated list of classes this class inherits from */
+			{
+				g_free(tag->atts.entry.inheritance);
+				tag->atts.entry.inheritance = g_strdup(value);
+			}
+			else if (0 == strcmp(key, "implementation")) /* implementation limit */
+				tag->atts.entry.impl = get_tag_impl(value);
+			else if (0 == strcmp(key, "line")) /* line */
+				tag->atts.entry.line = atol(value);
+			else if (0 == strcmp(key, "access")) /* access */
+				tag->atts.entry.access = get_tag_access(value);
+			else if (0 == strcmp(key, "class") ||
+					 0 == strcmp(key, "enum") ||
+					 0 == strcmp(key, "function") ||
+					 0 == strcmp(key, "struct") ||
+					 0 == strcmp(key, "union")) /* Name of the class/enum/function/struct/union in which this tag is a member */
+			{
+				g_free(tag->atts.entry.scope);
+				tag->atts.entry.scope = g_strdup(value);
+			}
+			else if (0 == strcmp(key, "file")) /* static (local) tag */
+				tag->atts.entry.local = TRUE;
+			else if (0 == strcmp(key, "signature")) /* arglist */
+			{
+				g_free(tag->atts.entry.arglist);
+				tag->atts.entry.arglist = g_strdup(value);
+			}
+		}
+	}
+
+	if (tm_tag_file_t != tag->type)
+		tag->atts.entry.file = file;
+	return TRUE;
+}
+
+TMTag *tm_tag_new_from_file(TMSourceFile *file, FILE *fp, gint mode, TMFileFormat format)
 {
 	TMTag *tag;
-	gboolean result;
+	gboolean result = FALSE;
 
 	TAG_NEW(tag);
 
-	if (format_pipe)
-		result = tm_tag_init_from_file_alt(tag, file, fp);
-	else
-		result = tm_tag_init_from_file(tag, file, fp);
+	switch (format)
+	{
+		case TM_FILE_FORMAT_TAGMANAGER:
+			result = tm_tag_init_from_file(tag, file, fp);
+			break;
+		case TM_FILE_FORMAT_PIPE:
+			result = tm_tag_init_from_file_alt(tag, file, fp);
+			break;
+		case TM_FILE_FORMAT_CTAGS:
+			result = tm_tag_init_from_file_ctags(tag, file, fp);
+			break;
+	}
 
 	if (! result)
 	{


Modified: tagmanager/src/tm_tag.h
13 files changed, 12 insertions(+), 1 deletions(-)
===================================================================
@@ -151,6 +151,12 @@
 	gint refcount; /*!< the reference count of the tag */
 } TMTag;
 
+typedef enum {
+	TM_FILE_FORMAT_TAGMANAGER,
+	TM_FILE_FORMAT_PIPE,
+	TM_FILE_FORMAT_CTAGS
+} TMFileFormat;
+
 /*!
  Prototype for user-defined tag comparison function. This is the type
  of argument that needs to be passed to tm_tags_sort_custom() and
@@ -195,6 +201,11 @@
 gboolean tm_tag_init_from_file_alt(TMTag *tag, TMSourceFile *file, FILE *fp);
 
 /*!
+ Same as tm_tag_init_from_file(), but parsing CTags tag file format
+*/
+gboolean tm_tag_init_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *fp);
+
+/*!
  Creates a new tag structure from a tagEntryInfo pointer and a TMSOurceFile pointer
  and returns a pointer to it.
  \param file - Pointer to the TMSourceFile structure containing the tag
@@ -207,7 +218,7 @@
  Same as tm_tag_new() except that the tag attributes are read from file.
  \param mode langType to use for the tag.
 */
-TMTag *tm_tag_new_from_file(TMSourceFile *file, FILE *fp, gint mode, gboolean format_pipe);
+TMTag *tm_tag_new_from_file(TMSourceFile *file, FILE *fp, gint mode, TMFileFormat format);
 
 /*!
  Writes tag information to the given FILE *.


Modified: tagmanager/src/tm_workspace.c
21 files changed, 15 insertions(+), 6 deletions(-)
===================================================================
@@ -146,7 +146,7 @@ gboolean tm_workspace_load_global_tags(const char *tags_file, gint mode)
 	guchar buf[BUFSIZ];
 	FILE *fp;
 	TMTag *tag;
-	gboolean format_pipe = FALSE;
+	TMFileFormat format = TM_FILE_FORMAT_TAGMANAGER;
 
 	if (NULL == theWorkspace)
 		return FALSE;
@@ -163,24 +163,33 @@ gboolean tm_workspace_load_global_tags(const char *tags_file, gint mode)
 	else
 	{	/* We read the first line for the format specification. */
 		if (buf[0] == '#' && strstr((gchar*) buf, "format=pipe") != NULL)
-			format_pipe = TRUE;
+			format = TM_FILE_FORMAT_PIPE;
 		else if (buf[0] == '#' && strstr((gchar*) buf, "format=tagmanager") != NULL)
-			format_pipe = FALSE;
+			format = TM_FILE_FORMAT_TAGMANAGER;
+		else if (buf[0] == '#' && strstr((gchar*) buf, "format=ctags") != NULL)
+			format = TM_FILE_FORMAT_CTAGS;
+		else if (strncmp((gchar*) buf, "!_TAG_", 6) == 0)
+			format = TM_FILE_FORMAT_CTAGS;
 		else
 		{	/* We didn't find a valid format specification, so we try to auto-detect the format
 			 * by counting the pipe characters on the first line and asumme pipe format when
 			 * we find more than one pipe on the line. */
-			guint i, pipe_cnt = 0;
+			guint i, pipe_cnt = 0, tab_cnt = 0;
 			for (i = 0; i < BUFSIZ && buf[i] != '\0' && pipe_cnt < 2; i++)
 			{
 				if (buf[i] == '|')
 					pipe_cnt++;
+				else if (buf[i] == '\t')
+					tab_cnt++;
 			}
-			format_pipe = (pipe_cnt > 1);
+			if (pipe_cnt > 1)
+				format = TM_FILE_FORMAT_PIPE;
+			else if (tab_cnt > 1)
+				format = TM_FILE_FORMAT_CTAGS;
 		}
 		rewind(fp); /* reset the file pointer, to start reading again from the beginning */
 	}
-	while (NULL != (tag = tm_tag_new_from_file(NULL, fp, mode, format_pipe)))
+	while (NULL != (tag = tm_tag_new_from_file(NULL, fp, mode, format)))
 		g_ptr_array_add(theWorkspace->global_tags, tag);
 	fclose(fp);
 



--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).


More information about the Commits mailing list