From 39feb5d3ebc8b5d104505338a6055eb22e5a6713 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 6 Aug 2026 14:33:30 +0530 Subject: [PATCH] Script to autoformat all files --- autoformat | 121 +++++++++++++++++++++++++++++++++++++++++ kitty/shaders/slang.py | 56 +++++++++++-------- local-agent.md | 4 +- 3 files changed, 155 insertions(+), 26 deletions(-) create mode 100755 autoformat diff --git a/autoformat b/autoformat new file mode 100755 index 000000000..c136786d4 --- /dev/null +++ b/autoformat @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# License: GPLv3 Copyright: 2026, Kovid Goyal + + +import concurrent.futures +import hashlib +import json +import os +import subprocess +import sys +import tempfile +import threading + +base = os.path.dirname(os.path.abspath(__file__)) + +ruff = subprocess.Popen(['ruff', 'format'], cwd=base, stderr=subprocess.STDOUT, stdout=subprocess.PIPE) +go = subprocess.Popen('gofmt -s -l -w tools kittens'.split(), cwd=base, stderr=subprocess.STDOUT, stdout=subprocess.PIPE) +ruff_output = b'' +go_output = b'' + + +def wait_ruff() -> None: + global ruff_output + ruff_output = ruff.communicate()[0] + + +def wait_go() -> None: + global go_output + go_output = go.communicate()[0] + + +threading.Thread(target=wait_ruff).start() +threading.Thread(target=wait_go).start() + +clang_files = [] +for x in os.listdir(base): + if x in ('dist', 'build', 'bypy', '3rdparty') or x.startswith('.'): + continue + for root, dirnames, files in os.walk(os.path.join(base, x)): + for file in files: + if file.startswith('wayland-') and os.path.basename(root) == 'glfw': + continue + ext = os.path.splitext(file)[1] + if ext in ('.c', '.h', '.m'): + clang_files.append(os.path.join(root, file)) + +CACHE_DIR = os.path.join(base, '.cache', 'autoformat') +CACHE_FILE = os.path.join(CACHE_DIR, 'clang_format.json') + + +def file_hash(path: str) -> str: + h = hashlib.md5() + with open(path, 'rb') as f: + h.update(f.read()) + return h.hexdigest() + + +def load_cache() -> dict[str, str]: + try: + with open(CACHE_FILE) as f: + return json.load(f) + except (FileNotFoundError, json.JSONDecodeError): + return {} + + +def save_cache(cache: dict[str, str]) -> None: + os.makedirs(CACHE_DIR, exist_ok=True) + fd, tmp = tempfile.mkstemp(dir=CACHE_DIR) + try: + with os.fdopen(fd, 'w') as f: + json.dump(cache, f, indent=2) + os.replace(tmp, CACHE_FILE) + except Exception: + os.unlink(tmp) + raise + + +cache: dict[str, str] = load_cache() +cache_lock = threading.Lock() + + +def run_clang_format(file_path: str) -> tuple[bool, str, str]: + rel_path = os.path.relpath(file_path, base) + current_hash = file_hash(file_path) + + with cache_lock: + if cache.get(rel_path) == current_hash: + return True, '', '' + + cmd = ['clang-format', '-style=file', '-i', file_path] + result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) + if result.returncode != 0: + return False, file_path, result.stderr + + new_hash = file_hash(file_path) + with cache_lock: + cache[rel_path] = new_hash + + return True, '', '' + + +clang_failed = False +with concurrent.futures.ThreadPoolExecutor(max_workers=os.cpu_count()) as executor: + futures = {executor.submit(run_clang_format, f): f for f in clang_files} + for future in concurrent.futures.wait(futures)[0]: + success, file_path, error_msg = future.result() + if not success: + print(f'[FAILED] {file_path}\n{error_msg}', file=sys.stderr) + clang_failed = True + +save_cache(cache) + +ruff.wait() +go.wait() +if ruff.wait() != 0: + sys.stderr.buffer.write(ruff_output) + raise SystemExit('Formatting of Python code failed') +if go.wait() != 0: + sys.stderr.buffer.write(go_output) + raise SystemExit('Formatting of Go code failed') +raise SystemExit('Formatting of C files failed' if clang_failed else 0) diff --git a/kitty/shaders/slang.py b/kitty/shaders/slang.py index 2c5678a7c..b805e9e5d 100644 --- a/kitty/shaders/slang.py +++ b/kitty/shaders/slang.py @@ -1097,15 +1097,17 @@ def is_valid_slot(x: str) -> TypeGuard[Slot]: VALID_VAR_TYPES: frozenset[str] = frozenset({'uint', 'int', 'float', 'double', 'bool'}) -SHADER_ANIMATION_EVENTS: frozenset[str] = frozenset({ - 'pointer-left-button-press', - 'os-window-focus-in', - 'os-window-focus-out', - 'window-focus-in', - 'window-focus-out', - 'tab-change', - 'bell-in-window', -}) +SHADER_ANIMATION_EVENTS: frozenset[str] = frozenset( + { + 'pointer-left-button-press', + 'os-window-focus-in', + 'os-window-focus-out', + 'window-focus-in', + 'window-focus-out', + 'tab-change', + 'bell-in-window', + } +) def is_valid_animation_event(name: str) -> bool: @@ -1123,12 +1125,12 @@ def parse_animation_events(raw: str) -> tuple[str, ...]: def parse_css_animation_curve(spec: str) -> EasingFunction: _NAMED: dict[str, tuple[str, str]] = { 'ease-in-out': ('cubic-bezier', '0.42, 0, 0.58, 1'), - 'linear': ('cubic-bezier', '0, 0, 1, 1'), - 'ease': ('cubic-bezier', '0.25, 0.1, 0.25, 1'), - 'ease-out': ('cubic-bezier', '0, 0, 0.58, 1'), - 'ease-in': ('cubic-bezier', '0.42, 0, 1, 1'), - 'step-start': ('steps', '1, start'), - 'step-end': ('steps', '1, end'), + 'linear': ('cubic-bezier', '0, 0, 1, 1'), + 'ease': ('cubic-bezier', '0.25, 0.1, 0.25, 1'), + 'ease-out': ('cubic-bezier', '0, 0, 0.58, 1'), + 'ease-in': ('cubic-bezier', '0.42, 0, 1, 1'), + 'step-start': ('steps', '1, start'), + 'step-end': ('steps', '1, end'), } def make(func_name: str, params: str) -> EasingFunction: @@ -1191,11 +1193,11 @@ class Group(TypedDict): output_texture: NamedTexture shaders: tuple[str, ...] vars: dict[str, tuple[str, str]] - animation_start: tuple[str, ...] # empty = no animation - animation_curve: EasingFunction # parsed easing curve - animation_step: int # nanoseconds between animation samples - animation_end_events: tuple[str, ...] # events that stop the animation - animation_end_duration: int | None # nanoseconds; None = no time limit + animation_start: tuple[str, ...] # empty = no animation + animation_curve: EasingFunction # parsed easing curve + animation_step: int # nanoseconds between animation samples + animation_end_events: tuple[str, ...] # events that stop the animation + animation_end_duration: int | None # nanoseconds; None = no time limit class Pipeline(TypedDict): @@ -1224,10 +1226,16 @@ def parse_pipeline_definition(lines: Iterable[str], pipeline_name: str, pipeline def init_group(*shaders: str) -> Group: return { - 'viewport_pos': (0, 0), 'viewport_size': (1, 1), 'output_texture': NamedTexture.default, - 'shaders': shaders, 'vars': {}, - 'animation_start': (), 'animation_curve': EasingFunction(), - 'animation_step': ANIMATION_SAMPLE_WAIT, 'animation_end_events': (), 'animation_end_duration': None, + 'viewport_pos': (0, 0), + 'viewport_size': (1, 1), + 'output_texture': NamedTexture.default, + 'shaders': shaders, + 'vars': {}, + 'animation_start': (), + 'animation_curve': EasingFunction(), + 'animation_step': ANIMATION_SAMPLE_WAIT, + 'animation_end_events': (), + 'animation_end_duration': None, } for line in lines: diff --git a/local-agent.md b/local-agent.md index 00ef6b7ab..9bc71becc 100644 --- a/local-agent.md +++ b/local-agent.md @@ -3,6 +3,7 @@ copy_resource: fonts copy_resource: bypy/b/linux/64/pkg/slang add_to_path: bypy/b/linux/64/pkg/slang/bin prepend_to_path: kitty/launcher +pre_commit: ./autoformat # System Instructions & Project Context @@ -29,7 +30,6 @@ make debug Execute the following two commands to fix any formatting issues in your code: ``` ruff check --fix -git ls-files '*.go' | xargs gofmt -w -s -l ``` Run the following command to type check python files: @@ -80,7 +80,7 @@ using: ## Verification Pipeline Before declaring a task complete, you must follow this exact verification lifecycle: -1. Run the linting tools above to cleanup any formatting issues in your code +1. Run the linting tools above to cleanup any simple issues in your code 2. Run the local **Build Command** to guarantee zero compilation or compilation-stage type errors. 3. Run the local **Test Command** to run the full test suite 4. If errors occur, analyze the stdout logs completely before writing a fix. Do not guess.