[gnome-todo] todo-txt: Rework parser
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-todo] todo-txt: Rework parser
- Date: Mon, 5 Feb 2018 01:04:39 +0000 (UTC)
commit 40c825e09d0784d0ce5f9074814123a906ec9936
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date: Sun Feb 4 22:37:56 2018 -0200
todo-txt: Rework parser
This commit introduces an improved and more robust
Todo.txt parser, but has one major drawback that we
have to figure out before the release: the Todo.txt
provider stopped supporting parent/child tasks.
plugins/todo-txt/gtd-plugin-todo-txt.c | 217 ++++++------
plugins/todo-txt/gtd-provider-todo-txt.c | 543 ++++++++++++++++---------------
plugins/todo-txt/gtd-todo-txt-parser.c | 537 +++++++++++++-----------------
plugins/todo-txt/gtd-todo-txt-parser.h | 42 +--
4 files changed, 644 insertions(+), 695 deletions(-)
---
diff --git a/plugins/todo-txt/gtd-plugin-todo-txt.c b/plugins/todo-txt/gtd-plugin-todo-txt.c
index e0cc350..cf66be9 100644
--- a/plugins/todo-txt/gtd-plugin-todo-txt.c
+++ b/plugins/todo-txt/gtd-plugin-todo-txt.c
@@ -18,6 +18,7 @@
#define G_LOG_DOMAIN "GtdPluginTodoTxt"
+#include "gtd-debug.h"
#include "gtd-plugin-todo-txt.h"
#include "gtd-provider-todo-txt.h"
@@ -53,88 +54,29 @@ enum
static void gtd_activatable_iface_init (GtdActivatableInterface *iface);
-G_DEFINE_DYNAMIC_TYPE_EXTENDED (GtdPluginTodoTxt, gtd_plugin_todo_txt, PEAS_TYPE_EXTENSION_BASE,
- 0,
- G_IMPLEMENT_INTERFACE_DYNAMIC (GTD_TYPE_ACTIVATABLE,
- gtd_activatable_iface_init))
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (GtdPluginTodoTxt, gtd_plugin_todo_txt, PEAS_TYPE_EXTENSION_BASE, 0,
+ G_IMPLEMENT_INTERFACE_DYNAMIC (GTD_TYPE_ACTIVATABLE,
gtd_activatable_iface_init))
-/*
- * GtdActivatable interface implementation
- */
-static void
-gtd_plugin_todo_txt_activate (GtdActivatable *activatable)
-{
- ;
-}
-
-static void
-gtd_plugin_todo_txt_deactivate (GtdActivatable *activatable)
-{
- ;
-}
-
-static GList*
-gtd_plugin_todo_txt_get_header_widgets (GtdActivatable *activatable)
-{
- return NULL;
-}
-
-static GtkWidget*
-gtd_plugin_todo_txt_get_preferences_panel (GtdActivatable *activatable)
-{
- GtdPluginTodoTxt *plugin = GTD_PLUGIN_TODO_TXT (activatable);
-
- return plugin->preferences_box;
-
-}
-
-static GList*
-gtd_plugin_todo_txt_get_panels (GtdActivatable *activatable)
-{
- return NULL;
-}
-
-static GList*
-gtd_plugin_todo_txt_get_providers (GtdActivatable *activatable)
-{
- GtdPluginTodoTxt *plugin = GTD_PLUGIN_TODO_TXT (activatable);
- return plugin->providers;
-}
-
-static void
-gtd_activatable_iface_init (GtdActivatableInterface *iface)
-{
- iface->activate = gtd_plugin_todo_txt_activate;
- iface->deactivate = gtd_plugin_todo_txt_deactivate;
- iface->get_header_widgets = gtd_plugin_todo_txt_get_header_widgets;
- iface->get_preferences_panel = gtd_plugin_todo_txt_get_preferences_panel;
- iface->get_panels = gtd_plugin_todo_txt_get_panels;
- iface->get_providers = gtd_plugin_todo_txt_get_providers;
-}
/*
- * Init
+ * Auxiliary methods
*/
static gboolean
-gtd_plugin_todo_txt_set_default_source (GtdPluginTodoTxt *self)
+set_default_source (GtdPluginTodoTxt *self)
{
- g_autofree gchar *default_file;
- GError *error;
+ g_autofree gchar *default_file = NULL;
+ g_autoptr (GError) error = NULL;
- error = NULL;
- default_file = g_build_filename (g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS),
- "todo.txt",
- NULL);
+ GTD_ENTRY;
+
+ default_file = g_build_filename (g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS), "todo.txt", NULL);
self->source_file = g_file_new_for_path (default_file);
if (g_file_query_exists (self->source_file, NULL))
- return TRUE;
+ GTD_RETURN (TRUE);
- g_file_create (self->source_file,
- G_FILE_CREATE_NONE,
- NULL,
- &error);
+ g_file_create (self->source_file, G_FILE_CREATE_NONE, NULL, &error);
if (error)
{
@@ -143,39 +85,35 @@ gtd_plugin_todo_txt_set_default_source (GtdPluginTodoTxt *self)
error->message,
NULL,
NULL);
-
- g_clear_error (&error);
- return FALSE;
+ GTD_RETURN (FALSE);
}
- return TRUE;
+ GTD_RETURN (TRUE);
}
static gboolean
-gtd_plugin_todo_txt_set_source (GtdPluginTodoTxt *self)
+setup_source (GtdPluginTodoTxt *self)
{
- GError *error;
- gchar *source;
+ g_autoptr (GError) error = NULL;
+ g_autofree gchar *source = NULL;
+
+ GTD_ENTRY;
- error = NULL;
source = g_settings_get_string (self->settings, "file");
if (!source || source[0] == '\0')
{
- if (!gtd_plugin_todo_txt_set_default_source (self))
- return FALSE;
+ if (!set_default_source (self))
+ GTD_RETURN (FALSE);
}
else
{
- self->source_file = g_file_new_for_uri (source);
+ self->source_file = g_file_new_for_path (source);
}
if (!g_file_query_exists (self->source_file, NULL))
{
- g_file_create (self->source_file,
- G_FILE_CREATE_NONE,
- NULL,
- &error);
+ g_file_create (self->source_file, G_FILE_CREATE_NONE, NULL, &error);
if (error)
{
@@ -184,46 +122,52 @@ gtd_plugin_todo_txt_set_source (GtdPluginTodoTxt *self)
error->message,
NULL,
NULL);
-
- g_clear_error (&error);
- return FALSE;
+ GTD_RETURN (FALSE);
}
}
- return TRUE;
+ GTD_RETURN (TRUE);
}
+
+/*
+ * Callbacks
+ */
+
static void
-gtd_plugin_todo_txt_source_changed_finished_cb (GtdPluginTodoTxt *self)
+on_source_changed_finished_cb (GtdPluginTodoTxt *self)
{
GtdProviderTodoTxt *provider;
gboolean set;
- set = gtd_plugin_todo_txt_set_source (self);
+ GTD_ENTRY;
+
+ set = setup_source (self);
if (!set)
- return;
+ GTD_RETURN ();
provider = gtd_provider_todo_txt_new (self->source_file);
self->providers = g_list_append (self->providers, provider);
g_signal_emit_by_name (self, "provider-added", provider);
+
+ GTD_EXIT;
}
static void
-gtd_plugin_todo_txt_source_changed_cb (GtkWidget *preference_panel,
- gpointer user_data)
+on_source_changed_cb (GtkWidget *preference_panel,
+ GtdPluginTodoTxt *self)
{
- GtdPluginTodoTxt *self;
GtdProviderTodoTxt *provider;
- self = GTD_PLUGIN_TODO_TXT (user_data);
+ GTD_ENTRY;
g_clear_object (&self->source_file);
g_settings_set_string (self->settings,
"file",
- gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (self->preferences)));
+ gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (self->preferences)));
if (self->providers)
{
@@ -235,9 +179,72 @@ gtd_plugin_todo_txt_source_changed_cb (GtkWidget *preference_panel,
g_signal_emit_by_name (self, "provider-removed", provider);
}
- gtd_plugin_todo_txt_source_changed_finished_cb (self);
+ on_source_changed_finished_cb (self);
+
+ GTD_EXIT;
+}
+
+
+/*
+ * GtdActivatable implementation
+ */
+
+static void
+gtd_plugin_todo_txt_activate (GtdActivatable *activatable)
+{
+ ;
}
+static void
+gtd_plugin_todo_txt_deactivate (GtdActivatable *activatable)
+{
+ ;
+}
+
+static GList*
+gtd_plugin_todo_txt_get_header_widgets (GtdActivatable *activatable)
+{
+ return NULL;
+}
+
+static GtkWidget*
+gtd_plugin_todo_txt_get_preferences_panel (GtdActivatable *activatable)
+{
+ GtdPluginTodoTxt *plugin = GTD_PLUGIN_TODO_TXT (activatable);
+
+ return plugin->preferences_box;
+
+}
+
+static GList*
+gtd_plugin_todo_txt_get_panels (GtdActivatable *activatable)
+{
+ return NULL;
+}
+
+static GList*
+gtd_plugin_todo_txt_get_providers (GtdActivatable *activatable)
+{
+ GtdPluginTodoTxt *plugin = GTD_PLUGIN_TODO_TXT (activatable);
+ return plugin->providers;
+}
+
+static void
+gtd_activatable_iface_init (GtdActivatableInterface *iface)
+{
+ iface->activate = gtd_plugin_todo_txt_activate;
+ iface->deactivate = gtd_plugin_todo_txt_deactivate;
+ iface->get_header_widgets = gtd_plugin_todo_txt_get_header_widgets;
+ iface->get_preferences_panel = gtd_plugin_todo_txt_get_preferences_panel;
+ iface->get_panels = gtd_plugin_todo_txt_get_panels;
+ iface->get_providers = gtd_plugin_todo_txt_get_providers;
+}
+
+
+/*
+ * GObject overrides
+ */
+
static void
gtd_plugin_todo_txt_finalize (GObject *object)
{
@@ -272,12 +279,10 @@ gtd_plugin_todo_txt_class_init (GtdPluginTodoTxtClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
- object_class->finalize = gtd_plugin_todo_txt_finalize;
+ object_class->finalize = gtd_plugin_todo_txt_finalize;
object_class->get_property = gtd_plugin_todo_txt_get_property;
- g_object_class_override_property (object_class,
- PROP_PREFERENCES_PANEL,
- "preferences-panel");
+ g_object_class_override_property (object_class, PROP_PREFERENCES_PANEL, "preferences-panel");
}
static void
@@ -288,7 +293,7 @@ gtd_plugin_todo_txt_init (GtdPluginTodoTxt *self)
gboolean set;
self->settings = g_settings_new ("org.gnome.todo.plugins.todo-txt");
- set = gtd_plugin_todo_txt_set_source (self);
+ set = setup_source (self);
self->providers = NULL;
if (set)
@@ -317,13 +322,9 @@ gtd_plugin_todo_txt_init (GtdPluginTodoTxt *self)
gtk_widget_show_all (self->preferences_box);
- g_signal_connect (self->preferences,
- "file-set",
- G_CALLBACK (gtd_plugin_todo_txt_source_changed_cb),
- self);
+ g_signal_connect (self->preferences, "file-set", G_CALLBACK (on_source_changed_cb), self);
}
-/* Empty class_finalize method */
static void
gtd_plugin_todo_txt_class_finalize (GtdPluginTodoTxtClass *klass)
{
diff --git a/plugins/todo-txt/gtd-provider-todo-txt.c b/plugins/todo-txt/gtd-provider-todo-txt.c
index 4f0808c..54bebdb 100644
--- a/plugins/todo-txt/gtd-provider-todo-txt.c
+++ b/plugins/todo-txt/gtd-provider-todo-txt.c
@@ -1,6 +1,7 @@
/* gtd-provider-todo-txt.c
*
- * Copyright (C) 2016 Rohit Kaushik <kaushikrohit325 gmail com>
+ * Copyright © 2016 Rohit Kaushik <kaushikrohit325 gmail com>
+ * 2018 Georges Basile Stavracas Neto <georges stavracas gmail com>
*
* 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
@@ -18,6 +19,7 @@
#define G_LOG_DOMAIN "GtdProviderTodoTxt"
+#include "gtd-debug.h"
#include "gtd-provider-todo-txt.h"
#include "gtd-plugin-todo-txt.h"
#include "gtd-todo-txt-parser.h"
@@ -29,7 +31,7 @@
struct _GtdProviderTodoTxt
{
- GtdObject parent;
+ GtdObject parent;
GIcon *icon;
@@ -41,16 +43,24 @@ struct _GtdProviderTodoTxt
GList *task_lists;
GPtrArray *cache;
+
+ guint64 task_counter;
gboolean should_reload;
};
+static void on_file_monitor_changed_cb (GFileMonitor *monitor,
+ GFile *first,
+ GFile *second,
+ GFileMonitorEvent event,
+ GtdProviderTodoTxt *self);
+
static void gtd_provider_iface_init (GtdProviderInterface *iface);
G_DEFINE_TYPE_WITH_CODE (GtdProviderTodoTxt, gtd_provider_todo_txt, GTD_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (GTD_TYPE_PROVIDER,
- gtd_provider_iface_init))
+ G_IMPLEMENT_INTERFACE (GTD_TYPE_PROVIDER, gtd_provider_iface_init))
-enum {
+enum
+{
PROP_0,
PROP_DEFAULT_TASKLIST,
PROP_DESCRIPTION,
@@ -62,266 +72,277 @@ enum {
LAST_PROP
};
+
/*
- * GtdProviderInterface implementation
+ * Auxiliary methods
*/
-static const gchar*
-gtd_provider_todo_txt_get_id (GtdProvider *provider)
-{
- return "todo-txt";
-}
-
-static const gchar*
-gtd_provider_todo_txt_get_name (GtdProvider *provider)
-{
- return _("Todo.txt");
-}
-
-static const gchar*
-gtd_provider_todo_txt_get_description (GtdProvider *provider)
-{
- return _("On the Todo.txt file");
-}
-
-static gboolean
-gtd_provider_todo_txt_get_enabled (GtdProvider *provider)
-{
- return TRUE;
-}
-
-static GIcon*
-gtd_provider_todo_txt_get_icon (GtdProvider *provider)
+static void
+print_task (GString *output,
+ GtdTask *task)
{
- GtdProviderTodoTxt *self;
+ GtdTaskList *list;
+ GDateTime *dt;
+ GtdTask *parent;
+ const gchar *list_name;
+ const gchar *title;
+ gint priority;
+ gboolean is_complete;
+
+ is_complete = gtd_task_get_complete (task);
+ title = gtd_task_get_title (task);
+ priority = gtd_task_get_priority (task);
+ dt = gtd_task_get_due_date (task);
+ list = gtd_task_get_list (task);
+ parent = gtd_task_get_parent (task);
+
+ list_name = gtd_task_list_get_name (list);
+
+ if (is_complete)
+ g_string_append (output, "x ");
+
+ if (priority)
+ {
+ if (priority == 1)
+ g_string_append (output, "(C) ");
+ else if (priority == 2)
+ g_string_append (output, "(B) ");
+ else if (priority == 3)
+ g_string_append (output, "(A) ");
+ }
- self = GTD_PROVIDER_TODO_TXT (provider);
+ g_string_append_printf (output, "%s @%s", title, list_name);
- return self->icon;
-}
+ if (dt)
+ {
+ g_autofree gchar *formatted_time = g_date_time_format (dt, "%F");
+ g_string_append_printf (output, " due:%s", formatted_time);
+ }
-static void
-emit_generic_error (GError *error)
-{
- g_warning ("%s: %s: %s",
- G_STRFUNC,
- "Error while opening Todo.txt",
- error->message);
-
- gtd_manager_emit_error_message (gtd_manager_get_default (),
- _("Error while opening Todo.txt"),
- error->message,
- NULL,
- NULL);
+ g_string_append (output, "\n");
}
static void
update_source (GtdProviderTodoTxt *self)
{
-
- GFileOutputStream *write_stream;
- GDataOutputStream *writer;
+ g_autofree gchar *output_path = NULL;
+ g_autoptr (GString) contents = NULL;
+ g_autoptr (GError) error = NULL;
GtdTaskList *list;
- GError *error;
- GList *tasks, *l;
guint i;
- error = NULL;
- tasks = NULL;
- l = NULL;
- self->should_reload = FALSE;
-
- write_stream = g_file_replace (self->source_file,
- NULL,
- TRUE,
- G_FILE_CREATE_NONE,
- NULL,
- &error);
- if (error)
- {
- emit_generic_error (error);
- g_error_free (error);
- return;
- }
+ GTD_ENTRY;
- writer = g_data_output_stream_new (G_OUTPUT_STREAM (write_stream));
+ contents = g_string_new ("");
for (i = 0; i < self->cache->len; i++)
{
- gchar *list_line;
+ g_autofree gchar *color_str = NULL;
+ g_autoptr (GdkRGBA) color = NULL;
+ GList *tasks, *l;
+
list = g_ptr_array_index (self->cache, i);
tasks = gtd_task_list_get_tasks (list);
- tasks = g_list_sort (tasks, (GCompareFunc) gtd_task_compare);
- list_line = gtd_todo_txt_parser_serialize_list (list);
+ /* Print the list as the first line */
+ color = gtd_task_list_get_color (list);
+ color_str = gdk_rgba_to_string (color);
- g_data_output_stream_put_string (writer,
- list_line,
- NULL,
- NULL);
+ g_string_append_printf (contents, "@%s color:%s\n",
+ gtd_task_list_get_name (list),
+ color_str);
+ /* And now each task */
for (l = tasks; l != NULL; l = l->next)
- {
- gchar *task_line;
+ print_task (contents, l->data);
+ }
+
+ output_path = g_file_get_path (self->source_file);
+ g_file_set_contents (output_path, contents->str, contents->len, &error);
+
+ if (error)
+ {
+ g_warning ("Error saving Todo.txt file: %s", error->message);
+ return;
+ }
- task_line = gtd_todo_txt_parser_serialize_task (l->data);
+ self->should_reload = FALSE;
- g_data_output_stream_put_string (writer,
- task_line,
- NULL,
- NULL);
+ GTD_EXIT;
+}
- g_free (task_line);
- }
+static void
+add_task_list (GtdProviderTodoTxt *self,
+ GtdTaskList *list)
+{
+ if (g_hash_table_contains (self->lists, gtd_task_list_get_name (list)))
+ return;
- g_free (list_line);
- }
+ g_ptr_array_add (self->cache, list);
+ g_hash_table_insert (self->lists, g_strdup (gtd_task_list_get_name (list)), list);
- g_output_stream_close (G_OUTPUT_STREAM (writer), NULL, NULL);
- g_output_stream_close (G_OUTPUT_STREAM (write_stream), NULL, NULL);
+ self->task_lists = g_list_append (self->task_lists, list);
}
-static GtdTaskList*
-create_list (GtdProviderTodoTxt *self,
- gchar *name)
+static void
+parse_task_list (GtdProviderTodoTxt *self,
+ const gchar *line)
{
- GtdTaskList *task_list;
+ g_autoptr (GtdTaskList) list = NULL;
- if (g_hash_table_contains (self->lists, name))
- return g_hash_table_lookup (self->lists, name);
+ list = gtd_todo_txt_parser_parse_task_list (GTD_PROVIDER (self), line);
- task_list = gtd_task_list_new (GTD_PROVIDER (self));
- gtd_task_list_set_is_removable (task_list, TRUE);
- g_ptr_array_add (self->cache, task_list);
- g_hash_table_insert (self->lists, g_strdup (name), task_list);
- gtd_task_list_set_name (task_list, name);
- self->task_lists = g_list_append (self->task_lists, task_list);
+ if (!list)
+ return;
- return task_list;
+ add_task_list (self, g_steal_pointer (&list));
}
static void
-gtd_provider_todo_txt_load_tasks (GtdProviderTodoTxt *self)
+parse_task (GtdProviderTodoTxt *self,
+ const gchar *line)
{
- GFileInputStream *readstream;
- GDataInputStream *reader;
+ g_autofree gchar *list_name = NULL;
GtdTaskList *list;
- GtdTask *parent_task;
GtdTask *task;
- GError *error;
- GList *tokens;
- gboolean valid;
- gchar *line_read;
- gchar *list_name;
- gchar *root_task_name;
- g_return_if_fail (G_IS_FILE (self->source_file));
+ task = gtd_todo_txt_parser_parse_task (GTD_PROVIDER (self), line, &list_name);
- tokens = NULL;
- error = NULL;
- readstream = g_file_read (self->source_file, NULL, &error);
+ /*
+ * Create the list if it doesn't exist yet; this might happen with todo.txt files
+ * that are not saved from GNOME To Do.
+ */
+ if (!g_hash_table_contains (self->lists, list_name))
+ {
+ GTD_TRACE_MSG ("Creating new list with name '%s'", list_name);
+
+ list = g_object_new (GTD_TYPE_TASK_LIST,
+ "provider", self,
+ "name", list_name,
+ "is-removable", TRUE,
+ NULL);
+
+ add_task_list (self, list);
+ }
+ else
+ {
+ GTD_TRACE_MSG ("List with name '%s' already exists, reusing", list_name);
+
+ list = g_hash_table_lookup (self->lists, list_name);
+ }
+
+ gtd_task_set_list (task, list);
+ gtd_task_list_save_task (list, task);
+
+ g_hash_table_insert (self->tasks, (gpointer) gtd_object_get_uid (GTD_OBJECT (task)), task);
+ self->task_counter++;
+}
+
+static void
+reload_tasks (GtdProviderTodoTxt *self)
+{
+ g_autofree gchar *input_path = NULL;
+ g_autofree gchar *file_contents = NULL;
+ g_autoptr (GError) error = NULL;
+ g_auto (GStrv) lines = NULL;
+ guint i;
+
+ GTD_ENTRY;
+
+ input_path = g_file_get_path (self->source_file);
+
+ g_debug ("Reading the contents of %s", input_path);
+
+ g_file_get_contents (input_path, &file_contents, NULL, &error);
if (error)
{
- emit_generic_error (error);
- g_error_free (error);
+ g_warning ("Error reading Todo.txt file: %s", error->message);
return;
}
+ self->task_counter = 0;
- reader = g_data_input_stream_new (G_INPUT_STREAM (readstream));
+ lines = g_strsplit (file_contents, "\n", -1);
- while (!error)
+ for (i = 0; lines && lines[i]; i++)
{
- line_read = g_data_input_stream_read_line (reader, NULL, NULL, &error);
+ GtdTodoTxtLineType line_type;
+ gchar *line;
+
+ line = lines[i];
+
+ /* Last element of the array is NULL */
+ if (!line || line[0] == '\0')
+ break;
+
+ g_strstrip (line);
+
+ GTD_TRACE_MSG ("Parsing line %d: %s", i, line);
+
+ line_type = gtd_todo_txt_parser_get_line_type (line, &error);
if (error)
{
- g_warning ("%s: %s: %s",
- G_STRFUNC,
- "Error while reading a line from Todo.txt",
- error->message);
-
- gtd_manager_emit_error_message (gtd_manager_get_default (),
- _("Error while reading a line from Todo.txt"),
- error->message,
- NULL,
- NULL);
- g_error_free (error);
-
+ g_warning ("Error parsing line %d: %s", i + 1, error->message);
+ g_clear_error (&error);
continue;
}
- if (!line_read)
- break;
-
- g_strstrip (line_read);
- tokens = gtd_todo_txt_parser_tokenize (line_read);
- valid = gtd_todo_txt_parser_validate_token_format (tokens);
-
- if (valid)
+ switch (line_type)
{
- if (g_list_length (tokens) == 1)
- {
- list_name = &((gchar*)tokens->data)[0];
- list_name++;
- create_list (self, list_name);
- continue;
- }
-
- task = gtd_provider_generate_task (GTD_PROVIDER (self));
- gtd_todo_txt_parser_parse_tokens (task, tokens);
-
- g_hash_table_insert (self->tasks, g_strdup (gtd_task_get_title (task)), task);
-
- list = create_list (self, g_object_get_data (G_OBJECT (task), "list_name"));
- gtd_task_set_list (task, list);
-
- if (g_object_get_data (G_OBJECT (task), "root_task_name"))
- {
- root_task_name = g_object_get_data (G_OBJECT (task), "root_task_name");
-
- if (g_hash_table_contains (self->tasks, root_task_name))
- {
- parent_task = g_hash_table_lookup (self->tasks, root_task_name);
- }
- else
- {
- parent_task = gtd_provider_generate_task (GTD_PROVIDER (self));
- gtd_task_set_list (parent_task, list);
- gtd_task_set_title (parent_task, g_object_get_data (G_OBJECT (task), "root_task_name"));
-
- g_hash_table_insert (self->tasks, root_task_name, parent_task);
- }
-
- gtd_task_add_subtask (parent_task, task);
- gtd_task_list_save_task (list, parent_task);
- }
-
- gtd_task_list_save_task (list, task);
+ case GTD_TODO_TXT_LINE_TYPE_TASKLIST:
+ parse_task_list (self, line);
+ break;
+
+ case GTD_TODO_TXT_LINE_TYPE_TASK:
+ parse_task (self, line);
+ break;
}
+ }
+
+ GTD_EXIT;
+}
+
+static void
+setup_file_monitor (GtdProviderTodoTxt *self)
+{
+ g_autoptr (GError) error = NULL;
- g_list_free_full (tokens, g_free);
- g_free (line_read);
+ self->monitor = g_file_monitor_file (self->source_file,
+ G_FILE_MONITOR_WATCH_MOVES,
+ NULL,
+ &error);
+
+ if (error)
+ {
+ gtd_manager_emit_error_message (gtd_manager_get_default (),
+ _("Error while opening the file monitor. Todo.txt will not be
monitored"),
+ error->message,
+ NULL,
+ NULL);
+ return;
}
- g_input_stream_close (G_INPUT_STREAM (reader), NULL, NULL);
- g_input_stream_close (G_INPUT_STREAM (readstream), NULL, NULL);
+ g_signal_connect (self->monitor, "changed", G_CALLBACK (on_file_monitor_changed_cb), self);
}
+
+/*
+ * Callbacks
+ */
+
static void
-gtd_provider_todo_txt_reload (GFileMonitor *monitor,
- GFile *first,
- GFile *second,
- GFileMonitorEvent event,
- GtdProviderTodoTxt *self)
+on_file_monitor_changed_cb (GFileMonitor *monitor,
+ GFile *first,
+ GFile *second,
+ GFileMonitorEvent event,
+ GtdProviderTodoTxt *self)
{
- GList *l;
+ GList *l = NULL;
guint i;
- l = NULL;
-
if (!self->should_reload)
{
self->should_reload = TRUE;
@@ -336,12 +357,13 @@ gtd_provider_todo_txt_reload (GFileMonitor *monitor,
g_signal_emit_by_name (self, "list-removed", l->data);
g_list_free (self->task_lists);
+
self->task_lists = NULL;
self->lists = g_hash_table_new ((GHashFunc) g_str_hash, (GEqualFunc) g_str_equal);
self->tasks = g_hash_table_new ((GHashFunc) g_str_hash, (GEqualFunc) g_str_equal);
self->cache = g_ptr_array_new ();
- gtd_provider_todo_txt_load_tasks (self);
+ reload_tasks (self);
for (i = 0; i < self->cache->len; i++)
{
@@ -351,28 +373,41 @@ gtd_provider_todo_txt_reload (GFileMonitor *monitor,
}
}
-static void
-gtd_provider_todo_txt_load_source_monitor (GtdProviderTodoTxt *self)
+
+/*
+ * GtdProviderInterface implementation
+ */
+
+static const gchar*
+gtd_provider_todo_txt_get_id (GtdProvider *provider)
{
- GError *error = NULL;
+ return "todo-txt";
+}
- self->monitor = g_file_monitor_file (self->source_file,
- G_FILE_MONITOR_WATCH_MOVES,
- NULL,
- &error);
+static const gchar*
+gtd_provider_todo_txt_get_name (GtdProvider *provider)
+{
+ return _("Todo.txt");
+}
- if (error)
- {
- gtd_manager_emit_error_message (gtd_manager_get_default (),
- _("Error while opening the file monitor. Todo.txt will not be
monitored"),
- error->message,
- NULL,
- NULL);
- g_clear_error (&error);
- return;
- }
+static const gchar*
+gtd_provider_todo_txt_get_description (GtdProvider *provider)
+{
+ return _("On the Todo.txt file");
+}
- g_signal_connect (self->monitor, "changed", G_CALLBACK (gtd_provider_todo_txt_reload), self);
+static gboolean
+gtd_provider_todo_txt_get_enabled (GtdProvider *provider)
+{
+ return TRUE;
+}
+
+static GIcon*
+gtd_provider_todo_txt_get_icon (GtdProvider *provider)
+{
+ GtdProviderTodoTxt *self = GTD_PROVIDER_TODO_TXT (provider);
+
+ return self->icon;
}
static void
@@ -381,14 +416,7 @@ gtd_provider_todo_txt_create_task (GtdProvider *provider,
GCancellable *cancellable,
GError **error)
{
- GtdProviderTodoTxt *self;
-
- self = GTD_PROVIDER_TODO_TXT (provider);
-
- g_return_if_fail (GTD_IS_TASK (task));
- g_return_if_fail (GTD_IS_TASK_LIST (gtd_task_get_list (task)));
-
- update_source (self);
+ update_source (GTD_PROVIDER_TODO_TXT (provider));
}
static void
@@ -397,15 +425,7 @@ gtd_provider_todo_txt_update_task (GtdProvider *provider,
GCancellable *cancellable,
GError **error)
{
- GtdProviderTodoTxt *self;
-
- self = GTD_PROVIDER_TODO_TXT (provider);
-
- g_return_if_fail (GTD_IS_TASK (task));
- g_return_if_fail (GTD_IS_TASK_LIST (gtd_task_get_list (task)));
- g_return_if_fail (G_IS_FILE (self->source_file));
-
- update_source (self);
+ update_source (GTD_PROVIDER_TODO_TXT (provider));
}
static void
@@ -414,15 +434,7 @@ gtd_provider_todo_txt_remove_task (GtdProvider *provider,
GCancellable *cancellable,
GError **error)
{
- GtdProviderTodoTxt *self;
-
- self = GTD_PROVIDER_TODO_TXT (provider);
-
- g_return_if_fail (GTD_IS_TASK (task));
- g_return_if_fail (GTD_IS_TASK_LIST (gtd_task_get_list (task)));
- g_return_if_fail (G_IS_FILE (self->source_file));
-
- update_source (self);
+ update_source (GTD_PROVIDER_TODO_TXT (provider));
}
static void
@@ -471,11 +483,7 @@ gtd_provider_todo_txt_remove_task_list (GtdProvider *provider,
GCancellable *cancellable,
GError **error)
{
- GtdProviderTodoTxt *self;
-
- self = GTD_PROVIDER_TODO_TXT (provider);
-
- g_return_if_fail (GTD_IS_TASK_LIST (list));
+ GtdProviderTodoTxt *self = GTD_PROVIDER_TODO_TXT (provider);
g_ptr_array_remove (self->cache, list);
self->task_lists = g_list_remove (self->task_lists, list);
@@ -488,9 +496,7 @@ gtd_provider_todo_txt_remove_task_list (GtdProvider *provider,
static GList*
gtd_provider_todo_txt_get_task_lists (GtdProvider *provider)
{
- GtdProviderTodoTxt *self;
-
- self = GTD_PROVIDER_TODO_TXT (provider);
+ GtdProviderTodoTxt *self = GTD_PROVIDER_TODO_TXT (provider);
return self->task_lists;
}
@@ -508,6 +514,17 @@ gtd_provider_todo_txt_set_default_task_list (GtdProvider *provider,
/* FIXME: implement me */
}
+static GtdTask*
+gtd_provider_todo_txt_generate_task (GtdProvider *provider)
+{
+ GtdProviderTodoTxt *self = (GtdProviderTodoTxt *) provider;
+ g_autofree gchar *uid = NULL;
+
+ uid = g_strdup_printf ("%ld", self->task_counter);
+
+ return g_object_new (GTD_TYPE_TASK, "uid", uid, NULL);
+}
+
static void
gtd_provider_iface_init (GtdProviderInterface *iface)
{
@@ -525,16 +542,13 @@ gtd_provider_iface_init (GtdProviderInterface *iface)
iface->get_task_lists = gtd_provider_todo_txt_get_task_lists;
iface->get_default_task_list = gtd_provider_todo_txt_get_default_task_list;
iface->set_default_task_list = gtd_provider_todo_txt_set_default_task_list;
+ iface->generate_task = gtd_provider_todo_txt_generate_task;
}
-GtdProviderTodoTxt*
-gtd_provider_todo_txt_new (GFile *source_file)
-{
- return g_object_new (GTD_TYPE_PROVIDER_TODO_TXT,
- "source", source_file,
- NULL);
-}
+/*
+ * GObject overrides
+ */
static void
gtd_provider_todo_txt_finalize (GObject *object)
@@ -601,8 +615,8 @@ gtd_provider_todo_txt_set_property (GObject *object,
{
case PROP_SOURCE:
self->source_file = g_value_dup_object (value);
- gtd_provider_todo_txt_load_source_monitor (self);
- gtd_provider_todo_txt_load_tasks (self);
+ setup_file_monitor (self);
+ reload_tasks (self);
break;
default:
@@ -624,7 +638,7 @@ gtd_provider_todo_txt_class_init (GtdProviderTodoTxtClass *klass)
g_param_spec_object ("source",
"Source file",
"The Todo.txt source file",
- G_TYPE_OBJECT,
+ G_TYPE_FILE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_override_property (object_class, PROP_DEFAULT_TASKLIST, "default-task-list");
@@ -640,11 +654,18 @@ gtd_provider_todo_txt_init (GtdProviderTodoTxt *self)
{
gtd_object_set_ready (GTD_OBJECT (self), TRUE);
- self->lists = g_hash_table_new ((GHashFunc) g_str_hash, (GEqualFunc) g_str_equal);
- self->tasks = g_hash_table_new ((GHashFunc) g_str_hash, (GEqualFunc) g_str_equal);
+ self->lists = g_hash_table_new (g_str_hash, g_str_equal);
+ self->tasks = g_hash_table_new (g_str_hash, g_str_equal);
self->cache = g_ptr_array_new ();
self->should_reload = TRUE;
-
- /* icon */
self->icon = G_ICON (g_themed_icon_new_with_default_fallbacks ("computer-symbolic"));
}
+
+GtdProviderTodoTxt*
+gtd_provider_todo_txt_new (GFile *source_file)
+{
+
+ return g_object_new (GTD_TYPE_PROVIDER_TODO_TXT,
+ "source", source_file,
+ NULL);
+}
diff --git a/plugins/todo-txt/gtd-todo-txt-parser.c b/plugins/todo-txt/gtd-todo-txt-parser.c
index 808d5a0..a43b919 100644
--- a/plugins/todo-txt/gtd-todo-txt-parser.c
+++ b/plugins/todo-txt/gtd-todo-txt-parser.c
@@ -18,31 +18,30 @@
#define G_LOG_DOMAIN "GtdTodoTxtParser"
+#include "gtd-debug.h"
#include "gtd-todo-txt-parser.h"
#include "gtd-provider-todo-txt.h"
#include <glib/gi18n.h>
-struct _GtdTodoTxtParser
-{
- GtdObject parent;
-};
-enum
+G_DEFINE_QUARK (GtdTodoTxtParserError, gtd_todo_txt_parser_error)
+
+
+typedef enum
{
- TASK_COMPLETE,
- TASK_PRIORITY,
- TASK_DATE,
- TASK_TITLE,
- TASK_LIST_NAME,
- ROOT_TASK_NAME,
- TASK_DUE_DATE
-};
-
-G_DEFINE_TYPE (GtdTodoTxtParser, gtd_todo_txt_parser, GTD_TYPE_OBJECT);
-
-gint
-gtd_todo_txt_parser_get_priority (gchar *token)
+ TOKEN_START,
+ TOKEN_COMPLETE,
+ TOKEN_PRIORITY,
+ TOKEN_DATE,
+ TOKEN_TITLE,
+ TOKEN_LIST_NAME,
+ TOKEN_LIST_COLOR,
+ TOKEN_DUE_DATE
+} Token;
+
+static gint
+parse_priority (const gchar *token)
{
switch (token[1])
{
@@ -62,8 +61,8 @@ gtd_todo_txt_parser_get_priority (gchar *token)
return 0;
}
-GDateTime*
-gtd_todo_txt_parser_get_date (gchar *token)
+static GDateTime*
+parse_date (const gchar *token)
{
GDateTime *dt;
GDate date;
@@ -87,8 +86,8 @@ gtd_todo_txt_parser_get_date (gchar *token)
return dt;
}
-gboolean
-gtd_todo_txt_parser_is_date (gchar *dt)
+static gboolean
+is_date (const gchar *dt)
{
GDate date;
@@ -98,393 +97,321 @@ gtd_todo_txt_parser_is_date (gchar *dt)
return g_date_valid (&date);
}
-gboolean
-gtd_todo_txt_parser_is_word (gchar *token)
-{
- guint pos;
- guint token_length;
-
- token_length = g_utf8_strlen (token, -1);
-
- for (pos = 0; pos < token_length; pos++)
- {
- if (!g_unichar_isalnum (token[pos]))
- return FALSE;
- }
-
- return TRUE;
-}
-
-gint
-gtd_todo_txt_parser_get_token_id (gchar *token,
- gint last_read)
+static Token
+parse_token_id (const gchar *token,
+ gint last_read)
{
gint token_length;
token_length = strlen (token);
if (!g_strcmp0 (token, "x"))
- return TASK_COMPLETE;
+ return TOKEN_COMPLETE;
if (token_length == 3 && token[0] == '(' && token[2] == ')')
- return TASK_PRIORITY;
+ return TOKEN_PRIORITY;
- if (!g_str_has_prefix (token , "due:") && gtd_todo_txt_parser_is_date (token))
- return TASK_DATE;
+ if (!g_str_has_prefix (token , "due:") && is_date (token))
+ return TOKEN_DATE;
- if (gtd_todo_txt_parser_is_word (token) &&
- (last_read == TASK_DATE ||
- last_read == TASK_PRIORITY ||
- last_read == TASK_COMPLETE||
- last_read == TASK_TITLE))
- {
- return TASK_TITLE;
- }
+ if (g_str_has_prefix (token , "color:"))
+ return TOKEN_LIST_COLOR;
if (token_length > 1 && token[0] == '@')
- return TASK_LIST_NAME;
+ return TOKEN_LIST_NAME;
+
+ if (g_str_has_prefix (token , "due:"))
+ return TOKEN_DUE_DATE;
- if (token_length > 1 && token[0] == '+')
- return ROOT_TASK_NAME;
+ if (last_read == TOKEN_START ||
+ last_read == TOKEN_DATE ||
+ last_read == TOKEN_PRIORITY ||
+ last_read == TOKEN_COMPLETE||
+ last_read == TOKEN_TITLE)
+ {
+ return TOKEN_TITLE;
+ }
+ else if (last_read == TOKEN_LIST_NAME)
+ {
+ return TOKEN_LIST_NAME;
+ }
- if (gtd_todo_txt_parser_is_word (token) && last_read == TASK_LIST_NAME)
- return TASK_LIST_NAME;
+ return -1;
+}
- if (gtd_todo_txt_parser_is_word (token) && last_read == ROOT_TASK_NAME)
- return ROOT_TASK_NAME;
+static GStrv
+tokenize_line (const gchar *line)
+{
+ GStrv tokens = NULL;
+ gsize i;
- if (g_str_has_prefix (token , "due:"))
- return TASK_DUE_DATE;
+ tokens = g_strsplit (line, " ", -1);
- return -1;
+ for (i = 0; tokens && tokens[i]; i++)
+ g_strstrip (tokens[i]);
+
+ return tokens;
}
-void
-gtd_todo_txt_parser_parse_tokens (GtdTask *task,
- GList *tokens)
+GtdTask*
+gtd_todo_txt_parser_parse_task (GtdProvider *provider,
+ const gchar *line,
+ gchar **out_list_name)
{
+ g_autoptr (GtdTask) task = NULL;
+ g_auto (GStrv) tokens = NULL;
GDateTime *dt;
GString *list_name;
GString *title;
- GString *root_task_name;
- GList *l;
+ GString *parent_task_name;
+ Token last_token;
+ Token token_id;
gboolean is_subtask;
- gint last_read_token;
- gint token_id;
+ guint i;
- l = NULL;
dt = NULL;
is_subtask = FALSE;
title = g_string_new (NULL);
list_name = g_string_new (NULL);
- root_task_name = g_string_new (NULL);
+ parent_task_name = g_string_new (NULL);
+ last_token = TOKEN_START;
- last_read_token = TASK_COMPLETE;
+ task = gtd_provider_generate_task (provider);
+ tokens = tokenize_line (line);
- for (l = tokens; l != NULL; l = l->next)
+ for (i = 0; tokens && tokens[i]; i++)
{
+ const gchar *token;
- gchar *str;
-
- g_strstrip (l->data);
- str = l->data;
- token_id = gtd_todo_txt_parser_get_token_id (l->data, last_read_token);
+ token = tokens[i];
+ token_id = parse_token_id (token, last_token);
switch (token_id)
{
- case TASK_COMPLETE:
- last_read_token = TASK_COMPLETE;
+ case TOKEN_COMPLETE:
gtd_task_set_complete (task, TRUE);
break;
- case TASK_PRIORITY:
- last_read_token = TASK_PRIORITY;
- gtd_task_set_priority (task, gtd_todo_txt_parser_get_priority (l->data));
+ case TOKEN_PRIORITY:
+ last_token = TOKEN_PRIORITY;
+ gtd_task_set_priority (task, parse_priority (token));
break;
- case TASK_DATE:
- last_read_token = TASK_DATE;
- dt = gtd_todo_txt_parser_get_date (l->data);
+ case TOKEN_DATE:
+ dt = parse_date (token);
break;
- case TASK_TITLE:
- last_read_token = TASK_TITLE;
- g_string_append (title, l->data);
+ case TOKEN_TITLE:
+ g_string_append (title, token);
g_string_append (title, " ");
break;
- case TASK_LIST_NAME:
- last_read_token = TASK_LIST_NAME;
- g_string_append (list_name, l->data);
+ case TOKEN_LIST_NAME:
+ g_string_append (list_name, token);
g_string_append (list_name, " ");
break;
- case ROOT_TASK_NAME:
- last_read_token = ROOT_TASK_NAME;
- is_subtask = TRUE;
- g_string_append (root_task_name, l->data);
- g_string_append (root_task_name, " ");
- break;
-
- case TASK_DUE_DATE:
- last_read_token = TASK_DUE_DATE;
- dt = gtd_todo_txt_parser_get_date (&str[4]);
+ case TOKEN_DUE_DATE:
+ dt = parse_date (token + strlen ("due:"));
gtd_task_set_due_date (task, dt);
break;
+ case TOKEN_LIST_COLOR:
+ case TOKEN_START:
default:
- return;
+ break;
}
+
+ last_token = token_id;
}
- g_strstrip (title->str);
+ g_strstrip (parent_task_name->str);
g_strstrip (list_name->str);
- g_strstrip (root_task_name->str);
+ g_strstrip (title->str);
+
gtd_task_set_title (task, title->str);
- g_object_set_data_full (G_OBJECT (task), "list_name", g_strdup (list_name->str + 1), g_free);
- if (is_subtask)
- g_object_set_data_full (G_OBJECT (task), "root_task_name", g_strdup (root_task_name->str + 1), g_free);
+ if (out_list_name)
+ *out_list_name = g_strdup (list_name->str + 1);
- g_string_free (root_task_name, TRUE);
+ g_string_free (parent_task_name, TRUE);
g_string_free (list_name, TRUE);
g_string_free (title, TRUE);
+
+ return g_steal_pointer (&task);
}
-gboolean
-gtd_todo_txt_parser_validate_token_format (GList *tokens)
+/**
+ * gtd_todo_txt_parser_parse_task_list:
+ * provider: the @GtdProvider of the new tasklist
+ * @line: the tasklist line to be parsed
+ *
+ * Parses a @GtdTaskList from @line. If there is a 'color:' token,
+ * it is taken into account.
+ *
+ * Returns: (transfer full)(nullable): A @GtdTaskList
+ */
+GtdTaskList*
+gtd_todo_txt_parser_parse_task_list (GtdProvider *provider,
+ const gchar *line)
{
- GList *it = NULL;
- gint token_id;
- gint position = 0;
+ g_autofree gchar *color = NULL;
+ g_auto (GStrv) tokens = NULL;
+ GtdTaskList *new_list;
+ GString *list_name;
+ guint i;
- gboolean complete_tk = FALSE;
- gboolean priority_tk = FALSE;
- gboolean task_list_name_tk = FALSE;
+ tokens = tokenize_line (line);
+ list_name = g_string_new (NULL);
- gint last_read = TASK_COMPLETE;
+ GTD_TRACE_MSG ("Parsing tasklist from line '%s'", line);
- for (it = tokens; it != NULL; it = it->next)
+ for (i = 0; tokens && tokens[i]; i++)
{
- gchar *str;
+ const gchar *token = tokens[i];
- str = it->data;
- token_id = gtd_todo_txt_parser_get_token_id (it->data, last_read);
- position++;
+ if (!token)
+ break;
- switch (token_id)
- {
- case TASK_COMPLETE:
- last_read = TASK_COMPLETE;
-
- if (position != 1)
- return FALSE;
- else
- complete_tk = TRUE;
+ if (g_str_has_prefix (token, "color:"))
+ color = g_strdup (token + strlen ("color:"));
+ else
+ g_string_append_printf (list_name, "%s ", token[0] == '@' ? token + 1 : token);
+ }
- break;
+ if (list_name->len == 0)
+ {
+ g_string_free (list_name, TRUE);
+ return NULL;
+ }
- case TASK_PRIORITY:
- last_read = TASK_PRIORITY;
+ g_strstrip (list_name->str);
- if (position != (complete_tk + 1))
- return FALSE;
- else
- priority_tk = TRUE;
+ new_list = g_object_new (GTD_TYPE_TASK_LIST,
+ "provider", provider,
+ "name", list_name->str,
+ "is-removable", TRUE,
+ NULL);
- break;
+ if (color)
+ {
+ GdkRGBA rgba;
- case TASK_DATE:
- last_read = TASK_DATE;
+ gdk_rgba_parse (&rgba, color);
- if (position != (complete_tk + priority_tk + 1))
- return FALSE;
+ gtd_task_list_set_color (new_list, &rgba);
+ }
- if (!gtd_todo_txt_parser_is_date (it->data))
- {
- gtd_manager_emit_error_message (gtd_manager_get_default (),
- _("Incorrect date"),
- _("Please make sure the date in Todo.txt is valid."),
- NULL,
- NULL);
- return FALSE;
- }
+ g_string_free (list_name, TRUE);
- break;
+ return new_list;
+}
- case TASK_TITLE:
- last_read = TASK_TITLE;
- break;
+/**
+ * gtd_todo_txt_parser_get_line_type:
+ * @line: the line to parse
+ * @error: (nullable): return location for a #GError
+ *
+ * Validates the given line and returns the line type.
+ *
+ * Returns: the line type
+ */
+GtdTodoTxtLineType
+gtd_todo_txt_parser_get_line_type (const gchar *line,
+ GError **error)
+{
+ GtdTodoTxtLineType line_type;
+ g_auto (GStrv) tokens;
+ gboolean task_list_name_tk;
+ Token last_read;
+ Token token_id;
+ gint i;
- case TASK_LIST_NAME:
- task_list_name_tk = TRUE;
- last_read = TASK_LIST_NAME;
- break;
+ GTD_ENTRY;
- case ROOT_TASK_NAME:
- last_read = ROOT_TASK_NAME;
- break;
+ tokens = tokenize_line (line);
+ last_read = TOKEN_START;
+ line_type = GTD_TODO_TXT_LINE_TYPE_TASKLIST;
+ task_list_name_tk = FALSE;
- case TASK_DUE_DATE:
- last_read = TASK_DUE_DATE;
+ for (i = 0; tokens && tokens[i]; i++)
+ {
+ const gchar *token = tokens[i];
- if (!gtd_todo_txt_parser_is_date (&str[4]))
- return FALSE;
+ token_id = parse_token_id (token, last_read);
+ switch (token_id)
+ {
+ case TOKEN_COMPLETE:
+ if (last_read == TOKEN_START)
+ line_type = GTD_TODO_TXT_LINE_TYPE_TASK;
break;
- default:
- gtd_manager_emit_error_message (gtd_manager_get_default (),
- _("Unrecognized token in a Todo.txt line"),
- _("To Do cannot recognize some tags in your Todo.txt file. Some
tasks may not be loaded"),
- NULL,
- NULL);
- return FALSE;
+ case TOKEN_PRIORITY:
+ if (last_read <= TOKEN_COMPLETE)
+ line_type = GTD_TODO_TXT_LINE_TYPE_TASK;
break;
- }
- }
-
- if (!task_list_name_tk)
- {
- gtd_manager_emit_error_message (gtd_manager_get_default (),
- _("No task list found for some tasks"),
- _("Some of the tasks in your Todo.txt file do not have a task list. To
Do supports tasks with a task list. Please add a list to all your tasks"),
- NULL,
- NULL);
- return FALSE;
- }
- return TRUE;
-}
-
-GList*
-gtd_todo_txt_parser_tokenize (const gchar *line)
-{
- GList *tokens = NULL;
- g_autofree GStrv token = NULL;
- gsize i;
-
- token = g_strsplit (line, " ", -1);
+ case TOKEN_DATE:
+ if (last_read <= TOKEN_PRIORITY)
+ {
+ line_type = GTD_TODO_TXT_LINE_TYPE_TASK;
- for (i = 0; token[i]; i++)
- {
- g_strstrip (token[i]);
- tokens = g_list_prepend (tokens, g_strdup (token[i]));
- }
+ if (!is_date (token))
+ {
+ g_set_error (error,
+ GTD_TODO_TXT_PARSER_ERROR,
+ GTD_TODO_TXT_PARSER_INVALID_DUE_DATE,
+ "Invalid date found");
- tokens = g_list_reverse (tokens);
+ GTD_RETURN (-1);
+ }
+ }
+ break;
- return tokens;
-}
+ case TOKEN_TITLE:
+ line_type = GTD_TODO_TXT_LINE_TYPE_TASK;
+ break;
-gchar*
-gtd_todo_txt_parser_serialize_list (GtdTaskList *list)
-{
- GString *description;
- const gchar *list_name;
+ case TOKEN_LIST_COLOR:
+ break;
- description = g_string_new (NULL);
- list_name = gtd_task_list_get_name (list);
+ case TOKEN_LIST_NAME:
+ task_list_name_tk = TRUE;
+ break;
- g_string_append (description, "@");
- g_string_append (description, list_name);
+ case TOKEN_DUE_DATE:
+ line_type = GTD_TODO_TXT_LINE_TYPE_TASK;
- g_string_append (description, "\n");
+ if (!is_date (token + strlen ("due:")))
+ {
+ g_set_error (error,
+ GTD_TODO_TXT_PARSER_ERROR,
+ GTD_TODO_TXT_PARSER_INVALID_DUE_DATE,
+ "Invalid date found");
- return g_string_free (description, FALSE);
-}
+ GTD_RETURN (-1);
+ }
-gchar*
-gtd_todo_txt_parser_serialize_task (GtdTask *task)
-{
- GtdTaskList *list;
- GDateTime *dt;
- GtdTask *parent;
- GString *description;
- const gchar *list_name;
- const gchar *title;
- gint priority;
- gboolean is_complete;
-
- description = g_string_new (NULL);
-
- is_complete = gtd_task_get_complete (task);
- title = gtd_task_get_title (task);
- priority = gtd_task_get_priority (task);
- dt = gtd_task_get_due_date (task);
- list = gtd_task_get_list (task);
- parent = gtd_task_get_parent (task);
-
- list_name = gtd_task_list_get_name (list);
-
- if (is_complete)
- g_string_append (description, "x ");
-
- if (priority)
- {
- if (priority == 1)
- g_string_append (description, "(C) ");
- else if (priority == 2)
- g_string_append (description, "(B) ");
- else if (priority == 3)
- g_string_append (description, "(A) ");
- }
+ break;
- g_string_append (description, title);
- g_string_append (description, " @");
- g_string_append (description, list_name);
+ case TOKEN_START:
+ /* Nothing */
+ break;
+ }
- if (parent)
- {
- g_string_append (description, " +");
- g_string_append (description, gtd_task_get_title (parent));
+ last_read = token_id;
}
- if (dt)
+ if (!task_list_name_tk)
{
- g_autofree gchar *formatted_time;
-
- formatted_time = g_date_time_format (dt, "%F");
+ g_set_error (error,
+ GTD_TODO_TXT_PARSER_ERROR,
+ GTD_TODO_TXT_PARSER_INVALID_LINE,
+ "No task list found");
- g_string_append (description, " due:");
- g_string_append (description, formatted_time);
+ GTD_RETURN (-1);
}
- g_string_append (description, "\n");
-
- return g_string_free (description, FALSE);
-}
-
-static void
-gtd_todo_txt_parser_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-}
-
-static void
-gtd_todo_txt_parser_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-}
-
-static void
-gtd_todo_txt_parser_class_init (GtdTodoTxtParserClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- object_class->get_property = gtd_todo_txt_parser_get_property;
- object_class->set_property = gtd_todo_txt_parser_set_property;
-
-}
-
-static void
-gtd_todo_txt_parser_init (GtdTodoTxtParser *self)
-{
- ;
+ GTD_RETURN (line_type);
}
diff --git a/plugins/todo-txt/gtd-todo-txt-parser.h b/plugins/todo-txt/gtd-todo-txt-parser.h
index 12ccbc4..a6a6669 100644
--- a/plugins/todo-txt/gtd-todo-txt-parser.h
+++ b/plugins/todo-txt/gtd-todo-txt-parser.h
@@ -25,33 +25,33 @@
G_BEGIN_DECLS
-#define GTD_TYPE_TODO_TXT_PARSER (gtd_todo_txt_parser_get_type())
+typedef enum
+{
+ GTD_TODO_TXT_PARSER_INVALID_DUE_DATE,
+ GTD_TODO_TXT_PARSER_INVALID_LINE,
+ GTD_TODO_TXT_PARSER_UNSUPPORTED_TOKEN,
+ GTD_TODO_TXT_PARSER_WRONG_LINE_TYPE,
+} GtdTodoTxtParserError;
-typedef struct _TaskData TaskData;
+typedef enum
+{
+ GTD_TODO_TXT_LINE_TYPE_TASKLIST,
+ GTD_TODO_TXT_LINE_TYPE_TASK,
+} GtdTodoTxtLineType;
-G_DECLARE_FINAL_TYPE (GtdTodoTxtParser, gtd_todo_txt_parser, GTD, TODO_TXT_PARSER, GtdObject)
+#define GTD_TODO_TXT_PARSER_ERROR (gtd_todo_txt_parser_error_quark ())
-gint gtd_todo_txt_parser_get_priority (gchar *token);
+GQuark gtd_todo_txt_parser_error_quark (void);
-GDateTime* gtd_todo_txt_parser_get_date (gchar *token);
+GtdTodoTxtLineType gtd_todo_txt_parser_get_line_type (const gchar *line,
+ GError **error);
-gboolean gtd_todo_txt_parser_is_date (gchar *dt);
+GtdTaskList* gtd_todo_txt_parser_parse_task_list (GtdProvider *provider,
+ const gchar *line);
-gboolean gtd_todo_txt_parser_is_word (gchar *token);
-
-gint gtd_todo_txt_parser_get_token_id (gchar *token,
- gint last_read);
-
-void gtd_todo_txt_parser_parse_tokens (GtdTask *task,
- GList *tokens);
-
-gboolean gtd_todo_txt_parser_validate_token_format (GList *tokens);
-
-GList* gtd_todo_txt_parser_tokenize (const gchar *line);
-
-gchar* gtd_todo_txt_parser_serialize_list (GtdTaskList *list);
-
-gchar* gtd_todo_txt_parser_serialize_task (GtdTask *task);
+GtdTask* gtd_todo_txt_parser_parse_task (GtdProvider *provider,
+ const gchar *line,
+ gchar **out_list_name);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]