<p align="center">
<img alt="trimmargin Logo" src="https://raw.githubusercontent.com/sashsinha/trimmargin/refs/heads/main/trimmargin_logo.png" width="250">
</p>
<h1 align="center">trimmargin ✂️📏</h1>
<h3 align="center">Kotlin-style <code>trimMargin</code> and friends for Python — with a tiny CLI</h3>
<p align="center">
<a href="https://pypi.org/project/trimmargin/"><img alt="PyPI" src="https://img.shields.io/pypi/v/trimmargin"></a>
<a href="https://pypi.org/project/trimmargin/"><img alt="Python Versions" src="https://img.shields.io/pypi/pyversions/trimmargin.svg"></a>
<a href="https://pypi.org/project/trimmargin/"><img alt="PyPI Status" src="https://img.shields.io/pypi/status/trimmargin"></a>
<a href="https://raw.githubusercontent.com/sashsinha/trimmargin/main/LICENSE"><img alt="License: MIT" src="https://raw.githubusercontent.com/sashsinha/simple-file-checksum/main/license.svg"></a>
<a href="https://pepy.tech/project/trimmargin"><img alt="Downloads" src="https://pepy.tech/badge/trimmargin"></a>
</p>
---
## Features
* 🔄 Normalizes line endings to `\n`
* 🧼 Drops first/last **blank** lines (edges only, not interior)
* 🧲 `trim_margin()` strips leading whitespace + a margin prefix (default `|`)
* 🔧 `replace_indent_by_margin()` like Kotlin’s, with a custom replacement indent
* 🪄 `trim_indent()` / `replace_indent()` built on `textwrap.dedent/indent`
* ➕ `prepend_indent()` behaves like Kotlin (blank-line quirk covered)
---
## Installation
```bash
# uv (recommended)
uv pip install trimmargin
# or pip
pip install trimmargin
```
---
## Quick Start
```python
from trimmargin import trim_margin, replace_indent_by_margin
text = """
|hello
|world
"""
print(trim_margin(text))
# hello
# world
print(replace_indent_by_margin(text, new_indent=">>> "))
# >>> hello
# >>> world
```
---
## Behavior Notes
* If a line’s first non-whitespace char starts the margin prefix, both the leading whitespace **and** the prefix are removed; other lines are unchanged.
* Only the first and last **blank** lines are dropped.
* Input may use `\r\n`, `\n`, or `\r`; output uses `\n`.
---
## CLI Usage 🚀
The `trimmargin` command reads a file or stdin and writes to stdout.
```bash
# default: trim-margin on stdin using prefix "|"
trimmargin < input.txt
# file input
trimmargin input.txt
# modes
trimmargin --mode replace-by-margin --new-indent ">>> " input.txt
trimmargin --mode trim-indent < input.txt
trimmargin --mode replace-indent --new-indent " " < input.txt
trimmargin --mode prepend --indent ">> "
# change margin prefix
trimmargin --prefix "§" < input.txt
# version
trimmargin --version
```
**Modes**
* `trim-margin` *(default)*: remove leading whitespace + `prefix`
* `replace-by-margin`: same detection as above, then add `new-indent`
* `trim-indent`: remove common indent via `textwrap.dedent`
* `replace-indent`: `dedent` then `indent` non-blank lines with `new-indent`
* `prepend`: prepend `indent` to non-blank lines; blank-line quirk matches Kotlin:
* if blank and `len(line) < len(indent)`: line becomes exactly `indent`
* else: leave blank line unchanged
---
## Development (uv) 🧑💻
```bash
# setup
uv sync --extra dev
# tests
uv run pytest
# lint & types
uv run ruff check .
uv run mypy src
# format
uv run ruff format .
# build & publish
uv build
uv publish
```
---
## Why This Exists ❓
`textwrap.dedent/indent` are great, but Kotlin’s margin-aware trimming is handy for multiline literals embedded in code and docs.
Raw data
{
"_id": null,
"home_page": null,
"name": "trimmargin",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "text, strings, indentation, kotlin, trimMargin, cli",
"author": "Sash Sinha",
"author_email": "Sash Sinha <sashsinha1@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/69/4d/263ed38995448d89f3a7d655fa032216b043229c8c313afef132df0bf61d/trimmargin-1.0.0.tar.gz",
"platform": null,
"description": "<p align=\"center\">\n <img alt=\"trimmargin Logo\" src=\"https://raw.githubusercontent.com/sashsinha/trimmargin/refs/heads/main/trimmargin_logo.png\" width=\"250\">\n</p>\n\n<h1 align=\"center\">trimmargin \u2702\ufe0f\ud83d\udccf</h1>\n<h3 align=\"center\">Kotlin-style <code>trimMargin</code> and friends for Python \u2014 with a tiny CLI</h3>\n\n<p align=\"center\">\n<a href=\"https://pypi.org/project/trimmargin/\"><img alt=\"PyPI\" src=\"https://img.shields.io/pypi/v/trimmargin\"></a>\n<a href=\"https://pypi.org/project/trimmargin/\"><img alt=\"Python Versions\" src=\"https://img.shields.io/pypi/pyversions/trimmargin.svg\"></a>\n<a href=\"https://pypi.org/project/trimmargin/\"><img alt=\"PyPI Status\" src=\"https://img.shields.io/pypi/status/trimmargin\"></a>\n<a href=\"https://raw.githubusercontent.com/sashsinha/trimmargin/main/LICENSE\"><img alt=\"License: MIT\" src=\"https://raw.githubusercontent.com/sashsinha/simple-file-checksum/main/license.svg\"></a>\n<a href=\"https://pepy.tech/project/trimmargin\"><img alt=\"Downloads\" src=\"https://pepy.tech/badge/trimmargin\"></a>\n</p>\n\n---\n\n## Features\n\n* \ud83d\udd04 Normalizes line endings to `\\n`\n* \ud83e\uddfc Drops first/last **blank** lines (edges only, not interior)\n* \ud83e\uddf2 `trim_margin()` strips leading whitespace + a margin prefix (default `|`)\n* \ud83d\udd27 `replace_indent_by_margin()` like Kotlin\u2019s, with a custom replacement indent\n* \ud83e\ude84 `trim_indent()` / `replace_indent()` built on `textwrap.dedent/indent`\n* \u2795 `prepend_indent()` behaves like Kotlin (blank-line quirk covered)\n\n---\n\n## Installation\n\n```bash\n# uv (recommended)\nuv pip install trimmargin\n\n# or pip\npip install trimmargin\n```\n\n---\n\n## Quick Start\n\n```python\nfrom trimmargin import trim_margin, replace_indent_by_margin\n\ntext = \"\"\"\n |hello\n |world\n\"\"\"\n\nprint(trim_margin(text))\n# hello\n# world\n\nprint(replace_indent_by_margin(text, new_indent=\">>> \"))\n# >>> hello\n# >>> world\n```\n\n---\n\n## Behavior Notes\n\n* If a line\u2019s first non-whitespace char starts the margin prefix, both the leading whitespace **and** the prefix are removed; other lines are unchanged.\n* Only the first and last **blank** lines are dropped.\n* Input may use `\\r\\n`, `\\n`, or `\\r`; output uses `\\n`.\n\n---\n\n## CLI Usage \ud83d\ude80\n\nThe `trimmargin` command reads a file or stdin and writes to stdout.\n\n```bash\n# default: trim-margin on stdin using prefix \"|\"\ntrimmargin < input.txt\n\n# file input\ntrimmargin input.txt\n\n# modes\ntrimmargin --mode replace-by-margin --new-indent \">>> \" input.txt\ntrimmargin --mode trim-indent < input.txt\ntrimmargin --mode replace-indent --new-indent \" \" < input.txt\ntrimmargin --mode prepend --indent \">> \"\n\n# change margin prefix\ntrimmargin --prefix \"\u00a7\" < input.txt\n\n# version\ntrimmargin --version\n```\n\n**Modes**\n\n* `trim-margin` *(default)*: remove leading whitespace + `prefix`\n* `replace-by-margin`: same detection as above, then add `new-indent`\n* `trim-indent`: remove common indent via `textwrap.dedent`\n* `replace-indent`: `dedent` then `indent` non-blank lines with `new-indent`\n* `prepend`: prepend `indent` to non-blank lines; blank-line quirk matches Kotlin:\n\n * if blank and `len(line) < len(indent)`: line becomes exactly `indent`\n * else: leave blank line unchanged\n\n---\n\n## Development (uv) \ud83e\uddd1\u200d\ud83d\udcbb\n\n```bash\n# setup\nuv sync --extra dev\n\n# tests\nuv run pytest\n\n# lint & types\nuv run ruff check .\nuv run mypy src\n\n# format\nuv run ruff format .\n\n# build & publish\nuv build\nuv publish\n```\n\n---\n\n## Why This Exists \u2753\n\n`textwrap.dedent/indent` are great, but Kotlin\u2019s margin-aware trimming is handy for multiline literals embedded in code and docs.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Kotlin-style trimMargin utilities for Python, with a tiny CLI.",
"version": "1.0.0",
"project_urls": {
"Homepage": "https://github.com/sashsinha/trimmargin",
"Issues": "https://github.com/sashsinha/trimmargin/issues"
},
"split_keywords": [
"text",
" strings",
" indentation",
" kotlin",
" trimmargin",
" cli"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "26caeff650b3b19b3bb595e277717d0043bd89a85205d6362d87a9bc22d6deac",
"md5": "0e776e225daa7f410cc05a3379ae7c55",
"sha256": "1b16c9243e015b0347edddd2b414fd02ba28cf7c07341923bd2035708e64f28f"
},
"downloads": -1,
"filename": "trimmargin-1.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "0e776e225daa7f410cc05a3379ae7c55",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 6761,
"upload_time": "2025-08-12T12:20:42",
"upload_time_iso_8601": "2025-08-12T12:20:42.187546Z",
"url": "https://files.pythonhosted.org/packages/26/ca/eff650b3b19b3bb595e277717d0043bd89a85205d6362d87a9bc22d6deac/trimmargin-1.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "694d263ed38995448d89f3a7d655fa032216b043229c8c313afef132df0bf61d",
"md5": "5e019649c39ca37df6aa72fe8f99310b",
"sha256": "55c6cf2a6de1f4bfc347a0d1ed781cdea805fca6a8d7c14e1c4699876a68d2b3"
},
"downloads": -1,
"filename": "trimmargin-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "5e019649c39ca37df6aa72fe8f99310b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 5136,
"upload_time": "2025-08-12T12:20:43",
"upload_time_iso_8601": "2025-08-12T12:20:43.507819Z",
"url": "https://files.pythonhosted.org/packages/69/4d/263ed38995448d89f3a7d655fa032216b043229c8c313afef132df0bf61d/trimmargin-1.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-12 12:20:43",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "sashsinha",
"github_project": "trimmargin",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "trimmargin"
}