Advanced Usage

Display Function

from typing import Any
from curses_fzf import FuzzyFinder

def display_name_property(item: Any) -> str:
    return item.name

fzf = FuzzyFinder(display=display_name_property)
result = fzf.find(data)

Since FuzzyFinder allows you to work with lists of any type of items, you may want to define a custom behavior of how it displays your items. In the above example we have a list of objects, using their name property to represent each item in filtered list.

The display() function must return a single line of text. A CursesFzfAssertion exception will be raised, if the function returns multi-line text. If you want to present more complex information, have a look at the preview() function.

The default behavior is to stringify the item provided:

FuzzyFinder(display=lambda item: str(item))

Related examples:

Preselect Function

from typing import Any
from curses_fzf import FuzzyFinder, ScoringResult

def preselect_items(item: Any, scoring_result: ScoringResult) -> bool:
    return item.get("calories", 999) < 400

fzf = FuzzyFinder(multi=True, preselect=preselect_items)
result = fzf.find(data)

If you use FuzzyFinder in multi selection mode, you can pre-select some items using the preselect() function. This function is expected to return True if the item should be selected.

The default implementation always returns False.

Related examples:

Image: multi select dicts with simple preview

Preview Function

import curses
from typing import Any
from curses_fzf import FuzzyFinder, ScoringResult, Color, ColorTheme

def my_preview(preview_window: curses.window, color_theme: ColorTheme, item: Any, result: ScoringResult) -> str:
    preview_window.addstr(1, 1, item.description, curses.color_pair(Color.RED))
    return ""

fzf = FuzzyFinder(preview=my_preview, preview_window_percentage=50)
result = fzf.find(data)

The preview() function (default None), if set, will show a preview window on the right side of the FuzzyFinder main window. You can use this window to present additional information about the item.

There are two possible ways to use this function:

Either you ignore the provided preview_window and simply return a string, that can also be a multi-line string. The FuzzyFinder will take care of the text not leaking out of the window boundaries. For example you can yaml.dump() dict items.

Or you return an empty string and use preview_window to modify the curses.window manually. If you do so, you should ensure to handle window boundaries correctly to avoid crashes, e.g. on terminal resizing. See ColorTheme for information on coloring, the selected color_theme is also provided to the preview() function for easy access.

Not only the item from filtered list is provided, but also the ScoringResult. This allows to display scoring related information.

You can use preview_window_percentage parameter of FuzzyFinder to define the width of the preview window. The default value is 40 percent of the terminal window. Don’t worry that the preview window might hide portions of your items, you can toggle the preview window any time using Ctrl+P.

Related examples:

Image: curses preview with scoring information

Scoring Function

from typing import Any
from curses_fzf import FuzzyFinder, ScoringResult

def my_scoring(query: str, candidate: str) -> ScoringResult:
    sr = ScoringResult(query, candidate)
    if sr.check_query_empty():
        return sr
    # ... scoring logic
    sr.add_match(match_index, matched_word, match_score)
    # ...
    return sr

fzf = FuzzyFinder(score=my_scoring)
result = fzf.find(data)

FuzzyFinder comes with built-in scoring functions (default scoring_fzf()). Scoring determines if an item is considered to match the query the user entered. The higher the score the higher the item gets sorted among the matches in the filtered list. If the score is 0 the item is considered to not be a match, it will not be displayed in the list at all.

A scoring function retrieves the query as its first argument and the candidate to match as the second. The candidate is the display() string of the item in question.

The function is supposed to return a ScoringResult.

Related examples:

ColorTheme Customization

from curses_fzf import FuzzyFinder, ColorTheme, Color

fzf = FuzzyFinder(color_theme=ColorTheme(text=Color.CYAN))
result = fzf.find(data)

ColorTheme can be used to customize text colors, e.g. to increase readability. Use the indexes defined via Color enum. If you want to register your own curses.color_pairs, the indexes 1 to 29 are safe to use.

Related examples:

Keymap And Custom Keybindings

import curses
from curses_fzf import FuzzyFinder

fzf = FuzzyFinder()
fzf.keymap[curses.KEY_F2] = {
    "function": lambda: fzf.kb_move_items_cursor_relative(-2),
    "key": "F2",
    "description": "Move cursor up by 2 items",
    "category": "Custom Keybindings"
}
result = fzf.find(data)

FuzzyFinder is designed to be highly customizable. For example you can define your own keybindings by modifying the keymap dictionary. The keys are the key codes as returned by curses.getch(), the values are dicts containing the function to be called and optional metadata such as the key name, description, and category for the help screen.

The minimal requirement for a keybinding is to provide a "function" key, its value will be called as a function when the user presses the corresponding keybinding.

If you also provide a "key" and "description", this keybinding will be shown in the help screen, which can be opened by pressing F1. You can also provide an optional "category" to group your keybindings in the help screen, the default category is "General Keybindings".

Related examples: