1 | <?php |
||
6 | final class HookFactory |
||
7 | { |
||
8 | /** |
||
9 | * Hook scripts |
||
10 | * |
||
11 | * These are shell-specific scripts that pass required information from that shell's |
||
12 | * completion system to the interface of the completion command in this module. |
||
13 | * |
||
14 | * The following placeholders are replaced with their value at runtime: |
||
15 | * |
||
16 | * %%function_name%% - name of the generated shell function run for completion |
||
17 | * %%program_name%% - command name completion will be enabled for |
||
18 | * %%program_path%% - path to program the completion is for/generated by |
||
19 | * %%completion_command%% - command to be run to compute completions |
||
20 | * |
||
21 | * NOTE: Comments are stripped out by HookFactory::stripComments as eval reads |
||
22 | * input as a single line, causing it to break if comments are included. |
||
23 | * While comments work using `... | source /dev/stdin`, existing installations |
||
24 | * are likely using eval as it's been part of the instructions for a while. |
||
25 | * |
||
26 | * @var array |
||
27 | */ |
||
28 | protected static $hooks = array( |
||
29 | // BASH Hook |
||
30 | 'bash' => <<<'END' |
||
31 | # BASH completion for %%program_path%% |
||
32 | function %%function_name%% { |
||
33 | |||
34 | # Copy BASH's completion variables to the ones the completion command expects |
||
35 | # These line up exactly as the library was originally designed for BASH |
||
36 | local CMDLINE_CONTENTS="$COMP_LINE"; |
||
37 | local CMDLINE_CURSOR_INDEX="$COMP_POINT"; |
||
38 | local CMDLINE_WORDBREAKS="$COMP_WORDBREAKS"; |
||
39 | |||
40 | export CMDLINE_CONTENTS CMDLINE_CURSOR_INDEX CMDLINE_WORDBREAKS; |
||
41 | |||
42 | local RESULT STATUS; |
||
43 | |||
44 | # Force splitting by newline instead of default delimiters |
||
45 | local IFS=$'\n'; |
||
46 | |||
47 | RESULT="$(%%completion_command%% </dev/null)"; |
||
48 | STATUS=$?; |
||
49 | |||
50 | local cur mail_check_backup; |
||
51 | |||
52 | mail_check_backup=$MAILCHECK; |
||
53 | MAILCHECK=-1; |
||
54 | |||
55 | _get_comp_words_by_ref -n : cur; |
||
56 | |||
57 | # Check if shell provided path completion is requested |
||
58 | # @see Completion\ShellPathCompletion |
||
59 | if [ $STATUS -eq 200 ]; then |
||
60 | # Turn file/dir completion on temporarily and give control back to BASH |
||
61 | compopt -o default; |
||
62 | return 0; |
||
63 | |||
64 | # Bail out if PHP didn't exit cleanly |
||
65 | elif [ $STATUS -ne 0 ]; then |
||
66 | echo -e "$RESULT"; |
||
67 | return $?; |
||
68 | fi; |
||
69 | |||
70 | COMPREPLY=(`compgen -W "$RESULT" -- $cur`); |
||
71 | |||
72 | __ltrim_colon_completions "$cur"; |
||
73 | |||
74 | MAILCHECK=mail_check_backup; |
||
75 | }; |
||
76 | |||
77 | if [ "$(type -t _get_comp_words_by_ref)" == "function" ]; then |
||
78 | complete -F %%function_name%% "%%program_name%%"; |
||
79 | else |
||
80 | >&2 echo "Completion was not registered for %%program_name%%:"; |
||
81 | >&2 echo "The 'bash-completion' package is required but doesn't appear to be installed."; |
||
82 | fi; |
||
83 | END |
||
84 | |||
85 | // ZSH Hook |
||
86 | , 'zsh' => <<<'END' |
||
87 | # ZSH completion for %%program_path%% |
||
88 | function %%function_name%% { |
||
89 | local -x CMDLINE_CONTENTS="$words"; |
||
90 | local -x CMDLINE_CURSOR_INDEX; |
||
91 | (( CMDLINE_CURSOR_INDEX = ${#${(j. .)words[1,CURRENT]}} )); |
||
92 | |||
93 | local RESULT STATUS; |
||
94 | RESULT=("${(@f)$( %%completion_command%% )}"); |
||
95 | STATUS=$?; |
||
96 | |||
97 | # Check if shell provided path completion is requested |
||
98 | # @see Completion\ShellPathCompletion |
||
99 | if [ $STATUS -eq 200 ]; then |
||
100 | _path_files; |
||
101 | return 0; |
||
102 | |||
103 | # Bail out if PHP didn't exit cleanly |
||
104 | elif [ $STATUS -ne 0 ]; then |
||
105 | echo -e "$RESULT"; |
||
106 | return $?; |
||
107 | fi; |
||
108 | |||
109 | compadd -- $RESULT; |
||
110 | }; |
||
111 | |||
112 | compdef %%function_name%% "%%program_name%%"; |
||
113 | END |
||
114 | ); |
||
115 | |||
116 | /** |
||
117 | * Return the names of shells that have hooks |
||
118 | * |
||
119 | * @return string[] |
||
120 | */ |
||
121 | public static function getShellTypes() |
||
125 | |||
126 | /** |
||
127 | * Return a completion hook for the specified shell type |
||
128 | * |
||
129 | * @param string $type - a key from self::$hooks |
||
130 | * @param string $programPath |
||
131 | * @param string $programName |
||
132 | * @param bool $multiple |
||
133 | * |
||
134 | * @return string |
||
135 | */ |
||
136 | public function generateHook($type, $programPath, $programName = null, $multiple = false) |
||
174 | |||
175 | /** |
||
176 | * Generate a function name that is unlikely to conflict with other generated function names in the same shell |
||
177 | */ |
||
178 | protected function generateFunctionName($programPath, $programName) |
||
186 | |||
187 | |||
188 | /** |
||
189 | * Make a string safe for use as a shell function name |
||
190 | * |
||
191 | * @param string $name |
||
192 | * @return string |
||
193 | */ |
||
194 | protected function sanitiseForFunctionName($name) |
||
199 | |||
200 | /** |
||
201 | * Strip '#' style comments from a string |
||
202 | * |
||
203 | * BASH's eval doesn't work with comments as it removes line breaks, so comments have to be stripped out |
||
204 | * for this method of sourcing the hook to work. Eval seems to be the most reliable method of getting a |
||
205 | * hook into a shell, so while it would be nice to render comments, this stripping is required for now. |
||
206 | * |
||
207 | * @param string $script |
||
208 | * @return string |
||
209 | */ |
||
210 | protected function stripComments($script) |
||
214 | } |
||
215 |