Skip to content

Commit 9356b7c

Browse files
Add whitespace sensitive tag support
1 parent 8a23a05 commit 9356b7c

File tree

9 files changed

+129
-19
lines changed

9 files changed

+129
-19
lines changed

meta/tags.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,11 @@ div:
141141
span:
142142
base: StylableTag
143143

144+
pre:
145+
base: WhitespaceSensitiveTag
146+
144147
textarea:
145-
base: StylableTag
148+
base: WhitespaceSensitiveTag
146149
attributes:
147150
required:
148151
doc: Whether the input is required to submit the form it is contained within.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
class {name}({base}):
2+
"""
3+
{description}
4+
5+
{attr_docs_outer}
6+
7+
[View full documentation]({link})
8+
"""
9+
def __init__(
10+
self,
11+
*children: ChildrenType,
12+
{attr_args}
13+
**attributes: AttributeType,
14+
) -> None:
15+
"""
16+
{description}
17+
18+
{attr_docs_inner}
19+
20+
[View full documentation]({link})
21+
"""
22+
attributes |= {
23+
{attr_unions}
24+
}
25+
super().__init__(*children, **attributes)
26+
27+
def __call__( # type: ignore
28+
self,
29+
*children: ChildrenType,
30+
{attr_args}
31+
**attributes: AttributeType,
32+
):
33+
"""
34+
{description}
35+
36+
{attr_docs_inner}
37+
38+
[View full documentation]({link})
39+
"""
40+
attributes |= {
41+
{attr_unions}
42+
}
43+
return super().__call__(*children, **attributes)
44+
45+
def _get_default_attributes(self, given: dict[str, AttributeType]) -> dict[str, AttributeType]:
46+
return {default_attrs}

meta/templates/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
https://creativecommons.org/licenses/by-sa/2.5/
99
"""
1010
from typing import Any, Optional, Union, Literal
11-
from ..__tag_base import Tag, SelfClosingTag
11+
from ..__tag_base import Tag, SelfClosingTag, WhitespaceSensitiveTag
1212
from ..__types import AttributeType, ChildrenType

pyhtml/__tag_base.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,47 @@ def _render(self, indent: int) -> list[str]:
171171
]
172172
else:
173173
return [f"{' ' * indent}<{self._get_tag_name()}/>"]
174+
175+
176+
class WhitespaceSensitiveTag(Tag):
177+
"""
178+
Whitespace-sensitive tags are tags where whitespace needs to be respected.
179+
"""
180+
def __init__(
181+
self,
182+
*children: ChildrenType,
183+
**attributes: AttributeType,
184+
) -> None:
185+
attributes |= {}
186+
super().__init__(*children, **attributes)
187+
188+
def __call__( # type: ignore
189+
self,
190+
*children: ChildrenType,
191+
**attributes: AttributeType,
192+
):
193+
attributes |= {}
194+
return super().__call__(*children, **attributes)
195+
196+
def _render(self, indent: int) -> list[str]:
197+
attributes = util.filter_attributes(util.dict_union(
198+
self._get_default_attributes(self.attributes),
199+
self.attributes,
200+
))
201+
202+
# Tag and attributes
203+
output = f"{' ' * indent}<{self._get_tag_name()}"
204+
205+
if len(attributes):
206+
output += f" {util.render_tag_attributes(attributes)}>"
207+
else:
208+
output += ">"
209+
210+
output += '\n'.join(util.render_children(
211+
self.children,
212+
self._escape_children(),
213+
0,
214+
))
215+
216+
output += f"</{self._get_tag_name()}>"
217+
return output.splitlines()

pyhtml/__tags/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"""
66
# Re-export renamed versions of tags
77
from .renames import input_, object_
8-
from .input import input
8+
from .input_tag import input
99
from .dangerous_raw_html import DangerousRawHtml
1010
from .comment import Comment
1111

pyhtml/__tags/generated.py

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
https://creativecommons.org/licenses/by-sa/2.5/
99
"""
1010
from typing import Any, Optional, Union, Literal
11-
from ..__tag_base import Tag, SelfClosingTag
11+
from ..__tag_base import Tag, SelfClosingTag, WhitespaceSensitiveTag
1212
from ..__types import AttributeType, ChildrenType
1313

1414
class html(Tag):
@@ -1857,7 +1857,7 @@ def _get_default_attributes(self, given: dict[str, AttributeType]) -> dict[str,
18571857
return {}
18581858

18591859

1860-
class pre(Tag):
1860+
class pre(WhitespaceSensitiveTag):
18611861
"""
18621862
Represents preformatted text which is to be presented exactly as written in the HTML file. The text is typically rendered using a non-proportional, or [monospaced](https://en.wikipedia.org/wiki/Monospaced_font), font. Whitespace inside this element is displayed as written.
18631863
@@ -5444,7 +5444,7 @@ def _get_default_attributes(self, given: dict[str, AttributeType]) -> dict[str,
54445444
return {'required': None, 'name': None, 'disabled': None, 'multiple': None}
54455445

54465446

5447-
class textarea(Tag):
5447+
class textarea(WhitespaceSensitiveTag):
54485448
"""
54495449
Represents a multi-line plain-text editing control, useful when you want to allow users to enter a sizeable amount of free-form text, for example, a comment on a review or feedback form.
54505450
@@ -5472,9 +5472,6 @@ def __init__(
54725472
maxlength: AttributeType = None,
54735473
wrap: Union[Literal['hard', 'soft'], None] = None,
54745474
readonly: Optional[bool] = None,
5475-
id: Optional[str] = None,
5476-
_class: Optional[str] = None,
5477-
style: Optional[str] = None,
54785475
**attributes: AttributeType,
54795476
) -> None:
54805477
"""
@@ -5493,9 +5490,6 @@ def __init__(
54935490
[View full documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea)
54945491
"""
54955492
attributes |= {
5496-
'_class': _class,
5497-
'id': id,
5498-
'style': style,
54995493
'required': required,
55005494
'name': name,
55015495
'rows': rows,
@@ -5520,9 +5514,6 @@ def __call__( # type: ignore
55205514
maxlength: AttributeType = None,
55215515
wrap: Union[Literal['hard', 'soft'], None] = None,
55225516
readonly: Optional[bool] = None,
5523-
id: Optional[str] = None,
5524-
_class: Optional[str] = None,
5525-
style: Optional[str] = None,
55265517
**attributes: AttributeType,
55275518
):
55285519
"""
@@ -5541,9 +5532,6 @@ def __call__( # type: ignore
55415532
[View full documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea)
55425533
"""
55435534
attributes |= {
5544-
'_class': _class,
5545-
'id': id,
5546-
'style': style,
55475535
'required': required,
55485536
'name': name,
55495537
'rows': rows,
File renamed without changes.

pyhtml/__tags/renames.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
we still export the originals.
88
"""
99
from .generated import object as object_ # type: ignore
10-
from .input import input as input_
10+
from .input_tag import input as input_
1111

1212
__all__ = [
1313
'object_',

tests/whitespace_sensitive_test.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""
2+
# Test / Whitespace Sensitive Test
3+
4+
Tests for rendering whitespace sensitive tags.
5+
"""
6+
import pyhtml as p
7+
8+
9+
def test_pre():
10+
assert str(p.pre("hello\nworld")) == '\n'.join([
11+
"<pre>hello",
12+
"world</pre>",
13+
])
14+
15+
16+
def test_textarea():
17+
assert str(p.textarea("hello\nworld")) == '\n'.join([
18+
"<textarea>hello",
19+
"world</textarea>",
20+
])
21+
22+
23+
def test_indentation_ignored():
24+
assert str(p.body(p.pre("hello\nworld"))) == '\n'.join([
25+
"<body>",
26+
" <pre>hello",
27+
"world</pre>",
28+
"</body>",
29+
])

0 commit comments

Comments
 (0)