I’ve been writing a lot of documentation lately. On the Stash team we keep the bulk of our developer documentation in the Stash git repository, right alongside our production code. This approach means that as we introduce new plugin points, developers can review and critique the documentation for those plugin points in the same pull request as the code change. This has proved a convenient feedback mechanism and has made keeping our developer documentation up-to-date much easier.

We use markdown syntax for our documentation, which gets transformed into HTML as part of our release process and uploaded to developer.atlassian.com as a static site. Markdown is specifically designed to be easy-to-read even in its unrendered text format, but when reviewing doc changes we typically build the docs to make sure the rendered version looks nice too. Building the docs currently means leaving the browser and running a special maven command in the Stash project, which doesn’t take long but any context switch (particularly one involving maven) is a bit of a productivity sink.

Fortunately, Stash supports markdown natively for rendering comments and pull request descriptions. It also has a nifty REST end-point used by the Stash UI for rendering previews of comments. This made it trivial to whip up a little bookmarklet that downloads the raw content of the file you’re viewing, render it using the comment preview REST resource and replace the source pane with the rendered content, turning this:

Unrendered Markdown

into this:

Rendered Markdown

It also works on any Stash source view.

With JQuery already on the page to do the DOM manipulation and handle the ajax requests, and a REST end-point to do all the heavy lifting, there’s really not much to it:

    /* grab the URL of the source file being viewed from the DOM */
    var sourceLink = $(".source-view-link:visible").attr("href"); /* pull request view */    
    if (!sourceLink) {
        sourceLink = $(".raw-view-link:visible").attr("href"); /* source view */
        if (!sourceLink) {
            alert("No source here.");
    /* we want the raw, undecorated content of the file */
    var rawLink = sourceLink + "&raw";

    var errorHandler = function(xhr, status, thrown) {
        /* happy path fo' lyfe */
        alert("error: " + status + " " + thrown);

    /* function for rendering markdown content via the markup renderer resource */
    var render = function(markdown) {
        $.ajax(AJS.contextPath() + "/rest/api/1.0/markup/preview", {
            type: "POST",
            success: function(data) {
                /* inject the rendered content into the view */
            error: errorHandler,
            data: markdown,
            contentType: "application/json",
            processData: false

    /* get the raw content and pass it to the markup renderer */
    $.ajax(rawLink, {
        success: render,
        error: errorHandler

BAM, instant productivity win!

To use it yourself, copy the following minified version as a new bookmark in your browser. I’ve tested it against Stash 2.1.0.

javascript:(function(){var c=$('.source-view-link:visible').attr('href');if(!c){c=$('.raw-view-link:visible').attr('href');if(!c){alert('No source here.');return}}var d=c+'&raw';var a=function(g,e,f){alert('error: '+e+' '+f)};var b=function(e){$.ajax(AJS.contextPath()+'/rest/api/1.0/markup/preview',{type:'POST',success:function(f){$('.source-container:visible').html(f.html)},error:a,data:e,contentType:'application/json',processData:false})};$.ajax(d,{success:b,error:a})}());

About Tim Pettersen

I'm a veteran Atlassian developer with almost a decade of service across the JIRA and Bitbucket teams. I speak and blog about developer workflows, Git, CI/CD, Java, and Atlassian's developer tools. Talk to me about plugin architecture, Node.js, Java, DVCS, or anything cool that you're hacking on!

View all posts by Tim Pettersen »