11from __future__ import annotations
22
33import re
4+ from dataclasses import dataclass
45from pathlib import Path
56from typing import TYPE_CHECKING
67
@@ -81,6 +82,33 @@ def get_badge_order() -> list[Badge]:
8182 ]
8283
8384
85+ @dataclass
86+ class MarkdownH1Status :
87+ """A way of keeping track of whether we're in a block of H1 tags.
88+
89+ We don't want to add badges inside a block of H1 tags.
90+ """
91+
92+ h1_count : int = 0
93+ in_block : bool = False
94+
95+ def update_from_line (self , line : str ) -> None :
96+ self .h1_count += self ._count_h1_open_tags (line )
97+ self .in_block = self .h1_count > 0
98+ self .h1_count -= self ._count_h1_close_tags (line )
99+
100+ @staticmethod
101+ def _count_h1_open_tags (line : str ) -> int :
102+ h1_start_match = re .match (r"(<h1\s.*>)" , line )
103+ if h1_start_match is not None :
104+ return len (h1_start_match .groups ())
105+ return 0
106+
107+ @staticmethod
108+ def _count_h1_close_tags (line : str ) -> int :
109+ return line .count ("</h1>" )
110+
111+
84112def add_badge (badge : Badge ) -> None :
85113 add_readme ()
86114
@@ -91,27 +119,28 @@ def add_badge(badge: Badge) -> None:
91119 print (badge .markdown )
92120 return
93121
94- prerequisites : list [Badge ] = []
95- for _b in get_badge_order ():
96- if badge .equivalent_to (_b ):
97- break
98- prerequisites .append (_b )
99-
100- content = path .read_text (encoding = "utf-8" )
122+ try :
123+ content = path .read_text (encoding = "utf-8" )
124+ except UnicodeDecodeError :
125+ warn_print (
126+ "README file uses an unsupported encoding, printing badge markdown instead..."
127+ )
128+ print (badge .markdown )
129+ return
101130
102131 original_lines = content .splitlines ()
103132
133+ prerequisites = _get_prerequisites (badge )
134+
104135 have_added = False
105136 have_encountered_badge = False
106- html_h1_count = 0
137+ h1_status = MarkdownH1Status ()
107138 lines : list [str ] = []
108139 for original_line in original_lines :
109140 if is_badge (original_line ):
110141 have_encountered_badge = True
111142
112- html_h1_count += _count_h1_open_tags (original_line )
113- in_block = html_h1_count > 0
114- html_h1_count -= _count_h1_close_tags (original_line )
143+ h1_status .update_from_line (original_line )
115144
116145 original_badge = Badge (markdown = original_line )
117146
@@ -126,7 +155,7 @@ def add_badge(badge: Badge) -> None:
126155 not original_line_is_prerequisite
127156 and (not is_blank (original_line ) or have_encountered_badge )
128157 and not is_header (original_line )
129- and not in_block
158+ and not h1_status . in_block
130159 ):
131160 lines .append (badge .markdown )
132161 have_added = True
@@ -160,6 +189,20 @@ def add_badge(badge: Badge) -> None:
160189 path .write_text (output , encoding = "utf-8" )
161190
162191
192+ def _get_prerequisites (badge : Badge ) -> list [Badge ]:
193+ """Get the prerequisites for a badge.
194+
195+ We want to place the badges in a specific order, so we need to check if we've got
196+ past those prerequisites.
197+ """
198+ prerequisites : list [Badge ] = []
199+ for _b in get_badge_order ():
200+ if badge .equivalent_to (_b ):
201+ break
202+ prerequisites .append (_b )
203+ return prerequisites
204+
205+
163206def _get_markdown_readme_path () -> Path :
164207 path = get_readme_path ()
165208
@@ -196,17 +239,6 @@ def is_badge(line: str) -> bool:
196239 )
197240
198241
199- def _count_h1_open_tags (line : str ) -> int :
200- h1_start_match = re .match (r"(<h1\s.*>)" , line )
201- if h1_start_match is not None :
202- return len (h1_start_match .groups ())
203- return 0
204-
205-
206- def _count_h1_close_tags (line : str ) -> int :
207- return line .count ("</h1>" )
208-
209-
210242def remove_badge (badge : Badge ) -> None :
211243 path = Path .cwd () / "README.md"
212244
0 commit comments