[Conkeror] [PATCH v2] New module to define webjumps for git repository summaries.

David Kettler kettler at internode.on.net
Tue Feb 10 03:04:21 PST 2009


A webjump to access repositories at a gitweb server can be defined
using define_gitweb_summary_webjump.  The alternative url for the
webjump can be a particular repository, the repository list or user
defined.

The default completer for the webjump will complete against the
repositories available at the gitweb server.  To enable this, the user
must specify where to store the OPML data from the server (using
either $opml_file or gitweb_webjumps_opml_directory) and fetch the
data once using gitweb-webjump-get-opml.

Examples:

In your rc have:

  require("gitweb-webjump.js");
  gitweb_webjumps_opml_directory = get_home_directory();
  gitweb_webjumps_opml_directory.appendRelativePath(".conkerorrc/gitweb-webjumps-opml");

  define_gitweb_summary_webjump("gitweb-ko", "http://git.kernel.org");
  define_gitweb_summary_webjump("gitweb-cz", "http://repo.or.cz/w");

You can now use the following webjumps:

  gitweb-cz conkeror
  gitweb-ko git/git

If you create the directory ~/.conkerorrc/gitweb-webjumps-opml/ and in
conkeror use M-x gitweb-webjump-get-opml and select gitweb-cz then,
once the download is finished, completions will be available for that
webjump.

With thanks to John Foerch for suggestions.
---

This implements the changes as discussed with John.  In particular,
the completer uses local copies of the OPML data; auto fetching was
too expensive.  Rather than attaching the opml url and filename to the
webjump, as I had suggested, I put them in the global
gitweb_webjumps_opml.

gitweb_webjump_completer could be changed to use all_word_completer.
One difference in behaviour is that the former fills in the completion
in the minibuffer when tab is pressed, but the latter does not.  I'm
not sure which is preferable.

There are three remaining problems, all related to gitweb-webjump-get-opml.

1a)

If you invoke gitweb-webjump-get-opml on gitweb-cz, and then attempt
to use completion on that webjump while the download is in progress,
conkeror will try to parse the incomplete file and fail.

The fix is to download to a .temp file and then rename once it's
complete.  That should be supported in save_uri and the download
manager so that the .temp suffix isn't visible in the UI.  I'm
surprised this isn't supported directly by the download engine;
mozilla does download to a temporary.

1b)

There seems to be another underlying problem (but this won't be an
issue once (1a) is fixed).

After the parser has failed once and the download is complete,
retrying the completer causes the parser to fail again, this time
erroneously.  After restarting conkeror, the file parses fine.

Here's a simpler way to reproduce:

Have a copy of the opml in the right file for some site (eg. run
gitweb-webjump-get-opml on it and wait for that to complete).  Copy
the opml file then truncate it from the middle of some line.  Run this
in conkeror (eg):

  M-: gitweb_webjump_get_completions_from_file(gitweb_webjumps_opml["gitweb-cz"].file)

You get a malformed-xml error on the console, which is correct.  Now
overwrite the mangled file with the copy.  Run the command again; you
still get the error, which is wrong.

2)

If you invoke gitweb-webjump-get-opml on gitweb-ko, an invalid OPML
file results.  The problem is that kernel.org
helpfully puts up a "Generating.." page with a meta refresh <meta
http-equiv="refresh" content="0"/>.  save_uri doesn't do the refresh.

A work around is to visit the OPML link with find-url and save it to
the right place; that will work fine.
---
 modules/gitweb-webjump.js |  173 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 173 insertions(+), 0 deletions(-)
 create mode 100644 modules/gitweb-webjump.js

diff --git a/modules/gitweb-webjump.js b/modules/gitweb-webjump.js
new file mode 100644
index 0000000..1a8317d
--- /dev/null
+++ b/modules/gitweb-webjump.js
@@ -0,0 +1,173 @@
+/**
+ * (C) Copyright 2009 David Kettler
+ *
+ * Use, modification, and distribution are subject to the terms specified in the
+ * COPYING file.
+ *
+ * Construct a webjump (with completer) to visit repository summary
+ * pages at a gitweb server.
+**/
+
+require("webjump.js");
+
+/* OPML files for completion on webjumps. */
+gitweb_webjumps_opml = {};
+
+define_variable("gitweb_webjumps_opml_directory", null,
+                "A directory for storing the OPML data corresponding to a " +
+                "gitweb webjump; the data can be fetched using " +
+                "gitweb-webjump-get-opml.  " +
+                "If the data is available for a webjump then it will provide " +
+                "completions for the available repositories.");
+
+
+function parse_gitweb_completions_from_dom_node(doc) {
+    let elems = doc.getElementsByTagName("outline");
+
+    let completions = [];
+    for (let i = 0; i < elems.length; ++i) {
+	let node = elems[i];
+	let type = node.getAttribute("type");
+	let name = node.getAttribute("text");
+	if (type == "rss" && name)
+	    completions.push(name.replace(/\.git$/, ""));
+    }
+    completions.sort();
+
+    return completions;
+}
+
+
+function gitweb_webjump_get_completions_from_file(file) {
+    var file_istream = Cc["@mozilla.org/network/file-input-stream;1"]
+        .createInstance(Ci.nsIFileInputStream);
+    file_istream.init(file, MODE_RDONLY, 0644, false);
+    var dom_parser = Cc["@mozilla.org/xmlextras/domparser;1"]
+        .createInstance(Ci.nsIDOMParser);
+    var doc = dom_parser.parseFromStream(file_istream, "UTF-8",
+                                         file.fileSize, "text/xml");
+
+    return parse_gitweb_completions_from_dom_node(doc);
+}
+
+
+/**
+ * A completer that gets the list of repositories from the gitweb
+ * generated OPML.
+ */
+function gitweb_webjump_completer(opml_file) {
+    let completions = null;
+    let file_time = 0;
+
+    return function (input, pos, conservative) {
+	if (pos == 0 && conservative)
+	    yield co_return(undefined);
+	let str = input.substring(0,pos);
+
+        if (opml_file.exists() && opml_file.lastModifiedTime > file_time) {
+            file_time = opml_file.lastModifiedTime;
+            completions = gitweb_webjump_get_completions_from_file(opml_file);
+        }
+        if (!completions)
+            yield co_return(null);
+
+        let words = trim_whitespace(input.toLowerCase()).split(/\s+/);
+        let data = completions.filter(function (x) {
+            for (var i = 0; i < words.length; ++i)
+                if (x.toLowerCase().indexOf(words[i]) == -1)
+                    return false;
+            return true;
+        });
+
+	let c = {count: data.length,
+		 get_string: function (i) data[i],
+		 get_description: function (i) data[i],
+		 get_input_state: function (i) [data[i]]};
+	yield co_return(c);
+    };
+}
+
+
+/* buffer is used only to associate with the download */
+define_keywords("$buffer");
+function gitweb_webjump_get_opml(key) {
+    keywords(arguments);
+    let opml = gitweb_webjumps_opml[key];
+    if (!opml)
+	return null;
+    save_uri(load_spec(opml.url), opml.file,
+             $buffer = arguments.$buffer, $use_cache = false);
+    return opml;
+}
+
+interactive("gitweb-webjump-get-opml",
+            "Fetch the OPML data corresponding to a gitweb webjump from " +
+            "the gitweb server and make it available to the gitweb webjump " +
+            "completer.",
+            function (I) {
+                var completions = [];
+                for (let i in gitweb_webjumps_opml)
+                    completions.push(i);
+		var key = yield I.minibuffer.read(
+		    $prompt = "Get OPML for gitweb webjump:",
+		    $history = "webjump",
+		    $completer = all_word_completer($completions = completions),
+		    $match_required = true);
+		gitweb_webjump_get_opml(key, $buffer = I.buffer);
+	    });
+
+
+/**
+ * Construct a webjump to visit repository summary pages at a gitweb
+ * server.
+ *
+ * If a repository name is supplied as $default then the alternative
+ * url is set to that repository at the gitweb site.  If an
+ * alternative is not specified by either $default or $alternative
+ * then it is set to the repository list page of the gitweb site.
+ *
+ * A completer is provided that uses the list of repositories from the
+ * OPML data on the gitweb server.  A local file for the OPML data
+ * must be specified either with $opml_file or via
+ * gitweb_webjumps_opml_directory.  The OPML data must be manually
+ * downloaded; eg. using gitweb-webjump-get-opml.  Each time the
+ * completer is used it will check if the file has been updated and
+ * reload if necessary.
+ *
+ * A completer may instead be specified with $completer.
+ */
+define_keywords("$default", "$alternative", "$completer", "$opml_file");
+function define_gitweb_summary_webjump(key, base_url) {
+    keywords(arguments);
+    let completer = arguments.$completer;
+    let alternative = arguments.$alternative;
+    let gitweb_url = base_url + "/gitweb.cgi";
+    let summary_url = gitweb_url + "?p=%s.git;a=summary";
+    let opml_url = gitweb_url + "?a=opml";
+
+    let opml_file = arguments.$opml_file;
+    if (typeof opml_file == 'string')
+        opml_file = make_file(opml_file);
+    if (!opml_file && gitweb_webjumps_opml_directory) {
+        opml_file = Cc["@mozilla.org/file/local;1"]
+            .createInstance(Ci.nsILocalFile);
+        if (gitweb_webjumps_opml_directory instanceof Ci.nsILocalFile)
+            opml_file.initWithFile(gitweb_webjumps_opml_directory);
+        else
+            opml_file.initWithPath(gitweb_webjumps_opml_directory);
+        opml_file.appendRelativePath(key + ".opml");
+    }
+
+    if (!completer && opml_file) {
+        completer = gitweb_webjump_completer(opml_file);
+        gitweb_webjumps_opml[key] = {file: opml_file, url: opml_url};
+    }
+    if (arguments.$default)
+        alternative = summary_url.replace("%s", arguments.$default);
+    if (!alternative)
+        alternative = gitweb_url;
+
+    define_webjump(key, summary_url,
+		   $completer = completer,
+                   $alternative = alternative);
+}
-- 
1.5.6.5



More information about the Conkeror mailing list