-
Notifications
You must be signed in to change notification settings - Fork 1
/
completion.ts
167 lines (144 loc) · 4.99 KB
/
completion.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// deno-lint-ignore-file no-explicit-any no-explicit-any
import { textEncoder } from "./lib/text-encoder.ts";
import { CommandFactory } from "./init.ts";
import { flag, Flags, flags } from "./flags.ts";
import * as bash from "./completions/bash.ts";
import * as fish from "./completions/fish.ts";
import * as zsh from "./completions/zsh.ts";
import { CommandConfig, DefaultContext } from "./command.ts";
import { writeIterable } from "./lib/write-iterable.ts";
const shellCommandFlags = flags({
"no-descriptions": flag({
short: "Disable completion descriptions",
}).boolean().default(false),
});
export function completion<
Context extends Record<string, unknown>,
GlobalOpts extends Flags,
>(
commandFactory: CommandFactory<Context, GlobalOpts>,
options:
& {
/**
* Change the name of the command
* @default "completion"
*/
name?: string;
}
& Pick<
CommandConfig<Context & DefaultContext, any, any>,
"aliases" | "short" | "long" | "use" | "hidden"
> = {},
) {
const { name = "completion", ...config } = options;
const command = commandFactory.command(name, {
short: "Generate an autocompletion script for the specified shell",
long: ({ root }) => `
Generate an autocompletion script for ${root.name} in the specified shell.
See each sub-command's help for details on how to use the generated script.
`,
...config,
commands: [
commandFactory.command("bash", {
short: "Generate an autocompletion script for the bash shell",
long: ({ root, path }) => `
Generate the autocompletion script for the bash shell.
This script depends on the \`bash-completion\` package.
If it is not installed already, you can install it via your OS's package manager.
To load completions in your current shell session:
\`\`\`
$ source <(${path.join(" ")} bash)
\`\`\`
To load completions for every new session, execute once:
Linux:
\`\`\`
$ ${path.join(" ")} bash > /etc/bash_completion.d/${root.name}
\`\`\`
macOS:
\`\`\`
$ ${
path.join(" ")
} bash > /usr/local/etc/bash_completion.d/${root.name}
\`\`\`
You will need to start a new shell for this setup to take effect.
`,
}).run(function ({ ctx }) {
write(bash.complete(ctx.root));
}),
commandFactory.command("zsh", {
short: "Generate an autocompletion script for the zsh shell",
long: ({ root, path }) => `
Generate the autocompletion script for the zsh shell.
If shell completion is not already enabled in your environment you will need
to enable it. You can execute the following once:
\`\`\`
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
\`\`\`
To load completions for every new session, execute once:
Linux:
\`\`\`
$ ${path.join(" ")} zsh > "\${fpath[1]}/_${root.name}"
\`\`\`
macOS:
\`\`\`
$ ${
path.join(" ")
} zsh > /usr/local/share/zsh/site-functions/_${root.name}
\`\`\`
Oh My Zsh:
\`\`\`
$ ${path.join(" ")} zsh > ~/.oh-my-zsh/completions/_${root.name}
\`\`\`
You will need to start a new shell for this setup to take effect.
`,
flags: shellCommandFlags,
}).run(function ({ flags, ctx }) {
write(
zsh.complete(ctx.root, {
ctx,
// @ts-expect-error: it's fine
disableDescriptions: flags["no-descriptions"],
}),
);
}),
commandFactory.command("fish", {
short: "Generate an autocompletion script for the fish shell",
long: ({ root, path }) => `
Generate the autocompletion script for the fish shell.
To load completions in your current shell session:
\`\`\`
$ ${path.join(" ")} fish | source
\`\`\`
To load completions for every new session, execute once:
\`\`\`
$ ${
path.join(" ")
} fish > ~/.config/fish/completions/${root.name}.fish
\`\`\`
You will need to start a new shell for this setup to take effect.
`,
flags: shellCommandFlags,
}).run(function ({ flags, ctx }) {
write(
fish.complete(ctx.root, {
ctx,
// @ts-expect-error: it's fine
disableDescriptions: flags["no-descriptions"],
}),
);
}),
],
})
.run(async ({ ctx }) => {
await writeIterable(command.help(ctx as any));
});
return command;
}
export async function write(stream: Iterable<string>): Promise<void> {
const writes: Promise<number>[] = [];
for (const line of stream) {
writes.push(Deno.stdout.write(textEncoder.encode(line + "\n")));
}
await Promise.all(writes);
Deno.exit(0);
}