2006-10-02 11:31:54 +00:00
|
|
|
/*
|
|
|
|
* symbols.c - this file is part of Geany, a fast and lightweight IDE
|
|
|
|
*
|
2008-01-06 18:11:57 +00:00
|
|
|
* Copyright 2006-2008 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
|
|
|
|
* Copyright 2006-2008 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
|
2006-10-02 11:31:54 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
* $Id$
|
|
|
|
*/
|
|
|
|
|
2007-02-24 11:41:56 +00:00
|
|
|
/*
|
2008-11-03 17:25:35 +00:00
|
|
|
* Symbol Tree and TagManager-related convenience functions.
|
|
|
|
* TagManager parses tags for each document, and also adds them to the workspace (session).
|
|
|
|
* Global tags are lists of tags for each filetype, loaded when a document with a
|
2007-02-24 11:41:56 +00:00
|
|
|
* matching filetype is first loaded.
|
|
|
|
*/
|
|
|
|
|
2008-07-08 16:30:37 +00:00
|
|
|
#include "SciLexer.h"
|
2006-10-02 11:31:54 +00:00
|
|
|
#include "geany.h"
|
2006-11-08 11:42:05 +00:00
|
|
|
|
|
|
|
#include <ctype.h>
|
2007-01-13 13:25:05 +00:00
|
|
|
#include <string.h>
|
2007-03-21 13:26:16 +00:00
|
|
|
#include <stdlib.h>
|
2006-11-08 11:42:05 +00:00
|
|
|
|
2007-12-12 20:04:45 +00:00
|
|
|
#include "prefix.h"
|
2006-10-02 11:31:54 +00:00
|
|
|
#include "symbols.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "filetypes.h"
|
2006-11-08 11:42:05 +00:00
|
|
|
#include "encodings.h"
|
|
|
|
#include "document.h"
|
2008-06-02 15:31:59 +00:00
|
|
|
#include "documentprivate.h"
|
2007-01-14 12:12:18 +00:00
|
|
|
#include "support.h"
|
2007-04-18 15:21:33 +00:00
|
|
|
#include "msgwindow.h"
|
2007-07-20 16:33:16 +00:00
|
|
|
#include "treeviews.h"
|
2007-08-23 11:34:06 +00:00
|
|
|
#include "main.h"
|
2007-09-25 12:39:41 +00:00
|
|
|
#include "navqueue.h"
|
|
|
|
#include "ui_utils.h"
|
2008-04-23 16:47:42 +00:00
|
|
|
#include "editor.h"
|
2008-07-08 16:30:37 +00:00
|
|
|
#include "sciwrappers.h"
|
2006-10-02 11:31:54 +00:00
|
|
|
|
2007-07-28 14:10:49 +00:00
|
|
|
|
2006-12-16 17:18:53 +00:00
|
|
|
const guint TM_GLOBAL_TYPE_MASK =
|
|
|
|
tm_tag_class_t | tm_tag_enum_t | tm_tag_interface_t |
|
|
|
|
tm_tag_struct_t | tm_tag_typedef_t | tm_tag_union_t;
|
|
|
|
|
|
|
|
|
2007-02-06 16:13:57 +00:00
|
|
|
static gchar **html_entities = NULL;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
gboolean tags_loaded;
|
|
|
|
const gchar *tag_file;
|
|
|
|
} TagFileInfo;
|
|
|
|
|
2008-08-28 16:59:20 +00:00
|
|
|
/* Check before adding any more tags files, usually they should be downloaded separately. */
|
2008-02-27 13:17:29 +00:00
|
|
|
enum /* Geany tag files */
|
2006-10-02 11:31:54 +00:00
|
|
|
{
|
|
|
|
GTF_C,
|
|
|
|
GTF_PASCAL,
|
|
|
|
GTF_PHP,
|
|
|
|
GTF_HTML_ENTITIES,
|
|
|
|
GTF_LATEX,
|
2008-06-10 16:39:17 +00:00
|
|
|
GTF_PYTHON,
|
2006-10-02 11:31:54 +00:00
|
|
|
GTF_MAX
|
|
|
|
};
|
|
|
|
|
|
|
|
static TagFileInfo tag_file_info[GTF_MAX] =
|
|
|
|
{
|
|
|
|
{FALSE, "global.tags"},
|
|
|
|
{FALSE, "pascal.tags"},
|
|
|
|
{FALSE, "php.tags"},
|
|
|
|
{FALSE, "html_entities.tags"},
|
2008-06-10 16:39:17 +00:00
|
|
|
{FALSE, "latex.tags"},
|
|
|
|
{FALSE, "python.tags"}
|
2006-10-02 11:31:54 +00:00
|
|
|
};
|
|
|
|
|
2007-05-11 11:58:56 +00:00
|
|
|
static gchar *user_tags_dir;
|
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
static GPtrArray *top_level_iter_names = NULL;
|
|
|
|
|
2006-10-02 11:31:54 +00:00
|
|
|
|
2008-02-20 11:24:23 +00:00
|
|
|
static void html_tags_loaded(void);
|
2007-05-11 11:58:56 +00:00
|
|
|
static void load_user_tags(filetype_id ft_id);
|
2006-10-02 11:31:54 +00:00
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* get the tags_ignore list, exported by tagmanager's options.c */
|
2008-02-24 10:18:36 +00:00
|
|
|
extern gchar **c_tags_ignore;
|
2008-02-18 19:51:34 +00:00
|
|
|
|
2008-02-20 11:24:23 +00:00
|
|
|
static void load_c_ignore_tags(void)
|
2008-02-18 19:51:34 +00:00
|
|
|
{
|
|
|
|
gchar *path = g_strconcat(app->configdir, G_DIR_SEPARATOR_S "ignore.tags", NULL);
|
|
|
|
gchar *content;
|
|
|
|
|
|
|
|
if (g_file_get_contents(path, &content, NULL, NULL))
|
|
|
|
{
|
|
|
|
c_tags_ignore = g_strsplit_set(content, " \n\r", -1);
|
|
|
|
g_free(content);
|
|
|
|
}
|
|
|
|
g_free(path);
|
|
|
|
}
|
|
|
|
|
2006-10-02 11:31:54 +00:00
|
|
|
|
2008-02-26 17:11:49 +00:00
|
|
|
/* Ensure that the global tags file(s) for the file_type_idx filetype is loaded.
|
|
|
|
* This provides autocompletion, calltips, etc. */
|
2006-10-02 11:31:54 +00:00
|
|
|
void symbols_global_tags_loaded(gint file_type_idx)
|
|
|
|
{
|
|
|
|
TagFileInfo *tfi;
|
2007-02-06 16:13:57 +00:00
|
|
|
gint tag_type;
|
2006-10-02 11:31:54 +00:00
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* load ignore list for C/C++ parser */
|
2008-02-18 19:51:34 +00:00
|
|
|
if ((file_type_idx == GEANY_FILETYPES_C || file_type_idx == GEANY_FILETYPES_CPP) &&
|
|
|
|
c_tags_ignore == NULL)
|
|
|
|
{
|
|
|
|
load_c_ignore_tags();
|
|
|
|
}
|
|
|
|
|
2008-02-26 17:11:49 +00:00
|
|
|
if (cl_options.ignore_global_tags || app->tm_workspace == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
load_user_tags(file_type_idx);
|
|
|
|
|
2006-10-02 11:31:54 +00:00
|
|
|
switch (file_type_idx)
|
|
|
|
{
|
2007-03-26 12:18:51 +00:00
|
|
|
case GEANY_FILETYPES_PHP:
|
2006-10-02 11:31:54 +00:00
|
|
|
case GEANY_FILETYPES_HTML:
|
|
|
|
html_tags_loaded();
|
2007-03-26 12:18:51 +00:00
|
|
|
}
|
|
|
|
switch (file_type_idx)
|
|
|
|
{
|
2007-02-06 16:13:57 +00:00
|
|
|
case GEANY_FILETYPES_CPP:
|
2008-02-27 13:17:29 +00:00
|
|
|
symbols_global_tags_loaded(GEANY_FILETYPES_C); /* load C global tags */
|
|
|
|
/* no C++ tagfile yet */
|
2007-03-26 12:18:51 +00:00
|
|
|
return;
|
2007-02-06 16:13:57 +00:00
|
|
|
case GEANY_FILETYPES_C: tag_type = GTF_C; break;
|
|
|
|
case GEANY_FILETYPES_PASCAL:tag_type = GTF_PASCAL; break;
|
|
|
|
case GEANY_FILETYPES_PHP: tag_type = GTF_PHP; break;
|
|
|
|
case GEANY_FILETYPES_LATEX: tag_type = GTF_LATEX; break;
|
2008-06-10 16:39:17 +00:00
|
|
|
case GEANY_FILETYPES_PYTHON:tag_type = GTF_PYTHON; break;
|
2006-10-02 11:31:54 +00:00
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
2007-02-06 16:13:57 +00:00
|
|
|
tfi = &tag_file_info[tag_type];
|
2006-10-02 11:31:54 +00:00
|
|
|
|
|
|
|
if (! tfi->tags_loaded)
|
|
|
|
{
|
2007-02-06 16:13:57 +00:00
|
|
|
gchar *fname = g_strconcat(app->datadir, G_DIR_SEPARATOR_S, tfi->tag_file, NULL);
|
|
|
|
gint tm_lang;
|
|
|
|
|
|
|
|
tm_lang = filetypes[file_type_idx]->lang;
|
|
|
|
tm_workspace_load_global_tags(fname, tm_lang);
|
2006-10-02 11:31:54 +00:00
|
|
|
tfi->tags_loaded = TRUE;
|
2007-02-06 16:13:57 +00:00
|
|
|
g_free(fname);
|
2006-10-02 11:31:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* HTML tagfile is just a list of entities for autocompletion (e.g. '&') */
|
2008-02-20 11:24:23 +00:00
|
|
|
static void html_tags_loaded(void)
|
2006-10-02 11:31:54 +00:00
|
|
|
{
|
|
|
|
TagFileInfo *tfi;
|
|
|
|
|
2007-08-23 11:34:06 +00:00
|
|
|
if (cl_options.ignore_global_tags) return;
|
2006-10-02 11:31:54 +00:00
|
|
|
|
|
|
|
tfi = &tag_file_info[GTF_HTML_ENTITIES];
|
|
|
|
if (! tfi->tags_loaded)
|
|
|
|
{
|
|
|
|
gchar *file = g_strconcat(app->datadir, G_DIR_SEPARATOR_S, tfi->tag_file, NULL);
|
2007-02-06 16:13:57 +00:00
|
|
|
|
2006-10-02 11:31:54 +00:00
|
|
|
html_entities = utils_read_file_in_array(file);
|
|
|
|
tfi->tags_loaded = TRUE;
|
|
|
|
g_free(file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-09 12:04:15 +00:00
|
|
|
GString *symbols_find_tags_as_string(GPtrArray *tags_array, guint tag_types, gint lang)
|
2006-10-02 11:31:54 +00:00
|
|
|
{
|
2006-11-08 11:42:05 +00:00
|
|
|
guint j;
|
2007-10-09 12:04:15 +00:00
|
|
|
TMTag *tag;
|
2006-10-02 11:31:54 +00:00
|
|
|
GString *s = NULL;
|
2006-11-08 11:42:05 +00:00
|
|
|
GPtrArray *typedefs;
|
2006-10-02 11:31:54 +00:00
|
|
|
|
2006-11-08 11:42:05 +00:00
|
|
|
g_return_val_if_fail(tags_array != NULL, NULL);
|
2006-10-02 11:31:54 +00:00
|
|
|
|
2006-11-08 11:42:05 +00:00
|
|
|
typedefs = tm_tags_extract(tags_array, tag_types);
|
|
|
|
|
|
|
|
if ((typedefs) && (typedefs->len > 0))
|
|
|
|
{
|
|
|
|
s = g_string_sized_new(typedefs->len * 10);
|
|
|
|
for (j = 0; j < typedefs->len; ++j)
|
2006-10-02 11:31:54 +00:00
|
|
|
{
|
2007-10-09 12:04:15 +00:00
|
|
|
tag = TM_TAG(typedefs->pdata[j]);
|
|
|
|
if (!(tag->atts.entry.scope))
|
2006-10-02 11:31:54 +00:00
|
|
|
{
|
2008-02-27 13:17:29 +00:00
|
|
|
/* tag->atts.file.lang contains (for some reason) the line of the tag if
|
|
|
|
* tag->atts.entry.file is not NULL */
|
2007-10-09 12:04:15 +00:00
|
|
|
gint tag_lang =
|
|
|
|
(tag->atts.entry.file) ? tag->atts.entry.file->lang : tag->atts.file.lang;
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* the check for tag_lang == lang is necessary to avoid wrong type colouring of
|
|
|
|
* e.g. PHP classes in C++ files
|
|
|
|
* lang = -2 disables the check */
|
2007-10-09 12:04:15 +00:00
|
|
|
if (tag->name && (tag_lang == lang || lang == -2))
|
2006-10-02 11:31:54 +00:00
|
|
|
{
|
2006-12-16 17:18:53 +00:00
|
|
|
if (j != 0)
|
|
|
|
g_string_append_c(s, ' ');
|
2007-10-09 12:04:15 +00:00
|
|
|
g_string_append(s, tag->name);
|
2006-10-02 11:31:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-03-24 17:24:39 +00:00
|
|
|
if (typedefs)
|
|
|
|
g_ptr_array_free(typedefs, TRUE);
|
2006-10-02 11:31:54 +00:00
|
|
|
return s;
|
|
|
|
}
|
2006-11-08 11:42:05 +00:00
|
|
|
|
|
|
|
|
2007-08-15 15:37:21 +00:00
|
|
|
const gchar *symbols_get_context_separator(gint ft_id)
|
2007-01-12 12:33:17 +00:00
|
|
|
{
|
|
|
|
gchar *cosep;
|
|
|
|
|
|
|
|
switch (ft_id)
|
|
|
|
{
|
2008-02-27 13:17:29 +00:00
|
|
|
case GEANY_FILETYPES_C: /* for C++ .h headers or C structs */
|
2007-01-12 12:33:17 +00:00
|
|
|
case GEANY_FILETYPES_CPP:
|
2008-08-27 12:44:54 +00:00
|
|
|
case GEANY_FILETYPES_GLSL: /* for structs */
|
2008-02-27 13:17:29 +00:00
|
|
|
/*case GEANY_FILETYPES_RUBY:*/ /* not sure what to use atm*/
|
2007-01-12 12:33:17 +00:00
|
|
|
{
|
|
|
|
static gchar cc[] = "::";
|
|
|
|
|
|
|
|
cosep = cc;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
static gchar def[] = ".";
|
|
|
|
|
|
|
|
cosep = def;
|
|
|
|
}
|
|
|
|
}
|
2008-02-27 13:17:29 +00:00
|
|
|
return cosep; /* return ptr to static string */
|
2007-01-12 12:33:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-20 11:24:23 +00:00
|
|
|
GString *symbols_get_macro_list(void)
|
2006-11-08 11:42:05 +00:00
|
|
|
{
|
|
|
|
guint j, i;
|
|
|
|
GPtrArray *ftags;
|
|
|
|
GString *words;
|
|
|
|
|
2007-03-24 12:10:43 +00:00
|
|
|
if (app->tm_workspace->work_objects == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2006-11-08 11:42:05 +00:00
|
|
|
ftags = g_ptr_array_sized_new(50);
|
|
|
|
words = g_string_sized_new(200);
|
|
|
|
|
|
|
|
for (j = 0; j < app->tm_workspace->work_objects->len; j++)
|
|
|
|
{
|
2008-03-24 17:24:39 +00:00
|
|
|
GPtrArray *tags;
|
|
|
|
|
2006-11-08 11:42:05 +00:00
|
|
|
tags = tm_tags_extract(TM_WORK_OBJECT(app->tm_workspace->work_objects->pdata[j])->tags_array,
|
|
|
|
tm_tag_enum_t | tm_tag_variable_t | tm_tag_macro_t | tm_tag_macro_with_arg_t);
|
|
|
|
if (NULL != tags)
|
|
|
|
{
|
2008-08-29 17:00:02 +00:00
|
|
|
for (i = 0; ((i < tags->len) && (i < editor_prefs.autocompletion_max_entries)); ++i)
|
2006-11-08 11:42:05 +00:00
|
|
|
{
|
|
|
|
g_ptr_array_add(ftags, (gpointer) tags->pdata[i]);
|
|
|
|
}
|
2008-03-24 17:24:39 +00:00
|
|
|
g_ptr_array_free(tags, TRUE);
|
2006-11-08 11:42:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
tm_tags_sort(ftags, NULL, FALSE);
|
|
|
|
for (j = 0; j < ftags->len; j++)
|
|
|
|
{
|
2008-03-20 15:48:03 +00:00
|
|
|
if (j > 0) g_string_append_c(words, '\n');
|
2006-11-08 11:42:05 +00:00
|
|
|
g_string_append(words, TM_TAG(ftags->pdata[j])->name);
|
|
|
|
}
|
|
|
|
g_ptr_array_free(ftags, TRUE);
|
|
|
|
return words;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-03-27 13:18:15 +00:00
|
|
|
/* Note: if tags is sorted, we can use bsearch or tm_tags_find() to speed this up. */
|
2006-11-08 11:42:05 +00:00
|
|
|
static TMTag *
|
|
|
|
symbols_find_tm_tag(const GPtrArray *tags, const gchar *tag_name)
|
|
|
|
{
|
|
|
|
guint i;
|
|
|
|
g_return_val_if_fail(tags != NULL, NULL);
|
|
|
|
|
|
|
|
for (i = 0; i < tags->len; ++i)
|
|
|
|
{
|
2006-12-07 16:09:45 +00:00
|
|
|
if (utils_str_equal(TM_TAG(tags->pdata[i])->name, tag_name))
|
2006-11-08 11:42:05 +00:00
|
|
|
return TM_TAG(tags->pdata[i]);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-03-24 16:44:43 +00:00
|
|
|
static TMTag *find_work_object_tag(const TMWorkObject *workobj,
|
|
|
|
const gchar *tag_name, gint type)
|
2006-11-08 11:42:05 +00:00
|
|
|
{
|
2008-03-24 17:24:39 +00:00
|
|
|
GPtrArray *tags;
|
2006-11-08 11:42:05 +00:00
|
|
|
TMTag *tmtag;
|
|
|
|
|
2008-03-24 16:44:43 +00:00
|
|
|
if (workobj != NULL)
|
2006-11-08 11:42:05 +00:00
|
|
|
{
|
2008-03-24 16:44:43 +00:00
|
|
|
tags = tm_tags_extract(workobj->tags_array, type);
|
|
|
|
if (tags != NULL)
|
2006-11-08 11:42:05 +00:00
|
|
|
{
|
2008-03-24 16:44:43 +00:00
|
|
|
tmtag = symbols_find_tm_tag(tags, tag_name);
|
2008-03-24 17:24:39 +00:00
|
|
|
|
|
|
|
g_ptr_array_free(tags, TRUE);
|
|
|
|
|
2008-03-24 16:44:43 +00:00
|
|
|
if (tmtag != NULL)
|
|
|
|
return tmtag;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL; /* not found */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static TMTag *find_workspace_tag(const gchar *tag_name, gint type)
|
|
|
|
{
|
|
|
|
guint j;
|
2008-04-08 15:21:54 +00:00
|
|
|
const GPtrArray *work_objects = NULL;
|
2008-03-24 16:44:43 +00:00
|
|
|
|
|
|
|
if (app->tm_workspace != NULL)
|
|
|
|
work_objects = app->tm_workspace->work_objects;
|
|
|
|
|
|
|
|
if (work_objects != NULL)
|
|
|
|
{
|
|
|
|
for (j = 0; j < work_objects->len; j++)
|
|
|
|
{
|
|
|
|
TMWorkObject *workobj = TM_WORK_OBJECT(work_objects->pdata[j]);
|
|
|
|
TMTag *tmtag;
|
|
|
|
|
2008-03-24 17:02:50 +00:00
|
|
|
tmtag = find_work_object_tag(workobj, tag_name, type);
|
2006-11-08 11:42:05 +00:00
|
|
|
if (tmtag != NULL)
|
|
|
|
return tmtag;
|
|
|
|
}
|
|
|
|
}
|
2008-02-27 13:17:29 +00:00
|
|
|
return NULL; /* not found */
|
2006-11-08 11:42:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-20 11:24:23 +00:00
|
|
|
const gchar **symbols_get_html_entities(void)
|
2006-12-15 17:09:05 +00:00
|
|
|
{
|
|
|
|
if (html_entities == NULL)
|
2008-02-27 13:17:29 +00:00
|
|
|
html_tags_loaded(); /* if not yet created, force creation of the array but shouldn't occur */
|
2006-12-15 17:09:05 +00:00
|
|
|
|
2007-03-26 12:18:51 +00:00
|
|
|
return (const gchar **) html_entities;
|
2006-12-15 17:09:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-20 11:24:23 +00:00
|
|
|
void symbols_finalize(void)
|
2006-12-15 17:09:05 +00:00
|
|
|
{
|
|
|
|
g_strfreev(html_entities);
|
2008-02-18 19:51:34 +00:00
|
|
|
g_strfreev(c_tags_ignore);
|
2006-12-15 17:09:05 +00:00
|
|
|
}
|
2007-01-13 13:25:05 +00:00
|
|
|
|
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
/* sort by name, then line */
|
|
|
|
static gint compare_symbol(const TMTag *tag_a, const TMTag *tag_b)
|
2007-01-13 13:25:05 +00:00
|
|
|
{
|
2007-04-23 15:15:13 +00:00
|
|
|
gint ret;
|
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
if (tag_a == NULL || tag_b == NULL)
|
|
|
|
return 0;
|
2007-01-13 13:25:05 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
ret = strcmp(tag_a->name, tag_b->name);
|
2007-04-23 15:15:13 +00:00
|
|
|
if (ret == 0)
|
|
|
|
{
|
2008-11-03 17:25:35 +00:00
|
|
|
return tag_a->atts.entry.line - tag_b->atts.entry.line;
|
2007-04-23 15:15:13 +00:00
|
|
|
}
|
|
|
|
return ret;
|
2007-01-13 13:25:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-08-16 17:19:16 +00:00
|
|
|
/* sort by line only */
|
2008-11-03 17:25:35 +00:00
|
|
|
static gint compare_symbol_lines(gconstpointer a, gconstpointer b)
|
2007-08-16 17:19:16 +00:00
|
|
|
{
|
2008-11-03 17:25:35 +00:00
|
|
|
const TMTag *tag_a = TM_TAG(a);
|
|
|
|
const TMTag *tag_b = TM_TAG(b);
|
2007-08-16 17:19:16 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
if (a == NULL || b == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return tag_a->atts.entry.line - tag_b->atts.entry.line;
|
2007-08-16 17:19:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
static GList *get_tag_list(GeanyDocument *doc, guint tag_types)
|
2007-01-13 13:25:05 +00:00
|
|
|
{
|
2008-11-03 17:25:35 +00:00
|
|
|
GList *tag_names = NULL;
|
|
|
|
TMTag *tag;
|
|
|
|
guint i;
|
2007-01-13 13:25:05 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
g_return_val_if_fail(doc, NULL);
|
2007-04-23 15:15:13 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
if (!doc->tm_file || !doc->tm_file->tags_array)
|
|
|
|
return NULL;
|
2007-07-31 17:07:33 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
for (i = 0; i < doc->tm_file->tags_array->len; ++i)
|
|
|
|
{
|
|
|
|
tag = TM_TAG(doc->tm_file->tags_array->pdata[i]);
|
|
|
|
if (tag == NULL)
|
|
|
|
return NULL;
|
2007-04-23 15:15:13 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
if (tag->type & tag_types)
|
|
|
|
{
|
|
|
|
tag_names = g_list_append(tag_names, tag);
|
2007-01-13 13:25:05 +00:00
|
|
|
}
|
|
|
|
}
|
2008-11-03 17:25:35 +00:00
|
|
|
tag_names = g_list_sort(tag_names, compare_symbol_lines);
|
|
|
|
return tag_names;
|
2007-01-13 13:25:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* amount of types in the symbol list (currently max. 8 are used) */
|
2007-09-17 15:28:27 +00:00
|
|
|
#define MAX_SYMBOL_TYPES (sizeof(tv_iters) / sizeof(GtkTreeIter))
|
|
|
|
|
2007-01-14 12:12:18 +00:00
|
|
|
struct TreeviewSymbols
|
|
|
|
{
|
|
|
|
GtkTreeIter tag_function;
|
|
|
|
GtkTreeIter tag_class;
|
|
|
|
GtkTreeIter tag_macro;
|
|
|
|
GtkTreeIter tag_member;
|
|
|
|
GtkTreeIter tag_variable;
|
|
|
|
GtkTreeIter tag_namespace;
|
|
|
|
GtkTreeIter tag_struct;
|
2007-09-17 15:28:27 +00:00
|
|
|
GtkTreeIter tag_type;
|
2007-01-14 12:12:18 +00:00
|
|
|
GtkTreeIter tag_other;
|
|
|
|
} tv_iters;
|
|
|
|
|
2007-01-15 11:48:14 +00:00
|
|
|
|
2008-02-20 11:24:23 +00:00
|
|
|
static void init_tag_iters(void)
|
2007-01-14 12:12:18 +00:00
|
|
|
{
|
2008-02-27 13:17:29 +00:00
|
|
|
/* init all GtkTreeIters with -1 to make them invalid to avoid crashes when switching between
|
|
|
|
* filetypes(e.g. config file to Python crashes Geany without this) */
|
2007-01-14 12:12:18 +00:00
|
|
|
tv_iters.tag_function.stamp = -1;
|
|
|
|
tv_iters.tag_class.stamp = -1;
|
|
|
|
tv_iters.tag_member.stamp = -1;
|
|
|
|
tv_iters.tag_macro.stamp = -1;
|
|
|
|
tv_iters.tag_variable.stamp = -1;
|
|
|
|
tv_iters.tag_namespace.stamp = -1;
|
|
|
|
tv_iters.tag_struct.stamp = -1;
|
2007-09-17 15:28:27 +00:00
|
|
|
tv_iters.tag_type.stamp = -1;
|
2007-01-14 12:12:18 +00:00
|
|
|
tv_iters.tag_other.stamp = -1;
|
2007-01-15 11:48:14 +00:00
|
|
|
}
|
2007-01-14 12:12:18 +00:00
|
|
|
|
2007-01-15 11:48:14 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
static GdkPixbuf *get_tag_icon(const gchar *icon_name)
|
2007-05-23 12:32:06 +00:00
|
|
|
{
|
2007-07-22 14:38:47 +00:00
|
|
|
static GtkIconTheme *icon_theme = NULL;
|
|
|
|
static gint x, y;
|
|
|
|
|
|
|
|
if (icon_theme == NULL)
|
|
|
|
{
|
2007-12-12 20:04:45 +00:00
|
|
|
#ifndef G_OS_WIN32
|
2007-12-19 15:37:10 +00:00
|
|
|
gchar *path = g_strconcat(GEANY_DATADIR, "/icons", NULL);
|
2007-12-12 20:04:45 +00:00
|
|
|
#endif
|
2007-07-22 14:38:47 +00:00
|
|
|
gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &x, &y);
|
|
|
|
icon_theme = gtk_icon_theme_get_default();
|
2007-07-23 09:26:05 +00:00
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
gtk_icon_theme_append_search_path(icon_theme, "share\\icons");
|
|
|
|
#else
|
2007-12-12 20:04:45 +00:00
|
|
|
gtk_icon_theme_append_search_path(icon_theme, path);
|
|
|
|
g_free(path);
|
2007-07-23 09:26:05 +00:00
|
|
|
#endif
|
2007-07-22 14:38:47 +00:00
|
|
|
}
|
2008-11-03 17:25:35 +00:00
|
|
|
return gtk_icon_theme_load_icon(icon_theme, icon_name, x, 0, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Adds symbol list groups in (iter*, title) pairs.
|
|
|
|
* The list must be ended with NULL. */
|
|
|
|
static void G_GNUC_NULL_TERMINATED
|
|
|
|
tag_list_add_groups(GtkTreeStore *tree_store, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
GtkTreeIter *iter;
|
|
|
|
|
|
|
|
g_return_if_fail(top_level_iter_names);
|
2007-05-23 12:32:06 +00:00
|
|
|
|
2007-05-23 17:20:47 +00:00
|
|
|
va_start(args, tree_store);
|
2007-05-23 12:32:06 +00:00
|
|
|
for (; iter = va_arg(args, GtkTreeIter*), iter != NULL;)
|
|
|
|
{
|
|
|
|
gchar *title = va_arg(args, gchar*);
|
2007-07-20 16:33:16 +00:00
|
|
|
gchar *icon_name = va_arg(args, gchar *);
|
|
|
|
GdkPixbuf *icon = NULL;
|
|
|
|
|
2007-07-22 14:38:47 +00:00
|
|
|
if (icon_name)
|
|
|
|
{
|
2008-11-03 17:25:35 +00:00
|
|
|
icon = get_tag_icon(icon_name);
|
2007-07-20 16:33:16 +00:00
|
|
|
}
|
2007-05-23 12:32:06 +00:00
|
|
|
|
2007-05-23 17:20:47 +00:00
|
|
|
g_assert(title != NULL);
|
2008-11-03 17:25:35 +00:00
|
|
|
g_ptr_array_add(top_level_iter_names, title);
|
|
|
|
|
2007-05-23 17:20:47 +00:00
|
|
|
gtk_tree_store_append(tree_store, iter, NULL);
|
2007-07-20 16:33:16 +00:00
|
|
|
|
2007-08-24 11:31:27 +00:00
|
|
|
if (G_IS_OBJECT(icon))
|
|
|
|
{
|
2008-11-03 17:25:35 +00:00
|
|
|
gtk_tree_store_set(tree_store, iter, SYMBOLS_COLUMN_ICON, icon, -1);
|
2007-07-22 14:38:47 +00:00
|
|
|
g_object_unref(icon);
|
2007-08-24 11:31:27 +00:00
|
|
|
}
|
2008-11-03 17:25:35 +00:00
|
|
|
gtk_tree_store_set(tree_store, iter, SYMBOLS_COLUMN_NAME, title, -1);
|
2007-05-23 12:32:06 +00:00
|
|
|
}
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
static void add_top_level_items(GeanyDocument *doc)
|
2007-01-15 11:48:14 +00:00
|
|
|
{
|
2008-06-15 13:35:48 +00:00
|
|
|
filetype_id ft_id = doc->file_type->id;
|
2008-09-26 17:28:50 +00:00
|
|
|
GtkTreeStore *tag_store = doc->priv->tag_store;
|
2007-01-15 11:48:14 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
if (top_level_iter_names == NULL)
|
|
|
|
top_level_iter_names = g_ptr_array_new();
|
|
|
|
else
|
|
|
|
g_ptr_array_set_size(top_level_iter_names, 0);
|
|
|
|
|
2007-01-15 11:48:14 +00:00
|
|
|
init_tag_iters();
|
|
|
|
|
|
|
|
switch (ft_id)
|
2007-01-14 12:12:18 +00:00
|
|
|
{
|
|
|
|
case GEANY_FILETYPES_DIFF:
|
|
|
|
{
|
2007-05-23 17:20:47 +00:00
|
|
|
tag_list_add_groups(tag_store,
|
2007-07-20 16:33:16 +00:00
|
|
|
&(tv_iters.tag_function), _("Files"), NULL, NULL);
|
2007-01-14 12:12:18 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GEANY_FILETYPES_DOCBOOK:
|
|
|
|
{
|
2007-05-23 17:20:47 +00:00
|
|
|
tag_list_add_groups(tag_store,
|
2007-07-20 16:33:16 +00:00
|
|
|
&(tv_iters.tag_function), _("Chapter"), NULL,
|
|
|
|
&(tv_iters.tag_class), _("Section"), NULL,
|
|
|
|
&(tv_iters.tag_member), _("Sect1"), NULL,
|
|
|
|
&(tv_iters.tag_macro), _("Sect2"), NULL,
|
|
|
|
&(tv_iters.tag_variable), _("Sect3"), NULL,
|
|
|
|
&(tv_iters.tag_struct), _("Appendix"), NULL,
|
|
|
|
&(tv_iters.tag_other), _("Other"), NULL,
|
|
|
|
NULL);
|
2008-02-27 13:17:29 +00:00
|
|
|
/* &(tv_iters.tag_namespace), _("Other"), NULL, NULL); */
|
2007-01-14 12:12:18 +00:00
|
|
|
break;
|
|
|
|
}
|
2007-05-23 12:32:06 +00:00
|
|
|
case GEANY_FILETYPES_HASKELL:
|
2007-05-23 17:20:47 +00:00
|
|
|
tag_list_add_groups(tag_store,
|
2007-07-20 16:33:16 +00:00
|
|
|
&tv_iters.tag_namespace, _("Module"), NULL,
|
|
|
|
&tv_iters.tag_struct, _("Types"), NULL,
|
|
|
|
&tv_iters.tag_macro, _("Type constructors"), NULL,
|
|
|
|
&tv_iters.tag_function, _("Functions"), "classviewer-method",
|
2007-05-23 12:32:06 +00:00
|
|
|
NULL);
|
|
|
|
break;
|
2007-07-02 17:09:48 +00:00
|
|
|
case GEANY_FILETYPES_CONF:
|
|
|
|
tag_list_add_groups(tag_store,
|
2007-07-20 16:33:16 +00:00
|
|
|
&tv_iters.tag_namespace, _("Sections"), NULL,
|
|
|
|
&tv_iters.tag_macro, _("Keys"), NULL,
|
2007-07-02 17:09:48 +00:00
|
|
|
NULL);
|
|
|
|
break;
|
2007-01-14 12:12:18 +00:00
|
|
|
case GEANY_FILETYPES_LATEX:
|
|
|
|
{
|
2007-05-23 17:20:47 +00:00
|
|
|
tag_list_add_groups(tag_store,
|
2007-07-20 16:33:16 +00:00
|
|
|
&(tv_iters.tag_function), _("Command"), NULL,
|
|
|
|
&(tv_iters.tag_class), _("Environment"), NULL,
|
|
|
|
&(tv_iters.tag_member), _("Section"), NULL,
|
|
|
|
&(tv_iters.tag_macro), _("Subsection"), NULL,
|
|
|
|
&(tv_iters.tag_variable), _("Subsubsection"), NULL,
|
|
|
|
&(tv_iters.tag_struct), _("Label"), NULL,
|
|
|
|
&(tv_iters.tag_namespace), _("Chapter"), NULL,
|
|
|
|
&(tv_iters.tag_other), _("Other"), NULL,
|
|
|
|
NULL);
|
2007-01-14 12:12:18 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-11-07 14:39:45 +00:00
|
|
|
case GEANY_FILETYPES_MATLAB:
|
|
|
|
{
|
|
|
|
tag_list_add_groups(tag_store,
|
|
|
|
&(tv_iters.tag_function), _("Functions"), "classviewer-method",
|
|
|
|
&(tv_iters.tag_struct), _("Structures"), "classviewer-struct",
|
|
|
|
NULL);
|
|
|
|
break;
|
|
|
|
}
|
2007-01-14 12:12:18 +00:00
|
|
|
case GEANY_FILETYPES_PERL:
|
|
|
|
{
|
2007-05-23 17:20:47 +00:00
|
|
|
tag_list_add_groups(tag_store,
|
2007-07-20 16:33:16 +00:00
|
|
|
&(tv_iters.tag_class), _("Package"), NULL,
|
|
|
|
&(tv_iters.tag_function), _("Functions"), "classviewer-method",
|
|
|
|
&(tv_iters.tag_member), _("My"), NULL,
|
|
|
|
&(tv_iters.tag_macro), _("Local"), NULL,
|
|
|
|
&(tv_iters.tag_variable), _("Our"), NULL,
|
|
|
|
NULL);
|
2008-02-27 13:17:29 +00:00
|
|
|
/*&(tv_iters.tag_struct), _("Label"), NULL,*/
|
|
|
|
/*&(tv_iters.tag_namespace), _("Begin"), NULL,*/
|
|
|
|
/*&(tv_iters.tag_other), _("Other"), NULL, NULL);*/
|
2007-01-14 12:12:18 +00:00
|
|
|
break;
|
|
|
|
}
|
2007-07-09 15:11:18 +00:00
|
|
|
case GEANY_FILETYPES_PHP:
|
|
|
|
{
|
|
|
|
tag_list_add_groups(tag_store,
|
2007-11-18 17:17:59 +00:00
|
|
|
&(tv_iters.tag_struct), _("Interfaces"), "classviewer-struct",
|
2007-07-20 16:33:16 +00:00
|
|
|
&(tv_iters.tag_class), _("Classes"), "classviewer-class",
|
|
|
|
&(tv_iters.tag_function), _("Functions"), "classviewer-method",
|
2007-11-18 17:17:59 +00:00
|
|
|
&(tv_iters.tag_macro), _("Constants"), "classviewer-macro",
|
2007-07-20 16:33:16 +00:00
|
|
|
&(tv_iters.tag_variable), _("Variables"), "classviewer-var",
|
|
|
|
NULL);
|
2008-02-27 13:17:29 +00:00
|
|
|
/*&(tv_iters.tag_struct), _("Label"),*/
|
|
|
|
/*&(tv_iters.tag_namespace), _("Begin"),*/
|
|
|
|
/*&(tv_iters.tag_other), _("Other"), NULL);*/
|
2007-07-09 15:11:18 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-03-09 18:50:41 +00:00
|
|
|
case GEANY_FILETYPES_HTML:
|
|
|
|
{
|
|
|
|
tag_list_add_groups(tag_store,
|
|
|
|
&(tv_iters.tag_function), _("JavaScript functions"), NULL,
|
|
|
|
&(tv_iters.tag_member), _("Anchor"), NULL,
|
|
|
|
&(tv_iters.tag_namespace), _("Heading (H1)"), NULL,
|
|
|
|
&(tv_iters.tag_class), _("Heading (H2)"), NULL,
|
|
|
|
&(tv_iters.tag_variable), _("Heading (H3)"), NULL,
|
|
|
|
NULL);
|
|
|
|
break;
|
|
|
|
}
|
2008-10-12 17:52:22 +00:00
|
|
|
case GEANY_FILETYPES_CSS:
|
|
|
|
{
|
|
|
|
tag_list_add_groups(tag_store,
|
|
|
|
&(tv_iters.tag_class), _("Classes"), "classviewer-class",
|
|
|
|
&(tv_iters.tag_variable), _("ID Selectors"), "classviewer-var",
|
|
|
|
&(tv_iters.tag_struct), _("Type Selectors"), "classviewer-struct", NULL);
|
|
|
|
break;
|
|
|
|
}
|
2007-08-14 11:50:03 +00:00
|
|
|
case GEANY_FILETYPES_REST:
|
|
|
|
{
|
|
|
|
tag_list_add_groups(tag_store,
|
|
|
|
&(tv_iters.tag_namespace), _("Chapter"), NULL,
|
|
|
|
&(tv_iters.tag_member), _("Section"), NULL,
|
|
|
|
&(tv_iters.tag_macro), _("Subsection"), NULL,
|
|
|
|
&(tv_iters.tag_variable), _("Subsubsection"), NULL,
|
|
|
|
NULL);
|
|
|
|
break;
|
|
|
|
}
|
2007-01-14 12:12:18 +00:00
|
|
|
case GEANY_FILETYPES_RUBY:
|
|
|
|
{
|
2007-05-23 17:20:47 +00:00
|
|
|
tag_list_add_groups(tag_store,
|
2007-11-29 18:24:06 +00:00
|
|
|
&(tv_iters.tag_namespace), _("Modules"), NULL,
|
2007-07-20 16:33:16 +00:00
|
|
|
&(tv_iters.tag_class), _("Classes"), "classviewer-class",
|
2007-11-29 18:24:06 +00:00
|
|
|
&(tv_iters.tag_member), _("Singletons"), "classviewer-struct",
|
2007-07-20 16:33:16 +00:00
|
|
|
&(tv_iters.tag_function), _("Methods"), "classviewer-method",
|
|
|
|
NULL);
|
2008-02-27 13:17:29 +00:00
|
|
|
/*&(tv_iters.tag_namespace), _("Begin"),*/
|
|
|
|
/*&(tv_iters.tag_other), _("Other"), NULL);*/
|
2007-01-14 12:12:18 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-03-21 16:47:17 +00:00
|
|
|
case GEANY_FILETYPES_TCL:
|
2007-01-14 12:12:18 +00:00
|
|
|
case GEANY_FILETYPES_PYTHON:
|
|
|
|
{
|
2007-05-23 17:20:47 +00:00
|
|
|
tag_list_add_groups(tag_store,
|
2007-07-20 16:33:16 +00:00
|
|
|
&(tv_iters.tag_class), _("Classes"), "classviewer-class",
|
|
|
|
&(tv_iters.tag_member), _("Methods"), "classviewer-member",
|
|
|
|
&(tv_iters.tag_function), _("Functions"), "classviewer-method",
|
2007-08-07 12:36:03 +00:00
|
|
|
&(tv_iters.tag_variable), _("Variables"), "classviewer-var",
|
2007-07-20 16:33:16 +00:00
|
|
|
NULL);
|
2008-02-27 13:17:29 +00:00
|
|
|
/*&(tv_iters.tag_macro), _("Mixin"),*/
|
|
|
|
/*&(tv_iters.tag_variable), _("Variables"),*/
|
|
|
|
/*&(tv_iters.tag_struct), _("Members"),*/
|
|
|
|
/*&(tv_iters.tag_namespace), _("Begin"),*/
|
|
|
|
/*&(tv_iters.tag_other), _("Other"), NULL);*/
|
2007-01-14 12:12:18 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GEANY_FILETYPES_VHDL:
|
|
|
|
{
|
2007-05-23 17:20:47 +00:00
|
|
|
tag_list_add_groups(tag_store,
|
2007-07-20 16:33:16 +00:00
|
|
|
&(tv_iters.tag_function), _("Functions"), "classviewer-method",
|
2008-02-27 13:17:29 +00:00
|
|
|
/*&(tv_iters.tag_class), _("Constants"),*/
|
|
|
|
/*&(tv_iters.tag_member), _("Members"),*/
|
|
|
|
/*&(tv_iters.tag_macro), _("Macros"),*/
|
2007-07-20 16:33:16 +00:00
|
|
|
&(tv_iters.tag_variable), _("Variables"), "classviewer-var",
|
|
|
|
NULL);
|
2008-02-27 13:17:29 +00:00
|
|
|
/*&(tv_iters.tag_namespace), _("Namespaces"),*/
|
|
|
|
/*&(tv_iters.tag_struct), _("Signals"),*/
|
|
|
|
/*&(tv_iters.tag_other), _("Other"), NULL);*/
|
2007-01-14 12:12:18 +00:00
|
|
|
break;
|
|
|
|
}
|
2007-01-15 11:48:14 +00:00
|
|
|
case GEANY_FILETYPES_JAVA:
|
2007-01-14 12:12:18 +00:00
|
|
|
{
|
2007-05-23 17:20:47 +00:00
|
|
|
tag_list_add_groups(tag_store,
|
2007-07-20 16:33:16 +00:00
|
|
|
&(tv_iters.tag_namespace), _("Package"), NULL,
|
2007-11-18 17:17:59 +00:00
|
|
|
&(tv_iters.tag_struct), _("Interfaces"), "classviewer-struct",
|
2007-07-20 16:33:16 +00:00
|
|
|
&(tv_iters.tag_class), _("Classes"), "classviewer-class",
|
|
|
|
&(tv_iters.tag_function), _("Methods"), "classviewer-method",
|
|
|
|
&(tv_iters.tag_member), _("Members"), "classviewer-member",
|
2008-02-27 13:17:29 +00:00
|
|
|
/*&(tv_iters.tag_macro), _("Macros"),*/
|
|
|
|
/*&(tv_iters.tag_variable), _("Variables"),*/
|
2007-07-20 16:33:16 +00:00
|
|
|
&(tv_iters.tag_other), _("Other"), "classviewer-other",
|
|
|
|
NULL);
|
2007-01-15 11:48:14 +00:00
|
|
|
break;
|
|
|
|
}
|
2007-07-26 09:50:12 +00:00
|
|
|
case GEANY_FILETYPES_HAXE:
|
|
|
|
{
|
|
|
|
tag_list_add_groups(tag_store,
|
2007-09-17 15:28:27 +00:00
|
|
|
&(tv_iters.tag_struct), _("Interfaces"), "classviewer-struct",
|
2007-07-26 09:50:12 +00:00
|
|
|
&(tv_iters.tag_class), _("Classes"), "classviewer-class",
|
|
|
|
&(tv_iters.tag_function), _("Methods"), "classviewer-method",
|
2007-11-18 17:17:59 +00:00
|
|
|
&(tv_iters.tag_type), _("Types"), "classviewer-macro",
|
2007-07-26 09:50:12 +00:00
|
|
|
&(tv_iters.tag_variable), _("Variables"), "classviewer-var",
|
2007-09-17 15:28:27 +00:00
|
|
|
&(tv_iters.tag_other), _("Other"), "classviewer-other",
|
2007-07-26 09:50:12 +00:00
|
|
|
NULL);
|
|
|
|
break;
|
|
|
|
}
|
2007-11-23 16:05:53 +00:00
|
|
|
case GEANY_FILETYPES_BASIC:
|
|
|
|
{
|
|
|
|
tag_list_add_groups(tag_store,
|
|
|
|
&(tv_iters.tag_function), _("Functions"), "classviewer-method",
|
|
|
|
&(tv_iters.tag_variable), _("Variables"), "classviewer-var",
|
|
|
|
&(tv_iters.tag_macro), _("Constants"), "classviewer-macro",
|
|
|
|
&(tv_iters.tag_struct), _("Types"), "classviewer-namespace",
|
|
|
|
&(tv_iters.tag_namespace), _("Labels"), "classviewer-member",
|
|
|
|
&(tv_iters.tag_other), _("Other"), "classviewer-other",
|
|
|
|
NULL);
|
|
|
|
break;
|
|
|
|
}
|
2008-07-14 18:27:20 +00:00
|
|
|
case GEANY_FILETYPES_F77:
|
2008-03-03 20:16:52 +00:00
|
|
|
case GEANY_FILETYPES_FORTRAN:
|
|
|
|
{
|
|
|
|
tag_list_add_groups(tag_store,
|
2008-07-14 18:27:20 +00:00
|
|
|
&(tv_iters.tag_namespace), _("Module"), "classviewer-class",
|
2008-03-03 20:16:52 +00:00
|
|
|
&(tv_iters.tag_struct), _("Interfaces"), "classviewer-struct",
|
|
|
|
&(tv_iters.tag_function), _("Functions"), "classviewer-method",
|
|
|
|
&(tv_iters.tag_member), _("Subroutines"), "classviewer-method",
|
|
|
|
&(tv_iters.tag_variable), _("Variables"), "classviewer-var",
|
|
|
|
&(tv_iters.tag_type), _("Types"), "classviewer-namespace",
|
|
|
|
&(tv_iters.tag_macro), _("Blocks"), "classviewer-member",
|
|
|
|
&(tv_iters.tag_other), _("Other"), "classviewer-other",
|
|
|
|
NULL);
|
|
|
|
break;
|
|
|
|
}
|
2008-03-05 18:18:19 +00:00
|
|
|
case GEANY_FILETYPES_ASM:
|
|
|
|
{
|
|
|
|
tag_list_add_groups(tag_store,
|
|
|
|
&(tv_iters.tag_namespace), _("Labels"), "classviewer-namespace",
|
|
|
|
&(tv_iters.tag_function), _("Macros"), "classviewer-method",
|
|
|
|
&(tv_iters.tag_macro), _("Defines"), "classviewer-macro",
|
|
|
|
&(tv_iters.tag_struct), _("Types"), "classviewer-struct",
|
|
|
|
NULL);
|
|
|
|
break;
|
|
|
|
}
|
2008-03-23 15:25:42 +00:00
|
|
|
case GEANY_FILETYPES_MAKE:
|
|
|
|
tag_list_add_groups(tag_store,
|
|
|
|
&tv_iters.tag_function, _("Targets"), "classviewer-method",
|
|
|
|
&tv_iters.tag_macro, _("Macros"), "classviewer-macro",
|
|
|
|
NULL);
|
|
|
|
break;
|
2007-01-15 11:48:14 +00:00
|
|
|
case GEANY_FILETYPES_D:
|
|
|
|
default:
|
|
|
|
{
|
2007-05-23 17:20:47 +00:00
|
|
|
if (ft_id == GEANY_FILETYPES_D)
|
2007-07-20 16:33:16 +00:00
|
|
|
tag_list_add_groups(tag_store,
|
|
|
|
&(tv_iters.tag_namespace), _("Module"), NULL, NULL);
|
|
|
|
else
|
|
|
|
tag_list_add_groups(tag_store,
|
|
|
|
&(tv_iters.tag_namespace), _("Namespaces"), "classviewer-namespace", NULL);
|
2007-01-15 11:48:14 +00:00
|
|
|
|
2007-05-23 17:20:47 +00:00
|
|
|
tag_list_add_groups(tag_store,
|
2007-07-20 16:33:16 +00:00
|
|
|
&(tv_iters.tag_class), _("Classes"), "classviewer-class",
|
|
|
|
&(tv_iters.tag_function), _("Functions"), "classviewer-method",
|
|
|
|
&(tv_iters.tag_member), _("Members"), "classviewer-member",
|
|
|
|
&(tv_iters.tag_struct), _("Structs / Typedefs"), "classviewer-struct",
|
|
|
|
NULL);
|
2007-05-23 17:20:47 +00:00
|
|
|
|
2007-01-15 11:48:14 +00:00
|
|
|
if (ft_id != GEANY_FILETYPES_D)
|
|
|
|
{
|
2007-05-23 17:20:47 +00:00
|
|
|
tag_list_add_groups(tag_store,
|
2007-07-20 16:33:16 +00:00
|
|
|
&(tv_iters.tag_macro), _("Macros"), "classviewer-macro", NULL);
|
2007-01-15 11:48:14 +00:00
|
|
|
}
|
2007-05-23 17:20:47 +00:00
|
|
|
tag_list_add_groups(tag_store,
|
2007-07-20 16:33:16 +00:00
|
|
|
&(tv_iters.tag_variable), _("Variables"), "classviewer-var",
|
|
|
|
&(tv_iters.tag_other), _("Other"), "classviewer-other", NULL);
|
2007-01-14 12:12:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-07-28 14:10:49 +00:00
|
|
|
/* the following code surely can be improved, at the moment it collects some iters
|
|
|
|
* for removal and after that the actual removal is done. I didn't find a way to find and remove
|
|
|
|
* an empty row in one loop (next iter fails then) */
|
2008-05-12 13:46:19 +00:00
|
|
|
static void hide_empty_rows(GtkTreeStore *store)
|
2007-07-28 14:10:49 +00:00
|
|
|
{
|
|
|
|
GtkTreeIter iter, *iters[MAX_SYMBOL_TYPES] = { NULL };
|
2007-09-17 15:28:27 +00:00
|
|
|
guint i = 0;
|
2007-07-28 14:10:49 +00:00
|
|
|
|
2008-05-12 13:46:19 +00:00
|
|
|
if (! gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter))
|
2008-02-27 13:17:29 +00:00
|
|
|
return; /* stop when first iter is invalid, i.e. no elements */
|
2007-07-28 14:10:49 +00:00
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
do /* first collect the iters we need to delete empty rows */
|
2007-07-28 14:10:49 +00:00
|
|
|
{
|
2008-05-12 13:46:19 +00:00
|
|
|
if (! gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store), &iter))
|
2007-07-28 14:10:49 +00:00
|
|
|
iters[i++] = gtk_tree_iter_copy(&iter);
|
2008-05-12 13:46:19 +00:00
|
|
|
} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
|
2007-07-28 14:10:49 +00:00
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* now actually delete the collected iters */
|
2007-07-28 14:10:49 +00:00
|
|
|
for (i = 0; i < MAX_SYMBOL_TYPES; i++)
|
|
|
|
{
|
|
|
|
if (iters[i] == NULL)
|
|
|
|
break;
|
|
|
|
gtk_tree_store_remove(store, iters[i]);
|
|
|
|
gtk_tree_iter_free(iters[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
static const gchar *get_symbol_name(GeanyDocument *doc, const TMTag *tag,
|
|
|
|
gboolean found_parent)
|
2007-01-13 13:25:05 +00:00
|
|
|
{
|
2008-11-03 17:25:35 +00:00
|
|
|
gchar *utf8_name;
|
|
|
|
const gchar *scope = tag->atts.entry.scope;
|
|
|
|
static GString *buffer = NULL; /* buffer will be small so we can keep it for reuse */
|
|
|
|
gboolean doc_is_utf8 = FALSE;
|
|
|
|
|
|
|
|
/* encodings_convert_to_utf8_from_charset() fails with charset "None", so skip conversion
|
|
|
|
* for None at this point completely */
|
|
|
|
if (utils_str_equal(doc->encoding, "UTF-8") ||
|
|
|
|
utils_str_equal(doc->encoding, "None"))
|
|
|
|
doc_is_utf8 = TRUE;
|
|
|
|
|
|
|
|
if (! doc_is_utf8)
|
|
|
|
utf8_name = encodings_convert_to_utf8_from_charset(tag->name,
|
|
|
|
(gsize)-1, doc->encoding, TRUE);
|
|
|
|
else
|
|
|
|
utf8_name = tag->name;
|
2008-06-15 13:35:48 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
if (utf8_name == NULL)
|
|
|
|
return NULL;
|
2007-05-23 12:32:06 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
if (!buffer)
|
|
|
|
buffer = g_string_new(NULL);
|
2008-03-18 16:09:11 +00:00
|
|
|
else
|
2008-11-03 17:25:35 +00:00
|
|
|
g_string_truncate(buffer, 0);
|
2008-03-18 16:09:11 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
/* check first char of scope is a wordchar */
|
|
|
|
if (!found_parent && scope &&
|
|
|
|
strpbrk(scope, GEANY_WORDCHARS) == scope)
|
|
|
|
{
|
|
|
|
const gchar *sep = symbols_get_context_separator(FILETYPE_ID(doc->file_type));
|
2007-01-13 13:25:05 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
g_string_append(buffer, scope);
|
|
|
|
g_string_append(buffer, sep);
|
|
|
|
}
|
|
|
|
g_string_append(buffer, utf8_name);
|
|
|
|
|
|
|
|
if (! doc_is_utf8)
|
|
|
|
g_free(utf8_name);
|
|
|
|
|
|
|
|
g_string_append_printf(buffer, " [%lu]", tag->atts.entry.line);
|
|
|
|
|
|
|
|
return buffer->str;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* find the last word in "foo::bar::blah", e.g. "blah" */
|
|
|
|
const gchar *get_parent_name(const TMTag *tag, filetype_id ft_id)
|
|
|
|
{
|
|
|
|
const gchar *scope = tag->atts.entry.scope;
|
2008-10-02 15:48:50 +00:00
|
|
|
const gchar *separator = symbols_get_context_separator(ft_id);
|
2008-11-03 17:25:35 +00:00
|
|
|
const gchar *str, *ptr;
|
2007-01-13 13:25:05 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
if (!scope)
|
|
|
|
return NULL;
|
2008-03-18 16:09:11 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
str = scope;
|
2007-01-13 13:25:05 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
while (1)
|
2007-01-13 13:25:05 +00:00
|
|
|
{
|
2008-11-03 17:25:35 +00:00
|
|
|
ptr = strstr(str, separator);
|
|
|
|
if (ptr)
|
|
|
|
{
|
|
|
|
str = ptr + strlen(separator);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
2007-04-23 15:15:13 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
return NZV(str) ? str : NULL;
|
|
|
|
}
|
2007-01-14 12:12:18 +00:00
|
|
|
|
2007-04-23 15:15:13 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
static GtkTreeIter *get_tag_type_iter(TMTagType tag_type, filetype_id ft_id)
|
|
|
|
{
|
|
|
|
GtkTreeIter *iter = NULL;
|
|
|
|
|
|
|
|
switch (tag_type)
|
|
|
|
{
|
|
|
|
case tm_tag_prototype_t:
|
|
|
|
case tm_tag_method_t:
|
|
|
|
case tm_tag_function_t:
|
2007-01-13 13:25:05 +00:00
|
|
|
{
|
2008-11-03 17:25:35 +00:00
|
|
|
iter = &tv_iters.tag_function;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tm_tag_macro_t:
|
|
|
|
case tm_tag_macro_with_arg_t:
|
|
|
|
{
|
|
|
|
iter = &tv_iters.tag_macro;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tm_tag_class_t:
|
|
|
|
{
|
|
|
|
iter = &tv_iters.tag_class;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tm_tag_member_t:
|
|
|
|
case tm_tag_field_t:
|
|
|
|
{
|
|
|
|
iter = &tv_iters.tag_member;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tm_tag_typedef_t:
|
|
|
|
case tm_tag_enum_t:
|
|
|
|
{
|
|
|
|
/* TODO separate C-like types here also */
|
|
|
|
if (ft_id == GEANY_FILETYPES_HAXE)
|
2007-01-13 13:25:05 +00:00
|
|
|
{
|
2008-11-03 17:25:35 +00:00
|
|
|
iter = &tv_iters.tag_type;
|
2007-01-13 13:25:05 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-11-03 17:25:35 +00:00
|
|
|
/* fall through */
|
|
|
|
}
|
|
|
|
case tm_tag_union_t:
|
|
|
|
case tm_tag_struct_t:
|
|
|
|
case tm_tag_interface_t:
|
|
|
|
{
|
|
|
|
iter = &tv_iters.tag_struct;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case tm_tag_variable_t:
|
|
|
|
{
|
|
|
|
iter = &tv_iters.tag_variable;
|
|
|
|
break;
|
2007-01-13 13:25:05 +00:00
|
|
|
}
|
2008-11-03 17:25:35 +00:00
|
|
|
case tm_tag_namespace_t:
|
|
|
|
case tm_tag_package_t:
|
|
|
|
{
|
|
|
|
iter = &tv_iters.tag_namespace;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
iter = &tv_iters.tag_other;
|
2007-01-13 13:25:05 +00:00
|
|
|
}
|
2008-11-03 17:25:35 +00:00
|
|
|
}
|
|
|
|
if (iter->stamp != -1)
|
|
|
|
return iter;
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
2007-07-20 16:33:16 +00:00
|
|
|
|
2008-10-02 12:06:20 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
static void add_tree_tag(GeanyDocument *doc, const TMTag *tag, GHashTable *parent_hash)
|
|
|
|
{
|
|
|
|
filetype_id ft_id = FILETYPE_ID(doc->file_type);
|
|
|
|
GtkTreeStore *tree_store = doc->priv->tag_store;
|
|
|
|
GtkTreeIter *parent = NULL;
|
2008-10-02 12:06:20 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
parent = get_tag_type_iter(tag->type, ft_id);
|
2008-10-02 12:06:20 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
if (parent)
|
|
|
|
{
|
|
|
|
const gchar *name;
|
|
|
|
const gchar *parent_name = get_parent_name(tag, ft_id);
|
|
|
|
GtkTreeIter iter;
|
|
|
|
GtkTreeIter *icon_iter = NULL, *child = NULL;
|
|
|
|
GdkPixbuf *icon = NULL;
|
2007-07-20 16:33:16 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
child = &iter;
|
|
|
|
icon_iter = (parent != &tv_iters.tag_other) ? parent : &tv_iters.tag_variable;
|
2007-07-20 16:33:16 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
gtk_tree_model_get(GTK_TREE_MODEL(tree_store), icon_iter,
|
|
|
|
SYMBOLS_COLUMN_ICON, &icon, -1);
|
|
|
|
|
|
|
|
if (parent_name)
|
|
|
|
{
|
|
|
|
GtkTreeIter *parent_search =
|
|
|
|
(GtkTreeIter *)g_hash_table_lookup(parent_hash, parent_name);
|
|
|
|
|
|
|
|
if (parent_search)
|
|
|
|
parent = parent_search;
|
|
|
|
else
|
|
|
|
parent_name = NULL;
|
2008-10-02 12:06:20 +00:00
|
|
|
}
|
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
/* check if the current tag is a parent of other tags */
|
2008-11-10 16:57:18 +00:00
|
|
|
if (g_hash_table_lookup_extended(parent_hash, tag->name, NULL, NULL) &&
|
|
|
|
!utils_str_equal(tag->name, parent_name)) /* prevent Foo::Foo from making parent = child */
|
2007-07-22 14:38:47 +00:00
|
|
|
{
|
2008-11-03 17:25:35 +00:00
|
|
|
GtkTreeIter *new_iter = g_new0(GtkTreeIter, 1);
|
2007-07-20 16:33:16 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
/* set an iter value for the hash key */
|
|
|
|
g_hash_table_insert(parent_hash, tag->name, new_iter);
|
|
|
|
/* instead of ignoring the appended child iter below, use the one in the hash table */
|
|
|
|
child = new_iter;
|
2007-07-20 16:33:16 +00:00
|
|
|
}
|
2008-11-03 17:25:35 +00:00
|
|
|
|
|
|
|
gtk_tree_store_append(tree_store, child, parent);
|
|
|
|
|
|
|
|
name = get_symbol_name(doc, tag, (parent_name != NULL));
|
|
|
|
gtk_tree_store_set(tree_store, child,
|
|
|
|
SYMBOLS_COLUMN_ICON, icon,
|
|
|
|
SYMBOLS_COLUMN_NAME, name,
|
|
|
|
SYMBOLS_COLUMN_TAG, tag, -1);
|
|
|
|
|
|
|
|
if (G_LIKELY(G_IS_OBJECT(icon)))
|
|
|
|
g_object_unref(icon);
|
2007-01-13 13:25:05 +00:00
|
|
|
}
|
2008-11-03 17:25:35 +00:00
|
|
|
else
|
|
|
|
geany_debug("Missing symbol-tree parent iter for type %d!", tag->type);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void add_tree_tags(GeanyDocument *doc, const GList *tags)
|
|
|
|
{
|
|
|
|
const GList *item;
|
|
|
|
GHashTable *parent_hash;
|
|
|
|
|
|
|
|
/* Create a hash table "parent_tag_name":(GtkTreeIter*) */
|
|
|
|
parent_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
|
|
|
|
|
|
|
|
/* find and store all parent names in the hash table */
|
|
|
|
for (item = tags; item; item = g_list_next(item))
|
|
|
|
{
|
|
|
|
const TMTag *tag = item->data;
|
|
|
|
const gchar *name = get_parent_name(tag, FILETYPE_ID(doc->file_type));
|
|
|
|
|
|
|
|
if (name)
|
|
|
|
g_hash_table_insert(parent_hash, (gpointer)name, NULL);
|
|
|
|
}
|
|
|
|
for (item = tags; item; item = g_list_next(item))
|
|
|
|
{
|
|
|
|
const TMTag *tag = item->data;
|
|
|
|
|
|
|
|
add_tree_tag(doc, tag, parent_hash);
|
2007-01-13 13:25:05 +00:00
|
|
|
}
|
2008-10-02 15:48:50 +00:00
|
|
|
g_hash_table_destroy(parent_hash);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
/* @param item Must be a (gpointer*) for implementation reasons.
|
|
|
|
* @example gchar *name = *item; (for when the GPtrArray contains char pointers). */
|
|
|
|
#define foreach_ptr_array(item, ptr_array) \
|
|
|
|
for (item = ptr_array->pdata; item < &ptr_array->pdata[ptr_array->len]; item++)
|
|
|
|
|
|
|
|
/* we don't want to sort 1st-level nodes, but we can't return 0 because the tree sort
|
|
|
|
* is not stable, so the order is already lost. */
|
|
|
|
static gint compare_top_level_names(const gchar *a, const gchar *b)
|
|
|
|
{
|
|
|
|
gpointer *item;
|
|
|
|
|
|
|
|
foreach_ptr_array(item, top_level_iter_names)
|
|
|
|
{
|
|
|
|
const gchar *name = *item;
|
|
|
|
|
|
|
|
if (utils_str_equal(name, a))
|
|
|
|
return -1;
|
|
|
|
if (utils_str_equal(name, b))
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
g_warning("Couldn't find top level node '%s' or '%s'!", a, b);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-05 12:50:29 +00:00
|
|
|
static gboolean tag_has_missing_parent(const TMTag *tag, GtkTreeStore *store,
|
|
|
|
GtkTreeIter *iter)
|
|
|
|
{
|
|
|
|
/* if the tag has a parent tag, it should be at depth >= 2 */
|
|
|
|
return NZV(tag->atts.entry.scope) &&
|
|
|
|
gtk_tree_store_iter_depth(store, iter) == 1;
|
|
|
|
}
|
2008-11-04 18:14:04 +00:00
|
|
|
|
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
static gint tree_sort_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
gboolean sort_by_name = GPOINTER_TO_INT(user_data);
|
|
|
|
const TMTag *tag_a, *tag_b;
|
|
|
|
|
|
|
|
gtk_tree_model_get(model, a, SYMBOLS_COLUMN_TAG, &tag_a, -1);
|
|
|
|
gtk_tree_model_get(model, b, SYMBOLS_COLUMN_TAG, &tag_b, -1);
|
|
|
|
|
2008-11-05 12:50:29 +00:00
|
|
|
/* Check if the iters can be sorted based on tag name and line, not tree item name.
|
|
|
|
* Sort by tree name if the scope was prepended, e.g. 'ScopeNameWithNoTag::TagName'. */
|
|
|
|
if (tag_a && !tag_has_missing_parent(tag_a, GTK_TREE_STORE(model), a) &&
|
|
|
|
tag_b && !tag_has_missing_parent(tag_b, GTK_TREE_STORE(model), b))
|
2008-11-03 17:25:35 +00:00
|
|
|
{
|
|
|
|
return sort_by_name ? compare_symbol(tag_a, tag_b) :
|
|
|
|
compare_symbol_lines(tag_a, tag_b);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gchar *astr, *bstr;
|
|
|
|
gint cmp;
|
|
|
|
|
|
|
|
gtk_tree_model_get(model, a, SYMBOLS_COLUMN_NAME, &astr, -1);
|
|
|
|
gtk_tree_model_get(model, b, SYMBOLS_COLUMN_NAME, &bstr, -1);
|
|
|
|
|
|
|
|
/* if a is toplevel, b must be also */
|
|
|
|
if (gtk_tree_store_iter_depth(GTK_TREE_STORE(model), a) == 0)
|
2008-11-04 18:14:04 +00:00
|
|
|
{
|
2008-11-03 17:25:35 +00:00
|
|
|
cmp = compare_top_level_names(astr, bstr);
|
2008-11-04 18:14:04 +00:00
|
|
|
}
|
2008-11-03 17:25:35 +00:00
|
|
|
else
|
2008-11-04 18:14:04 +00:00
|
|
|
{
|
2008-11-03 17:25:35 +00:00
|
|
|
cmp = strcmp(astr, bstr);
|
|
|
|
|
2008-11-04 18:14:04 +00:00
|
|
|
/* sort duplicate 'ScopeName::OverloadedTagName' items by line as well */
|
2008-11-05 12:58:53 +00:00
|
|
|
if (tag_a && tag_b)
|
|
|
|
if (!sort_by_name ||
|
|
|
|
(utils_str_equal(tag_a->name, tag_b->name) &&
|
|
|
|
utils_str_equal(tag_a->atts.entry.scope, tag_b->atts.entry.scope)))
|
|
|
|
cmp = compare_symbol_lines(tag_a, tag_b);
|
2008-11-04 18:14:04 +00:00
|
|
|
}
|
2008-11-03 17:25:35 +00:00
|
|
|
g_free(astr);
|
|
|
|
g_free(bstr);
|
|
|
|
return cmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void sort_tree(GtkTreeStore *store, gboolean sort_by_name)
|
|
|
|
{
|
|
|
|
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), 1, tree_sort_func,
|
|
|
|
GINT_TO_POINTER(sort_by_name), NULL);
|
|
|
|
|
|
|
|
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), 1, GTK_SORT_ASCENDING);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-02 15:48:50 +00:00
|
|
|
gboolean symbols_recreate_tag_list(GeanyDocument *doc, gint sort_mode)
|
|
|
|
{
|
2008-11-03 17:25:35 +00:00
|
|
|
GList *tags;
|
2008-10-02 15:48:50 +00:00
|
|
|
static gint prev_sort_mode = SYMBOLS_SORT_BY_NAME;
|
|
|
|
|
|
|
|
g_return_val_if_fail(doc != NULL, FALSE);
|
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
tags = get_tag_list(doc, tm_tag_max_t);
|
|
|
|
if (tags == NULL)
|
2008-10-02 15:48:50 +00:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
/* Make sure the model stays with us after the tree view unrefs it */
|
|
|
|
g_object_ref(GTK_TREE_MODEL(doc->priv->tag_store));
|
|
|
|
/* Detach model from view */
|
|
|
|
gtk_tree_view_set_model(GTK_TREE_VIEW(doc->priv->tag_tree), NULL);
|
|
|
|
/* Clear all contents */
|
|
|
|
gtk_tree_store_clear(doc->priv->tag_store);
|
|
|
|
|
|
|
|
/* add grandparent type iters */
|
2008-11-03 17:25:35 +00:00
|
|
|
add_top_level_items(doc);
|
2008-10-02 15:48:50 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
add_tree_tags(doc, tags);
|
|
|
|
g_list_free(tags);
|
2008-10-02 12:06:20 +00:00
|
|
|
|
2008-09-26 17:28:50 +00:00
|
|
|
hide_empty_rows(doc->priv->tag_store);
|
2008-10-02 12:06:20 +00:00
|
|
|
|
2008-11-03 17:25:35 +00:00
|
|
|
if (sort_mode == SYMBOLS_SORT_USE_PREVIOUS)
|
|
|
|
sort_mode = prev_sort_mode;
|
|
|
|
else
|
|
|
|
prev_sort_mode = sort_mode;
|
|
|
|
|
|
|
|
sort_tree(doc->priv->tag_store, sort_mode == SYMBOLS_SORT_BY_NAME);
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* Re-attach model to view */
|
2008-09-26 17:28:50 +00:00
|
|
|
gtk_tree_view_set_model(GTK_TREE_VIEW(doc->priv->tag_tree),
|
|
|
|
GTK_TREE_MODEL(doc->priv->tag_store));
|
|
|
|
g_object_unref(GTK_TREE_MODEL(doc->priv->tag_store));
|
|
|
|
gtk_tree_view_expand_all(GTK_TREE_VIEW(doc->priv->tag_tree));
|
2008-05-12 13:46:19 +00:00
|
|
|
|
2007-01-13 13:25:05 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-27 11:00:29 +00:00
|
|
|
/* Detects a global tags filetype from the *.lang.* language extension.
|
|
|
|
* Returns NULL if there was no matching TM language. */
|
2008-05-16 14:17:54 +00:00
|
|
|
static GeanyFiletype *detect_global_tags_filetype(const gchar *utf8_filename)
|
2007-04-27 11:00:29 +00:00
|
|
|
{
|
|
|
|
gchar *tags_ext;
|
|
|
|
gchar *shortname = g_strdup(utf8_filename);
|
2008-05-16 14:17:54 +00:00
|
|
|
GeanyFiletype *ft = NULL;
|
2007-04-27 11:00:29 +00:00
|
|
|
|
|
|
|
tags_ext = strstr(shortname, ".tags");
|
|
|
|
if (tags_ext)
|
|
|
|
{
|
2008-02-27 13:17:29 +00:00
|
|
|
*tags_ext = '\0'; /* remove .tags extension */
|
2008-03-05 17:09:57 +00:00
|
|
|
ft = filetypes_detect_from_extension(shortname);
|
2007-04-27 11:00:29 +00:00
|
|
|
}
|
|
|
|
g_free(shortname);
|
|
|
|
|
2007-05-26 15:26:05 +00:00
|
|
|
if (ft == NULL || ! filetype_has_tags(ft))
|
2007-04-27 11:00:29 +00:00
|
|
|
return NULL;
|
|
|
|
return ft;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-03-21 13:26:16 +00:00
|
|
|
/* Adapted from anjuta-2.0.2/global-tags/tm_global_tags.c, thanks.
|
2007-04-27 11:00:29 +00:00
|
|
|
* Needs full paths for filenames, except for C/C++ tag files, when CFLAGS includes
|
|
|
|
* the relevant path.
|
2007-03-21 13:26:16 +00:00
|
|
|
* Example:
|
2007-04-27 11:00:29 +00:00
|
|
|
* CFLAGS=-I/home/user/libname-1.x geany -g libname.d.tags libname.h */
|
2007-09-28 11:48:55 +00:00
|
|
|
int symbols_generate_global_tags(int argc, char **argv, gboolean want_preprocess)
|
2007-03-21 13:26:16 +00:00
|
|
|
{
|
|
|
|
/* -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;
|
2007-04-27 11:00:29 +00:00
|
|
|
const char *tags_file = argv[1];
|
|
|
|
char *utf8_fname;
|
2008-05-16 14:17:54 +00:00
|
|
|
GeanyFiletype *ft;
|
2007-04-27 11:00:29 +00:00
|
|
|
|
|
|
|
utf8_fname = utils_get_utf8_from_locale(tags_file);
|
|
|
|
ft = detect_global_tags_filetype(utf8_fname);
|
|
|
|
g_free(utf8_fname);
|
|
|
|
|
|
|
|
if (ft == NULL)
|
|
|
|
{
|
2007-04-30 11:56:50 +00:00
|
|
|
g_printerr(_("Unknown filetype extension for \"%s\".\n"), tags_file);
|
2007-04-27 11:00:29 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2008-07-03 13:46:01 +00:00
|
|
|
/* load ignore list for C/C++ parser */
|
|
|
|
if (ft->id == GEANY_FILETYPES_C || ft->id == GEANY_FILETYPES_CPP)
|
|
|
|
load_c_ignore_tags();
|
|
|
|
|
2007-09-28 11:48:55 +00:00
|
|
|
if (want_preprocess && (ft->id == GEANY_FILETYPES_C || ft->id == GEANY_FILETYPES_CPP))
|
2007-04-27 11:00:29 +00:00
|
|
|
command = g_strdup_printf("%s %s", pre_process, NVL(getenv("CFLAGS"), ""));
|
|
|
|
else
|
2008-02-27 13:17:29 +00:00
|
|
|
command = NULL; /* don't preprocess */
|
2007-04-27 11:00:29 +00:00
|
|
|
|
|
|
|
geany_debug("Generating %s tags file.", ft->name);
|
2007-11-30 18:01:43 +00:00
|
|
|
tm_get_workspace();
|
2007-11-01 11:49:29 +00:00
|
|
|
status = tm_workspace_create_global_tags(app->configdir, command,
|
2007-03-21 13:26:16 +00:00
|
|
|
(const char **) (argv + 2),
|
2007-04-27 11:00:29 +00:00
|
|
|
argc - 2, tags_file, ft->lang);
|
2007-03-21 13:26:16 +00:00
|
|
|
g_free(command);
|
2008-07-03 13:46:01 +00:00
|
|
|
symbols_finalize(); /* free c_tags_ignore data */
|
2007-04-27 11:00:29 +00:00
|
|
|
if (! status)
|
|
|
|
{
|
2007-11-30 18:01:43 +00:00
|
|
|
g_printerr(_("Failed to create tags file, perhaps because no tags "
|
|
|
|
"were found.\n"));
|
2007-03-21 13:26:16 +00:00
|
|
|
return 1;
|
2007-04-27 11:00:29 +00:00
|
|
|
}
|
2007-03-21 13:26:16 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-04-30 11:56:50 +00:00
|
|
|
g_printerr(_("Usage: %s -g <Tag File> <File list>\n\n"), argv[0]);
|
|
|
|
g_printerr(_("Example:\n"
|
2007-03-21 13:26:16 +00:00
|
|
|
"CFLAGS=`pkg-config gtk+-2.0 --cflags` %s -g gtk2.c.tags"
|
2007-04-30 11:56:50 +00:00
|
|
|
" /usr/include/gtk-2.0/gtk/gtk.h\n"), argv[0]);
|
2007-03-21 13:26:16 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-20 11:24:23 +00:00
|
|
|
void symbols_show_load_tags_dialog(void)
|
2007-04-18 15:21:33 +00:00
|
|
|
{
|
|
|
|
GtkWidget *dialog;
|
|
|
|
GtkFileFilter *filter;
|
|
|
|
|
2008-05-22 14:41:28 +00:00
|
|
|
dialog = gtk_file_chooser_dialog_new(_("Load Tags"), GTK_WINDOW(main_widgets.window),
|
2007-04-18 15:21:33 +00:00
|
|
|
GTK_FILE_CHOOSER_ACTION_OPEN,
|
|
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
|
|
|
GTK_STOCK_OPEN, GTK_RESPONSE_OK,
|
|
|
|
NULL);
|
2007-05-15 15:16:16 +00:00
|
|
|
gtk_widget_set_name(dialog, "GeanyDialog");
|
2007-04-18 15:21:33 +00:00
|
|
|
filter = gtk_file_filter_new();
|
|
|
|
gtk_file_filter_set_name(filter, _("Geany tag files (*.tags)"));
|
|
|
|
gtk_file_filter_add_pattern(filter, "*.tags");
|
|
|
|
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
|
|
|
|
|
|
|
|
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
|
|
|
|
{
|
|
|
|
GSList *flist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
|
|
|
|
GSList *item;
|
|
|
|
|
|
|
|
for (item = flist; item != NULL; item = g_slist_next(item))
|
|
|
|
{
|
|
|
|
gchar *fname = item->data;
|
|
|
|
gchar *utf8_fname;
|
2008-05-16 14:17:54 +00:00
|
|
|
GeanyFiletype *ft;
|
2007-04-18 15:21:33 +00:00
|
|
|
|
|
|
|
utf8_fname = utils_get_utf8_from_locale(fname);
|
2007-04-27 11:00:29 +00:00
|
|
|
ft = detect_global_tags_filetype(utf8_fname);
|
|
|
|
|
|
|
|
if (ft != NULL && tm_workspace_load_global_tags(fname, ft->lang))
|
2007-10-24 10:52:48 +00:00
|
|
|
ui_set_statusbar(TRUE, _("Loaded %s tags file '%s'."), ft->name, utf8_fname);
|
2007-04-27 11:00:29 +00:00
|
|
|
else
|
2007-10-24 10:52:48 +00:00
|
|
|
ui_set_statusbar(TRUE, _("Could not load tags file '%s'."), utf8_fname);
|
2007-04-27 11:00:29 +00:00
|
|
|
|
2007-04-18 15:21:33 +00:00
|
|
|
g_free(utf8_fname);
|
|
|
|
g_free(fname);
|
|
|
|
}
|
|
|
|
g_slist_free(flist);
|
|
|
|
}
|
|
|
|
gtk_widget_destroy(dialog);
|
|
|
|
}
|
2007-05-11 11:58:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* Fills a hash table with filetype keys that hold a linked list of filenames. */
|
|
|
|
static GHashTable *get_tagfile_hash(const GSList *file_list)
|
|
|
|
{
|
|
|
|
const GSList *node;
|
|
|
|
GHashTable *hash = g_hash_table_new(NULL, NULL);
|
|
|
|
|
|
|
|
for (node = file_list; node != NULL; node = g_slist_next(node))
|
|
|
|
{
|
|
|
|
GList *fnames;
|
|
|
|
gchar *fname = node->data;
|
|
|
|
gchar *utf8_fname = utils_get_utf8_from_locale(fname);
|
2008-05-16 14:17:54 +00:00
|
|
|
GeanyFiletype *ft = detect_global_tags_filetype(utf8_fname);
|
2007-05-11 11:58:56 +00:00
|
|
|
|
|
|
|
g_free(utf8_fname);
|
|
|
|
|
2008-05-07 13:54:21 +00:00
|
|
|
if (FILETYPE_ID(ft) < GEANY_FILETYPES_NONE)
|
2007-05-11 11:58:56 +00:00
|
|
|
{
|
2008-02-27 13:17:29 +00:00
|
|
|
fnames = g_hash_table_lookup(hash, ft); /* may be NULL */
|
2007-05-11 11:58:56 +00:00
|
|
|
fnames = g_list_append(fnames, fname);
|
|
|
|
g_hash_table_insert(hash, ft, fnames);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
geany_debug("Unknown filetype for file '%s'.", fname);
|
|
|
|
}
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-20 11:24:23 +00:00
|
|
|
static GHashTable *init_user_tags(void)
|
2007-05-11 11:58:56 +00:00
|
|
|
{
|
|
|
|
GSList *file_list;
|
|
|
|
GHashTable *lang_hash;
|
|
|
|
|
|
|
|
user_tags_dir = g_strconcat(app->configdir, G_DIR_SEPARATOR_S, "tags", NULL);
|
|
|
|
file_list = utils_get_file_list(user_tags_dir, NULL, NULL);
|
|
|
|
lang_hash = get_tagfile_hash(file_list);
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* don't need to delete list contents because they are now used for hash contents */
|
2007-05-11 11:58:56 +00:00
|
|
|
g_slist_free(file_list);
|
2007-07-04 12:07:57 +00:00
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* create the tags dir for next time if it doesn't exist */
|
2007-07-04 12:07:57 +00:00
|
|
|
if (! g_file_test(user_tags_dir, G_FILE_TEST_IS_DIR))
|
|
|
|
{
|
|
|
|
utils_mkdir(user_tags_dir, FALSE);
|
|
|
|
}
|
2007-05-11 11:58:56 +00:00
|
|
|
return lang_hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void load_user_tags(filetype_id ft_id)
|
|
|
|
{
|
2008-05-07 13:54:21 +00:00
|
|
|
static guchar tags_loaded[GEANY_FILETYPES_NONE] = {0};
|
2007-05-11 11:58:56 +00:00
|
|
|
static GHashTable *lang_hash = NULL;
|
|
|
|
GList *fnames;
|
|
|
|
const GList *node;
|
2008-05-16 14:17:54 +00:00
|
|
|
const GeanyFiletype *ft = filetypes[ft_id];
|
2007-05-11 11:58:56 +00:00
|
|
|
|
2008-05-07 13:54:21 +00:00
|
|
|
g_return_if_fail(ft_id < GEANY_FILETYPES_NONE);
|
2007-05-11 11:58:56 +00:00
|
|
|
|
|
|
|
if (tags_loaded[ft_id])
|
|
|
|
return;
|
2008-02-27 13:17:29 +00:00
|
|
|
tags_loaded[ft_id] = TRUE; /* prevent reloading */
|
2007-05-11 11:58:56 +00:00
|
|
|
|
|
|
|
if (lang_hash == NULL)
|
|
|
|
lang_hash = init_user_tags();
|
|
|
|
|
|
|
|
fnames = g_hash_table_lookup(lang_hash, ft);
|
|
|
|
|
|
|
|
for (node = fnames; node != NULL; node = g_list_next(node))
|
|
|
|
{
|
|
|
|
const gint tm_lang = ft->lang;
|
|
|
|
gchar *fname;
|
|
|
|
|
|
|
|
fname = g_strconcat(user_tags_dir, G_DIR_SEPARATOR_S, node->data, NULL);
|
2008-08-28 16:59:20 +00:00
|
|
|
if (tm_workspace_load_global_tags(fname, tm_lang))
|
|
|
|
{
|
|
|
|
geany_debug("Loaded %s (%s), total tags: %u.", fname, ft->name,
|
|
|
|
tm_get_workspace()->global_tags->len);
|
|
|
|
}
|
2007-05-11 11:58:56 +00:00
|
|
|
g_free(fname);
|
|
|
|
}
|
|
|
|
g_list_foreach(fnames, (GFunc) g_free, NULL);
|
|
|
|
g_list_free(fnames);
|
|
|
|
g_hash_table_remove(lang_hash, (gpointer) ft);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-25 12:39:41 +00:00
|
|
|
gboolean symbols_goto_tag(const gchar *name, gboolean definition)
|
|
|
|
{
|
|
|
|
const gint forward_types = tm_tag_prototype_t | tm_tag_externvar_t;
|
|
|
|
gint type;
|
2008-03-24 16:44:43 +00:00
|
|
|
TMTag *tmtag = NULL;
|
2008-06-15 13:35:48 +00:00
|
|
|
GeanyDocument *old_doc = document_get_current();
|
2008-02-10 12:34:28 +00:00
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* goto tag definition: all except prototypes / forward declarations / externs */
|
2007-09-25 12:39:41 +00:00
|
|
|
type = (definition) ? tm_tag_max_t - forward_types : forward_types;
|
|
|
|
|
2008-03-24 16:44:43 +00:00
|
|
|
/* first look in the current document */
|
2008-06-15 13:35:48 +00:00
|
|
|
if (old_doc != NULL && old_doc->tm_file)
|
|
|
|
tmtag = find_work_object_tag(old_doc->tm_file, name, type);
|
2008-03-24 16:44:43 +00:00
|
|
|
|
|
|
|
/* if not found, look in the workspace */
|
|
|
|
if (tmtag == NULL)
|
|
|
|
tmtag = find_workspace_tag(name, type);
|
|
|
|
|
2007-09-25 12:39:41 +00:00
|
|
|
if (tmtag != NULL)
|
|
|
|
{
|
2008-06-15 13:35:48 +00:00
|
|
|
GeanyDocument *new_doc = document_find_by_real_path(
|
2008-06-03 17:22:04 +00:00
|
|
|
tmtag->atts.entry.file->work_object.file_name);
|
|
|
|
|
2008-02-27 13:17:29 +00:00
|
|
|
/* not found in opened document, should open */
|
2008-06-15 13:35:48 +00:00
|
|
|
if (new_doc == NULL)
|
2008-01-02 21:20:33 +00:00
|
|
|
{
|
2008-06-15 13:35:48 +00:00
|
|
|
new_doc = document_open_file(tmtag->atts.entry.file->work_object.file_name, FALSE, NULL, NULL);
|
2008-01-02 21:20:33 +00:00
|
|
|
}
|
2007-09-25 12:39:41 +00:00
|
|
|
|
2008-06-15 13:35:48 +00:00
|
|
|
if (navqueue_goto_line(old_doc, new_doc, tmtag->atts.entry.line))
|
2007-09-25 12:39:41 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
2008-02-27 13:17:29 +00:00
|
|
|
/* if we are here, there was no match and we are beeping ;-) */
|
2007-09-25 12:39:41 +00:00
|
|
|
utils_beep();
|
|
|
|
if (type == forward_types)
|
2007-10-24 10:52:48 +00:00
|
|
|
ui_set_statusbar(FALSE, _("Forward declaration \"%s\" not found."), name);
|
2007-09-25 12:39:41 +00:00
|
|
|
else
|
2007-10-24 10:52:48 +00:00
|
|
|
ui_set_statusbar(FALSE, _("Definition of \"%s\" not found."), name);
|
2007-09-25 12:39:41 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-08 16:30:37 +00:00
|
|
|
/* This could perhaps be improved to check for #if, class etc. */
|
|
|
|
static gint get_function_fold_number(GeanyDocument *doc)
|
|
|
|
{
|
|
|
|
/* for Java the functions are always one fold level above the class scope */
|
|
|
|
if (FILETYPE_ID(doc->file_type) == GEANY_FILETYPES_JAVA)
|
|
|
|
return SC_FOLDLEVELBASE + 1;
|
|
|
|
else
|
|
|
|
return SC_FOLDLEVELBASE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Should be used only with symbols_get_current_function. */
|
|
|
|
static gboolean current_function_changed(GeanyDocument *doc, gint cur_line, gint fold_level)
|
|
|
|
{
|
|
|
|
static gint old_line = -2;
|
|
|
|
static GeanyDocument *old_doc = NULL;
|
|
|
|
static gint old_fold_num = -1;
|
|
|
|
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)
|
|
|
|
ret = TRUE;
|
|
|
|
else
|
|
|
|
if (cur_line == old_line)
|
|
|
|
ret = FALSE;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* if the line has only changed by 1 */
|
|
|
|
if (abs(cur_line - old_line) == 1)
|
|
|
|
{
|
|
|
|
const gint fn_fold =
|
|
|
|
get_function_fold_number(doc);
|
|
|
|
/* It's the same function if the fold number hasn't changed, or both the new
|
|
|
|
* and old fold numbers are above the function fold number. */
|
|
|
|
gboolean same =
|
|
|
|
fold_num == old_fold_num ||
|
|
|
|
(old_fold_num > fn_fold && fold_num > fn_fold);
|
|
|
|
|
|
|
|
ret = ! same;
|
|
|
|
}
|
|
|
|
else ret = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* record current line and file index for next time */
|
|
|
|
old_line = cur_line;
|
|
|
|
old_doc = doc;
|
|
|
|
old_fold_num = fold_num;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Parse the function name up to 2 lines before tag_line.
|
|
|
|
* C++ like syntax should be parsed by parse_cpp_function_at_line, otherwise the return
|
|
|
|
* type or argument names can be confused with the function name. */
|
|
|
|
static gchar *parse_function_at_line(ScintillaObject *sci, gint tag_line)
|
|
|
|
{
|
|
|
|
gint start, end, max_pos;
|
|
|
|
gchar *cur_tag;
|
|
|
|
gint fn_style;
|
|
|
|
|
|
|
|
switch (sci_get_lexer(sci))
|
|
|
|
{
|
|
|
|
case SCLEX_RUBY: fn_style = SCE_RB_DEFNAME; break;
|
|
|
|
case SCLEX_PYTHON: fn_style = SCE_P_DEFNAME; break;
|
|
|
|
default: fn_style = SCE_C_IDENTIFIER; /* several lexers use SCE_C_IDENTIFIER */
|
|
|
|
}
|
|
|
|
start = sci_get_position_from_line(sci, tag_line - 2);
|
|
|
|
max_pos = sci_get_position_from_line(sci, tag_line + 1);
|
|
|
|
while (sci_get_style_at(sci, start) != fn_style
|
|
|
|
&& start < max_pos) start++;
|
|
|
|
|
|
|
|
end = start;
|
|
|
|
while (sci_get_style_at(sci, end) == fn_style
|
|
|
|
&& end < max_pos) end++;
|
|
|
|
|
|
|
|
if (start == end) return NULL;
|
|
|
|
cur_tag = g_malloc(end - start + 1);
|
|
|
|
sci_get_text_range(sci, start, end, cur_tag);
|
|
|
|
return cur_tag;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Parse the function name */
|
|
|
|
static gchar *parse_cpp_function_at_line(ScintillaObject *sci, gint tag_line)
|
|
|
|
{
|
|
|
|
gint start, end, first_pos, max_pos;
|
|
|
|
gint tmp;
|
|
|
|
gchar c;
|
|
|
|
gchar *cur_tag;
|
|
|
|
|
|
|
|
first_pos = end = sci_get_position_from_line(sci, tag_line);
|
|
|
|
max_pos = sci_get_position_from_line(sci, tag_line + 1);
|
|
|
|
tmp = 0;
|
|
|
|
/* goto the begin of function body */
|
|
|
|
while (end < max_pos &&
|
|
|
|
(tmp = sci_get_char_at(sci, end)) != '{' &&
|
|
|
|
tmp != 0) end++;
|
|
|
|
if (tmp == 0) end --;
|
|
|
|
|
|
|
|
/* go back to the end of function identifier */
|
|
|
|
while (end > 0 && end > first_pos - 500 &&
|
|
|
|
(tmp = sci_get_char_at(sci, end)) != '(' &&
|
|
|
|
tmp != 0) end--;
|
|
|
|
end--;
|
|
|
|
if (end < 0) end = 0;
|
|
|
|
|
|
|
|
/* skip whitespaces between identifier and ( */
|
|
|
|
while (end > 0 && isspace(sci_get_char_at(sci, end))) end--;
|
|
|
|
|
|
|
|
start = end;
|
|
|
|
c = 0;
|
|
|
|
/* Use tmp to find SCE_C_IDENTIFIER or SCE_C_GLOBALCLASS chars */
|
|
|
|
while (start >= 0 && ((tmp = sci_get_style_at(sci, start)) == SCE_C_IDENTIFIER
|
|
|
|
|| tmp == SCE_C_GLOBALCLASS
|
|
|
|
|| (c = sci_get_char_at(sci, start)) == '~'
|
|
|
|
|| c == ':'))
|
|
|
|
start--;
|
|
|
|
if (start != 0 && start < end) start++; /* correct for last non-matching char */
|
|
|
|
|
|
|
|
if (start == end) return NULL;
|
|
|
|
cur_tag = g_malloc(end - start + 2);
|
|
|
|
sci_get_text_range(sci, start, end + 1, cur_tag);
|
|
|
|
return cur_tag;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 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 tag_line = -1;
|
|
|
|
static gchar *cur_tag = NULL;
|
|
|
|
gint line;
|
|
|
|
gint fold_level;
|
|
|
|
TMWorkObject *tm_file;
|
|
|
|
|
|
|
|
if (doc == NULL) /* reset current function */
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-07-14 11:13:54 +00:00
|
|
|
line = sci_get_current_line(doc->editor->sci);
|
|
|
|
fold_level = sci_get_fold_level(doc->editor->sci, line);
|
2008-07-08 16:30:37 +00:00
|
|
|
/* 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 document has no changes, get the previous function name from TM */
|
|
|
|
if(! doc->changed && tm_file != NULL && tm_file->tags_array != NULL)
|
|
|
|
{
|
|
|
|
const TMTag *tag = (const TMTag*) tm_get_current_function(tm_file->tags_array, line);
|
|
|
|
|
|
|
|
if (tag != NULL)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
const gint fn_fold = get_function_fold_number(doc);
|
|
|
|
|
|
|
|
tag_line = line;
|
|
|
|
do /* find the top level fold point */
|
|
|
|
{
|
2008-07-14 11:13:54 +00:00
|
|
|
tag_line = sci_get_fold_parent(doc->editor->sci, tag_line);
|
|
|
|
fold_level = sci_get_fold_level(doc->editor->sci, tag_line);
|
2008-07-08 16:30:37 +00:00
|
|
|
} while (tag_line >= 0 &&
|
|
|
|
(fold_level & SC_FOLDLEVELNUMBERMASK) != fn_fold);
|
|
|
|
|
|
|
|
if (tag_line >= 0)
|
|
|
|
{
|
2008-07-14 11:13:54 +00:00
|
|
|
if (sci_get_lexer(doc->editor->sci) == SCLEX_CPP)
|
|
|
|
cur_tag = parse_cpp_function_at_line(doc->editor->sci, tag_line);
|
2008-07-08 16:30:37 +00:00
|
|
|
else
|
2008-07-14 11:13:54 +00:00
|
|
|
cur_tag = parse_function_at_line(doc->editor->sci, tag_line);
|
2008-07-08 16:30:37 +00:00
|
|
|
|
|
|
|
if (cur_tag != NULL)
|
|
|
|
{
|
|
|
|
*tagname = cur_tag;
|
|
|
|
return tag_line;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cur_tag = g_strdup(_("unknown"));
|
|
|
|
*tagname = cur_tag;
|
|
|
|
tag_line = -1;
|
|
|
|
return tag_line;
|
|
|
|
}
|
|
|
|
|
|
|
|
|