Skip to content

Commit 7c913f2

Browse files
Accept formatted text in the display text of completions.
1 parent 6bac8f6 commit 7c913f2

File tree

7 files changed

+235
-89
lines changed

7 files changed

+235
-89
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#!/usr/bin/env python
2+
"""
3+
Demonstration of a custom completer class and the possibility of styling
4+
completions independently by passing formatted text objects to the "display"
5+
and "display_meta" arguments of "Completion".
6+
"""
7+
from __future__ import unicode_literals
8+
from prompt_toolkit.completion import Completion, Completer
9+
from prompt_toolkit.formatted_text import HTML
10+
from prompt_toolkit.shortcuts import prompt, CompleteStyle
11+
12+
13+
animals = [
14+
'alligator', 'ant', 'ape', 'bat', 'bear', 'beaver', 'bee', 'bison',
15+
'butterfly', 'cat', 'chicken', 'crocodile', 'dinosaur', 'dog', 'dolphin',
16+
'dove', 'duck', 'eagle', 'elephant',
17+
]
18+
19+
animal_family = {
20+
'alligator': 'reptile',
21+
'ant': 'insect',
22+
'ape': 'mammal',
23+
'bat': 'mammal',
24+
'bear': 'mammal',
25+
'beaver': 'mammal',
26+
'bee': 'insect',
27+
'bison': 'mammal',
28+
'butterfly': 'insect',
29+
'cat': 'mammal',
30+
'chicken': 'bird',
31+
'crocodile': 'reptile',
32+
'dinosaur': 'reptile',
33+
'dog': 'mammal',
34+
'dolphin': 'mammal',
35+
'dove': 'bird',
36+
'duck': 'bird',
37+
'eagle': 'bird',
38+
'elephant': 'mammal',
39+
}
40+
41+
family_colors = {
42+
'mammal': 'ansimagenta',
43+
'insect': 'ansigreen',
44+
'reptile': 'ansired',
45+
'bird': 'ansiyellow',
46+
}
47+
48+
meta = {
49+
'alligator': HTML('An <ansired>alligator</ansired> is a <u>crocodilian</u> in the genus Alligator of the family Alligatoridae.'),
50+
'ant': HTML('<ansired>Ants</ansired> are eusocial <u>insects</u> of the family Formicidae.'),
51+
'ape': HTML('<ansired>Apes</ansired> (Hominoidea) are a branch of Old World tailless anthropoid catarrhine <u>primates</u>.'),
52+
'bat': HTML('<ansired>Bats</ansired> are mammals of the order <u>Chiroptera</u>.'),
53+
'bee': HTML('<ansired>Bees</ansired> are flying <u>insects</u> closely related to wasps and ants.'),
54+
'beaver': HTML('The <ansired>beaver</ansired> (genus Castor) is a large, primarily <u>nocturnal</u>, semiaquatic <u>rodent</u>.'),
55+
'bear': HTML('<ansired>Bears</ansired> are carnivoran <u>mammals</u> of the family Ursidae.'),
56+
'butterfly': HTML('<ansiblue>Butterflies</ansiblue> are <u>insects</u> in the macrolepidopteran clade Rhopalocera from the order Lepidoptera.'),
57+
# ...
58+
}
59+
60+
61+
class AnimalCompleter(Completer):
62+
def get_completions(self, document, complete_event):
63+
word = document.get_word_before_cursor()
64+
for animal in animals:
65+
if animal.startswith(word):
66+
if animal in animal_family:
67+
family = animal_family[animal]
68+
family_color = family_colors.get(family, 'default')
69+
70+
display = HTML(
71+
'%s<b>:</b> <ansired>(<' + family_color + '>%s</' + family_color + '>)</ansired>'
72+
) % (animal, family)
73+
else:
74+
display = animal
75+
76+
yield Completion(
77+
animal,
78+
start_position=-len(word),
79+
display=display,
80+
display_meta=meta.get(animal)
81+
)
82+
83+
84+
def main():
85+
# Simple completion menu.
86+
print('(The completion menu displays colors.)')
87+
prompt('Type an animal: ', completer=AnimalCompleter())
88+
89+
# Multi-column menu.
90+
prompt('Type an animal: ', completer=AnimalCompleter(),
91+
complete_style=CompleteStyle.MULTI_COLUMN)
92+
93+
# Readline-like
94+
prompt('Type an animal: ', completer=AnimalCompleter(),
95+
complete_style=CompleteStyle.READLINE_LIKE)
96+
97+
98+
if __name__ == '__main__':
99+
main()

prompt_toolkit/completion/base.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ class Completion(object):
2323
:param start_position: Position relative to the cursor_position where the
2424
new text will start. The text will be inserted between the
2525
start_position and the original cursor position.
26-
:param display: (optional string) If the completion has to be displayed
27-
differently in the completion menu.
28-
:param display_meta: (Optional string) Meta information about the
29-
completion, e.g. the path or source where it's coming from.
26+
:param display: (optional string or formatted text) If the completion has
27+
to be displayed differently in the completion menu.
28+
:param display_meta: (Optional string or formatted text) Meta information
29+
about the completion, e.g. the path or source where it's coming from.
3030
This can also be a callable that returns a string.
3131
:param style: Style string.
3232
:param selected_style: Style string, used for a selected completion.
@@ -36,19 +36,18 @@ def __init__(self, text, start_position=0, display=None, display_meta=None,
3636
style='', selected_style=''):
3737
assert isinstance(text, text_type)
3838
assert isinstance(start_position, int)
39-
assert display is None or isinstance(display, text_type)
40-
assert display_meta is None or isinstance(display_meta, text_type)
4139
assert isinstance(style, text_type)
4240
assert isinstance(selected_style, text_type)
4341

42+
from prompt_toolkit.formatted_text import to_formatted_text
4443
self.text = text
4544
self.start_position = start_position
4645
self._display_meta = display_meta
4746

4847
if display is None:
49-
self.display = text
50-
else:
51-
self.display = display
48+
display = text
49+
50+
self.display = to_formatted_text(display)
5251

5352
self.style = style
5453
self.selected_style = selected_style
@@ -74,18 +73,23 @@ def __eq__(self, other):
7473
def __hash__(self):
7574
return hash((self.text, self.start_position, self.display, self._display_meta))
7675

76+
@property
77+
def display_text(self):
78+
" The 'display' field as plain text. "
79+
from prompt_toolkit.formatted_text import fragment_list_to_text
80+
return fragment_list_to_text(self.display)
81+
7782
@property
7883
def display_meta(self):
7984
" Return meta-text. (This is lazy when using a callable). "
80-
meta = self._display_meta
85+
from prompt_toolkit.formatted_text import to_formatted_text
86+
return to_formatted_text(self._display_meta)
8187

82-
if meta is None:
83-
return ''
84-
85-
if callable(meta):
86-
return meta()
87-
88-
return meta
88+
@property
89+
def display_meta_text(self):
90+
" The 'meta' field as plain text. "
91+
from prompt_toolkit.formatted_text import fragment_list_to_text
92+
return fragment_list_to_text(self.display_meta)
8993

9094
def new_completion_from_position(self, position):
9195
"""

prompt_toolkit/completion/word_completer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ class WordCompleter(Completer):
1414
1515
:param words: List of words or callable that returns a list of words.
1616
:param ignore_case: If True, case-insensitive completion.
17-
:param meta_dict: Optional dict mapping words to their meta-information.
17+
:param meta_dict: Optional dict mapping words to their meta-text. (This
18+
should map strings to strings or formatted text.)
1819
:param WORD: When True, use WORD characters.
1920
:param sentence: When True, don't complete by comparing the word before the
2021
cursor, but by comparing all the text before the cursor. In this case,

prompt_toolkit/key_binding/bindings/completion.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def _display_completions_like_readline(app, completions):
8585
# completions. (Keep in mind that completions are displayed
8686
# alphabetically column-wise.)
8787
max_compl_width = min(term_width,
88-
max(get_cwidth(c.text) for c in completions) + 1)
88+
max(get_cwidth(c.display_text) for c in completions) + 1)
8989
column_count = max(1, term_width // max_compl_width)
9090
completions_per_page = column_count * (term_height - 1)
9191
page_count = int(math.ceil(len(completions) / float(completions_per_page)))
@@ -107,9 +107,11 @@ def display(page):
107107
try:
108108
completion = page_columns[c][r]
109109

110-
result.extend(to_formatted_text(
111-
completion.text.ljust(max_compl_width),
112-
style=completion.style))
110+
result.extend(to_formatted_text(completion.display, style=completion.style))
111+
112+
# Add padding.
113+
padding = max_compl_width - get_cwidth(completion.display_text)
114+
result.append((completion.style, ' ' * padding,))
113115
except IndexError:
114116
pass
115117
result.append(('', '\n'))

0 commit comments

Comments
 (0)