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;
Reading the code from the bottom up.
export default completionSpec;
Exports the completion specification.
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.
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:
- Support no arguments
- 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",
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:
- Enter
defaultbrowser
- A list of detected HTTP handlers are suggested via Fig
- You click escape and no argument is provided to
defaultbrowser
- You pick one from the list using Fig
- 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