Skip to content

Commit bd5a904

Browse files
committed
feat: expand replacement table and change dash mapping to single hyphen
- Change en/em dash replacement from '--' to '-' - Add hyphen variants: U+2010 HYPHEN, U+2011 NON-BREAKING HYPHEN, U+2012 FIGURE DASH, U+2015 HORIZONTAL BAR, U+FE58 SMALL EM DASH - Add soft hyphen (U+00AD) removal (invisible layout hint) - Add bullets: U+2022 BULLET, U+2023 TRIANGULAR BULLET -> *, U+2043 HYPHEN BULLET -> - - Add dot leaders: U+2024 -> ., U+2025 -> .. - Add arrows: U+2190 -> <-, U+2192 -> ->, U+2191 -> ^, U+2193 -> v - Add math operators: U+00D7 -> x, U+00F7 -> /, U+2044 -> /
1 parent e345992 commit bd5a904

5 files changed

Lines changed: 106 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818

1919
### Changed
2020

21+
- En dash and em dash now replace to `-` instead of `--`
22+
- Expanded `--fix` replacement table with: hyphen variants (U+2010-2012, U+2015,
23+
U+FE58), soft hyphen (removed), bullets, dot leaders, arrows (`->`, `<-`, `^`,
24+
`v`), and math operators (`x`, `/`)
2125
- Add `pytest-sugar` for improved test output
2226
- Replace mypy with [ty](https://github.com/astral-sh/ty) for type checking
2327
- Move dev dependencies from `optional-dependencies` to `dependency-groups`

docs/check-unicode.1

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -317,19 +317,49 @@ Lines already flushed remain in the output.
317317
.B Smart quotes
318318
\(lq\(rq \(oq\(cq and variants \(-> replaced with ASCII quotes
319319
.TP
320-
.B Dashes
321-
Em dash (U+2014), en dash (U+2013), minus sign (U+2212) \(-> replaced with
322-
.B \-\-
323-
or
320+
.B Dashes and hyphens
321+
Em dash, en dash, figure dash, horizontal bar, minus sign, and other
322+
dash\-like characters \(-> replaced with
324323
.BR \- .
325324
.TP
325+
.B Soft hyphen
326+
U+00AD \(-> removed (invisible layout hint, not content).
327+
.TP
326328
.B Fancy spaces
327329
Non\-breaking space, em space, thin space, and 14 other Unicode space characters
328330
\(-> replaced with a regular space.
329331
.TP
330332
.B Ellipsis
331333
Horizontal ellipsis (U+2026) \(-> replaced with
332334
.BR ... .
335+
.TP
336+
.B Bullets
337+
Bullet (U+2022), triangular bullet, hyphen bullet \(-> replaced with
338+
.B *
339+
or
340+
.BR \- .
341+
.TP
342+
.B Dot leaders
343+
One dot leader, two dot leader \(-> replaced with
344+
.B .
345+
or
346+
.BR .. .
347+
.TP
348+
.B Arrows
349+
\(-> and \(<- \(-> replaced with
350+
.B \->
351+
and
352+
.BR <\- ;
353+
\(ua and \(da \(-> replaced with
354+
.B ^
355+
and
356+
.BR v .
357+
.TP
358+
.B Math operators
359+
Multiplication sign (\(mu) \(-> replaced with
360+
.BR x ;
361+
division sign (\(di) and fraction slash \(-> replaced with
362+
.BR / .
333363
.
334364
.SS Dangerous invisible characters (never auto\-fixed)
335365
.TP

src/check_unicode/categories.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,17 @@
3636
0x201F: '"', # DOUBLE HIGH-REVERSED-9 QUOTATION MARK
3737
0x00AB: '"', # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
3838
0x00BB: '"', # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
39-
# Dashes
40-
0x2013: "--", # EN DASH
41-
0x2014: "--", # EM DASH
42-
# Minus
39+
# Dashes and hyphens
40+
0x2010: "-", # HYPHEN
41+
0x2011: "-", # NON-BREAKING HYPHEN
42+
0x2012: "-", # FIGURE DASH
43+
0x2013: "-", # EN DASH
44+
0x2014: "-", # EM DASH
45+
0x2015: "-", # HORIZONTAL BAR
4346
0x2212: "-", # MINUS SIGN
47+
0xFE58: "-", # SMALL EM DASH
48+
# Soft hyphen (invisible layout hint, not content)
49+
0x00AD: "", # SOFT HYPHEN
4450
# Fancy spaces -> regular space
4551
0x00A0: " ", # NO-BREAK SPACE
4652
0x2000: " ", # EN QUAD
@@ -57,4 +63,20 @@
5763
0x3000: " ", # IDEOGRAPHIC SPACE
5864
# Ellipsis
5965
0x2026: "...", # HORIZONTAL ELLIPSIS
66+
# Bullets
67+
0x2022: "*", # BULLET
68+
0x2023: "*", # TRIANGULAR BULLET
69+
0x2043: "-", # HYPHEN BULLET
70+
# Dot leaders
71+
0x2024: ".", # ONE DOT LEADER
72+
0x2025: "..", # TWO DOT LEADER
73+
# Arrows
74+
0x2190: "<-", # LEFTWARDS ARROW
75+
0x2192: "->", # RIGHTWARDS ARROW
76+
0x2191: "^", # UPWARDS ARROW
77+
0x2193: "v", # DOWNWARDS ARROW
78+
# Math operators
79+
0x00D7: "x", # MULTIPLICATION SIGN
80+
0x00F7: "/", # DIVISION SIGN
81+
0x2044: "/", # FRACTION SLASH
6082
}

tests/test_cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def test_fix_multiple_files_all_fixed(self, tmp_path: Path) -> None:
121121
f2.write_text("word\u2014word\n", encoding="utf-8")
122122
assert main(["--fix", str(f1), str(f2)]) == 1
123123
assert f1.read_text(encoding="utf-8") == 'He said "hello"\n'
124-
assert f2.read_text(encoding="utf-8") == "word--word\n"
124+
assert f2.read_text(encoding="utf-8") == "word-word\n"
125125

126126
def test_fix_dangerous_still_reported(self, tmp_path: Path) -> None:
127127
"""Fix mode does not remove dangerous characters."""

tests/test_fixer.py

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,33 @@ class TestFixText:
116116
("\u201equote\u201f", '"quote"'),
117117
("\u00abguillemet\u00bb", '"guillemet"'),
118118
("\u2039angle\u203a", "'angle'"),
119-
("word\u2014word", "word--word"),
120-
("1\u20132", "1--2"),
119+
("word\u2014word", "word-word"),
120+
("1\u20132", "1-2"),
121121
("x \u2212 y", "x - y"),
122122
("hello\u00a0world", "hello world"),
123123
("a\u2003b", "a b"),
124124
("a\u2009b", "a b"),
125125
("a\u200ab", "a b"),
126126
("a\u3000b", "a b"),
127127
("wait\u2026", "wait..."),
128+
("a\u2010b", "a-b"),
129+
("a\u2011b", "a-b"),
130+
("a\u2012b", "a-b"),
131+
("a\u2015b", "a-b"),
132+
("a\ufe58b", "a-b"),
133+
("soft\u00adhyphen", "softhyphen"),
134+
("\u2022 item", "* item"),
135+
("\u2023 item", "* item"),
136+
("\u2043 item", "- item"),
137+
("ch\u20241", "ch.1"),
138+
("ch\u20251", "ch..1"),
139+
("a \u2192 b", "a -> b"),
140+
("b \u2190 a", "b <- a"),
141+
("\u2191up", "^up"),
142+
("\u2193down", "vdown"),
143+
("2 \u00d7 3", "2 x 3"),
144+
("6 \u00f7 2", "6 / 2"),
145+
("1\u20442", "1/2"),
128146
],
129147
ids=[
130148
"smart-double-quotes",
@@ -143,6 +161,24 @@ class TestFixText:
143161
"hair-space",
144162
"ideographic-space",
145163
"ellipsis",
164+
"hyphen",
165+
"non-breaking-hyphen",
166+
"figure-dash",
167+
"horizontal-bar",
168+
"small-em-dash",
169+
"soft-hyphen",
170+
"bullet",
171+
"triangular-bullet",
172+
"hyphen-bullet",
173+
"one-dot-leader",
174+
"two-dot-leader",
175+
"right-arrow",
176+
"left-arrow",
177+
"up-arrow",
178+
"down-arrow",
179+
"multiplication-sign",
180+
"division-sign",
181+
"fraction-slash",
146182
],
147183
)
148184
def test_fix_replaces_character(self, input_text: str, expected: str) -> None:
@@ -169,7 +205,7 @@ def test_mixed_fixable_nonfixable_dangerous(self) -> None:
169205
def test_multiline_text(self) -> None:
170206
"""fix_text handles multi-line strings correctly."""
171207
text = "line1 \u201chi\u201d\nline2 word\u2014word\nline3 wait\u2026\n"
172-
expected = 'line1 "hi"\nline2 word--word\nline3 wait...\n'
208+
expected = 'line1 "hi"\nline2 word-word\nline3 wait...\n'
173209
assert fix_text(text) == expected
174210

175211

@@ -182,8 +218,8 @@ class TestFixFileReplacements:
182218
("\u201chello\u201d", '"hello"'),
183219
("It\u2019s", "It's"),
184220
("\u2018word\u2019", "'word'"),
185-
("word\u2014word", "word--word"),
186-
("1\u20132", "1--2"),
221+
("word\u2014word", "word-word"),
222+
("1\u20132", "1-2"),
187223
("x \u2212 y", "x - y"),
188224
("hello\u00a0world", "hello world"),
189225
("a\u2003b", "a b"),

0 commit comments

Comments
 (0)