Don't use temporary file when creating tag files without running preprocessor

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.
This commit is contained in:
Jiří Techet 2022-04-15 18:44:18 +02:00
parent 30f5514465
commit 8c7081c652
2 changed files with 80 additions and 78 deletions

View File

@ -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;
@ -503,47 +469,26 @@ 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)
{
@ -553,7 +498,6 @@ gboolean tm_workspace_create_global_tags(const char *pre_process_cmd, const char
}
else
goto cleanup;
}
source_file = tm_source_file_new(temp_file, tm_source_file_get_lang_name(lang));
if (!source_file)
@ -577,6 +521,66 @@ cleanup:
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)

View File

@ -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