diff --git a/.clang-format b/.clang-format index f6b8fdf..b4d7ff9 100644 --- a/.clang-format +++ b/.clang-format @@ -1,2 +1,9 @@ --- BasedOnStyle: LLVM + +# Include sorting +SortIncludes: true + +# Spacing +MaxEmptyLinesToKeep: 2 +SeparateDefinitionBlocks: Always diff --git a/.codespellignore b/.codespellignore new file mode 100644 index 0000000..c714049 --- /dev/null +++ b/.codespellignore @@ -0,0 +1,9 @@ +# Words that codespell should ignore +# Add common false positives here +inout +uart +dout +din +Tage +alle +Aktion diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 0000000..c64c8c2 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,32 @@ +{ + "default": true, + "MD013": { + "line_length": 300, + "tables": true, + "code_blocks": true + }, + "MD033": false, + "MD012": { + "maximum": 2 + }, + "MD026": { + "punctuation": ".,;:!" + }, + "MD024": { + "siblings_only": false + }, + "MD029": { + "style": "ordered" + }, + "MD046": { + "style": "fenced" + }, + "MD049": { + "style": "underscore" + }, + "MD050": { + "style": "asterisk" + }, + "MD052": false, + "MD053": false +} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..b413fa2 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,101 @@ +# yaml-language-server: $schema=https://json.schemastore.org/pre-commit-config.json + +# Global exclude pattern for build artifacts and dependencies +exclude: | + (?x)^( + build/| + managed_components/| + .*\.bin| + .*\.elf| + .*\.hex| + .*\.o| + flake.lock| + dependencies.lock| + assets/case/ + ) + +repos: + # General file checks + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + # Line ending checks + - id: end-of-file-fixer + - id: fix-byte-order-marker + - id: mixed-line-ending + args: [--fix=lf] + + # Whitespace & formatting + - id: trailing-whitespace + + # File format checks + - id: check-added-large-files + args: [--maxkb=1000] + - id: check-ast + - id: check-case-conflict + - id: check-docstring-first + - id: check-json + - id: check-merge-conflict + - id: detect-private-key + - id: debug-statements + + - repo: https://github.com/asottile/pyupgrade + rev: v3.21.2 + hooks: + - id: pyupgrade + args: [--py38-plus] + + # Spell checking + - repo: https://github.com/codespell-project/codespell + rev: v2.4.2 + hooks: + - id: codespell + args: [--ignore-words=.codespellignore] + + # YAML linting + - repo: https://github.com/adrienverge/yamllint + rev: v1.37.1 + hooks: + - id: yamllint + args: + [ + --strict, + -d, + "{extends: default, rules: {line-length: {max: 120}, document-start: disable}}", + ] + + # Markdown linting + - repo: https://github.com/DavidAnson/markdownlint-cli2 + rev: v0.17.1 + hooks: + - id: markdownlint-cli2 + + # Python formatting + - repo: https://github.com/psf/black + rev: 26.1.0 + hooks: + - id: black + language_version: python3 + args: [--quiet] + + # JavaScript/JSON/CSS/HTML/YAML formatting and C/C++ formatting + - repo: local + hooks: + - id: prettier + name: prettier + entry: prettier + language: system + types_or: [javascript, jsx, json, css, scss, html, yaml] + exclude: \.md$ + args: [--write, --ignore-unknown] + - id: clang-format + name: clang-format + entry: clang-format + language: system + types_or: [c, c++] + args: [-i] + - id: nixfmt + name: nixfmt + entry: nixfmt + language: system + types: [nix] diff --git a/README.md b/README.md index cfe1b81..00955ed 100644 --- a/README.md +++ b/README.md @@ -70,14 +70,14 @@ You have to short-circuit `R0` on the RS485 boards to enable the termination res ### Required tools - `ESP-IDF` (includes `idf.py`) -- `Python 3` - `invoke` (for project tasks) -- Optional but recommended formatters: - - `clang-format` - - `black` - - `prettier` - - `svgo` - - `nixfmt` + +- Optional but recommended for development: + - `pre-commit` (for code quality hooks) + - `clang-format` (C/C++) + - `prettier` (JavaScript/CSS/HTML/YAML) + - `svgo` (SVG optimization) + - `nixfmt` (Nix formatting) ### Environment setup @@ -101,6 +101,20 @@ invoke reset invoke config ``` +### Pre-commit hooks + +This project uses [pre-commit](https://pre-commit.com/) to automatically check code quality, formatting, and common mistakes before committing. + +**Setup:** + +```bash +# Install pre-commit hooks +pre-commit install + +# Optionally, run all hooks on all files +pre-commit run --all-files +``` + --- ## 📦 Case diff --git a/flake.nix b/flake.nix index 0791d12..4b7ff81 100644 --- a/flake.nix +++ b/flake.nix @@ -24,11 +24,11 @@ pkgs.python3 pkgs.python3Packages.invoke - # Formatting tools + pkgs.pre-commit pkgs.clang-tools - pkgs.nodePackages.prettier - pkgs.nodePackages.svgo - pkgs.black + pkgs.svgo + pkgs.prettier + pkgs.nixfmt ]; }; diff --git a/tasks.py b/tasks.py index d782c10..536824a 100644 --- a/tasks.py +++ b/tasks.py @@ -46,7 +46,14 @@ def clean(c): @task def config(c): """Open menuconfig to edit project settings""" - c.run("idf.py menuconfig --color-scheme=monochrome", pty=True) + is_windows = os.name == "nt" + if is_windows: + # Windows doesn't provide a POSIX pty, not sure how to open menuconfig interactive + print( + "Please run 'idf.py menuconfig' directly in the terminal to edit project settings. This option is not supported in the invoke task on Windows." + ) + else: + c.run("idf.py menuconfig --color-scheme=monochrome", pty=True) @task @@ -76,66 +83,18 @@ def reset(c): @task def format(c): - """Format all source files with clang-format, black, prettier, nixfmt, and svgo""" - missing_tools = [] - - print("Formatting C files...") - result = c.run( - "find main components -name '*.c' -o -name '*.h' | xargs clang-format -i", + """Format all source files using pre-commit hooks and optimize SVGs""" + print("\nOptimizing SVG files...") + c.run( + "find . -name '*.svg' -not -path './build/*' -not -path './managed_components/*' | xargs -r svgo --quiet --final-newline", warn=True, - hide=False, ) - if result.exited == 127 or (not result.ok and result.exited != 0): - # Check if tool exists - check = c.run("command -v clang-format", warn=True, hide=True) - if not check.ok: - missing_tools.append("clang-format") - - print("Formatting Python files...") - result = c.run("black tasks.py", warn=True, hide=False) - if result.exited == 127 or (not result.ok and result.exited != 0): - check = c.run("command -v black", warn=True, hide=True) - if not check.ok: - missing_tools.append("black") - - print("Formatting Nix files...") - result = c.run("nixfmt flake.nix", warn=True, hide=False) - if result.exited == 127 or (not result.ok and result.exited != 0): - check = c.run("command -v nixfmt", warn=True, hide=True) - if not check.ok: - missing_tools.append("nixfmt") - - print("Formatting SVG files...") - result = c.run( - "find . -name '*.svg' -not -path './build/*' -not -path './managed_components/*' | xargs -r svgo", - warn=True, - hide=False, - ) - if result.exited == 127 or (not result.ok and result.exited != 0): - check = c.run("command -v svgo", warn=True, hide=True) - if not check.ok: - missing_tools.append("svgo") - - print("Formatting other files...") - result = c.run( - "prettier --write '**/*.{js,json,yaml,yml,md,html,css}'", warn=True, hide=False - ) - if result.exited == 127 or (not result.ok and result.exited != 0): - check = c.run("command -v prettier", warn=True, hide=True) - if not check.ok: - missing_tools.append("prettier") - - if missing_tools: - print("\n" + "=" * 60) - print(f"❌ ERROR: Missing formatting tools: {', '.join(missing_tools)}") - print("=" * 60) - print("Please install them or reload the nix-shell.") - print("=" * 60 + "\n") - sys.exit(1) + is_windows = os.name == "nt" + if is_windows: + # Windows doesn't provide a POSIX pty + c.run("pre-commit run --all-files") else: - print("\n" + "=" * 60) - print("✅ All files formatted successfully!") - print("=" * 60 + "\n") + c.run("pre-commit run --all-files", pty=True) @task