Filters¶
Many places in quo require a boolean value that can change over time. For instance:
to specify whether a formatted text needs to be bold or in italic.
to specify whether a part of the layout needs to be visible or not;
or to decide whether a certain key binding needs to be active or not.
These booleans are often dynamic and can change at runtime. For instance, the
search toolbar should only be visible when the user is actually searching (when
the search buffer has the focus). The wrap_lines
option could be changed
with a certain key binding. And that key binding could only work when the
default buffer got the focus.
In quo, we decided to reduce the amount of state in the whole framework, and apply a simple kind of reactive programming to describe the flow of these booleans as expressions. (It’s one-way only: if a key binding needs to know whether it’s active or not, it can follow this flow by evaluating an expression.)
The (abstract) base class is Filter
, which
wraps an expression that takes no input and evaluates to a boolean. Getting the
state of a filter is done by simply calling it.
The most obvious way to create such a Filter
instance is by creating a Condition
instance
from a function. For instance, the following condition will evaluate to True
when the user is searching:
from quo.console import get_app
from quo.filters import Condition
@Condition
def is_searching():
return get_app().is_searching
This filter can then be used in a key binding, like in the following snippet:»
from quo.keys import bind
@bind.add('ctrl-t', filter=is_searching)
def _(event):
# Do, something, but only when searching.
pass
If we want to know the boolean value of this filter, we have to call it like a function:
print(is_searching())
Built-in filters
¶
There are many built-in filters, ready to use. All of them have a lowercase name, because they represent the wrapped function underneath, and can be called as a function.
has_arg
has_completions
has_focus
buffer_has_focus
has_selection
has_validation_error
is_aborting
is_done
is_read_only
is_multiline
renderer_height_is_known
in_editing_mode
in_paste_mode
vi_mode
vi_navigation_mode
vi_insert_mode
vi_insert_multiple_mode
vi_replace_mode
vi_selection_mode
vi_waiting_for_text_object_mode
vi_digraph_mode
emacs_mode
emacs_insert_mode
emacs_selection_mode
is_searching
control_is_searchable
vi_search_direction_reversed
Combining filters
¶
Filters can be chained with the &
(AND) and |
(OR) operators and
negated with the ~
(negation) operator.
Some examples:
from quo.keys import bind
@bind.add('ctrl-t', filter=~is_searching)
def _(event):
" Do something, but not while searching. "
pass
@bind.add('ctrl-t', filter=has_search | has_selection)
def _(event):
" Do something, but only when searching or when there is a selection. "
pass
to_filter
¶
Finally, in many situations you want your code to expose an API that is able to deal with both booleans as well as filters. For instance, when for most users a boolean works fine because they don’t need to change the value over time, while some advanced users want to be able this value to a certain setting or event that does changes over time.
In order to handle both use cases, there is a utility called
to_filter()
.
This is a function that takes either a boolean or an actual Filter
instance, and always returns a Filter
.
from quo.filters import Condition, to_filter, has_search, has_selection
# In each of the following three examples, 'f' will be a `Filter`
# instance.
f = to_filter(True)
f = to_filter(False)
f = to_filter(Condition(lambda: True))
f = to_filter(has_search | has_selection)