Creating Bookmark Groups in Conkeror

A feature I like in web-browsers is the ability to collect bookmarks together into a group which I can then open all at once. For Conkeror, my favorite browser, there already exists an example of how to implement that functionality. But today I want to share with you a recent approach I came up with which I feel is better.

Nit-Picking the Example Above

The Conkeror wiki contains this nugget:

interactive("open-all",
    "opens bookmarks I visit frequently",
    function(I){
         load_url_in_new_buffer("http://www.UrlNr1.com",I.window);
         load_url_in_new_buffer("http://www.UrlNr2.org",I.window);
         load_url_in_new_buffer("http://www.UrlNr3.org",I.window);
    });

This is a great start, but we can improve on this command in a couple of ways.

  1. We can eliminate the repetative calls to load_url_in_new_buffer().

  2. We can extend the command to support multiple groups of bookmarks.

Step One, Defining Our Groups

I started out by first deciding on how I wanted to represent the data for this, i.e. the bookmark groups. Often I find that first thinking about the structure of my data leads me naturally towards the logic for how to manipulate and process said data. In this case I wanted to define my groups of bookmarks like so:

let bookmark_groups = {
  "Lua": [
    "http://www.lua.org/manual/5.2/",
    "http://www.love2d.org/wiki/Main_Page"
  ],
  "Reddit": [
    "http://reddit.com/r/programming",
    "http://reddit.com/r/emacs",
    "http://reddit.com/r/coolgithubprojects",
    "http://reddit.com/r/lua",
  ],
  "Scheme": [
    "http://www.schemers.org/Documents/Standards/R5RS/HTML/",
    "http://scsh.net/docu/html/man-Z-H-1.html#node_toc_start",
    "http://scsh.net/docu/docu.html"
  ],
  "Bloodborne": [
    "https://www.reddit.com/r/huntersbell/new/",
    "http://bloodborne.wikidot.com/"
  ]
};

After looking at the data in this structure I was easily able to begin mapping out the process for actually using said data:

  1. I need to write a command which accepts the name of a group, ideally only allowing names for which there are actual groups of bookmarks.

  2. Given that name, my command needs to iterate through the array of bookmarks for that group and open each URL in a new buffer (like a ‘tab’ in other browsers).

Step Two, Implementing That Command

Before writing the command itself via interactive() I first thought about the core, fundamental logic. The heart of the command will need a name for a bookmark group, i.e. a key for the bookmark_groups hash above, so that it can execute this:

bookmark_groups[name].forEach(
  function (element, index, array) {
    load_url_in_new_buffer(element, I.window);
});

Note that this avoids the repetition of load_url_in_new_buffer() which I pointed out earlier.

But how to get a value for that name variable? In Conkeror I can do this by reading input from the ‘minibuffer’, the small bar of input that appears when I do things like type in a URL to visit, or a filename to save, etc. I will show you the code first and then walk through it.

var name = yield I.minibuffer.read(
  $prompt = "Group", $history = "Group",
  $completer = new prefix_completer($completions = Object.keys(bookmark_groups)),
  $require_match,
  $auto_complete);

Most of the time in Conkeror you have access to the current ‘interactive context’ and by convention everyone uses the variable name I for it. And through that context I can access the minibuffer so as to call I.minibuffer.read(). This gives me a way to obtain user input. But as you can see read() accepts a variety of keyword parameters, which in Conkeror all begin with $, again by convention.

Here is a breakdown of each keyword parameter.

$prompt

The text to display in the minibufer when asking for input.

$history

A name which Conkeror will use to remember previous choices. Often its value is the same as that given to $prompt. There is not a requirement, just a common practice.

$completer

This is the most complex parameter, so I suggest having the relevant documentation on hand. Basically we want to assign to $completer a new prefix_completer, which itself accepts various keywords. The $completions keyword lets us assign an array of strings which the minibuffer will consider to be acceptable completions. By using Object.keys() we can easily get an array of all the group names from bookmark_groups.

$require_match

The presence of this keyword means the minibuffer will not accept any input that is not one of the valid completions we defined above.

$auto_complete

This keyword makes the minibuffer auto-complete the input as soon as we’ve typed enough characters for there to be only one possible valid completion. It’s not necessary but it’s a small time-saver.

With all of that out of the way I now have the code for loading all URLs in a bookmark group and a way to enter a group name from the minibuffer. Time to wrap it all together into a command.

Last Step, Defining the Interactive Command

Given the explanation of the code thus far, I hope this will make sense without the need for additional commentary:

interactive("open-bookmark-group",
    "Opens all bookmarks in the given group name.",
    function (I) {
      var name = yield I.minibuffer.read(
        $prompt = "Group", $history = "Group",
        $completer = new prefix_completer($completions = Object.keys(bookmark_groups)),
        $default_completion = "Plutono",
        $require_match,
        $auto_complete);

      bookmark_groups[name].forEach(
        function (element, index, array) {
          load_url_in_new_buffer(element, I.window);
      });
    });

define_key(content_buffer_normal_keymap, "C-c b", "open-bookmark-group");

With this code in place I could run M-x open-bookmark-group but as you can see I bound the command to C-c b as a shortcut.

And that’s it! Whenever I want to modify bookmark groups or add new ones all I need to do is change the bookmark_groups variable, never needing to change the code for open-bookmark-group as a result.

If you use Conkeror then hopefully you will find this useful. And if not then I hope it at least serves as a nice demonstration of how you can customize the browser via JavaScript.

Advertisements

One thought on “Creating Bookmark Groups in Conkeror

Add Your Thoughts

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s