Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,6 @@ tmp/
# Temporary files
*.tmp
*.bak

# Nix build result
/result
4 changes: 3 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ We follow a strict PR-based workflow. All changes go through:
git clone https://github.com/bug-ops/helix-trainer.git
cd helix-trainer

# Enter the dev shell (provides Rust 1.89, cargo-watch, cargo-deny, and native deps)
nix develop

Comment on lines +22 to +24
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The quick start now suggests nix develop provides the dev tooling, but later steps require cargo +nightly fmt (rustup toolchain selector). The Nix dev shell currently pins a stable toolchain, so contributors following these instructions inside nix develop may be unable to run the formatting step. Either include a nightly toolchain/rustfmt in the dev shell or adjust the docs for Nix users.

Copilot uses AI. Check for mistakes.
# Create feature branch
git checkout -b feature/amazing-feature
```

## Pre-Commit Checks

> [!IMPORTANT]
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,24 @@ cd helix-trainer-*/
> [!TIP]
> Verify checksums with `sha256sum -c helix-trainer-*.sha256`

### Nix

Run directly without installing:

```bash
nix run github:bug-ops/helix-trainer
```

Or add to your NixOS/home-manager configuration:

```nix
# flake.nix
inputs.helix-trainer.url = "github:bug-ops/helix-trainer";

# configuration.nix or home.nix
environment.systemPackages = [ inputs.helix-trainer.packages.${system}.default ];
Comment on lines +112 to +113
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The home-manager example uses environment.systemPackages, which is a NixOS option and won’t work in home.nix. Also ${system} isn’t defined in typical module configs; use ${pkgs.system} (or pkgs.system) and show the correct option for home-manager (e.g., home.packages).

Suggested change
# configuration.nix or home.nix
environment.systemPackages = [ inputs.helix-trainer.packages.${system}.default ];
# configuration.nix (NixOS)
environment.systemPackages = [ inputs.helix-trainer.packages.${pkgs.system}.default ];
# home.nix (home-manager)
home.packages = [ inputs.helix-trainer.packages.${pkgs.system}.default ];

Copilot uses AI. Check for mistakes.
```

### Build from source

> [!WARNING]
Expand Down
116 changes: 116 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

141 changes: 141 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
{
description = "Interactive trainer for learning Helix editor keybindings";

inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
crane.url = "github:ipetkov/crane";
helix-src = {
url = "github:helix-editor/helix/25.07.1";
flake = false;
};
};

outputs = inputs:
inputs.flake-parts.lib.mkFlake {inherit inputs;} {
systems = ["x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin"];

perSystem = {
self',
pkgs,
lib,
system,
...
}: let
rustToolchain = pkgs.rust-bin.stable."1.89.0".default;
craneLib = (inputs.crane.mkLib pkgs).overrideToolchain rustToolchain;

nativeBuildInputs = with pkgs; [
pkg-config
rustToolchain
];

buildInputs = with pkgs;
[
oniguruma # syntect regex-onig
]
++ lib.optionals stdenv.isLinux [
alsa-lib # rodio audio playback
]
++ lib.optionals stdenv.isDarwin [
darwin.apple_sdk.frameworks.AudioUnit
darwin.apple_sdk.frameworks.CoreAudio
darwin.apple_sdk.frameworks.CoreFoundation
];

# Patch the vendored deps to include languages.toml that
# helix-loader expects via include_bytes!("../../languages.toml")
vendorDir = craneLib.vendorCargoDeps {src = ./.;};
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vendorCargoDeps is using src = ./.; (unfiltered), which will make Nix rebuild vendored deps when unrelated working-tree artifacts appear/change (notably the result symlink that this PR now ignores in git). Consider reusing the same filtered src you define below (or a cleanSource helper) so builds are stable and don’t depend on local artifacts.

Suggested change
vendorDir = craneLib.vendorCargoDeps {src = ./.;};
vendorDir = craneLib.vendorCargoDeps { src = src; };

Copilot uses AI. Check for mistakes.
patchedVendorDir = pkgs.runCommand "patched-vendor-deps" {} ''
cp -rL ${vendorDir} $out
chmod -R +w $out
for loader_dir in $out/*/helix-loader-*; do
if [ -d "$loader_dir" ]; then
cp ${inputs.helix-src}/languages.toml "$(dirname "$loader_dir")/languages.toml"
fi
done
substituteInPlace $out/config.toml \
--replace-fail "${vendorDir}" "$out"
'';

src = let
# Start with crane's standard Rust source filter
cargoFilter = craneLib.filterCargoSources;
# Also include non-Rust assets embedded via include_bytes!/include_str!
assetsFilter = path: _type: let
relPath = lib.removePrefix (toString ./. + "/") (toString path);
in
lib.hasPrefix "assets/" relPath
|| lib.hasPrefix "quests/" relPath
|| lib.hasPrefix "scenarios/" relPath
|| lib.hasPrefix "locales/" relPath;
in
lib.cleanSourceWith {
src = ./.;
filter = path: type: (cargoFilter path type) || (assetsFilter path type);
};

commonArgs = {
inherit src;
strictDeps = true;
cargoVendorDir = patchedVendorDir;
inherit nativeBuildInputs buildInputs;
RUSTONIG_SYSTEM_LIBONIG = "1";
};

cargoArtifacts = craneLib.buildDepsOnly commonArgs;
in {
_module.args.pkgs = import inputs.nixpkgs {
inherit system;
overlays = [(import inputs.rust-overlay)];
};

packages = {
helix-trainer = craneLib.buildPackage (commonArgs
// {
inherit cargoArtifacts;
# Some tests require HOME/filesystem access unavailable in the sandbox
doCheck = false;
meta = {
description = "Interactive trainer for learning Helix editor keybindings";
homepage = "https://github.com/bug-ops/helix-trainer";
license = lib.licenses.mit;
mainProgram = "helix-trainer";
};
});
default = self'.packages.helix-trainer;
};

checks = {
inherit (self'.packages) helix-trainer;

clippy = craneLib.cargoClippy (commonArgs
// {
inherit cargoArtifacts;
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Nix clippy check doesn’t enable all Cargo features (--all-features), so it can miss warnings that CI currently catches. Align this with the repository’s clippy invocation used in CI to avoid drift.

Suggested change
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
cargoClippyExtraArgs = "--all-features --all-targets -- --deny warnings";

Copilot uses AI. Check for mistakes.
});

fmt = craneLib.cargoFmt {
inherit src;
};
};

devShells.default = craneLib.devShell {
checks = self'.checks;

packages = with pkgs; [
cargo-watch
cargo-deny
cargo-nextest
];

inherit buildInputs;
RUSTONIG_SYSTEM_LIBONIG = "1";
};
};
};
}