Skip to content

Commit

Permalink
Refactor Header
Browse files Browse the repository at this point in the history
- Now instead of using Header.choose_header() method must be used
  str(Header()).
- Add new enums for defining Header styles and Header levels.
  • Loading branch information
didix21 committed Apr 29, 2023
1 parent 2d31ac5 commit a4f2085
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 234 deletions.
21 changes: 10 additions & 11 deletions mdutils/mdutils.py
Expand Up @@ -21,7 +21,7 @@
import mdutils.tools.Table
import mdutils.tools.TableOfContents
from mdutils.fileutils.fileutils import MarkDownFile
from mdutils.tools.Header import Header
from mdutils.tools.Header import Header, HeaderStyle
from mdutils.tools.Image import Image
from mdutils.tools.Link import Inline, Reference
from mdutils.tools.TextUtils import TextUtils
Expand Down Expand Up @@ -57,9 +57,8 @@ def __init__(self, file_name: str, title: str = "", author: str = ""):
"""
self.file_name = file_name
self.author = author
self.header = Header()
self.textUtils = TextUtils
self.title = self.header.choose_header(level=1, title=title, style="setext")
self.title = str(Header(level=1, title=title, style=HeaderStyle.SETEXT))
self.table_of_contents = ""
self.file_data_text = ""
self._table_titles = []
Expand Down Expand Up @@ -136,9 +135,9 @@ def new_header(
if add_table_of_contents == "y":
self.__add_new_item_table_of_content(level, title)
self.___update_file_data(
self.header.choose_header(level, title, style, header_id)
str(Header(level, title, HeaderStyle[style.upper()], header_id))
)
return self.header.choose_header(level, title, style, header_id)
return str(Header(level, title, HeaderStyle[style.upper()], header_id))

def __add_new_item_table_of_content(self, level: int, item: Union[List[str], str]):
"""Automatically add new atx headers to the table of contents.
Expand Down Expand Up @@ -182,9 +181,9 @@ def new_table_of_contents(

if marker:
self.table_of_contents = ""
marker_table_of_contents = self.header.choose_header(
level=1, title=table_title, style="setext"
)
marker_table_of_contents = str(Header(
level=1, title=table_title, style=HeaderStyle.SETEXT
))
marker_table_of_contents += mdutils.tools.TableOfContents.TableOfContents().create_table_of_contents(
self._table_titles, depth
)
Expand All @@ -193,9 +192,9 @@ def new_table_of_contents(
)
else:
marker_table_of_contents = ""
self.table_of_contents += self.header.choose_header(
level=1, title=table_title, style="setext"
)
self.table_of_contents += str(Header(
level=1, title=table_title, style=HeaderStyle.SETEXT
))
self.table_of_contents += mdutils.tools.TableOfContents.TableOfContents().create_table_of_contents(
self._table_titles, depth
)
Expand Down
253 changes: 97 additions & 156 deletions mdutils/tools/Header.py
@@ -1,161 +1,115 @@
# Python
#
# This module implements a text class that allows to modify and create text on Markdown files.
#
# This file is part of mdutils. https://github.com/didix21/mdutils
#
# MIT License: (C) 2020 Dídac Coll
from enum import Enum, auto
import warnings


class Header:
"""Contain the main methods to define Headers on a Markdown file.
class AtxHeaderLevel(Enum):
TITLE = auto() # H1 - Main title (largest and most important)
HEADING = auto() # H2 - Section headings
SUBHEADING = auto() # H3 - Subsection headings
SUBSUBHEADING = auto() # H4 - Smaller subsection headings
MINORHEADING = auto() # H5 - Even smaller headings
LEASTHEADING = auto() # H6 - The smallest heading level

**Features available:**

* Create Markdown Titles: *atx* and *setext* formats are available.
* Create Header Hanchors.
* Auto generate a table of contents.
* Create Tables.
* **Bold**, *italics*, ``inline_code`` text converters.
* Align text to center.
* Add color to text.
"""
class SetextHeaderLevel(Enum):
TITLE = auto()
HEADING = auto()

# ********************************************************************
# * Atx-Style *
# ********************************************************************
@staticmethod
def atx_level_1(title: str, header_id: str = "") -> str:
"""Return a atx level 1 header.

:param str title: text title.
:param header_id: ID of the header for extended Markdown syntax
:return: a header title of form: ``'\\n#' + title + '\\n'``
:rtype: str
"""
if len(header_id):
header_id = " {#" + header_id + "}"

return "\n# " + title + header_id + "\n"
class HeaderStyle(Enum):
ATX = auto()
SETEXT = auto()

@staticmethod
def atx_level_2(title: str, header_id: str = "") -> str:
"""Return a atx level 2 header.

:param str title: text title.
:param header_id: ID of the header for extended Markdown syntax
:return: a header title of form: ``'\\n##' + title + '\\n'``
:rtype: str
"""
if len(header_id):
header_id = " {#" + header_id + "}"

return "\n## " + title + header_id + "\n"

@staticmethod
def atx_level_3(title: str, header_id: str = "") -> str:
"""Return a atx level 3 header.
class Header:
"""Contain the main methods to define Headers on a Markdown file.
:param str title: text title.
:param header_id: ID of the header for extended Markdown syntax
:return: a header title of form: ``'\\n###' + title + '\\n'``
:rtype: str
"""
if len(header_id):
header_id = " {#" + header_id + "}"
Features available:
- Create Markdown Titles: *atx* and *setext* formats are available.
- Create Header Anchors.
return "\n### " + title + header_id + "\n"
:Example:
>>> str(Header(level=1, title='New Header', style=HeaderStyle.ATX))
"""

@staticmethod
def atx_level_4(title: str, header_id: str = "") -> str:
"""Return a atx level 4 header.
def __init__(self, level: int, title: str, style: HeaderStyle, header_id: str = None) -> None:
"""Choose and return the header based on the given level, style, and title.
:param str title: text title.
:param header_id: ID of the header for extended Markdown syntax
:return: a header title of form: ``'\\n####' + title + '\\n'``
:rtype: str
:param level: HeaderLevel enum member, e.g., TITLE, HEADING, SUBHEADING, etc.
:param title: Text title.
:param style: HeaderStyle enum member, e.g., ATX, SETEXT.
:param header_id: ID of the header for extended Markdown syntax (optional)
:return: a header string based on the given level, style, and title
"""
if len(header_id):
header_id = " {#" + header_id + "}"
self.level = level
self.title = title
self.style = style
self.header_id = header_id

return "\n#### " + title + header_id + "\n"
def __str__(self) -> str:
return self._new(self.level, self.title, self.style, self.header_id)

@staticmethod
def atx_level_5(title: str, header_id: str = "") -> str:
"""Return a atx level 5 header.
def atx(level: AtxHeaderLevel, title: str, header_id: str = None) -> str:
"""Return an atx-style header.
:param str title: text title.
:param header_id: ID of the header for extended Markdown syntax
:return: a header title of form: ``'\\n#####' + title + '\\n'``
:rtype: str
:param title: Text title.
:param level: HeaderLevel enum member, e.g., TITLE, HEADING, SUBHEADING, etc.
:param header_id: ID of the header for extended Markdown syntax (optional)
:return: an atx-style header string
"""
if len(header_id):
header_id = " {#" + header_id + "}"

return "\n##### " + title + header_id + "\n"
header_id = Header._get_header_id(header_id)
return "\n" + "#" * level.value + " " + title + header_id + "\n"

@staticmethod
def atx_level_6(title: str, header_id: str = "") -> str:
"""Return a atx level 6 header.
def setext(level: SetextHeaderLevel, title: str) -> str:
"""Return a setext-style header.
:param str title: text title.
:param header_id: ID of the header for extended Markdown syntax
:return: a header title of form: ``'\\n######' + title + '\\n'``
:rtype: str
:param title: Text title.
:param level: HeaderLevel enum member, e.g., TITLE, HEADING.
:return: a setext-style header string
"""
if len(header_id):
header_id = " {#" + header_id + "}"
if level == SetextHeaderLevel.TITLE:
separator = "="
else:
separator = "-"

return "\n###### " + title + header_id + "\n"
return "\n" + title + "\n" + separator * len(title) + "\n"

# ********************************************************************
# * Setext-Style *
# ********************************************************************
@staticmethod
def setext_level_1(title: str) -> str:
"""Return a setext level 1 header.
def header_anchor(text: str, link: str = None) -> str:
"""Create an internal link to a defined header level in the markdown file.
:param str title: text title.
:return: a header titlte of form: ``'\\n' + title +'\\n==========\\n'``.
:rtype: str
"""
:param text: Displayed text for the link.
:param link: Internal link (optional). If not provided, it is generated based on the text.
:return: a header anchor string
return "\n" + title + "\n" + "".join(["=" for _ in title]) + "\n"
Examples:
@staticmethod
def setext_level_2(title: str) -> str:
"""Return a setext level 1 header.
1. Using the default generated link based on the text:
>>> header_link = Header.header_anchor("Section 1")
>>> print(header_link)
[Section 1](#section-1)
:param str title: text title.
:return: a header titlte of form: ``'\\n' + title +'\\n------------\\n'``.
:rtype: str
"""

return "\n" + title + "\n" + "".join(["-" for _ in title]) + "\n"

@staticmethod
def header_anchor(text: str, link: str = "") -> str:
"""Creates an internal link of a defined Header level 1 or level 2 in the markdown file.
2. Providing a custom link:
Giving a text string an text link you can create an internal link of already existing header. If the ``link``
string does not contain '#', it will creates an automatic link of the type ``#title-1``.
>>> header_link = Header.header_anchor("Section 1", "custom-link")
>>> print(header_link)
[Section 1](#custom-link)
:param text: it is the text that will be displayed.
:param link: it is the internal link.
:return: ``'[text](#link)'``
:type text: str
:type link: str
:rtype: string
3. Providing a link with an existing '#' symbol:
**Example:** [Title 1](#title-1)
>>> header_link = Header.header_anchor("Section 1", "#existing-link")
>>> print(header_link)
[Section 1](#existing-link)
"""
if link:
if link[0] != "#":
link = link.lower().replace(" ", "-")
else:
link = "#" + link
else:
if not link:
link = "#" + text.lower().replace(" ", "-")
elif link[0] != "#":
link = link.lower().replace(" ", "-")
else:
link = "#" + link

return "[" + text + "](" + link + ")"

Expand All @@ -180,39 +134,26 @@ def choose_header(
:param header_id: ID of the header for extended Markdown syntax
:return:
"""
if style.lower() == "atx":
if level == 1:
return Header.atx_level_1(title, header_id)
elif level == 2:
return Header.atx_level_2(title, header_id)
elif level == 3:
return Header.atx_level_3(title, header_id)
elif level == 4:
return Header.atx_level_4(title, header_id)
elif level == 5:
return Header.atx_level_5(title, header_id)
elif level == 6:
return Header.atx_level_6(title, header_id)
else:
raise ValueError(
"For 'atx' style, level's expected value: 1, 2, 3, 4, 5 or 6, but level = "
+ str(level)
)
elif style.lower() == "setext":
if level == 1:
return Header.setext_level_1(title)
elif level == 2:
return Header.setext_level_2(title)
else:
raise ValueError(
"For 'setext' style, level's expected value: 1, 2, 3, 4, 5 or 6, but level = "
+ str(level)
)
warnings.warn(
"This method is deprecated. Use the Header class instead, `str(Header())`, this method will be removed on 3.0.0 version",
DeprecationWarning,
stacklevel=2,
)
return str(Header(level, title, HeaderStyle[style.upper()], header_id))

def _new(self, level: int, title: str, style: HeaderStyle = HeaderStyle.ATX, header_id: str = None) -> str:
if style == HeaderStyle.ATX:
return Header.atx(AtxHeaderLevel(level), title, header_id)
elif style == HeaderStyle.SETEXT:
return Header.setext(SetextHeaderLevel(level), title)
else:
raise ValueError(
"style's expected value: 'atx' or 'setext', but style = "
+ style.lower()
)
raise ValueError("style's expected value: 'HeaderStyle.ATX' or 'HeaderStyle.SETEXT'")

@staticmethod
def _get_header_id(header_id: str = None) -> str:
if header_id:
return " {#" + header_id + "}"
return ""


if __name__ == "__main__":
Expand Down

0 comments on commit a4f2085

Please sign in to comment.