I have attached the patch for forward synchronization.

This patch is a lot more complex than the previous one, and I haven't
really tested it rigorously (although I installed it on my production
system today).

It introduces a new command ":synctex <line>:<column>:<source file>"
(same syntax than the "synctex view -i" parameter). This command runs
synctex, parses the result and displays the result using the existing
"search-results" mechanism.

It introduces one new intltool string (the description of the :synctex
command), and reuses two others (the error messages wrong parameter
count and no document open). I haven't updated the translation yet.


The big open question:
How do you connect that feature to a text editor?

My personal solution to this is a vim-script that uses xdotool to
simulate keypresses that issue the :synctex command. But that seems like
a big ugly hack. A better solution would be to somehow make it possible
to send remote commands to zathura, similar to "vim --remote". But I
have no idea how "vim --remote" is implemented, I have to do more
research. I'm not sure whether it is possible to implement that in a
platform independent way...
diff --git a/config.c b/config.c
index 6db7ffc..5d7a956 100644
--- a/config.c
+++ b/config.c
@@ -9,6 +9,7 @@
 #include "render.h"
 #include "marks.h"
 #include "utils.h"
+#include "synctex.h"
 
 #include <girara/settings.h>
 #include <girara/session.h>
@@ -308,6 +309,7 @@ config_load_default(zathura_t* zathura)
   girara_inputbar_command_add(gsession, "nohlsearch", "nohl", cmd_nohlsearch,  
    NULL,         _("Don't highlight current search results"));
   girara_inputbar_command_add(gsession, "hlsearch",   NULL,   cmd_hlsearch,    
    NULL,         _("Highlight current search results"));
   girara_inputbar_command_add(gsession, "version",    NULL,   cmd_version,     
    NULL,         _("Show version information"));
+  girara_inputbar_command_add(gsession, "synctex",    NULL,   cmd_synctex,     
    NULL,         _("Synctex forward synchronization"));
 
   girara_special_command_add(gsession, '/', cmd_search, true, FORWARD,  NULL);
   girara_special_command_add(gsession, '?', cmd_search, true, BACKWARD, NULL);
diff --git a/synctex.c b/synctex.c
index 9a9c91f..d4258fb 100644
--- a/synctex.c
+++ b/synctex.c
@@ -1,12 +1,16 @@
 /* See LICENSE file for license and copyright information */
 
+#include <glib.h>
+#include <glib/gi18n.h>
+
 #include "synctex.h"
 
 #include "zathura.h"
 #include "page.h"
 #include "document.h"
+#include "utils.h"
 
-#include <glib.h>
+#include <girara/session.h>
 
 void
 synctex_edit(zathura_t* zathura, zathura_page_t* page, int x, int y)
@@ -29,3 +33,211 @@ synctex_edit(zathura_t* zathura, zathura_page_t* page, int 
x, int y)
 
   g_free(buffer);
 }
+
+static void
+synctex_record_hits(zathura_t* zathura, int page_idx, girara_list_t* hits, 
bool first)
+{
+  zathura_page_t* page = zathura_document_get_page(zathura->document, 
page_idx-1);
+  if (page == NULL)
+    return;
+
+  GtkWidget* page_widget = zathura_page_get_widget(zathura, page);
+  g_object_set(page_widget, "draw-links", FALSE, NULL);
+  g_object_set(page_widget, "search-results", hits, NULL);
+
+  if (first) {
+    page_set_delayed(zathura, zathura_page_get_index(page));
+    g_object_set(page_widget, "search-current", 0, NULL);
+  }
+}
+
+enum {
+  SYNCTEX_RESULT_BEGIN = 1,
+  SYNCTEX_RESULT_END,
+  SYNCTEX_PROP_PAGE,
+  SYNCTEX_PROP_H,
+  SYNCTEX_PROP_V,
+  SYNCTEX_PROP_WIDTH,
+  SYNCTEX_PROP_HEIGHT,
+};
+
+static struct token {
+  const char* name;
+  guint token;
+} scanner_tokens[] = {
+  {"SyncTeX result begin", SYNCTEX_RESULT_BEGIN},
+  {"SyncTeX result end", SYNCTEX_RESULT_END},
+  {"Page:", SYNCTEX_PROP_PAGE},
+  {"h:", SYNCTEX_PROP_H},
+  {"v:", SYNCTEX_PROP_V},
+  {"W:", SYNCTEX_PROP_WIDTH},
+  {"H:", SYNCTEX_PROP_HEIGHT},
+  {NULL, 0}
+};
+
+static GScannerConfig scanner_config = {
+  .cset_skip_characters = "\n\r",
+  .cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z,
+  .cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z ": ",
+  .cpair_comment_single = NULL,
+  .case_sensitive = TRUE,
+  .scan_identifier = TRUE,
+  .scan_symbols = TRUE,
+  .scan_float = TRUE,
+  .numbers_2_int = TRUE,
+};
+
+static double
+scan_float(GScanner* scanner) {
+  switch (g_scanner_get_next_token(scanner)) {
+    case G_TOKEN_FLOAT:
+      return g_scanner_cur_value(scanner).v_float;
+    case G_TOKEN_INT:
+      return g_scanner_cur_value(scanner).v_int;
+    default:
+      return 0.0;
+  }
+}
+
+static bool
+synctex_view(zathura_t* zathura, char* position)
+{
+  char* filename = g_strdup(zathura_document_get_path(zathura->document));
+  char* argv[] = {"synctex", "view", "-i", position, "-o", filename, NULL};
+  gint output;
+
+  bool ret = g_spawn_async_with_pipes(NULL, argv, NULL, G_SPAWN_SEARCH_PATH | 
G_SPAWN_STDERR_TO_DEV_NULL, NULL, NULL, NULL, NULL, &output, NULL, NULL);
+  g_free(filename);
+
+  if (!ret) {
+    return false;
+  }
+
+  GScanner* scanner = g_scanner_new(&scanner_config);
+  struct token* tokens = scanner_tokens; 
+  while (tokens->name) {
+    g_scanner_add_symbol(scanner, tokens->name, 
GINT_TO_POINTER(tokens->token));
+    tokens++;
+  }
+
+  g_scanner_input_file(scanner, output);
+
+  bool found_begin = false, found_end = false;
+  while (!found_begin && !found_end) {
+    switch (g_scanner_get_next_token(scanner)) {
+      case G_TOKEN_EOF:
+        found_end = true;
+        break;
+
+      case G_TOKEN_SYMBOL:
+        switch (GPOINTER_TO_INT(g_scanner_cur_value(scanner).v_identifier)) {
+          case SYNCTEX_RESULT_BEGIN:
+            found_begin = true;
+            break;
+        }
+        break;
+
+      default:
+        /* skip everything else */
+        break;
+    }
+  }
+
+  if (found_begin) {
+    unsigned int number_of_pages = 
zathura_document_get_number_of_pages(zathura->document);
+    for (unsigned int page_id = 0; page_id < number_of_pages; ++page_id) {
+      zathura_page_t* page = zathura_document_get_page(zathura->document, 
page_id);
+      if (page == NULL) {
+        continue;
+      }
+      g_object_set(zathura_page_get_widget(zathura, page), "search-results", 
NULL, NULL);
+    }
+  }
+
+  ret = false;
+  int page = -1, nextpage;
+  girara_list_t* hitlist = NULL;
+  zathura_rectangle_t* rectangle = NULL;
+
+  while (!found_end) {
+    switch (g_scanner_get_next_token(scanner)) {
+      case G_TOKEN_EOF:
+        found_end = true;
+        break;
+
+      case G_TOKEN_SYMBOL:
+        switch (GPOINTER_TO_INT(g_scanner_cur_value(scanner).v_identifier)) {
+          case SYNCTEX_RESULT_END:
+            found_end = true;
+            break;
+
+          case SYNCTEX_PROP_PAGE:
+            if (g_scanner_get_next_token(scanner) == G_TOKEN_INT) {
+              nextpage = g_scanner_cur_value(scanner).v_int;
+              if (page != nextpage) {
+                if (hitlist) {
+                  synctex_record_hits(zathura, page, hitlist, !ret);
+                  ret = true;
+                }
+                hitlist = girara_list_new2((girara_free_function_t) g_free);
+                page = nextpage;
+              }
+              rectangle = g_malloc0(sizeof(zathura_rectangle_t));
+              girara_list_append(hitlist, rectangle);
+            }
+            break;
+
+          case SYNCTEX_PROP_H:
+            rectangle->x1 = scan_float(scanner);
+            break;
+
+          case SYNCTEX_PROP_V:
+            rectangle->y2 = scan_float(scanner);
+            break;
+
+          case SYNCTEX_PROP_WIDTH:
+            rectangle->x2 = rectangle->x1 + scan_float(scanner);
+            break;
+
+          case SYNCTEX_PROP_HEIGHT:
+            rectangle->y1 = rectangle->y2 - scan_float(scanner);
+            break;
+        }
+        break;
+
+      default:
+        /* skip everything else */
+        break;
+    }
+  }
+
+  if (hitlist) {
+    synctex_record_hits(zathura, page, hitlist, !ret);
+    ret = true;
+  }
+
+  g_scanner_destroy(scanner);
+  close(output);
+  return ret;
+}
+
+bool
+cmd_synctex(girara_session_t* session, girara_list_t* argument_list)
+{
+  g_return_val_if_fail(session != NULL, false);
+  g_return_val_if_fail(session->global.data != NULL, false);
+  zathura_t* zathura = session->global.data;
+  if (zathura->document == NULL) {
+    girara_notify(session, GIRARA_ERROR, _("No document opened."));
+    return false;
+  }
+
+  const unsigned int argc = girara_list_size(argument_list);
+  if (argc != 1) {
+    girara_notify(session, GIRARA_ERROR, _("Invalid number of arguments 
given."));
+    return false;
+  }
+
+  char* position = girara_list_nth(argument_list, 0);
+  return synctex_view(zathura, position);
+}
diff --git a/synctex.h b/synctex.h
index 1514117..373f519 100644
--- a/synctex.h
+++ b/synctex.h
@@ -6,5 +6,6 @@
 #include "types.h"
 
 void synctex_edit(zathura_t* zathura, zathura_page_t* page, int x, int y);
+bool cmd_synctex(girara_session_t* session, girara_list_t* argument_list);
 
 #endif
_______________________________________________
zathura mailing list
[email protected]
http://lists.pwmt.org/mailman/listinfo/zathura

Reply via email to