@jals1212/pi-spaces-cli 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/README.md +10 -3
- package/dist/cli.js +273 -27
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,43 @@ commands and options and a patch version fixes.
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## 0.6.2
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- `pi-space --help <TAB>` (and `-h`) offers the commands with their summary,
|
|
14
|
+
`completion` included, so the help of a command is one Tab away.
|
|
15
|
+
- `--every` is completed in its own `target` group instead of among the space
|
|
16
|
+
names, since it is not a space.
|
|
17
|
+
- The completion text of the first `--help` says it also takes a command:
|
|
18
|
+
`print the help, or a command's: --help <command>`.
|
|
19
|
+
|
|
20
|
+
## 0.6.1
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- `pi-space <command> --help` (also `-h`, and `pi-space --help <command>`)
|
|
25
|
+
prints the help of one command: what it does, its arguments and options, the
|
|
26
|
+
call options that apply to it, and examples. `--help` anywhere after the
|
|
27
|
+
command word counts, so `pi-space remove work --help` prints the help and
|
|
28
|
+
removes nothing. After a space name it still belongs to Pi:
|
|
29
|
+
`pi-space work --help` starts Pi with `--help`. An unknown command exits with
|
|
30
|
+
status 2. It reads no config file and starts nothing.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- Completion groups its candidates (commands, spaces, profiles, options) and
|
|
35
|
+
describes them: each command shows a one-line summary, `base` and the spaces
|
|
36
|
+
are told apart, a profile shows its command, and `remove` offers `--yes` and
|
|
37
|
+
`--no-backup`. `--help` is offered, as the options are, once the word starts
|
|
38
|
+
with `-`. The hidden `__complete` prints `<group>\t<value>\t<description>`
|
|
39
|
+
lines instead of bare words.
|
|
40
|
+
- `pi-space -<TAB>` offers the call options not typed yet (`--profile`,
|
|
41
|
+
`--dir`, `--config`, `--command`, `--setup`), and `--help` and `--version`
|
|
42
|
+
when nothing comes before.
|
|
43
|
+
- Completion no longer offers `completion` among the commands; it still works
|
|
44
|
+
typed in full, and `pi-space completion <TAB>` still offers `zsh`.
|
|
45
|
+
|
|
9
46
|
## 0.6.0
|
|
10
47
|
|
|
11
48
|
### Added
|
package/README.md
CHANGED
|
@@ -28,9 +28,12 @@ The command words are reserved: a space can never be named `list`, `new`, `remov
|
|
|
28
28
|
| `pi-space install <name\|--every> <source> [pi install options...]` | Runs Pi's `install` in a space. The source is required. |
|
|
29
29
|
| `pi-space uninstall <name\|--every> <source> [pi options...]` | Runs Pi's `uninstall` (its alias of `remove`) in a space. The source is required. |
|
|
30
30
|
| `pi-space completion zsh` | Prints the zsh completion script (see [Shell completion](#shell-completion)). Any other shell, or none, exits with status 2. |
|
|
31
|
+
| `pi-space <command> --help` | Shows the help of one command: what it does, its arguments and options, the call options that apply, and examples (`pi-space --help <command>` and `-h` work too). |
|
|
31
32
|
| `pi-space --help` | Shows the usage. |
|
|
32
33
|
| `pi-space --version` | Prints the version. |
|
|
33
34
|
|
|
35
|
+
`--help` (or `-h`) anywhere after a command word shows that command's help and does nothing else: `pi-space remove work --help` removes nothing, and it reads no config file. After a space name every argument belongs to Pi, so `pi-space work --help` starts Pi with `--help`. An unknown command (`pi-space --help ghost`) exits with status 2 and lists the commands.
|
|
36
|
+
|
|
34
37
|
`--new` and `--remove`, the options of 0.3, were replaced by `new` and `remove`; they exit with status 2 and name the command to use.
|
|
35
38
|
|
|
36
39
|
### Packages in a space, or in every space
|
|
@@ -160,7 +163,7 @@ alias my-space='pi-space --profile my' # any POSIX shell
|
|
|
160
163
|
|
|
161
164
|
## Shell completion
|
|
162
165
|
|
|
163
|
-
`pi-space` completes its commands, the profile names after `--profile`, files after `--config` and `--dir`, and the spaces. For zsh, add to `~/.zshrc` (after `compinit`):
|
|
166
|
+
`pi-space` completes its commands (each with a one-line summary), the profile names after `--profile`, files after `--config` and `--dir`, and the spaces. For zsh, add to `~/.zshrc` (after `compinit`):
|
|
164
167
|
|
|
165
168
|
```sh
|
|
166
169
|
source <(pi-space completion zsh)
|
|
@@ -170,14 +173,18 @@ It asks the command itself for the candidates each time, through the hidden `pi-
|
|
|
170
173
|
|
|
171
174
|
| After | Candidates |
|
|
172
175
|
| --- | --- |
|
|
173
|
-
| `pi-space` (and the call options) | the commands, `base` and the spaces |
|
|
174
|
-
| `--profile` | the profile names, `pi` included |
|
|
176
|
+
| `pi-space` (and the call options) | the commands, then `base` and the spaces (`completion` is not offered, but works typed in full) |
|
|
177
|
+
| `--profile` | the profile names, `pi` included, each with its command |
|
|
175
178
|
| `--config`, `--dir` | files (the shell's own completion) |
|
|
176
179
|
| `remove`, `rename` | the spaces |
|
|
177
180
|
| `setup` | the spaces and `--every` |
|
|
178
181
|
| `list`, `update`, `install`, `uninstall` | `base`, the spaces and `--every` |
|
|
179
182
|
| `completion` | `zsh` |
|
|
180
183
|
|
|
184
|
+
In first place the call options not typed yet come too, and `--help` and `--version` when nothing comes before. After a command word the options come too: `--help` for every command, and `--yes` and `--no-backup` after `remove`. zsh shows them once the word starts with `-`, so `pi-space new <TAB>` never fills in a lone `--help`.
|
|
185
|
+
|
|
186
|
+
The candidates come in groups, each with a description, and zsh lists each group apart: commands, spaces (`base` is "the home Pi starts with"), profiles, shells and options. The hidden `pi-space __complete` prints one `<group>\t<value>\t<description>` line per candidate (or the single line `__files__` when the shell should complete a file), so a script for another shell can use it.
|
|
187
|
+
|
|
181
188
|
Completion also works through an abbreviation or alias like the one above: they expand first, so `my-space <TAB>` completes what `pi-space --profile my <TAB>` would. A config file that is wrong, or a profile that does not exist, just gives no candidates; completion never prints an error.
|
|
182
189
|
|
|
183
190
|
## Preparing a new space
|
package/dist/cli.js
CHANGED
|
@@ -51,6 +51,7 @@ export const USAGE = `Usage: pi-space [--command <cmd>] [--dir <path>] [--setup
|
|
|
51
51
|
pi-space install <name|--every> <source> install a package in a space (more words go to Pi)
|
|
52
52
|
pi-space uninstall <name|--every> <source> remove a package from a space (more words go to Pi)
|
|
53
53
|
pi-space completion zsh print the zsh completion script (source <(pi-space completion zsh))
|
|
54
|
+
pi-space <command> --help the help of one command
|
|
54
55
|
pi-space --help print this help
|
|
55
56
|
pi-space --version print the version
|
|
56
57
|
--every takes the place of the name: base first (not for setup), then every space in turn; a failure does not stop the rest, and the exit status is 1 if any failed.
|
|
@@ -90,6 +91,7 @@ const REPLACED = {
|
|
|
90
91
|
|
|
91
92
|
|
|
92
93
|
|
|
94
|
+
|
|
93
95
|
|
|
94
96
|
|
|
95
97
|
|
|
@@ -109,6 +111,161 @@ const REPLACED = {
|
|
|
109
111
|
|
|
110
112
|
const CALL_OPTIONS = { "--command": "command", "--dir": "dir", "--setup": "setup", "--profile": "profile", "--config": "config" } ;
|
|
111
113
|
|
|
114
|
+
function isHelpFlag(word ) {
|
|
115
|
+
return word === "--help" || word === "-h";
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** What each call option is, for the help of the commands it applies to. */
|
|
119
|
+
const CALL_HELP = {
|
|
120
|
+
command: ["--command <cmd>", 'the program to run instead of $PI_SPACES_COMMAND (default: pi), e.g. "my-launcher --"'],
|
|
121
|
+
dir: ["--dir <path>", "the spaces folder instead of $PI_SPACES_DIR; the Pi it starts gets it too"],
|
|
122
|
+
setup: ["--setup <cmd>", "the setup command instead of $PI_SPACES_SETUP; {home} and {name} become the space's folder and name"],
|
|
123
|
+
profile: ["--profile <name>", 'the profile of the config file instead of its "default"'],
|
|
124
|
+
config: ["--config <path>", "the config file instead of $PI_SPACES_CONFIG (default: ~/.pi-spaces/config.json)"],
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/** The help of one command; the summary is the one line completion shows next to it. */
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
const PI_ARGS = ["[pi args...]", "more words go to Pi, e.g. --extensions, --all or a source"];
|
|
143
|
+
const NAME_OR_EVERY = [
|
|
144
|
+
["<name>", "base or a space"],
|
|
145
|
+
["--every", "base first, then every space in turn; a failure does not stop the rest, and the exit status is 1 if any failed"],
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
/** The commands, in the order completion lists them. They are reserved words: the core never lets a space be named one. */
|
|
149
|
+
const COMMANDS = {
|
|
150
|
+
list: {
|
|
151
|
+
summary: "list the spaces, or the packages of one",
|
|
152
|
+
usage: "list [<name|--every>]",
|
|
153
|
+
about: ["With a name it runs Pi's list in that space; with --every, in base and then every space."],
|
|
154
|
+
args: NAME_OR_EVERY,
|
|
155
|
+
options: [],
|
|
156
|
+
calls: ["command", "dir", "profile", "config"],
|
|
157
|
+
examples: ["pi-space list", "pi-space list work", "pi-space list --every"],
|
|
158
|
+
},
|
|
159
|
+
new: {
|
|
160
|
+
summary: "create a space",
|
|
161
|
+
usage: "new <name>",
|
|
162
|
+
about: ["Its settings.json declares the extension. It runs the setup command in it when there is one, and does not start Pi."],
|
|
163
|
+
args: [["<name>", "the name of the new space"]],
|
|
164
|
+
options: [],
|
|
165
|
+
calls: ["dir", "setup", "profile", "config"],
|
|
166
|
+
examples: ["pi-space new work", "pi-space --setup 'my-launcher --home {home} setup' new work"],
|
|
167
|
+
},
|
|
168
|
+
setup: {
|
|
169
|
+
summary: "run the setup command in a space",
|
|
170
|
+
usage: "setup <name|--every>",
|
|
171
|
+
about: ["It needs a setup command: --setup or $PI_SPACES_SETUP. It never runs in base."],
|
|
172
|
+
args: [
|
|
173
|
+
["<name>", "a space"],
|
|
174
|
+
["--every", "every space, never base; a failure does not stop the rest, and the exit status is 1 if any failed"],
|
|
175
|
+
],
|
|
176
|
+
options: [],
|
|
177
|
+
calls: ["dir", "setup", "profile", "config"],
|
|
178
|
+
examples: ["pi-space setup work", "pi-space setup --every", "pi-space --setup 'my-launcher --home {home} setup' setup --every"],
|
|
179
|
+
},
|
|
180
|
+
rename: {
|
|
181
|
+
summary: "rename a space",
|
|
182
|
+
usage: "rename <old> <new>",
|
|
183
|
+
about: ["It refuses what remove refuses: base, the space you are in, and a space that holds the base home."],
|
|
184
|
+
args: [
|
|
185
|
+
["<old>", "the space to rename"],
|
|
186
|
+
["<new>", "its new name"],
|
|
187
|
+
],
|
|
188
|
+
options: [],
|
|
189
|
+
calls: ["dir", "profile", "config"],
|
|
190
|
+
examples: ["pi-space rename work client"],
|
|
191
|
+
},
|
|
192
|
+
remove: {
|
|
193
|
+
summary: "delete a space, keeping a backup",
|
|
194
|
+
usage: "remove <name> [--yes|-y] [--no-backup]",
|
|
195
|
+
about: ["It deletes the space and everything in it, after asking, and keeps a backup in .backups unless told not to."],
|
|
196
|
+
args: [["<name>", "the space to delete"]],
|
|
197
|
+
options: [
|
|
198
|
+
{ flag: "--yes", alias: "-y", text: "do not ask; keep a backup" },
|
|
199
|
+
{ flag: "--no-backup", text: "delete without a backup" },
|
|
200
|
+
],
|
|
201
|
+
calls: ["dir", "profile", "config"],
|
|
202
|
+
examples: ["pi-space remove work", "pi-space remove work --yes", "pi-space remove work --yes --no-backup"],
|
|
203
|
+
},
|
|
204
|
+
update: {
|
|
205
|
+
summary: "update the packages of a space",
|
|
206
|
+
usage: "update <name|--every> [pi args...]",
|
|
207
|
+
about: ["It runs Pi's update in a space, or in base and then every space."],
|
|
208
|
+
args: [...NAME_OR_EVERY, PI_ARGS],
|
|
209
|
+
options: [],
|
|
210
|
+
calls: ["command", "dir", "profile", "config"],
|
|
211
|
+
examples: ["pi-space update work", "pi-space update base --extensions", "pi-space --command 'my-launcher --' update --every"],
|
|
212
|
+
},
|
|
213
|
+
install: {
|
|
214
|
+
summary: "install a package in a space",
|
|
215
|
+
usage: "install <name|--every> <source> [pi args...]",
|
|
216
|
+
about: ["It runs Pi's install in a space, or in base and then every space."],
|
|
217
|
+
args: [...NAME_OR_EVERY, ["<source>", "the package to install, e.g. npm:some-package"], PI_ARGS],
|
|
218
|
+
options: [],
|
|
219
|
+
calls: ["command", "dir", "profile", "config"],
|
|
220
|
+
examples: ["pi-space install work npm:some-package", "pi-space install --every npm:some-package"],
|
|
221
|
+
},
|
|
222
|
+
uninstall: {
|
|
223
|
+
summary: "remove a package from a space",
|
|
224
|
+
usage: "uninstall <name|--every> <source> [pi args...]",
|
|
225
|
+
about: ["It runs Pi's uninstall (its alias of remove) in a space, or in base and then every space."],
|
|
226
|
+
args: [...NAME_OR_EVERY, ["<source>", "the package to remove, e.g. npm:some-package"], PI_ARGS],
|
|
227
|
+
options: [],
|
|
228
|
+
calls: ["command", "dir", "profile", "config"],
|
|
229
|
+
examples: ["pi-space uninstall work npm:some-package", "pi-space uninstall --every npm:some-package"],
|
|
230
|
+
},
|
|
231
|
+
completion: {
|
|
232
|
+
summary: "print the shell completion script",
|
|
233
|
+
usage: "completion <shell>",
|
|
234
|
+
about: ["Supported shells: zsh."],
|
|
235
|
+
args: [["<shell>", "zsh"]],
|
|
236
|
+
options: [],
|
|
237
|
+
calls: [],
|
|
238
|
+
examples: ["pi-space completion zsh", "source <(pi-space completion zsh)"],
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
/** --help after a command; GENERAL_OPTIONS has the --help that comes first. */
|
|
243
|
+
const HELP_OPTION = { flag: "--help", alias: "-h", text: "the help of this command" };
|
|
244
|
+
|
|
245
|
+
/** The options that stand alone in first place, as completion describes them. */
|
|
246
|
+
const GENERAL_OPTIONS = [
|
|
247
|
+
["--help", "print the help, or a command's: --help <command>"],
|
|
248
|
+
["--version", "print the version"],
|
|
249
|
+
];
|
|
250
|
+
|
|
251
|
+
/** The help of one command: usage, what it does, arguments, options, the call options that apply, and examples. */
|
|
252
|
+
function commandHelp(name ) {
|
|
253
|
+
const help = COMMANDS[name];
|
|
254
|
+
const options = [...help.options, HELP_OPTION].map((option) => [option.alias ? `${option.flag}, ${option.alias}` : option.flag, option.text]);
|
|
255
|
+
const calls = help.calls.map((call) => CALL_HELP[call]);
|
|
256
|
+
const width = Math.max(...[...help.args, ...options, ...calls].map(([label]) => label.length)) + 2;
|
|
257
|
+
const rows = (list ) => list.map(([label, text]) => ` ${label.padEnd(width)}${text}`);
|
|
258
|
+
const sections = [
|
|
259
|
+
[`Usage: pi-space ${help.usage}`],
|
|
260
|
+
[`${help.summary[0].toUpperCase()}${help.summary.slice(1)}.`, ...help.about],
|
|
261
|
+
["Arguments:", ...rows(help.args)],
|
|
262
|
+
["Options:", ...rows(options)],
|
|
263
|
+
...(calls.length > 0 ? [["Call options (before the command, for this call only):", ...rows(calls)]] : []),
|
|
264
|
+
["Examples:", ...help.examples.map((example) => ` ${example}`)],
|
|
265
|
+
];
|
|
266
|
+
return `${sections.map((section) => section.join("\n")).join("\n\n")}\n`;
|
|
267
|
+
}
|
|
268
|
+
|
|
112
269
|
/**
|
|
113
270
|
* The call options (--command, --dir, --setup, --profile, --config) come first; after a space name every argument belongs to Pi.
|
|
114
271
|
* The commands (list, new, setup, remove, rename, update, install, uninstall, completion) are reserved words, so they
|
|
@@ -134,10 +291,12 @@ export function parseCli(args ) {
|
|
|
134
291
|
function parseCommand(args ) {
|
|
135
292
|
const [first, ...rest] = args;
|
|
136
293
|
if (first === undefined) return { kind: "picker" };
|
|
137
|
-
if (first
|
|
294
|
+
if (isHelpFlag(first)) return parseHelp(rest);
|
|
138
295
|
if (first === "--version" || first === "-v") return { kind: "version" };
|
|
139
296
|
if (Object.hasOwn(REPLACED, first)) return { kind: "replaced", option: first, instead: REPLACED[first ] };
|
|
140
297
|
if (first.startsWith("-")) return { kind: "unknown-option", option: first };
|
|
298
|
+
// --help after a command word is that command's help; after a space name it belongs to Pi (the launch below).
|
|
299
|
+
if (Object.hasOwn(COMMANDS, first) && rest.some(isHelpFlag)) return { kind: "command-help", command: first };
|
|
141
300
|
if (first === "list" && rest.length === 0) return { kind: "list" };
|
|
142
301
|
if (PACKAGE_VERBS.includes(first)) return parsePackages(first , rest);
|
|
143
302
|
if (first === "new") return rest.length === 1 ? { kind: "new", name: rest[0] } : { kind: "usage" };
|
|
@@ -156,6 +315,13 @@ function parseCommand(args ) {
|
|
|
156
315
|
return { kind: "launch", name: first, args: rest };
|
|
157
316
|
}
|
|
158
317
|
|
|
318
|
+
/** `--help` alone is the general help; `--help <command>` is that command's. */
|
|
319
|
+
function parseHelp(rest ) {
|
|
320
|
+
if (rest.length === 0) return { kind: "help" };
|
|
321
|
+
if (rest.length === 1) return { kind: "command-help", command: rest[0] };
|
|
322
|
+
return { kind: "usage" };
|
|
323
|
+
}
|
|
324
|
+
|
|
159
325
|
/** `setup <name>` or `setup --every`: exactly one argument. */
|
|
160
326
|
function parseSetup(rest ) {
|
|
161
327
|
if (rest.length !== 1) return { kind: "usage" };
|
|
@@ -386,6 +552,13 @@ export async function main(argv , io ) {
|
|
|
386
552
|
case "help":
|
|
387
553
|
io.stdout.write(USAGE);
|
|
388
554
|
return io.exit(0);
|
|
555
|
+
case "command-help":
|
|
556
|
+
if (Object.hasOwn(COMMANDS, command.command)) {
|
|
557
|
+
io.stdout.write(commandHelp(command.command));
|
|
558
|
+
return io.exit(0);
|
|
559
|
+
}
|
|
560
|
+
io.stderr.write(`pi-space: No help for "${command.command}"; commands: ${Object.keys(COMMANDS).join(", ")}.\n`);
|
|
561
|
+
return io.exit(2);
|
|
389
562
|
case "version":
|
|
390
563
|
io.stdout.write(`${VERSION}\n`);
|
|
391
564
|
return io.exit(0);
|
|
@@ -458,28 +631,59 @@ function printCompletion(shell , io ) {
|
|
|
458
631
|
const FILES = "__files__";
|
|
459
632
|
|
|
460
633
|
/**
|
|
461
|
-
* The zsh script: it asks `pi-space __complete <the words before the cursor>` for candidates, one per line
|
|
462
|
-
* and
|
|
463
|
-
*
|
|
634
|
+
* The zsh script: it asks `pi-space __complete <the words before the cursor>` for candidates, one per line as
|
|
635
|
+
* `<group>\t<value>\t<description>`, and hands each group to _describe (a ":" in a value is escaped for it).
|
|
636
|
+
* The `__files__` marker falls back to the shell's file completion. An abbreviation or alias has already
|
|
637
|
+
* expanded by then, so `my-space <TAB>` completes the words of what it stands for.
|
|
638
|
+
* The options show only for a word that starts with "-", so `pi-space new <TAB>` never inserts a lone --help.
|
|
464
639
|
*/
|
|
465
640
|
const ZSH_COMPLETION = [
|
|
466
641
|
"#compdef pi-space",
|
|
467
642
|
"# zsh completion for pi-space. Load it from .zshrc with: source <(pi-space completion zsh)",
|
|
468
643
|
"_pi_space() {",
|
|
469
|
-
" local -a lines",
|
|
644
|
+
" local -a lines cmds spaces targets profiles shells opts",
|
|
645
|
+
" local line group value text ret=1",
|
|
470
646
|
' lines=("${(@f)$(command "${words[1]}" __complete "${(@)words[2,CURRENT-1]}" 2>/dev/null)}")',
|
|
471
647
|
` if [[ "\${lines[1]}" == ${FILES} ]]; then`,
|
|
472
648
|
" _files",
|
|
473
649
|
" return",
|
|
474
650
|
" fi",
|
|
475
|
-
'
|
|
476
|
-
|
|
651
|
+
' for line in "${(@)lines}"; do',
|
|
652
|
+
' [[ -n "$line" ]] || continue',
|
|
653
|
+
" group=${line%%$'\\t'*}",
|
|
654
|
+
" line=${line#*$'\\t'}",
|
|
655
|
+
" value=${line%%$'\\t'*}",
|
|
656
|
+
" text=${line#*$'\\t'}",
|
|
657
|
+
" [[ \"$text\" == \"$line\" ]] && text=",
|
|
658
|
+
" value=${value//:/\\\\:}",
|
|
659
|
+
' case "$group" in',
|
|
660
|
+
' command) cmds+=("$value:$text") ;;',
|
|
661
|
+
' space) spaces+=("$value:$text") ;;',
|
|
662
|
+
' target) targets+=("$value:$text") ;;',
|
|
663
|
+
' profile) profiles+=("$value:$text") ;;',
|
|
664
|
+
' shell) shells+=("$value:$text") ;;',
|
|
665
|
+
' option) [[ "$PREFIX" == -* ]] && opts+=("$value:$text") ;;',
|
|
666
|
+
" esac",
|
|
667
|
+
" done",
|
|
668
|
+
" (( $#cmds )) && _describe -t commands 'command' cmds && ret=0",
|
|
669
|
+
" (( $#spaces )) && _describe -t spaces 'space' spaces && ret=0",
|
|
670
|
+
" (( $#targets )) && _describe -t targets 'target' targets && ret=0",
|
|
671
|
+
" (( $#profiles )) && _describe -t profiles 'profile' profiles && ret=0",
|
|
672
|
+
" (( $#shells )) && _describe -t shells 'shell' shells && ret=0",
|
|
673
|
+
" (( $#opts )) && _describe -t options 'option' opts && ret=0",
|
|
674
|
+
" return ret",
|
|
477
675
|
"}",
|
|
478
676
|
"compdef _pi_space pi-space",
|
|
479
677
|
"",
|
|
480
678
|
].join("\n");
|
|
481
679
|
|
|
482
|
-
|
|
680
|
+
/** The groups of a `__complete` line; `target` is --every, which stands in for a space name. */
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
/** One `__complete` line; the description never holds a tab or a newline. */
|
|
684
|
+
function candidate(group , value , text ) {
|
|
685
|
+
return `${group}\t${value}\t${text.replace(/\s+/g, " ")}`;
|
|
686
|
+
}
|
|
483
687
|
|
|
484
688
|
/**
|
|
485
689
|
* The hidden `__complete <words before the cursor>`: one candidate per line, honoring the call options already
|
|
@@ -496,6 +700,49 @@ function complete(words , io ) {
|
|
|
496
700
|
return io.exit(0);
|
|
497
701
|
}
|
|
498
702
|
|
|
703
|
+
/** The commands with their summary; in first place `completion` is left out (it still works typed in full), after --help it is not. */
|
|
704
|
+
function commandCandidates(withCompletion ) {
|
|
705
|
+
return Object.entries(COMMANDS)
|
|
706
|
+
.filter(([name]) => withCompletion || name !== "completion")
|
|
707
|
+
.map(([name, help]) => candidate("command", name, help.summary));
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/** The call options not typed yet, then --help and --version when nothing was typed before. */
|
|
711
|
+
function firstOptions(call , alone ) {
|
|
712
|
+
const calls = Object.entries(CALL_OPTIONS)
|
|
713
|
+
.filter(([, key]) => call[key] === undefined)
|
|
714
|
+
.map(([flag, key]) => candidate("option", flag, CALL_HELP[key][1]));
|
|
715
|
+
const general = alone ? GENERAL_OPTIONS.map(([flag, text]) => candidate("option", flag, text)) : [];
|
|
716
|
+
return [...calls, ...general];
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/** `base`, if asked, then the spaces. */
|
|
720
|
+
function spaceCandidates(spaces , withBase ) {
|
|
721
|
+
return [...(withBase ? [candidate("space", BASE, "the home Pi starts with")] : []), ...spaces.map((name) => candidate("space", name, "space"))];
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/** What follows a command word: its target, its own options and --help. */
|
|
725
|
+
function commandArguments(command , spaces ) {
|
|
726
|
+
const every = (text ) => candidate("target", "--every", text);
|
|
727
|
+
const options = [...COMMANDS[command].options, HELP_OPTION].map((option) => candidate("option", option.flag, option.text));
|
|
728
|
+
switch (command) {
|
|
729
|
+
case "remove":
|
|
730
|
+
case "rename":
|
|
731
|
+
return [...spaceCandidates(spaces, false), ...options];
|
|
732
|
+
case "setup":
|
|
733
|
+
return [...spaceCandidates(spaces, false), every("every space"), ...options];
|
|
734
|
+
case "list":
|
|
735
|
+
case "update":
|
|
736
|
+
case "install":
|
|
737
|
+
case "uninstall":
|
|
738
|
+
return [...spaceCandidates(spaces, true), every("every space (base first)"), ...options];
|
|
739
|
+
case "completion":
|
|
740
|
+
return [...SHELLS.map((shell) => candidate("shell", shell, `the ${shell} completion script`)), ...options];
|
|
741
|
+
default:
|
|
742
|
+
return options;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
499
746
|
function candidates(words , io ) {
|
|
500
747
|
const call = {};
|
|
501
748
|
let index = 0;
|
|
@@ -509,33 +756,32 @@ function candidates(words , io ) {
|
|
|
509
756
|
const { dir } = resolveSettings(call, io);
|
|
510
757
|
const rest = words.slice(index);
|
|
511
758
|
const spaces = listSpaces(dir);
|
|
512
|
-
if (rest.length === 0) return [...
|
|
759
|
+
if (rest.length === 0) return [...commandCandidates(false), ...spaceCandidates(spaces, true), ...firstOptions(call, index === 0)];
|
|
513
760
|
if (rest.length > 1) return [];
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
return spaces;
|
|
518
|
-
case "setup":
|
|
519
|
-
return [...spaces, "--every"];
|
|
520
|
-
case "list":
|
|
521
|
-
case "update":
|
|
522
|
-
case "install":
|
|
523
|
-
case "uninstall":
|
|
524
|
-
return [BASE, ...spaces, "--every"];
|
|
525
|
-
case "completion":
|
|
526
|
-
return SHELLS;
|
|
527
|
-
default:
|
|
528
|
-
return [];
|
|
529
|
-
}
|
|
761
|
+
if (isHelpFlag(rest[0])) return commandCandidates(true);
|
|
762
|
+
if (!Object.hasOwn(COMMANDS, rest[0])) return [];
|
|
763
|
+
return commandArguments(rest[0], spaces);
|
|
530
764
|
}
|
|
531
765
|
|
|
532
|
-
/** The candidates for the value of a call option: profile names, the shell's files for a path, nothing for a command. */
|
|
766
|
+
/** The candidates for the value of a call option: profile names with their command, the shell's files for a path, nothing for a command. */
|
|
533
767
|
function optionValues(option , call , io ) {
|
|
534
|
-
if (option === "--profile")
|
|
768
|
+
if (option === "--profile") {
|
|
769
|
+
const { config } = resolveSettings(call, io);
|
|
770
|
+
return profileNames(config).map((name) => candidate("profile", name, profileText(config, name, io)));
|
|
771
|
+
}
|
|
535
772
|
if (option === "--config" || option === "--dir") return [FILES];
|
|
536
773
|
return [];
|
|
537
774
|
}
|
|
538
775
|
|
|
776
|
+
/** A profile's command, else "profile". */
|
|
777
|
+
function profileText(config , name , io ) {
|
|
778
|
+
try {
|
|
779
|
+
return profileByName(config, name, io.env, io.home).command || "profile";
|
|
780
|
+
} catch {
|
|
781
|
+
return "profile";
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
539
785
|
/** Prints the reason and returns false when the setup command has an unclosed quote. */
|
|
540
786
|
function checkSetup(io , source ) {
|
|
541
787
|
try {
|
package/package.json
CHANGED