[anjuta] git: Process log data asynchronously



commit 0681b47521b7b1ff84f560201f521a52fd1c533a
Author: James Liggett <jrliggett cox net>
Date:   Tue Dec 14 01:28:41 2010 -0800

    git: Process log data asynchronously

 plugins/git/Makefile.am            |    5 +-
 plugins/git/git-log-command.c      |  199 +++++++---------------------
 plugins/git/git-log-command.h      |    1 +
 plugins/git/git-log-data-command.c |  250 ++++++++++++++++++++++++++++++++++++
 plugins/git/git-log-data-command.h |   61 +++++++++
 5 files changed, 365 insertions(+), 151 deletions(-)
---
diff --git a/plugins/git/Makefile.am b/plugins/git/Makefile.am
index d986b52..46aa34f 100644
--- a/plugins/git/Makefile.am
+++ b/plugins/git/Makefile.am
@@ -217,7 +217,10 @@ libanjuta_git_la_SOURCES = \
 	git-rebase-pane.c \
 	git-rebase-pane.h \
 	git-log-pane.c \
-	git-log-pane.h
+	git-log-pane.h \
+	git-log-data-command.c \
+	git-log-data-command.h
+	
 
 libanjuta_git_la_LDFLAGS = $(ANJUTA_PLUGIN_LDFLAGS)
 
diff --git a/plugins/git/git-log-command.c b/plugins/git/git-log-command.c
index a63526e..c937940 100644
--- a/plugins/git/git-log-command.c
+++ b/plugins/git/git-log-command.c
@@ -22,24 +22,13 @@
  * 	Boston, MA  02110-1301, USA.
  */
 
-#define COMMIT_REGEX "^commit ([[:xdigit:]]{40})"
-#define PARENT_REGEX "^parents (.*)"
-#define AUTHOR_REGEX "^author (.*)"
-#define TIME_REGEX "^time (\\d*)"
-#define SHORT_LOG_REGEX "^(?:short log) (.*)"
-
 #include "git-log-command.h"
 
 struct _GitLogCommandPriv
 {
-	GQueue *output_queue;
-	GHashTable *revisions;
-	GitRevision *current_revision;
-	GRegex *commit_regex;
-	GRegex *parent_regex;
-	GRegex *author_regex;
-	GRegex *time_regex;
-	GRegex *short_log_regex;
+	GitLogDataCommand *data_command;
+	guint return_code;
+
 	gchar *branch;
 	gchar *path;
 	
@@ -55,44 +44,43 @@ struct _GitLogCommandPriv
 G_DEFINE_TYPE (GitLogCommand, git_log_command, GIT_TYPE_COMMAND);
 
 static void
+on_data_command_data_arrived (AnjutaCommand *command, GitLogCommand *self)
+{
+	anjuta_command_notify_data_arrived (command);
+}
+
+static void
+on_data_command_finished (AnjutaCommand *command, guint return_code,
+                          GitLogCommand *self)
+{
+	ANJUTA_COMMAND_CLASS (git_log_command_parent_class)->notify_complete (ANJUTA_COMMAND (self),
+	                                                                      self->priv->return_code);
+}
+
+static void
 git_log_command_init (GitLogCommand *self)
 {
 	self->priv = g_new0 (GitLogCommandPriv, 1);
-	self->priv->output_queue = g_queue_new ();
-	self->priv->revisions = g_hash_table_new_full (g_str_hash, g_str_equal,
-												   g_free, g_object_unref);
-	self->priv->commit_regex = g_regex_new (COMMIT_REGEX, 0, 0, NULL);
-	self->priv->parent_regex = g_regex_new (PARENT_REGEX, 0, 0, NULL);
-	self->priv->author_regex = g_regex_new (AUTHOR_REGEX, 0, 0, NULL);
-	self->priv->time_regex = g_regex_new (TIME_REGEX, 0, 0, NULL);
-	self->priv->short_log_regex = g_regex_new (SHORT_LOG_REGEX, 0, 0, NULL);
+
+	self->priv->data_command = git_log_data_command_new ();
+
+	g_signal_connect (G_OBJECT (self->priv->data_command), "data-arrived",
+	                  G_CALLBACK (on_data_command_data_arrived),
+	                  self);
+
+	g_signal_connect (G_OBJECT (self->priv->data_command), "command-finished",
+	                  G_CALLBACK (on_data_command_finished),
+	                  self);
 }
 
 static void
 git_log_command_finalize (GObject *object)
 {
 	GitLogCommand *self;
-	GList *current_output;
 	
 	self = GIT_LOG_COMMAND (object);
-	current_output = self->priv->output_queue->head;
-	
-	while (current_output)
-	{
-		g_object_unref (current_output->data);
-		current_output = g_list_next (current_output);
-	}
-	
-	g_queue_free (self->priv->output_queue);
-	g_hash_table_destroy (self->priv->revisions);
-	g_regex_unref (self->priv->commit_regex);
-	g_regex_unref (self->priv->parent_regex);
-	g_regex_unref (self->priv->author_regex);
-	g_regex_unref (self->priv->time_regex);
-	g_regex_unref (self->priv->short_log_regex);
-	g_free (self->priv->branch);
-	g_free (self->priv->path);
 	
+	g_object_unref (self->priv->data_command);
 	g_free (self->priv->author);
 	g_free (self->priv->grep);
 	g_free (self->priv->since_date);
@@ -177,127 +165,37 @@ git_log_command_run (AnjutaCommand *command)
 		git_command_add_arg (GIT_COMMAND (command), "--");
 		git_command_add_arg (GIT_COMMAND (command), self->priv->path);
 	}
+
+	/* Start the data processing task */
+	anjuta_command_start (ANJUTA_COMMAND (self->priv->data_command));
 	
 	return 0;
 }
 
 static void
-git_log_command_handle_output (GitCommand *git_command, const gchar *output)
+git_log_command_notify_complete (AnjutaCommand *command, guint return_code)
 {
 	GitLogCommand *self;
-	GMatchInfo *commit_match_info;
-	GMatchInfo *parent_match_info;
-	GMatchInfo *author_match_info;
-	GMatchInfo *time_match_info;
-	GMatchInfo *short_log_match_info;
-	gchar *commit_sha;
-	gchar *parents;
-	gchar **parent_shas;
-	gint i;
-	GitRevision *parent_revision;
-	gchar *author;
-	gchar *time;
-	gchar *short_log;
-	
-	self = GIT_LOG_COMMAND (git_command);
 
-	commit_match_info = NULL;
-	parent_match_info = NULL;
-	author_match_info = NULL;
-	time_match_info = NULL;
-	short_log_match_info = NULL;
-	
-	/* Entries are delimited by the hex value 0x0c */
-	if (*output == 0x0c && self->priv->current_revision)
-	{	
-		g_queue_push_tail (self->priv->output_queue, 
-						   self->priv->current_revision);
-		anjuta_command_notify_data_arrived (ANJUTA_COMMAND (git_command));
-	}
-	
-	if (g_regex_match (self->priv->commit_regex, output, 0, &commit_match_info))
-	{
-		commit_sha = g_match_info_fetch (commit_match_info, 1);
-		
-		self->priv->current_revision = g_hash_table_lookup (self->priv->revisions, 
-															commit_sha);
-		
-		if (!self->priv->current_revision)
-		{
-			self->priv->current_revision = git_revision_new ();
-			git_revision_set_sha (self->priv->current_revision, commit_sha);
-			g_hash_table_insert (self->priv->revisions, g_strdup (commit_sha),
-								 g_object_ref (self->priv->current_revision));
-		}
-		
-		g_free (commit_sha);
-	}
-	else if (g_regex_match (self->priv->parent_regex, output, 0, 
-							&parent_match_info))
-	{	
-		parents = g_match_info_fetch (parent_match_info, 1);
-		parent_shas = g_strsplit (parents, " ", -1);
-		
-		for (i = 0; parent_shas[i]; i++)
-		{
-			parent_revision = g_hash_table_lookup (self->priv->revisions,
-												   parent_shas[i]);
-			
-			if (!parent_revision)
-			{
-				parent_revision = git_revision_new ();
-				git_revision_set_sha (parent_revision, parent_shas[i]);
-				g_hash_table_insert (self->priv->revisions,
-									 g_strdup (parent_shas[i]),
-									 g_object_ref (parent_revision));
-			}
-			
-			git_revision_add_child (parent_revision,
-									self->priv->current_revision);
-		}
-		
-		g_free (parents);
-		g_strfreev (parent_shas);
-	}
-	else if (g_regex_match (self->priv->author_regex, output, 0, 
-			 &author_match_info))
-	{
-		author = g_match_info_fetch (author_match_info, 1);
-		git_revision_set_author (self->priv->current_revision, author);
-		
-		g_free (author);
-	}
-	else if (g_regex_match (self->priv->time_regex, output, 0, 
-			 &time_match_info))
-	{
-		time = g_match_info_fetch (time_match_info, 1);
-		git_revision_set_date (self->priv->current_revision, atol (time));
-		
-		g_free (time);
-	}
-	else if (g_regex_match (self->priv->short_log_regex, output, 0, 
-							&short_log_match_info))
-	{
-		short_log = g_match_info_fetch (short_log_match_info, 1);
-		git_revision_set_short_log (self->priv->current_revision, short_log);
-		
-		g_free (short_log);
-	}
-	
-	if (commit_match_info)
-		g_match_info_free (commit_match_info);
+	self = GIT_LOG_COMMAND (command);
 
-	if (parent_match_info)
-		g_match_info_free (parent_match_info);
+	/* Send an empty string to the data processing command so that it knows
+	 * to stop when it's done processing data. The command will finish when 
+	 * the processing thread finishes, and not when git stops executing */
+	git_log_data_command_push_line (self->priv->data_command, "");
 
-	if (author_match_info)
-		g_match_info_free (author_match_info);
+	/* Use the git return code */
+	self->priv->return_code = return_code;
+}
 
-	if (time_match_info)
-		g_match_info_free (time_match_info);
+static void
+git_log_command_handle_output (GitCommand *git_command, const gchar *output)
+{
+	GitLogCommand *self;
+	
+	self = GIT_LOG_COMMAND (git_command);
 
-	if (short_log_match_info)
-		g_match_info_free (short_log_match_info);
+	git_log_data_command_push_line (self->priv->data_command, output);
 }
 
 static void
@@ -310,6 +208,7 @@ git_log_command_class_init (GitLogCommandClass *klass)
 	object_class->finalize = git_log_command_finalize;
 	parent_class->output_handler = git_log_command_handle_output;
 	command_class->run = git_log_command_run;
+	command_class->notify_complete = git_log_command_notify_complete;
 }
 
 
@@ -343,5 +242,5 @@ git_log_command_new (const gchar *working_directory,
 GQueue *
 git_log_command_get_output_queue (GitLogCommand *self)
 {
-	return self->priv->output_queue;
+	return git_log_data_command_get_output (self->priv->data_command);
 }
diff --git a/plugins/git/git-log-command.h b/plugins/git/git-log-command.h
index 5d80600..a708d71 100644
--- a/plugins/git/git-log-command.h
+++ b/plugins/git/git-log-command.h
@@ -28,6 +28,7 @@
 #include <glib-object.h>
 #include <stdlib.h>
 #include "git-command.h"
+#include "git-log-data-command.h"
 #include "git-revision.h"
 
 G_BEGIN_DECLS
diff --git a/plugins/git/git-log-data-command.c b/plugins/git/git-log-data-command.c
new file mode 100644
index 0000000..e9821c6
--- /dev/null
+++ b/plugins/git/git-log-data-command.c
@@ -0,0 +1,250 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * anjuta
+ * Copyright (C) James Liggett 2010 <jrliggett cox net>
+ * 
+ * anjuta 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 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * anjuta 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "git-log-data-command.h"
+
+#define COMMIT_REGEX "^commit ([[:xdigit:]]{40})"
+#define PARENT_REGEX "^parents (.*)"
+#define AUTHOR_REGEX "^author (.*)"
+#define TIME_REGEX "^time (\\d*)"
+#define SHORT_LOG_REGEX "^(?:short log) (.*)"
+
+struct _GitLogDataCommandPriv
+{
+	GAsyncQueue *input_queue;
+	GQueue *output_queue;
+	GHashTable *revisions;
+	GitRevision *current_revision;
+	GRegex *commit_regex;
+	GRegex *parent_regex;
+	GRegex *author_regex;
+	GRegex *time_regex;
+	GRegex *short_log_regex;
+};
+
+G_DEFINE_TYPE (GitLogDataCommand, git_log_data_command, 
+               ANJUTA_TYPE_ASYNC_COMMAND);
+
+static void
+git_log_data_command_init (GitLogDataCommand *self)
+{
+	self->priv = g_new0 (GitLogDataCommandPriv, 1);
+	self->priv->input_queue = g_async_queue_new_full (g_free);
+	self->priv->output_queue = g_queue_new ();
+	self->priv->revisions = g_hash_table_new_full (g_str_hash, g_str_equal,
+												   g_free, g_object_unref);
+	self->priv->commit_regex = g_regex_new (COMMIT_REGEX, 0, 0, NULL);
+	self->priv->parent_regex = g_regex_new (PARENT_REGEX, 0, 0, NULL);
+	self->priv->author_regex = g_regex_new (AUTHOR_REGEX, 0, 0, NULL);
+	self->priv->time_regex = g_regex_new (TIME_REGEX, 0, 0, NULL);
+	self->priv->short_log_regex = g_regex_new (SHORT_LOG_REGEX, 0, 0, NULL);
+}
+
+static void
+git_log_data_command_finalize (GObject *object)
+{
+	GitLogDataCommand *self;
+	GList *current_output;
+	
+	self = GIT_LOG_DATA_COMMAND (object);
+
+	g_async_queue_unref (self->priv->input_queue);
+	current_output = self->priv->output_queue->head;
+	
+	while (current_output)
+	{
+		g_object_unref (current_output->data);
+		current_output = g_list_next (current_output);
+	}
+	
+	g_queue_free (self->priv->output_queue);
+	g_hash_table_destroy (self->priv->revisions);
+	g_regex_unref (self->priv->commit_regex);
+	g_regex_unref (self->priv->parent_regex);
+	g_regex_unref (self->priv->author_regex);
+	g_regex_unref (self->priv->time_regex);
+	g_regex_unref (self->priv->short_log_regex);
+
+	G_OBJECT_CLASS (git_log_data_command_parent_class)->finalize (object);
+}
+
+static guint
+git_log_data_command_run (AnjutaCommand *command)
+{
+	GitLogDataCommand *self;
+	gchar *line;
+	GMatchInfo *commit_match_info;
+	GMatchInfo *parent_match_info;
+	GMatchInfo *author_match_info;
+	GMatchInfo *time_match_info;
+	GMatchInfo *short_log_match_info;
+	gchar *commit_sha;
+	gchar *parents;
+	gchar **parent_shas;
+	gint i;
+	GitRevision *parent_revision;
+	gchar *author;
+	gchar *time;
+	gchar *short_log;
+
+	self = GIT_LOG_DATA_COMMAND (command);
+
+	while ((line = g_async_queue_pop (self->priv->input_queue)))
+	{
+		/* An empty string means there's nothing left to process */
+		if (g_utf8_strlen (line, -1) == 0)
+		{
+			g_free (line);
+			break;
+		}
+
+		commit_match_info = NULL;
+		parent_match_info = NULL;
+		author_match_info = NULL;
+		time_match_info = NULL;
+		short_log_match_info = NULL;
+
+		/* Entries are delimited by the hex value 0x0c */
+		if (*line == 0x0c && self->priv->current_revision)
+		{	
+			anjuta_async_command_lock (ANJUTA_ASYNC_COMMAND (command));
+			g_queue_push_tail (self->priv->output_queue, 
+			                   self->priv->current_revision);
+			anjuta_async_command_unlock (ANJUTA_ASYNC_COMMAND (command));
+			
+			anjuta_command_notify_data_arrived (command);
+		}
+
+		if (g_regex_match (self->priv->commit_regex, line, 0, &commit_match_info))
+		{
+			commit_sha = g_match_info_fetch (commit_match_info, 1);
+
+			self->priv->current_revision = g_hash_table_lookup (self->priv->revisions, 
+			                                                    commit_sha);
+
+			if (!self->priv->current_revision)
+			{
+				self->priv->current_revision = git_revision_new ();
+				git_revision_set_sha (self->priv->current_revision, commit_sha);
+				g_hash_table_insert (self->priv->revisions, g_strdup (commit_sha),
+				                     g_object_ref (self->priv->current_revision));
+			}
+
+			g_free (commit_sha);
+		}
+		else if (g_regex_match (self->priv->parent_regex, line, 0, 
+		                        &parent_match_info))
+		{	
+			parents = g_match_info_fetch (parent_match_info, 1);
+			parent_shas = g_strsplit (parents, " ", -1);
+
+			for (i = 0; parent_shas[i]; i++)
+			{
+				parent_revision = g_hash_table_lookup (self->priv->revisions,
+				                                       parent_shas[i]);
+
+				if (!parent_revision)
+				{
+					parent_revision = git_revision_new ();
+					git_revision_set_sha (parent_revision, parent_shas[i]);
+					g_hash_table_insert (self->priv->revisions,
+					                     g_strdup (parent_shas[i]),
+					                     g_object_ref (parent_revision));
+				}
+
+				git_revision_add_child (parent_revision,
+				                        self->priv->current_revision);
+			}
+
+			g_free (parents);
+			g_strfreev (parent_shas);
+		}
+		else if (g_regex_match (self->priv->author_regex, line, 0, 
+		                        &author_match_info))
+		{
+			author = g_match_info_fetch (author_match_info, 1);
+			git_revision_set_author (self->priv->current_revision, author);
+
+			g_free (author);
+		}
+		else if (g_regex_match (self->priv->time_regex, line, 0, 
+		                        &time_match_info))
+		{
+			time = g_match_info_fetch (time_match_info, 1);
+			git_revision_set_date (self->priv->current_revision, atol (time));
+
+			g_free (time);
+		}
+		else if (g_regex_match (self->priv->short_log_regex, line, 0, 
+		                        &short_log_match_info))
+		{
+			short_log = g_match_info_fetch (short_log_match_info, 1);
+			git_revision_set_short_log (self->priv->current_revision, short_log);
+
+			g_free (short_log);
+		}
+
+		if (commit_match_info)
+			g_match_info_free (commit_match_info);
+
+		if (parent_match_info)
+			g_match_info_free (parent_match_info);
+
+		if (author_match_info)
+			g_match_info_free (author_match_info);
+
+		if (time_match_info)
+			g_match_info_free (time_match_info);
+
+		if (short_log_match_info)
+			g_match_info_free (short_log_match_info);
+
+		g_free (line);
+	}
+
+	return 0;
+}
+
+static void
+git_log_data_command_class_init (GitLogDataCommandClass *klass)
+{
+	GObjectClass* object_class = G_OBJECT_CLASS (klass);
+	AnjutaCommandClass *command_class = ANJUTA_COMMAND_CLASS (klass);
+	
+	object_class->finalize = git_log_data_command_finalize;
+	command_class->run = git_log_data_command_run;
+}
+
+GitLogDataCommand *
+git_log_data_command_new (void)
+{
+	return g_object_new (GIT_TYPE_LOG_DATA_COMMAND, NULL);
+}
+
+GQueue *
+git_log_data_command_get_output (GitLogDataCommand *self)
+{
+	return self->priv->output_queue;
+}
+
+void
+git_log_data_command_push_line (GitLogDataCommand *self, const gchar *line)
+{
+	g_async_queue_push (self->priv->input_queue, g_strdup (line));
+}
diff --git a/plugins/git/git-log-data-command.h b/plugins/git/git-log-data-command.h
new file mode 100644
index 0000000..f7f73e5
--- /dev/null
+++ b/plugins/git/git-log-data-command.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * anjuta
+ * Copyright (C) James Liggett 2010 <jrliggett cox net>
+ * 
+ * anjuta 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 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * anjuta 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _GIT_LOG_DATA_COMMAND_H_
+#define _GIT_LOG_DATA_COMMAND_H_
+
+#include <glib-object.h>
+#include <stdlib.h>
+#include <libanjuta/anjuta-async-command.h>
+#include "git-revision.h"
+
+G_BEGIN_DECLS
+
+#define GIT_TYPE_LOG_DATA_COMMAND             (git_log_data_command_get_type ())
+#define GIT_LOG_DATA_COMMAND(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIT_TYPE_LOG_DATA_COMMAND, GitLogDataCommand))
+#define GIT_LOG_DATA_COMMAND_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GIT_TYPE_LOG_DATA_COMMAND, GitLogDataCommandClass))
+#define GIT_IS_LOG_DATA_COMMAND(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIT_TYPE_LOG_DATA_COMMAND))
+#define GIT_IS_LOG_DATA_COMMAND_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GIT_TYPE_LOG_DATA_COMMAND))
+#define GIT_LOG_DATA_COMMAND_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GIT_TYPE_LOG_DATA_COMMAND, GitLogDataCommandClass))
+
+typedef struct _GitLogDataCommandClass GitLogDataCommandClass;
+typedef struct _GitLogDataCommand GitLogDataCommand;
+typedef struct _GitLogDataCommandPriv GitLogDataCommandPriv;
+
+struct _GitLogDataCommandClass
+{
+	AnjutaAsyncCommandClass parent_class;
+};
+
+struct _GitLogDataCommand
+{
+	AnjutaAsyncCommand parent_instance;
+
+	GitLogDataCommandPriv *priv;
+};
+
+GType git_log_data_command_get_type (void) G_GNUC_CONST;
+GitLogDataCommand *git_log_data_command_new (void);
+GQueue *git_log_data_command_get_output (GitLogDataCommand *self);
+void git_log_data_command_push_line (GitLogDataCommand *self, 
+                                     const gchar *line);
+
+G_END_DECLS
+
+#endif /* _GIT_LOG_DATA_COMMAND_H_ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]