Skip to content

Devdocs ​

Writing the manifest ​

When the script is called without arguments, it must return a json manifest describing the extension and its commands.

We will use the jq to generate this manifest. jq is a command-line JSON processor. It is available on most linux distributions, and can be installed on macOS using homebrew.

If you are not familiar with jq, I recommend reading this tutorial: https://earthly.dev/blog/jq-select/

Note that the script must be executable, and must have a shebang line at the top indicating the interpreter to use.

sh
#!/bin/sh

set -eu

# When the number of arguments is 0, return the manifest
if [ $# -eq 0 ]; then
    jq -n '{
        title: "Devdocs",
        description: "Search the devdocs.io documentation",
        commands: [
            {
                name: "search-docsets",
                title: "Search docsets",
                mode: "filter"
            }
        ]
    }'
    exit 0
fi

echo "Not implemented" >&2
exit 1

Here we define a single command named search-docsets, with a mode set to filter.

We can run our script to see the generated manifest:

console
$ ./devdocs.sh
{
  "commands": [
    {
      "mode": "filter",
      "name": "search-docsets",
      "title": "Search docsets"
    }
  ],
  "description": "Search the devdocs.io documentation",
  "title": "Devdocs"
}

And use the sunbeam validate command to validate the manifest:

console
$ ./devdocs.sh | sunbeam validate manifest
✅ Manifest is valid!

Now that we have a valid manifest, we can install the extension.

console
sunbeam extension install ./devdocs.sh

We can now run sunbeam devdocs --help to see the generated help, and sunbeam devdocs to list the root commands (defined in the root field of the manifest).

Handling commands ​

When the user run a command, the script is called with the command name as first argument. Let's implement the search-docsets command.

The search-docsets command has a filter mode, so the script must return a valid list when called with this argument.

sh
#!/bin/sh

set -eu

# When the number of arguments is 0, return the manifest
if [ $# -eq 0 ]; then
    jq -n '{
        title: "Devdocs",
        description: "Search the devdocs.io documentation",
        commands: [
            {
                name: "search-docsets",
                title: "Search docsets",
                mode: "filter"
            }
        ]
    }'
    exit 0
fi

# extract the command name from the payload passed as first argument to the script
COMMAND=$(echo "$1" | jq -r '.command')

# When the command name is "search-docsets", the list of docsets is returned
if [ "$COMMAND" = "search-docsets" ]; then
  curl https://devdocs.io/docs/docs.json | jq '{
    items: map({
      title: .name,
      subtitle: (.release // "latest"),
      accessories: [ .slug ],
      actions: [
        {
            title: "Open in Browser",
            type: "open",
            url: "https://devdocs.io/\(.slug)",
        }
      ]
    })
  }'
fi

Here we pipe the json catalog to the query command to transform it into a list, with each docset as an item. We also add an action to open the docset in the user default browser.

Let's run our script to see the generated view:

console
sunbeam devdocs search-docsets

Chaining Commands ​

This extension is still pretty basic. Sunbeam really shines when you start chaining commands together. Let's add a new command to search the documentation of a specific docset.

sh
#!/bin/sh

set -eu

if [ $# -eq 0 ]; then
    jq -n '{
        title: "Devdocs",
        description: "Search the devdocs.io documentation",
        commands: [
            {
                name: "search-docsets",
                title: "Search docsets",
                mode: "filter"
            },
            {
                name: "search-entries",
                title: "Search entries",
                mode: "filter",
                params: [
                    {
                        name: "docset",
                        type: "string",
                        title: "Docset Slug"
                    }
                ]
            }
        ]
    }'
    exit 0
fi

COMMAND=$(echo "$1" | jq -r '.command')
if [ "$COMMAND" = "search-docsets" ]; then
    # ...
elif [ "$COMMAND" = "search-entries" ]; then
    # we extract the slug param from the payload
    DOCSET=$(echo "$1" | jq -r '.params.docset')

    curl "https://devdocs.io/docs/$DOCSET/index.json" | jq --arg docset "$DOCSET" '.entries | {

        items: map({
            title: .name,
            subtitle: .type,
            actions: [
                {
                    title: "Open in Browser",
                    type: "open",
                    url: "https://devdocs.io/\($docset)/\(.path)",
                },
                {
                    type: "copy",
                    title: "Copy URL",
                    key: "c",
                    text: "https://devdocs.io/\($docset)/\(.path)",
                }
            ]
        })
    }'
fi

Here we add a new command named search-entries, with a filter mode. We also add a docset parameter, which is required.

If we run sunbeam devdocs --help again, we can see the new command help:

console
$ sunbeam devdocs --help
Search the devdocs.io documentation

Usage:
  extension [flags]
  extension [command]

Available Commands:
  search-docsets Search docsets
  search-entries Search entries

Flags:
  -h, --help   help for extension

Use "extension [command] --help" for more information about a command.

Let's run the command to see the generated view:

console
sunbeam devdocs search-entries --docset=go

If we want to be able to go from the docsets list to the entries list, we can add run action to the docsets list:

sh
#!/bin/sh

set -eu

# When the number of arguments is 0, return the manifest
if [ $# -eq 0 ]; then
    ...
fi

if [ "$1" = "search-docsets" ]; then
    curl https://devdocs.io/docs/docs.json | jq '{
        items: map({
          title: .name,
          subtitle: (.release // "latest"),
          accessories: [ .slug ],
          actions: [
            {
                type: "run",
                title: "Search \(.name) entries",
                command: "search-entries",
                params: {
                    docset: .slug
                }
            },
            {
                type: "open",
                title: "Open in Browser",
                url: "https://devdocs.io/\(.slug)",
                exit: true
            }
          ]
        })
    }'
elif [ "$1" = "search-entries" ]; then
    # ...
fi

Now we can start by listing the docsets, select the one we are interested in, and then search the entries of this docset.

Adding new root items ​

When we installed the extension using sunbeam extension install ./devdocs.sh, an entry was added to the extensions map in ~/.config/sunbeam/sunbeam.json.

json
{
  "extensions": {
    "devdocs": {
      "origin": "<path-to-extension>/devdocs.sh"
    }
  }
}

As a user of the extension, we can add shortcuts to specific docsets:

json
{
  "extensions": {
    "devdocs": {
      "origin": "<path-to-extension>/devdocs.sh",
      "root": [
        {
          "title": "Search Go documentation",
          "command": "search-entries",
          "params": {
            "docset": "go"
          }
        }
      ]
    }
  }
}

Each time you add a new extension to sunbeam, you gain access to new commands, and you can add new shortcuts to your config file.

ℹ️ The source code of this extension is available here: https://github.com/pomdtr/sunbeam-devdocs/blob/main/sunbeam-extension.