Skip to content

Commit

Permalink
Don't use temporary file when creating tag files without running prep…
Browse files Browse the repository at this point in the history
…rocessor

The tm_workspace_create_global_tags() function generates tags for
two cases:

1. Source files pre-processed by C pre-processor. It first generates
a file combining the parsed header files by creating a temporary
file in which all the header files are included and this file is passed
to the pre-processor. The result of the pre-processed file is then
parsed by the ctags parser.
2. Source files directly parsed by the ctags parser. In this case all
the source files are concatenated to a single file which is then parsed
by the ctags parser.

This patch leaves (1) more or less unchanged; however, the creation of
the temporary file in (2) is unnecessary - the individual files can
be parsed directly, the tags from all the parses can be combined, sorted
and pruned without creating the temporary file.

The temporary file is a problem for unit tests where some languages
use the file name as the name of module in which the tags are defined
and by using a different name, the unit test generates a different tag
file every time it's run.

Note the changed output of the process_order unit test caused by this
change. The test parses two files, one containing

enum {
	I1_E1,
	I1_E2,
};

the other contining

enum {
	I2_E1,
	I2_E2,
};

Previously, because the files were concatenated the enums were different
tags and the anonnymous tag renaming function renamed them to anon_enum_1
and anon_enum_2. Because now the files are parsed separately, the
enum from the first file gets renamed to anon_enum_1 and when parsing
the second file, we get the name anon_enum_1 as well for the second enum.
This however isn't a problem - we don't display global tags in the
symbol tree, autocompletion, and they are also ignored in scope completion
so this shouldn't matter much.
  • Loading branch information
techee committed Aug 28, 2022
1 parent 30f5514 commit 8c7081c
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 78 deletions.
148 changes: 76 additions & 72 deletions src/tagmanager/tm_workspace.c
Expand Up @@ -387,40 +387,6 @@ static gboolean write_includes_file(const gchar *outf, GList *includes_files)
return fclose(fp) == 0;
}


static gboolean combine_source_files(const gchar *outf, GList *file_list)
{
FILE *fp = g_fopen(outf, "w");
GList *node = file_list;

if (!fp)
return FALSE;

while (node)
{
const char *fname = node->data;
char *contents;
size_t length;
GError *err = NULL;

if (! g_file_get_contents(fname, &contents, &length, &err))
{
fprintf(stderr, "Unable to read file: %s\n", err->message);
g_error_free(err);
}
else
{
fwrite(contents, length, 1, fp);
fwrite("\n", 1, 1, fp); /* in case file doesn't end in newline (e.g. windows). */
g_free(contents);
}
node = g_list_next (node);
}

return fclose(fp) == 0;
}


static gchar *create_temp_file(const gchar *tpl)
{
gchar *name;
Expand Down Expand Up @@ -503,57 +469,35 @@ static gchar *pre_process_file(const gchar *cmd, const gchar *inf)
return outf;
}

/* Creates a list of global tags. Ideally, this should be created once during
installations so that all users can use the same file. This is because a full
scale global tag list can occupy several megabytes of disk space.
@param pre_process_cmd The pre-processing command. This is executed via system(),
so you can pass stuff like 'gcc -E -dD -P `gnome-config --cflags gnome`'.
@param sources Source files to process. Wildcards such as '/usr/include/a*.h'
are allowed.
@param tags_file The file where the tags will be stored.
@param lang The language to use for the tags file.
@return TRUE on success, FALSE on failure.
*/
gboolean tm_workspace_create_global_tags(const char *pre_process_cmd, const char **sources,
int sources_count, const char *tags_file, TMParserType lang)
static gboolean create_global_tags_preprocessed(const char *pre_process_cmd,
GList *source_files, const char *tags_file, TMParserType lang)
{
gboolean ret = FALSE;
TMSourceFile *source_file;
GList *source_files;
gboolean ret = FALSE;
gchar *temp_file2;
gchar *temp_file = create_temp_file("tmp_XXXXXX.cpp");
GPtrArray *filtered_tags;

if (!temp_file)
return FALSE;

source_files = lookup_sources(sources, sources_count);

#ifdef TM_DEBUG
g_message ("writing out files to %s\n", temp_file);
#endif
if (pre_process_cmd)
ret = write_includes_file(temp_file, source_files);
else
ret = combine_source_files(temp_file, source_files);

g_list_free_full(source_files, g_free);
if (!ret)
if (!temp_file)
return FALSE;

if (!write_includes_file(temp_file, source_files))
goto cleanup;
ret = FALSE;

if (pre_process_cmd)
{
gchar *temp_file2 = pre_process_file(pre_process_cmd, temp_file);
temp_file2 = pre_process_file(pre_process_cmd, temp_file);

if (temp_file2)
{
g_unlink(temp_file);
g_free(temp_file);
temp_file = temp_file2;
}
else
goto cleanup;
if (temp_file2)
{
g_unlink(temp_file);
g_free(temp_file);
temp_file = temp_file2;
}
else
goto cleanup;

source_file = tm_source_file_new(temp_file, tm_source_file_get_lang_name(lang));
if (!source_file)
Expand All @@ -577,6 +521,66 @@ gboolean tm_workspace_create_global_tags(const char *pre_process_cmd, const char
return ret;
}

static gboolean create_global_tags_direct(GList *source_files, const char *tags_file,
TMParserType lang)
{
GList *node;
GPtrArray *filtered_tags;
GPtrArray *tags = g_ptr_array_new();
GSList *tm_source_files = NULL;
gboolean ret = FALSE;

for (node = source_files; node; node = node->next)
{
TMSourceFile *source_file = tm_source_file_new(node->data, tm_source_file_get_lang_name(lang));
if (source_file)
{
guint i;
tm_source_files = g_slist_prepend(tm_source_files, source_file);
tm_source_file_parse(source_file, NULL, 0, FALSE);
for (i = 0; i < source_file->tags_array->len; i++)
g_ptr_array_add(tags, source_file->tags_array->pdata[i]);
}
}

filtered_tags = tm_tags_extract(tags, ~tm_tag_local_var_t);
tm_tags_sort(filtered_tags, global_tags_sort_attrs, TRUE, FALSE);

if (filtered_tags->len > 0)
ret = tm_source_file_write_tags_file(tags_file, filtered_tags);

g_ptr_array_free(tags, TRUE);
g_ptr_array_free(filtered_tags, TRUE);
g_slist_free_full(tm_source_files, (GDestroyNotify)tm_source_file_free);

return ret;
}

/* Creates a list of global tags. Ideally, this should be created once during
installations so that all users can use the same file. This is because a full
scale global tag list can occupy several megabytes of disk space.
@param pre_process_cmd The pre-processing command. This is executed via system(),
so you can pass stuff like 'gcc -E -dD -P `gnome-config --cflags gnome`'.
@param sources Source files to process. Wildcards such as '/usr/include/a*.h'
are allowed.
@param tags_file The file where the tags will be stored.
@param lang The language to use for the tags file.
@return TRUE on success, FALSE on failure.
*/
gboolean tm_workspace_create_global_tags(const char *pre_process_cmd, const char **sources,
int sources_count, const char *tags_file, TMParserType lang)
{
gboolean ret = FALSE;
GList *source_files = lookup_sources(sources, sources_count);

if (pre_process_cmd)
ret = create_global_tags_preprocessed(pre_process_cmd, source_files, tags_file, lang);
else
ret = create_global_tags_direct(source_files, tags_file, lang);

g_list_free_full(source_files, g_free);
return ret;
}

static void fill_find_tags_array(GPtrArray *dst, const GPtrArray *src,
const char *name, const char *scope, TMTagType type, TMParserType lang)
Expand Down
10 changes: 4 additions & 6 deletions tests/ctags/process_order.c.tags
Expand Up @@ -2,11 +2,9 @@ I1_E1
enumerator: anon_enum_1 :: I1_E1
I1_E2�4�anon_enum_1�0
enumerator: anon_enum_1 :: I1_E2
I2_E1�4�anon_enum_2�0
enumerator: anon_enum_2 :: I2_E1
I2_E2�4�anon_enum_2�0
enumerator: anon_enum_2 :: I2_E2
I2_E1�4�anon_enum_1�0
enumerator: anon_enum_1 :: I2_E1
I2_E2�4�anon_enum_1�0
enumerator: anon_enum_1 :: I2_E2
anon_enum_1�2�1
enum: anon_enum_1 flags: 1
anon_enum_2�2�1
enum: anon_enum_2 flags: 1

0 comments on commit 8c7081c

Please sign in to comment.