[latexila/wip/build-tools-revamp] PostProcessor and PostProcessorAllOutput
- From: Sébastien Wilmet <swilmet src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [latexila/wip/build-tools-revamp] PostProcessor and PostProcessorAllOutput
- Date: Fri, 23 May 2014 23:05:33 +0000 (UTC)
commit 4e36ea24b3207451ac30c12683a317a2ee32158a
Author: Sébastien Wilmet <swilmet gnome org>
Date: Fri May 23 12:25:30 2014 +0200
PostProcessor and PostProcessorAllOutput
The post processors work on a GInputStream obtained by the GSubprocess.
The GInputStream is read asynchronously.
Before, the Vala code worked on the full command output, as one big
string.
docs/reference/Makefile.am | 4 +-
docs/reference/latexila-docs.xml | 2 +
docs/reference/latexila-sections.txt | 40 +++
src/liblatexila/latexila-build-job.c | 84 +++---
src/liblatexila/latexila-build-view.c | 4 +-
src/liblatexila/latexila-build-view.h | 2 +-
.../latexila-post-processor-all-output.c | 73 ++---
src/liblatexila/latexila-post-processor.c | 308 +++++++++++++++++++-
src/liblatexila/latexila-post-processor.h | 23 +-
9 files changed, 430 insertions(+), 110 deletions(-)
---
diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am
index 9d900a7..7f86569 100644
--- a/docs/reference/Makefile.am
+++ b/docs/reference/Makefile.am
@@ -53,9 +53,7 @@ EXTRA_HFILES =
# Header files or dirs to ignore when scanning. Use base file/dir names
# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code
IGNORE_HFILES = \
- latexila-enum-types.h \
- latexila-post-processor.h \
- latexila-post-processor-all-output.h
+ latexila-enum-types.h
# Images to copy into HTML directory.
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
diff --git a/docs/reference/latexila-docs.xml b/docs/reference/latexila-docs.xml
index 2e09e5f..a1b8a38 100644
--- a/docs/reference/latexila-docs.xml
+++ b/docs/reference/latexila-docs.xml
@@ -17,6 +17,8 @@
<xi:include href="xml/build-tools-default.xml"/>
<xi:include href="xml/build-tools-personal.xml"/>
<xi:include href="xml/build-view.xml"/>
+ <xi:include href="xml/post-processor.xml"/>
+ <xi:include href="xml/post-processor-all-output.xml"/>
<xi:include href="xml/synctex.xml"/>
<xi:include href="xml/utils.xml"/>
</chapter>
diff --git a/docs/reference/latexila-sections.txt b/docs/reference/latexila-sections.txt
index 9c5ce08..1b830d0 100644
--- a/docs/reference/latexila-sections.txt
+++ b/docs/reference/latexila-sections.txt
@@ -131,6 +131,46 @@ latexila_build_view_get_type
</SECTION>
<SECTION>
+<FILE>post-processor</FILE>
+<TITLE>LatexilaPostProcessor</TITLE>
+LatexilaPostProcessor
+LatexilaPostProcessorType
+latexila_build_messages_free
+latexila_post_processor_get_name_from_type
+latexila_post_processor_get_type_from_name
+latexila_post_processor_process_async
+latexila_post_processor_process_finish
+latexila_post_processor_get_messages
+<SUBSECTION Standard>
+LATEXILA_IS_POST_PROCESSOR
+LATEXILA_IS_POST_PROCESSOR_CLASS
+LATEXILA_POST_PROCESSOR
+LATEXILA_POST_PROCESSOR_CLASS
+LATEXILA_POST_PROCESSOR_GET_CLASS
+LATEXILA_TYPE_POST_PROCESSOR
+LatexilaPostProcessorClass
+LatexilaPostProcessorPrivate
+latexila_post_processor_get_type
+</SECTION>
+
+<SECTION>
+<FILE>post-processor-all-output</FILE>
+<TITLE>LatexilaPostProcessorAllOutput</TITLE>
+LatexilaPostProcessorAllOutput
+latexila_post_processor_all_output_new
+<SUBSECTION Standard>
+LATEXILA_IS_POST_PROCESSOR_ALL_OUTPUT
+LATEXILA_IS_POST_PROCESSOR_ALL_OUTPUT_CLASS
+LATEXILA_POST_PROCESSOR_ALL_OUTPUT
+LATEXILA_POST_PROCESSOR_ALL_OUTPUT_CLASS
+LATEXILA_POST_PROCESSOR_ALL_OUTPUT_GET_CLASS
+LATEXILA_TYPE_POST_PROCESSOR_ALL_OUTPUT
+LatexilaPostProcessorAllOutputClass
+LatexilaPostProcessorAllOutputPrivate
+latexila_post_processor_all_output_get_type
+</SECTION>
+
+<SECTION>
<FILE>synctex</FILE>
<TITLE>LatexilaSynctex</TITLE>
LatexilaSynctex
diff --git a/src/liblatexila/latexila-build-job.c b/src/liblatexila/latexila-build-job.c
index bd35801..641fc99 100644
--- a/src/liblatexila/latexila-build-job.c
+++ b/src/liblatexila/latexila-build-job.c
@@ -29,6 +29,7 @@
#include <string.h>
#include <glib/gi18n.h>
#include "latexila-build-view.h"
+#include "latexila-post-processor-all-output.h"
#include "latexila-utils.h"
#include "latexila-enum-types.h"
@@ -42,7 +43,7 @@ struct _LatexilaBuildJobPrivate
GFile *file;
LatexilaBuildView *build_view;
GtkTreeIter job_title;
- GNode *build_messages;
+ LatexilaPostProcessor *post_processor;
};
enum
@@ -54,30 +55,6 @@ enum
G_DEFINE_TYPE_WITH_PRIVATE (LatexilaBuildJob, latexila_build_job, G_TYPE_OBJECT)
-static gboolean
-free_build_msg (GNode *node,
- gpointer user_data)
-{
- latexila_build_msg_free (node->data);
- return FALSE;
-}
-
-static void
-free_build_messages (GNode *build_messages)
-{
- if (build_messages != NULL)
- {
- g_node_traverse (build_messages,
- G_POST_ORDER,
- G_TRAVERSE_ALL,
- -1,
- free_build_msg,
- NULL);
-
- g_node_destroy (build_messages);
- }
-}
-
static void
latexila_build_job_get_property (GObject *object,
guint prop_id,
@@ -138,6 +115,7 @@ latexila_build_job_dispose (GObject *object)
g_clear_object (&build_job->priv->task);
g_clear_object (&build_job->priv->file);
g_clear_object (&build_job->priv->build_view);
+ g_clear_object (&build_job->priv->post_processor);
G_OBJECT_CLASS (latexila_build_job_parent_class)->dispose (object);
}
@@ -148,7 +126,6 @@ latexila_build_job_finalize (GObject *object)
LatexilaBuildJob *build_job = LATEXILA_BUILD_JOB (object);
g_free (build_job->priv->command);
- free_build_messages (build_job->priv->build_messages);
G_OBJECT_CLASS (latexila_build_job_parent_class)->finalize (object);
}
@@ -408,11 +385,27 @@ display_command_line (LatexilaBuildJob *build_job)
}
static void
+post_processor_cb (LatexilaPostProcessor *pp,
+ GAsyncResult *result,
+ LatexilaBuildJob *build_job)
+{
+ const GNode *messages;
+
+ latexila_post_processor_process_finish (pp, result);
+
+ messages = latexila_post_processor_get_messages (pp);
+
+ latexila_build_view_append_messages (build_job->priv->build_view,
+ &build_job->priv->job_title,
+ messages,
+ TRUE);
+}
+
+static void
subprocess_wait_cb (GSubprocess *subprocess,
GAsyncResult *result,
LatexilaBuildJob *build_job)
{
- LatexilaBuildMsg *msg;
gboolean ret;
LatexilaBuildState state;
@@ -433,21 +426,11 @@ subprocess_wait_cb (GSubprocess *subprocess,
state = LATEXILA_BUILD_STATE_FAILED;
}
- msg = latexila_build_msg_new ();
- msg->text = g_strdup ("build job output");
- msg->type = LATEXILA_BUILD_MSG_TYPE_INFO;
-
- latexila_build_view_append_single_message (build_job->priv->build_view,
- &build_job->priv->job_title,
- msg);
-
latexila_build_view_set_title_state (build_job->priv->build_view,
&build_job->priv->job_title,
state);
g_task_return_boolean (build_job->priv->task, ret);
-
- latexila_build_msg_free (msg);
g_object_unref (subprocess);
}
@@ -461,9 +444,16 @@ launch_subprocess (LatexilaBuildJob *build_job)
gchar **argv;
GError *error = NULL;
- /* No output for the moment */
- launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE |
- G_SUBPROCESS_FLAGS_STDERR_SILENCE);
+ if (build_job->priv->post_processor_type == LATEXILA_POST_PROCESSOR_TYPE_NO_OUTPUT)
+ {
+ launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE |
+ G_SUBPROCESS_FLAGS_STDERR_SILENCE);
+ }
+ else
+ {
+ launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE |
+ G_SUBPROCESS_FLAGS_STDERR_MERGE);
+ }
parent_dir = g_file_get_parent (build_job->priv->file);
working_directory = g_file_get_path (parent_dir);
@@ -485,6 +475,18 @@ launch_subprocess (LatexilaBuildJob *build_job)
return;
}
+ if (build_job->priv->post_processor_type != LATEXILA_POST_PROCESSOR_TYPE_NO_OUTPUT)
+ {
+ g_clear_object (&build_job->priv->post_processor);
+ build_job->priv->post_processor = latexila_post_processor_all_output_new ();
+
+ latexila_post_processor_process_async (build_job->priv->post_processor,
+ g_subprocess_get_stdout_pipe (subprocess),
+ g_task_get_cancellable (build_job->priv->task),
+ (GAsyncReadyCallback) post_processor_cb,
+ build_job);
+ }
+
g_subprocess_wait_async (subprocess,
g_task_get_cancellable (build_job->priv->task),
(GAsyncReadyCallback) subprocess_wait_cb,
@@ -525,7 +527,7 @@ latexila_build_job_run_async (LatexilaBuildJob *build_job,
g_clear_object (&build_job->priv->build_view);
build_job->priv->build_view = g_object_ref (build_view);
- free_build_messages (build_job->priv->build_messages);
+ g_clear_object (&build_job->priv->post_processor);
if (!display_command_line (build_job))
{
diff --git a/src/liblatexila/latexila-build-view.c b/src/liblatexila/latexila-build-view.c
index eb485c5..9e74780 100644
--- a/src/liblatexila/latexila-build-view.c
+++ b/src/liblatexila/latexila-build-view.c
@@ -738,10 +738,10 @@ latexila_build_view_append_single_message (LatexilaBuildView *build_view,
void
latexila_build_view_append_messages (LatexilaBuildView *build_view,
GtkTreeIter *parent,
- GNode *messages,
+ const GNode *messages,
gboolean expand)
{
- GNode *node;
+ const GNode *node;
for (node = messages; node != NULL; node = node->next)
{
diff --git a/src/liblatexila/latexila-build-view.h b/src/liblatexila/latexila-build-view.h
index c85b7cd..bc4245e 100644
--- a/src/liblatexila/latexila-build-view.h
+++ b/src/liblatexila/latexila-build-view.h
@@ -139,7 +139,7 @@ GtkTreeIter latexila_build_view_append_single_message (LatexilaBui
void latexila_build_view_append_messages (LatexilaBuildView *build_view,
GtkTreeIter *parent,
- GNode *messages,
+ const GNode *messages,
gboolean expand);
void latexila_build_view_remove_children (LatexilaBuildView *build_view,
diff --git a/src/liblatexila/latexila-post-processor-all-output.c
b/src/liblatexila/latexila-post-processor-all-output.c
index f4bf671..392ab44 100644
--- a/src/liblatexila/latexila-post-processor-all-output.c
+++ b/src/liblatexila/latexila-post-processor-all-output.c
@@ -17,74 +17,60 @@
* along with LaTeXila. If not, see <http://www.gnu.org/licenses/>.
*/
+/**
+ * SECTION:post-processor-all-output
+ * @title: LatexilaPostProcessorAllOutput
+ * @short_description: all-output post-processor
+ *
+ * A post-processor that keeps all the output. Nothing is filtered.
+ */
+
#include "latexila-post-processor-all-output.h"
+#include "latexila-build-view.h"
struct _LatexilaPostProcessorAllOutputPrivate
{
- GSList *messages;
+ GNode *messages;
+ GNode *last_message;
};
G_DEFINE_TYPE_WITH_PRIVATE (LatexilaPostProcessorAllOutput, latexila_post_processor_all_output,
LATEXILA_TYPE_POST_PROCESSOR)
static void
-latexila_post_processor_all_output_process (LatexilaPostProcessor *post_processor,
- const gchar *output)
+latexila_post_processor_all_output_process_lines (LatexilaPostProcessor *post_processor,
+ gchar **lines)
{
LatexilaPostProcessorAllOutput *pp = LATEXILA_POST_PROCESSOR_ALL_OUTPUT (post_processor);
- gchar **lines;
- gchar **l;
-
- lines = g_strsplit (output, "\n", 0);
-
- for (l = lines; l != NULL && *l != NULL; l++)
- {
- pp->priv->messages = g_slist_prepend (pp->priv->messages, *l);
- }
+ gint i;
- /* Generally a single \n is present at the end of the output, so an empty line
- * is added to the list. But we don't want to display it.
- * TODO check if it is still the case in C.
- */
-#if 0
- if (pp->priv->messages != NULL)
+ for (i = 0; lines != NULL && lines[i] != NULL; i++)
{
- gchar *line = pp->priv->messages->data;
- g_assert (line != NULL);
+ LatexilaBuildMsg *msg = latexila_build_msg_new ();
+ msg->text = lines[i];
+ msg->type = LATEXILA_BUILD_MSG_TYPE_INFO;
- if (line[0] == '\0')
- {
- GSList *removed_element = pp->priv->messages;
-
- pp->priv->messages = g_slist_remove_link (pp->priv->messages, pp->priv->messages);
-
- g_slist_free_full (removed_element, g_free);
- }
+ pp->priv->last_message = g_node_insert_data_after (pp->priv->messages,
+ pp->priv->last_message,
+ msg);
}
-#endif
- pp->priv->messages = g_slist_reverse (pp->priv->messages);
-
- /* Do not use g_strfreev() because the strings are reused in the list. */
g_free (lines);
}
-static GSList *
+static const GNode *
latexila_post_processor_all_output_get_messages (LatexilaPostProcessor *post_processor)
{
LatexilaPostProcessorAllOutput *pp = LATEXILA_POST_PROCESSOR_ALL_OUTPUT (post_processor);
- return pp->priv->messages;
+ return pp->priv->messages->children;
}
static void
latexila_post_processor_all_output_finalize (GObject *object)
{
- LatexilaPostProcessorAllOutputPrivate *priv;
-
- priv = latexila_post_processor_all_output_get_instance_private (LATEXILA_POST_PROCESSOR_ALL_OUTPUT
(object));
+ LatexilaPostProcessorAllOutput *pp = LATEXILA_POST_PROCESSOR_ALL_OUTPUT (object);
- g_slist_free_full (priv->messages, g_free);
- priv->messages = NULL;
+ latexila_build_messages_free (pp->priv->messages);
G_OBJECT_CLASS (latexila_post_processor_all_output_parent_class)->finalize (object);
}
@@ -97,7 +83,7 @@ latexila_post_processor_all_output_class_init (LatexilaPostProcessorAllOutputCla
object_class->finalize = latexila_post_processor_all_output_finalize;
- post_processor_class->process = latexila_post_processor_all_output_process;
+ post_processor_class->process_lines = latexila_post_processor_all_output_process_lines;
post_processor_class->get_messages = latexila_post_processor_all_output_get_messages;
}
@@ -105,8 +91,15 @@ static void
latexila_post_processor_all_output_init (LatexilaPostProcessorAllOutput *pp)
{
pp->priv = latexila_post_processor_all_output_get_instance_private (pp);
+
+ pp->priv->messages = g_node_new (NULL);
}
+/**
+ * latexila_post_processor_all_output_new:
+ *
+ * Returns: a new #LatexilaPostProcessorAllOutput object.
+ */
LatexilaPostProcessor *
latexila_post_processor_all_output_new (void)
{
diff --git a/src/liblatexila/latexila-post-processor.c b/src/liblatexila/latexila-post-processor.c
index 19e2b40..a5a461b 100644
--- a/src/liblatexila/latexila-post-processor.c
+++ b/src/liblatexila/latexila-post-processor.c
@@ -17,10 +17,36 @@
* along with LaTeXila. If not, see <http://www.gnu.org/licenses/>.
*/
+/**
+ * SECTION:post-processor
+ * @title: LatexilaPostProcessor
+ * @short_description: post-processor base class
+ *
+ * When running a build tool, a post-processor is used to filter the output to
+ * display only the relevant messages. It can be the output of a build job
+ * command, or a log file, etc.
+ */
+
#include "latexila-post-processor.h"
+#include "latexila-build-view.h"
+
+#define BUFFER_SIZE 4096
struct _LatexilaPostProcessorPrivate
{
+ GTask *task;
+ GInputStream *stream;
+
+ /* "+1" so we can nul-terminate the buffer. */
+ gchar buffer[BUFFER_SIZE + 1];
+
+ /* The @buffer is split by lines. But since the stream is read with a fixed
+ * size (BUFFER_SIZE), the last string returned by g_strsplit() is stored in
+ * @line_buffer. When the next block is read, the first line returned is
+ * appended to @line_buffer to have the whole line.
+ */
+ GString *line_buffer;
+
guint has_details : 1;
guint show_details : 1;
};
@@ -34,6 +60,9 @@ enum
G_DEFINE_TYPE_WITH_PRIVATE (LatexilaPostProcessor, latexila_post_processor, G_TYPE_OBJECT)
+/* Prototypes */
+static void read_stream (LatexilaPostProcessor *pp);
+
/**
* latexila_post_processor_get_type_from_name:
* @name: the name of the post-processor.
@@ -102,6 +131,36 @@ latexila_post_processor_get_name_from_type (LatexilaPostProcessorType type)
}
}
+static gboolean
+free_build_msg (GNode *node,
+ gpointer user_data)
+{
+ latexila_build_msg_free (node->data);
+ return FALSE;
+}
+
+/**
+ * latexila_build_messages_free:
+ * @build_messages: a tree of #LatexilaBuildMsg's.
+ *
+ * Frees a tree of #LatexilaBuildMsg's.
+ */
+void
+latexila_build_messages_free (GNode *build_messages)
+{
+ if (build_messages != NULL)
+ {
+ g_node_traverse (build_messages,
+ G_POST_ORDER,
+ G_TRAVERSE_ALL,
+ -1,
+ free_build_msg,
+ NULL);
+
+ g_node_destroy (build_messages);
+ }
+}
+
static void
latexila_post_processor_get_property (GObject *object,
guint prop_id,
@@ -151,17 +210,39 @@ latexila_post_processor_set_property (GObject *object,
}
static void
-latexila_post_processor_process_default (LatexilaPostProcessor *pp,
- const gchar *output)
+latexila_post_processor_dispose (GObject *object)
{
- g_return_if_fail (LATEXILA_IS_POST_PROCESSOR (pp));
+ LatexilaPostProcessor *pp = LATEXILA_POST_PROCESSOR (object);
+
+ g_clear_object (&pp->priv->task);
+ g_clear_object (&pp->priv->stream);
+
+ G_OBJECT_CLASS (latexila_post_processor_parent_class)->dispose (object);
}
-static GSList *
-latexila_post_processor_get_messages_default (LatexilaPostProcessor *pp)
+static void
+latexila_post_processor_finalize (GObject *object)
{
- g_return_val_if_fail (LATEXILA_IS_POST_PROCESSOR (pp), NULL);
+ LatexilaPostProcessor *pp = LATEXILA_POST_PROCESSOR (object);
+ if (pp->priv->line_buffer != NULL)
+ {
+ g_string_free (pp->priv->line_buffer, TRUE);
+ }
+
+ G_OBJECT_CLASS (latexila_post_processor_parent_class)->finalize (object);
+}
+
+static void
+latexila_post_processor_process_lines_default (LatexilaPostProcessor *pp,
+ gchar **lines)
+{
+ g_strfreev (lines);
+}
+
+static const GNode *
+latexila_post_processor_get_messages_default (LatexilaPostProcessor *pp)
+{
return NULL;
}
@@ -172,8 +253,10 @@ latexila_post_processor_class_init (LatexilaPostProcessorClass *klass)
object_class->get_property = latexila_post_processor_get_property;
object_class->set_property = latexila_post_processor_set_property;
+ object_class->dispose = latexila_post_processor_dispose;
+ object_class->finalize = latexila_post_processor_finalize;
- klass->process = latexila_post_processor_process_default;
+ klass->process_lines = latexila_post_processor_process_lines_default;
klass->get_messages = latexila_post_processor_get_messages_default;
g_object_class_install_property (object_class,
@@ -203,22 +286,219 @@ latexila_post_processor_init (LatexilaPostProcessor *pp)
pp->priv = latexila_post_processor_get_instance_private (pp);
}
-LatexilaPostProcessor *
-latexila_post_processor_new (void)
+static void
+read_stream_cb (GInputStream *stream,
+ GAsyncResult *result,
+ LatexilaPostProcessor *pp)
+{
+ gssize bytes_read;
+ GCancellable *cancellable;
+ gchar **lines;
+ GError *error = NULL;
+
+ bytes_read = g_input_stream_read_finish (stream, result, &error);
+
+ cancellable = g_task_get_cancellable (pp->priv->task);
+ if (g_cancellable_is_cancelled (cancellable))
+ {
+ if (error != NULL)
+ {
+ g_error_free (error);
+ }
+
+ g_task_return_boolean (pp->priv->task, FALSE);
+ return;
+ }
+
+ if (error != NULL)
+ {
+ g_warning ("Error while reading the post-processor stream: %s", error->message);
+ g_error_free (error);
+ g_task_return_boolean (pp->priv->task, FALSE);
+ return;
+ }
+
+ /* End of stream reached, process line_buffer. */
+ if (bytes_read == 0)
+ {
+ /* Generally a single \n is present at the end of the stream, so an empty
+ * line is present in line_buffer. But we don't want to display it in the
+ * build view.
+ */
+ if (pp->priv->line_buffer != NULL &&
+ pp->priv->line_buffer->str != NULL &&
+ pp->priv->line_buffer->str[0] != '\0')
+ {
+ lines = g_new (gchar *, 2);
+ lines[0] = g_string_free (pp->priv->line_buffer, FALSE);
+ lines[1] = NULL;
+
+ pp->priv->line_buffer = NULL;
+
+ LATEXILA_POST_PROCESSOR_GET_CLASS (pp)->process_lines (pp, lines);
+ }
+
+ /* finished! */
+ g_task_return_boolean (pp->priv->task, TRUE);
+ return;
+ }
+
+ pp->priv->buffer[bytes_read] = '\0';
+
+ lines = g_strsplit (pp->priv->buffer, "\n", 0);
+ g_assert (lines != NULL);
+ g_assert (lines[0] != NULL);
+
+ if (pp->priv->line_buffer != NULL)
+ {
+ /* Merge line_buffer and the first line */
+ g_string_append (pp->priv->line_buffer, lines[0]);
+ }
+
+ /* If a second line exists, we can call process_lines().
+ * The first line must be replaced by the contents of line_buffer.
+ * And the last line must go to line_buffer.
+ */
+ if (lines[1] != NULL)
+ {
+ gint last_line;
+
+ if (pp->priv->line_buffer != NULL)
+ {
+ g_free (lines[0]);
+ lines[0] = g_string_free (pp->priv->line_buffer, FALSE);
+ pp->priv->line_buffer = NULL;
+ }
+
+ for (last_line = 1; lines[last_line+1] != NULL; last_line++);
+
+ pp->priv->line_buffer = g_string_new (lines[last_line]);
+ g_free (lines[last_line]);
+ lines[last_line] = NULL;
+
+ LATEXILA_POST_PROCESSOR_GET_CLASS (pp)->process_lines (pp, lines);
+ }
+ else
+ {
+ /* If not already done above, put the first line to line_buffer. */
+ if (pp->priv->line_buffer == NULL)
+ {
+ pp->priv->line_buffer = g_string_new (lines[0]);
+ }
+
+ g_strfreev (lines);
+ }
+
+ /* Unfortunately for the computer, it is not finished. */
+ read_stream (pp);
+}
+
+static void
+read_stream (LatexilaPostProcessor *pp)
{
- return g_object_new (LATEXILA_TYPE_POST_PROCESSOR, NULL);
+ g_input_stream_read_async (pp->priv->stream,
+ &pp->priv->buffer,
+ BUFFER_SIZE,
+ G_PRIORITY_DEFAULT,
+ g_task_get_cancellable (pp->priv->task),
+ (GAsyncReadyCallback) read_stream_cb,
+ pp);
}
+/**
+ * latexila_post_processor_process_async:
+ * @pp: a post-processor.
+ * @stream: the input stream to process.
+ * @cancellable: a #GCancellable.
+ * @callback: the callback to call when the operation is finished.
+ * @user_data: the data to pass to the callback.
+ *
+ * Asynchronously process an input stream. The input stream can for example come
+ * from the output of a command launched with #GSubprocess, or it can be the
+ * input stream of a file (e.g. the LaTeX log file), etc.
+ *
+ * @callback will be called when the operation is finished. You can then call
+ * latexila_post_processor_process_finish().
+ */
void
-latexila_post_processor_process (LatexilaPostProcessor *pp,
- const gchar *output)
+latexila_post_processor_process_async (LatexilaPostProcessor *pp,
+ GInputStream *stream,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
g_return_if_fail (LATEXILA_IS_POST_PROCESSOR (pp));
+ g_return_if_fail (G_IS_INPUT_STREAM (stream));
+ g_return_if_fail (G_IS_CANCELLABLE (cancellable));
+ g_return_if_fail (pp->priv->task == NULL);
- LATEXILA_POST_PROCESSOR_GET_CLASS (pp)->process (pp, output);
+ pp->priv->task = g_task_new (pp, cancellable, callback, user_data);
+ pp->priv->stream = g_object_ref (stream);
+
+ if (pp->priv->line_buffer != NULL)
+ {
+ g_string_free (pp->priv->line_buffer, TRUE);
+ pp->priv->line_buffer = NULL;
+ }
+
+ read_stream (pp);
}
-GSList *
+/**
+ * latexila_post_processor_process_finish:
+ * @pp: a post-processor.
+ * @result: a #GAsyncResult.
+ *
+ * Finishes an operation started with latexila_post_processor_process_async().
+ * After calling this function, you can get the filtered messages with
+ * latexila_post_processor_get_messages().
+ */
+void
+latexila_post_processor_process_finish (LatexilaPostProcessor *pp,
+ GAsyncResult *result)
+{
+ g_return_if_fail (g_task_is_valid (result, pp));
+
+ g_task_propagate_boolean (G_TASK (result), NULL);
+
+ g_clear_object (&pp->priv->task);
+ g_clear_object (&pp->priv->stream);
+
+ if (pp->priv->line_buffer != NULL)
+ {
+ g_string_free (pp->priv->line_buffer, TRUE);
+ }
+}
+
+/**
+ * latexila_post_processor_get_messages:
+ * @pp: a post-processor.
+ *
+ * Gets the filtered messages. Call this function only after calling
+ * latexila_post_processor_process_finish().
+ *
+ * Another solution would have been to pass the #LatexilaBuildView to the
+ * post-processor, so the filtered messages can directly be outputed to the
+ * build view as they come. But some post-processors don't know what to output
+ * directly. The latexmk post-processor can have a simplified output with only
+ * the LaTeX messages, in which case the detailed messages are also available,
+ * but this can be known only when all the stream has been processed.
+ *
+ * Obviously if the build view is passed to the post-processor, the latexmk
+ * post-processor can output its messages only at the end. But another reason to
+ * not pass the build view is for the unit tests. It is easier for the unit
+ * tests to check the returned #GNode than analyzing a #GtkTreeView. Of course
+ * it would be possible to keep also the messages in a #GNode and have this
+ * function only for the unit tests, but it takes more memory (unless a custom
+ * #GtkTreeModel is implemented), or another function is needed to configure
+ * whether a #GNode is kept in memory or not... It becomes a little too
+ * complicated, and doesn't really worth the effort as most users use latexmk.
+ *
+ * The current solution is "good enough".
+ *
+ * Returns: the tree of filtered messages.
+ */
+const GNode *
latexila_post_processor_get_messages (LatexilaPostProcessor *pp)
{
g_return_val_if_fail (LATEXILA_IS_POST_PROCESSOR (pp), NULL);
diff --git a/src/liblatexila/latexila-post-processor.h b/src/liblatexila/latexila-post-processor.h
index 51e5bab..3f244f9 100644
--- a/src/liblatexila/latexila-post-processor.h
+++ b/src/liblatexila/latexila-post-processor.h
@@ -20,8 +20,7 @@
#ifndef __LATEXILA_POST_PROCESSOR_H__
#define __LATEXILA_POST_PROCESSOR_H__
-#include <glib.h>
-#include <glib-object.h>
+#include <gio/gio.h>
#include "latexila-types.h"
G_BEGIN_DECLS
@@ -66,10 +65,10 @@ struct _LatexilaPostProcessorClass
{
GObjectClass parent_class;
- void (* process) (LatexilaPostProcessor *post_processor,
- const gchar *output);
+ void (* process_lines) (LatexilaPostProcessor *pp,
+ gchar **lines);
- GSList * (* get_messages) (LatexilaPostProcessor *post_processor);
+ const GNode * (* get_messages) (LatexilaPostProcessor *pp);
};
GType latexila_post_processor_get_type (void) G_GNUC_CONST;
@@ -79,12 +78,18 @@ gboolean latexila_post_processor_get_type_from_name (const gch
const gchar * latexila_post_processor_get_name_from_type (LatexilaPostProcessorType type);
-LatexilaPostProcessor * latexila_post_processor_new (void);
+void latexila_build_messages_free (GNode *build_messages);
-void latexila_post_processor_process (LatexilaPostProcessor *post_processor,
- const gchar *output);
+void latexila_post_processor_process_async (LatexilaPostProcessor *pp,
+ GInputStream *stream,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
-GSList * latexila_post_processor_get_messages (LatexilaPostProcessor
*post_processor);
+void latexila_post_processor_process_finish (LatexilaPostProcessor *pp,
+ GAsyncResult *result);
+
+const GNode * latexila_post_processor_get_messages (LatexilaPostProcessor *pp);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]