Metadata-Version: 2.5
Name: cellstr
Version: 0.2.1
Summary: Display-width aware len, slice, pad and truncate for terminal text: wide CJK, emoji sequences, combining marks and ANSI codes
Project-URL: Homepage, https://lianes8studio.com
Project-URL: Repository, https://github.com/JaviLianes8/cellstr
Project-URL: Issues, https://github.com/JaviLianes8/cellstr/issues
Project-URL: Changelog, https://github.com/JaviLianes8/cellstr/blob/main/CHANGELOG.md
Project-URL: LinkedIn, https://www.linkedin.com/in/jlianes/
Project-URL: Funding, https://buymeacoffee.com/jlianesglrs
Author-email: Javier Lianes <javier@lianes8studio.com>
License-Expression: MIT
License-File: LICENSE
License-File: LICENSE-UNICODE
Keywords: ansi,cli,east-asian-width,emoji,grapheme,terminal,truncate,unicode,wcwidth,width
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Terminals
Classifier: Topic :: Text Processing
Classifier: Typing :: Typed
Requires-Python: >=3.10
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: mypy; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Requires-Dist: twine; extra == 'dev'
Requires-Dist: wcwidth==0.2.13; extra == 'dev'
Description-Content-Type: text/markdown

# cellstr

Display-width aware `len`, slicing, padding and truncation for terminal text: East Asian
wide characters, emoji sequences, combining marks, zero-width characters and ANSI escape
sequences, measured the way a terminal draws them. Typed, zero dependencies, Unicode 18.0.0.

## Install

```
pip install cellstr
```

## Usage

`len`, slicing and `str.ljust` count code points, so they misalign any text that carries
colour codes, CJK characters, emoji or accents. `cellstr` counts display cells.

```python
from cellstr import cell_len, cell_slice, fit, ljust, truncate

warn = "\x1b[31mWARN\x1b[0m"  # red "WARN"
cell_len(warn)  # -> 4
cell_len("你好")  # -> 4
cell_len("👨‍👩‍👧")  # -> 2
cell_len("🇯🇵")  # -> 2
cell_len("é")  # -> 1

cell_slice(warn, 0, 2)  # -> "\x1b[31mWA\x1b[0m"
cell_slice("a你b", 0, 2)  # -> "a"
cell_slice("x👨‍👩‍👧y", 0, 2)  # -> "x"

ljust("你", 4)  # -> "你  "
truncate("hello world", 8)  # -> "hello..."
truncate("/usr/local/lib/python", 12, position="middle")  # -> "/usr/...thon"
fit("张伟张伟", 5, suffix="")  # -> "张伟 "
```

What it gets right:

- **Grapheme clusters.** A ZWJ family, a skin tone sequence, a flag, a keycap or a letter
  with accents is one unit: measured once and never split by slicing or truncation.
- **Escape sequences.** SGR colours, OSC 8 hyperlinks, cursor movement and the other
  ECMA-48 families count as nothing and are kept in place, so styled text stays styled.
- **Ambiguous width.** Greek, Cyrillic, box drawing and circled digits are one cell in
  Western terminals and two in CJK ones. Pass `ambiguous=2` to any function.
- **Tabs.** Pass `tabsize=8` to measure tab stops; slicing and padding turn tabs into the
  spaces they cover. `expand_tabs()` does it on its own.
- **Real Unicode data.** Tables are generated by a committed script from a committed
  Unicode Character Database snapshot, and the full grapheme break test suite runs.

```python
from cellstr import cell_len, iter_cells, truncate_detailed

cell_len("αβ─", ambiguous=2)  # -> 6
cell_len("a\tb", tabsize=8)  # -> 9
[c.width for c in iter_cells("a你🇯🇵")]  # -> [1, 2, 2]
truncate_detailed("hello world", 8).truncated  # -> True
```

Public API: `cell_len`, `char_width`, `grapheme_width`, `iter_graphemes`, `iter_cells`
(`Cell`), `cell_slice`, `ljust`, `rjust`, `center`, `fit`, `truncate`,
`truncate_detailed` (`Truncation`), `expand_tabs`, `strip_ansi`, `UNICODE_VERSION`, and
the `Align` and `Position` literal types. Every function is fully typed and documented.

## Command line

```
$ printf 'abc\n你好\n' | cellstr measure
3
4
$ printf '/usr/local/lib/python3/site-packages\n' | cellstr truncate 16 --position middle
/usr/lo...ckages
$ printf 'id\n12345\n' | cellstr pad 8 --align right
      id
   12345
```

Commands: `measure` (`--max`), `truncate N`, `pad N`, `fit N` and `slice START [STOP]`,
all with `--ambiguous` and `--tabsize`. `cellstr --help` lists runnable examples.

## Terminals disagree

There is no standard for how many cells a character takes, and terminals differ on
ambiguous-width characters, emoji ZWJ sequences and variation selectors. `cellstr`
follows Markus Kuhn's reference `wcwidth` for single code points and
measures emoji sequences as one glyph, as terminals with grapheme support draw them.
[GUIDE.md](GUIDE.md) has the exact rules, concrete examples of where terminals differ, a
recipe for code point by code point measuring, and every difference from the `wcwidth`
package.

## Why

`wcwidth` measures plain-text width well, but it does not give you a typed, cohesive set
of string operations: slicing by column that keeps escapes and graphemes intact, padding,
fitting a table cell and width-aware truncation with a suffix at either end or in the
middle. `strip-ansi` style tools remove escapes but know nothing about width. `cellstr`
does all of it in one small, fully typed, zero-dependency package.

## License

MIT. See [LICENSE](LICENSE). The property tables in `src/cellstr/_ucd_*.py` are generated
from the Unicode Character Database and are covered by the Unicode License v3, see [LICENSE-UNICODE](LICENSE-UNICODE).

## Links

- Website: https://lianes8studio.com
- GitHub: https://github.com/JaviLianes8
- LinkedIn: https://www.linkedin.com/in/jlianes/
