diff --git a/meta/tags.yml b/meta/tags.yml index ba6c912..779ae2d 100644 --- a/meta/tags.yml +++ b/meta/tags.yml @@ -141,8 +141,11 @@ div: span: base: StylableTag +pre: + base: WhitespaceSensitiveTag + textarea: - base: StylableTag + base: WhitespaceSensitiveTag attributes: required: doc: Whether the input is required to submit the form it is contained within. diff --git a/meta/templates/class_attrs_WhitespaceSensitiveTag.py b/meta/templates/class_attrs_WhitespaceSensitiveTag.py new file mode 100644 index 0000000..41e3596 --- /dev/null +++ b/meta/templates/class_attrs_WhitespaceSensitiveTag.py @@ -0,0 +1,46 @@ +class {name}({base}): + """ + {description} + + {attr_docs_outer} + + [View full documentation]({link}) + """ + def __init__( + self, + *children: ChildrenType, + {attr_args} + **attributes: AttributeType, + ) -> None: + """ + {description} + + {attr_docs_inner} + + [View full documentation]({link}) + """ + attributes |= { + {attr_unions} + } + super().__init__(*children, **attributes) + + def __call__( # type: ignore + self, + *children: ChildrenType, + {attr_args} + **attributes: AttributeType, + ): + """ + {description} + + {attr_docs_inner} + + [View full documentation]({link}) + """ + attributes |= { + {attr_unions} + } + return super().__call__(*children, **attributes) + + def _get_default_attributes(self, given: dict[str, AttributeType]) -> dict[str, AttributeType]: + return {default_attrs} diff --git a/meta/templates/main.py b/meta/templates/main.py index 6228d81..3b80466 100644 --- a/meta/templates/main.py +++ b/meta/templates/main.py @@ -8,5 +8,5 @@ https://creativecommons.org/licenses/by-sa/2.5/ """ from typing import Any, Optional, Union, Literal -from ..__tag_base import Tag, SelfClosingTag +from ..__tag_base import Tag, SelfClosingTag, WhitespaceSensitiveTag from ..__types import AttributeType, ChildrenType diff --git a/pyhtml/__tag_base.py b/pyhtml/__tag_base.py index 669e981..2333a69 100644 --- a/pyhtml/__tag_base.py +++ b/pyhtml/__tag_base.py @@ -171,3 +171,47 @@ def _render(self, indent: int) -> list[str]: ] else: return [f"{' ' * indent}<{self._get_tag_name()}/>"] + + +class WhitespaceSensitiveTag(Tag): + """ + Whitespace-sensitive tags are tags where whitespace needs to be respected. + """ + def __init__( + self, + *children: ChildrenType, + **attributes: AttributeType, + ) -> None: + attributes |= {} + super().__init__(*children, **attributes) + + def __call__( # type: ignore + self, + *children: ChildrenType, + **attributes: AttributeType, + ): + attributes |= {} + return super().__call__(*children, **attributes) + + def _render(self, indent: int) -> list[str]: + attributes = util.filter_attributes(util.dict_union( + self._get_default_attributes(self.attributes), + self.attributes, + )) + + # Tag and attributes + output = f"{' ' * indent}<{self._get_tag_name()}" + + if len(attributes): + output += f" {util.render_tag_attributes(attributes)}>" + else: + output += ">" + + output += '\n'.join(util.render_children( + self.children, + self._escape_children(), + 0, + )) + + output += f"{self._get_tag_name()}>" + return output.splitlines() diff --git a/pyhtml/__tags/__init__.py b/pyhtml/__tags/__init__.py index 81f305b..dbfccb4 100644 --- a/pyhtml/__tags/__init__.py +++ b/pyhtml/__tags/__init__.py @@ -5,7 +5,7 @@ """ # Re-export renamed versions of tags from .renames import input_, object_ -from .input import input +from .input_tag import input from .dangerous_raw_html import DangerousRawHtml from .comment import Comment diff --git a/pyhtml/__tags/generated.py b/pyhtml/__tags/generated.py index 0d800b0..70c05ce 100644 --- a/pyhtml/__tags/generated.py +++ b/pyhtml/__tags/generated.py @@ -8,7 +8,7 @@ https://creativecommons.org/licenses/by-sa/2.5/ """ from typing import Any, Optional, Union, Literal -from ..__tag_base import Tag, SelfClosingTag +from ..__tag_base import Tag, SelfClosingTag, WhitespaceSensitiveTag from ..__types import AttributeType, ChildrenType class html(Tag): @@ -1857,7 +1857,7 @@ def _get_default_attributes(self, given: dict[str, AttributeType]) -> dict[str, return {} -class pre(Tag): +class pre(WhitespaceSensitiveTag): """ 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. @@ -5444,7 +5444,7 @@ def _get_default_attributes(self, given: dict[str, AttributeType]) -> dict[str, return {'required': None, 'name': None, 'disabled': None, 'multiple': None} -class textarea(Tag): +class textarea(WhitespaceSensitiveTag): """ 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. @@ -5472,9 +5472,6 @@ def __init__( maxlength: AttributeType = None, wrap: Union[Literal['hard', 'soft'], None] = None, readonly: Optional[bool] = None, - id: Optional[str] = None, - _class: Optional[str] = None, - style: Optional[str] = None, **attributes: AttributeType, ) -> None: """ @@ -5493,9 +5490,6 @@ def __init__( [View full documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea) """ attributes |= { - '_class': _class, - 'id': id, - 'style': style, 'required': required, 'name': name, 'rows': rows, @@ -5520,9 +5514,6 @@ def __call__( # type: ignore maxlength: AttributeType = None, wrap: Union[Literal['hard', 'soft'], None] = None, readonly: Optional[bool] = None, - id: Optional[str] = None, - _class: Optional[str] = None, - style: Optional[str] = None, **attributes: AttributeType, ): """ @@ -5541,9 +5532,6 @@ def __call__( # type: ignore [View full documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea) """ attributes |= { - '_class': _class, - 'id': id, - 'style': style, 'required': required, 'name': name, 'rows': rows, diff --git a/pyhtml/__tags/input.py b/pyhtml/__tags/input_tag.py similarity index 100% rename from pyhtml/__tags/input.py rename to pyhtml/__tags/input_tag.py diff --git a/pyhtml/__tags/renames.py b/pyhtml/__tags/renames.py index d9f7f4e..8c830d0 100644 --- a/pyhtml/__tags/renames.py +++ b/pyhtml/__tags/renames.py @@ -7,7 +7,7 @@ we still export the originals. """ from .generated import object as object_ # type: ignore -from .input import input as input_ +from .input_tag import input as input_ __all__ = [ 'object_', diff --git a/tests/whitespace_sensitive_test.py b/tests/whitespace_sensitive_test.py new file mode 100644 index 0000000..9eb2951 --- /dev/null +++ b/tests/whitespace_sensitive_test.py @@ -0,0 +1,29 @@ +""" +# Test / Whitespace Sensitive Test + +Tests for rendering whitespace sensitive tags. +""" +import pyhtml as p + + +def test_pre(): + assert str(p.pre("hello\nworld")) == '\n'.join([ + "
hello", + "world", + ]) + + +def test_textarea(): + assert str(p.textarea("hello\nworld")) == '\n'.join([ + "", + ]) + + +def test_indentation_ignored(): + assert str(p.body(p.pre("hello\nworld"))) == '\n'.join([ + "", + "
hello", + "world", + "", + ])