flowmark 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flowmark-0.2.0/PKG-INFO +124 -0
- flowmark-0.2.0/README.md +104 -0
- {flowmark-0.1.0 → flowmark-0.2.0}/pyproject.toml +27 -15
- flowmark-0.2.0/src/flowmark/__init__.py +14 -0
- flowmark-0.2.0/src/flowmark/cli.py +176 -0
- flowmark-0.2.0/src/flowmark/line_wrappers.py +105 -0
- {flowmark-0.1.0 → flowmark-0.2.0}/src/flowmark/markdown_filling.py +30 -143
- {flowmark-0.1.0 → flowmark-0.2.0}/src/flowmark/text_filling.py +8 -6
- flowmark-0.1.0/PKG-INFO +0 -53
- flowmark-0.1.0/README.md +0 -33
- flowmark-0.1.0/src/flowmark/__init__.py +0 -19
- {flowmark-0.1.0 → flowmark-0.2.0}/LICENSE +0 -0
- {flowmark-0.1.0 → flowmark-0.2.0}/src/flowmark/sentence_split_regex.py +0 -0
- {flowmark-0.1.0 → flowmark-0.2.0}/src/flowmark/text_wrapping.py +0 -0
flowmark-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: flowmark
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Better line wrapping and formatting for plaintext and Markdown
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: Joshua Levy
|
|
7
|
+
Author-email: joshua@cal.berkeley.edu
|
|
8
|
+
Requires-Python: >=3.10,<4.0
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Requires-Dist: marko (>=2.1.2,<3.0.0)
|
|
16
|
+
Requires-Dist: regex (>=2024.11.6,<2025.0.0)
|
|
17
|
+
Requires-Dist: strif (>=2.0.0,<3.0.0)
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# flowmark
|
|
21
|
+
|
|
22
|
+
Flowmark is a new Python implementation of text line wrapping and filling.
|
|
23
|
+
|
|
24
|
+
It simplifies and generalizes Python's
|
|
25
|
+
[`textwrap`](https://docs.python.org/3/library/textwrap.html) with a few more
|
|
26
|
+
capabilities:
|
|
27
|
+
|
|
28
|
+
- Full customizability of initial and subsequent indentation strings
|
|
29
|
+
|
|
30
|
+
- Control over when to split words, by default using a word splitter that won't break
|
|
31
|
+
lines within HTML tags
|
|
32
|
+
|
|
33
|
+
In addition, it adds optional support for Markdown and offers Markdown auto-formatting,
|
|
34
|
+
like [markdownfmt](https://github.com/shurcooL/markdownfmt), also with controllable line
|
|
35
|
+
wrapping options.
|
|
36
|
+
|
|
37
|
+
One key use case is to normalize Markdown in a standard, readable way that makes diffs
|
|
38
|
+
easy to read and use on GitHub.
|
|
39
|
+
This can be useful for documentation workflows and also to compare LLM outputs that are
|
|
40
|
+
Markdown.
|
|
41
|
+
|
|
42
|
+
Finally, it has options to use heuristics to split on sentences, which can make diffs
|
|
43
|
+
much more readable. (For an example of this, look at the
|
|
44
|
+
[Markdown source](https://github.com/jlevy/flowmark/blob/main/README.md?plain=1) of this
|
|
45
|
+
readme file.)
|
|
46
|
+
|
|
47
|
+
It aims to be small and simple and have only a few dependencies, currently only
|
|
48
|
+
[`marko`](https://github.com/frostming/marko) and
|
|
49
|
+
[`regex`](https://pypi.org/project/regex/).
|
|
50
|
+
|
|
51
|
+
This is a new and simple package (previously I'd implemented something like this
|
|
52
|
+
[for Atom](https://github.com/jlevy/atom-flowmark)) but I plan to add more support for
|
|
53
|
+
command line usage and VSCode/Cursor auto-formatting in the future.
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
The simplest way to use the tool is to use pipx:
|
|
58
|
+
|
|
59
|
+
```shell
|
|
60
|
+
pipx install flowmark
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
To use as a library, use pip or poetry to install `flowmark`.
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
|
|
67
|
+
Flowmark can be used as a library or as a CLI.
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
$ flowmark --help
|
|
71
|
+
usage: flowmark [-h] [-o OUTPUT] [-w WIDTH] [-p] [-s] [-i] [--nobackup] [file]
|
|
72
|
+
|
|
73
|
+
Flowmark: Better line wrapping and formatting for plaintext and Markdown
|
|
74
|
+
|
|
75
|
+
positional arguments:
|
|
76
|
+
file Input file (use '-' for stdin)
|
|
77
|
+
|
|
78
|
+
options:
|
|
79
|
+
-h, --help show this help message and exit
|
|
80
|
+
-o, --output OUTPUT Output file (use '-' for stdout)
|
|
81
|
+
-w, --width WIDTH Line width to wrap to
|
|
82
|
+
-p, --plaintext Process as plaintext (no Markdown parsing)
|
|
83
|
+
-s, --sentences Enable sentence-based line breaks (only applies to Markdown mode)
|
|
84
|
+
-i, --inplace Edit the file in place (ignores --output)
|
|
85
|
+
--nobackup Do not make a backup of the original file when using --inplace
|
|
86
|
+
|
|
87
|
+
Flowmark provides enhanced text wrapping capabilities with special handling for
|
|
88
|
+
Markdown content. It can:
|
|
89
|
+
|
|
90
|
+
- Format Markdown with proper line wrapping while preserving structure
|
|
91
|
+
and normalizing Markdown formatting
|
|
92
|
+
|
|
93
|
+
- Optionally break lines at sentence boundaries for better diff readability
|
|
94
|
+
|
|
95
|
+
- Process plaintext with HTML-aware word splitting
|
|
96
|
+
|
|
97
|
+
It is both a library and a command-line tool.
|
|
98
|
+
|
|
99
|
+
Command-line usage examples:
|
|
100
|
+
|
|
101
|
+
# Format a Markdown file to stdout
|
|
102
|
+
flowmark README.md
|
|
103
|
+
|
|
104
|
+
# Format a Markdown file and save to a new file
|
|
105
|
+
flowmark README.md -o README_formatted.md
|
|
106
|
+
|
|
107
|
+
# Edit a file in-place (with or without making a backup)
|
|
108
|
+
flowmark --inplace README.md
|
|
109
|
+
flowmark --inplace --nobackup README.md
|
|
110
|
+
|
|
111
|
+
# Process plaintext instead of Markdown
|
|
112
|
+
flowmark --plaintext text.txt
|
|
113
|
+
|
|
114
|
+
# Use sentences to guide line breaks (good for many purposes git history and diffs)
|
|
115
|
+
flowmark --sentences README.md
|
|
116
|
+
|
|
117
|
+
For more details, see: https://github.com/jlevy/flowmark
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
* * *
|
|
121
|
+
|
|
122
|
+
*This project was built from
|
|
123
|
+
[simple-modern-poetry](https://github.com/jlevy/simple-modern-poetry).*
|
|
124
|
+
|
flowmark-0.2.0/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# flowmark
|
|
2
|
+
|
|
3
|
+
Flowmark is a new Python implementation of text line wrapping and filling.
|
|
4
|
+
|
|
5
|
+
It simplifies and generalizes Python's
|
|
6
|
+
[`textwrap`](https://docs.python.org/3/library/textwrap.html) with a few more
|
|
7
|
+
capabilities:
|
|
8
|
+
|
|
9
|
+
- Full customizability of initial and subsequent indentation strings
|
|
10
|
+
|
|
11
|
+
- Control over when to split words, by default using a word splitter that won't break
|
|
12
|
+
lines within HTML tags
|
|
13
|
+
|
|
14
|
+
In addition, it adds optional support for Markdown and offers Markdown auto-formatting,
|
|
15
|
+
like [markdownfmt](https://github.com/shurcooL/markdownfmt), also with controllable line
|
|
16
|
+
wrapping options.
|
|
17
|
+
|
|
18
|
+
One key use case is to normalize Markdown in a standard, readable way that makes diffs
|
|
19
|
+
easy to read and use on GitHub.
|
|
20
|
+
This can be useful for documentation workflows and also to compare LLM outputs that are
|
|
21
|
+
Markdown.
|
|
22
|
+
|
|
23
|
+
Finally, it has options to use heuristics to split on sentences, which can make diffs
|
|
24
|
+
much more readable. (For an example of this, look at the
|
|
25
|
+
[Markdown source](https://github.com/jlevy/flowmark/blob/main/README.md?plain=1) of this
|
|
26
|
+
readme file.)
|
|
27
|
+
|
|
28
|
+
It aims to be small and simple and have only a few dependencies, currently only
|
|
29
|
+
[`marko`](https://github.com/frostming/marko) and
|
|
30
|
+
[`regex`](https://pypi.org/project/regex/).
|
|
31
|
+
|
|
32
|
+
This is a new and simple package (previously I'd implemented something like this
|
|
33
|
+
[for Atom](https://github.com/jlevy/atom-flowmark)) but I plan to add more support for
|
|
34
|
+
command line usage and VSCode/Cursor auto-formatting in the future.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
The simplest way to use the tool is to use pipx:
|
|
39
|
+
|
|
40
|
+
```shell
|
|
41
|
+
pipx install flowmark
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
To use as a library, use pip or poetry to install `flowmark`.
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
Flowmark can be used as a library or as a CLI.
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
$ flowmark --help
|
|
52
|
+
usage: flowmark [-h] [-o OUTPUT] [-w WIDTH] [-p] [-s] [-i] [--nobackup] [file]
|
|
53
|
+
|
|
54
|
+
Flowmark: Better line wrapping and formatting for plaintext and Markdown
|
|
55
|
+
|
|
56
|
+
positional arguments:
|
|
57
|
+
file Input file (use '-' for stdin)
|
|
58
|
+
|
|
59
|
+
options:
|
|
60
|
+
-h, --help show this help message and exit
|
|
61
|
+
-o, --output OUTPUT Output file (use '-' for stdout)
|
|
62
|
+
-w, --width WIDTH Line width to wrap to
|
|
63
|
+
-p, --plaintext Process as plaintext (no Markdown parsing)
|
|
64
|
+
-s, --sentences Enable sentence-based line breaks (only applies to Markdown mode)
|
|
65
|
+
-i, --inplace Edit the file in place (ignores --output)
|
|
66
|
+
--nobackup Do not make a backup of the original file when using --inplace
|
|
67
|
+
|
|
68
|
+
Flowmark provides enhanced text wrapping capabilities with special handling for
|
|
69
|
+
Markdown content. It can:
|
|
70
|
+
|
|
71
|
+
- Format Markdown with proper line wrapping while preserving structure
|
|
72
|
+
and normalizing Markdown formatting
|
|
73
|
+
|
|
74
|
+
- Optionally break lines at sentence boundaries for better diff readability
|
|
75
|
+
|
|
76
|
+
- Process plaintext with HTML-aware word splitting
|
|
77
|
+
|
|
78
|
+
It is both a library and a command-line tool.
|
|
79
|
+
|
|
80
|
+
Command-line usage examples:
|
|
81
|
+
|
|
82
|
+
# Format a Markdown file to stdout
|
|
83
|
+
flowmark README.md
|
|
84
|
+
|
|
85
|
+
# Format a Markdown file and save to a new file
|
|
86
|
+
flowmark README.md -o README_formatted.md
|
|
87
|
+
|
|
88
|
+
# Edit a file in-place (with or without making a backup)
|
|
89
|
+
flowmark --inplace README.md
|
|
90
|
+
flowmark --inplace --nobackup README.md
|
|
91
|
+
|
|
92
|
+
# Process plaintext instead of Markdown
|
|
93
|
+
flowmark --plaintext text.txt
|
|
94
|
+
|
|
95
|
+
# Use sentences to guide line breaks (good for many purposes git history and diffs)
|
|
96
|
+
flowmark --sentences README.md
|
|
97
|
+
|
|
98
|
+
For more details, see: https://github.com/jlevy/flowmark
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
* * *
|
|
102
|
+
|
|
103
|
+
*This project was built from
|
|
104
|
+
[simple-modern-poetry](https://github.com/jlevy/simple-modern-poetry).*
|
|
@@ -1,27 +1,43 @@
|
|
|
1
|
-
[
|
|
1
|
+
[project]
|
|
2
2
|
name = "flowmark"
|
|
3
|
-
# Keep this a dev version, as the dynamic versioning plugin is used for actual release versions:
|
|
4
|
-
version = "0.1.0"
|
|
5
3
|
description = "Better line wrapping and formatting for plaintext and Markdown"
|
|
6
|
-
authors = [
|
|
4
|
+
authors = [
|
|
5
|
+
{ name="Joshua Levy", email="joshua@cal.berkeley.edu" },
|
|
6
|
+
]
|
|
7
7
|
readme = "README.md"
|
|
8
8
|
license = "MIT"
|
|
9
|
-
|
|
9
|
+
requires-python = ">=3.10,<4.0"
|
|
10
|
+
dynamic = [ "dependencies", ]
|
|
11
|
+
version = "0.2.0"
|
|
12
|
+
|
|
13
|
+
[tool.poetry]
|
|
10
14
|
requires-poetry = ">=2.0"
|
|
15
|
+
packages = [
|
|
16
|
+
{ include = "flowmark", from = "src" },
|
|
17
|
+
]
|
|
18
|
+
# Keep this a dev version, as the dynamic versioning plugin is used for actual release versions:
|
|
19
|
+
|
|
20
|
+
# See: https://github.com/mtkennerly/poetry-dynamic-versioning
|
|
21
|
+
[build-system]
|
|
22
|
+
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning>=1.7.0,<2.0.0"]
|
|
23
|
+
build-backend = "poetry_dynamic_versioning.backend"
|
|
11
24
|
|
|
12
|
-
|
|
25
|
+
[project.scripts]
|
|
26
|
+
# Add script entry points here.
|
|
27
|
+
flowmark = "flowmark.cli:main"
|
|
28
|
+
|
|
29
|
+
# See:
|
|
13
30
|
# https://github.com/python-poetry/poetry-plugin-shell
|
|
14
|
-
# https://github.com/mtkennerly/poetry-dynamic-versioning
|
|
15
31
|
# https://github.com/MousaZeidBaker/poetry-plugin-up
|
|
16
32
|
[tool.poetry.requires-plugins]
|
|
17
33
|
poetry-plugin-shell = "^1.0.1"
|
|
18
|
-
poetry-dynamic-versioning = "^1.7.0"
|
|
19
34
|
poetry-plugin-up = "^0.9.0"
|
|
20
35
|
|
|
21
36
|
[tool.poetry.dependencies]
|
|
22
|
-
python = "^3.10"
|
|
37
|
+
python = "^3.10,<4.0"
|
|
23
38
|
marko = "^2.1.2"
|
|
24
39
|
regex = "^2024.11.6"
|
|
40
|
+
strif = "^2.0.0"
|
|
25
41
|
|
|
26
42
|
[tool.poetry.group.dev.dependencies]
|
|
27
43
|
black = "^24.10.0"
|
|
@@ -32,10 +48,6 @@ mypy = "^1.15.0"
|
|
|
32
48
|
codespell = "^2.4.1"
|
|
33
49
|
rich = "^13.9.4"
|
|
34
50
|
|
|
35
|
-
[build-system]
|
|
36
|
-
requires = ["poetry-core"]
|
|
37
|
-
build-backend = "poetry.core.masonry.api"
|
|
38
|
-
|
|
39
51
|
# Auto-generated version for builds based on git tag or commit.
|
|
40
52
|
#
|
|
41
53
|
# Preferred styles of versioning (PEP 440 compliant):
|
|
@@ -61,10 +73,10 @@ format-jinja = """{%- if distance == 0 -%}
|
|
|
61
73
|
{%- endif -%}"""
|
|
62
74
|
|
|
63
75
|
[tool.black]
|
|
64
|
-
line-length =
|
|
76
|
+
line-length = 100
|
|
65
77
|
|
|
66
78
|
[tool.ruff]
|
|
67
|
-
line-length =
|
|
79
|
+
line-length = 100
|
|
68
80
|
|
|
69
81
|
[tool.ruff.lint]
|
|
70
82
|
ignore = ["E402", "E731", "E712"]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
__all__ = (
|
|
2
|
+
"fill_text",
|
|
3
|
+
"fill_markdown",
|
|
4
|
+
"html_md_word_splitter",
|
|
5
|
+
"line_wrap_by_sentence",
|
|
6
|
+
"line_wrap_to_width",
|
|
7
|
+
"wrap_paragraph",
|
|
8
|
+
"wrap_paragraph_lines",
|
|
9
|
+
"Wrap",
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from .markdown_filling import fill_markdown, line_wrap_by_sentence, line_wrap_to_width
|
|
13
|
+
from .text_filling import fill_text, Wrap
|
|
14
|
+
from .text_wrapping import html_md_word_splitter, wrap_paragraph, wrap_paragraph_lines
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Flowmark: Better line wrapping and formatting for plaintext and Markdown
|
|
4
|
+
|
|
5
|
+
Flowmark provides enhanced text wrapping capabilities with special handling for
|
|
6
|
+
Markdown content. It can:
|
|
7
|
+
|
|
8
|
+
- Format Markdown with proper line wrapping while preserving structure
|
|
9
|
+
and normalizing Markdown formatting
|
|
10
|
+
|
|
11
|
+
- Optionally break lines at sentence boundaries for better diff readability
|
|
12
|
+
|
|
13
|
+
- Process plaintext with HTML-aware word splitting
|
|
14
|
+
|
|
15
|
+
It is both a library and a command-line tool.
|
|
16
|
+
|
|
17
|
+
Command-line usage examples:
|
|
18
|
+
|
|
19
|
+
# Format a Markdown file to stdout
|
|
20
|
+
flowmark README.md
|
|
21
|
+
|
|
22
|
+
# Format a Markdown file and save to a new file
|
|
23
|
+
flowmark README.md -o README_formatted.md
|
|
24
|
+
|
|
25
|
+
# Edit a file in-place (with or without making a backup)
|
|
26
|
+
flowmark --inplace README.md
|
|
27
|
+
flowmark --inplace --nobackup README.md
|
|
28
|
+
|
|
29
|
+
# Process plaintext instead of Markdown
|
|
30
|
+
flowmark --plaintext text.txt
|
|
31
|
+
|
|
32
|
+
# Use sentences to guide line breaks (good for many purposes git history and diffs)
|
|
33
|
+
flowmark --sentences README.md
|
|
34
|
+
|
|
35
|
+
For more details, see: https://github.com/jlevy/flowmark
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
import argparse
|
|
39
|
+
import sys
|
|
40
|
+
from dataclasses import dataclass
|
|
41
|
+
from typing import List, Optional
|
|
42
|
+
|
|
43
|
+
from strif import atomic_output_file
|
|
44
|
+
|
|
45
|
+
from flowmark import fill_markdown, fill_text, html_md_word_splitter, Wrap
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class Options:
|
|
50
|
+
"""Command-line options for the flowmark tool."""
|
|
51
|
+
|
|
52
|
+
file: str
|
|
53
|
+
output: str
|
|
54
|
+
width: int
|
|
55
|
+
plaintext: bool
|
|
56
|
+
sentences: bool
|
|
57
|
+
inplace: bool
|
|
58
|
+
nobackup: bool
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _parse_args(args: Optional[List[str]] = None) -> Options:
|
|
62
|
+
"""Parse command-line arguments for the flowmark tool."""
|
|
63
|
+
# Use the module's docstring as the description
|
|
64
|
+
module_doc = __doc__ or ""
|
|
65
|
+
doc_parts = module_doc.split("\n\n")
|
|
66
|
+
description = doc_parts[0]
|
|
67
|
+
epilog = "\n\n".join(doc_parts[1:])
|
|
68
|
+
|
|
69
|
+
parser = argparse.ArgumentParser(
|
|
70
|
+
description=description,
|
|
71
|
+
epilog=epilog,
|
|
72
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
73
|
+
)
|
|
74
|
+
parser.add_argument(
|
|
75
|
+
"file",
|
|
76
|
+
nargs="?",
|
|
77
|
+
type=str,
|
|
78
|
+
default="-",
|
|
79
|
+
help="Input file (use '-' for stdin)",
|
|
80
|
+
)
|
|
81
|
+
parser.add_argument(
|
|
82
|
+
"-o",
|
|
83
|
+
"--output",
|
|
84
|
+
type=str,
|
|
85
|
+
default="-",
|
|
86
|
+
help="Output file (use '-' for stdout)",
|
|
87
|
+
)
|
|
88
|
+
parser.add_argument("-w", "--width", type=int, default=88, help="Line width to wrap to")
|
|
89
|
+
parser.add_argument(
|
|
90
|
+
"-p", "--plaintext", action="store_true", help="Process as plaintext (no Markdown parsing)"
|
|
91
|
+
)
|
|
92
|
+
parser.add_argument(
|
|
93
|
+
"-s",
|
|
94
|
+
"--sentences",
|
|
95
|
+
action="store_true",
|
|
96
|
+
default=True,
|
|
97
|
+
help="Enable sentence-based line breaks (only applies to Markdown mode)",
|
|
98
|
+
)
|
|
99
|
+
parser.add_argument(
|
|
100
|
+
"-i", "--inplace", action="store_true", help="Edit the file in place (ignores --output)"
|
|
101
|
+
)
|
|
102
|
+
parser.add_argument(
|
|
103
|
+
"--nobackup",
|
|
104
|
+
action="store_true",
|
|
105
|
+
help="Do not make a backup of the original file when using --inplace",
|
|
106
|
+
)
|
|
107
|
+
parsed_args = parser.parse_args(args)
|
|
108
|
+
|
|
109
|
+
return Options(
|
|
110
|
+
file=parsed_args.file,
|
|
111
|
+
output=parsed_args.output,
|
|
112
|
+
width=parsed_args.width,
|
|
113
|
+
plaintext=parsed_args.plaintext,
|
|
114
|
+
sentences=parsed_args.sentences,
|
|
115
|
+
inplace=parsed_args.inplace,
|
|
116
|
+
nobackup=parsed_args.nobackup,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def main(args: Optional[List[str]] = None) -> int:
|
|
121
|
+
"""
|
|
122
|
+
Main entry point for the flowmark CLI.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
args: Command-line arguments (uses sys.argv if None)
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Exit code (0 for success, non-zero for errors)
|
|
129
|
+
"""
|
|
130
|
+
options = _parse_args(args)
|
|
131
|
+
|
|
132
|
+
# Handle input.
|
|
133
|
+
if options.file == "-":
|
|
134
|
+
text = sys.stdin.read()
|
|
135
|
+
else:
|
|
136
|
+
with open(options.file, "r") as f:
|
|
137
|
+
text = f.read()
|
|
138
|
+
|
|
139
|
+
if options.plaintext:
|
|
140
|
+
# Plaintext mode
|
|
141
|
+
result = fill_text(
|
|
142
|
+
text,
|
|
143
|
+
text_wrap=Wrap.WRAP,
|
|
144
|
+
width=options.width,
|
|
145
|
+
word_splitter=html_md_word_splitter, # Still use HTML/MD aware splitter by default
|
|
146
|
+
)
|
|
147
|
+
else:
|
|
148
|
+
# Markdown mode
|
|
149
|
+
result = fill_markdown(
|
|
150
|
+
text,
|
|
151
|
+
width=options.width,
|
|
152
|
+
by_sentence=options.sentences,
|
|
153
|
+
dedent_input=True,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Handle output
|
|
157
|
+
if options.inplace:
|
|
158
|
+
if options.file == "-":
|
|
159
|
+
print("Error: Cannot use --inplace with stdin", file=sys.stderr)
|
|
160
|
+
return 1
|
|
161
|
+
backup_suffix = ".orig" if not options.nobackup else ""
|
|
162
|
+
with atomic_output_file(options.file, backup_suffix=backup_suffix) as tmp_path:
|
|
163
|
+
with open(tmp_path, "w") as f:
|
|
164
|
+
f.write(result)
|
|
165
|
+
else:
|
|
166
|
+
if options.output == "-":
|
|
167
|
+
sys.stdout.write(result)
|
|
168
|
+
else:
|
|
169
|
+
with open(options.output, "w") as f:
|
|
170
|
+
f.write(result)
|
|
171
|
+
|
|
172
|
+
return 0
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
if __name__ == "__main__":
|
|
176
|
+
sys.exit(main())
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
from typing import Callable, List, Protocol
|
|
2
|
+
|
|
3
|
+
from flowmark.sentence_split_regex import split_sentences_regex
|
|
4
|
+
from flowmark.text_filling import DEFAULT_WRAP_WIDTH
|
|
5
|
+
from flowmark.text_wrapping import DEFAULT_LEN_FUNCTION, wrap_paragraph, wrap_paragraph_lines
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
DEFAULT_MIN_LINE_LEN = 20
|
|
9
|
+
"""Default minimum line length for sentence breaking."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class LineWrapper(Protocol):
|
|
13
|
+
"""
|
|
14
|
+
Takes a text string and any indents to use, and returns the wrapped text.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __call__(self, text: str, initial_indent: str, subsequent_indent: str) -> str: ...
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SentenceSplitter(Protocol):
|
|
21
|
+
"""Takes a text string and returns a list of sentences."""
|
|
22
|
+
|
|
23
|
+
def __call__(self, text: str) -> List[str]: ...
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def split_sentences_no_min_length(text: str) -> List[str]:
|
|
27
|
+
return split_sentences_regex(text, min_length=0)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def line_wrap_to_width(
|
|
31
|
+
width: int = DEFAULT_WRAP_WIDTH,
|
|
32
|
+
len_fn: Callable[[str], int] = DEFAULT_LEN_FUNCTION,
|
|
33
|
+
) -> LineWrapper:
|
|
34
|
+
"""
|
|
35
|
+
Wrap lines of text to a given width.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def line_wrapper(text: str, initial_indent: str, subsequent_indent: str) -> str:
|
|
39
|
+
return wrap_paragraph(
|
|
40
|
+
text,
|
|
41
|
+
width=width,
|
|
42
|
+
initial_indent=initial_indent,
|
|
43
|
+
subsequent_indent=subsequent_indent,
|
|
44
|
+
len_fn=len_fn,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
return line_wrapper
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def line_wrap_by_sentence(
|
|
51
|
+
split_sentences: SentenceSplitter = split_sentences_no_min_length,
|
|
52
|
+
width: int = DEFAULT_WRAP_WIDTH,
|
|
53
|
+
min_line_len: int = DEFAULT_MIN_LINE_LEN,
|
|
54
|
+
len_fn: Callable[[str], int] = DEFAULT_LEN_FUNCTION,
|
|
55
|
+
) -> LineWrapper:
|
|
56
|
+
"""
|
|
57
|
+
Wrap lines of text to a given width but also keep sentences on their own lines.
|
|
58
|
+
If the last line ends up shorter than `min_line_len`, it's combined with the
|
|
59
|
+
next sentence.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def line_wrapper(text: str, initial_indent: str, subsequent_indent: str) -> str:
|
|
63
|
+
text = text.replace("\n", " ")
|
|
64
|
+
lines: List[str] = []
|
|
65
|
+
first_line = True
|
|
66
|
+
length = len_fn
|
|
67
|
+
initial_indent_len = len_fn(initial_indent)
|
|
68
|
+
subsequent_indent_len = len_fn(subsequent_indent)
|
|
69
|
+
|
|
70
|
+
sentences = split_sentences(text)
|
|
71
|
+
|
|
72
|
+
for i, sentence in enumerate(sentences):
|
|
73
|
+
current_column = initial_indent_len if first_line else subsequent_indent_len
|
|
74
|
+
if len(lines) > 0 and length(lines[-1]) < min_line_len:
|
|
75
|
+
current_column += length(lines[-1])
|
|
76
|
+
|
|
77
|
+
wrapped = wrap_paragraph_lines(
|
|
78
|
+
sentence,
|
|
79
|
+
width=width,
|
|
80
|
+
initial_column=current_column,
|
|
81
|
+
subsequent_offset=subsequent_indent_len,
|
|
82
|
+
)
|
|
83
|
+
# If last line is shorter than min_line_len, combine with next line.
|
|
84
|
+
# Also handles if the first word doesn't fit.
|
|
85
|
+
if (
|
|
86
|
+
len(lines) > 0
|
|
87
|
+
and length(lines[-1]) < min_line_len
|
|
88
|
+
and length(lines[-1]) + 1 + length(wrapped[0]) <= width
|
|
89
|
+
):
|
|
90
|
+
lines[-1] += " " + wrapped[0]
|
|
91
|
+
wrapped.pop(0)
|
|
92
|
+
|
|
93
|
+
lines.extend(wrapped)
|
|
94
|
+
|
|
95
|
+
first_line = False
|
|
96
|
+
|
|
97
|
+
# Now insert the indents and assemble the paragraph.
|
|
98
|
+
if initial_indent and len(lines) > 0:
|
|
99
|
+
lines[0] = initial_indent + lines[0]
|
|
100
|
+
if subsequent_indent and len(lines) > 1:
|
|
101
|
+
lines[1:] = [subsequent_indent + line for line in lines[1:]]
|
|
102
|
+
|
|
103
|
+
return "\n".join(lines)
|
|
104
|
+
|
|
105
|
+
return line_wrapper
|
|
@@ -12,7 +12,7 @@ discussion on why line wrapping this way is convenient.)
|
|
|
12
12
|
import re
|
|
13
13
|
from contextlib import contextmanager
|
|
14
14
|
from textwrap import dedent
|
|
15
|
-
from typing import Callable, cast, Generator, List,
|
|
15
|
+
from typing import Callable, cast, Generator, List, Optional, Tuple
|
|
16
16
|
|
|
17
17
|
from marko import block, inline
|
|
18
18
|
from marko.block import HTMLBlock
|
|
@@ -20,34 +20,9 @@ from marko.parser import Parser
|
|
|
20
20
|
from marko.renderer import Renderer
|
|
21
21
|
from marko.source import Source
|
|
22
22
|
|
|
23
|
-
from .
|
|
24
|
-
from .
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
DEFAULT_WRAP_WIDTH = 88
|
|
28
|
-
"""
|
|
29
|
-
Default wrap width for Markdown content. This is a compromise between traditional
|
|
30
|
-
but sometimes impractically narrow 80-char console width and being too wide to
|
|
31
|
-
read comfortably for text, markup, and code. 88 is the same as Black.
|
|
32
|
-
"""
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
DEFAULT_MIN_LINE_LEN = 20
|
|
36
|
-
"""Default minimum line length for sentence breaking."""
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class LineWrapper(Protocol):
|
|
40
|
-
"""Takes a text string and any indents to use, and returns the wrapped text."""
|
|
41
|
-
|
|
42
|
-
def __call__(
|
|
43
|
-
self, text: str, initial_indent: str, subsequent_indent: str
|
|
44
|
-
) -> str: ...
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class SentenceSplitter(Protocol):
|
|
48
|
-
"""Takes a text string and returns a list of sentences."""
|
|
49
|
-
|
|
50
|
-
def __call__(self, text: str) -> List[str]: ...
|
|
23
|
+
from flowmark.line_wrappers import line_wrap_by_sentence, line_wrap_to_width, LineWrapper
|
|
24
|
+
from flowmark.sentence_split_regex import split_sentences_regex
|
|
25
|
+
from flowmark.text_filling import DEFAULT_WRAP_WIDTH
|
|
51
26
|
|
|
52
27
|
|
|
53
28
|
def _normalize_html_comments(text: str, break_str: str = "\n\n") -> str:
|
|
@@ -73,9 +48,7 @@ def _ensure_surrounding_breaks(
|
|
|
73
48
|
break_str: str = "\n\n",
|
|
74
49
|
) -> str:
|
|
75
50
|
for start_tag, end_tag in tag_pairs:
|
|
76
|
-
pattern = re.compile(
|
|
77
|
-
rf"(\s*{re.escape(start_tag)}.*?{re.escape(end_tag)}\s*)", re.DOTALL
|
|
78
|
-
)
|
|
51
|
+
pattern = re.compile(rf"(\s*{re.escape(start_tag)}.*?{re.escape(end_tag)}\s*)", re.DOTALL)
|
|
79
52
|
|
|
80
53
|
def replacer(match: re.Match[str]) -> str:
|
|
81
54
|
if not filter(match.group(0)):
|
|
@@ -123,9 +96,7 @@ class _MarkdownNormalizer(Renderer):
|
|
|
123
96
|
|
|
124
97
|
def __init__(self, line_wrapper: LineWrapper) -> None:
|
|
125
98
|
super().__init__()
|
|
126
|
-
self._prefix: str =
|
|
127
|
-
"" # The prefix on the first line, with a bullet, such as ` - `.
|
|
128
|
-
)
|
|
99
|
+
self._prefix: str = "" # The prefix on the first line, with a bullet, such as ` - `.
|
|
129
100
|
self._second_prefix: str = "" # The prefix on subsequent lines, such as ` `.
|
|
130
101
|
self._suppress_item_break: bool = True
|
|
131
102
|
self._line_wrapper = line_wrapper
|
|
@@ -136,9 +107,7 @@ class _MarkdownNormalizer(Renderer):
|
|
|
136
107
|
return super().__enter__()
|
|
137
108
|
|
|
138
109
|
@contextmanager
|
|
139
|
-
def container(
|
|
140
|
-
self, prefix: str, second_prefix: str = ""
|
|
141
|
-
) -> Generator[None, None, None]:
|
|
110
|
+
def container(self, prefix: str, second_prefix: str = "") -> Generator[None, None, None]:
|
|
142
111
|
old_prefix, old_second_prefix = self._prefix, self._second_prefix
|
|
143
112
|
self._prefix += prefix
|
|
144
113
|
self._second_prefix += second_prefix
|
|
@@ -199,9 +168,7 @@ class _MarkdownNormalizer(Renderer):
|
|
|
199
168
|
extra_text = f" {extra}" if extra else ""
|
|
200
169
|
lang_text = f"{lang}{extra_text}" if lang else ""
|
|
201
170
|
lines = [f"{self._prefix}```{lang_text}"]
|
|
202
|
-
lines.extend(
|
|
203
|
-
f"{self._second_prefix}{line}" for line in code_content.splitlines()
|
|
204
|
-
)
|
|
171
|
+
lines.extend(f"{self._second_prefix}{line}" for line in code_content.splitlines())
|
|
205
172
|
lines.append(f"{self._second_prefix}```")
|
|
206
173
|
self._prefix = self._second_prefix
|
|
207
174
|
return "\n".join(lines) + "\n"
|
|
@@ -224,9 +191,7 @@ class _MarkdownNormalizer(Renderer):
|
|
|
224
191
|
return result
|
|
225
192
|
|
|
226
193
|
def render_heading(self, element: block.Heading) -> str:
|
|
227
|
-
result = (
|
|
228
|
-
f"{self._prefix}{'#' * element.level} {self.render_children(element)}\n"
|
|
229
|
-
)
|
|
194
|
+
result = f"{self._prefix}{'#' * element.level} {self.render_children(element)}\n"
|
|
230
195
|
self._prefix = self._second_prefix
|
|
231
196
|
return result
|
|
232
197
|
|
|
@@ -259,16 +224,10 @@ class _MarkdownNormalizer(Renderer):
|
|
|
259
224
|
|
|
260
225
|
def render_link(self, element: inline.Link) -> str:
|
|
261
226
|
link_text = self.render_children(element)
|
|
262
|
-
link_title = (
|
|
263
|
-
'"{}"'.format(element.title.replace('"', '\\"')) if element.title else None
|
|
264
|
-
)
|
|
227
|
+
link_title = '"{}"'.format(element.title.replace('"', '\\"')) if element.title else None
|
|
265
228
|
assert self.root_node
|
|
266
229
|
label = next(
|
|
267
|
-
(
|
|
268
|
-
k
|
|
269
|
-
for k, v in self.root_node.link_ref_defs.items()
|
|
270
|
-
if v == (element.dest, link_title)
|
|
271
|
-
),
|
|
230
|
+
(k for k, v in self.root_node.link_ref_defs.items() if v == (element.dest, link_title)),
|
|
272
231
|
None,
|
|
273
232
|
)
|
|
274
233
|
if label is not None:
|
|
@@ -283,9 +242,7 @@ class _MarkdownNormalizer(Renderer):
|
|
|
283
242
|
|
|
284
243
|
def render_image(self, element: inline.Image) -> str:
|
|
285
244
|
template = ""
|
|
286
|
-
title = (
|
|
287
|
-
' "{}"'.format(element.title.replace('"', '\\"')) if element.title else ""
|
|
288
|
-
)
|
|
245
|
+
title = ' "{}"'.format(element.title.replace('"', '\\"')) if element.title else ""
|
|
289
246
|
return template.format(self.render_children(element), element.dest, title)
|
|
290
247
|
|
|
291
248
|
def render_literal(self, element: inline.Literal) -> str:
|
|
@@ -310,89 +267,33 @@ def split_sentences_no_min_length(text: str) -> List[str]:
|
|
|
310
267
|
return split_sentences_regex(text, min_length=0)
|
|
311
268
|
|
|
312
269
|
|
|
313
|
-
def
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
subsequent_indent: str,
|
|
270
|
+
def fill_markdown(
|
|
271
|
+
markdown_text: str,
|
|
272
|
+
dedent_input: bool = True,
|
|
317
273
|
width: int = DEFAULT_WRAP_WIDTH,
|
|
274
|
+
by_sentence: bool = False,
|
|
275
|
+
line_wrapper: Optional[LineWrapper] = None,
|
|
318
276
|
) -> str:
|
|
319
277
|
"""
|
|
320
|
-
|
|
321
|
-
"""
|
|
322
|
-
return wrap_paragraph(
|
|
323
|
-
text,
|
|
324
|
-
width=width,
|
|
325
|
-
initial_indent=initial_indent,
|
|
326
|
-
subsequent_indent=subsequent_indent,
|
|
327
|
-
)
|
|
278
|
+
Normalize and wrap Markdown text filling paragraphs to the full width.
|
|
328
279
|
|
|
280
|
+
Wraps lines and adds line breaks within paragraphs and on
|
|
281
|
+
best-guess estimations of sentences, to make diffs more readable.
|
|
329
282
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
width: int = DEFAULT_WRAP_WIDTH,
|
|
336
|
-
min_line_len: int = DEFAULT_MIN_LINE_LEN,
|
|
337
|
-
len_fn: Callable[[str], int] = DEFAULT_LEN_FUNCTION,
|
|
338
|
-
) -> str:
|
|
339
|
-
"""
|
|
340
|
-
Wrap lines of text to a given width but also keep sentences on their own lines.
|
|
341
|
-
If the last line ends up shorter than min_line_len, it's combined with the next sentence.
|
|
283
|
+
Also enforces that all list items have two newlines between them, so
|
|
284
|
+
that items are separate paragraphs when viewed as plaintext.
|
|
285
|
+
|
|
286
|
+
Optionally also dedents and strips the input, so it can be used
|
|
287
|
+
on docstrings.
|
|
342
288
|
"""
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
length = len_fn
|
|
347
|
-
initial_indent_len = len_fn(initial_indent)
|
|
348
|
-
subsequent_indent_len = len_fn(subsequent_indent)
|
|
349
|
-
|
|
350
|
-
sentences = split_sentences(text)
|
|
351
|
-
|
|
352
|
-
for i, sentence in enumerate(sentences):
|
|
353
|
-
current_column = initial_indent_len if first_line else subsequent_indent_len
|
|
354
|
-
if len(lines) > 0 and length(lines[-1]) < min_line_len:
|
|
355
|
-
current_column += length(lines[-1])
|
|
356
|
-
|
|
357
|
-
wrapped = wrap_paragraph_lines(
|
|
358
|
-
sentence,
|
|
359
|
-
width=width,
|
|
360
|
-
initial_column=current_column,
|
|
361
|
-
subsequent_offset=subsequent_indent_len,
|
|
289
|
+
if line_wrapper is None:
|
|
290
|
+
line_wrapper = (
|
|
291
|
+
line_wrap_by_sentence(width=width) if by_sentence else line_wrap_to_width(width=width)
|
|
362
292
|
)
|
|
363
|
-
# If last line is shorter than min_line_len, combine with next line.
|
|
364
|
-
# Also handles if the first word doesn't fit.
|
|
365
|
-
if (
|
|
366
|
-
len(lines) > 0
|
|
367
|
-
and length(lines[-1]) < min_line_len
|
|
368
|
-
and length(lines[-1]) + 1 + length(wrapped[0]) <= width
|
|
369
|
-
):
|
|
370
|
-
lines[-1] += " " + wrapped[0]
|
|
371
|
-
wrapped.pop(0)
|
|
372
|
-
|
|
373
|
-
lines.extend(wrapped)
|
|
374
|
-
|
|
375
|
-
first_line = False
|
|
376
|
-
|
|
377
|
-
# Now insert the indents and assemble the paragraph.
|
|
378
|
-
if initial_indent and len(lines) > 0:
|
|
379
|
-
lines[0] = initial_indent + lines[0]
|
|
380
|
-
if subsequent_indent and len(lines) > 1:
|
|
381
|
-
lines[1:] = [subsequent_indent + line for line in lines[1:]]
|
|
382
|
-
|
|
383
|
-
return "\n".join(lines)
|
|
384
293
|
|
|
294
|
+
if dedent_input:
|
|
295
|
+
markdown_text = dedent(markdown_text).strip()
|
|
385
296
|
|
|
386
|
-
def normalize_markdown(
|
|
387
|
-
markdown_text: str, line_wrapper: LineWrapper = wrap_lines_using_sentences
|
|
388
|
-
) -> str:
|
|
389
|
-
"""
|
|
390
|
-
Normalize Markdown text. Wraps lines and adds line breaks within paragraphs and on
|
|
391
|
-
best-guess estimations of sentences, to make diffs more readable.
|
|
392
|
-
|
|
393
|
-
Also enforces that all list items have two newlines between them, so that items
|
|
394
|
-
are separate paragraphs when viewed as plaintext.
|
|
395
|
-
"""
|
|
396
297
|
markdown_text = markdown_text.strip() + "\n"
|
|
397
298
|
|
|
398
299
|
# If we want to normalize HTML blocks or comments.
|
|
@@ -403,17 +304,3 @@ def normalize_markdown(
|
|
|
403
304
|
parsed = parser.parse(markdown_text)
|
|
404
305
|
result = _MarkdownNormalizer(line_wrapper).render(parsed)
|
|
405
306
|
return result
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
def fill_markdown(
|
|
409
|
-
markdown_text: str,
|
|
410
|
-
dedent_input: bool = True,
|
|
411
|
-
line_wrapper: LineWrapper = wrap_lines_to_width,
|
|
412
|
-
) -> str:
|
|
413
|
-
"""
|
|
414
|
-
Normalize and wrap Markdown text filling paragraphs to the full width.
|
|
415
|
-
Also dedents and strips the input, so it can be used on docstrings.
|
|
416
|
-
"""
|
|
417
|
-
if dedent_input:
|
|
418
|
-
markdown_text = dedent(markdown_text).strip()
|
|
419
|
-
return normalize_markdown(markdown_text, line_wrapper=line_wrapper)
|
|
@@ -2,7 +2,7 @@ import re
|
|
|
2
2
|
from enum import Enum
|
|
3
3
|
from typing import Callable, List
|
|
4
4
|
|
|
5
|
-
from .text_wrapping import (
|
|
5
|
+
from flowmark.text_wrapping import (
|
|
6
6
|
DEFAULT_LEN_FUNCTION,
|
|
7
7
|
html_md_word_splitter,
|
|
8
8
|
WordSplitter,
|
|
@@ -10,6 +10,12 @@ from .text_wrapping import (
|
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
DEFAULT_WRAP_WIDTH = 88
|
|
13
|
+
"""
|
|
14
|
+
Default wrap width. This is a compromise between traditional but sometimes
|
|
15
|
+
impractically narrow 80-char console width and being too wide to read comfortably
|
|
16
|
+
for text, markup, and code. 88 is the same as Black.
|
|
17
|
+
"""
|
|
18
|
+
|
|
13
19
|
|
|
14
20
|
DEFAULT_INDENT = " "
|
|
15
21
|
|
|
@@ -98,11 +104,7 @@ def fill_text(
|
|
|
98
104
|
"""
|
|
99
105
|
|
|
100
106
|
if not text_wrap.should_wrap:
|
|
101
|
-
indent =
|
|
102
|
-
extra_indent + DEFAULT_INDENT
|
|
103
|
-
if text_wrap == Wrap.INDENT_ONLY
|
|
104
|
-
else extra_indent
|
|
105
|
-
)
|
|
107
|
+
indent = extra_indent + DEFAULT_INDENT if text_wrap == Wrap.INDENT_ONLY else extra_indent
|
|
106
108
|
lines = text.splitlines()
|
|
107
109
|
if lines:
|
|
108
110
|
return "\n".join(indent + line for line in lines)
|
flowmark-0.1.0/PKG-INFO
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: flowmark
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: Better line wrapping and formatting for plaintext and Markdown
|
|
5
|
-
License: MIT
|
|
6
|
-
Author: Joshua Levy
|
|
7
|
-
Author-email: joshua@cal.berkeley.edu
|
|
8
|
-
Requires-Python: >=3.10,<4.0
|
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
-
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
-
Requires-Dist: marko (>=2.1.2,<3.0.0)
|
|
16
|
-
Requires-Dist: regex (>=2024.11.6,<2025.0.0)
|
|
17
|
-
Project-URL: Repository, https://github.com/jlevy/flowmark
|
|
18
|
-
Description-Content-Type: text/markdown
|
|
19
|
-
|
|
20
|
-
# flowmark
|
|
21
|
-
|
|
22
|
-
Flowmark is a new Python implementation of text line wrapping and filling.
|
|
23
|
-
|
|
24
|
-
It can be used as a more flexible alternative to Python's
|
|
25
|
-
[`textwrap`](https://docs.python.org/3/library/textwrap.html) with a few more features,
|
|
26
|
-
such as full customizability of initial and subsequent indentation strings, and more
|
|
27
|
-
control over when to split words, so it won't break lines within HTML tags.
|
|
28
|
-
|
|
29
|
-
It also combines line wrapping with support for Markdown and offers Markdown
|
|
30
|
-
auto-formatting, like [markdownfmt](https://github.com/shurcooL/markdownfmt), also with
|
|
31
|
-
controllable line wrapping options.
|
|
32
|
-
|
|
33
|
-
One key use case is to normalize Markdown in a standard, readable way that makes diffs
|
|
34
|
-
easy to read and use on GitHub.
|
|
35
|
-
This can be useful for documentation workflows and also to compare LLM outputs that are
|
|
36
|
-
Markdown.
|
|
37
|
-
|
|
38
|
-
Finally, it has options to use heuristics to split on sentences, which can make diffs
|
|
39
|
-
much more readable. (For an example of this, look at the source to this readme file.)
|
|
40
|
-
|
|
41
|
-
It aims to be small and simple and have only a few dependencies, currently only
|
|
42
|
-
[`marko`](https://github.com/frostming/marko) and
|
|
43
|
-
[`regex`](https://pypi.org/project/regex/).
|
|
44
|
-
|
|
45
|
-
This is a new and simple package (previously I'd implemented something like this
|
|
46
|
-
[for Atom](https://github.com/jlevy/atom-flowmark)) but I plan to add more support for
|
|
47
|
-
command line usage and VSCode/Cursor auto-formatting in the future.
|
|
48
|
-
|
|
49
|
-
* * *
|
|
50
|
-
|
|
51
|
-
*This project was built from
|
|
52
|
-
[simple-modern-poetry](https://github.com/jlevy/simple-modern-poetry).*
|
|
53
|
-
|
flowmark-0.1.0/README.md
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# flowmark
|
|
2
|
-
|
|
3
|
-
Flowmark is a new Python implementation of text line wrapping and filling.
|
|
4
|
-
|
|
5
|
-
It can be used as a more flexible alternative to Python's
|
|
6
|
-
[`textwrap`](https://docs.python.org/3/library/textwrap.html) with a few more features,
|
|
7
|
-
such as full customizability of initial and subsequent indentation strings, and more
|
|
8
|
-
control over when to split words, so it won't break lines within HTML tags.
|
|
9
|
-
|
|
10
|
-
It also combines line wrapping with support for Markdown and offers Markdown
|
|
11
|
-
auto-formatting, like [markdownfmt](https://github.com/shurcooL/markdownfmt), also with
|
|
12
|
-
controllable line wrapping options.
|
|
13
|
-
|
|
14
|
-
One key use case is to normalize Markdown in a standard, readable way that makes diffs
|
|
15
|
-
easy to read and use on GitHub.
|
|
16
|
-
This can be useful for documentation workflows and also to compare LLM outputs that are
|
|
17
|
-
Markdown.
|
|
18
|
-
|
|
19
|
-
Finally, it has options to use heuristics to split on sentences, which can make diffs
|
|
20
|
-
much more readable. (For an example of this, look at the source to this readme file.)
|
|
21
|
-
|
|
22
|
-
It aims to be small and simple and have only a few dependencies, currently only
|
|
23
|
-
[`marko`](https://github.com/frostming/marko) and
|
|
24
|
-
[`regex`](https://pypi.org/project/regex/).
|
|
25
|
-
|
|
26
|
-
This is a new and simple package (previously I'd implemented something like this
|
|
27
|
-
[for Atom](https://github.com/jlevy/atom-flowmark)) but I plan to add more support for
|
|
28
|
-
command line usage and VSCode/Cursor auto-formatting in the future.
|
|
29
|
-
|
|
30
|
-
* * *
|
|
31
|
-
|
|
32
|
-
*This project was built from
|
|
33
|
-
[simple-modern-poetry](https://github.com/jlevy/simple-modern-poetry).*
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
__all__ = (
|
|
2
|
-
"fill_text",
|
|
3
|
-
"fill_markdown",
|
|
4
|
-
"normalize_markdown",
|
|
5
|
-
"wrap_paragraph",
|
|
6
|
-
"wrap_paragraph_lines",
|
|
7
|
-
"wrap_lines_to_width",
|
|
8
|
-
"wrap_lines_using_sentences",
|
|
9
|
-
"Wrap",
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
from .markdown_filling import (
|
|
13
|
-
fill_markdown,
|
|
14
|
-
normalize_markdown,
|
|
15
|
-
wrap_lines_to_width,
|
|
16
|
-
wrap_lines_using_sentences,
|
|
17
|
-
)
|
|
18
|
-
from .text_filling import fill_text, Wrap
|
|
19
|
-
from .text_wrapping import wrap_paragraph, wrap_paragraph_lines
|
|
File without changes
|
|
File without changes
|
|
File without changes
|