Reputation: 2570
I'm trying to write a function to display a custom view when users press the tab button. Apparently "set_completion_display_matches_hook" function is what I need, I can display a custom view, but the problem is that I have to press Enter to get a prompt again.
The solution in Python2 seems to be that (solution here):
def match_display_hook(self, substitution, matches, longest_match_length):
print ''
for match in matches:
print match
print self.prompt.rstrip(),
print readline.get_line_buffer(),
readline.redisplay()
But it doesn't work with Python3. I made these syntax changes :
def match_display_hook(self, substitution, matches, longest_match_length):
print('\n----------------------------------------------\n')
for match in matches:
print(match)
print(self.prompt.rstrip() + readline.get_line_buffer())
readline.redisplay()
Any ideas please ?
Upvotes: 4
Views: 999
Reputation: 26
this one worked for me for redisplaying substitution and the end of matches display for python3:
def match_display_hook(self, substitution, matches, longest_match_length):
print("")
for match in matches:
print(match)
print("")
sys.stdout.write(substitution)
sys.stdout.flush()
return None
while previous ones using print prompt didn't. (didn't get to the bottom of the problem)
Upvotes: 0
Reputation: 1263
The redisplay()
function
void
rl_redisplay (void)
Change what's displayed on the screen to reflect the current contents of rl_line_buffer.
In your example you have written to stdout
, but not changed that buffer.
Print and flush as described by in other answer should work.
One issue you will have, however, is cursor position. Say you have this scenario:
$ cmd some_file ^ +---- User has back-tracked here and want to insert an option. <TAB> completion with print and flush will put cursor at end of `some_file' and the line will get an extra 15 spaces after that ...
To remedy this one way is to first get cursor position, then use ANSI sequences to re-position the cursor.
buf = readline.get_line_buffer()
x = readline.get_endidx()
print(self.prompt + buf, end = '')
if x < len(buf):
""" Set cursor at old column position """
print("\r\033[%dC" % (x + len(self.prompt)), end = '')
sys.stdout.flush()
Now, of course, you get another issue if prompt
has ANSI sequences in-iteself. Typically color or the like. Then you can not use len(prompt)
but have to find printed / visible length.
One has to use open and close bytes elsewhere, typically \0x01
and \0x02
respectively.
So one typically get:
prompt = '\001\033[31;1m\002VISIBLE_TEXT\001\033[0m\002 '
instead of:
prompt = '\033[31;1mVISIBLE_TEXT\033[0m '
With those guards it should be easy enough to strip out the visible text.
Typically something like:
clean_prompt = re.sub(r'\001[^\002]*\002', '', prompt))
Cache the length of that and use when printing the readline manually. Note that you also have to remove the guards when using it manually - as in the hook function. (But it is needed in input(prompt)
Upvotes: 1
Reputation: 135
First, the Python 2 code uses commas to leave the line unfinished. In Python 3, it's done using end
keyword:
print(self.prompt.rstrip(), readline.get_line_buffer(), sep='', end='')
Then, a flush is required to actually display the unfinished line (due to line buffering):
sys.stdout.flush()
The redisplay()
call does not seem to be needed.
The final code:
def match_display_hook(self, substitution, matches, longest_match_length):
print()
for match in matches:
print(match)
print(self.prompt.rstrip(), readline.get_line_buffer(), sep='', end='')
sys.stdout.flush()
Upvotes: 1