1"""
2Filters that accept a `Application` as argument.
3"""
4
5from __future__ import annotations
6
7from typing import TYPE_CHECKING, cast
8
9from prompt_toolkit.application.current import get_app
10from prompt_toolkit.cache import memoized
11from prompt_toolkit.enums import EditingMode
12
13from .base import Condition
14
15if TYPE_CHECKING:
16 from prompt_toolkit.layout.layout import FocusableElement
17
18
19__all__ = [
20 "has_arg",
21 "has_completions",
22 "completion_is_selected",
23 "has_focus",
24 "buffer_has_focus",
25 "has_selection",
26 "has_suggestion",
27 "has_validation_error",
28 "is_done",
29 "is_read_only",
30 "is_multiline",
31 "renderer_height_is_known",
32 "in_editing_mode",
33 "in_paste_mode",
34 "vi_mode",
35 "vi_navigation_mode",
36 "vi_insert_mode",
37 "vi_insert_multiple_mode",
38 "vi_replace_mode",
39 "vi_selection_mode",
40 "vi_waiting_for_text_object_mode",
41 "vi_digraph_mode",
42 "vi_recording_macro",
43 "emacs_mode",
44 "emacs_insert_mode",
45 "emacs_selection_mode",
46 "shift_selection_mode",
47 "is_searching",
48 "control_is_searchable",
49 "vi_search_direction_reversed",
50]
51
52
53# NOTE: `has_focus` below should *not* be `memoized`. It can reference any user
54# control. For instance, if we would continuously create new
55# `PromptSession` instances, then previous instances won't be released,
56# because this memoize (which caches results in the global scope) will
57# still refer to each instance.
58def has_focus(value: FocusableElement) -> Condition:
59 """
60 Enable when this buffer has the focus.
61 """
62 from prompt_toolkit.buffer import Buffer
63 from prompt_toolkit.layout import walk
64 from prompt_toolkit.layout.containers import Container, Window, to_container
65 from prompt_toolkit.layout.controls import UIControl
66
67 if isinstance(value, str):
68
69 def test() -> bool:
70 return get_app().current_buffer.name == value
71
72 elif isinstance(value, Buffer):
73
74 def test() -> bool:
75 return get_app().current_buffer == value
76
77 elif isinstance(value, UIControl):
78
79 def test() -> bool:
80 return get_app().layout.current_control == value
81
82 else:
83 value = to_container(value)
84
85 if isinstance(value, Window):
86
87 def test() -> bool:
88 return get_app().layout.current_window == value
89
90 else:
91
92 def test() -> bool:
93 # Consider focused when any window inside this container is
94 # focused.
95 current_window = get_app().layout.current_window
96
97 for c in walk(cast(Container, value)):
98 if isinstance(c, Window) and c == current_window:
99 return True
100 return False
101
102 @Condition
103 def has_focus_filter() -> bool:
104 return test()
105
106 return has_focus_filter
107
108
109@Condition
110def buffer_has_focus() -> bool:
111 """
112 Enabled when the currently focused control is a `BufferControl`.
113 """
114 return get_app().layout.buffer_has_focus
115
116
117@Condition
118def has_selection() -> bool:
119 """
120 Enable when the current buffer has a selection.
121 """
122 return bool(get_app().current_buffer.selection_state)
123
124
125@Condition
126def has_suggestion() -> bool:
127 """
128 Enable when the current buffer has a suggestion.
129 """
130 buffer = get_app().current_buffer
131 return buffer.suggestion is not None and buffer.suggestion.text != ""
132
133
134@Condition
135def has_completions() -> bool:
136 """
137 Enable when the current buffer has completions.
138 """
139 state = get_app().current_buffer.complete_state
140 return state is not None and len(state.completions) > 0
141
142
143@Condition
144def completion_is_selected() -> bool:
145 """
146 True when the user selected a completion.
147 """
148 complete_state = get_app().current_buffer.complete_state
149 return complete_state is not None and complete_state.current_completion is not None
150
151
152@Condition
153def is_read_only() -> bool:
154 """
155 True when the current buffer is read only.
156 """
157 return get_app().current_buffer.read_only()
158
159
160@Condition
161def is_multiline() -> bool:
162 """
163 True when the current buffer has been marked as multiline.
164 """
165 return get_app().current_buffer.multiline()
166
167
168@Condition
169def has_validation_error() -> bool:
170 "Current buffer has validation error."
171 return get_app().current_buffer.validation_error is not None
172
173
174@Condition
175def has_arg() -> bool:
176 "Enable when the input processor has an 'arg'."
177 return get_app().key_processor.arg is not None
178
179
180@Condition
181def is_done() -> bool:
182 """
183 True when the CLI is returning, aborting or exiting.
184 """
185 return get_app().is_done
186
187
188@Condition
189def renderer_height_is_known() -> bool:
190 """
191 Only True when the renderer knows it's real height.
192
193 (On VT100 terminals, we have to wait for a CPR response, before we can be
194 sure of the available height between the cursor position and the bottom of
195 the terminal. And usually it's nicer to wait with drawing bottom toolbars
196 until we receive the height, in order to avoid flickering -- first drawing
197 somewhere in the middle, and then again at the bottom.)
198 """
199 return get_app().renderer.height_is_known
200
201
202@memoized()
203def in_editing_mode(editing_mode: EditingMode) -> Condition:
204 """
205 Check whether a given editing mode is active. (Vi or Emacs.)
206 """
207
208 @Condition
209 def in_editing_mode_filter() -> bool:
210 return get_app().editing_mode == editing_mode
211
212 return in_editing_mode_filter
213
214
215@Condition
216def in_paste_mode() -> bool:
217 return get_app().paste_mode()
218
219
220@Condition
221def vi_mode() -> bool:
222 return get_app().editing_mode == EditingMode.VI
223
224
225@Condition
226def vi_navigation_mode() -> bool:
227 """
228 Active when the set for Vi navigation key bindings are active.
229 """
230 from prompt_toolkit.key_binding.vi_state import InputMode
231
232 app = get_app()
233
234 if (
235 app.editing_mode != EditingMode.VI
236 or app.vi_state.operator_func
237 or app.vi_state.waiting_for_digraph
238 or app.current_buffer.selection_state
239 ):
240 return False
241
242 return (
243 app.vi_state.input_mode == InputMode.NAVIGATION
244 or app.vi_state.temporary_navigation_mode
245 or app.current_buffer.read_only()
246 )
247
248
249@Condition
250def vi_insert_mode() -> bool:
251 from prompt_toolkit.key_binding.vi_state import InputMode
252
253 app = get_app()
254
255 if (
256 app.editing_mode != EditingMode.VI
257 or app.vi_state.operator_func
258 or app.vi_state.waiting_for_digraph
259 or app.current_buffer.selection_state
260 or app.vi_state.temporary_navigation_mode
261 or app.current_buffer.read_only()
262 ):
263 return False
264
265 return app.vi_state.input_mode == InputMode.INSERT
266
267
268@Condition
269def vi_insert_multiple_mode() -> bool:
270 from prompt_toolkit.key_binding.vi_state import InputMode
271
272 app = get_app()
273
274 if (
275 app.editing_mode != EditingMode.VI
276 or app.vi_state.operator_func
277 or app.vi_state.waiting_for_digraph
278 or app.current_buffer.selection_state
279 or app.vi_state.temporary_navigation_mode
280 or app.current_buffer.read_only()
281 ):
282 return False
283
284 return app.vi_state.input_mode == InputMode.INSERT_MULTIPLE
285
286
287@Condition
288def vi_replace_mode() -> bool:
289 from prompt_toolkit.key_binding.vi_state import InputMode
290
291 app = get_app()
292
293 if (
294 app.editing_mode != EditingMode.VI
295 or app.vi_state.operator_func
296 or app.vi_state.waiting_for_digraph
297 or app.current_buffer.selection_state
298 or app.vi_state.temporary_navigation_mode
299 or app.current_buffer.read_only()
300 ):
301 return False
302
303 return app.vi_state.input_mode == InputMode.REPLACE
304
305
306@Condition
307def vi_replace_single_mode() -> bool:
308 from prompt_toolkit.key_binding.vi_state import InputMode
309
310 app = get_app()
311
312 if (
313 app.editing_mode != EditingMode.VI
314 or app.vi_state.operator_func
315 or app.vi_state.waiting_for_digraph
316 or app.current_buffer.selection_state
317 or app.vi_state.temporary_navigation_mode
318 or app.current_buffer.read_only()
319 ):
320 return False
321
322 return app.vi_state.input_mode == InputMode.REPLACE_SINGLE
323
324
325@Condition
326def vi_selection_mode() -> bool:
327 app = get_app()
328 if app.editing_mode != EditingMode.VI:
329 return False
330
331 return bool(app.current_buffer.selection_state)
332
333
334@Condition
335def vi_waiting_for_text_object_mode() -> bool:
336 app = get_app()
337 if app.editing_mode != EditingMode.VI:
338 return False
339
340 return app.vi_state.operator_func is not None
341
342
343@Condition
344def vi_digraph_mode() -> bool:
345 app = get_app()
346 if app.editing_mode != EditingMode.VI:
347 return False
348
349 return app.vi_state.waiting_for_digraph
350
351
352@Condition
353def vi_recording_macro() -> bool:
354 "When recording a Vi macro."
355 app = get_app()
356 if app.editing_mode != EditingMode.VI:
357 return False
358
359 return app.vi_state.recording_register is not None
360
361
362@Condition
363def emacs_mode() -> bool:
364 "When the Emacs bindings are active."
365 return get_app().editing_mode == EditingMode.EMACS
366
367
368@Condition
369def emacs_insert_mode() -> bool:
370 app = get_app()
371 if (
372 app.editing_mode != EditingMode.EMACS
373 or app.current_buffer.selection_state
374 or app.current_buffer.read_only()
375 ):
376 return False
377 return True
378
379
380@Condition
381def emacs_selection_mode() -> bool:
382 app = get_app()
383 return bool(
384 app.editing_mode == EditingMode.EMACS and app.current_buffer.selection_state
385 )
386
387
388@Condition
389def shift_selection_mode() -> bool:
390 app = get_app()
391 return bool(
392 app.current_buffer.selection_state
393 and app.current_buffer.selection_state.shift_mode
394 )
395
396
397@Condition
398def is_searching() -> bool:
399 "When we are searching."
400 app = get_app()
401 return app.layout.is_searching
402
403
404@Condition
405def control_is_searchable() -> bool:
406 "When the current UIControl is searchable."
407 from prompt_toolkit.layout.controls import BufferControl
408
409 control = get_app().layout.current_control
410
411 return (
412 isinstance(control, BufferControl) and control.search_buffer_control is not None
413 )
414
415
416@Condition
417def vi_search_direction_reversed() -> bool:
418 "When the '/' and '?' key bindings for Vi-style searching have been reversed."
419 return get_app().reverse_vi_search_direction()