From f187104136d32e7b3debfb0b0df104a6d6068f79 Mon Sep 17 00:00:00 2001 From: Yura Lazarev Date: Sun, 14 Jun 2026 23:18:57 +0200 Subject: [PATCH] chore: add treefmt formatting (nix fmt) and format the tree Wire treefmt via treefmt-nix: nixfmt, dhall format, purs-tidy (.tidyrc.json) and LuaFormatter for the FFI (.lua-format, kept over StyLua because it preserves the parentheses pslua's parser needs). `nix fmt` formats; the dev shell installs a content-based pre-commit hook and CI runs `nix fmt && git diff --exit-code` (content-based, since the in-place formatters bump mtime and would trip treefmt --fail-on-change). Lua lines budget 130 cols, matching the raised `luacheck --max-line-length`. The bulk of the diff is the first format pass. --- .github/workflows/ci.yml | 5 +++- .gitignore | 2 ++ .lua-format | 10 ++++++++ .tidyrc.json | 10 ++++++++ AGENTS.md | 16 ++++++++++--- flake.lock | 23 +++++++++++++++++- flake.nix | 43 ++++++++++++++++++++++++++++++---- src/Control/Alt.purs | 4 +++- src/Control/Comonad.purs | 3 ++- src/Control/Extend.lua | 4 +--- src/Control/Extend.purs | 18 ++++++++++---- src/Control/Lazy.purs | 2 +- src/Control/Plus.purs | 3 ++- src/Data/Monoid/Alternate.purs | 12 +++++++--- test/regression/extend.lua | 4 ++-- treefmt.nix | 43 ++++++++++++++++++++++++++++++++++ 16 files changed, 176 insertions(+), 26 deletions(-) create mode 100644 .lua-format create mode 100644 .tidyrc.json create mode 100644 treefmt.nix diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d624d2e..4dc2d20 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,4 +24,7 @@ jobs: run: if [ -f scripts/test ]; then nix develop -c bash ./scripts/test; fi - name: Luacheck - run: nix develop -c luacheck --quiet --std lua51 --no-unused-args src/ + run: nix develop -c luacheck --quiet --std lua51 --no-unused-args --max-line-length 130 src/ + + - name: Format check + run: nix fmt && git diff --exit-code diff --git a/.gitignore b/.gitignore index db67e9a..e070528 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /.* !/.gitignore !/.github/ +!/.tidyrc.json +!/.lua-format /output/ diff --git a/.lua-format b/.lua-format new file mode 100644 index 0000000..2945014 --- /dev/null +++ b/.lua-format @@ -0,0 +1,10 @@ +# LuaFormatter config for the hand-written FFI under src/. +# 2-space indent. Keep simple functions on one line; column_limit sits a few +# columns under luacheck's 130 limit because lua-format under-counts the leading +# indent and trailing comma, so this keeps every emitted line within 130. +indent_width: 2 +use_tab: false +column_limit: 126 +continuation_indent_width: 2 +keep_simple_function_one_line: true +keep_simple_control_block_one_line: true diff --git a/.tidyrc.json b/.tidyrc.json new file mode 100644 index 0000000..8636af8 --- /dev/null +++ b/.tidyrc.json @@ -0,0 +1,10 @@ +{ + "importSort": "source", + "importWrap": "source", + "indent": 2, + "operatorsFile": null, + "ribbon": 1, + "typeArrowPlacement": "first", + "unicode": "source", + "width": 80 +} diff --git a/AGENTS.md b/AGENTS.md index dde531a..a312901 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,11 +4,21 @@ A PureScript→Lua FFI fork in the [`purescript-lua`](https://github.com/purescr ## Commands -All commands run inside the nix dev shell: - - Build: `nix develop -c ./scripts/build` - Test (only if the fork has `scripts/test`): `nix develop -c bash ./scripts/test` -- Lint: `nix develop -c luacheck --quiet --std lua51 --no-unused-args src/` +- Lint: `nix develop -c luacheck --quiet --std lua51 --no-unused-args --max-line-length 130 src/` +- Format: `nix fmt` (check: `nix fmt && git diff --exit-code`) + +## Formatting + +`nix fmt` runs treefmt (`treefmt.nix`): nixfmt for Nix, `dhall format`, purs-tidy +for `*.purs` (config in `.tidyrc.json`), and LuaFormatter for the `*.lua` FFI +(config in `.lua-format`). LuaFormatter is used over StyLua because it keeps the +parentheses pslua's foreign-file parser requires. The Lua line budget is 130 +columns, matching the `luacheck --max-line-length` above. The check is +content-based (`nix fmt && git diff --exit-code`) rather than `treefmt --ci`, +since the in-place formatters bump mtime even when content is unchanged, which +trips treefmt's `--fail-on-change`. CI and the pre-commit hook use it. ## Lua 5.1 target diff --git a/flake.lock b/flake.lock index 6b6c417..c47b792 100644 --- a/flake.lock +++ b/flake.lock @@ -740,7 +740,8 @@ "flake-utils": "flake-utils", "nixpkgs": "nixpkgs", "pslua": "pslua", - "purescript-overlay": "purescript-overlay" + "purescript-overlay": "purescript-overlay", + "treefmt-nix": "treefmt-nix" } }, "stackage": { @@ -803,6 +804,26 @@ "repo": "default", "type": "github" } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1780220602, + "narHash": "sha256-eynAfOmbmxJnkp7YewvCEbShNnnYJ9gLLqkzsYtBPeM=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "db947814a175b7ca6ded66e21383d938df01c227", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 5198865..4d86c4b 100644 --- a/flake.nix +++ b/flake.nix @@ -9,16 +9,33 @@ inputs.nixpkgs.follows = "nixpkgs"; }; pslua.url = "github:purescript-lua/purescript-lua"; + treefmt-nix = { + url = "github:numtide/treefmt-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; - outputs = { self, nixpkgs, flake-utils, purescript-overlay, pslua }: - flake-utils.lib.eachDefaultSystem (system: + outputs = + { + self, + nixpkgs, + flake-utils, + purescript-overlay, + pslua, + treefmt-nix, + }: + flake-utils.lib.eachDefaultSystem ( + system: let pkgs = import nixpkgs { inherit system; overlays = [ purescript-overlay.overlays.default ]; }; - in { + treefmtEval = treefmt-nix.lib.evalModule pkgs ./treefmt.nix; + in + { + formatter = treefmtEval.config.build.wrapper; + checks.formatting = treefmtEval.config.build.check self; devShell = pkgs.mkShell { buildInputs = with pkgs; [ dhall @@ -31,8 +48,26 @@ spago-bin.spago-0_21_0 treefmt ]; + # Install a content-based pre-commit hook. It compares the working + # tree diff before and after `nix fmt`, so it only objects to changes + # the formatter itself introduces (not the developer's existing + # unstaged work) and is not fooled by formatters that only bump mtime. + # Rewritten each shell entry to stay in sync with this flake. + shellHook = '' + hook=.git/hooks/pre-commit + if [ -d .git ]; then + printf '%s\n' \ + '#!/usr/bin/env bash' \ + 'before=$(git diff)' \ + 'nix fmt >/dev/null 2>&1 || exit 0' \ + '[ "$before" = "$(git diff)" ] || { echo "nix fmt changed files; re-stage them, then commit." >&2; exit 1; }' \ + > "$hook" + chmod +x "$hook" + fi + ''; }; - }); + } + ); # --- Flake Local Nix Configuration ---------------------------- nixConfig = { diff --git a/src/Control/Alt.purs b/src/Control/Alt.purs index a813270..46799d4 100644 --- a/src/Control/Alt.purs +++ b/src/Control/Alt.purs @@ -1,5 +1,7 @@ module Control.Alt - ( class Alt, alt, (<|>) + ( class Alt + , alt + , (<|>) , module Data.Functor ) where diff --git a/src/Control/Comonad.purs b/src/Control/Comonad.purs index d471e2d..d2bd43f 100644 --- a/src/Control/Comonad.purs +++ b/src/Control/Comonad.purs @@ -1,5 +1,6 @@ module Control.Comonad - ( class Comonad, extract + ( class Comonad + , extract , module Control.Extend , module Data.Functor ) where diff --git a/src/Control/Extend.lua b/src/Control/Extend.lua index 8fa0a10..ccbdfc8 100644 --- a/src/Control/Extend.lua +++ b/src/Control/Extend.lua @@ -5,9 +5,7 @@ return { local result = {} for i = 1, len do local suffix = {} - for j = i, len do - suffix[j - i + 1] = xs[j] - end + for j = i, len do suffix[j - i + 1] = xs[j] end result[i] = f(suffix) end return result diff --git a/src/Control/Extend.purs b/src/Control/Extend.purs index 367ccd5..2aed78c 100644 --- a/src/Control/Extend.purs +++ b/src/Control/Extend.purs @@ -1,7 +1,13 @@ module Control.Extend - ( class Extend, extend, (<<=), extendFlipped, (=>>) - , composeCoKleisli, (=>=) - , composeCoKleisliFlipped, (=<=) + ( class Extend + , extend + , (<<=) + , extendFlipped + , (=>>) + , composeCoKleisli + , (=>=) + , composeCoKleisliFlipped + , (=<=) , duplicate , module Data.Functor ) where @@ -41,13 +47,15 @@ extendFlipped w f = f <<= w infixl 1 extendFlipped as =>> -- | Forwards co-Kleisli composition. -composeCoKleisli :: forall b a w c. Extend w => (w a -> b) -> (w b -> c) -> w a -> c +composeCoKleisli + :: forall b a w c. Extend w => (w a -> b) -> (w b -> c) -> w a -> c composeCoKleisli f g w = g (f <<= w) infixr 1 composeCoKleisli as =>= -- | Backwards co-Kleisli composition. -composeCoKleisliFlipped :: forall b a w c. Extend w => (w b -> c) -> (w a -> b) -> w a -> c +composeCoKleisliFlipped + :: forall b a w c. Extend w => (w b -> c) -> (w a -> b) -> w a -> c composeCoKleisliFlipped f g w = f (g <<= w) infixr 1 composeCoKleisliFlipped as =<= diff --git a/src/Control/Lazy.purs b/src/Control/Lazy.purs index 3434d09..de3eb2a 100644 --- a/src/Control/Lazy.purs +++ b/src/Control/Lazy.purs @@ -22,4 +22,4 @@ instance lazyUnit :: Lazy Unit where fix :: forall l. Lazy l => (l -> l) -> l fix f = go where - go = defer \_ -> f go + go = defer \_ -> f go diff --git a/src/Control/Plus.purs b/src/Control/Plus.purs index f8724ea..f7ffda8 100644 --- a/src/Control/Plus.purs +++ b/src/Control/Plus.purs @@ -1,5 +1,6 @@ module Control.Plus - ( class Plus, empty + ( class Plus + , empty , module Control.Alt , module Data.Functor ) where diff --git a/src/Data/Monoid/Alternate.purs b/src/Data/Monoid/Alternate.purs index 9a16f1e..a636381 100644 --- a/src/Data/Monoid/Alternate.purs +++ b/src/Data/Monoid/Alternate.purs @@ -28,19 +28,25 @@ derive newtype instance ordAlternate :: Ord (f a) => Ord (Alternate f a) derive newtype instance ord1Alternate :: Ord1 f => Ord1 (Alternate f) -derive newtype instance boundedAlternate :: Bounded (f a) => Bounded (Alternate f a) +derive newtype instance boundedAlternate :: + Bounded (f a) => + Bounded (Alternate f a) derive newtype instance functorAlternate :: Functor f => Functor (Alternate f) derive newtype instance applyAlternate :: Apply f => Apply (Alternate f) -derive newtype instance applicativeAlternate :: Applicative f => Applicative (Alternate f) +derive newtype instance applicativeAlternate :: + Applicative f => + Applicative (Alternate f) derive newtype instance altAlternate :: Alt f => Alt (Alternate f) derive newtype instance plusAlternate :: Plus f => Plus (Alternate f) -derive newtype instance alternativeAlternate :: Alternative f => Alternative (Alternate f) +derive newtype instance alternativeAlternate :: + Alternative f => + Alternative (Alternate f) derive newtype instance bindAlternate :: Bind f => Bind (Alternate f) diff --git a/test/regression/extend.lua b/test/regression/extend.lua index cface42..3e982c8 100644 --- a/test/regression/extend.lua +++ b/test/regression/extend.lua @@ -6,8 +6,8 @@ local M = assert(dofile("src/Control/Extend.lua")) -- arrayExtend f xs : position i (1-based) receives f(xs[i..#xs]). -local lengths = M.arrayExtend(function(suffix) return #suffix end)({ 10, 20, 30 }) +local lengths = M.arrayExtend(function(suffix) return #suffix end)({10, 20, 30}) assert(lengths[1] == 3 and lengths[2] == 2 and lengths[3] == 1, - "arrayExtend suffix lengths wrong: " .. table.concat(lengths, ",")) + "arrayExtend suffix lengths wrong: " .. table.concat(lengths, ",")) print("OK arrayExtend applies f to each array suffix") diff --git a/treefmt.nix b/treefmt.nix new file mode 100644 index 0000000..0f57573 --- /dev/null +++ b/treefmt.nix @@ -0,0 +1,43 @@ +{ pkgs, ... }: +{ + projectRootFile = "flake.nix"; + + # Nix — RFC 166 formatter. + programs.nixfmt.enable = true; + + # Dhall — spago.dhall / packages.dhall layout. + programs.dhall.enable = true; + + # PureScript — purs-tidy is not a first-class treefmt program, so wire it via + # the generic mechanism. It picks up `.tidyrc.json` from the project root. + settings.formatter.purs-tidy = { + command = "${pkgs.purs-tidy}/bin/purs-tidy"; + options = [ "format-in-place" ]; + includes = [ "*.purs" ]; + }; + + # Lua FFI — LuaFormatter keeps the parentheses pslua's foreign-file parser + # requires (unlike StyLua, which strips them). Config in `.lua-format`. + settings.formatter.lua-format = { + command = "${pkgs.luaformatter}/bin/lua-format"; + options = [ + "-i" + "-c" + ".lua-format" + ]; + includes = [ "*.lua" ]; + }; + + # Never format generated output or vendored trees. + settings.global.excludes = [ + "dist/*" + "output/*" + ".spago/*" + "node_modules/*" + "*.lock" + "flake.lock" + "spago.lock" + ".tidyrc.json" + ".lua-format" + ]; +}