Skip to content

Commit cb5f62e

Browse files
authored
Fixed Issue Where Linking Would Strip Styles (#171)
* Added the missing test case * Managed to get tests to pass without breaking anything new * Updated dependencies for testing and docs * Incremented minor version * Discovered a mistake in repr * Updated pyproject file to 2.0 * Patched up the tests * Upgraded workflows to use poetry 2.1 * Updated version history * Walked back version to a prerelease * Added b1 to the docs
1 parent c87bff6 commit cb5f62e

File tree

10 files changed

+567
-342
lines changed

10 files changed

+567
-342
lines changed

.github/workflows/deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
fail-fast: true
1212
matrix:
1313
python-version: ["3.9"]
14-
poetry-version: ["1.4"]
14+
poetry-version: ["2.1"]
1515
os: ["ubuntu-latest"]
1616

1717
runs-on: ${{ matrix.os }}

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
fail-fast: true
1212
matrix:
1313
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
14-
poetry-version: ["1.4"]
14+
poetry-version: ["2.1"]
1515
os: [ubuntu-latest, macos-latest, windows-latest]
1616

1717
runs-on: ${{ matrix.os }}

docs/version-history.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ as follows:
1313
v2.x
1414
----
1515

16+
* v2.4.0b1 [:pr:`171`]
17+
18+
* Upgraded Poetry file format to 2.x
19+
* Added `breakline()` and `unbreakline()` methods
20+
* Fixed issue where inserting a link in a paragraph would strip styles
21+
1622
* v2.3.0 [:pr:`168`]
1723

1824
* Added a line break option to Inline text to grant a little more control over formatting

poetry.lock

Lines changed: 375 additions & 280 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
1-
# Poetry settings
2-
[tool.poetry]
1+
[project]
32
name = "SnakeMD"
43
description = "A markdown generation library for Python."
5-
version = "2.3.0"
64
license = "MIT"
7-
8-
authors = [
9-
"Jeremy Grifski <jeremy.grifski@therenegadecoder.com>"
10-
]
11-
5+
version = "2.4.0b1"
6+
dynamic = ["classifiers"]
127
readme = "README.md"
8+
authors = [{name = "Jeremy Grifski", email = "jeremy.grifski@therenegadecoder.com"}]
9+
requires-python = '>=3.9,<4.0'
10+
dependencies = []
11+
12+
[project.urls]
1313
homepage = "https://www.snakemd.io"
1414
repository = "https://github.com/TheRenegadeCoder/SnakeMD"
1515
documentation = "https://www.snakemd.io/en/latest/docs/"
16+
Changelog = "https://www.snakemd.io/en/latest/version-history/"
17+
18+
# Poetry settings
19+
[tool.poetry]
1620

1721
classifiers=[
1822
"Development Status :: 5 - Production/Stable",
@@ -25,12 +29,7 @@ classifiers=[
2529
"Operating System :: OS Independent",
2630
"Topic :: Documentation :: Sphinx",
2731
]
28-
29-
[tool.poetry.urls]
30-
Changelog = "https://www.snakemd.io/en/latest/version-history/"
31-
32-
[tool.poetry.dependencies]
33-
python = "^3.9"
32+
requires-poetry = '>=2.0,<3.0'
3433

3534
[tool.poetry.group.test.dependencies]
3635
coverage = "^7.2"
@@ -99,5 +98,5 @@ max-line-length = 88
9998

10099
# Build system settings
101100
[build-system]
102-
requires = ["poetry-core"]
101+
requires = ['poetry-core (>=2.0,<3.0)']
103102
build-backend = "poetry.core.masonry.api"

snakemd/elements.py

Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,8 @@ def __repr__(self) -> str:
184184
f"bold={self._bold!r}, "
185185
f"italics={self._italics!r}, "
186186
f"strikethrough={self._strikethrough!r}, "
187-
f"code={self._code!r}"
187+
f"code={self._code!r}, "
188+
f"linebreak={self._linebreak!r}"
188189
")"
189190
)
190191

@@ -384,6 +385,38 @@ def uncode(self) -> Inline:
384385
"""
385386
self._code = False
386387
return self
388+
389+
def breakline(self) -> Inline:
390+
"""
391+
Adds a linebreak to self.
392+
393+
.. doctest:: inline
394+
395+
>>> inline = Inline("Hello").breakline()
396+
>>> print(inline)
397+
Hello<br />
398+
399+
:return:
400+
self
401+
"""
402+
self._linebreak = True
403+
return self
404+
405+
def unbreakline(self) -> Inline:
406+
"""
407+
Removes linebreak from self.
408+
409+
.. doctest:: inline
410+
411+
>>> inline = Inline("Hello", linebreak=True).unbreakline()
412+
>>> print(inline)
413+
Hello
414+
415+
:return:
416+
self
417+
"""
418+
self._linebreak = False
419+
return self
387420

388421
def link(self, link: str) -> Inline:
389422
"""
@@ -448,6 +481,19 @@ def reset(self) -> Inline:
448481
self._bold = False
449482
self._strikethrough = False
450483
return self
484+
485+
def _apply_styles_from(self, text: Inline) -> Inline:
486+
"""
487+
A helper method that applies text styling to self from another
488+
Inline object. This includes only boolean styling information
489+
related to the behavior of is_text(). In other words, link, image,
490+
and code information is not copied over.
491+
"""
492+
self._bold = text._bold
493+
self._italics = text._italics
494+
self._strikethrough = text._strikethrough
495+
self._linebreak = text._linebreak
496+
return self
451497

452498

453499
class Block(Element): # pylint: disable=too-few-public-methods
@@ -1056,23 +1102,35 @@ def _replace_any(self, target: str, text: Inline, count: int = -1) -> Paragraph:
10561102
:return:
10571103
self
10581104
"""
1059-
i = 0
1060-
content = []
1105+
content: list[Inline] = []
10611106
for inline_text in self._content:
1062-
if (
1063-
inline_text.is_text()
1064-
and len(items := inline_text.get_text().split(target)) > 1
1065-
):
1107+
# Skip inline elements that we don't care about
1108+
if not inline_text.is_text() or target not in inline_text.get_text():
1109+
content.append(inline_text)
1110+
continue
1111+
# Split the inline element into pieces and reapply styles
1112+
else:
1113+
# Redistributes styles
1114+
items = [
1115+
Inline(item)._apply_styles_from(inline_text)
1116+
for item in inline_text.get_text().split(target, count)
1117+
]
1118+
1119+
# Adds text back in with appropriate style information
10661120
for item in items:
1067-
content.append(Inline(item))
1068-
if count == -1 or i < count:
1069-
content.append(text)
1070-
i += 1
1071-
else:
1072-
content.append(Inline(target))
1121+
content.append(item)
1122+
content.append(text._apply_styles_from(inline_text))
10731123
content.pop()
1074-
else:
1075-
content.append(inline_text)
1124+
1125+
# Trim empty strings from edges
1126+
if content[-1].get_text() == "":
1127+
content.pop()
1128+
if content[0].get_text() == "":
1129+
content = content[1:]
1130+
1131+
# Remove line breaks that may have been distributed by applying styles
1132+
content[:-1] = map(lambda item: item.unbreakline(), content[:-1])
1133+
10761134
self._content = content
10771135
return self
10781136

tests/elements/test_heading.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ def test_heading_empty():
2525
r"bold=False, "
2626
r"italics=False, "
2727
r"strikethrough=False, "
28-
r"code=False"
28+
r"code=False, "
29+
r"linebreak=False"
2930
r")]"
3031
r", level=1)"
3132
)
@@ -51,7 +52,8 @@ def test_heading_str_level_one():
5152
r"bold=False, "
5253
r"italics=False, "
5354
r"strikethrough=False, "
54-
r"code=False"
55+
r"code=False, "
56+
r"linebreak=False"
5557
r")]"
5658
r", level=1)"
5759
)
@@ -77,7 +79,8 @@ def test_heading_inline_level_one():
7779
r"bold=False, "
7880
r"italics=False, "
7981
r"strikethrough=False, "
80-
r"code=False"
82+
r"code=False, "
83+
r"linebreak=False"
8184
r")]"
8285
r", level=1)"
8386
)
@@ -103,7 +106,8 @@ def test_heading_inline_bold_level_one():
103106
r"bold=True, "
104107
r"italics=False, "
105108
r"strikethrough=False, "
106-
r"code=False"
109+
r"code=False, "
110+
r"linebreak=False"
107111
r")]"
108112
r", level=1)"
109113
)
@@ -129,7 +133,8 @@ def test_heading_str_level_two():
129133
r"bold=False, "
130134
r"italics=False, "
131135
r"strikethrough=False, "
132-
r"code=False"
136+
r"code=False, "
137+
r"linebreak=False"
133138
r")]"
134139
r", level=2)"
135140
)
@@ -155,7 +160,8 @@ def test_heading_str_level_three():
155160
r"bold=False, "
156161
r"italics=False, "
157162
r"strikethrough=False, "
158-
r"code=False"
163+
r"code=False, "
164+
r"linebreak=False"
159165
r")]"
160166
r", level=3)"
161167
)
@@ -181,7 +187,8 @@ def test_heading_str_level_four():
181187
r"bold=False, "
182188
r"italics=False, "
183189
r"strikethrough=False, "
184-
r"code=False"
190+
r"code=False, "
191+
r"linebreak=False"
185192
r")]"
186193
r", level=4)"
187194
)
@@ -207,7 +214,8 @@ def test_heading_str_level_five():
207214
r"bold=False, "
208215
r"italics=False, "
209216
r"strikethrough=False, "
210-
r"code=False"
217+
r"code=False, "
218+
r"linebreak=False"
211219
r")]"
212220
r", level=5)"
213221
)
@@ -233,7 +241,8 @@ def test_heading_str_level_six():
233241
r"bold=False, "
234242
r"italics=False, "
235243
r"strikethrough=False, "
236-
r"code=False"
244+
r"code=False, "
245+
r"linebreak=False"
237246
r")]"
238247
r", level=6)"
239248
)
@@ -260,7 +269,8 @@ def test_heading_list():
260269
r"bold=False, "
261270
r"italics=False, "
262271
r"strikethrough=False, "
263-
r"code=False"
272+
r"code=False, "
273+
r"linebreak=False"
264274
r"), "
265275
r"Inline("
266276
r"text=' heading', "
@@ -269,7 +279,8 @@ def test_heading_list():
269279
r"bold=False, "
270280
r"italics=False, "
271281
r"strikethrough=False, "
272-
r"code=False"
282+
r"code=False, "
283+
r"linebreak=False"
273284
r")"
274285
r"]"
275286
r", level=1)"
@@ -298,7 +309,8 @@ def test_heading_list_styling():
298309
r"bold=True, "
299310
r"italics=False, "
300311
r"strikethrough=False, "
301-
r"code=False"
312+
r"code=False, "
313+
r"linebreak=False"
302314
r"), "
303315
r"Inline("
304316
r"text=' heading', "
@@ -307,7 +319,8 @@ def test_heading_list_styling():
307319
r"bold=False, "
308320
r"italics=False, "
309321
r"strikethrough=False, "
310-
r"code=False"
322+
r"code=False, "
323+
r"linebreak=False"
311324
r")"
312325
r"]"
313326
r", level=1)"

0 commit comments

Comments
 (0)