Latest revision as of 01:40, 11 May 2012

So, you've got an idea for a great extension to xmonad, and you think
other people will enjoy using it, too. The only problem is, you've
never written an xmonad extension before, "darcs" sounds scary, and
you're not sure what the proper procedures are. Well, never fear ---
this tutorial will have you writing xmonad extensions in no time!

As a running example, let's suppose you want to develop an extension
to pop up a "Hello world" message when the user presses a certain key combination. We'll walk through the entire process of creating,
testing, and submitting such an extension. I encourage you to follow along, either by copying and pasting the code shown here, or you can just use it as a guideline for making your own extension.

Before we begin, here are some other resources which may be useful to
you during this process:

YAHT and the Haskell wikibook are excellent introductions to the Haskell programming language. LYAH is worth a look as well.

The first step in creating an xmonad extension is to get the latest
development sources using darcs. darcs is a distributed revision
control system, written in Haskell, which is used to track the source
code for xmonad and the xmonad-contrib extension library. If you
already have the darcs sources, you can skip this section.

First, of course, you will have to install darcs itself; it probably
already exists as a package for your operating system's package
manager. Otherwise, you can grab a tarball from darcs.net.

Next, create a directory where you will store the xmonad repositories.

Be patient, it might take a while to download the entire repositories
the first time. After the initial download, however, subsequent pulls
should be much faster, since darcs will only need to download any new
patches.

To keep your local repositories up-to-date, you should periodically
run the command darcs pull in each directory in order to download
any new patches, especially before starting any new development.
Darcs is generally good at merging patches, but it's best not to make
it work any harder than necessary.

Now restart xmonad (run touch ~/.xmonad/xmonad.hs, then hit mod-q). Note, if you are upgrading from a previous version of xmonad, you may need to first unregister the old packages with ghc. Type ghc-pkg list xmonad and ghc-pkg list xmonad-contrib at a prompt; if multiple versions are listed you need to unregister all but the newest. For example:

If you pull new patches into the xmonad repository or make changes to it, and rebuild the xmonad library, you should do a clean build of the xmonad-contrib library afterwards (cabal clean && cabal install). Otherwise, the Cabal build process for xmonad-contrib may not notice that the xmonad library (which xmonad-contrib depends on) has changed, and xmonad-contrib will not get rebuilt properly, leading to possible crashes at runtime. Annoying, but necessary.

The version of Haddock which is still bundled in several OS package systems (Debian unstable, for example) is too old to handle some of the features used in xmonad. The most recent version of haddock (as of 15 November 2010) is 2.8.1. Until it is updated in your OS's package system, you will have to install the latest version of haddock manually; you can download it from Hackage. Or, if you have the fabulous cabal-install tool, you can just type cabal install haddock at a prompt.

Now we've got... a nice, blank module! What now? Well, let's begin by adding some standard header stuff. In practice you can just open up some other module and copy and paste, changing the stuff that's appropriate to change.

The + indicates the line that we added to the .cabal file. (Note that the module list is generally kept in alphabetical order.) Be sure to indent with spaces, not tabs! Since indentation is important in .cabal files (just as in Python or Haskell), tabs are not allowed.

Now, to test out our new module, we can just import it into xmonad.hs, add a keybinding for the

helloWorld

action, and restart with mod-q. Now (assuming you have the xmessage utility installed) hitting the selected keybinding should pop up a little "Hello, world" window in the upper-left corner of the screen. Neat!

Well, our module works, but it's in no state to be distributed to the wider world yet! We'll have to clean it up to conform to a few standards for xmonad-contrib source code. In particular:

All exported functions should have Haddock documentation and explicit type signatures. It's a good idea to give documentation and explicit type signatures for unexported functions, too, to make it easier for others to understand or modify your code.

A "Usage" section should be added in the comments, explaining the purpose of the module and giving examples of its use.

It's usual to explicitly list the module's exports, rather than implicitly exporting everything.

(the pipe character | indicates a Haddock comment), and some usage information which will get included in the generated Haddock documentation (Haddock generates documentation for exported functions in the order they are listed in the export list between parentheses following the module declaration, along with any extra comments included there). Some quick notes about Haddock documentation format:

Use @ symbols to surround code which should be printed in a verbatim style.

Use 'single quotes' around function names to generate links to their documentation. For example, see how 'helloWorld' has single quotes in the usage information shown above.

Use "double quotes" around module names to generate links to their documentation.

Use /front slashes/ to italicize.

Escape literal quotes and frontslashes with a backslash.

Literal code blocks can be included with "bird tracks", i.e. greater-than symbols preceding the code. Be sure to put a blank line on either side of such code blocks.

It's really important to test the generated documentation before submitting our new extension. A module with poor or missing documentation (or great documentation which is not included because of parse errors) will not be very useful to people. Conversely, a module with excellent documentation and good examples will be a pleasure to use, easier to modify and extend, and a help to others trying to use the code as an example for creating their own extension!

To generate the documentation, first rebuild the xmonad-contrib library, then use cabal haddock:

Generating the Haddock documentation will generate a ton of warnings such as "could not find link destinations for: M.Map"; you can ignore these. The important line is the last one, which says whether the documentation was successfully created, and where it was generated.

In this case, to view the generated documentation, you should point your browser to something like file:///path/to/XMonadContrib/dist/doc/html/xmonad-contrib/index.html, then navigate to the documentation for your module. Check that the formatting, links, and so on are correct. For example, when first writing this tutorial, I forgot to escape the double quotes around "hello world" in the comments for the

helloWorld

function, which therefore showed up as a link to the (nonexistent) "hello world" module, instead of showing literal double quote characters.

If you make any changes to your documentation, you can simply rerun cabal haddock to regenerate; there's no need to rebuild everything as long as you only changed documentation.

Once you are satisfied with your documentation, it's probably a good idea to rebuild the xmonad-contrib library, restart xmonad, and test your extension one more time.

While it's no huge chore to build all the Haddock documentation, and you should do that before submitting your extension, sometimes you just want to build documentation for one or a few modules that aren't necessarily even finished yet. The following bash alias is a quick and dirty way to build html documentation in a draft document directory for the files specified on the command line.

The -s option makes darcs display a summary; if you leave the -s option off it will show more details about each patch.

To record the patches, type darcs record at a prompt. If this is your first time recording any patches, it will first ask you for your name and e-mail address. Then it will prompt you for each change, asking whether you would like to record it. In general, you will find that darcs has a very nice interface, prompting you about most things and giving you a lot of flexibility in choosing what exactly you would like to do, without having to memorize lots of complicated command-line flags. In this particular case, it's nice that you can hand-pick which changes should be recorded as a patch -- so you could be working on several things at once, and record them separately as you finish, without messing up the other things.

Note that for the xmonad-contrib repository, darcs is configured to run some tests before committing a patch. In particular, it will try compiling everything from scratch and generating all the documentation. If there are any errors, you must fix them before you will be allowed to record your patch. It can be a bit tiresome waiting for the tests to complete (especially if you are on a slower computer), but it ensures that no one can accidentally push a patch which breaks the repository. The repository should always be in a compilable state.

After recording a patch, running darcs changes --last 1 should show you that your patch is recorded. At this point darcs whatsnew will once again say 'No changes!' because there are no longer any unrecorded changes.

And now, the moment of truth: sending your patch to the xmonad mailing list for inclusion in the main repository! First make sure you are subscribed; as an anti-spam measure, only list subscribers are allowed to post.

Now, if you have a mail agent configured correctly, all you have to do is type darcs send at the prompt, and it will send the patches you select as attachments to an e-mail to the xmonad list (xmonad@haskell.org). Alternatively, you can darcs send to an output file with the -o option: