flowmark 0.3.2__tar.gz → 0.3.3__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.3.2 → flowmark-0.3.3}/PKG-INFO +14 -8
- {flowmark-0.3.2 → flowmark-0.3.3}/README.md +13 -7
- {flowmark-0.3.2 → flowmark-0.3.3}/src/flowmark/__init__.py +4 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/src/flowmark/sentence_split_regex.py +43 -18
- flowmark-0.3.3/tests/test_sentences.py +30 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/.copier-answers.yml +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/.github/workflows/ci.yml +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/.github/workflows/publish.yml +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/.gitignore +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/LICENSE +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/Makefile +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/development.md +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/devtools/lint.py +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/poetry.lock +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/publishing.md +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/pyproject.toml +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/src/flowmark/cli.py +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/src/flowmark/frontmatter.py +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/src/flowmark/line_wrappers.py +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/src/flowmark/markdown_filling.py +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/src/flowmark/text_filling.py +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/src/flowmark/text_wrapping.py +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/tests/test_filling.py +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/tests/test_frontmatter.py +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/tests/test_ref_docs.py +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/tests/test_wrapping.py +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/tests/testdocs/testdoc.orig.md +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/tests/testdocs/testdoc.out.plain.md +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/tests/testdocs/testdoc.out.semantic.md +0 -0
- {flowmark-0.3.2 → flowmark-0.3.3}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flowmark
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Better line wrapping and formatting for plaintext and Markdown
|
|
5
5
|
Project-URL: Repository, https://github.com/jlevy/flowmark
|
|
6
6
|
Author-email: Joshua Levy <joshua@cal.berkeley.edu>
|
|
@@ -18,10 +18,9 @@ Description-Content-Type: text/markdown
|
|
|
18
18
|
Flowmark is a new Python implementation of **text and Markdown line wrapping and
|
|
19
19
|
filling**, with an emphasis on making **git diffs** and **LLM edits** to text documents
|
|
20
20
|
easier to diff and review.
|
|
21
|
-
after updating
|
|
22
21
|
|
|
23
|
-
In addition, it
|
|
24
|
-
|
|
22
|
+
In addition, it offers **Markdown auto-formatting and normalization** as a library or
|
|
23
|
+
from the command line.
|
|
25
24
|
This is much like [markdownfmt](https://github.com/shurcooL/markdownfmt) or
|
|
26
25
|
[prettier's Markdown support](https://prettier.io/blog/2017/11/07/1.8.0) but is pure
|
|
27
26
|
Python and has (in my humble opinion) better options and defaults.
|
|
@@ -73,8 +72,7 @@ The `--auto` option is just the same as `--inplace --nobackup --semantic`.
|
|
|
73
72
|
|
|
74
73
|
## Use Cases
|
|
75
74
|
|
|
76
|
-
|
|
77
|
-
command.
|
|
75
|
+
The main ways to use Flowmark are:
|
|
78
76
|
|
|
79
77
|
- To **autoformat Markdown on save in VSCode/Cursor** or any other editor that supports
|
|
80
78
|
running a command on save.
|
|
@@ -83,6 +81,9 @@ The `--auto` option is just the same as `--inplace --nobackup --semantic`.
|
|
|
83
81
|
formatting styles). This can be especially useful for documentation and editing
|
|
84
82
|
workflows where clean diffs and minimal merge conflicts on GitHub are important.
|
|
85
83
|
|
|
84
|
+
- As a **command line formatter** to format text or Markdown files using the `flowmark`
|
|
85
|
+
command.
|
|
86
|
+
|
|
86
87
|
- As a **library to autoformat Markdown**. For example, it is great to normalize the
|
|
87
88
|
outputs from LLMs to be consistent, or to run on the inputs and outputs of LLM
|
|
88
89
|
transformations that edit text, so that the resulting diffs are clean.
|
|
@@ -95,6 +96,8 @@ The `--auto` option is just the same as `--inplace --nobackup --semantic`.
|
|
|
95
96
|
subsequent indentation** and **when to split words and lines**, e.g. using a word
|
|
96
97
|
splitter that won't break lines within HTML tags.
|
|
97
98
|
|
|
99
|
+
Other features:
|
|
100
|
+
|
|
98
101
|
- Flowmark has the option to to use **semantic line breaks** (using a heuristic to break
|
|
99
102
|
lines on sentences sentences when that is reasonable), which is an underrated feature
|
|
100
103
|
that can **make diffs on GitHub much more readable**. The the change may seem subtle
|
|
@@ -104,8 +107,11 @@ The `--auto` option is just the same as `--inplace --nobackup --semantic`.
|
|
|
104
107
|
[Markdown source](https://github.com/jlevy/flowmark/blob/main/README.md?plain=1) of
|
|
105
108
|
this readme file.)
|
|
106
109
|
|
|
107
|
-
- Very
|
|
108
|
-
|
|
110
|
+
- Very simple and fast **regex-based sentence splitting**. It's just based on letters
|
|
111
|
+
and punctuation so isn't perfect but works well for these purposes (and is much faster
|
|
112
|
+
and simpler than a proper sentence parser like SpaCy).
|
|
113
|
+
It should work fine for English and many other latin/Cyrillic languages but hasn't
|
|
114
|
+
been tested on CJK.
|
|
109
115
|
|
|
110
116
|
It aims to be small and simple and have only a few dependencies, currently only
|
|
111
117
|
[`marko`](https://github.com/frostming/marko),
|
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
Flowmark is a new Python implementation of **text and Markdown line wrapping and
|
|
4
4
|
filling**, with an emphasis on making **git diffs** and **LLM edits** to text documents
|
|
5
5
|
easier to diff and review.
|
|
6
|
-
after updating
|
|
7
6
|
|
|
8
|
-
In addition, it
|
|
9
|
-
|
|
7
|
+
In addition, it offers **Markdown auto-formatting and normalization** as a library or
|
|
8
|
+
from the command line.
|
|
10
9
|
This is much like [markdownfmt](https://github.com/shurcooL/markdownfmt) or
|
|
11
10
|
[prettier's Markdown support](https://prettier.io/blog/2017/11/07/1.8.0) but is pure
|
|
12
11
|
Python and has (in my humble opinion) better options and defaults.
|
|
@@ -58,8 +57,7 @@ The `--auto` option is just the same as `--inplace --nobackup --semantic`.
|
|
|
58
57
|
|
|
59
58
|
## Use Cases
|
|
60
59
|
|
|
61
|
-
|
|
62
|
-
command.
|
|
60
|
+
The main ways to use Flowmark are:
|
|
63
61
|
|
|
64
62
|
- To **autoformat Markdown on save in VSCode/Cursor** or any other editor that supports
|
|
65
63
|
running a command on save.
|
|
@@ -68,6 +66,9 @@ The `--auto` option is just the same as `--inplace --nobackup --semantic`.
|
|
|
68
66
|
formatting styles). This can be especially useful for documentation and editing
|
|
69
67
|
workflows where clean diffs and minimal merge conflicts on GitHub are important.
|
|
70
68
|
|
|
69
|
+
- As a **command line formatter** to format text or Markdown files using the `flowmark`
|
|
70
|
+
command.
|
|
71
|
+
|
|
71
72
|
- As a **library to autoformat Markdown**. For example, it is great to normalize the
|
|
72
73
|
outputs from LLMs to be consistent, or to run on the inputs and outputs of LLM
|
|
73
74
|
transformations that edit text, so that the resulting diffs are clean.
|
|
@@ -80,6 +81,8 @@ The `--auto` option is just the same as `--inplace --nobackup --semantic`.
|
|
|
80
81
|
subsequent indentation** and **when to split words and lines**, e.g. using a word
|
|
81
82
|
splitter that won't break lines within HTML tags.
|
|
82
83
|
|
|
84
|
+
Other features:
|
|
85
|
+
|
|
83
86
|
- Flowmark has the option to to use **semantic line breaks** (using a heuristic to break
|
|
84
87
|
lines on sentences sentences when that is reasonable), which is an underrated feature
|
|
85
88
|
that can **make diffs on GitHub much more readable**. The the change may seem subtle
|
|
@@ -89,8 +92,11 @@ The `--auto` option is just the same as `--inplace --nobackup --semantic`.
|
|
|
89
92
|
[Markdown source](https://github.com/jlevy/flowmark/blob/main/README.md?plain=1) of
|
|
90
93
|
this readme file.)
|
|
91
94
|
|
|
92
|
-
- Very
|
|
93
|
-
|
|
95
|
+
- Very simple and fast **regex-based sentence splitting**. It's just based on letters
|
|
96
|
+
and punctuation so isn't perfect but works well for these purposes (and is much faster
|
|
97
|
+
and simpler than a proper sentence parser like SpaCy).
|
|
98
|
+
It should work fine for English and many other latin/Cyrillic languages but hasn't
|
|
99
|
+
been tested on CJK.
|
|
94
100
|
|
|
95
101
|
It aims to be small and simple and have only a few dependencies, currently only
|
|
96
102
|
[`marko`](https://github.com/frostming/marko),
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
__all__ = (
|
|
2
2
|
"fill_text",
|
|
3
3
|
"fill_markdown",
|
|
4
|
+
"first_sentence",
|
|
5
|
+
"first_sentences",
|
|
4
6
|
"html_md_word_splitter",
|
|
5
7
|
"line_wrap_by_sentence",
|
|
6
8
|
"line_wrap_to_width",
|
|
9
|
+
"split_sentences_regex",
|
|
7
10
|
"wrap_paragraph",
|
|
8
11
|
"wrap_paragraph_lines",
|
|
9
12
|
"Wrap",
|
|
@@ -11,5 +14,6 @@ __all__ = (
|
|
|
11
14
|
|
|
12
15
|
from .line_wrappers import line_wrap_by_sentence, line_wrap_to_width
|
|
13
16
|
from .markdown_filling import fill_markdown
|
|
17
|
+
from .sentence_split_regex import first_sentence, first_sentences, split_sentences_regex
|
|
14
18
|
from .text_filling import Wrap, fill_text
|
|
15
19
|
from .text_wrapping import html_md_word_splitter, wrap_paragraph, wrap_paragraph_lines
|
|
@@ -2,43 +2,43 @@ from collections.abc import Callable
|
|
|
2
2
|
|
|
3
3
|
import regex
|
|
4
4
|
|
|
5
|
-
# These heuristics are from Flowmark:
|
|
6
|
-
# https://github.com/jlevy/atom-flowmark/blob/master/lib/remark-smart-word-wrap.js#L17-L33
|
|
7
|
-
|
|
8
|
-
# They work pretty well when used for formatting and editing documents in English.
|
|
9
|
-
# Note this is smarter than Python textwrap's simple heuristic:
|
|
10
|
-
# https://github.com/python/cpython/blob/main/Lib/textwrap.py#L105-L110
|
|
11
|
-
|
|
12
|
-
# Heuristic: End of sentence must be two letters or more, with the last letter lowercase,
|
|
13
|
-
# followed by a period, exclamation point, question mark. A final or preceding parenthesis
|
|
14
|
-
# or quote is allowed.
|
|
15
|
-
#
|
|
16
|
-
# Does not break on colon or semicolon currently as that seems to have false positives too
|
|
17
|
-
# often with code or other syntax.
|
|
18
|
-
#
|
|
19
5
|
# XXX: Could also handle rare cases with both quotes and parentheses at sentence end
|
|
20
6
|
# but may not be worth it. Also does not detect sentences ending in numerals, which
|
|
21
7
|
# tends to cause too many false positives. Should be OK for most Latin languages but
|
|
22
8
|
# may need to rethink the 2-letter restriction for some languages.
|
|
23
|
-
|
|
9
|
+
# See also:
|
|
10
|
+
# https://github.com/jlevy/atom-flowmark/blob/master/lib/remark-smart-word-wrap.js#L17-L33
|
|
11
|
+
SENTENCE_END_RE = regex.compile(r"(\b\p{L}+[\p{Ll}])([.?!]['\"’”)]?|['\"’”)][.?!]) *$")
|
|
24
12
|
|
|
25
13
|
# Second heuristic: Very short sentences often not so useful.
|
|
26
14
|
SENTENCE_MIN_LENGTH = 15
|
|
27
15
|
|
|
28
16
|
|
|
29
17
|
def heuristic_end_of_sentence(word: str) -> bool:
|
|
30
|
-
return bool(
|
|
18
|
+
return bool(SENTENCE_END_RE.search(word))
|
|
31
19
|
|
|
32
20
|
|
|
33
21
|
def split_sentences_regex(
|
|
34
22
|
text: str,
|
|
35
|
-
heuristic: Callable[[str], bool] = heuristic_end_of_sentence,
|
|
36
23
|
min_length: int = SENTENCE_MIN_LENGTH,
|
|
24
|
+
heuristic: Callable[[str], bool] = heuristic_end_of_sentence,
|
|
37
25
|
) -> list[str]:
|
|
38
26
|
"""
|
|
39
|
-
Split text into sentences using an approximate, fast regex heuristic.
|
|
27
|
+
Split text into sentences using an approximate, fast regex heuristic.
|
|
28
|
+
|
|
40
29
|
Goal is to be conservative, not perfect, avoiding excessive breaks.
|
|
41
30
|
|
|
31
|
+
The default heuristic: End of sentence must be two letters or more,
|
|
32
|
+
with the last letter lowercase, followed by a period, exclamation point,
|
|
33
|
+
question mark. A final or preceding parenthesis or quote is allowed.
|
|
34
|
+
Does not break on colon or semicolon as that seems to have false
|
|
35
|
+
positives too often with code or other syntax.
|
|
36
|
+
|
|
37
|
+
They work pretty well when used for formatting and editing documents
|
|
38
|
+
in English. It should be reasonable for most Latin languages.
|
|
39
|
+
Note this is smarter than Python textwrap's simpler heuristic:
|
|
40
|
+
https://github.com/python/cpython/blob/main/Lib/textwrap.py#L105-L110
|
|
41
|
+
|
|
42
42
|
:param text: The text to split into sentences.
|
|
43
43
|
:param heuristic: A callable that returns True if text ends at the end of a sentence.
|
|
44
44
|
:param min_length: The minimum length of a sentence in characters.
|
|
@@ -59,3 +59,28 @@ def split_sentences_regex(
|
|
|
59
59
|
if sentence:
|
|
60
60
|
sentences.append(" ".join(sentence))
|
|
61
61
|
return sentences
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def first_sentences(
|
|
65
|
+
text: str,
|
|
66
|
+
n: int,
|
|
67
|
+
min_length: int = SENTENCE_MIN_LENGTH,
|
|
68
|
+
heuristic: Callable[[str], bool] = heuristic_end_of_sentence,
|
|
69
|
+
) -> list[str]:
|
|
70
|
+
"""
|
|
71
|
+
Return the first n sentences from the text.
|
|
72
|
+
"""
|
|
73
|
+
return split_sentences_regex(text, min_length=min_length, heuristic=heuristic)[:n]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def first_sentence(
|
|
77
|
+
text: str,
|
|
78
|
+
min_length: int = SENTENCE_MIN_LENGTH,
|
|
79
|
+
heuristic: Callable[[str], bool] = heuristic_end_of_sentence,
|
|
80
|
+
) -> str:
|
|
81
|
+
"""
|
|
82
|
+
Return the first sentence from the text. Returns input text unchanged if no
|
|
83
|
+
sentences are found.
|
|
84
|
+
"""
|
|
85
|
+
sentences = split_sentences_regex(text, min_length=min_length, heuristic=heuristic)
|
|
86
|
+
return sentences[0] if sentences else text
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from flowmark import first_sentence, split_sentences_regex
|
|
2
|
+
|
|
3
|
+
LONG_TEXT = """
|
|
4
|
+
End of sentence must be two letters or more,
|
|
5
|
+
with the last letter lowercase, followed by a period, exclamation point,
|
|
6
|
+
question mark. A final or preceding parenthesis or quote is allowed.
|
|
7
|
+
Does not break on colon or semicolon as that seems to have false
|
|
8
|
+
positives too often with code or other syntax.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
FIRST_SENTENCE = "End of sentence must be two letters or more, with the last letter lowercase, followed by a period, exclamation point, question mark."
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_split_sentences():
|
|
15
|
+
assert split_sentences_regex("test!") == ["test!"]
|
|
16
|
+
assert split_sentences_regex("test! random words") == ["test! random words"]
|
|
17
|
+
|
|
18
|
+
split_sentences = split_sentences_regex(LONG_TEXT)
|
|
19
|
+
print(split_sentences)
|
|
20
|
+
assert len(split_sentences) == 3
|
|
21
|
+
assert split_sentences[0] == FIRST_SENTENCE
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_first_sentence():
|
|
25
|
+
assert first_sentence(LONG_TEXT) == FIRST_SENTENCE
|
|
26
|
+
|
|
27
|
+
assert first_sentence("") == ""
|
|
28
|
+
assert first_sentence(" ") == " "
|
|
29
|
+
assert first_sentence("hello") == "hello"
|
|
30
|
+
assert first_sentence(" hello\n") == "hello"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|