Mixins Tutorial

Chocolat can be extended plugins called mixins. Mixins are written in JavaScript and run on node.js.

For this tutorial, we'll be writing a mixin that looks up a word on wikipedia and displays the wikipedia article. Like the OS X dictionary popover.

Sounds complex? This is the entire code:

Hooks.addMenuItem("Go/Search in Wikipedia", "cmd-ctrl-w", function() {
    Recipe.run(function(r) {
        var wordrange = r.wordRangeForRange(r.selection);
        var word = r.textInRange(wordrange);

        var win = new Popover(Editor.current(), wordrange);
        win.size = { width: 320, height: 480 };
        win.url = 'http://en.m.wikipedia.org/w/index.php?search='
                    + encodeURIComponent(word)
                    + '&title=Special%3ASearch';
        win.run();
    });
});

My First Mixin

Chocolat looks for mixins in three places:

  1. ~/.chocolat/mixins
  2. ~/Library/Application Support/Chocolat/Mixins
  3. Chocolat.app/Contents/SharedSupport/mixins

I'll be using ~/.chocolat/mixins, but you can use Application Support if you prefer.


So let's get started...

mkdir -p ~/.chocolat/mixins/wikipedia.chocmixin
choc ~/.chocolat/mixins/wikipedia.chocmixin/init.js

First, we need to add a "Search on Wikipedia" menu item to the Go menu:

Hooks.addMenuItem("Go/Search on Wikipedia", "cmd-ctrl-w", function () {
    Alert.show("Hello World!");
});

Save that and—like magic—the "Search on Wikipedia" menu item will appear in the Go menu! Click it.

Finding the Current Word

OK, so we want to look up the current word on Wikipedia. For that, we need to find the current word. How?

The Recipe class is a one-stop-shop for text manipulation. A recipe lets you modify text and selection safely and atomically.

Each Recipe starts out with a call to Recipe.run(), to which you pass a function:

Recipe.run(function(r) {
    // do something with r
});

The r object is the instance of the Recipe class. To find the current range, call .wordRangeForRange() on the recipe and pass it the current selection. Chocolat uses Cocoa's idea of a "selection", a text cursor is just a selection of length 0.

Recipe.run(function(r) {
    var wordrange = r.wordRangeForRange(r.selection);
});

Getting the string for that range is another method on the recipe.

Recipe.run(function(r) {
    var wordrange = r.wordRangeForRange(r.selection);
    var word = r.textInRange(wordrange);
});

All together now:

Hooks.addMenuItem("Go/Search on Wikipedia", "cmd-ctrl-w", function () {
    Recipe.run(function(r) {
        var wordrange = r.wordRangeForRange(r.selection);
        var word = r.textInRange(wordrange);
        Alert.show("Your nearest word is '" + word + "'!");
    });
});

Adding a UI

Since Chocolat's scripting language is JavaScript, it's only fitting that the UIs are made using HTML.

Think about that. You can display any UI in Chocolat that you can construct using HTML, CSS and JS. Cool!

For the purposes of this tutorial, we just want to show a page from wikipedia.org in a popover. To that, create a new instance of Popover and set the url property.

// Create a popover attached to the current editor
// and positioned over the word range we found earlier
var win = new Popover(Editor.current(), wordrange);

// Set it to the size of an iPhone screen
win.size = { width: 320, height: 480 };

// Do a search on m.wikipedia.org
win.url = 'http://en.m.wikipedia.org/w/index.php?search='
            + encodeURIComponent(word)
            + '&title=Special%3ASearch';

// Show the popover
win.run();

That's all there is to it. Add that code to the recipe code and try looking something up.

Hooks.addMenuItem("Go/Search in Wikipedia", "cmd-ctrl-w", function() {
    Recipe.run(function(r) {
        var wordrange = r.wordRangeForRange(r.selection);
        var word = r.textInRange(wordrange);

        var win = new Popover(Editor.current(), wordrange);
        win.size = { width: 320, height: 480 };
        win.url = 'http://en.m.wikipedia.org/w/index.php?search='
                    + encodeURIComponent(word)
                    + '&title=Special%3ASearch';
        win.run();
    });
});

Sharing with the World

So you've made the world's best mixin and want to make it available to everybody. Simple!

  1. Create an account on GitHub if you don't have one already.
  2. Create a repo for your mixin push to it. The repo should be the mixin: do not enclose the mixin in a containing folder.
  3. Log in to mixins.chocolatapp.com and select the repo from the list.
  4. Fill in the metadata, and you're done!

Open Actions › Install Extras to see your mixin among everybody else's.

Chocolat uses the CommonJS package.json format. You do not have to include a package.json file with your mixin, but it's good manners if you want to distribute it.

Why JavaScript?

Everybody always asks me why the hell would you choose JavaScript? It's not because we're hipsters—and we certainly don't think JS is perfect. The short answer is that we tried everything and node.js is the only thing that worked.

  1. it's Fast. Google has done a great job. V8 is one of the fastest VMs out there, next to LuaJIT and the JVM.

  2. it's Easy to Embed. V8 definitely has the best API of any language implementation I've tried. (CPython has the worst BTW. It's almost worse than OpenSSL)

  3. it has Batteries Included. Unlike Lua, which would be a great language if it weren't so bloody useless. Want to do an http request? Find a library. Splitting a path? Find a library. Don't even think about finding the SHA-1 of something. Ugh.

  4. it's Well Known. There are other languages, but they generally are not well known enough. A Clojure-based API would be awesome ...for the one guy who knows Clojure.

Everybody knows JavaScript.