diff --git a/moo.kdevelop b/moo.kdevelop index 9d777b43..5eb8a801 100644 --- a/moo.kdevelop +++ b/moo.kdevelop @@ -50,7 +50,7 @@ - --enable-debug --enable-all-gcc-warnings + --enable-debug --enable-all-gcc-warnings --without-python build/debug kdevgccoptions kdevgppoptions diff --git a/moo/mooedit/Makefile.am b/moo/mooedit/Makefile.am index c6c97888..921e11f9 100644 --- a/moo/mooedit/Makefile.am +++ b/moo/mooedit/Makefile.am @@ -33,7 +33,6 @@ mooedit_include_headers = \ moolinemark.h \ moolineview.h \ moooutputfilter.h \ - moooutputfiltersimple.h \ mooplugin-loader.h \ mooplugin-macro.h \ mooplugin.h \ @@ -67,6 +66,7 @@ mooedit_noinst_headers = \ moolangmgr-private.h \ moolangmgr.h \ moolinebuffer.h \ + moooutputfiltersimple.h \ moopluginprefs-glade.h \ mooprint-glade.h \ mootext-private.h \ diff --git a/moo/mooedit/context.cfg b/moo/mooedit/context.cfg index 706605cf..95887ad9 100644 --- a/moo/mooedit/context.cfg +++ b/moo/mooedit/context.cfg @@ -1,6 +1,6 @@ [tool] id = SwitchHeaderAndImplementation -name = Switch Header And Implementation +name = Switch Header and Implementation langs = C, GAP type = moo-script options = need-file diff --git a/moo/mooedit/filters.cfg b/moo/mooedit/filters.cfg new file mode 100644 index 00000000..bf529212 --- /dev/null +++ b/moo/mooedit/filters.cfg @@ -0,0 +1,4 @@ +[filter] +id = bison +name = Bison +patterns = /(?P[^:]+):(?P\d+)\.(\d+|\d+\-\d+):/ stderr diff --git a/moo/mooedit/glade/mooedittools.glade b/moo/mooedit/glade/mooedittools.glade index ff59798f..9086c496 100644 --- a/moo/mooedit/glade/mooedittools.glade +++ b/moo/mooedit/glade/mooedittools.glade @@ -691,7 +691,7 @@ True - 2 + 3 2 False 0 @@ -706,7 +706,7 @@ GTK_JUSTIFY_LEFT False False - 0 + 1 0.5 0 0 @@ -753,7 +753,7 @@ Something GTK_JUSTIFY_LEFT False False - 0 + 1 0.5 0 0 @@ -790,6 +790,53 @@ Something fill + + + + True + Filter: + False + False + GTK_JUSTIFY_LEFT + False + False + 1 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 2 + 3 + fill + + + + + + + True + None +Something + + False + True + + + 1 + 2 + 2 + 3 + fill + fill + + 0 diff --git a/moo/mooedit/moocommand-exe-unix.c b/moo/mooedit/moocommand-exe-unix.c index 132a33a7..a422fd6e 100644 --- a/moo/mooedit/moocommand-exe-unix.c +++ b/moo/mooedit/moocommand-exe-unix.c @@ -19,6 +19,7 @@ #include "mooutils/mooi18n.h" #include "mooutils/mooglade.h" #include "mooutils/mooutils-fs.h" +#include "mooutils/mooutils-misc.h" #include "mooutils/moospawn.h" #include #include @@ -33,6 +34,11 @@ #define MOO_COMMAND_EXE_INPUT_DEFAULT MOO_COMMAND_EXE_INPUT_NONE #define MOO_COMMAND_EXE_OUTPUT_DEFAULT MOO_COMMAND_EXE_OUTPUT_NONE +enum { + COLUMN_NAME, + COLUMN_ID +}; + enum { KEY_INPUT, KEY_OUTPUT, @@ -644,6 +650,92 @@ init_combo (GtkComboBox *combo, g_object_unref (store); } +static void +init_filter_combo (GtkComboBox *combo) +{ + GtkListStore *store; + GtkCellRenderer *cell; + GtkTreeIter iter; + GSList *ids; + + cell = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell, + "text", COLUMN_NAME, NULL); + + store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); + gtk_combo_box_set_model (combo, GTK_TREE_MODEL (store)); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, COLUMN_NAME, _("None"), -1); + + ids = moo_command_filter_list (); + + while (ids) + { + const char *name; + char *id = ids->data; + + id = ids->data; + ids = g_slist_delete_link (ids, ids); + name = moo_command_filter_lookup (id); + + if (!name) + { + g_critical ("%s: oops", G_STRLOC); + continue; + } + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COLUMN_ID, id, + COLUMN_NAME, _(name), + -1); + + g_free (id); + } + + g_object_unref (store); +} + +static void +set_filter_combo (GtkComboBox *combo, + const char *id) +{ + GtkTreeModel *model; + GtkTreeIter iter; + + model = gtk_combo_box_get_model (combo); + + if (!id) + { + gtk_combo_box_set_active (combo, 0); + return; + } + + if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 1)) + { + do { + char *id_here; + + gtk_tree_model_get (model, &iter, COLUMN_ID, &id_here, -1); + + if (!strcmp (id_here, id)) + { + gtk_combo_box_set_active_iter (combo, &iter); + g_free (id_here); + return; + } + + g_free (id_here); + } + while (gtk_tree_model_iter_next (model, &iter)); + } + + g_warning ("unknown filter %s", id); + gtk_combo_box_set_active (combo, -1); +} + static GtkWidget * exe_type_create_widget (G_GNUC_UNUSED MooCommandType *type) { @@ -666,6 +758,7 @@ exe_type_create_widget (G_GNUC_UNUSED MooCommandType *type) init_combo (moo_glade_xml_get_widget (xml, "input"), input_names, G_N_ELEMENTS (input_names)); init_combo (moo_glade_xml_get_widget (xml, "output"), output_names, G_N_ELEMENTS (output_names)); + init_filter_combo (moo_glade_xml_get_widget (xml, "filter")); g_object_set_data_full (G_OBJECT (page), "moo-glade-xml", xml, g_object_unref); return page; @@ -699,6 +792,18 @@ exe_type_load_data (G_GNUC_UNUSED MooCommandType *type, parse_output (moo_command_data_get (data, KEY_OUTPUT), &index); gtk_combo_box_set_active (moo_glade_xml_get_widget (xml, "output"), index); + + if (index == MOO_COMMAND_EXE_OUTPUT_PANE) + { + gtk_widget_set_sensitive (moo_glade_xml_get_widget (xml, "filter"), TRUE); + set_filter_combo (moo_glade_xml_get_widget (xml, "filter"), + moo_command_data_get (data, KEY_FILTER)); + } + else + { + gtk_widget_set_sensitive (moo_glade_xml_get_widget (xml, "filter"), FALSE); + set_filter_combo (moo_glade_xml_get_widget (xml, "filter"), NULL); + } } @@ -723,7 +828,7 @@ exe_type_save_data (G_GNUC_UNUSED MooCommandType *type, new_cmd_line = moo_text_view_get_text (textview); cmd_line = moo_command_data_get_code (data); - if (strcmp (cmd_line ? cmd_line : "", new_cmd_line ? new_cmd_line : "") != 0) + if (!_moo_str_equal (cmd_line, new_cmd_line)) { moo_command_data_set_code (data, new_cmd_line); changed = TRUE; @@ -747,6 +852,34 @@ exe_type_save_data (G_GNUC_UNUSED MooCommandType *type, changed = TRUE; } + if (index == MOO_COMMAND_EXE_OUTPUT_PANE) + { + const char *old_filter; + char *new_filter = NULL; + GtkComboBox *combo = moo_glade_xml_get_widget (xml, "filter"); + GtkTreeIter iter; + + if (gtk_combo_box_get_active_iter (combo, &iter)) + { + GtkTreeModel *model = gtk_combo_box_get_model (combo); + gtk_tree_model_get (model, &iter, COLUMN_ID, &new_filter, -1); + } + + old_filter = moo_command_data_get (data, KEY_FILTER); + + if (!_moo_str_equal (old_filter, new_filter)) + { + moo_command_data_set (data, KEY_FILTER, new_filter); + changed = TRUE; + } + + g_free (new_filter); + } + else + { + moo_command_data_set (data, KEY_FILTER, NULL); + } + g_free (new_cmd_line); return changed; } @@ -763,7 +896,7 @@ exe_type_data_equal (G_GNUC_UNUSED MooCommandType *type, { const char *val1 = moo_command_data_get (data1, i); const char *val2 = moo_command_data_get (data2, i); - if (strcmp (val1 ? val1 : "", val2 ? val2 : "") != 0) + if (!_moo_str_equal (val1, val2)) return FALSE; } diff --git a/moo/mooedit/moooutputfilter.c b/moo/mooedit/moooutputfilter.c index 79b7ae75..c7c6d84f 100644 --- a/moo/mooedit/moooutputfilter.c +++ b/moo/mooedit/moooutputfilter.c @@ -57,9 +57,9 @@ moo_output_filter_class_init (MooOutputFilterClass *klass) G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MooOutputFilterClass, cmd_start), - g_signal_accumulator_true_handled, NULL, - _moo_marshal_BOOL__VOID, - G_TYPE_BOOLEAN, 0); + NULL, NULL, + _moo_marshal_VOID__VOID, + G_TYPE_NONE, 0); signals[CMD_EXIT] = g_signal_new ("cmd-exit", @@ -176,3 +176,61 @@ moo_output_filter_new (void) { return g_object_new (MOO_TYPE_OUTPUT_FILTER, NULL); } + + +GType +moo_file_line_data_get_type (void) +{ + static GType type = 0; + + if (!type) + type = g_boxed_type_register_static ("MooFileLineData", + (GBoxedCopyFunc) moo_file_line_data_copy, + (GBoxedFreeFunc) moo_file_line_data_free); + + return type; +} + + +MooFileLineData * +moo_file_line_data_new (const char *file, + int line, + int character) +{ + MooFileLineData *data; + + g_return_val_if_fail (file != NULL, NULL); + + data = g_new0 (MooFileLineData, 1); + data->file = g_strdup (file); + data->line = line; + data->character = character; + + return data; +} + + +MooFileLineData * +moo_file_line_data_copy (MooFileLineData *data) +{ + MooFileLineData *copy = NULL; + + if (data) + { + copy = g_memdup (data, sizeof (MooFileLineData)); + copy->file = g_strdup (data->file); + } + + return copy; +} + + +void +moo_file_line_data_free (MooFileLineData *data) +{ + if (data) + { + g_free (data->file); + g_free (data); + } +} diff --git a/moo/mooedit/moooutputfilter.h b/moo/mooedit/moooutputfilter.h index 23c58eb0..a37a0aef 100644 --- a/moo/mooedit/moooutputfilter.h +++ b/moo/mooedit/moooutputfilter.h @@ -19,6 +19,8 @@ G_BEGIN_DECLS +#define MOO_TYPE_FILE_LINE_DATA (moo_file_line_data_get_type ()) + #define MOO_TYPE_OUTPUT_FILTER (moo_output_filter_get_type ()) #define MOO_OUTPUT_FILTER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), MOO_TYPE_OUTPUT_FILTER, MooOutputFilter)) #define MOO_OUTPUT_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOO_TYPE_OUTPUT_FILTER, MooOutputFilterClass)) @@ -28,6 +30,13 @@ G_BEGIN_DECLS typedef struct _MooOutputFilter MooOutputFilter; typedef struct _MooOutputFilterClass MooOutputFilterClass; +typedef struct _MooFileLineData MooFileLineData; + +struct _MooFileLineData { + char *file; + int line; + int character; +}; struct _MooOutputFilter { GObject base; @@ -50,21 +59,28 @@ struct _MooOutputFilterClass { }; -GType moo_output_filter_get_type (void) G_GNUC_CONST; +GType moo_output_filter_get_type (void) G_GNUC_CONST; +GType moo_file_line_data_get_type (void) G_GNUC_CONST; -MooOutputFilter *moo_output_filter_new (void); +MooFileLineData *moo_file_line_data_new (const char *file, + int line, + int character); +MooFileLineData *moo_file_line_data_copy (MooFileLineData *data); +void moo_file_line_data_free (MooFileLineData *data); -void moo_output_filter_set_view (MooOutputFilter *filter, - MooLineView *view); -MooLineView *moo_output_filter_get_view (MooOutputFilter *filter); +MooOutputFilter *moo_output_filter_new (void); -gboolean moo_output_filter_stdout_line (MooOutputFilter *filter, - const char *line); -gboolean moo_output_filter_stderr_line (MooOutputFilter *filter, - const char *line); -void moo_output_filter_cmd_start (MooOutputFilter *filter); -gboolean moo_output_filter_cmd_exit (MooOutputFilter *filter, - int status); +void moo_output_filter_set_view (MooOutputFilter *filter, + MooLineView *view); +MooLineView *moo_output_filter_get_view (MooOutputFilter *filter); + +gboolean moo_output_filter_stdout_line (MooOutputFilter *filter, + const char *line); +gboolean moo_output_filter_stderr_line (MooOutputFilter *filter, + const char *line); +void moo_output_filter_cmd_start (MooOutputFilter *filter); +gboolean moo_output_filter_cmd_exit (MooOutputFilter *filter, + int status); G_END_DECLS diff --git a/moo/mooedit/moooutputfiltersimple.c b/moo/mooedit/moooutputfiltersimple.c index b688dfc3..763d12ad 100644 --- a/moo/mooedit/moooutputfiltersimple.c +++ b/moo/mooedit/moooutputfiltersimple.c @@ -12,31 +12,762 @@ */ #include "mooedit/moooutputfiltersimple.h" +#include "mooedit/moocmdview.h" +#include "mooedit/moocommand.h" +#include "mooedit/mookeyfile.h" +#include "mooutils/eggregex.h" +#include "mooutils/mooutils-gobject.h" +#include "mooutils/mooutils-misc.h" #include +#define FILTERS_FILE "filters.cfg" +#define ITEM_FILTER "filter" +#define KEY_ID "id" +#define KEY_DELETED "deleted" +#define KEY_BUILTIN "builtin" +#define KEY_NAME "name" +#define KEY_ENABLED "enabled" +#define KEY_PATTERNS "patterns" -G_DEFINE_TYPE (MooOutputFilterSimple, moo_output_filter_simple, G_TYPE_OBJECT) + +typedef struct { + EggRegex *re; + MooOutputTextType type; +} RegexInfo; + +typedef struct { + guint ref_count; + RegexInfo *patterns; + guint n_patterns; +} FilterInfo; + +typedef struct { + GHashTable *hash; +} FilterStore; + +struct _MooOutputFilterSimplePrivate { + FilterInfo *info; +}; + + +static FilterStore *filter_store; + + +static void filter_store_init (void); + +static FilterInfo *filter_info_new (guint n_patterns); +static FilterInfo *filter_info_ref (FilterInfo *info); +static void filter_info_unref (FilterInfo *info); + +static void view_activate (MooLineView *view, + int line, + MooOutputFilterSimple *filter); + + +G_DEFINE_TYPE (MooOutputFilterSimple, _moo_output_filter_simple, MOO_TYPE_OUTPUT_FILTER) static void -moo_output_filter_simple_class_init (G_GNUC_UNUSED MooOutputFilterSimpleClass *klass) +moo_output_filter_simple_dispose (GObject *object) { + MooOutputFilterSimple *filter = MOO_OUTPUT_FILTER_SIMPLE (object); + + if (filter->priv->info) + { + filter_info_unref (filter->priv->info); + filter->priv->info = NULL; + } + + G_OBJECT_CLASS (_moo_output_filter_simple_parent_class)->dispose (object); } static void -moo_output_filter_simple_init (G_GNUC_UNUSED MooOutputFilterSimple *filter) +moo_output_filter_simple_attach (MooOutputFilter *base) { + MooOutputFilterSimple *filter = MOO_OUTPUT_FILTER_SIMPLE (base); + + g_return_if_fail (filter->priv->info != NULL); + + g_signal_connect (base->view, "activate", + G_CALLBACK (view_activate), filter); +} + + +static void +moo_output_filter_simple_detach (MooOutputFilter *base) +{ + MooOutputFilterSimple *filter = MOO_OUTPUT_FILTER_SIMPLE (base); + + g_return_if_fail (filter->priv->info != NULL); + + g_signal_handlers_disconnect_by_func (base->view, (gpointer) view_activate, filter); +} + + +static void +view_activate (MooLineView *view, + int line, + MooOutputFilterSimple *filter) +{ + MooFileLineData *data; + + g_return_if_fail (MOO_IS_LINE_VIEW (view)); + g_return_if_fail (MOO_IS_OUTPUT_FILTER_SIMPLE (filter)); + + data = moo_line_view_get_boxed (view, line, MOO_TYPE_FILE_LINE_DATA); + + if (!data) + return; + + g_print ("clicked: %s:%d:%d\n", data->file, data->line, data->character); + + moo_file_line_data_free (data); +} + + +static MooFileLineData * +parse_file_line (const char *file, + const char *line, + const char *character) +{ + MooFileLineData *data; + + if (!file || !file[0]) + return NULL; + + data = moo_file_line_data_new (file, -1, -1); + data->line = _moo_convert_string_to_int (line, -1); + data->character = _moo_convert_string_to_int (character, -1); + + return data; +} + +static void +process_result (const char *text, + EggRegex *regex, + MooOutputTextType type, + MooLineView *view) +{ + char *file, *line, *character; + MooFileLineData *data; + int line_no; + GtkTextTag *tag; + + file = egg_regex_fetch_named (regex, "file", text); + line = egg_regex_fetch_named (regex, "line", text); + character = egg_regex_fetch_named (regex, "character", text); + + tag = moo_line_view_lookup_tag (view, type == MOO_OUTPUT_STDOUT ? + MOO_CMD_VIEW_STDOUT : MOO_CMD_VIEW_STDERR); + line_no = moo_line_view_write_line (view, text, -1, tag); + + data = parse_file_line (file, line, character); + + if (data) + { + moo_line_view_set_boxed (view, line_no, MOO_TYPE_FILE_LINE_DATA, data); + moo_file_line_data_free (data); + } + + g_free (file); + g_free (line); + g_free (character); +} + + +static gboolean +process_line (MooOutputFilterSimple *filter, + const char *text, + FilterInfo *info, + MooOutputTextType type) +{ + guint i; + + for (i = 0; i < info->n_patterns; ++i) + { + RegexInfo *regex = &info->patterns[i]; + + if (regex->type != type && regex->type != MOO_OUTPUT_ALL) + continue; + + if (!egg_regex_match (regex->re, text, 0)) + continue; + + process_result (text, regex->re, type, MOO_OUTPUT_FILTER(filter)->view); + + return TRUE; + } + + return FALSE; +} + + +static gboolean +moo_output_filter_simple_stdout_line (MooOutputFilter *base, + const char *line) +{ + MooOutputFilterSimple *filter = MOO_OUTPUT_FILTER_SIMPLE (base); + g_return_val_if_fail (filter->priv->info != NULL, FALSE); + return process_line (filter, line, filter->priv->info, MOO_OUTPUT_STDOUT); +} + + +static gboolean +moo_output_filter_simple_stderr_line (MooOutputFilter *base, + const char *line) +{ + MooOutputFilterSimple *filter = MOO_OUTPUT_FILTER_SIMPLE (base); + g_return_val_if_fail (filter->priv->info != NULL, FALSE); + return process_line (filter, line, filter->priv->info, MOO_OUTPUT_STDERR); +} + + +static void +_moo_output_filter_simple_class_init (MooOutputFilterSimpleClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MooOutputFilterClass *filter_class = MOO_OUTPUT_FILTER_CLASS (klass); + + object_class->dispose = moo_output_filter_simple_dispose; + + filter_class->attach = moo_output_filter_simple_attach; + filter_class->detach = moo_output_filter_simple_detach; + filter_class->stdout_line = moo_output_filter_simple_stdout_line; + filter_class->stderr_line = moo_output_filter_simple_stderr_line; + + g_type_class_add_private (klass, sizeof (MooOutputFilterSimplePrivate)); +} + + +static void +_moo_output_filter_simple_init (MooOutputFilterSimple *filter) +{ + filter->priv = G_TYPE_INSTANCE_GET_PRIVATE (filter, MOO_TYPE_OUTPUT_FILTER_SIMPLE, MooOutputFilterSimplePrivate); +} + + +static void +filter_store_init (void) +{ + if (!filter_store) + { + filter_store = g_new0 (FilterStore, 1); + filter_store->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, + (GDestroyNotify) filter_info_unref); + } +} + + +static FilterInfo * +filter_info_ref (FilterInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + info->ref_count++; + return info; +} + + +static void +filter_info_unref (FilterInfo *info) +{ + guint i; + + if (!info || --info->ref_count) + return; + + for (i = 0; i < info->n_patterns; ++i) + if (info->patterns[i].re) + egg_regex_unref (info->patterns[i].re); + + g_free (info->patterns); + g_free (info); +} + + +static FilterInfo * +filter_info_new (guint n_patterns) +{ + FilterInfo *info = g_new0 (FilterInfo, 1); + + info->ref_count = 1; + info->n_patterns = n_patterns; + info->patterns = g_new0 (RegexInfo, n_patterns); + + return info; +} + + +static void +prepend_id (const char *id, + G_GNUC_UNUSED gpointer info, + GSList **list) +{ + *list = g_slist_prepend (*list, g_strdup (id)); +} + +static void +filter_store_clear (void) +{ + GSList *list = NULL; + + if (!filter_store) + return; + + g_hash_table_foreach (filter_store->hash, (GHFunc) prepend_id, &list); + + while (list) + { + moo_command_filter_unregister (list->data); + g_free (list->data); + list = g_slist_delete_link (list, list); + } +} + + +static MooOutputFilter * +factory_func (const char *id, + G_GNUC_UNUSED gpointer data) +{ + MooOutputFilterSimple *filter; + FilterInfo *info; + + g_return_val_if_fail (id != NULL, NULL); + g_return_val_if_fail (filter_store != NULL, NULL); + + info = g_hash_table_lookup (filter_store->hash, id); + g_return_val_if_fail (info != NULL, NULL); + + filter = g_object_new (MOO_TYPE_OUTPUT_FILTER_SIMPLE, NULL); + filter->priv->info = filter_info_ref (info); + + return MOO_OUTPUT_FILTER (filter); +} + + +static void +filter_removed (char *id) +{ + g_return_if_fail (id != NULL); + g_return_if_fail (filter_store != NULL); + g_hash_table_remove (filter_store->hash, id); + g_free (id); +} + +static void +filter_store_add (const char *id, + const char *name, + FilterInfo *info) +{ + g_return_if_fail (id != NULL); + g_return_if_fail (name != NULL); + g_return_if_fail (info != NULL); + + moo_command_filter_register (id, name, factory_func, g_strdup (id), + (GDestroyNotify) filter_removed); + + filter_store_init (); + g_hash_table_insert (filter_store->hash, g_strdup (id), info); +} + + +static void +filter_store_set_filters (GSList *list) +{ + filter_store_clear (); + + while (list) + { + guint i; + MooOutputFilterInfo *info; + FilterInfo *filter_info; + + info = list->data; + list = list->next; + + filter_info = filter_info_new (info->n_patterns); + + for (i = 0; i < info->n_patterns; ++i) + { + MooOutputPatternInfo *pattern_info; + EggRegex *regex; + GError *error = NULL; + + pattern_info = info->patterns[i]; + regex = egg_regex_new (pattern_info->pattern, 0, 0, &error); + + if (!regex) + { + g_warning ("could not create regex: %s", error->message); + g_error_free (error); + filter_info_unref (filter_info); + filter_info = NULL; + break; + } + + egg_regex_optimize (regex, NULL); + + filter_info->patterns[i].re = regex; + filter_info->patterns[i].type = pattern_info->type; + } + + if (filter_info) + filter_store_add (info->id, info->name, filter_info); + } +} + + +MooOutputFilterInfo * +_moo_output_filter_info_new (void) +{ + MooOutputFilterInfo *info = g_new0 (MooOutputFilterInfo, 1); + info->ref_count = 1; + return info; +} + + +MooOutputFilterInfo * +_moo_output_filter_info_ref (MooOutputFilterInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + info->ref_count++; + return info; +} + + +static void +moo_output_pattern_info_free (MooOutputPatternInfo *p) +{ + if (p) + { + g_free (p->pattern); + g_free (p); + } +} + +void +_moo_output_filter_info_unref (MooOutputFilterInfo *info) +{ + guint i; + + g_return_if_fail (info != NULL); + + if (--info->ref_count) + return; + + for (i = 0; i < info->n_patterns; ++i) + moo_output_pattern_info_free (info->patterns[i]); + + g_free (info->patterns); + g_free (info->id); + g_free (info->name); + g_free (info); +} + + +/****************************************************************************/ +/* Loading and saving + */ + +#define CHAR_IS_SPACE(c) ((c) == ' ' || (c) == '\t') +#define CHAR_IS_SEPARATOR(c) ((c) == ',' || (c) == ';') + +static gboolean +parse_patterns (MooOutputFilterInfo *info, + const char *string, + const char *file) +{ + GSList *patterns = NULL; + guint i; + + if (!string || !string[0]) + { + g_warning ("patterns missing in filter %s in file %s", + info->id, file); + return FALSE; + } + + while (TRUE) + { + gunichar delim; + char *pattern_start, *pattern_end; + MooOutputPatternInfo *pattern_info; + + while (*string && CHAR_IS_SPACE (*string)) + string++; + + if (!*string) + break; + + delim = g_utf8_get_char (string); + pattern_start = g_utf8_next_char (string); + pattern_end = g_utf8_strchr (pattern_start, -1, delim); + + if (!pattern_end) + { + g_warning ("unterminated pattern: '%s'", string); + goto error; + } + + pattern_info = g_new0 (MooOutputPatternInfo, 1); + pattern_info->pattern = g_strndup (pattern_start, pattern_end - pattern_start); + pattern_info->type = MOO_OUTPUT_ALL; + patterns = g_slist_prepend (patterns, pattern_info); + + string = g_utf8_next_char (pattern_end); + + while (*string && CHAR_IS_SPACE (*string)) + string++; + + if (!strncmp (string, "stdout", strlen ("stdout"))) + { + pattern_info->type = MOO_OUTPUT_STDOUT; + string += strlen ("stdout"); + } + else if (!strncmp (string, "stderr", strlen ("stderr"))) + { + pattern_info->type = MOO_OUTPUT_STDERR; + string += strlen ("stderr"); + } + + if (CHAR_IS_SEPARATOR (*string)) + string++; + } + + info->n_patterns = g_slist_length (patterns); + info->patterns = g_new0 (MooOutputPatternInfo*, info->n_patterns); + + patterns = g_slist_reverse (patterns); + i = 0; + + while (patterns) + { + info->patterns[i++] = patterns->data; + patterns = g_slist_delete_link (patterns, patterns); + } + + return TRUE; + +error: + g_slist_foreach (patterns, (GFunc) g_free, NULL); + g_slist_free (patterns); + return FALSE; +} + + +static MooOutputFilterInfo * +parse_item (MooKeyFileItem *item, + const char *file) +{ + char *pattern_string; + MooOutputFilterInfo *info; + + if (strcmp (moo_key_file_item_name (item), ITEM_FILTER)) + { + g_warning ("invalid group %s in file %s", moo_key_file_item_name (item), file); + return NULL; + } + + info = _moo_output_filter_info_new (); + info->id = moo_key_file_item_steal (item, KEY_ID); + info->name = moo_key_file_item_steal (item, KEY_NAME); + info->enabled = moo_key_file_item_steal_bool (item, KEY_ENABLED, TRUE); + info->deleted = moo_key_file_item_steal_bool (item, KEY_DELETED, FALSE); + info->builtin = moo_key_file_item_steal_bool (item, KEY_BUILTIN, FALSE); + + if (!info->id) + { + g_warning ("filter id missing in file %s", file); + _moo_output_filter_info_unref (info); + return NULL; + } + + if (info->deleted || info->builtin) + return info; + + pattern_string = moo_key_file_item_steal (item, KEY_PATTERNS); + + if (!parse_patterns (info, pattern_string, file)) + { + _moo_output_filter_info_unref (info); + return NULL; + } + + g_free (pattern_string); + + return info; +} + +static GSList * +parse_key_file (MooKeyFile *key_file, + const char *filename) +{ + guint n_items, i; + GSList *list = NULL; + + n_items = moo_key_file_n_items (key_file); + + for (i = 0; i < n_items; ++i) + { + MooKeyFileItem *item = moo_key_file_nth_item (key_file, i); + MooOutputFilterInfo *info = parse_item (item, filename); + if (info) + list = g_slist_prepend (list, info); + } + + return g_slist_reverse (list); +} + +static GSList * +parse_file_simple (const char *filename) +{ + MooKeyFile *key_file; + GError *error = NULL; + GSList *list = NULL; + + g_return_val_if_fail (filename != NULL, NULL); + + key_file = moo_key_file_new_from_file (filename, &error); + + if (key_file) + { + list = parse_key_file (key_file, filename); + moo_key_file_unref (key_file); + } + else + { + g_warning ("could not load file '%s': %s", filename, error->message); + g_error_free (error); + } + + return list; +} + +static void +parse_file (const char *filename, + GSList **list, + GHashTable *ids) +{ + GSList *new_list; + + new_list = parse_file_simple (filename); + + if (!new_list) + return; + + while (new_list) + { + MooOutputFilterInfo *info, *old_info; + GSList *old_link; + + info = new_list->data; + g_return_if_fail (info->id != NULL); + + old_link = g_hash_table_lookup (ids, info->id); + old_info = old_link ? old_link->data : NULL; + + if (old_link) + { + *list = g_slist_delete_link (*list, old_link); + g_hash_table_remove (ids, info->id); + } + + if (info->deleted) + { + _moo_output_filter_info_unref (info); + info = NULL; + } + else if (info->builtin) + { + _moo_output_filter_info_unref (info); + info = old_info ? _moo_output_filter_info_ref (old_info) : NULL; + } + + if (info) + { + *list = g_slist_prepend (*list, info); + g_hash_table_insert (ids, g_strdup (info->id), *list); + } + + if (old_info) + _moo_output_filter_info_unref (old_info); + + new_list = g_slist_delete_link (new_list, new_list); + } +} + + +static void +find_filters_files (char ***sys_files_p, + char **user_file_p) +{ + int i; + char **files; + guint n_files; + GPtrArray *sys_files = NULL; + + *sys_files_p = NULL; + *user_file_p = NULL; + + files = moo_get_data_files (FILTERS_FILE, MOO_DATA_SHARE, &n_files); + + if (!n_files) + return; + + if (g_file_test (files[n_files - 1], G_FILE_TEST_EXISTS)) + *user_file_p = g_strdup (files[n_files - 1]); + + for (i = 0; i < (int) n_files - 1; ++i) + { + if (g_file_test (files[i], G_FILE_TEST_EXISTS)) + { + if (!sys_files) + sys_files = g_ptr_array_new (); + g_ptr_array_add (sys_files, g_strdup (files[i])); + } + } + + if (sys_files) + { + g_ptr_array_add (sys_files, NULL); + *sys_files_p = (char**) g_ptr_array_free (sys_files, FALSE); + } + + g_strfreev (files); +} + +static GSList * +parse_filters (void) +{ + char **sys_files, *user_file; + GSList *list = NULL; + GHashTable *ids = NULL; + char **p; + + find_filters_files (&sys_files, &user_file); + ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + for (p = sys_files; p && *p; ++p) + parse_file (*p, &list, ids); + + if (user_file) + parse_file (user_file, &list, ids); + + g_hash_table_destroy (ids); + g_strfreev (sys_files); + g_free (user_file); + + return g_slist_reverse (list); } void -_moo_command_filter_simple_init (void) +_moo_command_filter_simple_load (void) { - static gboolean been_here = FALSE; + GSList *list = parse_filters (); - if (been_here) - return; + filter_store_set_filters (list); - been_here = TRUE; + g_slist_foreach (list, (GFunc) _moo_output_filter_info_unref, NULL); + g_slist_free (list); } diff --git a/moo/mooedit/moooutputfiltersimple.h b/moo/mooedit/moooutputfiltersimple.h index f93b5fd4..0bbbcb6e 100644 --- a/moo/mooedit/moooutputfiltersimple.h +++ b/moo/mooedit/moooutputfiltersimple.h @@ -19,7 +19,7 @@ G_BEGIN_DECLS -#define MOO_TYPE_OUTPUT_FILTER_SIMPLE (moo_output_filter_simple_get_type ()) +#define MOO_TYPE_OUTPUT_FILTER_SIMPLE (_moo_output_filter_simple_get_type ()) #define MOO_OUTPUT_FILTER_SIMPLE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), MOO_TYPE_OUTPUT_FILTER_SIMPLE, MooOutputFilterSimple)) #define MOO_OUTPUT_FILTER_SIMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOO_TYPE_OUTPUT_FILTER_SIMPLE, MooOutputFilterSimpleClass)) #define MOO_IS_OUTPUT_FILTER_SIMPLE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), MOO_TYPE_OUTPUT_FILTER_SIMPLE)) @@ -29,6 +29,14 @@ G_BEGIN_DECLS typedef struct _MooOutputFilterSimple MooOutputFilterSimple; typedef struct _MooOutputFilterSimplePrivate MooOutputFilterSimplePrivate; typedef struct _MooOutputFilterSimpleClass MooOutputFilterSimpleClass; +typedef struct _MooOutputFilterInfo MooOutputFilterInfo; +typedef struct _MooOutputPatternInfo MooOutputPatternInfo; + +typedef enum { + MOO_OUTPUT_STDOUT, + MOO_OUTPUT_STDERR, + MOO_OUTPUT_ALL +} MooOutputTextType; struct _MooOutputFilterSimple { MooOutputFilter base; @@ -39,10 +47,30 @@ struct _MooOutputFilterSimpleClass { MooOutputFilterClass base_class; }; +struct _MooOutputPatternInfo { + char *pattern; + MooOutputTextType type; +}; -GType moo_output_filter_simple_get_type (void) G_GNUC_CONST; +struct _MooOutputFilterInfo { + char *id; + char *name; + MooOutputPatternInfo **patterns; + guint n_patterns; + guint ref_count; + guint deleted : 1; + guint builtin : 1; + guint enabled : 1; +}; -void _moo_command_filter_simple_init (void); + +GType _moo_output_filter_simple_get_type (void) G_GNUC_CONST; + +void _moo_command_filter_simple_load (void); + +MooOutputFilterInfo *_moo_output_filter_info_new (void); +MooOutputFilterInfo *_moo_output_filter_info_ref (MooOutputFilterInfo *info); +void _moo_output_filter_info_unref (MooOutputFilterInfo *info); G_END_DECLS diff --git a/moo/moopython/pygtk/moocommand.defs b/moo/moopython/pygtk/moocommand.defs index 6bc1725a..db7117c1 100644 --- a/moo/moopython/pygtk/moocommand.defs +++ b/moo/moopython/pygtk/moocommand.defs @@ -368,3 +368,46 @@ '("int" "status") ) ) + + +(define-function command_filter_register + (c-name "moo_command_filter_register") + (return-type "none") + (parameters + '("const-char*" "id") + '("const-char*" "name") + '("MooCommandFilterFactory" "factory_func") + '("gpointer" "data") + '("GDestroyNotify" "data_notify") + ) +) + +(define-function command_filter_unregister + (c-name "moo_command_filter_unregister") + (return-type "none") + (parameters + '("const-char*" "id") + ) +) + +(define-function command_filter_lookup + (c-name "moo_command_filter_lookup") + (return-type "const-char*") + (parameters + '("const-char*" "id") + ) +) + +(define-function command_filter_list + (c-name "moo_command_filter_list") + (return-type "string-slist") + (caller-owns-return "t") +) + +(define-function command_filter_create + (c-name "moo_command_filter_create") + (return-type "MooOutputFilter*") + (parameters + '("const-char*" "id") + ) +)