Skip to content

Instantly share code, notes, and snippets.

@ansaso
Last active November 15, 2021 16:44
Show Gist options
  • Save ansaso/55d2a66b4fcb0820493d721d4727af46 to your computer and use it in GitHub Desktop.
Save ansaso/55d2a66b4fcb0820493d721d4727af46 to your computer and use it in GitHub Desktop.
Fish completion from json
#########################
#### fish completion ####
#########################
# in /path/to/fish/completions/{mycommand}.fish
# store json completion for command as environment variable
# to be passed down to python script
set -x json_completion (cat "path/to/mycommand.json")
# pipe current arguments to python script stdin
# (make sure python script is executable and in path)
complete -f -c mycommand -a "(__fish_print_cmd_args_without_options | json_to_fish_completion.py)"
{
"option1": null,
"option2": {},
"option3": {
"description": null
},
"option4": "some description",
"option5": {
"description": "some description"
},
"subcommand1": [
"option1.1",
"option1.2"
],
"subcommand2": {
"option2.1": "some description",
"option2.2": null
},
"subcommand3": {
"options": {
"option3.1": "some description",
"option3.2": null
}
},
"subcommand4": {
"options": [
"option4.1",
"option4.2"
]
},
"subcommand5": {
"description": "some description",
"options": {
"option5.1": null,
"subcommand5.1": {
"description": "some description",
"options": [
"option5.1.1",
"option5.1.2"
]
}
}
}
}
[
"option1",
"option2",
]
#!/usr/bin/env python
#######################
#### python script ####
#######################
import sys
from typing import Union, List, Dict, Any, Optional
import os
import json
# get content of json file read and stored as environment variable
# in fish completion file which calls this script
JSON_COMPLETION = os.getenv("json_completion")
if not JSON_COMPLETION:
raise OSError("json_completion environment variable not set")
# type
Options = Union[List[str], Dict[str, Any]]
def option_string(
option: str,
description: Optional[str] = None,
is_subcommand: bool = False
):
# fish parses tab as separator between option and description
option = option + '\t'
# apply formatting to distinguish subcommands
if is_subcommand:
option = option + "* "
if description:
option = option + description
return option
def is_simple_dict(props) -> bool:
return (
("options" not in props) and
("description" not in props) and
(len(props) > 0)
)
# translate json schema to fish options
def get_options(options: Options):
new_options = []
if isinstance(options, list):
for option in options:
new_options.append(option_string(option, description=None))
elif isinstance(options, dict):
for option, props in options.items():
if isinstance(props, str):
new_options.append(option_string(option, description=props, is_subcommand=False))
elif props is None:
new_options.append(option_string(option, description=None, is_subcommand=False))
elif isinstance(props, dict):
desc = props.get("description", None)
is_subcommand = ("options" in props) or is_simple_dict(props)
new_options.append(option_string(option, description=desc, is_subcommand=is_subcommand))
elif isinstance(props, list):
is_subcommand = len(props) > 0
new_options.append(option_string(option, description=None, is_subcommand=is_subcommand))
return new_options
if __name__ == "__main__":
lines = sys.stdin.readlines()
indexes = lines[1:]
options = json.loads(JSON_COMPLETION)
depth = 0
for index in indexes:
props = options.get(index.strip())
if isinstance(props, dict):
if "options" in props:
options = props["options"]
elif is_simple_dict(props):
options = props
elif isinstance(props, list):
if len(props) > 0:
options = props
else:
break
depth += 1
if len(indexes) >= depth:
sys.stdout.write('\n'.join(get_options(options)))
sys.exit(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment