Improve symbols_get_current_function() a lot and make it more flexible

Finding the current function now better handles the case the current
line is after a function but outside its scope, and many other issues
the scope reporting had.
This commit is contained in:
Colomban Wendling 2012-09-17 22:41:24 +02:00
parent 9d2dab8fcf
commit 491a45f614

View File

@ -1959,20 +1959,22 @@ static gint get_function_fold_number(GeanyDocument *doc)
}
/* Should be used only with symbols_get_current_function. */
static gboolean current_function_changed(GeanyDocument *doc, gint cur_line, gint fold_level)
/* Should be used only with get_current_tag_cached.
* tag_types caching might trigger recomputation too often but this isn't used differently often
* enough to be an issue for now */
static gboolean current_tag_changed(GeanyDocument *doc, gint cur_line, gint fold_level, guint tag_types)
{
static gint old_line = -2;
static GeanyDocument *old_doc = NULL;
static gint old_fold_num = -1;
static guint old_tag_types = 0;
const gint fold_num = fold_level & SC_FOLDLEVELNUMBERMASK;
gboolean ret;
/* check if the cached line and file index have changed since last time: */
if (doc == NULL || doc != old_doc)
if (doc == NULL || doc != old_doc || old_tag_types != tag_types)
ret = TRUE;
else
if (cur_line == old_line)
else if (cur_line == old_line)
ret = FALSE;
else
{
@ -1989,6 +1991,7 @@ static gboolean current_function_changed(GeanyDocument *doc, gint cur_line, gint
old_line = cur_line;
old_doc = doc;
old_fold_num = fold_num;
old_tag_types = tag_types;
return ret;
}
@ -2069,83 +2072,79 @@ static gchar *parse_cpp_function_at_line(ScintillaObject *sci, gint tag_line)
}
/* Sets *tagname to point at the current function or tag name.
* If doc is NULL, reset the cached current tag data to ensure it will be reparsed on the next
* call to this function.
* Returns: line number of the current tag, or -1 if unknown. */
gint symbols_get_current_function(GeanyDocument *doc, const gchar **tagname)
static gint get_fold_header_after(ScintillaObject *sci, gint line)
{
static gint tag_line = -1;
static gchar *cur_tag = NULL;
gint line;
gint fold_level;
TMWorkObject *tm_file;
gint line_count = sci_get_line_count(sci);
if (doc == NULL) /* reset current function */
for (; line < line_count; line++)
{
current_function_changed(NULL, -1, -1);
g_free(cur_tag);
cur_tag = g_strdup(_("unknown"));
if (tagname != NULL)
*tagname = cur_tag;
tag_line = -1;
return tag_line;
if (sci_get_fold_level(sci, line) & SC_FOLDLEVELHEADERFLAG)
return line;
}
return -1;
}
static gint get_current_tag_name(GeanyDocument *doc, gchar **tagname, guint tag_types)
{
gint line;
gint parent;
line = sci_get_current_line(doc->editor->sci);
fold_level = sci_get_fold_level(doc->editor->sci, line);
/* check if the cached line and file index have changed since last time: */
if (! current_function_changed(doc, line, fold_level))
{
/* we can assume same current function as before */
*tagname = cur_tag;
return tag_line;
}
g_free(cur_tag); /* free the old tag, it will be replaced. */
/* if line is at base fold level, we're not in a function */
if ((fold_level & SC_FOLDLEVELNUMBERMASK) == SC_FOLDLEVELBASE)
{
cur_tag = g_strdup(_("unknown"));
*tagname = cur_tag;
tag_line = -1;
return tag_line;
}
tm_file = doc->tm_file;
/* if the tags are up-to-date, get the previous function name from TM */
if (tm_file != NULL && tm_file->tags_array != NULL &&
parent = sci_get_fold_parent(doc->editor->sci, line);
/* if we're inside a fold level and we have up-to-date tags, get the function from TM */
if (parent >= 0 && doc->tm_file != NULL && doc->tm_file->tags_array != NULL &&
(! doc->changed || editor_prefs.autocompletion_update_freq > 0))
{
const TMTag *tag = (const TMTag*) tm_get_current_function(tm_file->tags_array, line + 1);
const TMTag *tag = tm_get_current_tag(doc->tm_file->tags_array, parent + 1, tag_types);
if (tag != NULL)
if (tag)
{
gchar *tmp;
tmp = tag->atts.entry.scope;
cur_tag = tmp ? g_strconcat(tmp, "::", tag->name, NULL) : g_strdup(tag->name);
*tagname = cur_tag;
tag_line = tag->atts.entry.line - 1;
return tag_line;
gint tag_line = tag->atts.entry.line - 1;
gint last_child = line + 1;
/* if it may be a false positive because we're inside a fold level not inside anything
* we match, e.g. a #if in C or C++, we check we're inside the fold level that start
* right after the tag we got from TM */
if (abs(tag_line - parent) > 1)
{
gint tag_fold = get_fold_header_after(doc->editor->sci, tag_line);
if (tag_fold >= 0)
last_child = scintilla_send_message(doc->editor->sci, SCI_GETLASTCHILD, tag_fold, -1);
}
if (line <= last_child)
{
if (tag->atts.entry.scope)
*tagname = g_strconcat(tag->atts.entry.scope,
symbols_get_context_separator(doc->file_type->id), tag->name, NULL);
else
*tagname = g_strdup(tag->name);
return tag_line;
}
}
}
/* parse the current function name here because TM line numbers may have changed,
* and it would take too long to reparse the whole file. */
if (doc->file_type != NULL && doc->file_type->id != GEANY_FILETYPES_NONE)
/* for the poor guy with a modified document and without real time tag parsing, we fallback
* to dirty and inaccurate hand-parsing */
else if (parent >= 0 && doc->file_type != NULL && doc->file_type->id != GEANY_FILETYPES_NONE)
{
const gint fn_fold = get_function_fold_number(doc);
gint tag_line = parent;
gint fold_level = sci_get_fold_level(doc->editor->sci, tag_line);
tag_line = line;
do /* find the top level fold point */
/* find the top level fold point */
while (tag_line >= 0 && (fold_level & SC_FOLDLEVELNUMBERMASK) != fn_fold)
{
tag_line = sci_get_fold_parent(doc->editor->sci, tag_line);
fold_level = sci_get_fold_level(doc->editor->sci, tag_line);
} while (tag_line >= 0 &&
(fold_level & SC_FOLDLEVELNUMBERMASK) != fn_fold);
}
if (tag_line >= 0)
{
gchar *cur_tag;
if (sci_get_lexer(doc->editor->sci) == SCLEX_CPP)
cur_tag = parse_cpp_function_at_line(doc->editor->sci, tag_line);
else
@ -2159,13 +2158,52 @@ gint symbols_get_current_function(GeanyDocument *doc, const gchar **tagname)
}
}
cur_tag = g_strdup(_("unknown"));
*tagname = cur_tag;
tag_line = -1;
*tagname = g_strdup(_("unknown"));
return -1;
}
static gint get_current_tag_name_cached(GeanyDocument *doc, const gchar **tagname, guint tag_types)
{
static gint tag_line = -1;
static gchar *cur_tag = NULL;
if (doc == NULL) /* reset current function */
{
current_tag_changed(NULL, -1, -1, 0);
g_free(cur_tag);
cur_tag = g_strdup(_("unknown"));
if (tagname != NULL)
*tagname = cur_tag;
tag_line = -1;
}
else
{
gint line = sci_get_current_line(doc->editor->sci);
gint fold_level = sci_get_fold_level(doc->editor->sci, line);
if (current_tag_changed(doc, line, fold_level, tag_types))
{
g_free(cur_tag);
tag_line = get_current_tag_name(doc, &cur_tag, tag_types);
}
*tagname = cur_tag;
}
return tag_line;
}
/* Sets *tagname to point at the current function or tag name.
* If doc is NULL, reset the cached current tag data to ensure it will be reparsed on the next
* call to this function.
* Returns: line number of the current tag, or -1 if unknown. */
gint symbols_get_current_function(GeanyDocument *doc, const gchar **tagname)
{
return get_current_tag_name_cached(doc, tagname, tm_tag_function_t | tm_tag_method_t);
}
static void on_symbol_tree_sort_clicked(GtkMenuItem *menuitem, gpointer user_data)
{
gint sort_mode = GPOINTER_TO_INT(user_data);