* Set command palette caret tint to white * Add command palette window actions and shortcut sync
190 lines
5.9 KiB
Python
190 lines
5.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Lint test to catch SwiftUI patterns that cause performance issues.
|
|
|
|
This test checks for:
|
|
1. Text(_:style:) with auto-updating date styles (.time, .timer, .relative)
|
|
These cause continuous view updates and can lead to high CPU usage.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import List, Tuple
|
|
|
|
|
|
def get_repo_root():
|
|
"""Get the repository root directory."""
|
|
# Try git first
|
|
result = subprocess.run(
|
|
["git", "rev-parse", "--show-toplevel"],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
if result.returncode == 0:
|
|
return Path(result.stdout.strip())
|
|
|
|
# Fall back to finding GhosttyTabs directory
|
|
cwd = Path.cwd()
|
|
if cwd.name == "GhosttyTabs" or (cwd / "Sources").exists():
|
|
return cwd
|
|
if (cwd.parent / "GhosttyTabs").exists():
|
|
return cwd.parent / "GhosttyTabs"
|
|
|
|
# Last resort: use current directory
|
|
return cwd
|
|
|
|
|
|
def find_swift_files(repo_root: Path) -> List[Path]:
|
|
"""Find all Swift files in Sources directory (excluding vendored code)."""
|
|
sources_dir = repo_root / "Sources"
|
|
if not sources_dir.exists():
|
|
return []
|
|
return list(sources_dir.rglob("*.swift"))
|
|
|
|
|
|
def check_autoupdating_text_styles(files: List[Path]) -> List[Tuple[Path, int, str]]:
|
|
"""
|
|
Check for Text(_:style:) with auto-updating date styles.
|
|
|
|
These patterns cause continuous SwiftUI view updates:
|
|
- Text(date, style: .time) - updates every second/minute
|
|
- Text(date, style: .timer) - updates continuously
|
|
- Text(date, style: .relative) - updates periodically
|
|
- Text(date, style: .offset) - updates periodically
|
|
|
|
Instead, use static formatting:
|
|
- Text(date.formatted(date: .omitted, time: .shortened))
|
|
"""
|
|
violations = []
|
|
|
|
# Patterns that indicate auto-updating Text with Date
|
|
# The key patterns are: Text(something, style: .time/timer/relative/offset)
|
|
problematic_patterns = [
|
|
"style: .time",
|
|
"style: .timer",
|
|
"style: .relative",
|
|
"style: .offset",
|
|
"style:.time",
|
|
"style:.timer",
|
|
"style:.relative",
|
|
"style:.offset",
|
|
]
|
|
|
|
for file_path in files:
|
|
try:
|
|
content = file_path.read_text()
|
|
lines = content.split('\n')
|
|
|
|
for line_num, line in enumerate(lines, start=1):
|
|
# Skip comments
|
|
stripped = line.strip()
|
|
if stripped.startswith("//"):
|
|
continue
|
|
|
|
for pattern in problematic_patterns:
|
|
if pattern in line:
|
|
violations.append((file_path, line_num, line.strip()))
|
|
break
|
|
except Exception as e:
|
|
print(f"Warning: Could not read {file_path}: {e}", file=sys.stderr)
|
|
|
|
return violations
|
|
|
|
|
|
def check_command_palette_caret_tint(repo_root: Path) -> List[str]:
|
|
"""Ensure command palette text inputs keep a white caret tint."""
|
|
content_view = repo_root / "Sources" / "ContentView.swift"
|
|
if not content_view.exists():
|
|
return [f"Missing expected file: {content_view}"]
|
|
|
|
try:
|
|
content = content_view.read_text()
|
|
except Exception as e:
|
|
return [f"Could not read {content_view}: {e}"]
|
|
|
|
checks = [
|
|
(
|
|
"search input",
|
|
r"TextField\(commandPaletteSearchPlaceholder, text: \$commandPaletteQuery\)(?P<body>.*?)"
|
|
r"\.focused\(\$isCommandPaletteSearchFocused\)",
|
|
),
|
|
(
|
|
"rename input",
|
|
r"TextField\(target\.placeholder, text: \$commandPaletteRenameDraft\)(?P<body>.*?)"
|
|
r"\.focused\(\$isCommandPaletteRenameFocused\)",
|
|
),
|
|
]
|
|
|
|
violations: List[str] = []
|
|
for label, pattern in checks:
|
|
match = re.search(pattern, content, flags=re.DOTALL)
|
|
if not match:
|
|
violations.append(
|
|
f"Could not locate command palette {label} TextField block in Sources/ContentView.swift"
|
|
)
|
|
continue
|
|
|
|
body = match.group("body")
|
|
if ".tint(.white)" not in body:
|
|
violations.append(
|
|
f"Command palette {label} TextField must use `.tint(.white)` in Sources/ContentView.swift"
|
|
)
|
|
|
|
return violations
|
|
|
|
|
|
def main():
|
|
"""Run the lint checks."""
|
|
repo_root = get_repo_root()
|
|
swift_files = find_swift_files(repo_root)
|
|
|
|
print(f"Checking {len(swift_files)} Swift files for performance issues...")
|
|
|
|
# Check for auto-updating Text styles
|
|
style_violations = check_autoupdating_text_styles(swift_files)
|
|
tint_violations = check_command_palette_caret_tint(repo_root)
|
|
has_failures = False
|
|
|
|
if style_violations:
|
|
has_failures = True
|
|
print("\n❌ LINT FAILURES: Auto-updating Text styles found")
|
|
print("=" * 60)
|
|
print("These patterns cause continuous SwiftUI view updates and high CPU usage:")
|
|
print()
|
|
|
|
for file_path, line_num, line in style_violations:
|
|
rel_path = file_path.relative_to(repo_root)
|
|
print(f" {rel_path}:{line_num}")
|
|
print(f" {line}")
|
|
print()
|
|
|
|
print("FIX: Replace with static formatting:")
|
|
print(" Instead of: Text(date, style: .time)")
|
|
print(" Use: Text(date.formatted(date: .omitted, time: .shortened))")
|
|
print()
|
|
|
|
if tint_violations:
|
|
has_failures = True
|
|
print("\n❌ LINT FAILURES: Command palette caret tint drifted")
|
|
print("=" * 60)
|
|
print("The command palette search and rename text fields must keep a white caret:")
|
|
print()
|
|
for message in tint_violations:
|
|
print(f" {message}")
|
|
print()
|
|
print("FIX: Set command palette TextField tint modifiers to `.white`.")
|
|
print()
|
|
|
|
if has_failures:
|
|
return 1
|
|
|
|
print("✅ No linted SwiftUI pattern regressions found")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|