Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 104 additions & 7 deletions packages/cli_tools/README_completion.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,17 @@ the completion shell script. Two tools are currently supported.

The shell script needs to be installed by end users to enable completion.

### Enable experimental feature
### Enable the feature

Enable this feature by constructing `BetterCommandRunner`
with the flag `enableCompletionCommand` set to `true`.

### Example implementation

See a complete example command implementation that uses this feature in
the `example` folder:
[command_completion_example.dart](example/command_completion_example.dart)

Enable this experimental feature by constructing `BetterCommandRunner`
with the flag `experimentalCompletionCommand` set to `true`.

## Using the tool `carapace`

Expand Down Expand Up @@ -71,11 +78,13 @@ For more information and installing in other shells, see:
https://carapace-sh.github.io/carapace-bin/setup.html


### Distribution
### Installation

End users will need to install `carapace` and copy the Yaml file to the proper
location, even if the Yaml file is distributed with the command.

See also [Distribution](#distribution) below.


## Using the tool `completely`

Expand Down Expand Up @@ -147,7 +156,95 @@ autoload -Uz +X compinit && compinit
autoload -Uz +X bashcompinit && bashcompinit
```

### Distribution
### Installation

For end users, the generated bash script can be installed directly in their
`~/.local/share/bash-completion/completions/`.


## Distribution

Two sub-commands are provided to help command developers to distribute the
completion capability to their end users.

### Embedding the completions scripts in the command package / executable

The `completion embed` sub-command is intended for the command developer rather
than the end users. It embeds a script in the command source code, so that
it later can be installed by end users with `completion install`.

Usage:

```sh
$ my-command completion embed --help
Embed a command line completion script in the command source code

Usage: example completion embed [arguments]
-h, --help Print this usage information.
-t, --target (mandatory) The target tool format

[completely] Use the `completely` tool (https://github.com/bashly-framework/completely)
[carapace] Use the `carapace` tool (https://carapace.sh/)

-f, --script-file Read the script file to embed from a file instead of stdin
-o, --output-file The Dart file name to write ("-" for stdout)
(defaults to "completion_script_<target>.dart")
-d, --output-dir Override the directory to write the Dart source file to
(defaults to "Directory: '/Users/christer/dev/cli_tools/packages/cli_tools/lib/src'")
```

### For end-users: Installing the completion script

End users run the install command:

```sh
$ my-command completion install --help
Install a command line completion script

Usage: example completion install [arguments]
-h, --help Print this usage information.
-t, --target (mandatory) The target tool format

[completely] Use the `completely` tool (https://github.com/bashly-framework/completely)
[carapace] Use the `carapace` tool (https://carapace.sh/)

-e, --exec-name Override the name of the executable
-d, --write-dir Override the directory to write the script to
```

Examples:

```sh
my-command completion install -t completely
```
or
```sh
my-command completion install -t carapace
source <(carapace example) # for bash; for others see https://carapace.sh/setup.html
```

### Generation examples:

For end users, the generated bash script can be distributed as a file for them
to install directly in their `~/.local/share/bash-completion/completions/`.
In order to regenerate the completion scripts, run these commands for each
target.

(Use the `embed` sub-command's `--output-dir` option to specify a different
directory for the generated source files, `lib/src/` is the default.)

Completely:
```sh
my-command completion generate -t completely | completely generate - example.bash
my-command completion embed -t completely -f example.bash -d example/
```

Carapace:
```sh
my-command completion generate -t carapace -f example.yaml
my-command completion embed -t carapace -f example.yaml -d example/
```

Or as one-liners:
```sh
my-command completion generate -t completely | completely generate - - | my-command completion embed -t completely
my-command completion generate -t carapace | my-command completion embed -t carapace
```
54 changes: 54 additions & 0 deletions packages/cli_tools/example/command_completion_example.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import 'dart:io' show exitCode;

import 'package:cli_tools/better_command_runner.dart';
import 'package:config/config.dart';

import 'completion_script_carapace.dart';
import 'completion_script_completely.dart';

/// Example of using [BetterCommandRunner] with command line completion.
///
/// Run this example program like so:
/// ```sh
/// dart example/command_completion_example.dart completion install -t completely
/// ```
/// or
/// ```sh
/// dart example/command_completion_example.dart completion install -t carapace
/// source <(carapace example) # for bash, for others see https://carapace.sh/setup.html
/// ```
///
/// In order to regenerate the completion scripts, run these commands for each
/// target.
///
/// See also [README_completion.md].
///
/// Completely:
/// ```sh
/// dart example/command_completion_example.dart completion generate -t completely | completely generate - example.bash
/// dart example/command_completion_example.dart completion embed -t completely -f example.bash -d example/
/// ```
///
/// Carapace:
/// ```sh
/// dart example/command_completion_example.dart completion generate -t carapace -f example.yaml
/// dart example/command_completion_example.dart completion embed -t carapace -f example.yaml -d example/
/// ```
Future<void> main(final List<String> args) async {
final commandRunner = BetterCommandRunner(
'example',
'Example CLI command',
enableCompletionCommand: true,
embeddedCompletions: [
completionScriptCompletely,
completionScriptCarapace,
],
);

try {
await commandRunner.run(args);
} on UsageException catch (e) {
print(e);
exitCode = 1;
}
}
65 changes: 65 additions & 0 deletions packages/cli_tools/example/completion_script_carapace.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/// This file is auto-generated.
library;

import 'package:cli_tools/better_command_runner.dart' show CompletionTarget;

const String _completionScript = r'''
# yaml-language-server: $schema=https://carapace.sh/schemas/command.json
name: example
persistentFlags:
-q, --quiet: Suppress all cli output. Is overridden by -v, --verbose.
-v, --verbose: Prints additional information useful for development. Overrides --q, --quiet.
commands:
- name: completion
commands:
- name: generate
flags:
-t, --target=!: The target tool format
-e, --exec-name=: Override the name of the executable
-f, --file=: Write the specification to a file instead of stdout
completion:
flag:
target: ["completely", "carapace"]
file: ["$files"]
- name: embed
flags:
-t, --target=!: The target tool format
-f, --script-file=: The script file to embed
-o, --output-file=: The Dart file name to write
-d, --output-dir=: Override the directory to write the Dart source file to
completion:
flag:
target: ["completely", "carapace"]
output-dir: ["$directories"]
- name: install
flags:
-t, --target=!: The target tool format
-e, --exec-name=: Override the name of the executable
-d, --write-dir=: Override the directory to write the script to
completion:
flag:
target: ["completely", "carapace"]
write-dir: ["$directories"]
- name: install
flags:
-t, --target=!: The target tool format
-e, --exec-name=: Override the name of the executable
-d, --write-dir=: Override the directory to write the script to
completion:
flag:
target: ["completely", "carapace"]
write-dir: ["$directories"]
''';

/// Embedded script for command line completion for `carapace`.
const completionScriptCarapace = (
target: CompletionTarget.carapace,
script: _completionScript,
);
139 changes: 139 additions & 0 deletions packages/cli_tools/example/completion_script_completely.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/// This file is auto-generated.
library;

import 'package:cli_tools/better_command_runner.dart' show CompletionTarget;

const String _completionScript = r'''
# example completion -*- shell-script -*-

# This bash completions script was generated by
# completely (https://github.com/bashly-framework/completely)
# Modifying it manually is not recommended

_example_completions_filter() {
local words="$1"
local cur=${COMP_WORDS[COMP_CWORD]}
local result=()

# words the user already typed (excluding the command itself)
local used=()
if ((COMP_CWORD > 1)); then
used=("${COMP_WORDS[@]:1:$((COMP_CWORD - 1))}")
fi

if [[ "${cur:0:1}" == "-" ]]; then
# Completing an option: offer everything (including options)
echo "$words"

else
# Completing a non-option: offer only non-options,
# and don't re-offer ones already used earlier in the line.
for word in $words; do
[[ "${word:0:1}" == "-" ]] && continue

local seen=0
for u in "${used[@]}"; do
if [[ "$u" == "$word" ]]; then
seen=1
break
fi
done
((!seen)) && result+=("$word")
done

echo "${result[*]}"
fi
}

_example_completions() {
local cur=${COMP_WORDS[COMP_CWORD]}
local compwords=()
if ((COMP_CWORD > 0)); then
compwords=("${COMP_WORDS[@]:1:$((COMP_CWORD - 1))}")
fi
local compline="${compwords[*]}"

COMPREPLY=()

case "$compline" in
'completion install'*'--write-dir')
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -- "$cur")
;;

'completion embed'*'--output-dir')
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -- "$cur")
;;

'completion generate'*'--target')
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_example_completions_filter "completely carapace")" -- "$cur")
;;

'completion install'*'--target')
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_example_completions_filter "completely carapace")" -- "$cur")
;;

'completion generate'*'--file')
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A file -- "$cur")
;;

'completion embed'*'--target')
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_example_completions_filter "completely carapace")" -- "$cur")
;;

'completion generate'*'-t')
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_example_completions_filter "completely carapace")" -- "$cur")
;;

'completion generate'*'-f')
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A file -- "$cur")
;;

'completion install'*'-d')
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -- "$cur")
;;

'completion install'*'-t')
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_example_completions_filter "completely carapace")" -- "$cur")
;;

'completion embed'*'-t')
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_example_completions_filter "completely carapace")" -- "$cur")
;;

'completion embed'*'-d')
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -- "$cur")
;;

'completion generate'*)
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_example_completions_filter "--quiet -q --verbose -v --target -t --exec-name -e --file -f")" -- "$cur")
;;

'completion install'*)
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_example_completions_filter "--quiet -q --verbose -v --target -t --exec-name -e --write-dir -d")" -- "$cur")
;;

'completion embed'*)
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_example_completions_filter "--quiet -q --verbose -v --target -t --script-file -f --output-file -o --output-dir -d")" -- "$cur")
;;

'completion'*)
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_example_completions_filter "generate embed install --quiet -q --verbose -v")" -- "$cur")
;;

*)
while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_example_completions_filter "completion --quiet -q --verbose -v")" -- "$cur")
;;

esac
} &&
complete -F _example_completions example

# ex: filetype=sh

''';

/// Embedded script for command line completion for `completely`.
const completionScriptCompletely = (
target: CompletionTarget.completely,
script: _completionScript,
);
Loading