Minor dependency updates
[yaffs-website] / vendor / stecman / symfony-console-completion / src / HookFactory.php
1 <?php
2
3
4 namespace Stecman\Component\Symfony\Console\BashCompletion;
5
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     RESULT="$(%%completion_command%% </dev/null)";
45     STATUS=$?;
46
47     local cur mail_check_backup;
48
49     mail_check_backup=$MAILCHECK;
50     MAILCHECK=-1;
51
52     _get_comp_words_by_ref -n : cur;
53
54     # Check if shell provided path completion is requested
55     # @see Completion\ShellPathCompletion
56     if [ $STATUS -eq 200 ]; then
57         _filedir;
58         return 0;
59
60     # Bail out if PHP didn't exit cleanly
61     elif [ $STATUS -ne 0 ]; then
62         echo -e "$RESULT";
63         return $?;
64     fi;
65
66     COMPREPLY=(`compgen -W "$RESULT" -- $cur`);
67
68     __ltrim_colon_completions "$cur";
69
70     MAILCHECK=mail_check_backup;
71 };
72
73 if [ "$(type -t _get_comp_words_by_ref)" == "function" ]; then
74     complete -F %%function_name%% "%%program_name%%";
75 else
76     >&2 echo "Completion was not registered for %%program_name%%:";
77     >&2 echo "The 'bash-completion' package is required but doesn't appear to be installed.";
78 fi
79 END
80
81         // ZSH Hook
82         , 'zsh' => <<<'END'
83 # ZSH completion for %%program_path%%
84 function %%function_name%% {
85     local -x CMDLINE_CONTENTS="$words"
86     local -x CMDLINE_CURSOR_INDEX
87     (( CMDLINE_CURSOR_INDEX = ${#${(j. .)words[1,CURRENT]}} ))
88
89     local RESULT STATUS
90     RESULT=("${(@f)$( %%completion_command%% )}")
91     STATUS=$?;
92
93     # Check if shell provided path completion is requested
94     # @see Completion\ShellPathCompletion
95     if [ $STATUS -eq 200 ]; then
96         _path_files;
97         return 0;
98
99     # Bail out if PHP didn't exit cleanly
100     elif [ $STATUS -ne 0 ]; then
101         echo -e "$RESULT";
102         return $?;
103     fi;
104
105     compadd -- $RESULT
106 };
107
108 compdef %%function_name%% "%%program_name%%";
109 END
110     );
111
112     /**
113      * Return the names of shells that have hooks
114      *
115      * @return string[]
116      */
117     public static function getShellTypes()
118     {
119         return array_keys(self::$hooks);
120     }
121
122     /**
123      * Return a completion hook for the specified shell type
124      *
125      * @param string $type - a key from self::$hooks
126      * @param string $programPath
127      * @param string $programName
128      * @param bool   $multiple
129      *
130      * @return string
131      */
132     public function generateHook($type, $programPath, $programName = null, $multiple = false)
133     {
134         if (!isset(self::$hooks[$type])) {
135             throw new \RuntimeException(sprintf(
136                 "Cannot generate hook for unknown shell type '%s'. Available hooks are: %s",
137                 $type,
138                 implode(', ', self::getShellTypes())
139             ));
140         }
141
142         // Use the program path if an alias/name is not given
143         $programName = $programName ?: $programPath;
144
145         if ($multiple) {
146             $completionCommand = '$1 _completion';
147         } else {
148             $completionCommand = $programPath . ' _completion';
149         }
150
151         return str_replace(
152             array(
153                 '%%function_name%%',
154                 '%%program_name%%',
155                 '%%program_path%%',
156                 '%%completion_command%%',
157             ),
158             array(
159                 $this->generateFunctionName($programPath, $programName),
160                 $programName,
161                 $programPath,
162                 $completionCommand
163             ),
164             $this->stripComments(self::$hooks[$type])
165         );
166     }
167
168     /**
169      * Generate a function name that is unlikely to conflict with other generated function names in the same shell
170      */
171     protected function generateFunctionName($programPath, $programName)
172     {
173         return sprintf(
174             '_%s_%s_complete',
175             $this->sanitiseForFunctionName(basename($programName)),
176             substr(md5($programPath), 0, 16)
177         );
178     }
179
180
181     /**
182      * Make a string safe for use as a shell function name
183      *
184      * @param string $name
185      * @return string
186      */
187     protected function sanitiseForFunctionName($name)
188     {
189         $name = str_replace('-', '_', $name);
190         return preg_replace('/[^A-Za-z0-9_]+/', '', $name);
191     }
192
193     /**
194      * Strip '#' style comments from a string
195      *
196      * BASH's eval doesn't work with comments as it removes line breaks, so comments have to be stripped out
197      * for this method of sourcing the hook to work. Eval seems to be the most reliable method of getting a
198      * hook into a shell, so while it would be nice to render comments, this stripping is required for now.
199      *
200      * @param string $script
201      * @return string
202      */
203     protected function stripComments($script)
204     {
205         return preg_replace('/(^\s*\#.*$)/m', '', $script);
206     }
207 }