Add option --generate-tags (-g) to generate a global tags file from

a list of source files. Currently this is only likely to work
correctly for C-like source files. Run 'geany -g' for syntax info.
Remove short option for hidden option --generate-data-files.
Update tm_workspace_create_global_tags() from Anjuta 1.2.4a.


git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@1411 ea778897-0a13-0410-b9d1-a72fbfd435f5
This commit is contained in:
Nick Treleaven 2007-03-21 13:26:16 +00:00
parent 1a8ecdbfe3
commit d3dfe1dd1d
5 changed files with 151 additions and 69 deletions

View File

@ -2,6 +2,12 @@
* src/project.c: * src/project.c:
Made all project dialogs modal. Made all project dialogs modal.
* src/main.c, src/symbols.c, src/symbols.h, tagmanager/tm_workspace.c:
Add option --generate-tags (-g) to generate a global tags file from
a list of source files. Currently this is only likely to work
correctly for C-like source files. Run 'geany -g' for syntax info.
Remove short option for hidden option --generate-data-files.
Update tm_workspace_create_global_tags() from Anjuta 1.2.4a.
2007-03-20 Enrico Tröger <enrico.troeger@uvena.de> 2007-03-20 Enrico Tröger <enrico.troeger@uvena.de>

View File

@ -87,13 +87,15 @@ static gchar *lib_vte = NULL;
static gboolean ignore_socket = FALSE; static gboolean ignore_socket = FALSE;
#endif #endif
static gboolean generate_datafiles = FALSE; static gboolean generate_datafiles = FALSE;
static gboolean generate_tags = FALSE;
static GOptionEntry entries[] = static GOptionEntry entries[] =
{ {
{ "column", 0, 0, G_OPTION_ARG_INT, &cl_options.goto_column, N_("set initial column number for the first opened file (useful in conjunction with --line)"), NULL }, { "column", 0, 0, G_OPTION_ARG_INT, &cl_options.goto_column, N_("set initial column number for the first opened file (useful in conjunction with --line)"), NULL },
{ "config", 'c', 0, G_OPTION_ARG_FILENAME, &alternate_config, N_("use an alternate configuration directory"), NULL }, { "config", 'c', 0, G_OPTION_ARG_FILENAME, &alternate_config, N_("use an alternate configuration directory"), NULL },
{ "debug", 'd', 0, G_OPTION_ARG_NONE, &debug_mode, N_("runs in debug mode (means being verbose)"), NULL }, { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug_mode, N_("runs in debug mode (means being verbose)"), NULL },
{ "generate-data-files", 'g', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &generate_datafiles, "", NULL }, { "generate-tags", 'g', 0, G_OPTION_ARG_NONE, &generate_tags, N_("generate global tags file (see documentation)"), NULL },
{ "generate-data-files", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &generate_datafiles, "", NULL },
#ifdef HAVE_SOCKET #ifdef HAVE_SOCKET
{ "new-instance", 'i', 0, G_OPTION_ARG_NONE, &ignore_socket, N_("don't open files in a running instance, force opening a new instance"), NULL }, { "new-instance", 'i', 0, G_OPTION_ARG_NONE, &ignore_socket, N_("don't open files in a running instance, force opening a new instance"), NULL },
#endif #endif
@ -433,6 +435,12 @@ static void parse_command_line_options(gint *argc, gchar ***argv)
exit(0); exit(0);
} }
if (generate_tags)
{
gboolean ret = symbols_generate_global_tags(*argc, *argv);
exit(ret);
}
app->debug_mode = debug_mode; app->debug_mode = debug_mode;
#ifdef GEANY_DEBUG #ifdef GEANY_DEBUG

View File

@ -32,6 +32,7 @@
#include <ctype.h> #include <ctype.h>
#include <string.h> #include <string.h>
#include <stdlib.h>
#include "symbols.h" #include "symbols.h"
#include "utils.h" #include "utils.h"
@ -688,3 +689,41 @@ gboolean symbols_recreate_tag_list(gint idx)
} }
/* Adapted from anjuta-2.0.2/global-tags/tm_global_tags.c, thanks.
* Needs full path and \" quoting characters around filenames.
* Example:
* geany -g tagsfile \"/home/nmt/svn/geany/src/d*.h\" */
int symbols_generate_global_tags(int argc, char **argv)
{
/* -E pre-process, -dD output user macros, -p prof info (?),
* -undef remove builtin macros (seems to be needed with FC5 gcc 4.1.1 */
const char pre_process[] = "gcc -E -dD -p -undef";
if (argc > 2)
{
/* Create global taglist */
int status;
char *command;
command = g_strdup_printf("%s %s", pre_process,
NVL(getenv("CFLAGS"), ""));
//printf(">%s<\n", command);
status = tm_workspace_create_global_tags(command,
(const char **) (argv + 2),
argc - 2, argv[1]);
g_free(command);
if (!status)
return 1;
}
else
{
fprintf(stderr, "Usage: %s -g <Tag File> <File list>\n\n", argv[0]);
fprintf(stderr, "Each file in <File list> must be enclosed in double quotes.\n");
fprintf(stderr, "Example:\n"
"CFLAGS=`pkg-config gtk+-2.0 --cflags` %s -g gtk2.c.tags"
" \\\"/usr/include/gtk-2.0/gtk/gtk.h\\\"\n", argv[0]);
return 1;
}
return 0;
}

View File

@ -48,4 +48,6 @@ void symbols_finalize();
gboolean symbols_recreate_tag_list(gint idx); gboolean symbols_recreate_tag_list(gint idx);
int symbols_generate_global_tags(int argc, char **argv);
#endif #endif

View File

@ -33,6 +33,7 @@
#include "tm_workspace.h" #include "tm_workspace.h"
#include "tm_project.h" #include "tm_project.h"
static TMWorkspace *theWorkspace = NULL; static TMWorkspace *theWorkspace = NULL;
guint workspace_class_id = 0; guint workspace_class_id = 0;
@ -156,6 +157,31 @@ gboolean tm_workspace_load_global_tags(const char *tags_file, gint mode)
return TRUE; return TRUE;
} }
static guint tm_file_inode_hash(gconstpointer key)
{
struct stat file_stat;
const char *filename = (const char*)key;
if (stat(filename, &file_stat) == 0)
{
#ifdef TM_DEBUG
g_message ("Hash for '%s' is '%d'\n", filename, file_stat.st_ino);
#endif
return g_direct_hash (GUINT_TO_POINTER (file_stat.st_ino));
} else {
return 0;
}
}
static void tm_move_entries_to_g_list(gpointer key, gpointer value, gpointer user_data)
{
GList **pp_list = (GList**)user_data;
if (user_data == NULL)
return;
*pp_list = g_list_prepend(*pp_list, value);
}
gboolean tm_workspace_create_global_tags(const char *pre_process, const char **includes gboolean tm_workspace_create_global_tags(const char *pre_process, const char **includes
, int includes_count, const char *tags_file) , int includes_count, const char *tags_file)
{ {
@ -169,11 +195,9 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i
FILE *fp; FILE *fp;
TMWorkObject *source_file; TMWorkObject *source_file;
GPtrArray *tags_array; GPtrArray *tags_array;
GHashTable *includes_files_hash;
GList *includes_files = NULL; GList *includes_files = NULL;
guint list_len; GList *node;
guint idx_main;
guint idx_sub;
int remove_count = 0;
#ifdef G_OS_WIN32 #ifdef G_OS_WIN32
char *temp_file = g_strdup_printf("%s_%d_%ld_1.cpp", P_tmpdir, getpid(), time(NULL)); char *temp_file = g_strdup_printf("%s_%d_%ld_1.cpp", P_tmpdir, getpid(), time(NULL));
char *temp_file2 = g_strdup_printf("%s_%d_%ld_2.cpp", P_tmpdir, getpid(), time(NULL)); char *temp_file2 = g_strdup_printf("%s_%d_%ld_2.cpp", P_tmpdir, getpid(), time(NULL));
@ -181,11 +205,18 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i
char *temp_file = g_strdup_printf("%s/%d_%ld_1.cpp", P_tmpdir, getpid(), time(NULL)); char *temp_file = g_strdup_printf("%s/%d_%ld_1.cpp", P_tmpdir, getpid(), time(NULL));
char *temp_file2 = g_strdup_printf("%s/%d_%ld_2.cpp", P_tmpdir, getpid(), time(NULL)); char *temp_file2 = g_strdup_printf("%s/%d_%ld_2.cpp", P_tmpdir, getpid(), time(NULL));
#endif #endif
TMTagAttrType sort_attrs[] = { tm_tag_attr_name_t, tm_tag_attr_scope_t TMTagAttrType sort_attrs[] = {
, tm_tag_attr_type_t, 0}; tm_tag_attr_name_t, tm_tag_attr_scope_t,
tm_tag_attr_type_t, 0
};
if (NULL == (fp = fopen(temp_file, "w"))) if (NULL == (fp = fopen(temp_file, "w")))
return FALSE; return FALSE;
includes_files_hash = g_hash_table_new_full (tm_file_inode_hash,
g_direct_equal,
NULL, g_free);
#ifdef HAVE_GLOB_H #ifdef HAVE_GLOB_H
globbuf.gl_offs = 0; globbuf.gl_offs = 0;
for(idx_inc = 0; idx_inc < includes_count; idx_inc++) for(idx_inc = 0; idx_inc < includes_count; idx_inc++)
@ -195,91 +226,87 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i
strncpy(clean_path, includes[idx_inc] + 1, dirty_len - 1); strncpy(clean_path, includes[idx_inc] + 1, dirty_len - 1);
clean_path[dirty_len - 2] = 0; clean_path[dirty_len - 2] = 0;
//printf("[o][%s]\n", clean_path); #ifdef TM_DEBUG
g_message ("[o][%s]\n", clean_path);
#endif
glob(clean_path, 0, NULL, &globbuf); glob(clean_path, 0, NULL, &globbuf);
//printf("matches: %d\n", globbuf.gl_pathc);
#ifdef TM_DEBUG
g_message ("matches: %d\n", globbuf.gl_pathc);
#endif
for(idx_glob = 0; idx_glob < globbuf.gl_pathc; idx_glob++) for(idx_glob = 0; idx_glob < globbuf.gl_pathc; idx_glob++)
{ {
includes_files = g_list_append(includes_files, strdup(globbuf.gl_pathv[idx_glob])); #ifdef TM_DEBUG
//printf(">>> %s\n", globbuf.gl_pathv[idx_glob]); g_message (">>> %s\n", globbuf.gl_pathv[idx_glob]);
#endif
if (!g_hash_table_lookup(includes_files_hash,
globbuf.gl_pathv[idx_glob]))
{
char* file_name_copy = strdup(globbuf.gl_pathv[idx_glob]);
g_hash_table_insert(includes_files_hash, file_name_copy,
file_name_copy);
#ifdef TM_DEBUG
g_message ("Added ...\n");
#endif
}
} }
globfree(&globbuf); globfree(&globbuf);
free(clean_path); free(clean_path);
} }
#else #else // no glob support
for(idx_inc = 0; idx_inc < includes_count; idx_inc++) for(idx_inc = 0; idx_inc < includes_count; idx_inc++)
{ {
includes_files = g_list_append(includes_files, strdup(includes[idx_inc])); if (!g_hash_table_lookup(includes_files_hash,
includes[idx_inc]))
{
char* file_name_copy = strdup(includes[idx_inc]);
g_hash_table_insert(includes_files_hash, file_name_copy,
file_name_copy);
}
} }
#endif #endif
/* Checks for duplicate file entries which would case trouble */ /* Checks for duplicate file entries which would case trouble */
g_hash_table_foreach(includes_files_hash, tm_move_entries_to_g_list,
&includes_files);
includes_files = g_list_reverse (includes_files);
#ifdef TM_DEBUG
g_message ("writing out files to %s\n", temp_file);
#endif
node = includes_files;
while (node)
{ {
struct stat main_stat; char *str = g_strdup_printf("#include \"%s\"\n", (char*)node->data);
struct stat sub_stat;
remove_count = 0;
list_len = g_list_length(includes_files);
/* We look for files with the same inode */
for(idx_main = 0; idx_main < list_len; idx_main++)
{
// printf("%d / %d\n", idx_main, list_len - 1);
stat(g_list_nth_data(includes_files, idx_main), &main_stat);
for(idx_sub = idx_main + 1; idx_sub < list_len; idx_sub++)
{
GList *element = NULL;
stat(g_list_nth_data(includes_files, idx_sub), &sub_stat);
if(main_stat.st_ino != sub_stat.st_ino)
continue;
/* Inodes match */
element = g_list_nth(includes_files, idx_sub);
/* printf("%s == %s\n", g_list_nth_data(includes_files, idx_main),
g_list_nth_data(includes_files, idx_sub)); */
/* We delete the duplicate entry from the list */
includes_files = g_list_remove_link(includes_files, element);
remove_count++;
/* Don't forget to free the mallocs (we duplicated every string earlier!) */
free(element->data);
idx_sub--; /* Cause the inner loop not to move; good since we removed
an element at the current position; we don't have to worry
about the outer loop because the inner loop always starts
after the outer loop's index */
list_len = g_list_length(includes_files);
}
}
}
printf("writing out files to %s\n", temp_file);
for(idx_main = 0; idx_main < g_list_length(includes_files); idx_main++)
{
char *str = g_strdup_printf("#include \"%s\"\n",
(char*)g_list_nth_data(includes_files,
idx_main));
int str_len = strlen(str); int str_len = strlen(str);
fwrite(str, str_len, 1, fp); fwrite(str, str_len, 1, fp);
free(str); free(str);
free(g_list_nth(includes_files, idx_main) -> data); node = g_list_next (node);
} }
g_list_free (includes_files);
g_hash_table_destroy(includes_files_hash);
includes_files_hash = NULL;
includes_files = NULL;
fclose(fp); fclose(fp);
command = g_strdup_printf("%s %s >%s", pre_process, temp_file, temp_file2); /* FIXME: The following grep command it be remove the lines
* G_BEGIN_DECLS and G_END_DECLS from the header files. The reason is
* that in tagmanager, the files are not correctly parsed and the typedefs
* following these lines are incorrectly parsed. The real fix should,
* of course be in tagmanager (c) parser. This is just a temporary fix.
*/
command = g_strdup_printf("%s %s | grep -v -E '^\\s*(G_BEGIN_DECLS|G_END_DECLS)\\s*$' > %s",
pre_process, temp_file, temp_file2);
#ifdef TM_DEBUG
g_message("Executing: %s", command);
#endif
system(command); system(command);
g_free(command); g_free(command);
unlink(temp_file); unlink(temp_file);