unicode-segmentation-rs 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.
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.github/actions/pre-commit-setup/action.yml +4 -3
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.github/workflows/ci.yml +2 -2
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.github/workflows/pre-commit.yml +1 -0
- unicode_segmentation_rs-0.3.3/.github/workflows/security-audit.yml +42 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.pre-commit-config.yaml +2 -2
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/Cargo.lock +11 -11
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/Cargo.toml +2 -2
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/PKG-INFO +9 -1
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/README.md +8 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/src/lib.rs +149 -44
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.editorconfig +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.github/FUNDING.yml +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.github/PULL_REQUEST_TEMPLATE.md.license +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.github/release.yml +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.github/renovate.json +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.github/renovate.json.license +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.github/workflows/dependency-review.yml +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.github/workflows/pull_requests.yaml +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.gitignore +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.yamllint.yml +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/LICENSE +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/LICENSES/Apache-2.0.txt +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/LICENSES/CC0-1.0.txt +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/REUSE.toml +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/SECURITY.md +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/pyproject.toml +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/tests/test_gettext_wrap.py +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/tests/test_readme_examples.py +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/tests/test_unicode_segmentation.py +0 -0
- {unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/unicode_segmentation_rs.pyi +0 -0
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: CC0-1.0
|
|
4
4
|
|
|
5
|
+
# This file is maintained in https://github.com/WeblateOrg/meta/
|
|
6
|
+
|
|
5
7
|
name: pre-commit setup
|
|
6
8
|
description: Prepare pre-commit environment
|
|
7
9
|
|
|
@@ -19,8 +21,7 @@ runs:
|
|
|
19
21
|
- name: Setup Python
|
|
20
22
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
21
23
|
with:
|
|
22
|
-
python-version: '3.
|
|
23
|
-
allow-prereleases: true
|
|
24
|
+
python-version: '3.14'
|
|
24
25
|
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
25
26
|
with:
|
|
26
|
-
version: 0.12.
|
|
27
|
+
version: 0.12.3
|
|
@@ -53,7 +53,7 @@ jobs:
|
|
|
53
53
|
with:
|
|
54
54
|
save-cache: ${{ github.ref == 'refs/heads/main' }}
|
|
55
55
|
cache-suffix: ${{ steps.setup_python.outputs.python-version }}
|
|
56
|
-
version: 0.12.
|
|
56
|
+
version: 0.12.3
|
|
57
57
|
activate-environment: 'true'
|
|
58
58
|
- run: uv sync
|
|
59
59
|
- name: Run tests
|
|
@@ -315,7 +315,7 @@ jobs:
|
|
|
315
315
|
persist-credentials: false
|
|
316
316
|
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
317
317
|
- name: Generate artifact attestation
|
|
318
|
-
uses: actions/attest-build-provenance@
|
|
318
|
+
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
|
|
319
319
|
with:
|
|
320
320
|
subject-path: wheels-*/*
|
|
321
321
|
- name: Publish to PyPI
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Copyright © Michal Čihař <michal@weblate.org>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
name: Security audit
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
pull_request:
|
|
9
|
+
push:
|
|
10
|
+
branches:
|
|
11
|
+
- main
|
|
12
|
+
paths:
|
|
13
|
+
- Cargo.lock
|
|
14
|
+
- Cargo.toml
|
|
15
|
+
- .github/workflows/security-audit.yml
|
|
16
|
+
schedule:
|
|
17
|
+
- cron: 37 4 * * 1
|
|
18
|
+
workflow_dispatch:
|
|
19
|
+
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
|
|
23
|
+
env:
|
|
24
|
+
# renovate: datasource=crate depName=cargo-audit
|
|
25
|
+
CARGO_AUDIT_VERSION: 0.22.2
|
|
26
|
+
|
|
27
|
+
jobs:
|
|
28
|
+
rustsec:
|
|
29
|
+
name: RustSec audit
|
|
30
|
+
runs-on: ubuntu-24.04
|
|
31
|
+
timeout-minutes: 15
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
34
|
+
with:
|
|
35
|
+
persist-credentials: false
|
|
36
|
+
- name: Install cargo-audit
|
|
37
|
+
uses: taiki-e/install-action@6c6fd71fe4fb72c3697d269963d0e15df8adedad # v2.85.10
|
|
38
|
+
with:
|
|
39
|
+
fallback: none
|
|
40
|
+
tool: cargo-audit@${{ env.CARGO_AUDIT_VERSION }}
|
|
41
|
+
- name: Audit Rust dependencies
|
|
42
|
+
run: cargo audit --file Cargo.lock
|
|
@@ -21,7 +21,7 @@ repos:
|
|
|
21
21
|
hooks:
|
|
22
22
|
- id: yamllint
|
|
23
23
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
24
|
-
rev: v0.16.
|
|
24
|
+
rev: v0.16.2
|
|
25
25
|
hooks:
|
|
26
26
|
- id: ruff-check
|
|
27
27
|
args:
|
|
@@ -52,7 +52,7 @@ repos:
|
|
|
52
52
|
hooks:
|
|
53
53
|
- id: zizmor
|
|
54
54
|
- repo: https://github.com/mongodb/kingfisher
|
|
55
|
-
rev: v1.
|
|
55
|
+
rev: v1.112.0
|
|
56
56
|
hooks:
|
|
57
57
|
- id: kingfisher-auto
|
|
58
58
|
- repo: https://github.com/rvben/rumdl-pre-commit
|
|
@@ -37,9 +37,9 @@ dependencies = [
|
|
|
37
37
|
|
|
38
38
|
[[package]]
|
|
39
39
|
name = "pyo3"
|
|
40
|
-
version = "0.29.
|
|
40
|
+
version = "0.29.2"
|
|
41
41
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
42
|
-
checksum = "
|
|
42
|
+
checksum = "4688ddedf473e32662b9b067670129a8afb8c18e351482c70d62ba4a88171e8b"
|
|
43
43
|
dependencies = [
|
|
44
44
|
"libc",
|
|
45
45
|
"once_cell",
|
|
@@ -51,18 +51,18 @@ dependencies = [
|
|
|
51
51
|
|
|
52
52
|
[[package]]
|
|
53
53
|
name = "pyo3-build-config"
|
|
54
|
-
version = "0.29.
|
|
54
|
+
version = "0.29.2"
|
|
55
55
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
56
|
-
checksum = "
|
|
56
|
+
checksum = "f41027e41b4bd03f6e60f9f417fe24a6341a6bb744edd62b6f709f2a52ea30e9"
|
|
57
57
|
dependencies = [
|
|
58
58
|
"target-lexicon",
|
|
59
59
|
]
|
|
60
60
|
|
|
61
61
|
[[package]]
|
|
62
62
|
name = "pyo3-ffi"
|
|
63
|
-
version = "0.29.
|
|
63
|
+
version = "0.29.2"
|
|
64
64
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
65
|
-
checksum = "
|
|
65
|
+
checksum = "e591a95526fead067432c3b3a33fc74770b87b1e04e73671090d9c2055a2b327"
|
|
66
66
|
dependencies = [
|
|
67
67
|
"libc",
|
|
68
68
|
"pyo3-build-config",
|
|
@@ -70,9 +70,9 @@ dependencies = [
|
|
|
70
70
|
|
|
71
71
|
[[package]]
|
|
72
72
|
name = "pyo3-macros"
|
|
73
|
-
version = "0.29.
|
|
73
|
+
version = "0.29.2"
|
|
74
74
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
75
|
-
checksum = "
|
|
75
|
+
checksum = "73225868fc1cd84eef2c3c230ddb91273bf1de46aeb8a4248da76d32a0924a1c"
|
|
76
76
|
dependencies = [
|
|
77
77
|
"proc-macro2",
|
|
78
78
|
"pyo3-macros-backend",
|
|
@@ -82,9 +82,9 @@ dependencies = [
|
|
|
82
82
|
|
|
83
83
|
[[package]]
|
|
84
84
|
name = "pyo3-macros-backend"
|
|
85
|
-
version = "0.29.
|
|
85
|
+
version = "0.29.2"
|
|
86
86
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
87
|
-
checksum = "
|
|
87
|
+
checksum = "571575aa3749fa6216757dd47d2a3e7ef360f329a40f0666a9fbd14889024952"
|
|
88
88
|
dependencies = [
|
|
89
89
|
"heck",
|
|
90
90
|
"proc-macro2",
|
|
@@ -138,7 +138,7 @@ checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
|
|
|
138
138
|
|
|
139
139
|
[[package]]
|
|
140
140
|
name = "unicode-segmentation-rs"
|
|
141
|
-
version = "0.3.
|
|
141
|
+
version = "0.3.3"
|
|
142
142
|
dependencies = [
|
|
143
143
|
"pyo3",
|
|
144
144
|
"unicode-linebreak",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[dependencies]
|
|
2
|
-
pyo3 = {version = "0.29.
|
|
2
|
+
pyo3 = {version = "0.29.2", features = ["extension-module", "abi3-py310"]}
|
|
3
3
|
unicode-linebreak = "0.1.5"
|
|
4
4
|
unicode-segmentation = "1.13.3"
|
|
5
5
|
unicode-width = "0.2.2"
|
|
@@ -19,4 +19,4 @@ license = "Apache-2.0"
|
|
|
19
19
|
name = "unicode-segmentation-rs"
|
|
20
20
|
readme = "README.md"
|
|
21
21
|
repository = "https://github.com/WeblateOrg/unicode-segmentation-rs"
|
|
22
|
-
version = "0.3.
|
|
22
|
+
version = "0.3.3"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: unicode-segmentation-rs
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: 3.10
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -26,6 +26,11 @@ Project-URL: Issue Tracker, https://github.com/WeblateOrg/unicode-segmentation-r
|
|
|
26
26
|
Project-URL: Source Code, https://github.com/WeblateOrg/unicode-segmentation-rs
|
|
27
27
|
Project-URL: Twitter, https://twitter.com/WeblateOrg
|
|
28
28
|
|
|
29
|
+
<a href="https://weblate.org/"><img alt="Weblate" src="https://s.weblate.org/cdn/Logo-Darktext-borders.png" height="80px" /></a>
|
|
30
|
+
|
|
31
|
+
**Weblate is libre software web-based continuous localization system,
|
|
32
|
+
used by over 2500 libre projects and companies in more than 165 countries.**
|
|
33
|
+
|
|
29
34
|
# unicode-segmentation-rs
|
|
30
35
|
|
|
31
36
|
Python bindings for the Rust [unicode-segmentation](https://docs.rs/unicode-segmentation/), [unicode-linebreak](https://docs.rs/unicode-linebreak/), and [unicode-width](https://docs.rs/unicode-width/) crates, providing Unicode text segmentation and width calculation according to Unicode standards.
|
|
@@ -235,6 +240,9 @@ print(lines)
|
|
|
235
240
|
|
|
236
241
|
## API Reference
|
|
237
242
|
|
|
243
|
+
Functions returning lists materialize the complete result. Applications processing very large or
|
|
244
|
+
untrusted text should enforce an input-size limit appropriate for their environment.
|
|
245
|
+
|
|
238
246
|
### `graphemes(text: str, is_extended: bool) -> list[str]`
|
|
239
247
|
|
|
240
248
|
Split a string into grapheme clusters. Set `is_extended=True` for extended grapheme clusters (recommended).
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
<a href="https://weblate.org/"><img alt="Weblate" src="https://s.weblate.org/cdn/Logo-Darktext-borders.png" height="80px" /></a>
|
|
2
|
+
|
|
3
|
+
**Weblate is libre software web-based continuous localization system,
|
|
4
|
+
used by over 2500 libre projects and companies in more than 165 countries.**
|
|
5
|
+
|
|
1
6
|
# unicode-segmentation-rs
|
|
2
7
|
|
|
3
8
|
Python bindings for the Rust [unicode-segmentation](https://docs.rs/unicode-segmentation/), [unicode-linebreak](https://docs.rs/unicode-linebreak/), and [unicode-width](https://docs.rs/unicode-width/) crates, providing Unicode text segmentation and width calculation according to Unicode standards.
|
|
@@ -207,6 +212,9 @@ print(lines)
|
|
|
207
212
|
|
|
208
213
|
## API Reference
|
|
209
214
|
|
|
215
|
+
Functions returning lists materialize the complete result. Applications processing very large or
|
|
216
|
+
untrusted text should enforce an input-size limit appropriate for their environment.
|
|
217
|
+
|
|
210
218
|
### `graphemes(text: str, is_extended: bool) -> list[str]`
|
|
211
219
|
|
|
212
220
|
Split a string into grapheme clusters. Set `is_extended=True` for extended grapheme clusters (recommended).
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
#[pyo3::pymodule(gil_used = false)]
|
|
6
6
|
mod unicode_segmentation_rs {
|
|
7
|
+
use std::ops::Range;
|
|
8
|
+
|
|
7
9
|
use pyo3::prelude::*;
|
|
8
10
|
use pyo3::types::PyList;
|
|
9
11
|
use unicode_linebreak::{BreakOpportunity, linebreaks as unicode_linebreaks};
|
|
@@ -57,14 +59,16 @@ mod unicode_segmentation_rs {
|
|
|
57
59
|
|
|
58
60
|
/// Get Unicode line-break opportunities as UTF-8 byte offsets.
|
|
59
61
|
#[pyfunction]
|
|
60
|
-
fn line_breaks(text: &str) -> PyResult<
|
|
62
|
+
fn line_breaks<'py>(py: Python<'py>, text: &str) -> PyResult<Bound<'py, PyList>> {
|
|
61
63
|
if text.is_empty() {
|
|
62
|
-
return Ok(
|
|
64
|
+
return Ok(PyList::empty(py));
|
|
63
65
|
}
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
PyList::new(
|
|
68
|
+
py,
|
|
69
|
+
unicode_linebreaks(text)
|
|
70
|
+
.map(|(offset, opportunity)| (offset, opportunity == BreakOpportunity::Mandatory)),
|
|
71
|
+
)
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
/// Split a string at every Unicode line-break opportunity.
|
|
@@ -100,21 +104,25 @@ mod unicode_segmentation_rs {
|
|
|
100
104
|
/// - Handles CJK characters with proper width calculation
|
|
101
105
|
/// - Breaks long words only when necessary
|
|
102
106
|
#[pyfunction]
|
|
103
|
-
fn gettext_wrap(
|
|
107
|
+
fn gettext_wrap<'py>(
|
|
108
|
+
py: Python<'py>,
|
|
109
|
+
text: &str,
|
|
110
|
+
width: usize,
|
|
111
|
+
) -> PyResult<Bound<'py, PyList>> {
|
|
104
112
|
if text.is_empty() || width == 0 {
|
|
105
113
|
return if text.is_empty() {
|
|
106
|
-
Ok(
|
|
114
|
+
Ok(PyList::empty(py))
|
|
107
115
|
} else {
|
|
108
|
-
|
|
116
|
+
PyList::new(py, [text])
|
|
109
117
|
};
|
|
110
118
|
}
|
|
111
119
|
|
|
112
|
-
|
|
120
|
+
PyList::new(py, wrap_po_chunks(text, PoChunks::new(text), width))
|
|
113
121
|
}
|
|
114
122
|
|
|
115
|
-
/// Iterator over
|
|
123
|
+
/// Iterator over chunk byte ranges split using word boundaries and PO-specific rules.
|
|
116
124
|
struct PoChunks<'a> {
|
|
117
|
-
|
|
125
|
+
text_len: usize,
|
|
118
126
|
word_bounds: UWordBoundIndices<'a>,
|
|
119
127
|
chunk_start: Option<usize>,
|
|
120
128
|
last_char: Option<char>,
|
|
@@ -124,7 +132,7 @@ mod unicode_segmentation_rs {
|
|
|
124
132
|
impl<'a> PoChunks<'a> {
|
|
125
133
|
fn new(text: &'a str) -> Self {
|
|
126
134
|
Self {
|
|
127
|
-
text,
|
|
135
|
+
text_len: text.len(),
|
|
128
136
|
word_bounds: text.split_word_bound_indices(),
|
|
129
137
|
chunk_start: None,
|
|
130
138
|
last_char: None,
|
|
@@ -133,11 +141,7 @@ mod unicode_segmentation_rs {
|
|
|
133
141
|
}
|
|
134
142
|
|
|
135
143
|
/// Add a word-boundary segment and return the completed chunk's byte range, if any.
|
|
136
|
-
fn add_segment(
|
|
137
|
-
&mut self,
|
|
138
|
-
segment_start: usize,
|
|
139
|
-
segment: &str,
|
|
140
|
-
) -> Option<std::ops::Range<usize>> {
|
|
144
|
+
fn add_segment(&mut self, segment_start: usize, segment: &str) -> Option<Range<usize>> {
|
|
141
145
|
let first_char = segment.chars().next().unwrap();
|
|
142
146
|
let should_merge = self.last_char.is_some_and(|last_char| {
|
|
143
147
|
(self.second_last_char.is_none()
|
|
@@ -176,8 +180,8 @@ mod unicode_segmentation_rs {
|
|
|
176
180
|
}
|
|
177
181
|
}
|
|
178
182
|
|
|
179
|
-
impl
|
|
180
|
-
type Item =
|
|
183
|
+
impl Iterator for PoChunks<'_> {
|
|
184
|
+
type Item = Range<usize>;
|
|
181
185
|
|
|
182
186
|
fn next(&mut self) -> Option<Self::Item> {
|
|
183
187
|
while let Some((segment_start, segment)) = self.word_bounds.next() {
|
|
@@ -196,48 +200,100 @@ mod unicode_segmentation_rs {
|
|
|
196
200
|
);
|
|
197
201
|
}
|
|
198
202
|
|
|
199
|
-
return Some(
|
|
203
|
+
return Some(completed_start..segment_end);
|
|
200
204
|
}
|
|
201
205
|
|
|
202
206
|
if let Some(completed) = self.add_segment(segment_start, segment) {
|
|
203
|
-
return Some(
|
|
207
|
+
return Some(completed);
|
|
204
208
|
}
|
|
205
209
|
}
|
|
206
210
|
|
|
207
|
-
self.chunk_start
|
|
208
|
-
.take()
|
|
209
|
-
.map(|start| &self.text[start..self.text.len()])
|
|
211
|
+
self.chunk_start.take().map(|start| start..self.text_len)
|
|
210
212
|
}
|
|
211
213
|
}
|
|
212
214
|
|
|
213
|
-
///
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
215
|
+
/// Iterator that wraps chunk ranges into borrowed lines respecting the width limit.
|
|
216
|
+
struct WrappedLines<'a, I>
|
|
217
|
+
where
|
|
218
|
+
I: Iterator<Item = Range<usize>>,
|
|
219
|
+
{
|
|
220
|
+
text: &'a str,
|
|
221
|
+
chunks: I,
|
|
222
|
+
width: usize,
|
|
223
|
+
current_start: Option<usize>,
|
|
224
|
+
current_end: usize,
|
|
225
|
+
current_width: usize,
|
|
226
|
+
emit_current: bool,
|
|
227
|
+
}
|
|
218
228
|
|
|
219
|
-
|
|
220
|
-
|
|
229
|
+
impl<'a, I> Iterator for WrappedLines<'a, I>
|
|
230
|
+
where
|
|
231
|
+
I: Iterator<Item = Range<usize>>,
|
|
232
|
+
{
|
|
233
|
+
type Item = &'a str;
|
|
221
234
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
235
|
+
fn next(&mut self) -> Option<Self::Item> {
|
|
236
|
+
if self.emit_current {
|
|
237
|
+
self.emit_current = false;
|
|
238
|
+
self.current_width = 0;
|
|
239
|
+
let start = self.current_start.take()?;
|
|
240
|
+
return Some(&self.text[start..self.current_end]);
|
|
225
241
|
}
|
|
226
|
-
current_line.push_str(chunk);
|
|
227
|
-
current_width += chunk_width;
|
|
228
242
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
243
|
+
for chunk_range in self.chunks.by_ref() {
|
|
244
|
+
let chunk_start = chunk_range.start;
|
|
245
|
+
let chunk_end = chunk_range.end;
|
|
246
|
+
debug_assert_eq!(
|
|
247
|
+
self.current_end, chunk_start,
|
|
248
|
+
"wrapped chunks should be contiguous"
|
|
249
|
+
);
|
|
250
|
+
let chunk = &self.text[chunk_range];
|
|
251
|
+
let chunk_width = chunk.width();
|
|
252
|
+
|
|
253
|
+
if self.current_width + chunk_width > self.width {
|
|
254
|
+
if let Some(completed_start) = self.current_start.replace(chunk_start) {
|
|
255
|
+
let completed_end = self.current_end;
|
|
256
|
+
self.current_end = chunk_end;
|
|
257
|
+
self.current_width = chunk_width;
|
|
258
|
+
self.emit_current = chunk.ends_with("\\n");
|
|
259
|
+
return Some(&self.text[completed_start..completed_end]);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if self.current_start.is_none() {
|
|
264
|
+
self.current_start = Some(chunk_start);
|
|
265
|
+
}
|
|
266
|
+
self.current_end = chunk_end;
|
|
267
|
+
self.current_width += chunk_width;
|
|
268
|
+
|
|
269
|
+
// Force break on \n
|
|
270
|
+
if chunk.ends_with("\\n") {
|
|
271
|
+
self.current_width = 0;
|
|
272
|
+
let start = self.current_start.take()?;
|
|
273
|
+
return Some(&self.text[start..self.current_end]);
|
|
274
|
+
}
|
|
233
275
|
}
|
|
234
|
-
}
|
|
235
276
|
|
|
236
|
-
|
|
237
|
-
|
|
277
|
+
self.current_width = 0;
|
|
278
|
+
self.current_start
|
|
279
|
+
.take()
|
|
280
|
+
.map(|start| &self.text[start..self.current_end])
|
|
238
281
|
}
|
|
282
|
+
}
|
|
239
283
|
|
|
240
|
-
|
|
284
|
+
fn wrap_po_chunks<C>(text: &str, chunks: C, width: usize) -> WrappedLines<'_, C::IntoIter>
|
|
285
|
+
where
|
|
286
|
+
C: IntoIterator<Item = Range<usize>>,
|
|
287
|
+
{
|
|
288
|
+
WrappedLines {
|
|
289
|
+
text,
|
|
290
|
+
chunks: chunks.into_iter(),
|
|
291
|
+
width,
|
|
292
|
+
current_start: None,
|
|
293
|
+
current_end: 0,
|
|
294
|
+
current_width: 0,
|
|
295
|
+
emit_current: false,
|
|
296
|
+
}
|
|
241
297
|
}
|
|
242
298
|
|
|
243
299
|
/// Check if a string contains only mergeable characters
|
|
@@ -300,4 +356,53 @@ mod unicode_segmentation_rs {
|
|
|
300
356
|
| '~'
|
|
301
357
|
)
|
|
302
358
|
}
|
|
359
|
+
|
|
360
|
+
#[cfg(test)]
|
|
361
|
+
mod tests {
|
|
362
|
+
use std::cell::Cell;
|
|
363
|
+
|
|
364
|
+
use super::wrap_po_chunks;
|
|
365
|
+
|
|
366
|
+
#[test]
|
|
367
|
+
fn wrapped_lines_are_produced_incrementally() {
|
|
368
|
+
let consumed = Cell::new(0);
|
|
369
|
+
let text = "first second third fourth";
|
|
370
|
+
let chunks = [0..6, 6..13, 13..19, 19..25]
|
|
371
|
+
.into_iter()
|
|
372
|
+
.inspect(|_| consumed.set(consumed.get() + 1));
|
|
373
|
+
let mut lines = wrap_po_chunks(text, chunks, 7);
|
|
374
|
+
|
|
375
|
+
assert_eq!(lines.next(), Some("first "));
|
|
376
|
+
assert_eq!(consumed.get(), 2);
|
|
377
|
+
assert_eq!(lines.collect::<Vec<_>>(), ["second ", "third ", "fourth"]);
|
|
378
|
+
assert_eq!(consumed.get(), 4);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
#[test]
|
|
382
|
+
fn wrapped_lines_preserve_edge_cases() {
|
|
383
|
+
let text = "first \\nlast";
|
|
384
|
+
assert_eq!(
|
|
385
|
+
wrap_po_chunks(text, [0..6, 6..8, 8..12], 6).collect::<Vec<_>>(),
|
|
386
|
+
["first ", "\\n", "last"]
|
|
387
|
+
);
|
|
388
|
+
assert!(
|
|
389
|
+
wrap_po_chunks("", std::iter::empty(), 10)
|
|
390
|
+
.collect::<Vec<_>>()
|
|
391
|
+
.is_empty()
|
|
392
|
+
);
|
|
393
|
+
assert_eq!(
|
|
394
|
+
wrap_po_chunks("unwrapped", std::iter::once(0..9), 0).collect::<Vec<_>>(),
|
|
395
|
+
["unwrapped"]
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
#[test]
|
|
400
|
+
fn wrapped_lines_borrow_from_the_input() {
|
|
401
|
+
let text = "first second";
|
|
402
|
+
let line = wrap_po_chunks(text, [0..6, 6..12], 20).next().unwrap();
|
|
403
|
+
|
|
404
|
+
assert_eq!(line, text);
|
|
405
|
+
assert_eq!(line.as_ptr(), text.as_ptr());
|
|
406
|
+
}
|
|
407
|
+
}
|
|
303
408
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.github/ISSUE_TEMPLATE/config.yml
RENAMED
|
File without changes
|
|
File without changes
|
{unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.github/PULL_REQUEST_TEMPLATE.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.github/renovate.json.license
RENAMED
|
File without changes
|
|
File without changes
|
{unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/.github/workflows/pull_requests.yaml
RENAMED
|
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
|
{unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/tests/test_readme_examples.py
RENAMED
|
File without changes
|
{unicode_segmentation_rs-0.3.2 → unicode_segmentation_rs-0.3.3}/tests/test_unicode_segmentation.py
RENAMED
|
File without changes
|
|
File without changes
|