Skip to content

Commit 26a6274

Browse files
prompt_toolkit 2.0 refactoring: rewrite of the architecture (part 1).
(>74 commits squashed)
1 parent 8c3b54d commit 26a6274

164 files changed

Lines changed: 9280 additions & 6300 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,192 @@
11
CHANGELOG
22
=========
33

4+
2.0.0: ...
5+
-----------------
6+
7+
Version 2.0 includes a big refactoring of the internal architecture. This
8+
includes the merge of the CommandLineInterface and the Application object, a
9+
rewrite of how user controls are focussed, a rewrite of how event loops work
10+
and the removal of the buffers dictionary. This introduces many backwards
11+
incompatible changes, but the result is a very nice and powerful architecture.
12+
13+
Most architectural changes effect full screen applications. For applications
14+
that use `prompt_toolkit.shortcuts` for simple prompts, there are fewer
15+
incompatibilities.
16+
17+
Changes:
18+
19+
- No automatic translation from \r into \n during the input processing. These
20+
are two different keys that can be handled independently. This is a big
21+
backward-incompatibility, because the `Enter` key is `ControlM`, not
22+
`ControlJ`. So, now that we stopped translating \r into \n, it could be that
23+
custom key bindings for `Enter` don't work anymore. Make sure to use
24+
`Keys.Enter` instead of `Keys.ControlJ` in the key bindings for handling the
25+
`Enter` key.
26+
27+
- The `CommandLineInterface` and the `Application` classes are merged. First,
28+
`CommandLineInterface` contained all the I/O objects (like the input, output
29+
and event loop), while the `Application` contained everything else. There was
30+
no practical reason to keep this separation. (`CommandLineInterface` was
31+
mostly a proxy to `Application`.)
32+
33+
A consequence is that all almost code which received a
34+
`CommandLineInterface`, will now receive an `Application`. Usually, where we
35+
had an attribute `cli`, we'll now have an attribute `app`.
36+
37+
* `CLIFilter` will now take an `Application`, so it was renamed to `AppFilter`.
38+
* `to_cli_filter` was renamed to `to_app_filter`.
39+
40+
- The `buffers` dictionary (`CommandLineInterface.buffers`) does not exist
41+
anymore. Further, `buffers` was a `BufferMapping` that keeps track of which
42+
buffer has the focus. This significantly reduces the freedom for creating
43+
complex applications. We wanted to move toward a layout that can be defined
44+
as a collection of and relation between user widgets. A user widget does not
45+
need to have a `Buffer` underneath and any widget should be focussable.
46+
47+
* `layout.focus.Focus` was introduced and `Application.focussed_control` was
48+
added.
49+
50+
- The key bindings were refactored. It became much more flexible to combine
51+
sets of key bindings.
52+
53+
* `Registry` has been renamed to `KeyBindings`.
54+
* The `add_binding` function has been renamed to simply `add`.
55+
* Every `load_*` function returns one `KeyBindings` objects, instead of
56+
populating an existing one, like before.
57+
* `ConditionalKeyBindings` was added. This can be used to enable/disable
58+
all the key bindings from a given `Registry`.
59+
* A function named `merge_key_bindings` was added. This takes a list of
60+
`KeyBindings` and merges them into one.
61+
* `key_binding.defaults.load_key_bindings` was added to load all the key
62+
bindings.
63+
* `KeyBindingManager` has been removed completely.
64+
* `input_processor` was renamed to `key_processor`.
65+
66+
- User controls can define key bindings, which are active when the user control
67+
is focussed.
68+
69+
* `UIControl` got a `get_key_bindings` (abstract) method.
70+
71+
- Changes in the layout engine:
72+
73+
* `LayoutDimension` was renamed to `Dimension`.
74+
* `VSplit` and `HSplit` now take a `padding` argument.
75+
* `VSplit` and `HSplit` now take an `align` argument.
76+
(TOP/CENTER/BOTTOM/JUSTIFY) or (LEFT/CENTER/RIGHT/JUSTIFY).
77+
* `Float` now takes `allow_cover_cursor` and `attach_to_window` arguments.
78+
* `Window` got an `Align` argument. This can be used for the alignment of the
79+
content. `TokenListControl` does not have an allignment argument anymore.
80+
* `Window` got a `token` and `get_token` argument. This is used to fill the
81+
window's background using the given `Token`. That way, a window can for
82+
instance be given a background color.
83+
* `FillControl` does not exist anymore. Use the `token` and `char` arguments
84+
of the `Window` class instead.
85+
* `DummyControl` was added.
86+
* `VSplit` and `HSplit` classes also take a `token` as argument, these are
87+
passed to the `Window` class and applied to the content as well.
88+
89+
- Changes to `BufferControl`:
90+
91+
* `BufferControl` now takes a `input_processor` as argument. (Singular
92+
instead of plural). If you want to combine multiple, they have to be merged
93+
together using `merge_input_processors`.
94+
95+
* The `InputProcessor` class has been refactored. The `apply_transformation`
96+
method should now accept a `TransformationInput` object.
97+
98+
* The text `(reverse-i-search)` is now displayed through a processor. (See
99+
the `shortcuts` module for an example of the usage.)
100+
101+
- Changes related to `shortcuts.prompt`:
102+
103+
* There is now a class `Prompt` that which has a method `prompt`. Both the
104+
class and the method take about the same arguments. This can be used to
105+
create a session. Every `prompt` call of the same instance will reuse all
106+
the arguments given to the class itself. This can be used for instance to
107+
keep the history between multiple calls.
108+
109+
Of course, it's still possible to call the global `prompt` function. This
110+
will create a new instance every time when it's called.
111+
112+
* The `prompt` function now takes an `extra_key_bindings` argument instead of
113+
`key_bindings_registry`. This should only contain the additional bindings.
114+
115+
- Changes to the event loops:
116+
117+
* The event loop API is now closer to how asyncio works. A prompt_toolkit
118+
`Application` now has a `Future` object. Calling the `.start()` method
119+
creates and returns that `Future`. An event loop has a `run_until_complete`
120+
method that takes a future and runs the event loop until the Future is set.
121+
122+
* `Application` still has a method `run()` that underneath still runs the
123+
event loop until the `Future` is set and returns that result.
124+
125+
* The asyncio adaptors (like the asyncio event loop integration) now require
126+
Python 3.5.
127+
128+
* The `Input` and `Output` classes has some changes. (Not really important.)
129+
130+
- Changes to the `filters` module:
131+
132+
* All filters have been turned into functions. For instance, `IsDone`
133+
became `is_done` and `HasCompletions` became `has_completions`.
134+
135+
This was done because almost all classes were called without any arguments
136+
in the `__init__` causing additional braces everywhere. This means that
137+
`HasCompletions()` has to be replaced by `has_completions` (without
138+
parenthesis).
139+
140+
The few filters that took arguments as input, are also functions, but still
141+
have to be called with the given arguments.
142+
143+
- Other renames:
144+
145+
* `IncrementalSearchDirection` was renamed to `SearchDirection`.
146+
147+
- Other new featers:
148+
149+
* `DummyAutoSuggest` and `DynamicAutoSuggest` were added.
150+
* `DummyClipboard` and `DynamicClipboard` were added.
151+
* `DummyCompleter` and `DynamicCompleter` were added.
152+
* `DummyHistory` and `DynamicHistory` was added.
153+
154+
* `to_container` and `to_window` utilities were added.
155+
156+
* Added a small collection of widgets. These are more complex collections of
157+
user controls that are ready to embed in a layout.
158+
159+
****
160+
- `run_sub_applications` returns a future instead of taking a callback.
161+
162+
- Key() object does not exist anymore. Keys are all strings.
163+
164+
165+
166+
- Deprecated
167+
168+
169+
Migrating readline applications:
170+
171+
TODO: finish changelog...
172+
- Removed AcceptAction, AbortAction
173+
buffer.initial_document was renamed to buffer.document
174+
175+
TODO: use focussed_window instead of focussed_control
176+
177+
TODO: clean up 'contrib'.
178+
clean up filters. Use @Condition as much as possible.
179+
rename pop_focus()
180+
Focus() should be an attribute of the Layout() class.
181+
rename current_buffer to focussed_buffer
182+
allow setting of focussed_buffer. (Search in the UI.)
183+
184+
TODO: - remove alignment in TokenListControl
185+
- add background token to control.
186+
187+
TODO: add to_container() function that automatically wraps stuff in a window.
188+
189+
4190
1.0.9: 2016-11-07
5191
-----------------
6192

README.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ Shells:
127127
- `haxor-news <https://github.com/donnemartin/haxor-news>`_: A Hacker News CLI.
128128
- `gitsome <https://github.com/donnemartin/gitsome>`_: A Git/Shell Autocompleter with GitHub Integration.
129129
- `http-prompt <https://github.com/eliangcs/http-prompt>`_: An interactive command-line HTTP client.
130+
- `coconut <http://coconut-lang.org/>`_: Functional programming in Python.
131+
- `Ergonomica <https://ergonomica.github.io/>`_: A Bash alternative written in Python.
130132

131133
Full screen applications:
132134

examples/abortaction.retry.py

Lines changed: 0 additions & 14 deletions
This file was deleted.

examples/dialogs/input_dialog.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/usr/bin/env python
2+
"""
3+
Example of an input box dialog.
4+
"""
5+
from __future__ import unicode_literals
6+
from prompt_toolkit.shortcuts.dialogs import input_dialog
7+
8+
9+
def main():
10+
result = input_dialog(
11+
title='Input dialog example',
12+
text='Please type your name:')
13+
14+
print('Result = {}'.format(result))
15+
16+
17+
if __name__ == '__main__':
18+
main()

examples/dialogs/messagebox.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/env python
2+
"""
3+
Example of a message box window.
4+
"""
5+
from __future__ import unicode_literals
6+
from prompt_toolkit.shortcuts.dialogs import message_dialog
7+
8+
9+
def main():
10+
message_dialog(
11+
title='Example dialog window',
12+
text='Do you want to continue?\nPress ENTER to quit.')
13+
14+
15+
if __name__ == '__main__':
16+
main()
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env python
2+
"""
3+
Example of an password input dialog.
4+
"""
5+
from __future__ import unicode_literals
6+
from prompt_toolkit.shortcuts.dialogs import input_dialog
7+
8+
9+
def main():
10+
result = input_dialog(
11+
title='Password dialog example',
12+
text='Please type your password:',
13+
password=True)
14+
15+
print('Result = {}'.format(result))
16+
17+
18+
if __name__ == '__main__':
19+
main()
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env python
2+
"""
3+
Example of a progress bar dialog.
4+
"""
5+
from __future__ import unicode_literals
6+
from prompt_toolkit.shortcuts.dialogs import progress_dialog
7+
import time
8+
import os
9+
10+
11+
def worker(set_percentage, log_text):
12+
"""
13+
This worker function is called by `progress_dialog`. It will run in a
14+
background thread.
15+
16+
The `set_percentage` function can be used to update the progress bar, while
17+
the `log_text` function can be used to log text in the logging window.
18+
"""
19+
percentage = 0
20+
for dirpath, dirnames, filenames in os.walk('../..'):
21+
for f in filenames:
22+
log_text('{} / {}\n'.format(dirpath, f))
23+
set_percentage(percentage + 1)
24+
percentage += 2
25+
time.sleep(.1)
26+
27+
if percentage == 100:
28+
break
29+
if percentage == 100:
30+
break
31+
32+
# Show 100% for a second, before quitting.
33+
set_percentage(100)
34+
time.sleep(1)
35+
return
36+
37+
38+
def main():
39+
progress_dialog(
40+
title='Progress dialog example',
41+
text='As an examples, we walk through the filesystem and print '
42+
'all directories',
43+
run_callback=worker)
44+
45+
46+
if __name__ == '__main__':
47+
main()

examples/dialogs/radio_dialog.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env python
2+
"""
3+
Example of a radio list box dialog.
4+
"""
5+
from __future__ import unicode_literals
6+
from prompt_toolkit.shortcuts.dialogs import radiolist_dialog
7+
8+
9+
def main():
10+
result = radiolist_dialog(
11+
values=[
12+
('red', 'Red'),
13+
('green', 'Green'),
14+
('blue', 'Blue'),
15+
('orange', 'Orange'),
16+
],
17+
title='Radiolist dialog example',
18+
text='Please select a color:')
19+
20+
print('Result = {}'.format(result))
21+
22+
23+
if __name__ == '__main__':
24+
main()
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env python
2+
"""
3+
Example of a style dialog window.
4+
All dialog shortcuts take a `style` argument in order to apply a custom
5+
styling.
6+
"""
7+
from __future__ import unicode_literals
8+
from prompt_toolkit.shortcuts.dialogs import message_dialog
9+
from prompt_toolkit.styles import style_from_dict
10+
from prompt_toolkit.token import Token
11+
12+
13+
# Custom color scheme.
14+
example_style = style_from_dict({
15+
Token.Dialog: 'bg:#88ff88',
16+
Token.Dialog.Body: 'bg:#000000 #00ff00',
17+
Token.Dialog | Token.Frame.Label: 'bg:#ffffff #000000',
18+
Token.Dialog.Body | Token.Shadow: 'bg:#00aa00',
19+
})
20+
21+
22+
def main():
23+
message_dialog(
24+
title='Styled dialog window',
25+
text='Do you want to continue?\nPress ENTER to quit.',
26+
style=example_style)
27+
28+
29+
if __name__ == '__main__':
30+
main()

examples/dialogs/yes_no_dialog.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/usr/bin/env python
2+
"""
3+
Example of confirmation (yes/no) dialog window.
4+
"""
5+
from __future__ import unicode_literals
6+
from prompt_toolkit.shortcuts.dialogs import yes_no_dialog
7+
8+
9+
def main():
10+
result = yes_no_dialog(
11+
title='Yes/No dialog example',
12+
text='Do you want to confirm?')
13+
14+
print('Result = {}'.format(result))
15+
16+
17+
if __name__ == '__main__':
18+
main()

0 commit comments

Comments
 (0)