[latexila/wip/latexila-next] LatexilaPostProcessorLatex (not finished)
- From: Sébastien Wilmet <swilmet src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [latexila/wip/latexila-next] LatexilaPostProcessorLatex (not finished)
- Date: Sat, 26 Jul 2014 01:41:51 +0000 (UTC)
commit 16dfcd188b87ce3131b688d8d3efabe667b66d3c
Author: Sébastien Wilmet <swilmet gnome org>
Date: Sat Jul 12 13:40:12 2014 +0200
LatexilaPostProcessorLatex (not finished)
src/liblatexila/Makefile.am | 2 +
src/liblatexila/latexila-build-job.c | 1 +
src/liblatexila/latexila-build-view.c | 26 +-
src/liblatexila/latexila-build-view.h | 2 +
src/liblatexila/latexila-post-processor-latex.c | 1220 +++++++++++++++++++++++
src/liblatexila/latexila-post-processor-latex.h | 55 +
src/liblatexila/latexila-post-processor.c | 22 +
src/liblatexila/latexila-post-processor.h | 16 +
src/liblatexila/latexila-types.h | 1 +
src/liblatexila/latexila.h | 1 +
10 files changed, 1345 insertions(+), 1 deletions(-)
---
diff --git a/src/liblatexila/Makefile.am b/src/liblatexila/Makefile.am
index 1beb3cc..e2e33fa 100644
--- a/src/liblatexila/Makefile.am
+++ b/src/liblatexila/Makefile.am
@@ -20,6 +20,7 @@ liblatexila_headers = \
latexila-build-view.h \
latexila-post-processor.h \
latexila-post-processor-all-output.h \
+ latexila-post-processor-latex.h \
latexila-synctex.h \
latexila-types.h \
latexila-utils.h
@@ -33,6 +34,7 @@ liblatexila_sources = \
latexila-build-view.c \
latexila-post-processor.c \
latexila-post-processor-all-output.c \
+ latexila-post-processor-latex.c \
latexila-synctex.c \
latexila-utils.c
diff --git a/src/liblatexila/latexila-build-job.c b/src/liblatexila/latexila-build-job.c
index 5b176b3..d32d70f 100644
--- a/src/liblatexila/latexila-build-job.c
+++ b/src/liblatexila/latexila-build-job.c
@@ -481,6 +481,7 @@ launch_subprocess (LatexilaBuildJob *build_job)
build_job->priv->post_processor = latexila_post_processor_all_output_new ();
latexila_post_processor_process_async (build_job->priv->post_processor,
+ build_job->priv->file,
g_subprocess_get_stdout_pipe (subprocess),
g_task_get_cancellable (build_job->priv->task),
(GAsyncReadyCallback) post_processor_cb,
diff --git a/src/liblatexila/latexila-build-view.c b/src/liblatexila/latexila-build-view.c
index 1ee5260..af6680e 100644
--- a/src/liblatexila/latexila-build-view.c
+++ b/src/liblatexila/latexila-build-view.c
@@ -27,6 +27,7 @@
*/
#include "latexila-build-view.h"
+#include <string.h>
#include "latexila-utils.h"
#include "latexila-enum-types.h"
@@ -77,6 +78,27 @@ G_DEFINE_TYPE_WITH_PRIVATE (LatexilaBuildView, latexila_build_view, GTK_TYPE_TRE
static guint signals[LAST_SIGNAL];
/**
+ * latexila_build_msg_reinit:
+ * @build_msg: a #LatexilaBuildMsg.
+ *
+ * Reinitializes a #LatexilaBuildMsg.
+ */
+void
+latexila_build_msg_reinit (LatexilaBuildMsg *build_msg)
+{
+ g_assert (build_msg != NULL);
+
+ g_free (build_msg->text);
+ g_free (build_msg->filename);
+
+ memset (build_msg, 0, sizeof (LatexilaBuildMsg));
+
+ build_msg->start_line = -1;
+ build_msg->end_line = -1;
+ build_msg->expand = TRUE;
+}
+
+/**
* latexila_build_msg_new: (skip)
*
* Free the return value with latexila_build_msg_free() when no longer needed.
@@ -86,7 +108,9 @@ static guint signals[LAST_SIGNAL];
LatexilaBuildMsg *
latexila_build_msg_new (void)
{
- LatexilaBuildMsg *build_msg = g_slice_new0 (LatexilaBuildMsg);
+ LatexilaBuildMsg *build_msg;
+
+ build_msg = g_slice_new0 (LatexilaBuildMsg);
build_msg->start_line = -1;
build_msg->end_line = -1;
diff --git a/src/liblatexila/latexila-build-view.h b/src/liblatexila/latexila-build-view.h
index bc4245e..b99a962 100644
--- a/src/liblatexila/latexila-build-view.h
+++ b/src/liblatexila/latexila-build-view.h
@@ -113,6 +113,8 @@ struct _LatexilaBuildViewClass
LatexilaBuildMsg * latexila_build_msg_new (void);
+void latexila_build_msg_reinit (LatexilaBuildMsg *build_msg);
+
void latexila_build_msg_free (LatexilaBuildMsg *build_msg);
GType latexila_build_view_get_type (void) G_GNUC_CONST;
diff --git a/src/liblatexila/latexila-post-processor-latex.c b/src/liblatexila/latexila-post-processor-latex.c
new file mode 100644
index 0000000..7bf1d49
--- /dev/null
+++ b/src/liblatexila/latexila-post-processor-latex.c
@@ -0,0 +1,1220 @@
+/*
+ * This file is part of LaTeXila.
+ *
+ * Copyright (C) 2014 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * LaTeXila 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.
+ *
+ * LaTeXila 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 LaTeXila. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "latexila-post-processor-latex.h"
+#include <stdlib.h>
+#include <string.h>
+#include "latexila-build-view.h"
+
+#define NO_LINE (-1)
+
+/* If a message is split into several lines, we enter in a different state to
+ * fetch the end of the message.
+ */
+typedef enum
+{
+ STATE_START,
+ STATE_BADBOX,
+ STATE_WARNING,
+ STATE_ERROR,
+ STATE_ERROR_SEARCH_LINE,
+ STATE_FILENAME,
+ STATE_FILENAME_HEURISTIC
+} State;
+
+/* Opened file. They are present in a stack. It is used to know on which file an
+ * error or warning occurred.
+ */
+typedef struct
+{
+ gchar *filename;
+ guint reliable : 1;
+
+ /* Non-existent files are also pushed on the stack, because the corresponding
+ * ')' will pop it. If we don't push them, wrong files are popped.
+ * When a new message is added, the last _existing_ file is taken.
+ */
+ guint exists : 1;
+} OpenedFile;
+
+struct _LatexilaPostProcessorLatexPrivate
+{
+ GNode *messages;
+
+ /* The last message is used to insert in O(1) at the end of 'messages'. */
+ GNode *last_message;
+
+ /* Current message. */
+ LatexilaBuildMsg *cur_msg;
+
+ State state;
+
+ /* If a message is split into several lines, the lines are concatenated in
+ * line_buffer.
+ */
+ GString *line_buffer;
+ gint nb_lines;
+
+ /* If a filename is split into several lines. */
+ GString *filename_buffer;
+
+ /* The stack containing the files that TeX is processing. The top of the stack
+ * is the beginning of the list.
+ * Elements type: pointer to OpenedFile
+ */
+ GSList *stack_files;
+
+ /* The directory where the document is compiled. */
+ gchar *directory_path;
+
+ /* For statistics. */
+ gint nb_badboxes;
+ gint nb_warnings;
+ gint nb_errors;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (LatexilaPostProcessorLatex,
+ latexila_post_processor_latex,
+ LATEXILA_TYPE_POST_PROCESSOR)
+
+static OpenedFile *
+opened_file_new (void)
+{
+ return g_slice_new0 (OpenedFile);
+}
+
+static void
+opened_file_free (OpenedFile *opened_file)
+{
+ if (opened_file != NULL)
+ {
+ g_slice_free (OpenedFile, opened_file);
+ }
+}
+
+static gchar *
+get_current_filename (LatexilaPostProcessorLatex *pp)
+{
+ GSList *l;
+
+ for (l = pp->priv->stack_files; l != NULL; l = l->next)
+ {
+ OpenedFile *file = l->data;
+
+ /* TODO check lazily if the file exists */
+ if (file->exists)
+ {
+ return g_strdup (file->filename);
+ }
+ }
+
+ return NULL;
+}
+
+static void
+add_message (LatexilaPostProcessorLatex *pp,
+ gboolean set_filename)
+{
+ static GRegex *regex_spaces = NULL;
+ LatexilaBuildMsg *cur_msg;
+ GError *error = NULL;
+
+ cur_msg = pp->priv->cur_msg;
+ g_return_if_fail (cur_msg != NULL);
+
+ /* Exclude some useless messages. */
+ if (cur_msg->type == LATEXILA_BUILD_MSG_TYPE_WARNING &&
+ g_strcmp0 (cur_msg->text, "There were undefined references.") == 0)
+ {
+ latexila_build_msg_reinit (cur_msg);
+ return;
+ }
+
+ if (set_filename)
+ {
+ g_free (cur_msg->filename);
+ cur_msg->filename = get_current_filename (pp);
+ }
+
+ if (G_UNLIKELY (regex_spaces == NULL))
+ {
+ regex_spaces = g_regex_new ("\\s{2,}", G_REGEX_OPTIMIZE, 0, &error);
+
+ if (error != NULL)
+ {
+ g_warning ("PostProcessorLatex: %s", error->message);
+ g_error_free (error);
+ error = NULL;
+ }
+ }
+
+ if (G_LIKELY (regex_spaces != NULL))
+ {
+ gchar *new_text;
+
+ /* A message on several lines is sometimes indented, so when the lines are
+ * concatenated there are a lot of spaces. So multiple spaces are replaced
+ * by one space.
+ */
+ new_text = g_regex_replace (regex_spaces,
+ cur_msg->text,
+ -1, 0, " ", 0,
+ &error);
+
+ if (error != NULL)
+ {
+ g_warning ("PostProcessorLatex: %s", error->message);
+ g_error_free (error);
+ error = NULL;
+ }
+
+ if (new_text != NULL)
+ {
+ g_free (cur_msg->text);
+ cur_msg->text = new_text;
+ }
+ }
+
+ switch (cur_msg->type)
+ {
+ case LATEXILA_BUILD_MSG_TYPE_BADBOX:
+ pp->priv->nb_badboxes++;
+ break;
+
+ case LATEXILA_BUILD_MSG_TYPE_WARNING:
+ pp->priv->nb_warnings++;
+ break;
+
+ case LATEXILA_BUILD_MSG_TYPE_ERROR:
+ pp->priv->nb_errors++;
+ break;
+
+ default:
+ break;
+ }
+
+ pp->priv->last_message = g_node_insert_data_after (pp->priv->messages,
+ pp->priv->last_message,
+ cur_msg);
+
+ pp->priv->cur_msg = latexila_build_msg_new ();
+}
+
+static void
+latexila_post_processor_latex_start (LatexilaPostProcessor *post_processor,
+ GFile *file)
+{
+ LatexilaPostProcessorLatex *pp = LATEXILA_POST_PROCESSOR_LATEX (post_processor);
+ GFile *parent;
+
+ parent = g_file_get_parent (file);
+
+ g_free (pp->priv->directory_path);
+ pp->priv->directory_path = g_file_get_parse_name (parent);
+
+ g_object_unref (parent);
+}
+
+static void
+latexila_post_processor_latex_end (LatexilaPostProcessor *post_processor)
+{
+ LatexilaPostProcessorLatex *pp = LATEXILA_POST_PROCESSOR_LATEX (post_processor);
+ LatexilaBuildMsg *cur_msg = pp->priv->cur_msg;
+
+ latexila_build_msg_reinit (cur_msg);
+
+ /* Statistics. Since all the messages printed by TeX are in English, the
+ * string is not translated.
+ */
+ cur_msg->type = LATEXILA_BUILD_MSG_TYPE_INFO;
+ cur_msg->text = g_strdup_printf ("%d %s, %d %s, %d %s",
+ pp->priv->nb_errors,
+ pp->priv->nb_errors == 1 ? "error" : "errors",
+ pp->priv->nb_warnings,
+ pp->priv->nb_warnings == 1 ? "warning" : "warnings",
+ pp->priv->nb_badboxes,
+ pp->priv->nb_badboxes == 1 ? "badbox" : "badboxes");
+
+ add_message (pp, FALSE);
+}
+
+static gboolean
+detect_badbox_line (LatexilaPostProcessorLatex *pp,
+ const gchar *badbox_line,
+ gboolean current_line_is_empty)
+{
+ static GRegex *regex_badbox_lines = NULL;
+ static GRegex *regex_badbox_line = NULL;
+ static GRegex *regex_badbox_output = NULL;
+ LatexilaBuildMsg *cur_msg = pp->priv->cur_msg;
+ GError *error = NULL;
+
+ if (G_UNLIKELY (regex_badbox_lines == NULL))
+ {
+ regex_badbox_lines = g_regex_new ("(.*) at lines (\\d+)--(\\d+)",
+ G_REGEX_OPTIMIZE,
+ 0,
+ &error);
+
+ if (error != NULL)
+ {
+ g_warning ("PostProcessorLatex: %s", error->message);
+ g_error_free (error);
+ error = NULL;
+ return FALSE;
+ }
+ }
+
+ if (G_UNLIKELY (regex_badbox_line == NULL))
+ {
+ regex_badbox_line = g_regex_new ("(.*) at line (\\d+)",
+ G_REGEX_OPTIMIZE,
+ 0,
+ &error);
+
+ if (error != NULL)
+ {
+ g_warning ("PostProcessorLatex: %s", error->message);
+ g_error_free (error);
+ error = NULL;
+ return FALSE;
+ }
+ }
+
+ if (G_UNLIKELY (regex_badbox_output == NULL))
+ {
+ regex_badbox_output = g_regex_new ("(.*)has occurred while \\\\output is active",
+ G_REGEX_OPTIMIZE,
+ 0,
+ &error);
+
+ if (error != NULL)
+ {
+ g_warning ("PostProcessorLatex: %s", error->message);
+ g_error_free (error);
+ error = NULL;
+ return FALSE;
+ }
+ }
+
+ if (g_regex_match (regex_badbox_lines, badbox_line, 0, NULL))
+ {
+ gchar **strings;
+ gint n1;
+ gint n2;
+
+ pp->priv->state = STATE_START;
+
+ /* TODO use g_match_info_fetch_named() */
+ strings = g_regex_split (regex_badbox_lines, badbox_line, 0);
+
+ g_free (cur_msg->text);
+ cur_msg->text = g_strdup (strings[1]);
+
+ n1 = atoi (strings[2]);
+ n2 = atoi (strings[3]);
+
+ if (n1 <= n2)
+ {
+ cur_msg->start_line = n1;
+ cur_msg->end_line = n2;
+ }
+ else
+ {
+ cur_msg->start_line = n2;
+ cur_msg->end_line = n1;
+ }
+
+ g_strfreev (strings);
+ return TRUE;
+ }
+
+ if (g_regex_match (regex_badbox_line, badbox_line, 0, NULL))
+ {
+ gchar **strings;
+
+ pp->priv->state = STATE_START;
+
+ strings = g_regex_split (regex_badbox_line, badbox_line, 0);
+
+ g_free (cur_msg->text);
+ cur_msg->text = g_strdup (strings[1]);
+
+ cur_msg->start_line = atoi (strings[2]);
+
+ g_strfreev (strings);
+ return TRUE;
+ }
+
+ if (g_regex_match (regex_badbox_output, badbox_line, 0, NULL))
+ {
+ gchar **strings;
+
+ pp->priv->state = STATE_START;
+
+ strings = g_regex_split (regex_badbox_output, badbox_line, 0);
+
+ g_free (cur_msg->text);
+ cur_msg->text = g_strdup (strings[1]);
+ cur_msg->start_line = NO_LINE;
+
+ g_strfreev (strings);
+ return TRUE;
+ }
+
+ if (pp->priv->nb_lines > 4 || current_line_is_empty)
+ {
+ pp->priv->state = STATE_START;
+
+ g_free (cur_msg->text);
+ cur_msg->text = g_strdup (badbox_line);
+
+ cur_msg->start_line = NO_LINE;
+ return TRUE;
+ }
+
+ pp->priv->state = STATE_BADBOX;
+ return FALSE;
+}
+
+static gboolean
+detect_badbox (LatexilaPostProcessorLatex *pp,
+ const gchar *line)
+{
+ static GRegex *regex_badbox = NULL;
+ GError *error = NULL;
+
+ if (G_UNLIKELY (regex_badbox == NULL))
+ {
+ regex_badbox = g_regex_new ("^(Over|Under)full \\\\[hv]box",
+ G_REGEX_OPTIMIZE,
+ 0,
+ &error);
+
+ if (error != NULL)
+ {
+ g_warning ("PostProcessorLatex: %s", error->message);
+ g_error_free (error);
+ error = NULL;
+ return FALSE;
+ }
+ }
+
+ switch (pp->priv->state)
+ {
+ case STATE_START:
+ if (!g_regex_match (regex_badbox, line, 0, NULL))
+ {
+ return FALSE;
+ }
+
+ pp->priv->cur_msg->type = LATEXILA_BUILD_MSG_TYPE_BADBOX;
+
+ if (detect_badbox_line (pp, line, FALSE))
+ {
+ add_message (pp, TRUE);
+ }
+ else
+ {
+ if (pp->priv->line_buffer != NULL)
+ {
+ g_string_free (pp->priv->line_buffer, TRUE);
+ }
+
+ pp->priv->line_buffer = g_string_new (line);
+ pp->priv->nb_lines++;
+ }
+
+ return TRUE;
+
+ case STATE_BADBOX:
+ g_string_append (pp->priv->line_buffer, line);
+ pp->priv->nb_lines++;
+
+ if (detect_badbox_line (pp,
+ pp->priv->line_buffer->str,
+ line[0] == '\0'))
+ {
+ add_message (pp, TRUE);
+ pp->priv->nb_lines = 0;
+ }
+
+ /* The return value is not important here. */
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+static gboolean
+detect_warning_line (LatexilaPostProcessorLatex *pp,
+ const gchar *warning,
+ gboolean current_line_is_empty)
+{
+ static GRegex *regex_warning_line = NULL;
+ static GRegex *regex_warning_international_line = NULL;
+ LatexilaBuildMsg *cur_msg = pp->priv->cur_msg;
+ gint len;
+ GError *error = NULL;
+
+ if (G_UNLIKELY (regex_warning_line == NULL))
+ {
+ regex_warning_line = g_regex_new ("(.*) on input line (\\d+)\\.$",
+ G_REGEX_OPTIMIZE,
+ 0,
+ &error);
+
+ if (error != NULL)
+ {
+ g_warning ("PostProcessorLatex: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+ }
+
+ if (G_UNLIKELY (regex_warning_international_line == NULL))
+ {
+ regex_warning_international_line = g_regex_new ("(.*)(\\d+)\\.$",
+ G_REGEX_OPTIMIZE,
+ 0,
+ &error);
+
+ if (error != NULL)
+ {
+ g_warning ("PostProcessorLatex: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+ }
+
+ if (g_regex_match (regex_warning_line, warning, 0, NULL))
+ {
+ gchar **strings;
+
+ pp->priv->state = STATE_START;
+
+ strings = g_regex_split (regex_warning_line, warning, 0);
+
+ g_free (cur_msg->text);
+ cur_msg->text = g_strdup (strings[1]);
+
+ cur_msg->start_line = atoi (strings[2]);
+
+ g_strfreev (strings);
+ return TRUE;
+ }
+
+ if (g_regex_match (regex_warning_international_line, warning, 0, NULL))
+ {
+ gchar **strings;
+
+ pp->priv->state = STATE_START;
+
+ strings = g_regex_split (regex_warning_international_line, warning, 0);
+
+ g_free (cur_msg->text);
+ cur_msg->text = g_strdup (strings[1]);
+
+ cur_msg->start_line = atoi (strings[2]);
+
+ g_strfreev (strings);
+ return TRUE;
+ }
+
+ len = strlen (warning);
+ if (warning[len-1] == '.' || pp->priv->nb_lines > 5 || current_line_is_empty)
+ {
+ pp->priv->state = STATE_START;
+
+ g_free (cur_msg->text);
+ cur_msg->text = g_strdup (warning);
+
+ cur_msg->start_line = NO_LINE;
+ return TRUE;
+ }
+
+ pp->priv->state = STATE_WARNING;
+ return FALSE;
+}
+
+static gboolean
+detect_warning (LatexilaPostProcessorLatex *pp,
+ const gchar *line)
+{
+ static GRegex *regex_warning = NULL;
+ static GRegex *regex_warning_no_file = NULL;
+ LatexilaBuildMsg *cur_msg = pp->priv->cur_msg;
+ GMatchInfo *match_info;
+ GError *error = NULL;
+
+ if (G_UNLIKELY (regex_warning == NULL))
+ {
+ regex_warning = g_regex_new ("^(((! )?(La|pdf)TeX)|Package|Class)"
+ "(?P<name>.*) Warning[^:]*:\\s*(?P<contents>.*)",
+ G_REGEX_OPTIMIZE | G_REGEX_CASELESS,
+ 0,
+ &error);
+
+ if (error != NULL)
+ {
+ g_warning ("PostProcessorLatex: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+ }
+
+ if (G_UNLIKELY (regex_warning_no_file == NULL))
+ {
+ regex_warning_no_file = g_regex_new ("(No file .*)",
+ G_REGEX_OPTIMIZE,
+ 0,
+ &error);
+
+ if (error != NULL)
+ {
+ g_warning ("PostProcessorLatex: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+ }
+
+ switch (pp->priv->state)
+ {
+ case STATE_START:
+ g_regex_match (regex_warning, line, 0, &match_info);
+ if (g_match_info_matches (match_info))
+ {
+ gchar *contents;
+ gchar *name;
+
+ cur_msg->type = LATEXILA_BUILD_MSG_TYPE_WARNING;
+
+ contents = g_match_info_fetch_named (match_info, "contents");
+ name = g_match_info_fetch_named (match_info, "name");
+ name = g_strstrip (name);
+
+ if (name[0] != '\0')
+ {
+ gchar *new_contents = g_strdup_printf ("%s: %s", name, contents);
+ g_free (contents);
+ contents = new_contents;
+ }
+
+ if (detect_warning_line (pp, contents, FALSE))
+ {
+ add_message (pp, TRUE);
+ }
+ else
+ {
+ if (pp->priv->line_buffer != NULL)
+ {
+ g_string_free (pp->priv->line_buffer, TRUE);
+ }
+
+ pp->priv->line_buffer = g_string_new (contents);
+ pp->priv->nb_lines++;
+ }
+
+ g_free (contents);
+ g_free (name);
+ g_match_info_free (match_info);
+ return TRUE;
+ }
+
+ g_match_info_free (match_info);
+ match_info = NULL;
+
+ if (g_regex_match (regex_warning_no_file, line, 0, NULL))
+ {
+ gchar **strings;
+
+ cur_msg->type = LATEXILA_BUILD_MSG_TYPE_WARNING;
+
+ strings = g_regex_split (regex_warning_no_file, line, 0);
+
+ g_free (cur_msg->text);
+ cur_msg->text = g_strdup (strings[1]);
+
+ cur_msg->start_line = NO_LINE;
+
+ add_message (pp, TRUE);
+
+ g_strfreev (strings);
+ return TRUE;
+ }
+
+ return FALSE;
+
+ case STATE_WARNING:
+ g_string_append (pp->priv->line_buffer, line);
+ pp->priv->nb_lines++;
+
+ if (detect_warning_line (pp,
+ pp->priv->line_buffer->str,
+ line[0] == '\0'))
+ {
+ add_message (pp, TRUE);
+ pp->priv->nb_lines = 0;
+ }
+
+ /* The return value is not important here. */
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+static gboolean
+detect_error (LatexilaPostProcessorLatex *pp,
+ const gchar *line)
+{
+ static GRegex *regex_latex_error = NULL;
+ static GRegex *regex_pdflatex_error = NULL;
+ static GRegex *regex_tex_error = NULL;
+ static GRegex *regex_error_line = NULL;
+ gboolean found;
+ gchar *msg;
+ gint len;
+ LatexilaBuildMsg *cur_msg = pp->priv->cur_msg;
+ GError *error = NULL;
+
+ if (G_UNLIKELY (regex_latex_error == NULL))
+ {
+ regex_latex_error = g_regex_new ("^! LaTeX Error: (.*)$",
+ G_REGEX_OPTIMIZE,
+ 0,
+ &error);
+
+ if (error != NULL)
+ {
+ g_warning ("PostProcessorLatex: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+ }
+
+ if (G_UNLIKELY (regex_pdflatex_error == NULL))
+ {
+ regex_pdflatex_error = g_regex_new ("^Error: pdflatex (.*)$",
+ G_REGEX_OPTIMIZE,
+ 0,
+ &error);
+
+ if (error != NULL)
+ {
+ g_warning ("PostProcessorLatex: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+ }
+
+ if (G_UNLIKELY (regex_tex_error == NULL))
+ {
+ regex_tex_error = g_regex_new ("^! (.*)\\.$",
+ G_REGEX_OPTIMIZE,
+ 0,
+ &error);
+
+ if (error != NULL)
+ {
+ g_warning ("PostProcessorLatex: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+ }
+
+ if (G_UNLIKELY (regex_error_line == NULL))
+ {
+ regex_error_line = g_regex_new ("^l\\.(\\d+)(.*)",
+ G_REGEX_OPTIMIZE,
+ 0,
+ &error);
+
+ if (error != NULL)
+ {
+ g_warning ("PostProcessorLatex: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+ }
+
+ switch (pp->priv->state)
+ {
+ case STATE_START:
+ found = TRUE;
+ msg = NULL;
+
+ if (g_regex_match (regex_latex_error, line, 0, NULL))
+ {
+ gchar **strings = g_regex_split (regex_latex_error, line, 0);
+ msg = g_strdup (strings[1]);
+ g_strfreev (strings);
+ }
+ else if (g_regex_match (regex_pdflatex_error, line, 0, NULL))
+ {
+ gchar **strings = g_regex_split (regex_pdflatex_error, line, 0);
+ msg = g_strdup (strings[1]);
+ g_strfreev (strings);
+ }
+ else if (g_regex_match (regex_tex_error, line, 0, NULL))
+ {
+ gchar **strings = g_regex_split (regex_tex_error, line, 0);
+ msg = g_strdup (strings[1]);
+ g_strfreev (strings);
+ }
+ else
+ {
+ found = FALSE;
+ }
+
+ if (found)
+ {
+ pp->priv->nb_lines++;
+ cur_msg->type = LATEXILA_BUILD_MSG_TYPE_ERROR;
+
+ len = strlen (line);
+
+ /* The message is complete. */
+ if (line[len-1] == '.')
+ {
+ g_free (cur_msg->text);
+ cur_msg->text = msg;
+
+ pp->priv->state = STATE_ERROR_SEARCH_LINE;
+ }
+
+ /* The message is split into several lines. */
+ else
+ {
+ if (pp->priv->line_buffer != NULL)
+ {
+ g_string_free (pp->priv->line_buffer, TRUE);
+ }
+
+ pp->priv->line_buffer = g_string_new (msg);
+ pp->priv->state = STATE_ERROR;
+
+ g_free (msg);
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+
+ case STATE_ERROR:
+ g_string_append (pp->priv->line_buffer, line);
+ pp->priv->nb_lines++;
+
+ len = strlen (line);
+
+ if (line[len-1] == '.')
+ {
+ g_free (cur_msg->text);
+ cur_msg->text = g_string_free (pp->priv->line_buffer, FALSE);
+ pp->priv->line_buffer = NULL;
+
+ pp->priv->state = STATE_ERROR_SEARCH_LINE;
+ }
+ else if (pp->priv->nb_lines > 4)
+ {
+ g_free (cur_msg->text);
+ cur_msg->text = g_string_free (pp->priv->line_buffer, FALSE);
+ pp->priv->line_buffer = NULL;
+
+ cur_msg->start_line = NO_LINE;
+
+ add_message (pp, TRUE);
+
+ pp->priv->nb_lines = 0;
+ pp->priv->state = STATE_START;
+ }
+
+ /* The return value is not important here. */
+ return TRUE;
+
+ case STATE_ERROR_SEARCH_LINE:
+ pp->priv->nb_lines++;
+
+ if (g_regex_match (regex_error_line, line, 0, NULL))
+ {
+ gchar **strings = g_regex_split (regex_error_line, line, 0);
+ cur_msg->start_line = atoi (strings[1]);
+
+ add_message (pp, TRUE);
+
+ pp->priv->nb_lines = 0;
+ pp->priv->state = STATE_START;
+
+ g_strfreev (strings);
+ return TRUE;
+ }
+ else if (pp->priv->nb_lines > 11)
+ {
+ cur_msg->start_line = NO_LINE;
+ add_message (pp, TRUE);
+ pp->priv->nb_lines = 0;
+ pp->priv->state = STATE_START;
+ return TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+detect_other (LatexilaPostProcessorLatex *pp,
+ const gchar *line)
+{
+ static GRegex *regex_other_bytes = NULL;
+ LatexilaBuildMsg *cur_msg = pp->priv->cur_msg;
+ GMatchInfo *match_info;
+ GError *error = NULL;
+
+ if (G_UNLIKELY (regex_other_bytes == NULL))
+ {
+ regex_other_bytes = g_regex_new ("(?P<nb>\\d+) bytes",
+ G_REGEX_OPTIMIZE,
+ 0,
+ &error);
+
+ if (error != NULL)
+ {
+ g_warning ("PostProcessorLatex: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+ }
+
+ if (strstr (line, "Output written on") == NULL)
+ return FALSE;
+
+ cur_msg->start_line = NO_LINE;
+ cur_msg->type = LATEXILA_BUILD_MSG_TYPE_INFO;
+
+ g_regex_match (regex_other_bytes, line, 0, &match_info);
+
+ if (g_match_info_matches (match_info))
+ {
+ gchar *nb_bytes_str;
+ glong nb_bytes;
+ gchar *human_size;
+ gchar *new_line;
+
+ nb_bytes_str = g_match_info_fetch_named (match_info, "nb");
+ g_return_val_if_fail (nb_bytes_str != NULL, FALSE);
+
+ nb_bytes = atol (nb_bytes_str);
+ human_size = g_format_size (nb_bytes);
+
+ new_line = g_regex_replace_literal (regex_other_bytes, line, -1, 0, human_size, 0, &error);
+
+ if (error == NULL)
+ {
+ g_free (cur_msg->text);
+ cur_msg->text = new_line;
+ }
+ else
+ {
+ g_warning ("PostProcessorLatex: %s", error->message);
+
+ g_free (cur_msg->text);
+ cur_msg->text = g_strdup (line);
+ }
+
+ g_free (nb_bytes_str);
+ g_free (human_size);
+ }
+ else
+ {
+ g_free (cur_msg->text);
+ cur_msg->text = g_strdup (line);
+ }
+
+ add_message (pp, FALSE);
+
+ g_match_info_free (match_info);
+ return TRUE;
+}
+
+static void
+push_file_on_stack (LatexilaPostProcessorLatex *pp,
+ const gchar *filename,
+ gboolean reliable)
+{
+}
+
+static void
+pop_file_from_stack (LatexilaPostProcessorLatex *pp)
+{
+ if (pp->priv->stack_files != NULL)
+ {
+ OpenedFile *opened_file = pp->priv->stack_files->data;
+ opened_file_free (opened_file);
+ }
+
+ pp->priv->stack_files = g_slist_remove_link (pp->priv->stack_files,
+ pp->priv->stack_files);
+}
+
+static void
+update_stack_file_heuristic (LatexilaPostProcessorLatex *pp,
+ const gchar *line)
+{
+}
+
+/* There are basically two ways to detect the current file TeX is processing:
+ * 1) Use \Input (srctex or srcltx package) and \include exclusively. This will
+ * cause (La)TeX to print the line ":<+ filename" in the log file when opening
+ * a file, ":<-" when closing a file. Filenames pushed on the stack in this mode
+ * are marked as reliable.
+ *
+ * 2) Since people will probably also use the \input command, we also have to be
+ * to detect the old-fashioned way. TeX prints '(filename' when opening a file
+ * and a ')' when closing one. It is impossible to detect this with 100% certainty
+ * (TeX prints many messages and even text (a context) from the TeX source file,
+ * there could be unbalanced parentheses), so we use an heuristic algorithm.
+ * In heuristic mode a ')' will only be considered as a signal that TeX is closing
+ * a file if the top of the stack is not marked as "reliable".
+ *
+ * The method used here is almost the same as in Kile.
+ */
+static void
+update_stack_file (LatexilaPostProcessorLatex *pp,
+ const gchar *line)
+{
+ static GRegex *regex_file_pop = NULL;
+ GError *error = NULL;
+
+ if (G_UNLIKELY (regex_file_pop == NULL))
+ {
+ regex_file_pop = g_regex_new ("(\\) )?:<-$",
+ G_REGEX_OPTIMIZE,
+ 0,
+ &error);
+
+ if (error != NULL)
+ {
+ g_warning ("PostProcessorLatex: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+ }
+
+ switch (pp->priv->state)
+ {
+ case STATE_START:
+ case STATE_FILENAME_HEURISTIC:
+ /* TeX is opening a file. */
+ if (g_str_has_prefix (line, ":<+ "))
+ {
+ gchar *filename;
+
+ if (pp->priv->filename_buffer != NULL)
+ g_string_free (pp->priv->filename_buffer, TRUE);
+
+ filename = g_strdup (line + 4);
+ g_strstrip (filename);
+
+ pp->priv->filename_buffer = g_string_new (filename);
+ pp->priv->state = STATE_FILENAME;
+
+ g_free (filename);
+ }
+
+ /* TeX closed a file. */
+ else if (g_regex_match (regex_file_pop, line, 0, NULL) ||
+ g_str_has_prefix (line, ":<-"))
+ pop_file_from_stack (pp);
+
+ /* Fallback to the heuristic detection of filenames. */
+ else
+ update_stack_file_heuristic (pp, line);
+ break;
+
+ case STATE_FILENAME:
+ /* The partial filename was followed by '(', this means that TeX is
+ * signalling it is opening the file. We are sure the filename is
+ * complete now. Don't call update_stack_file_heuristic()
+ * since we don't want the filename on the stack twice.
+ */
+ if (line[0] == '(' ||
+ g_str_has_prefix (line, "\\openout"))
+ {
+ push_file_on_stack (pp, pp->priv->filename_buffer->str, TRUE);
+ pp->priv->state = STATE_START;
+ }
+
+ /* The partial filename was followed by a TeX error, meaning the
+ * file doesn't exist. Don't push it on the stack, instead try to
+ * detect the error.
+ */
+ else if (line[0] == '!')
+ {
+ pp->priv->state = STATE_START;
+ detect_error (pp, line);
+ }
+ else if (g_str_has_prefix (line, "No file"))
+ {
+ pp->priv->state = STATE_START;
+ detect_warning (pp, line);
+ }
+
+ /* The filename is not complete. */
+ else
+ {
+ gchar *line_stripped = g_strdup (line);
+ g_strstrip (line_stripped);
+ g_string_append (pp->priv->filename_buffer, line_stripped);
+ g_free (line_stripped);
+ }
+
+ default:
+ break;
+ }
+}
+
+static void
+process_line (LatexilaPostProcessorLatex *pp,
+ const gchar *line)
+{
+ g_assert (line != NULL);
+
+ switch (pp->priv->state)
+ {
+ case STATE_START:
+ if (line[0] == '\0')
+ {
+ return;
+ }
+
+ if (!(detect_badbox (pp, line) ||
+ detect_warning (pp, line) ||
+ detect_error (pp, line) ||
+ detect_other (pp, line)))
+ {
+ update_stack_file (pp, line);
+ }
+ break;
+
+ case STATE_BADBOX:
+ detect_badbox (pp, line);
+ break;
+
+ case STATE_WARNING:
+ detect_warning (pp, line);
+ break;
+
+ case STATE_ERROR:
+ case STATE_ERROR_SEARCH_LINE:
+ detect_error (pp, line);
+ break;
+
+ case STATE_FILENAME:
+ case STATE_FILENAME_HEURISTIC:
+ update_stack_file (pp, line);
+ break;
+
+ default:
+ pp->priv->state = STATE_START;
+ break;
+ }
+}
+
+static void
+latexila_post_processor_latex_process_lines (LatexilaPostProcessor *post_processor,
+ gchar **lines)
+{
+ LatexilaPostProcessorLatex *pp = LATEXILA_POST_PROCESSOR_LATEX (post_processor);
+ gint i;
+
+ for (i = 0; lines != NULL && lines[i] != NULL; i++)
+ {
+ process_line (pp, lines[i]);
+ g_free (lines[i]);
+ }
+
+ g_free (lines);
+}
+
+static const GNode *
+latexila_post_processor_latex_get_messages (LatexilaPostProcessor *post_processor)
+{
+ LatexilaPostProcessorLatex *pp = LATEXILA_POST_PROCESSOR_LATEX (post_processor);
+
+ return pp->priv->messages->children;
+}
+
+static void
+latexila_post_processor_latex_finalize (GObject *object)
+{
+ LatexilaPostProcessorLatex *pp = LATEXILA_POST_PROCESSOR_LATEX (object);
+
+ latexila_build_messages_free (pp->priv->messages);
+
+ if (pp->priv->cur_msg != NULL)
+ {
+ latexila_build_msg_free (pp->priv->cur_msg);
+ }
+
+ if (pp->priv->line_buffer != NULL)
+ {
+ g_string_free (pp->priv->line_buffer, TRUE);
+ }
+
+ if (pp->priv->filename_buffer != NULL)
+ {
+ g_string_free (pp->priv->filename_buffer, TRUE);
+ }
+
+ g_slist_free_full (pp->priv->stack_files, (GDestroyNotify) opened_file_free);
+
+ g_free (pp->priv->directory_path);
+
+ G_OBJECT_CLASS (latexila_post_processor_latex_parent_class)->finalize (object);
+}
+
+static void
+latexila_post_processor_latex_class_init (LatexilaPostProcessorLatexClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ LatexilaPostProcessorClass *pp_class = LATEXILA_POST_PROCESSOR_CLASS (klass);
+
+ object_class->finalize = latexila_post_processor_latex_finalize;
+
+ pp_class->start = latexila_post_processor_latex_start;
+ pp_class->end = latexila_post_processor_latex_end;
+ pp_class->process_lines = latexila_post_processor_latex_process_lines;
+ pp_class->get_messages = latexila_post_processor_latex_get_messages;
+}
+
+static void
+latexila_post_processor_latex_init (LatexilaPostProcessorLatex *pp)
+{
+ pp->priv = latexila_post_processor_latex_get_instance_private (pp);
+
+ pp->priv->cur_msg = latexila_build_msg_new ();
+ pp->priv->state = STATE_START;
+}
diff --git a/src/liblatexila/latexila-post-processor-latex.h b/src/liblatexila/latexila-post-processor-latex.h
new file mode 100644
index 0000000..bcd617a
--- /dev/null
+++ b/src/liblatexila/latexila-post-processor-latex.h
@@ -0,0 +1,55 @@
+/*
+ * This file is part of LaTeXila.
+ *
+ * Copyright (C) 2014 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * LaTeXila 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.
+ *
+ * LaTeXila 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 LaTeXila. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LATEXILA_POST_PROCESSOR_LATEX_H__
+#define __LATEXILA_POST_PROCESSOR_LATEX_H__
+
+#include <glib-object.h>
+#include "latexila-post-processor.h"
+#include "latexila-types.h"
+
+G_BEGIN_DECLS
+
+#define LATEXILA_TYPE_POST_PROCESSOR_LATEX (latexila_post_processor_latex_get_type ())
+#define LATEXILA_POST_PROCESSOR_LATEX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
LATEXILA_TYPE_POST_PROCESSOR_LATEX, LatexilaPostProcessorLatex))
+#define LATEXILA_POST_PROCESSOR_LATEX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
LATEXILA_TYPE_POST_PROCESSOR_LATEX, LatexilaPostProcessorLatexClass))
+#define LATEXILA_IS_POST_PROCESSOR_LATEX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
LATEXILA_TYPE_POST_PROCESSOR_LATEX))
+#define LATEXILA_IS_POST_PROCESSOR_LATEX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
LATEXILA_TYPE_POST_PROCESSOR_LATEX))
+#define LATEXILA_POST_PROCESSOR_LATEX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
LATEXILA_TYPE_POST_PROCESSOR_LATEX, LatexilaPostProcessorLatexClass))
+
+typedef struct _LatexilaPostProcessorLatexClass LatexilaPostProcessorLatexClass;
+typedef struct _LatexilaPostProcessorLatexPrivate LatexilaPostProcessorLatexPrivate;
+
+struct _LatexilaPostProcessorLatex
+{
+ LatexilaPostProcessor parent;
+
+ LatexilaPostProcessorLatexPrivate *priv;
+};
+
+struct _LatexilaPostProcessorLatexClass
+{
+ LatexilaPostProcessorClass parent_class;
+};
+
+GType latexila_post_processor_latex_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __LATEXILA_POST_PROCESSOR_LATEX_H__ */
diff --git a/src/liblatexila/latexila-post-processor.c b/src/liblatexila/latexila-post-processor.c
index 07f395c..74f69c4 100644
--- a/src/liblatexila/latexila-post-processor.c
+++ b/src/liblatexila/latexila-post-processor.c
@@ -238,12 +238,25 @@ latexila_post_processor_finalize (GObject *object)
}
static void
+latexila_post_processor_start_default (LatexilaPostProcessor *pp,
+ GFile *file)
+{
+ /* Do nothing. */
+}
+
+static void
latexila_post_processor_process_lines_default (LatexilaPostProcessor *pp,
gchar **lines)
{
g_strfreev (lines);
}
+static void
+latexila_post_processor_end_default (LatexilaPostProcessor *pp)
+{
+ /* Do nothing. */
+}
+
static const GNode *
latexila_post_processor_get_messages_default (LatexilaPostProcessor *pp)
{
@@ -260,7 +273,9 @@ latexila_post_processor_class_init (LatexilaPostProcessorClass *klass)
object_class->dispose = latexila_post_processor_dispose;
object_class->finalize = latexila_post_processor_finalize;
+ klass->start = latexila_post_processor_start_default;
klass->process_lines = latexila_post_processor_process_lines_default;
+ klass->end = latexila_post_processor_end_default;
klass->get_messages = latexila_post_processor_get_messages_default;
g_object_class_install_property (object_class,
@@ -412,6 +427,7 @@ read_stream (LatexilaPostProcessor *pp)
/**
* latexila_post_processor_process_async:
* @pp: a post-processor.
+ * @file: the #GFile on which the build tool is run.
* @stream: the input stream to process.
* @cancellable: a #GCancellable.
* @callback: the callback to call when the operation is finished.
@@ -426,12 +442,14 @@ read_stream (LatexilaPostProcessor *pp)
*/
void
latexila_post_processor_process_async (LatexilaPostProcessor *pp,
+ GFile *file,
GInputStream *stream,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (LATEXILA_IS_POST_PROCESSOR (pp));
+ g_return_if_fail (G_IS_FILE (file));
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);
@@ -439,6 +457,8 @@ latexila_post_processor_process_async (LatexilaPostProcessor *pp,
pp->priv->task = g_task_new (pp, cancellable, callback, user_data);
pp->priv->stream = g_object_ref (stream);
+ LATEXILA_POST_PROCESSOR_GET_CLASS (pp)->start (pp, file);
+
if (pp->priv->line_buffer != NULL)
{
g_string_free (pp->priv->line_buffer, TRUE);
@@ -465,6 +485,8 @@ latexila_post_processor_process_finish (LatexilaPostProcessor *pp,
g_task_propagate_boolean (G_TASK (result), NULL);
+ LATEXILA_POST_PROCESSOR_GET_CLASS (pp)->end (pp);
+
g_clear_object (&pp->priv->task);
g_clear_object (&pp->priv->stream);
diff --git a/src/liblatexila/latexila-post-processor.h b/src/liblatexila/latexila-post-processor.h
index 3f244f9..43e02d4 100644
--- a/src/liblatexila/latexila-post-processor.h
+++ b/src/liblatexila/latexila-post-processor.h
@@ -65,9 +65,24 @@ struct _LatexilaPostProcessorClass
{
GObjectClass parent_class;
+ /* Start of processing.
+ * @file is the GFile on which the build tool is run.
+ */
+ void (* start) (LatexilaPostProcessor *pp,
+ GFile *file);
+
+ /* The process_lines function takes ownership of @lines. Free with
+ * g_strfreev() if you don't reuse the contents.
+ */
void (* process_lines) (LatexilaPostProcessor *pp,
gchar **lines);
+ /* End of processing. */
+ void (* end) (LatexilaPostProcessor *pp);
+
+ /* Get the build messages. The elements are of type "LatexilaBuildMsg *".
+ * This function is called after end().
+ */
const GNode * (* get_messages) (LatexilaPostProcessor *pp);
};
@@ -81,6 +96,7 @@ const gchar * latexila_post_processor_get_name_from_type (LatexilaP
void latexila_build_messages_free (GNode *build_messages);
void latexila_post_processor_process_async (LatexilaPostProcessor *pp,
+ GFile *file,
GInputStream *stream,
GCancellable *cancellable,
GAsyncReadyCallback callback,
diff --git a/src/liblatexila/latexila-types.h b/src/liblatexila/latexila-types.h
index 1dd9da4..997aa99 100644
--- a/src/liblatexila/latexila-types.h
+++ b/src/liblatexila/latexila-types.h
@@ -32,6 +32,7 @@ typedef struct _LatexilaBuildToolsPersonal LatexilaBuildToolsPersonal;
typedef struct _LatexilaBuildView LatexilaBuildView;
typedef struct _LatexilaPostProcessor LatexilaPostProcessor;
typedef struct _LatexilaPostProcessorAllOutput LatexilaPostProcessorAllOutput;
+typedef struct _LatexilaPostProcessorLatex LatexilaPostProcessorLatex;
typedef struct _LatexilaSynctex LatexilaSynctex;
G_END_DECLS
diff --git a/src/liblatexila/latexila.h b/src/liblatexila/latexila.h
index a200ab9..34607ad 100644
--- a/src/liblatexila/latexila.h
+++ b/src/liblatexila/latexila.h
@@ -31,6 +31,7 @@
#include "latexila-build-view.h"
#include "latexila-post-processor.h"
#include "latexila-post-processor-all-output.h"
+#include "latexila-post-processor-latex.h"
#include "latexila-synctex.h"
#include "latexila-utils.h"
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]