My first extension to Fig

Jonas Brømsø - Dec 11 '22 - - Dev Community

I have seen quite a few recommendations on dev.to for Fig, the command line completion tool for macOS.

With macOS 10.15 "Catalina" Apple changed the default shell from Bash to Zsh and I decided to follow along, which meant that some of the command line completions I had implemented for Bash 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 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 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, which has been accepted and merged

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;
Enter fullscreen mode Exit fullscreen mode

Reading the code from the bottom up.

export default completionSpec;
Enter fullscreen mode Exit fullscreen mode

Exports the completion specification.

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

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.

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,
      };
    });
  },
};
Enter fullscreen mode Exit fullscreen mode

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:

  splitOn: "\n",
Enter fullscreen mode Exit fullscreen mode

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
  2. A list of detected HTTP handlers are suggested via Fig
  3. You click escape and no argument is provided to defaultbrowser
  4. You pick one from the list using Fig
  5. 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
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .