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.
Files changed (30) hide show
  1. {flowmark-0.3.2 → flowmark-0.3.3}/PKG-INFO +14 -8
  2. {flowmark-0.3.2 → flowmark-0.3.3}/README.md +13 -7
  3. {flowmark-0.3.2 → flowmark-0.3.3}/src/flowmark/__init__.py +4 -0
  4. {flowmark-0.3.2 → flowmark-0.3.3}/src/flowmark/sentence_split_regex.py +43 -18
  5. flowmark-0.3.3/tests/test_sentences.py +30 -0
  6. {flowmark-0.3.2 → flowmark-0.3.3}/.copier-answers.yml +0 -0
  7. {flowmark-0.3.2 → flowmark-0.3.3}/.github/workflows/ci.yml +0 -0
  8. {flowmark-0.3.2 → flowmark-0.3.3}/.github/workflows/publish.yml +0 -0
  9. {flowmark-0.3.2 → flowmark-0.3.3}/.gitignore +0 -0
  10. {flowmark-0.3.2 → flowmark-0.3.3}/LICENSE +0 -0
  11. {flowmark-0.3.2 → flowmark-0.3.3}/Makefile +0 -0
  12. {flowmark-0.3.2 → flowmark-0.3.3}/development.md +0 -0
  13. {flowmark-0.3.2 → flowmark-0.3.3}/devtools/lint.py +0 -0
  14. {flowmark-0.3.2 → flowmark-0.3.3}/poetry.lock +0 -0
  15. {flowmark-0.3.2 → flowmark-0.3.3}/publishing.md +0 -0
  16. {flowmark-0.3.2 → flowmark-0.3.3}/pyproject.toml +0 -0
  17. {flowmark-0.3.2 → flowmark-0.3.3}/src/flowmark/cli.py +0 -0
  18. {flowmark-0.3.2 → flowmark-0.3.3}/src/flowmark/frontmatter.py +0 -0
  19. {flowmark-0.3.2 → flowmark-0.3.3}/src/flowmark/line_wrappers.py +0 -0
  20. {flowmark-0.3.2 → flowmark-0.3.3}/src/flowmark/markdown_filling.py +0 -0
  21. {flowmark-0.3.2 → flowmark-0.3.3}/src/flowmark/text_filling.py +0 -0
  22. {flowmark-0.3.2 → flowmark-0.3.3}/src/flowmark/text_wrapping.py +0 -0
  23. {flowmark-0.3.2 → flowmark-0.3.3}/tests/test_filling.py +0 -0
  24. {flowmark-0.3.2 → flowmark-0.3.3}/tests/test_frontmatter.py +0 -0
  25. {flowmark-0.3.2 → flowmark-0.3.3}/tests/test_ref_docs.py +0 -0
  26. {flowmark-0.3.2 → flowmark-0.3.3}/tests/test_wrapping.py +0 -0
  27. {flowmark-0.3.2 → flowmark-0.3.3}/tests/testdocs/testdoc.orig.md +0 -0
  28. {flowmark-0.3.2 → flowmark-0.3.3}/tests/testdocs/testdoc.out.plain.md +0 -0
  29. {flowmark-0.3.2 → flowmark-0.3.3}/tests/testdocs/testdoc.out.semantic.md +0 -0
  30. {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.2
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 adds **support for Markdown** and offers **Markdown auto-formatting and
24
- normalization** as a library or from the command line.
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
- - As a **command line formatter** to format text or Markdown files using the `flowmark`
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, very simple and fast **regex-based sentence splitting**. This should work fine
108
- for English and many other latin/Cyrillic languages but it hasn't been tested on CJK.
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 adds **support for Markdown** and offers **Markdown auto-formatting and
9
- normalization** as a library or from the command line.
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
- - As a **command line formatter** to format text or Markdown files using the `flowmark`
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, very simple and fast **regex-based sentence splitting**. This should work fine
93
- for English and many other latin/Cyrillic languages but it hasn't been tested on CJK.
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
- SENTENCE_RE = regex.compile(r"(\b\p{L}+[\p{Ll}])([.?!]['\"’”)]?|['\"’”)][.?!]) *$")
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(SENTENCE_RE.search(word))
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. (English.)
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