Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,41 @@ $ uvx --from 'libtmux' --prerelease allow python

_Upcoming changes will be written here._

#### Pane.capture_pane() enhanced (#614)

The {meth}`~libtmux.pane.Pane.capture_pane` method now supports 5 new parameters
that expose additional tmux `capture-pane` flags:

| Parameter | tmux Flag | Description |
|-----------|-----------|-------------|
| `escape_sequences` | `-e` | Include ANSI escape sequences (colors, attributes) |
| `escape_non_printable` | `-C` | Escape non-printable chars as octal `\xxx` |
| `join_wrapped` | `-J` | Join wrapped lines back together |
| `preserve_trailing` | `-N` | Preserve trailing spaces at line ends |
| `trim_trailing` | `-T` | Trim trailing empty positions (tmux 3.4+) |

**Capturing colored output:**

```python
# Capture with ANSI escape sequences preserved
pane.send_keys('printf "\\033[31mRED\\033[0m"', enter=True)
output = pane.capture_pane(escape_sequences=True)
# Output contains: '\x1b[31mRED\x1b[0m'
```

**Joining wrapped lines:**

```python
# Long lines that wrap are joined back together
output = pane.capture_pane(join_wrapped=True)
```

**Version compatibility:**

The `trim_trailing` parameter requires tmux 3.4+. If used with an older version,
a warning is issued and the flag is ignored. All other parameters work with
libtmux's minimum supported version (tmux 3.2a).

## libtmux 0.51.0 (2025-12-06)

### Breaking changes
Expand Down
69 changes: 69 additions & 0 deletions docs/topics/pane_interaction.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,75 @@ True
True
```

### Capture with ANSI escape sequences

Capture colored output with escape sequences preserved using `escape_sequences=True`:

```python
>>> import time

>>> pane.send_keys('printf "\\033[31mRED\\033[0m \\033[32mGREEN\\033[0m"')
>>> time.sleep(0.1)

>>> # Capture with ANSI codes stripped (default)
>>> output = pane.capture_pane()
>>> 'RED' in '\\n'.join(output)
True

>>> # Capture with ANSI escape sequences preserved
>>> colored_output = pane.capture_pane(escape_sequences=True)
>>> isinstance(colored_output, list)
True
```

### Join wrapped lines

Long lines that wrap in the terminal can be joined back together:

```python
>>> import time

>>> # Send a very long line that will wrap
>>> pane.send_keys('echo "' + 'x' * 200 + '"')
>>> time.sleep(0.1)

>>> # Capture with wrapped lines joined
>>> output = pane.capture_pane(join_wrapped=True)
>>> isinstance(output, list)
True
```

### Preserve trailing spaces

By default, trailing spaces are trimmed. Use `preserve_trailing=True` to keep them:

```python
>>> import time

>>> pane.send_keys('printf "text \\n"') # 3 trailing spaces
>>> time.sleep(0.1)

>>> # Capture with trailing spaces preserved
>>> output = pane.capture_pane(preserve_trailing=True)
>>> isinstance(output, list)
True
```

### Capture flags summary

| Parameter | tmux Flag | Description |
|-----------|-----------|-------------|
| `escape_sequences` | `-e` | Include ANSI escape sequences (colors, attributes) |
| `escape_non_printable` | `-C` | Escape non-printable chars as octal `\xxx` |
| `join_wrapped` | `-J` | Join wrapped lines back together |
| `preserve_trailing` | `-N` | Preserve trailing spaces at line ends |
| `trim_trailing` | `-T` | Trim trailing empty positions (tmux 3.4+) |

:::{note}
The `trim_trailing` parameter requires tmux 3.4+. If used with an older version,
a warning is issued and the flag is ignored.
:::

## Waiting for Output

A common pattern in automation is waiting for a command to complete.
Expand Down
65 changes: 64 additions & 1 deletion src/libtmux/pane.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,14 @@ def capture_pane(
self,
start: t.Literal["-"] | int | None = None,
end: t.Literal["-"] | int | None = None,
*,
escape_sequences: bool = False,
escape_non_printable: bool = False,
join_wrapped: bool = False,
preserve_trailing: bool = False,
trim_trailing: bool = False,
) -> list[str]:
"""Capture text from pane.
r"""Capture text from pane.

``$ tmux capture-pane`` to pane.
``$ tmux capture-pane -S -10`` to pane.
Expand All @@ -344,17 +350,74 @@ def capture_pane(
Negative numbers are lines in the history.
``-`` is the end of the visible pane.
Default: None
escape_sequences : bool, optional
Include ANSI escape sequences for text and background attributes
(``-e`` flag). Useful for capturing colored output.
Default: False
escape_non_printable : bool, optional
Escape non-printable characters as octal ``\\xxx`` format
(``-C`` flag). Useful for binary-safe capture.
Default: False
join_wrapped : bool, optional
Join wrapped lines and preserve trailing spaces (``-J`` flag).
Lines that were wrapped by tmux will be joined back together.
Default: False
preserve_trailing : bool, optional
Preserve trailing spaces at each line's end (``-N`` flag).
Default: False
trim_trailing : bool, optional
Trim trailing positions with no characters (``-T`` flag).
Only includes characters up to the last used cell.
Requires tmux 3.4+. If used with tmux < 3.4, a warning
is issued and the flag is ignored.
Default: False

Returns
-------
list[str]
Captured pane content.

Examples
--------
>>> pane = window.split(shell='sh')
>>> pane.capture_pane()
['$']

>>> pane.send_keys('echo "Hello world"', enter=True)

>>> pane.capture_pane()
['$ echo "Hello world"', 'Hello world', '$']

>>> print(chr(10).join(pane.capture_pane()))
$ echo "Hello world"
Hello world
$
"""
import warnings

from libtmux.common import has_gte_version

cmd = ["capture-pane", "-p"]
if start is not None:
cmd.extend(["-S", str(start)])
if end is not None:
cmd.extend(["-E", str(end)])
if escape_sequences:
cmd.append("-e")
if escape_non_printable:
cmd.append("-C")
if join_wrapped:
cmd.append("-J")
if preserve_trailing:
cmd.append("-N")
if trim_trailing:
if has_gte_version("3.4"):
cmd.append("-T")
else:
warnings.warn(
"trim_trailing requires tmux 3.4+, ignoring",
stacklevel=2,
)
return self.cmd(*cmd).stdout

def send_keys(
Expand Down
Loading