or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application.mdcompletion.mdindex.mdkey-bindings.mdlayout.mdprompts.mdstyling.md

key-bindings.mddocs/

0

# Key Bindings and Input

1

2

Configurable key binding system supporting Vi and Emacs editing modes with customizable key mappings. The key binding system provides the foundation for all keyboard interaction in prompt-toolkit applications.

3

4

## Capabilities

5

6

### Key Binding Containers

7

8

Core classes for managing and organizing key bindings.

9

10

```python { .api }

11

class KeyBindings:

12

def __init__(self):

13

"""Create mutable key binding container."""

14

15

def add(self, *keys, **kwargs):

16

"""

17

Decorator to add key binding.

18

19

Parameters:

20

- *keys: Key sequence (e.g., 'c-c', 'a', 'escape c-c')

21

- filter: Filter determining when binding is active

22

- eager: bool, execute immediately without waiting for more keys

23

- is_global: bool, binding applies globally

24

- save_before: Function to call before executing

25

- record_in_macro: bool, record action in Vi macro

26

27

Returns:

28

Decorator function

29

"""

30

31

def remove(self, *keys):

32

"""

33

Remove key binding.

34

35

Parameters:

36

- *keys: Key sequence to remove

37

"""

38

39

def get_bindings_for_keys(self, keys):

40

"""

41

Get bindings matching key sequence.

42

43

Parameters:

44

- keys: Tuple of key presses

45

46

Returns:

47

List of matching bindings

48

"""

49

50

def get_bindings_starting_with_keys(self, keys):

51

"""

52

Get bindings that start with key sequence.

53

54

Parameters:

55

- keys: Tuple of key presses

56

57

Returns:

58

List of bindings starting with keys

59

"""

60

61

class KeyBindingsBase:

62

"""Abstract base class for key binding containers."""

63

64

def get_bindings_for_keys(self, keys):

65

"""Get bindings for key sequence."""

66

67

def get_bindings_starting_with_keys(self, keys):

68

"""Get bindings starting with key sequence."""

69

70

class ConditionalKeyBindings(KeyBindingsBase):

71

def __init__(self, key_bindings, filter):

72

"""

73

Key bindings active only when filter is true.

74

75

Parameters:

76

- key_bindings: KeyBindings instance to wrap

77

- filter: Filter determining when bindings are active

78

"""

79

80

class DynamicKeyBindings(KeyBindingsBase):

81

def __init__(self, get_key_bindings):

82

"""

83

Dynamic key bindings based on function.

84

85

Parameters:

86

- get_key_bindings: Function returning KeyBindings instance

87

"""

88

89

def merge_key_bindings(bindings_list):

90

"""

91

Merge multiple key binding objects.

92

93

Parameters:

94

- bindings_list: List of KeyBindings instances

95

96

Returns:

97

Merged KeyBindings instance

98

"""

99

```

100

101

### Key Press Events

102

103

Classes representing key press events and their context.

104

105

```python { .api }

106

class KeyPress:

107

def __init__(self, key, data=""):

108

"""

109

Represent a single key press.

110

111

Parameters:

112

- key: str, key identifier

113

- data: str, additional key data

114

"""

115

116

@property

117

def key(self):

118

"""str: The key identifier."""

119

120

@property

121

def data(self):

122

"""str: Additional key data."""

123

124

class KeyPressEvent:

125

def __init__(self, key_press, previous_key_sequence=None):

126

"""

127

Key press event with application context.

128

129

Parameters:

130

- key_press: KeyPress instance

131

- previous_key_sequence: Previous key sequence

132

"""

133

134

@property

135

def key_sequence(self):

136

"""List[KeyPress]: Complete key sequence."""

137

138

@property

139

def data(self):

140

"""str: Key data from current key press."""

141

142

@property

143

def app(self):

144

"""Application: Current application instance."""

145

146

@property

147

def current_buffer(self):

148

"""Buffer: Currently focused buffer."""

149

150

@property

151

def arg(self):

152

"""int: Numeric argument if provided."""

153

154

@property

155

def is_repeat(self):

156

"""bool: True if this is a repeat event."""

157

```

158

159

### Key Constants

160

161

Enumeration and constants for key identifiers.

162

163

```python { .api }

164

class Keys:

165

"""Key constant definitions."""

166

167

# Control keys

168

ControlA = "c-a"

169

ControlB = "c-b"

170

ControlC = "c-c"

171

ControlD = "c-d"

172

ControlE = "c-e"

173

ControlF = "c-f"

174

ControlG = "c-g"

175

ControlH = "c-h"

176

ControlI = "c-i"

177

ControlJ = "c-j"

178

ControlK = "c-k"

179

ControlL = "c-l"

180

ControlM = "c-m"

181

ControlN = "c-n"

182

ControlO = "c-o"

183

ControlP = "c-p"

184

ControlQ = "c-q"

185

ControlR = "c-r"

186

ControlS = "c-s"

187

ControlT = "c-t"

188

ControlU = "c-u"

189

ControlV = "c-v"

190

ControlW = "c-w"

191

ControlX = "c-x"

192

ControlY = "c-y"

193

ControlZ = "c-z"

194

195

# Special keys

196

Enter = "\r"

197

Escape = "\x1b"

198

Backspace = "\x7f"

199

Delete = "\x1b[3~"

200

Insert = "\x1b[2~"

201

Home = "\x1b[H"

202

End = "\x1b[F"

203

PageUp = "\x1b[5~"

204

PageDown = "\x1b[6~"

205

206

# Arrow keys

207

Up = "\x1b[A"

208

Down = "\x1b[B"

209

Right = "\x1b[C"

210

Left = "\x1b[D"

211

212

# Function keys

213

F1 = "\x1bOP"

214

F2 = "\x1bOQ"

215

F3 = "\x1bOR"

216

F4 = "\x1bOS"

217

F5 = "\x1b[15~"

218

F6 = "\x1b[17~"

219

F7 = "\x1b[18~"

220

F8 = "\x1b[19~"

221

F9 = "\x1b[20~"

222

F10 = "\x1b[21~"

223

F11 = "\x1b[23~"

224

F12 = "\x1b[24~"

225

226

# Meta/Alt keys

227

Escape = "\x1b"

228

229

# Shift combinations

230

ShiftTab = "\x1b[Z"

231

232

# Special characters

233

Tab = "\t"

234

Space = " "

235

236

ALL_KEYS = [

237

# List of all available key constants

238

Keys.ControlA, Keys.ControlB, Keys.ControlC,

239

# ... (complete list of all keys)

240

]

241

```

242

243

### Built-in Key Binding Sets

244

245

Pre-configured key binding sets for common editing modes.

246

247

```python { .api }

248

def load_vi_bindings():

249

"""

250

Load Vi-style key bindings.

251

252

Returns:

253

KeyBindings instance with Vi key mappings

254

255

Key bindings include:

256

- Normal mode: h/j/k/l navigation, i/a/o insert modes

257

- Insert mode: standard text editing

258

- Command mode: search, replace, file operations

259

- Visual mode: text selection and manipulation

260

"""

261

262

def load_emacs_bindings():

263

"""

264

Load Emacs-style key bindings.

265

266

Returns:

267

KeyBindings instance with Emacs key mappings

268

269

Key bindings include:

270

- Cursor movement: C-f/C-b/C-n/C-p

271

- Text editing: C-k/C-y/C-d/C-h

272

- Search: C-s/C-r for incremental search

273

- Word operations: M-f/M-b/M-d

274

"""

275

276

def load_basic_bindings():

277

"""

278

Load basic key bindings for simple editing.

279

280

Returns:

281

KeyBindings instance with basic key mappings

282

"""

283

284

def load_auto_suggest_bindings():

285

"""

286

Load key bindings for auto-suggestion features.

287

288

Returns:

289

KeyBindings instance for auto-suggest interaction

290

"""

291

292

def load_open_in_editor_bindings():

293

"""

294

Load key bindings for opening content in external editor.

295

296

Returns:

297

KeyBindings instance for editor integration

298

"""

299

300

def load_page_navigation_bindings():

301

"""

302

Load key bindings for page navigation.

303

304

Returns:

305

KeyBindings instance for page up/down navigation

306

"""

307

```

308

309

### Key Processing

310

311

Classes for processing key sequences and executing bindings.

312

313

```python { .api }

314

class KeyProcessor:

315

def __init__(self, key_bindings):

316

"""

317

Process key sequences and execute bindings.

318

319

Parameters:

320

- key_bindings: KeyBindings instance

321

"""

322

323

def feed(self, key_press):

324

"""

325

Feed key press to processor.

326

327

Parameters:

328

- key_press: KeyPress instance

329

330

Returns:

331

List of actions to execute

332

"""

333

334

def reset(self):

335

"""Reset key processor state."""

336

337

def empty(self):

338

"""

339

Check if processor has pending keys.

340

341

Returns:

342

bool: True if no pending keys

343

"""

344

345

def generate_completions(key_processor):

346

"""

347

Generate possible key completions.

348

349

Parameters:

350

- key_processor: KeyProcessor instance

351

352

Yields:

353

Possible key completion sequences

354

"""

355

```

356

357

### Vi Mode State

358

359

Classes for managing Vi editor mode state.

360

361

```python { .api }

362

class ViState:

363

def __init__(self):

364

"""Vi editor mode state management."""

365

366

@property

367

def input_mode(self):

368

"""InputMode: Current Vi input mode."""

369

370

@input_mode.setter

371

def input_mode(self, value):

372

"""Set Vi input mode."""

373

374

@property

375

def waiting_for_digraph(self):

376

"""bool: True if waiting for digraph input."""

377

378

@property

379

def operator_func(self):

380

"""Function: Current operator function."""

381

382

@property

383

def yank_buffer(self):

384

"""str: Current yank buffer content."""

385

386

class InputMode(Enum):

387

"""Vi input modes."""

388

INSERT = "vi-insert"

389

NAVIGATION = "vi-navigation"

390

REPLACE = "vi-replace"

391

REPLACE_SINGLE = "vi-replace-single"

392

INSERT_MULTIPLE = "vi-insert-multiple"

393

```

394

395

## Usage Examples

396

397

### Basic Key Bindings

398

399

```python

400

from prompt_toolkit.application import Application

401

from prompt_toolkit.key_binding import KeyBindings

402

from prompt_toolkit.layout import Layout

403

from prompt_toolkit.layout.controls import FormattedTextControl

404

405

# Create key bindings

406

kb = KeyBindings()

407

408

@kb.add('c-c')

409

def exit_app(event):

410

"""Exit on Ctrl-C."""

411

event.app.exit()

412

413

@kb.add('c-h')

414

def show_help(event):

415

"""Show help on Ctrl-H."""

416

print("Help: Press Ctrl-C to exit")

417

418

@kb.add('c-l')

419

def clear_screen(event):

420

"""Clear screen on Ctrl-L."""

421

event.app.invalidate()

422

423

# Create application with key bindings

424

app = Application(

425

layout=Layout(FormattedTextControl('Press Ctrl-H for help, Ctrl-C to exit')),

426

key_bindings=kb,

427

full_screen=True

428

)

429

430

app.run()

431

```

432

433

### Key Sequences and Combinations

434

435

```python

436

from prompt_toolkit.key_binding import KeyBindings

437

438

kb = KeyBindings()

439

440

# Single key

441

@kb.add('q')

442

def quit_app(event):

443

event.app.exit()

444

445

# Control key combination

446

@kb.add('c-x', 'c-c')

447

def exit_emacs_style(event):

448

"""Emacs-style exit sequence."""

449

event.app.exit()

450

451

# Escape sequences

452

@kb.add('escape', 'q')

453

def escape_quit(event):

454

"""Exit with Escape+q."""

455

event.app.exit()

456

457

# Function key

458

@kb.add('f1')

459

def show_help(event):

460

"""Show help on F1."""

461

print("F1 Help")

462

463

# Multiple key sequence

464

@kb.add('c-x', 's')

465

def save_file(event):

466

"""Save file with Ctrl-X, S."""

467

print("Saving file...")

468

469

# Arrow keys

470

@kb.add('up')

471

def move_up(event):

472

"""Move cursor up."""

473

print("Moving up")

474

475

@kb.add('down')

476

def move_down(event):

477

"""Move cursor down."""

478

print("Moving down")

479

```

480

481

### Conditional Key Bindings

482

483

```python

484

from prompt_toolkit.key_binding import KeyBindings

485

from prompt_toolkit.filters import has_focus, vi_mode, emacs_mode

486

487

kb = KeyBindings()

488

489

# Only active when specific buffer has focus

490

@kb.add('c-t', filter=has_focus('main'))

491

def toggle_feature(event):

492

"""Toggle feature when main buffer focused."""

493

print("Feature toggled")

494

495

# Vi mode specific bindings

496

@kb.add('i', filter=vi_mode)

497

def vi_insert_mode(event):

498

"""Enter Vi insert mode."""

499

from prompt_toolkit.key_binding.vi_state import InputMode

500

event.app.vi_state.input_mode = InputMode.INSERT

501

502

# Emacs mode specific bindings

503

@kb.add('c-n', filter=emacs_mode)

504

def emacs_next_line(event):

505

"""Emacs next line."""

506

buffer = event.current_buffer

507

buffer.cursor_down()

508

509

# Custom filter

510

from prompt_toolkit.filters import Condition

511

512

@Condition

513

def custom_condition():

514

return some_application_state

515

516

@kb.add('c-k', filter=custom_condition)

517

def custom_action(event):

518

"""Action only when custom condition is true."""

519

print("Custom action executed")

520

```

521

522

### Buffer Key Bindings

523

524

```python

525

from prompt_toolkit.key_binding import KeyBindings

526

from prompt_toolkit.filters import has_focus

527

528

kb = KeyBindings()

529

530

@kb.add('c-a')

531

def move_to_start(event):

532

"""Move cursor to start of line."""

533

event.current_buffer.cursor_position = 0

534

535

@kb.add('c-e')

536

def move_to_end(event):

537

"""Move cursor to end of line."""

538

buffer = event.current_buffer

539

buffer.cursor_position = len(buffer.text)

540

541

@kb.add('c-k')

542

def kill_line(event):

543

"""Kill from cursor to end of line."""

544

buffer = event.current_buffer

545

buffer.delete(count=len(buffer.text) - buffer.cursor_position)

546

547

@kb.add('c-w')

548

def kill_word(event):

549

"""Kill word backwards."""

550

buffer = event.current_buffer

551

pos = buffer.document.find_start_of_word(count=-1)

552

if pos:

553

buffer.delete_before_cursor(count=-pos)

554

555

@kb.add('c-y')

556

def yank(event):

557

"""Yank (paste) text."""

558

# Implementation would restore previously killed text

559

pass

560

```

561

562

### Vi Key Bindings

563

564

```python

565

from prompt_toolkit.key_binding import KeyBindings

566

from prompt_toolkit.key_binding.bindings.vi import load_vi_bindings

567

from prompt_toolkit.filters import vi_mode, vi_navigation_mode, vi_insert_mode

568

569

# Load base Vi bindings

570

kb = load_vi_bindings()

571

572

# Add custom Vi bindings

573

@kb.add('j', 'j', filter=vi_insert_mode)

574

def jj_escape(event):

575

"""Exit insert mode with 'jj'."""

576

from prompt_toolkit.key_binding.vi_state import InputMode

577

event.app.vi_state.input_mode = InputMode.NAVIGATION

578

579

@kb.add('g', 'g', filter=vi_navigation_mode)

580

def go_to_top(event):

581

"""Go to top of buffer with 'gg'."""

582

event.current_buffer.cursor_position = 0

583

584

@kb.add('G', filter=vi_navigation_mode)

585

def go_to_bottom(event):

586

"""Go to bottom of buffer with 'G'."""

587

buffer = event.current_buffer

588

buffer.cursor_position = len(buffer.text)

589

590

# Custom Vi command

591

@kb.add('c-x', 'c-e', filter=vi_mode)

592

def edit_in_editor(event):

593

"""Edit buffer in external editor."""

594

# Implementation would open external editor

595

pass

596

```

597

598

### Dynamic Key Bindings

599

600

```python

601

from prompt_toolkit.key_binding import KeyBindings, DynamicKeyBindings

602

from prompt_toolkit.filters import Condition

603

604

# Different key binding sets

605

basic_bindings = KeyBindings()

606

advanced_bindings = KeyBindings()

607

608

@basic_bindings.add('c-c')

609

def basic_exit(event):

610

event.app.exit()

611

612

@advanced_bindings.add('c-c')

613

def advanced_exit(event):

614

print("Advanced exit")

615

event.app.exit()

616

617

@advanced_bindings.add('f1')

618

def advanced_help(event):

619

print("Advanced help")

620

621

# Dynamic selection based on application state

622

def get_current_bindings():

623

if application_in_advanced_mode:

624

return advanced_bindings

625

else:

626

return basic_bindings

627

628

# Use dynamic bindings

629

dynamic_kb = DynamicKeyBindings(get_current_bindings)

630

```

631

632

### Key Binding with Arguments

633

634

```python

635

from prompt_toolkit.key_binding import KeyBindings

636

637

kb = KeyBindings()

638

639

@kb.add('c-u')

640

def universal_argument(event):

641

"""Set universal argument."""

642

# This would set a numeric argument for next command

643

event.app.key_processor.arg = event.arg or 4

644

645

@kb.add('c-k')

646

def kill_line_with_arg(event):

647

"""Kill lines with numeric argument."""

648

buffer = event.current_buffer

649

count = event.arg or 1

650

651

for _ in range(count):

652

# Kill one line

653

buffer.delete(count=len(buffer.document.current_line_after_cursor))

654

if buffer.document.cursor_position < len(buffer.text):

655

buffer.delete(count=1) # Delete newline

656

657

@kb.add('c-d')

658

def delete_with_arg(event):

659

"""Delete characters with numeric argument."""

660

buffer = event.current_buffer

661

count = event.arg or 1

662

buffer.delete(count=count)

663

```