Line Editor
The blessed.line_editor module provides LineEditor,
a headless single-line editor with readline-style keybindings, grapheme-aware cursor
movement, auto-suggest from history, password masking, and horizontal scrolling.
Note
The editor never writes to the terminal directly, your application controls when and where output appears.
Use the built-in render methods to produce
ready-to-print escape sequences, or read
display for raw display state
if you need fully custom rendering.
Overview
Feed keystrokes (from inkey() or async_inkey()) into
feed_key(). It returns a
LineEditResult with these fields:
linethe accepted string when Enter is pressed, otherwiseNone.interrupt,eof:Trueon Ctrl+C / Ctrl+D respectively.changedTruewhen the display needs redrawing.bella bell string to emit (empty when silent).
For bracketed paste, feed the pasted text through
insert_text() instead of
feed_key().
For async usage, simply replace term.inkey() with await term.async_inkey().
See Async Input in the keyboard documentation for more on async_inkey.
Example
A line editor with history, auto-suggest, password mode, and styled rendering:
1#!/usr/bin/env python3
2"""Line editor demo with clipboard, bracketed paste, history, and scrolling."""
3from functools import partial
4
5from blessed import Terminal
6from blessed.line_editor import LineEditor, LineHistory, LineEditResult
7
8term = Terminal()
9history = LineHistory()
10echo = partial(print, end='\r\n', flush=True)
11
12has_clipboard = term.does_osc52_clipboard()
13
14
15def copy_line(ed):
16 term.clipboard_copy(ed.line)
17 return LineEditResult()
18
19
20def paste_line(ed):
21 text = term.clipboard_paste()
22 return ed.insert_text(text) if text else LineEditResult()
23
24
25with term.raw(), term.cursor_shape(term.CursorShape.BLINKING_BLOCK), term.bracketed_paste():
26 if has_clipboard:
27 echo("press ^C and ^V for OS clipboard, type 'quit' to exit")
28 else:
29 echo("type 'quit' to exit")
30 echo()
31 while True:
32 margin = max(1, term.width // 5)
33 width = term.width - margin * 2
34 prompt = "Prompt> "
35 col = margin
36 ed_width = width - len(prompt)
37 keymap = {}
38 if has_clipboard:
39 keymap['KEY_CTRL_C'] = copy_line
40 keymap['KEY_CTRL_V'] = paste_line
41 ed = LineEditor(history=history, max_width=ed_width, limit=200,
42 bg_sgr=term.on_brown,
43 keymap=keymap or None)
44 echo(term.move_x(col) + prompt, end='')
45 row = term.get_location()[0]
46 ed_col = col + len(prompt)
47 echo(ed.render(term, row, ed_width, col=ed_col), end='')
48 while True:
49 key = term.inkey()
50 if key.name == 'BRACKETED_PASTE':
51 result = ed.insert_text(key.text)
52 else:
53 result = ed.feed_key(key)
54 if result.bell:
55 echo(result.bell, end='')
56 if result.changed:
57 key_str = str(key)
58 out = None
59 if key_str and key_str.isprintable() and len(key_str) == 1:
60 out = ed.render_insert(term, row, key_str)
61 elif getattr(key, "name", None) == "KEY_BACKSPACE":
62 out = ed.render_backspace(term, row)
63 if out is None:
64 out = ed.render(term, row, ed_width, col=ed_col)
65 echo(out, end='')
66 if result.line is not None:
67 echo()
68 if result.line:
69 echo(term.move_x(col) + f" => {result.line!r}")
70 break
71 if (result.line or '').strip() == 'quit':
72 break
History
LineHistory stores command history in memory.
History navigation (Up/Down) and auto-suggest (type a prefix, press Right to accept)
are enabled automatically when a history instance is attached to the editor.
For on-disk persistence, read/write the entries list directly (most recent
entry last).
Display State & Styling
Each call to display returns a
DisplayState with the visible text, cursor position,
suggestion suffix, and clipping indicators.
The editor ships with default SGR styling (light cream text, dark suggestion).
Override with text_sgr, suggestion_sgr, bg_sgr, or ellipsis_sgr:
editor = LineEditor(bg_sgr=term.on_brown, max_width=term.width)
When max_width is set and text overflows, overflow_left and
overflow_right indicate which edges are truncated. Use
ellipsis_sgr to style the overflow indicator.
Rendering Helpers
LineEditor provides three render methods that
build complete escape-sequence strings from the current display state:
render()Full redraw, always produces correct output.
render_insert()Fast-path after a character insert at end of buffer. Returns
Nonewhen a full redraw is needed instead.render_backspace()Fast-path after a backspace at end of buffer. Returns
Nonewhen a full redraw is needed instead.
Try the fast-path first and fall back to render() when it returns
None. See the example above for the complete
pattern.
Other Methods
clear()Reset the buffer, cursor, and undo history.
set_password_mode()Toggle password masking on or off mid-session.
Constructor Options
Beyond the styling and keybinding options shown above, LineEditor
accepts:
passwordIf
True, start in password mode (characters are masked). Toggle at runtime withset_password_mode().password_charReplacement character shown in password mode (default
"⚻").limitMaximum buffer length in characters (default 65536).
limit_bellBell string emitted when the limit is reached (default
"\\a").scroll_jumpFraction of
max_widthto scroll when the cursor overflows (default 0.5).
Custom Keybindings
Pass a keymap dict to override or extend the default emacs/readline bindings.
Keys are Keystroke .name strings (e.g. "KEY_CTRL_K"), values are
callables accepting a LineEditor and returning a
LineEditResult.
Override an existing binding:
def my_enter(editor):
# custom accept logic
return LineEditResult(line=editor.line, changed=True)
editor = LineEditor(keymap={"KEY_ENTER": my_enter})
Add a new binding:
def handle_f1(editor):
editor.insert_text("help!")
return LineEditResult(changed=True)
editor = LineEditor(keymap={"KEY_F1": handle_f1})
Disable a binding by setting it to None:
# Ctrl+C becomes a silent no-op instead of raising interrupt
editor = LineEditor(keymap={"KEY_CTRL_C": None})
Default Keybindings
These are the default emacs/readline bindings. All can be overridden via keymap.
Key |
Action |
|---|---|
Enter |
Accept line |
Ctrl+C |
Cancel (interrupt) |
Ctrl+D |
EOF on empty line, delete at cursor otherwise |
Left, Ctrl+B |
Move cursor left |
Right |
Accept auto-suggest at end, otherwise move right |
Ctrl+F |
Move cursor right |
Home, Ctrl+A |
Move to start of line |
End, Ctrl+E |
Move to end of line |
Shift+Left, Ctrl+Left |
Move word left |
Shift+Right, Ctrl+Right |
Move word right |
Backspace |
Delete character before cursor |
Delete |
Delete character at cursor |
Ctrl+K |
Kill to end of line |
Ctrl+U |
Kill to start of line |
Ctrl+W |
Kill word backward |
Ctrl+Y |
Yank (paste from kill ring) |
Up, Ctrl+P |
Previous history entry |
Down, Ctrl+N |
Next history entry |
Ctrl+Z |
Undo |