chore: format python codeicons

This commit is contained in:
2026-08-31 22:44:57 +02:00
committed by AramJonghu
co-authored by AramJonghu
parent cef91caebc
commit 51dc0fc026
+6 -76
View File
@@ -8,10 +8,6 @@ from urllib.error import HTTPError
from urllib.request import urlopen from urllib.request import urlopen
from xml.etree import ElementTree as ET from xml.etree import ElementTree as ET
# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
DEVICON_VERSION = "v2.17.0" DEVICON_VERSION = "v2.17.0"
ICONS = { ICONS = {
@@ -77,64 +73,40 @@ ICONS = {
"graphql": "graphql", "graphql": "graphql",
} }
# Markdown / syntax-highlighter aliases -> Devicon icon name.
ALIASES = { ALIASES = {
"cpp": "cpp", "cpp": "cpp",
"cc": "cpp", "cc": "cpp",
"cxx": "cpp", "cxx": "cpp",
"cs": "csharp", "cs": "csharp",
"js": "javascript", "js": "javascript",
"jsx": "javascript", "jsx": "javascript",
"ts": "typescript", "ts": "typescript",
"tsx": "typescript", "tsx": "typescript",
"py": "python", "py": "python",
"sh": "bash", "sh": "bash",
"shell": "bash", "shell": "bash",
"ps1": "powershell", "ps1": "powershell",
"vb": "visualbasic", "vb": "visualbasic",
"objective-c": "objectivec", "objective-c": "objectivec",
"obj-c": "objectivec", "obj-c": "objectivec",
"groovyscript": "groovy", "groovyscript": "groovy",
"pascal": "delphi", "pascal": "delphi",
"coffee": "coffeescript", "coffee": "coffeescript",
"mysql": "sql", "mysql": "sql",
"postgres": "sql", "postgres": "sql",
"postgresql": "sql", "postgresql": "sql",
"sqlite": "sql", "sqlite": "sql",
"yml": "yaml", "yml": "yaml",
"htm": "html", "htm": "html",
"scss": "sass", "scss": "sass",
"md": "markdown", "md": "markdown",
"dockerfile": "docker", "dockerfile": "docker",
"docker-compose": "docker", "docker-compose": "docker",
"tex": "latex", "tex": "latex",
"gql": "graphql", "gql": "graphql",
} }
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
ICON_VARIANTS = ( ICON_VARIANTS = (
"plain", "plain",
"original", "original",
@@ -158,9 +130,7 @@ def fetch_svg(devicon_name: str) -> str:
if error.code != 404: if error.code != 404:
raise raise
raise RuntimeError( raise RuntimeError(f"No usable SVG variant found for '{devicon_name}'.")
f"No usable SVG variant found for '{devicon_name}'."
)
def element_to_path(element) -> str: def element_to_path(element) -> str:
@@ -219,14 +189,6 @@ def element_to_path(element) -> str:
return "" return ""
# ---------------------------------------------------------------------------
# SVG path data normalization
#
# Devicon path data crams arc flags against the following coordinate
# (e.g. "a28.78 28.78 0 00-2.65-7.58"), which Qt's PathSvg parser handles
# unreliably. Parse every path and re-emit it with canonical spacing.
# ---------------------------------------------------------------------------
PATH_COMMANDS = "MmLlHhVvCcSsQqTtAaZz" PATH_COMMANDS = "MmLlHhVvCcSsQqTtAaZz"
_NUMBER = re.compile(r"[+-]?(?:\d+\.\d*|\.\d+|\d+)(?:[eE][+-]?\d+)?") _NUMBER = re.compile(r"[+-]?(?:\d+\.\d*|\.\d+|\d+)(?:[eE][+-]?\d+)?")
@@ -242,7 +204,7 @@ def _read_number(data: str, i: int) -> tuple[str, int]:
match = _NUMBER.match(data, i) match = _NUMBER.match(data, i)
if not match: if not match:
raise ValueError( raise ValueError(
f"Expected number in path data at {i}: {data[i:i + 24]!r}" f"Expected number in path data at {i}: {data[i : i + 24]!r}"
) )
return match.group(0), match.end() return match.group(0), match.end()
@@ -278,7 +240,6 @@ def parse_path(data: str) -> list[tuple[str, list[str]]]:
x, i = _read_number(data, i) x, i = _read_number(data, i)
y, i = _read_number(data, i) y, i = _read_number(data, i)
commands.append((implicit, [x, y])) commands.append((implicit, [x, y]))
# Subsequent coordinate pairs after a moveto are linetos.
implicit = "L" if implicit == "M" else "l" implicit = "L" if implicit == "M" else "l"
elif implicit in "LlTt": elif implicit in "LlTt":
x, i = _read_number(data, i) x, i = _read_number(data, i)
@@ -291,14 +252,12 @@ def parse_path(data: str) -> list[tuple[str, list[str]]]:
y, i = _read_number(data, i) y, i = _read_number(data, i)
commands.append((implicit, [y])) commands.append((implicit, [y]))
elif implicit in "Cc": elif implicit in "Cc":
# Cubic bezier: x1 y1 x2 y2 x y
p = [] p = []
for _ in range(6): for _ in range(6):
value, i = _read_number(data, i) value, i = _read_number(data, i)
p.append(value) p.append(value)
commands.append((implicit, p)) commands.append((implicit, p))
elif implicit in "SsQq": elif implicit in "SsQq":
# Smooth cubic / quadratic: x1 y1 x y
p = [] p = []
for _ in range(4): for _ in range(4):
value, i = _read_number(data, i) value, i = _read_number(data, i)
@@ -308,8 +267,6 @@ def parse_path(data: str) -> list[tuple[str, list[str]]]:
rx, i = _read_number(data, i) rx, i = _read_number(data, i)
ry, i = _read_number(data, i) ry, i = _read_number(data, i)
rotation, i = _read_number(data, i) rotation, i = _read_number(data, i)
# Arc flags are single digits, possibly crammed against
# the following coordinate, so read them positionally.
i = _skip_separators(data, i) i = _skip_separators(data, i)
large_arc = data[i] large_arc = data[i]
i += 1 i += 1
@@ -351,7 +308,6 @@ def extract_paths(svg: str) -> str:
normalized = normalize_path(path) normalized = normalize_path(path)
# Guard against a tokenizer that changes the path it normalizes.
if parse_path(normalized) != parse_path(path): if parse_path(normalized) != parse_path(path):
raise RuntimeError( raise RuntimeError(
"Path normalization round-trip mismatch:\n" "Path normalization round-trip mismatch:\n"
@@ -363,12 +319,10 @@ def extract_paths(svg: str) -> str:
if not paths: if not paths:
raise RuntimeError("SVG contains no drawable shape elements") raise RuntimeError("SVG contains no drawable shape elements")
# Multiple path elements are valid SVG path data when concatenated.
return " ".join(paths) return " ".join(paths)
def qml_string(value: str) -> str: def qml_string(value: str) -> str:
# JSON string escaping is valid for the string syntax we need in QML.
return json.dumps(value, ensure_ascii=False) return json.dumps(value, ensure_ascii=False)
@@ -381,10 +335,6 @@ def qml_identifier(name: str) -> str:
return name return name
# ---------------------------------------------------------------------------
# Generation
# ---------------------------------------------------------------------------
def generate(output: Path) -> None: def generate(output: Path) -> None:
icons = {} icons = {}
@@ -405,10 +355,6 @@ Singleton {
""" """
) )
# -------------------------------------------------------------------
# Icon path properties
# -------------------------------------------------------------------
for icon, path in icons.items(): for icon, path in icons.items():
identifier = qml_identifier(icon) identifier = qml_identifier(icon)
@@ -419,10 +365,6 @@ Singleton {
file.write("\n") file.write("\n")
# -------------------------------------------------------------------
# Language -> icon lookup
# -------------------------------------------------------------------
file.write( file.write(
""" function path(language) { """ function path(language) {
const key = language.toLowerCase() const key = language.toLowerCase()
@@ -435,8 +377,7 @@ Singleton {
identifier = qml_identifier(icon) identifier = qml_identifier(icon)
file.write( file.write(
f' case "{icon}":\n' f' case "{icon}":\n return {identifier}\n'
f" return {identifier}\n"
) )
for alias, icon in ALIASES.items(): for alias, icon in ALIASES.items():
@@ -448,8 +389,7 @@ Singleton {
identifier = qml_identifier(icon) identifier = qml_identifier(icon)
file.write( file.write(
f' case "{alias}":\n' f' case "{alias}":\n return {identifier}\n'
f" return {identifier}\n"
) )
file.write( file.write(
@@ -463,8 +403,6 @@ Singleton {
""" """
) )
# Human-readable names can initially be handled here.
# Add/remove these as your codeblock UI evolves.
names = { names = {
"c": "C", "c": "C",
"cpp": "C++", "cpp": "C++",
@@ -535,18 +473,14 @@ Singleton {
name = names[icon] name = names[icon]
file.write( file.write(f' case "{icon}":\n return "{name}"\n')
f' case "{icon}":\n'
f' return "{name}"\n'
)
for alias, icon in ALIASES.items(): for alias, icon in ALIASES.items():
if icon not in names: if icon not in names:
continue continue
file.write( file.write(
f' case "{alias}":\n' f' case "{alias}":\n return "{names[icon]}"\n'
f' return "{names[icon]}"\n'
) )
file.write( file.write(
@@ -561,10 +495,6 @@ Singleton {
print(f"Generated {output}") print(f"Generated {output}")
# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------
if len(sys.argv) != 2: if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} OUTPUT.qml", file=sys.stderr) print(f"Usage: {sys.argv[0]} OUTPUT.qml", file=sys.stderr)
sys.exit(1) sys.exit(1)