22"""Hijack the ArgComplete's bash completion handler to return AutoCompleter results"""
33
44import argcomplete
5+ from contextlib import redirect_stdout
56import copy
7+ from io import StringIO
68import os
79import shlex
810import sys
@@ -162,7 +164,13 @@ def __call__(self, argument_parser, completer=None, always_complete_options=True
162164 comp_point = int (os .environ ["COMP_POINT" ])
163165
164166 comp_line = argcomplete .ensure_str (comp_line )
165- ### SWAPPED FOR AUTOCOMPLETER
167+
168+ ##############################
169+ # SWAPPED FOR AUTOCOMPLETER
170+ #
171+ # Replaced with our own tokenizer function
172+ ##############################
173+
166174 # cword_prequote, cword_prefix, cword_suffix, comp_words, last_wordbreak_pos = split_line(comp_line, comp_point)
167175 tokens , _ , begidx , endidx = tokens_for_completion (comp_line , comp_point )
168176
@@ -172,7 +180,11 @@ def __call__(self, argument_parser, completer=None, always_complete_options=True
172180 # 2: python <script> [args]
173181 # 3: python -m <module> [args]
174182 start = int (os .environ ["_ARGCOMPLETE" ]) - 1
175- ### SWAPPED FOR AUTOCOMPLETER
183+ ##############################
184+ # SWAPPED FOR AUTOCOMPLETER
185+ #
186+ # Applying the same token dropping to our tokens
187+ ##############################
176188 # comp_words = comp_words[start:]
177189 tokens = tokens [start :]
178190
@@ -183,11 +195,20 @@ def __call__(self, argument_parser, completer=None, always_complete_options=True
183195 # "\nSUFFIX: {!r}".format(cword_suffix),
184196 # "\nWORDS:", comp_words)
185197
186- ### SWAPPED FOR AUTOCOMPLETER
198+ ##############################
199+ # SWAPPED FOR AUTOCOMPLETER
200+ #
201+ # Replaced with our own completion function and customizing the returned values
202+ ##############################
187203 # completions = self._get_completions(comp_words, cword_prefix, cword_prequote, last_wordbreak_pos)
188- completions = completer .complete_command (tokens , tokens [- 1 ], comp_line , begidx , endidx )
189204
190- if completions is not None :
205+ # capture stdout from the autocompleter
206+ result = StringIO ()
207+ with redirect_stdout (result ):
208+ completions = completer .complete_command (tokens , tokens [- 1 ], comp_line , begidx , endidx )
209+ outstr = result .getvalue ()
210+
211+ if completions :
191212 # If any completion has a space in it, then quote all completions
192213 # this improves the user experience so they don't nede to go back and add a quote
193214 if ' ' in '' .join (completions ):
@@ -196,6 +217,17 @@ def __call__(self, argument_parser, completer=None, always_complete_options=True
196217 argcomplete .debug ("\n Returning completions:" , completions )
197218
198219 output_stream .write (ifs .join (completions ).encode (argcomplete .sys_encoding ))
220+ elif outstr :
221+ # if there are no completions, but we got something from stdout, try to print help
222+
223+ # trick the bash completion into thinking there are 2 completions that are unlikely
224+ # to ever match.
225+ outstr = outstr .replace ('\n ' , ' ' ).replace ('\t ' , ' ' ).replace (' ' , ' ' ).strip ()
226+ # generate a filler entry that should always sort first
227+ filler = ' {0:><{width}}' .format ('' , width = len (outstr ))
228+ outstr = ifs .join ([filler , outstr ])
229+
230+ output_stream .write (outstr .encode (argcomplete .sys_encoding ))
199231 else :
200232 # if completions is None we assume we don't know how to handle it so let bash
201233 # go forward with normal filesystem completion
0 commit comments