Skip to main content

tome/commands/
init.rs

1use super::super::{
2    cli::InitArgs,
3    constants::SCRIPT_ROOT_ENVIRONMENT_VARIABLE,
4    shell_type::{get_shell_type, ShellType},
5};
6
7// because this string is intended to be formatted,
8// this can be very hard to read.
9// strings that will be evaluated by rust have single brackets: { }
10// strings that will be evaluated as part of the script have double brackets: {{ }}
11macro_rules! bash_zsh_init_body {
12  () => {
13    r#"
14if [[ -n ${{ZSH_VERSION-}} ]]; then
15    # bash completion emulation requires that zsh's completion has
16    # already been initialized. In addition, running the autoload
17    # expression with the ampersands seems to always result in
18    # compinit being executed, which clear completions that were
19    # previously bound by tome, resulting in the inability to
20    # instantiate multiple tome command sets.
21    if ! type complete > /dev/null; then
22        # add -C "" to skip compaudit, which forces
23        # an interactive dialog for compinit which doesn't
24        # work for interactive shells.
25        autoload +X compinit && compinit -C ""
26    fi
27    autoload +X bashcompinit && bashcompinit
28
29fi
30
31# completion is accomplished by three parts:
32# 1. passing all possible completions to {tome_executable}
33# 2. filtering for valid options using compgen
34# 3. appending to the valid option environment variable.
35function {function_name} {{
36    export {script_root_environment_variable}="{script_root}"
37    # capturing the results as a variable led to the command
38    # being to long for zsh to execute. (literally raising
39    # "command too long" )
40    eval `{tome_executable} command-execute {script_root} -s {shell} -- "$@"`
41}}
42
43function _{function_name}_completions {{
44    export {script_root_environment_variable}="{script_root}"
45    local token_to_complete tome_args
46    token_to_complete="${{COMP_WORDS[COMP_CWORD]}}";
47    tome_args="${{COMP_WORDS[@]:1}}";  # strip the first argument prefix, which is the function name
48    # strip the partial token_to_complete, if there is one
49    tome_args="${{tome_args%$token_to_complete}}";
50    all_options=`{tome_executable} command-complete {script_root} -s {shell} -- $tome_args`
51    valid_options=$(compgen -W "$all_options" -- "$token_to_complete")
52    COMPREPLY=($valid_options)
53}}
54
55complete -F _{function_name}_completions {function_name}
56"#
57  };
58}
59
60macro_rules! fish_init_body {
61    () => {
62        r#"
63# Note: to perform debugging when interface for tome changes
64# set set -l fish_trace on before the buggy part of fish script
65function __fish_tome_help_message
66  echo -e "--help\tPrint help\n"
67end
68
69function __fish_tome_complete_subcommands
70  # Arguments: tome command-complete ./example -s fish -- ARGS
71  $argv[1] $argv[2] $argv[3] $argv[4] $argv[5] $argv[6..-1] | tr " " "\n"
72  return 0
73end
74
75function __fish_tome_completion_inner
76  set -l help_msg
77  set -l tokens $argv
78  switch (count $tokens)
79  case 0
80    # Being used as function in fish
81    __fish_tome_complete_subcommands $tokens
82  case '*'
83    __fish_tome_complete_subcommands $tokens
84  end
85end
86
87function __fish_tome_completion_binary
88  set -l help_msg
89  set -l tokens $argv
90  switch (count $tokens)
91  case 0
92    echo "Bad codepath"
93    exit 1
94  case 1
95    # > tome
96    # needs commands
97    __fish_tome_help_message
98    echo -e "command-complete\tCompletions\n"
99    echo -e "command-execute\tExecute\n"
100    echo -e "help\tHelp\n"
101    echo -e "init\tInitialize Tome\n"
102  case 2
103    # > tome CMD
104    # return all current directories
105    ls -d -1 */
106    __fish_tome_help_message
107  case 3
108    echo -e "--shell\tShell\n"
109  case 4
110    echo -e "bash\tBash\n"
111    echo -e "zsh\tZsh\n"
112    echo -e "fish\tFish\n"
113  case 5
114    echo -e "--\tSeparator\n"
115  case '*'
116    __fish_tome_complete_subcommands $tokens
117  end
118end
119
120function __fish_tome_completion
121  set -l args (commandline -co)
122  set -x {script_root_environment_variable} "{script_root}"
123  switch $args[1]
124    case 'tome'
125      __fish_tome_completion_binary $args
126    case '*'
127      echo "Bad codepath"
128      exit 1
129  end
130end
131
132function __fish_tome_completion_fn
133  set -l dir $argv[1]
134  set -l cmdline (commandline -co)
135  # Drop function name
136  set -l cmd $cmdline[2..-1]
137  set -l args "{tome_executable}" command-complete $dir -s "{shell}" -- $cmd
138  __fish_tome_completion_inner $args
139end
140
141complete -c tome -f -a "(__fish_tome_completion)"
142
143# Alias for tome command
144function {function_name}
145  set -x {script_root_environment_variable} "{script_root}"
146  eval ({tome_executable} command-execute {script_root} -s {shell} -- $argv)
147end
148complete -c {function_name} -f -a "(__fish_tome_completion_fn {script_root} $argv)"
149# End tome alias
150"#
151    };
152}
153
154// given the location of the tome executable, return
155// back the init script for tome.
156pub fn init(tome_executable: &str, init_args: &InitArgs) -> Result<String, String> {
157    let shell = get_shell_type(&init_args.shell_type_or_path)?;
158    // Bootstrapping the sc section requires two parts:
159    // 1. creating the function in question
160    // 2. wiring up tab completion for the function.
161    //
162    // functions must be used instead of a script, as
163    // tome also supports commands that modify the
164    // current environment (such as cd you into a specific)
165    // directory.
166    match shell {
167        ShellType::FISH => Ok(format!(
168            fish_init_body!(),
169            tome_executable = tome_executable,
170            shell = &init_args.shell_type_or_path,
171            script_root = init_args.command_directory_path,
172            function_name = init_args.command_name,
173            script_root_environment_variable = SCRIPT_ROOT_ENVIRONMENT_VARIABLE,
174        )),
175        ShellType::BASH | ShellType::ZSH => Ok(format!(
176            bash_zsh_init_body!(),
177            tome_executable = tome_executable,
178            shell = &init_args.shell_type_or_path,
179            script_root = init_args.command_directory_path,
180            function_name = init_args.command_name,
181            script_root_environment_variable = SCRIPT_ROOT_ENVIRONMENT_VARIABLE,
182        )),
183        ShellType::UNKNOWN => Err(format!(
184            "could not determine shell from {}. Unable to init.",
185            &init_args.shell_type_or_path
186        )),
187    }
188}