-
Notifications
You must be signed in to change notification settings - Fork 317
Interactive readline
PsySH's interactive readline is a pure-PHP readline replacement that gives PsySH full control over input, editing, completion, and rendering. Instead of delegating to ext-readline or ext-libedit, it handles everything directly... which means better completions, multi-line editing, and a bunch of features that weren't possible before.
This is opt-in and experimental. Default behavior is completely unchanged.
// In your PsySH config file
'useExperimentalReadline' => true,
// 'useSuggestions' => true,
// 'useBracketedPaste' => true,
// 'useSyntaxHighlighting' => false,# Or from the command line
psysh --experimental-readlineRequires an interactive terminal (TTY). Falls back to the standard readline stack automatically in non-interactive contexts.
The old tab completion system matches strings against lists of known symbol names. The new completion engine is fundamentally different:
-
Syntax-aware: Parses your input with php-parser to understand context. It knows
$foo->needs instance methods,Foo::needs static members, and a barearraneeds global functions or classes. -
Type-aware: Resolves types through assignments, return types, and doc annotations.
$repo->find(1)->completes with methods on the return type. -
Runtime-value-aware: Inspects actual live objects in scope. If
$useris aUserinstance,$user->showsUser's real methods and properties, not every symbol name PHP knows about.
Type asum to find array_sum, or stl to find strtolower. No need to remember exact prefixes.
Completions display in a multi-column menu. Use arrow keys to browse, keep typing to filter. Enter to accept, Escape to dismiss.
Existing custom tab completion matchers (via the matchers config option) are automatically bridged into the new completion system via MatcherAdapterSource.
Interactive readline highlights PHP syntax and PsySH commands as you type.
'useSyntaxHighlighting' => true,This is enabled by default when interactive readline is active. If you run into terminal rendering issues, disable it with 'useSyntaxHighlighting' => false or config set useSyntaxHighlighting off.
Press Enter with unclosed brackets or an incomplete statement and the input continues on the next line, with proper indentation. Closing brackets auto-dedent. Shift+Enter always inserts a newline.
No more fighting the shell to write a multi-line closure or a long match expression.
Start a reverse history search with Ctrl+R. An overlay shows matching results with highlighting, smart-case filtering, and deduplication. Navigate with Up/Down or Ctrl+R/Ctrl+S, press Enter or Right to accept.
Type the beginning of a previous command, then press Up/Down to cycle through matching history entries. This works like Fish and Zsh: the text you've already typed acts as a filter.
Fish-style ghost text suggestions from your command history, shown as you type. Press Right or Ctrl+F to accept the full suggestion, Alt+Right to accept word-by-word.
Note
Autosuggestions are behind a separate feature flag because they're still a bit rough around the edges. Enable them with 'useSuggestions' => true in your config.
Opening brackets and quotes auto-insert their closing counterpart:
- Type
(→ inserts()with cursor between them - Type
)when cursor is before)→ skips over it instead of doubling - Backspace between
()→ deletes both
Works for (), [], {}, "", and ''.
Enable bracketed paste with 'useBracketedPaste' => true to have multi-line code inserted verbatim as a single block, without line-by-line execution or auto-indentation interference.
| Key | Action |
|---|---|
| Ctrl+A / Home | Move to start of line (toggles between first non-whitespace and column 0) |
| Ctrl+E / End | Move to end of line |
| Ctrl+B / Left | Move cursor left |
| Ctrl+F / Right | Move cursor right (or accept suggestion) |
| Alt+B / Alt+Left | Move word backward |
| Alt+F / Alt+Right | Move word forward (or accept suggestion word) |
| Up / Down | Move within multiline input, then navigate history |
| Key | Action |
|---|---|
| Ctrl+W | Delete word backward |
| Ctrl+K | Delete to end of line |
| Ctrl+U | Delete to start of line |
| Delete | Delete character forward |
| Ctrl+D | Delete forward (or exit on empty input) |
| Key | Action |
|---|---|
| Tab | Complete / indent |
| Ctrl+R | Reverse history search |
| Up / Down (with text) | Filtered history navigation |
| Key | Action |
|---|---|
| Enter | Submit (or continue on next line if incomplete) |
| Shift+Enter | Always insert newline |
| Ctrl+C | Clear current input |
| Ctrl+L | Clear screen |
| Ctrl+D (empty) | Exit |
- Interactive terminal (TTY)
- Symfony Console 5.1+ (for
Cursorsupport) -
ext-readlineandext-libeditare not required
This is experimental and we want your feedback! If you run into issues, rough edges, or have ideas for improvements, please open an issue and let us know. The goal is to make this the default readline implementation.