My first extension to Fig
Tags: fig, macos, opensource, contribution
Published: 2022-12-11 15:14:43

---
title: My first extension to Fig
published: true
description: defaultbrowser extension to Fig contributed
tags: fig, macos, opensource, contribution
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2022-12-11 15:13 +0000
---
I have seen quite a few recommendations on dev.to for [Fig](https://fig.io/), the command line completion tool for macOS.

With macOS 10.15 "Catalina" [Apple changed the default shell from Bash to Zsh](http://jonasbn.github.io/til/osx/translate_release_names.html) and I decided to follow along, which meant that some of the command line [completions I had implemented for Bash](https://github.com/jonasbn?tab=repositories&q=bash_completion) was no longer of any use to me and I had to look into completions for **Zsh**. 

Since I had a clean slate, I decided to give **Fig** a shot.

**Fig** is working on top of the shells so it supports both Bash and Zsh and it has a lot of completions in addition to looking very nice. After some time with **Zsh** and **Fig** I have started to find out what completions I miss from my old setup. I will start to look into getting these implemented some way or the other.

First shot was for [defaultbrowser](https://github.com/kerma/defaultbrowser) a marvellous command line tool, which let's you change your choice of default browser from the command line. I had previously done [a completion for Bash](https://github.com/jonasbn/bash_completion_defaultbrowser) in Bash, which was quite simple, but did the job and worked fine.

Fig's completions are implemented in Typescript, but there is lot of documentation and several examples and articles available online.

This is my final implementation for [my PR](https://github.com/withfig/autocomplete/pull/1726), which has been accepted and merged

```typescript
const getInstalledBrowsers: Fig.Generator = {
  script: "defaultbrowser",
  postProcess: function (out) {
    return out.split("\n").map((line) => {
      /* We ignore the already set browser */
      if (line.startsWith("*")) {
        return {};
      }
      const browserName = line.trim();
      return {
        name: browserName,
      };
    });
  },
};

const completionSpec: Fig.Spec = {
  name: "defaultbrowser",
  description: "Change your default browser from the CLI",
  args: { isOptional: true, generators: getInstalledBrowsers },
};

export default completionSpec;
```

Reading the code from the bottom up.

```typescript
export default completionSpec;
```

Exports the completion specification.

```typescript
const completionSpec: Fig.Spec = {
  name: "defaultbrowser",
  description: "Change your default browser from the CLI",
  args: { isOptional: true, generators: getInstalledBrowsers },
};
```

Implements the completion specification.

- It specifies name
- It provides a description
- It specifies the arguments
  - The arguments are optional, since we can call defaultbrowser withouyt arguments, this lists the detected HTTP handlers
  - It points to a generator, which generates the suggestions for arguments, pointing to our `getInstalledBrowsers`

The final function `getInstalledBrowsers` is the actual generator. In the case it is a wrapper on `defaultbrowser` itself, since `defaultbrowser` emits a list of detected HTTP handlers (browsers), when not provided with an argument.

```typescript
const getInstalledBrowsers: Fig.Generator = {
  script: "defaultbrowser",
  postProcess: function (out) {
    return out.split("\n").map((line) => {
      /* We ignore the already set browser */
      if (line.startsWith("*")) {
        return {};
      }
      const browserName = line.trim();
      return {
        name: browserName,
      };
    });
  },
};
```

So over all we need to:

1. Support no arguments
2. Support suggestions

We do not want to suggest the browser already set, so we need to filter this out. In the output the set browser is prefixed with a `*`, so we can filter on that.

Handling of no arguments was specified in the completion specfication.

One thing that was teasing me was that many of the examples I looks at had the following line in their generator:

```typescript
  splitOn: "\n",
```

I removed that before submitting my PR, since I found out that I was already splitting the line in my implementation, so where was not need to do this twice.

The completion works as follows:

1. Enter `defaultbrowser`
1. A list of detected HTTP handlers are suggested via Fig
1. You click escape and no argument is provided to `defaultbrowser`
1. You pick one from the list using Fig
1. This opens a dialog from the OS and you have to click "Use X" in order to change the setting

The PR has been accepted, meaning that the implementation should become available to you if you use **Fig** at some point. I do have the following points I am thinking about.

- Can I add a description to assist the listing of available browsers, so the completion becomes more user friendly. Since the list is dynamic and it will somewhat vary from installation to installation, I am not sure I can do this in a good way
- Should I look into implementing a _pure_ Zsh completion, I have skimmed the documentation and it seem doable, Zsh however is very complex and since I do not use it directly, since I use **Fig** I would not be _eating my own dog food_, but it would be a good learning experience, this might just not be the best use case for me personally at this time
- `defaultbrowser` has a challenge that you have to interact with a pop-up, to accept the change of default browser, so you have to interact outside the CLI, which is annoying, perhaps some effort should be put into that