feat: add import checking to format script

This commit is contained in:
2 * r + 2 * t 2026-03-22 02:21:53 +11:00
parent 6ff823408d
commit 71fc17735d

View file

@ -50,12 +50,89 @@ SECTION_NAMES = {
} }
RULE_COLOURS = { RULE_COLOURS = {
"import-order": GREEN,
"section-order": YELLOW, "section-order": YELLOW,
"missing-section-separator": CYAN, "missing-section-separator": CYAN,
"blank-after-open-brace": MAGENTA, "blank-after-open-brace": MAGENTA,
"blank-before-close-brace": MAGENTA, "blank-before-close-brace": MAGENTA,
} }
IMPORT_RE = re.compile(r"^import\s+(\S+)")
def import_group(module: str) -> tuple[int, int] | None:
"""Return (group, depth) for a module import, or None to skip."""
if module.startswith('"'):
return None
depth = module.count(".") + 1
if module == "QtQuick" or module.startswith("QtQuick."):
return (1, depth)
if module.startswith("Qt"):
return (2, depth)
if module == "Quickshell" or module.startswith("Quickshell."):
return (3, depth)
if module == "M3Shapes":
return (4, depth)
if module == "Caelestia" or module.startswith("Caelestia."):
return (5, depth)
if module == "qs.components" or module.startswith("qs.components."):
return (6, depth)
if module == "qs.services":
return (7, depth)
if module == "qs.config":
return (8, depth)
if module == "qs.utils":
return (9, depth)
if module == "qs.modules" or module.startswith("qs.modules."):
return (10, depth)
return None
def check_imports(filepath: Path, lines: list[str], rel: str) -> list[Violation]:
"""Check that module imports are in the required order."""
violations = []
imports: list[tuple[int, str, int, int]] = [] # (lineno, module, group, depth)
for i, line in enumerate(lines):
stripped = line.strip()
if not stripped or stripped.startswith("//") or stripped.startswith("pragma "):
continue
m = IMPORT_RE.match(stripped)
if m:
module = m.group(1)
result = import_group(module)
if result is not None:
group, depth = result
imports.append((i + 1, module, group, depth))
continue
break # end of import block
for j in range(1, len(imports)):
prev_lineno, prev_mod, prev_group, prev_depth = imports[j - 1]
curr_lineno, curr_mod, curr_group, curr_depth = imports[j]
if curr_group < prev_group:
violations.append(
Violation(
rel,
curr_lineno,
"import-order",
f"'{curr_mod}' should appear before '{prev_mod}'",
)
)
elif curr_group == prev_group and curr_depth < prev_depth:
violations.append(
Violation(
rel,
curr_lineno,
"import-order",
f"'{curr_mod}' should appear before '{prev_mod}' (less nested first)",
)
)
return violations
# Regexes # Regexes
PROPERTY_DECL_RE = re.compile(r"^(?:required\s+|readonly\s+|default\s+)*property\s") PROPERTY_DECL_RE = re.compile(r"^(?:required\s+|readonly\s+|default\s+)*property\s")
SIGNAL_RE = re.compile(r"^signal\s") SIGNAL_RE = re.compile(r"^signal\s")
@ -139,6 +216,8 @@ def check_file(filepath: Path) -> list[Violation]:
except (OSError, UnicodeDecodeError): except (OSError, UnicodeDecodeError):
return violations return violations
violations.extend(check_imports(filepath, lines, rel))
scopes: dict[str, ScopeTracker] = {} # indent -> tracker scopes: dict[str, ScopeTracker] = {} # indent -> tracker
in_block_comment = False in_block_comment = False
func_skip_depth = 0 # brace depth for skipping function bodies only func_skip_depth = 0 # brace depth for skipping function bodies only