diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..9a9a73c --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,194 @@ +# GitHub Copilot Instructions for np Repository + +## Repository Overview + +This repository provides Nix flake templates for setting up Research Development Environments (RDE). The primary template is located in `templates/rde/` and is designed for data science and research projects supporting R, Python, and Julia. + +## Project Structure + +- `flake.nix` - Root flake defining available templates +- `templates/rde/` - Main RDE template with Neovim-based development environment + - `flake.nix` - Template flake configuration with language support and tooling + - `flake.lock` - Locked dependencies for reproducibility + - `.envrc` - direnv configuration for automatic environment loading +- `.github/workflows/` - CI/CD workflows for testing and updates + - `check.yml` - Tests RDE template on Ubuntu + - `check_macos.yml` - Tests RDE template on macOS + - `update.yml` - Automated daily dependency updates + +## Key Technologies + +- **Nix Flakes**: Reproducible development environments +- **nixCats**: Custom Neovim configuration framework +- **direnv**: Automatic environment loading +- **Cachix**: Binary cache for faster builds (using `rde`, `rstats-on-nix`, and `nix-community` caches) + +## Language Support + +The RDE template supports multiple languages, controlled via `config.enabledLanguages` in `templates/rde/flake.nix`: + +- **R**: R wrapper, Quarto, air-formatter, language server, and custom R packages via overlays +- **Python**: Python 3, basedpyright LSP, UV package manager +- **Julia**: Julia REPL with Pluto.jl support + +## Development Commands + +The template provides project-specific commands (prefix with package name, default is `p`): + +- `p` - Launch Neovim +- `p-g` - Launch Neovide GUI +- `p-initProject` - Initialize project structure (data/, src/, docs/) +- `p-updateDeps` - Update all dependencies (flake inputs, R packages, Python packages) +- `p-r` - Launch R console +- `p-py` / `p-ipy` - Launch Python/IPython REPL +- `p-marimo` - Launch Marimo notebook +- `p-jl` - Launch Julia REPL +- `p-pluto` - Launch Pluto.jl notebook +- `p-devenv` - Devenv integration (when enabled) + +## Nix Flake Conventions + +### Configuration Structure + +The RDE template uses a centralized `config` object at the top of `flake.nix`: + +```nix +config = rec { + defaultPackageName = "p"; + enabledLanguages = { julia = false; python = false; r = true; }; + enabledPackages = { gitPlugins = enabledLanguages.r; devenv = false; }; + theme = { colorscheme = "kanagawa"; background = "dark"; }; +}; +``` + +### Overlays Pattern + +The template uses multiple overlays to extend nixpkgs: + +- `rOverlay` - Adds R packages via rix/rstats-on-nix +- `pythonOverlay` - Configures Python packages +- `rixOverlay` - Integrates R package snapshots from rstats-on-nix +- `projectScriptsOverlay` - Custom shell scripts for project management +- `extraPkgOverlay` - Additional theme and plugin configuration + +### Package Categories + +nixCats uses categories to organize functionality: + +- `lspsAndRuntimeDeps` - Language servers and runtime dependencies +- `startupPlugins` - Neovim plugins loaded at startup +- `optionalPlugins` - Plugins loaded on demand +- `environmentVariables` - Language-specific environment setup +- `extraWrapperArgs` - Additional wrapper arguments (e.g., unset PYTHONPATH for Python) + +## Testing & CI/CD + +### Local Testing + +```bash +# Build the RDE template +nix build ./templates/rde + +# Check the RDE template +nix flake check ./templates/rde + +# Enter development shell +nix develop ./templates/rde +``` + +### CI Workflows + +- **check.yml**: Runs on pushes to `templates/rde/flake.lock`, builds and checks the template on Ubuntu +- **check_macos.yml**: Tests on macOS when `update_rde` branch is pushed +- **update.yml**: Daily cron job that updates dependencies via `p-updateDeps` and creates PRs + +## Dependency Management + +### R Packages + +R packages are managed through rstats-on-nix pinned snapshots. The date is specified in the `rixpkgs.url` input: + +```nix +rixpkgs.url = "github:rstats-on-nix/nixpkgs/2025-12-15"; +``` + +Custom R packages can be added in `rOverlay` or via `r-packages.nix` file. + +### Python Packages + +Python packages use UV for management with nixpkgs Python as the interpreter: + +- Environment variables force UV to use nix Python: `UV_PYTHON_DOWNLOADS = "never"` and `UV_PYTHON = pkgs.python.interpreter` +- PYTHONPATH is explicitly unset via `extraWrapperArgs` + +### Flake Inputs + +Dependencies are tracked in `flake.lock`. Key inputs include: + +- `nixpkgs` - NixOS 25.11 +- `rixpkgs` - R package snapshots from rstats-on-nix +- `nixCats` - Custom Neovim configuration framework +- `fran` - Extra R packages overlay +- Plugin inputs for R.nvim ecosystem + +## Coding Style & Conventions + +1. **Nix Code**: + - Use `rec` for recursive attribute sets when needed + - Prefer `let...in` for local bindings + - Use `lib.optional` and `lib.optionalString` for conditional inclusion + - Keep configuration at the top of the file for easy customization + +2. **Shell Scripts** (in overlays): + - Always use `set -euo pipefail` for safety + - Provide user-friendly output with emojis and clear messages + - Check for existing files/directories before creating + +3. **Workflows**: + - Use `workflow_dispatch` for manual triggering + - Configure concurrency to cancel in-progress runs + - Use Cachix for binary caching with multiple caches + +## Common Patterns + +### Adding a New Language + +1. Add to `config.enabledLanguages` +2. Create overlay for language-specific packages +3. Add to `categoryDefinitions.lspsAndRuntimeDeps` +4. Add command aliases in `packageDefinitions.hosts` +5. Update `shellHook` with available commands + +### Adding Custom Scripts + +Add to `projectScriptsOverlay`: + +```nix +myScript = prev.writeShellScriptBin "myScript" '' + #!/usr/bin/env bash + set -euo pipefail + # script content +''; +``` + +### Plugin Integration + +For git-based plugins: +1. Add flake input with `flake = false` +2. Reference in nixCats inputs +3. Add to `startupPlugins` or `optionalPlugins` categories + +## Important Notes + +- The template creates a `.gitignore` that excludes data files by default +- R packages are installed to project-local `.Rlibs/` directory +- Python UV is configured to never download Python, always using nixpkgs version +- The template supports multiple platforms: x86_64-linux, aarch64-linux, aarch64-darwin +- Neovim is wrapped with language-specific environment variables and PATH additions + +## File Generation + +When asked to initialize projects or generate common files, follow the patterns in: +- `initProjectScript` for project structure +- `.gitignore` template for what to exclude +- `README.md` template for documentation structure diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index e228877..a231159 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -4,6 +4,17 @@ on: push: paths: - 'templates/rde/flake.lock' + - 'templates/rde/**/*.nix' + - 'templates/rde/**/*.sh' + - 'templates/rde/**/*.lua' + - '.github/workflows/check.yml' + pull_request: + paths: + - 'templates/rde/flake.lock' + - 'templates/rde/**/*.nix' + - 'templates/rde/**/*.sh' + - 'templates/rde/**/*.lua' + - '.github/workflows/check.yml' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -14,13 +25,199 @@ jobs: matrix: os: [ubuntu-latest] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: wimpysworld/nothing-but-nix@main with: hatchet-protocol: 'carve' - uses: cachix/install-nix-action@v31 with: github_access_token: ${{ secrets.GITHUB_TOKEN }} - - uses: DeterminateSystems/magic-nix-cache-action@main - - run: nix build ./templates/rde - - run: nix flake check ./templates/rde + - uses: cachix/cachix-action@v16 + with: + name: rde + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + extraPullNames: rstats-on-nix, nix-community +# - uses: DeterminateSystems/magic-nix-cache-action@main + + # Build and basic checks + - name: Build template + run: nix build ./templates/rde + - name: Run flake check + run: nix flake check ./templates/rde + + # Test development shell functionality + - name: Test dev shell enters successfully + run: | + cd templates/rde + nix develop --command bash -c "echo 'Dev shell works'" + + # Test R functionality (default enabled) + - name: Test R command is available + run: | + cd templates/rde + nix develop --command bash -c "which p-r" + + - name: Test R launches and runs basic command + run: | + cd templates/rde + nix develop --command bash -c "p-r --version" + + - name: Test R can execute simple code + run: | + cd templates/rde + nix develop --command bash -c "p-r -e 'print(1+1)'" + + - name: Test Quarto is available in R environment + run: | + cd templates/rde + nix develop --command bash -c "quarto --version" + + # Test Neovim functionality + - name: Test Neovim command is available + run: | + cd templates/rde + nix develop --command bash -c "which p" + + - name: Test Neovim launches in headless mode + run: | + cd templates/rde + nix develop --command bash -c "p --headless --version" + + # Test utility commands + - name: Test p-initProject command is available + run: | + cd templates/rde + nix develop --command bash -c "which p-initProject" + + - name: Test p-initProject creates project structure + run: | + TEST_DIR=$(mktemp -d) + cd "$TEST_DIR" + nix develop $GITHUB_WORKSPACE/templates/rde --command bash -c "p-initProject" + # Verify directories were created + test -d data/raw || exit 1 + test -d data/processed || exit 1 + test -d src || exit 1 + test -f README.md || exit 1 + test -f .gitignore || exit 1 + echo "Project structure created successfully" + # Cleanup + rm -rf "$TEST_DIR" + + - name: Test p-updateDeps command is available + run: | + cd templates/rde + nix develop --command bash -c "which p-updateDeps" + + # Test that package is built correctly + - name: Test default package can be run + run: | + cd templates/rde + nix run . -- --headless --version + + test-with-python: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: wimpysworld/nothing-but-nix@main + with: + hatchet-protocol: 'carve' + - uses: cachix/install-nix-action@v31 + with: + github_access_token: ${{ secrets.GITHUB_TOKEN }} + - uses: cachix/cachix-action@v16 + with: + name: rde + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + extraPullNames: rstats-on-nix, nix-community + + # Create a temporary directory with Python enabled + - name: Setup test directory with Python enabled + run: | + TEST_DIR=$(mktemp -d) + cp -r templates/rde/. "$TEST_DIR/" + cd "$TEST_DIR" + # Enable Python in the config (handle variations in whitespace) + sed -i 's/python[[:space:]]*=[[:space:]]*false;/python = true;/' flake.nix + # Verify the change was successful + grep -q "python[[:space:]]*=[[:space:]]*true;" flake.nix || { + echo "Error: Failed to enable Python in flake.nix" + exit 1 + } + echo "TEST_PYTHON_DIR=$TEST_DIR" >> $GITHUB_ENV + + - name: Build with Python enabled + run: | + cd "$TEST_PYTHON_DIR" + nix build + + - name: Test Python commands are available + run: | + cd "$TEST_PYTHON_DIR" + nix develop --command bash -c "which p-py && which p-ipy && which p-initPython" + + - name: Test Python launches + run: | + cd "$TEST_PYTHON_DIR" + nix develop --command bash -c "p-py --version" + + - name: Test Python can execute code + run: | + cd "$TEST_PYTHON_DIR" + nix develop --command bash -c "p-py -c 'print(1+1)'" + + - name: Test UV package manager is available + run: | + cd "$TEST_PYTHON_DIR" + nix develop --command bash -c "uv --version" + + test-with-julia: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: wimpysworld/nothing-but-nix@main + with: + hatchet-protocol: 'carve' + - uses: cachix/install-nix-action@v31 + with: + github_access_token: ${{ secrets.GITHUB_TOKEN }} + - uses: cachix/cachix-action@v16 + with: + name: rde + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + extraPullNames: rstats-on-nix, nix-community + + # Create a temporary directory with Julia enabled + - name: Setup test directory with Julia enabled + run: | + TEST_DIR=$(mktemp -d) + cp -r templates/rde/. "$TEST_DIR/" + cd "$TEST_DIR" + # Enable Julia in the config (handle variations in whitespace) + sed -i 's/julia[[:space:]]*=[[:space:]]*false;/julia = true;/' flake.nix + # Verify the change was successful + grep -q "julia[[:space:]]*=[[:space:]]*true;" flake.nix || { + echo "Error: Failed to enable Julia in flake.nix" + exit 1 + } + echo "TEST_JULIA_DIR=$TEST_DIR" >> $GITHUB_ENV + + - name: Build with Julia enabled + run: | + cd "$TEST_JULIA_DIR" + nix build + + - name: Test Julia commands are available + run: | + cd "$TEST_JULIA_DIR" + nix develop --command bash -c "which p-jl && which p-initJl" + + - name: Test Julia launches + run: | + cd "$TEST_JULIA_DIR" + nix develop --command bash -c "p-jl --version" + + - name: Test Julia can execute code + run: | + cd "$TEST_JULIA_DIR" + nix develop --command bash -c "p-jl -e 'println(1+1)'" diff --git a/.github/workflows/check_macos.yml b/.github/workflows/check_macos.yml index 958426d..06578c7 100644 --- a/.github/workflows/check_macos.yml +++ b/.github/workflows/check_macos.yml @@ -14,9 +14,14 @@ jobs: matrix: os: [macos-latest] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: cachix/install-nix-action@v31 with: github_access_token: ${{ secrets.GITHUB_TOKEN }} - - uses: DeterminateSystems/magic-nix-cache-action@main + - uses: cachix/cachix-action@v16 + with: + name: rde + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + extraPullNames: rstats-on-nix, nix-community - run: nix build ./templates/rde + - run: nix build ./templates/ed diff --git a/.github/workflows/flakehub-publish-rolling.yml b/.github/workflows/flakehub-publish-rolling.yml index 8403bbe..80ff807 100644 --- a/.github/workflows/flakehub-publish-rolling.yml +++ b/.github/workflows/flakehub-publish-rolling.yml @@ -10,7 +10,7 @@ jobs: id-token: "write" contents: "read" steps: - - uses: "actions/checkout@v5" + - uses: "actions/checkout@v6" with: persist-credentials: false - uses: "DeterminateSystems/determinate-nix-action@v3" diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index a72d5f3..891e992 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -3,23 +3,33 @@ name: update-flake-lock on: workflow_dispatch: # allows manual triggering schedule: - - cron: '0 3 * * *' + - cron: '0 6 * * *' jobs: lockfile: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v5 - - name: Install Determinate Nix - uses: DeterminateSystems/determinate-nix-action@v3 - - uses: DeterminateSystems/magic-nix-cache-action@main + uses: actions/checkout@v6 + - uses: wimpysworld/nothing-but-nix@main + with: + hatchet-protocol: 'carve' + - uses: cachix/install-nix-action@v31 + with: + github_access_token: ${{ secrets.GITHUB_TOKEN }} + - uses: cachix/cachix-action@v16 + with: + name: rde + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + extraPullNames: rstats-on-nix, nix-community + - run: cd templates/rde/ && nix develop -c p-updateDeps + - run: cd templates/ed/ && nix develop -c updateR - name: Update n flake.lock - uses: DeterminateSystems/update-flake-lock@v27 + uses: DeterminateSystems/update-flake-lock@v28 with: token: ${{ secrets.GH_TOKEN_FOR_UPDATES }} branch: update_rde - path-to-flake-dir: "templates/rde" + path-to-flake-dir: "templates/ed" pr-title: "Update RDE flake.lock" # Title of PR to be created pr-labels: | # Labels to be set on the PR dependencies diff --git a/README.md b/README.md new file mode 100644 index 0000000..bfa3c32 --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +# Nix Project Templates (np) + +A collection of Nix flake templates for reproducible development environments. + +## Templates + +### RDE (Research Development Environment) + +The default template for data science and research projects with support for R, Python, and Julia. + +**Quick start:** +```bash +nix flake init -t github:dwinkler1/np#rde +nix develop +``` + +**Features:** +- đŸ”Ŧ Multi-language support (R, Python, Julia) +- đŸ“Ļ Reproducible with Nix +- 🎨 Neovim-based IDE with LSP support +- 📊 Research-focused workflows +- 🔧 Modular and customizable + +See [templates/rde/README.md](templates/rde/README.md) for full documentation. + +## CI/CD + +All templates are automatically tested to ensure functionality: + +- **Build Tests**: Templates build successfully on Linux and macOS +- **Functionality Tests**: All commands and language support are verified +- **Configuration Tests**: Multiple configurations (R, Python, Julia) are tested +- **Automated Updates**: Dependencies are updated daily via automated PRs + +### CI Workflows + +- `.github/workflows/check.yml` - Comprehensive functionality tests (Ubuntu) +- `.github/workflows/check_macos.yml` - macOS compatibility tests +- `.github/workflows/update.yml` - Automated dependency updates + +## Usage + +1. **Initialize a new project:** + ```bash + nix flake init -t github:dwinkler1/np#rde + ``` + +2. **Enter development environment:** + ```bash + nix develop + # or with direnv + echo "use flake" > .envrc && direnv allow + ``` + +3. **Start working:** + ```bash + p-initProject # Create project structure + p # Launch Neovim + ``` + +## Contributing + +Contributions are welcome! Please ensure: +- All templates pass CI tests +- Documentation is updated for new features +- Code follows existing patterns + +## License + +See [LICENSE](LICENSE) file for details. diff --git a/flake.nix b/flake.nix index e2cb7cc..7b723a7 100644 --- a/flake.nix +++ b/flake.nix @@ -6,7 +6,11 @@ path = ./templates/rde; description = "Research Development Environment"; }; + ed = { + path = ./templates/ed; + description = "Simple nvim Environment"; + }; }; - defaultTemplate = self.templates.rde; + defaultTemplate = self.templates.ed; }; } diff --git a/templates/ed/.envrc b/templates/ed/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/templates/ed/.envrc @@ -0,0 +1 @@ +use flake diff --git a/templates/ed/.gitignore b/templates/ed/.gitignore new file mode 100644 index 0000000..52e8a70 --- /dev/null +++ b/templates/ed/.gitignore @@ -0,0 +1 @@ +.nvimcom diff --git a/templates/ed/flake.lock b/templates/ed/flake.lock new file mode 100644 index 0000000..5839626 --- /dev/null +++ b/templates/ed/flake.lock @@ -0,0 +1,148 @@ +{ + "nodes": { + "fran": { + "inputs": { + "nixpkgs": [ + "rixpkgs" + ] + }, + "locked": { + "lastModified": 1771133455, + "narHash": "sha256-DvHfkW3DctWV+YnigY4+5Stn9NgI38JqR2IScXDX8z8=", + "owner": "dwinkler1", + "repo": "fran", + "rev": "68b189ab981adae06590ee1929a70c8b86afd2b8", + "type": "github" + }, + "original": { + "owner": "dwinkler1", + "repo": "fran", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1771043024, + "narHash": "sha256-O1XDr7EWbRp+kHrNNgLWgIrB0/US5wvw9K6RERWAj6I=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3aadb7ca9eac2891d52a9dec199d9580a6e2bf44", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nvimConfig": { + "inputs": { + "fran": [ + "fran" + ], + "nixpkgs": [ + "nixpkgs" + ], + "plugins-cmp-pandoc-references": "plugins-cmp-pandoc-references", + "plugins-r": "plugins-r", + "rixpkgs": [ + "rixpkgs" + ], + "wrappers": "wrappers" + }, + "locked": { + "lastModified": 1770172480, + "narHash": "sha256-gem34C89cocBGuWLj7cdrAS5WkrOBp6V+ycQM/RhcCw=", + "owner": "dwinkler1", + "repo": "nvimConfig", + "rev": "8f5c05252e914cb3738039a7ec13eb7d5955227a", + "type": "github" + }, + "original": { + "owner": "dwinkler1", + "repo": "nvimConfig", + "type": "github" + } + }, + "plugins-cmp-pandoc-references": { + "flake": false, + "locked": { + "lastModified": 1743491695, + "narHash": "sha256-XsdneGNJzmRBggk8lz9JNDQYk7wbYfUAF2oZLXzFb9c=", + "owner": "jmbuhr", + "repo": "cmp-pandoc-references", + "rev": "130eae4f75029d6495808e0ea4b769fa1ce4c9ac", + "type": "github" + }, + "original": { + "owner": "jmbuhr", + "repo": "cmp-pandoc-references", + "type": "github" + } + }, + "plugins-r": { + "flake": false, + "locked": { + "lastModified": 1769736135, + "narHash": "sha256-T4QgcBL+LCXvrEiRE2JW4jtUKl8DKzFHk8czGUO1jgY=", + "owner": "R-nvim", + "repo": "R.nvim", + "rev": "2701ec64f5485e17c0e057081a9ae2058d776464", + "type": "github" + }, + "original": { + "owner": "R-nvim", + "repo": "R.nvim", + "type": "github" + } + }, + "rixpkgs": { + "locked": { + "lastModified": 1770035835, + "narHash": "sha256-Pi8f3LPTFmG7SZeAJEAamOWly1SZhEofT92uBqS4atY=", + "owner": "rstats-on-nix", + "repo": "nixpkgs", + "rev": "0d3fe7afce51d2126fdccf0b717d8048b90e4781", + "type": "github" + }, + "original": { + "owner": "rstats-on-nix", + "ref": "2026-02-02", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "fran": "fran", + "nixpkgs": "nixpkgs", + "nvimConfig": "nvimConfig", + "rixpkgs": "rixpkgs" + } + }, + "wrappers": { + "inputs": { + "nixpkgs": [ + "nvimConfig", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1769745458, + "narHash": "sha256-Fq6THKEiAKx+wWQ4wTg9HvOAyD6i8JkWfHVQDZH7Byo=", + "owner": "BirdeeHub", + "repo": "nix-wrapper-modules", + "rev": "cc5e052dd1e93ae75dc06e3fabf46f0820c272f2", + "type": "github" + }, + "original": { + "owner": "BirdeeHub", + "repo": "nix-wrapper-modules", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/templates/ed/flake.nix b/templates/ed/flake.nix new file mode 100644 index 0000000..46b9141 --- /dev/null +++ b/templates/ed/flake.nix @@ -0,0 +1,134 @@ +{ + description = "Project Editor"; + + outputs = { + self, + nixpkgs, + nvimConfig, + ... + } @ inputs: let + projectSettings = {pkgs}: { + cats = { + clickhouse = false; + gitPlugins = false; + julia = false; + lua = false; + markdown = false; + nix = true; + optional = false; + python = false; + r = false; + }; + + settings = let + # With `replace` packages are replaced otherwise they are merged with base packages + replace = pkgs.lib.mkForce; + in { + lang_packages = { + python = replace ( + (with pkgs.python3Packages; [ + duckdb + polars + ]) + ++ ( + if builtins.pathExists ./python-packages.nix + # p: with p; [ ... ] + then import ./python-packages.nix pkgs.python3Packages + else [] + ) + ); + + r = replace ( + (with pkgs.rpkgs.rPackages; [ + fixest + # pkgs.extraRPackages.musicMetadata + ]) + ++ ( + if builtins.pathExists ./r-packages.nix + # p: with p.rPackages; [ ... ] + then import ./r-packages.nix pkgs.rpkgs + else [] + ) + ); + + julia = replace ([ + "StatsBase" + ] + ++ ( + if builtins.pathExists ./julia-packages.nix + # [ ... ] + then import ./julia-packages.nix + else [] + )); + }; + colorscheme = "kanagawa"; + background = "dark"; + wrapRc = true; + }; + binName = "vv"; + + env = { + IS_PROJECT_EDITOR = "1"; + R_LIBS_USER = "./.nvimcom"; + }; + + extraPackages = with pkgs; [ + cowsay + ]; + + specs.extraLua = let + name = builtins.baseNameOf (builtins.toString ./.); + in { + data = pkgs.vimPlugins.mini-notify; + before = ["INIT_MAIN"]; + config = '' + require("mini.notify").setup() + vim.notify = MiniNotify.make_notify() + vim.notify("Welcome to ${name}!") + ''; + }; + }; + + systems = nixpkgs.lib.systems.flakeExposed; + forAllSystems = nixpkgs.lib.genAttrs systems; + overlays = [inputs.nvimConfig.overlays.dependencies]; + in { + packages = forAllSystems (system: let + pkgs = import nixpkgs {inherit system overlays;}; + baseNvim = nvimConfig.packages.${system}.default; + + nvim = (baseNvim.eval (projectSettings {inherit pkgs;})).config.wrapper; + default = nvim; + in { + default = nvim; + }); + + devShells = forAllSystems (system: let + pkgs = import nixpkgs {inherit system overlays;}; + nv = self.packages.${system}.default; + in { + default = pkgs.mkShell { + packages = [nv pkgs.updateR]; + }; + }); + }; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; + rixpkgs.url = "github:dwinkler1/rixpkgs/nixpkgs"; + fran = { + url = "github:dwinkler1/fran"; + inputs = { + nixpkgs.follows = "rixpkgs"; + }; + }; + nvimConfig = { + url = "github:dwinkler1/nvimConfig"; + inputs = { + rixpkgs.follows = "rixpkgs"; + nixpkgs.follows = "nixpkgs"; + fran.follows = "fran"; + }; + }; + }; +} diff --git a/templates/rde/.envrc b/templates/rde/.envrc index 3550a30..e66fd99 100644 --- a/templates/rde/.envrc +++ b/templates/rde/.envrc @@ -1 +1,3 @@ +export DIRENV_WARN_TIMEOUT=20s + use flake diff --git a/templates/rde/README.md b/templates/rde/README.md new file mode 100644 index 0000000..d3a4cbf --- /dev/null +++ b/templates/rde/README.md @@ -0,0 +1,378 @@ +# Research Development Environment (RDE) Template + +A modular Nix flake template for reproducible research environments with support for R, Python, and Julia. Designed for data science, statistical analysis, and computational research. + +## Features + +- đŸ”Ŧ **Multi-language support**: R, Python, Julia with integrated tooling +- đŸ“Ļ **Reproducible**: Nix ensures consistent environments across machines +- 🎨 **Neovim-based**: Powerful editor with LSP, completion, and more +- 📊 **Research-focused**: Pre-configured for data analysis workflows +- 🔧 **Modular**: Enable only the languages you need +- 📝 **Documented**: Comprehensive inline documentation + +## Quick Start + +### Installation + +```bash +# Initialize a new project with this template +nix flake init -t github:dwinkler1/np#rde + +# Enter the development environment +nix develop + +# Or use direnv for automatic activation +echo "use flake" > .envrc +direnv allow +``` + +### First Steps + +```bash +# Initialize project structure (creates directories, git repo) +p-initProject + +# Enable Python (if needed) +# Edit flake.nix: set enabledLanguages.python = true + +# Initialize Python project +p-initPython + +# Update all dependencies +p-updateDeps +``` + +## Structure + +The template is organized into several directories for better maintainability: + +``` +templates/rde/ +├── flake.nix # Main flake configuration (261 lines) +├── README.md # This file +├── overlays/ # Nix overlays for packages +│ ├── r.nix # R packages configuration +│ ├── python.nix # Python packages configuration +│ ├── rix.nix # rstats-on-nix integration +│ ├── theme.nix # Neovim theme configuration +│ └── project-scripts.nix # Project initialization scripts +├── hosts/ # Host/command configurations +│ ├── default.nix # Merges all host configs +│ ├── python.nix # Python commands (marimo, ipy, etc.) +│ ├── julia.nix # Julia commands (jl, pluto, etc.) +│ ├── r.nix # R commands +│ └── utils.nix # Utility commands (initProject, etc.) +├── lib/ # Helper functions +│ ├── shell-hook.nix # Dev shell welcome message +│ └── mini-notify-config.lua # Neovim notification filtering +└── scripts/ # Shell scripts + ├── initPython.sh # Initialize Python project + ├── initProject.sh # Initialize project structure + ├── updateDeps.sh # Update all dependencies + └── activateDevenv.sh # Activate devenv shell +``` + +## Configuration + +Edit the `config` section in `flake.nix` to customize your environment: + +### Basic Settings + +```nix +config = rec { + # Name for your project commands (e.g., myproject-r, myproject-py) + defaultPackageName = "p"; + + # Enable/disable language support + enabledLanguages = { + julia = false; # Julia with Pluto notebooks + python = false; # Python with uv package manager + r = true; # R with tidyverse and friends + }; + + # Additional features + enabledPackages = { + gitPlugins = enabledLanguages.r; # R.nvim plugin + devenv = false; # Additional dev environment + }; + + # Neovim color scheme + theme = rec { + colorscheme = "kanagawa"; # cyberdream, onedark, tokyonight, kanagawa + background = "dark"; # dark or light + }; +}; +``` + +### Language-Specific Configuration + +#### R Configuration + +Edit `overlays/r.nix` to add/remove R packages: + +```nix +reqPkgs = with final.rpkgs.rPackages; [ + tidyverse # Add your packages here + data_table + # ... more packages +]; +``` + +Or create `r-packages.nix` in your project: + +```nix +rpkgs: with rpkgs.rPackages; [ + ggplot2 + dplyr +] +``` + +#### Python Configuration + +Python packages are managed via `uv`: + +```bash +# Add packages to your project +uv add numpy pandas matplotlib + +# Or edit pyproject.toml directly +``` + +#### Julia Configuration + +Julia packages use the built-in package manager: + +```bash +# In Julia REPL (p-jl) +using Pkg +Pkg.add("DataFrames") +``` + +## Available Commands + +Commands are prefixed with your `defaultPackageName` (default: `p`). + +### Editor + +- `p` or `p-pvim`: Launch Neovim +- `p-g`: Launch Neovide (GUI) + +### R (when enabled) + +- `p-r`: R console with pre-loaded packages +- Includes: tidyverse, data.table, languageserver, quarto + +### Python (when enabled) + +- `p-py`: Python interpreter +- `p-ipy`: IPython REPL (enhanced interactive shell) +- `p-marimo`: Marimo notebooks (reactive notebooks) +- `p-initPython`: Initialize Python project with uv + +### Julia (when enabled) + +- `p-jl`: Julia REPL with project environment +- `p-pluto`: Pluto.jl notebooks (reactive notebooks) +- `p-initJl`: Initialize Julia project + +### Utilities + +- `p-initProject`: Create project directory structure +- `p-updateDeps`: Update all dependencies (R, Python, Julia, flake) + +## Project Workflow + +### 1. Initialize Project + +```bash +# Create standardized directory structure +p-initProject + +# Creates: +# - data/{raw,processed,interim}/ +# - docs/ +# - figures/ +# - tables/ +# - src/{analysis,data_prep,explore,utils}/ +# - .gitignore +# - README.md +``` + +### 2. Set Up Language Environment + +**For R:** +```bash +# R is enabled by default +# Just start using it +p-r +``` + +**For Python:** +```bash +# 1. Enable in flake.nix +# 2. Initialize project +p-initPython +# 3. Add packages +uv add numpy pandas scikit-learn +``` + +**For Julia:** +```bash +# 1. Enable in flake.nix +# 2. Initialize project +p-initJl +# 3. Packages are managed in Julia REPL +``` + +### 3. Development + +```bash +# Start Neovim +p + +# Or use notebooks +p-marimo # Python notebooks +p-pluto # Julia notebooks + +# R scripts work with p (Neovim has R support) +``` + +### 4. Keep Dependencies Updated + +```bash +# Update everything at once +p-updateDeps + +# This updates: +# - R packages (rixpkgs snapshot) +# - Python packages (via uv) +# - Julia packages (via Pkg) +# - Flake inputs +``` + +## Benefits of This Structure + +1. **Modularity**: Each component is in its own file, making it easier to understand and modify +2. **Maintainability**: Changes to one language or feature don't affect others +3. **Readability**: Main flake.nix is ~261 lines instead of 688 (62% reduction) +4. **Reusability**: Individual modules can be easily reused or replaced +5. **Testability**: Smaller files are easier to test and debug +6. **Documentation**: Comprehensive inline comments explain how everything works + +## Extending the Template + +### Add New R Packages + +**System-wide** (edit `overlays/r.nix`): +```nix +reqPkgs = with final.rpkgs.rPackages; [ + tidyverse + yourNewPackage # Add here +]; +``` + +**Project-specific** (create `r-packages.nix`): +```nix +rpkgs: with rpkgs.rPackages; [ + projectSpecificPackage +] +``` + +### Add New Python Packages + +```bash +uv add package-name +``` + +### Add New Commands + +Edit the appropriate file in `hosts/`: +- `hosts/python.nix` - Python commands +- `hosts/julia.nix` - Julia commands +- `hosts/r.nix` - R commands +- `hosts/utils.nix` - General utilities + +### Add New Scripts + +1. Create script in `scripts/` +2. Add to `overlays/project-scripts.nix` +3. Add to appropriate host file + +### Customize Neovim + +The template uses a pre-configured Neovim (nixCats). To customize: +- Edit theme in `config.theme` section +- Add plugins in `flake.nix` categoryDefinitions +- Modify LSP settings in `categoryDefinitions.lspsAndRuntimeDeps` + +## Troubleshooting + +### Nix Build Fails + +```bash +# Update flake inputs +nix flake update + +# Clear cache +nix-collect-garbage +``` + +### Python Packages Not Found + +```bash +# Sync environment +uv sync + +# Or re-initialize +p-initPython +``` + +### R Packages Not Available + +```bash +# Update R snapshot +p-updateDeps + +# Or check overlays/r.nix for package name +``` + +## CI and Testing + +This template is automatically tested on every change to ensure all functionality works correctly. The CI workflow (`.github/workflows/check.yml`) runs comprehensive tests including: + +### Default Configuration Tests (R enabled) +- ✅ Template builds successfully +- ✅ Flake check passes +- ✅ Development shell enters without errors +- ✅ Neovim launches in headless mode +- ✅ R console is available and runs code +- ✅ Utility commands (initProject, updateDeps) are available +- ✅ Project structure creation works correctly + +### Python Configuration Tests +- ✅ Template builds with Python enabled +- ✅ Python commands (p-py, p-ipy, p-initPython) are available +- ✅ Python interpreter launches and executes code + +### Julia Configuration Tests +- ✅ Template builds with Julia enabled +- ✅ Julia commands (p-jl, p-initJl) are available +- ✅ Julia REPL launches and executes code + +The CI runs on: +- Every push to template files (`.nix`, `.sh`, `.lua`, `flake.lock`) +- Every pull request affecting the template +- Manual dispatch for testing + +This ensures that users can confidently use the template knowing that all advertised functionality has been verified. + +## Usage + +Use this template with: + +```bash +nix flake init -t github:dwinkler1/np#rde +``` + +Then run `direnv allow` or enter the dev shell with `nix develop`. diff --git a/templates/rde/flake.lock b/templates/rde/flake.lock index 90be7b2..7617db3 100644 --- a/templates/rde/flake.lock +++ b/templates/rde/flake.lock @@ -1,24 +1,53 @@ { "nodes": { + "fran": { + "inputs": { + "nixpkgs": [ + "rixpkgs" + ] + }, + "locked": { + "lastModified": 1771133455, + "narHash": "sha256-DvHfkW3DctWV+YnigY4+5Stn9NgI38JqR2IScXDX8z8=", + "owner": "dwinkler1", + "repo": "fran", + "rev": "68b189ab981adae06590ee1929a70c8b86afd2b8", + "type": "github" + }, + "original": { + "owner": "dwinkler1", + "repo": "fran", + "type": "github" + } + }, "nixCats": { "inputs": { + "fran": [ + "fran" + ], "nixCats": "nixCats_2", "nixpkgs": [ "nixpkgs" ], - "plugins-cmp-pandoc-references": "plugins-cmp-pandoc-references", - "plugins-cmp-r": "plugins-cmp-r", - "plugins-r": "plugins-r", + "plugins-cmp-pandoc-references": [ + "plugins-cmp-pandoc-references" + ], + "plugins-cmp-r": [ + "plugins-cmp-r" + ], + "plugins-r": [ + "plugins-r" + ], "rixpkgs": [ "rixpkgs" ] }, "locked": { - "lastModified": 1756388494, - "narHash": "sha256-Y06QA/XZ8+4nmjwZfFUWMf80slfIxOnylLn8GCmXjew=", + "lastModified": 1770172580, + "narHash": "sha256-l/fKPLdKxq7PLRbExDxsS4Esel4yos/NFJOFcDpsm2E=", "owner": "dwinkler1", "repo": "nixCatsConfig", - "rev": "c1aa16d681ac53298477e78097fefabdaacc16a7", + "rev": "98079a0844e53afd0c593c8f4d1ee5381baf36f0", "type": "github" }, "original": { @@ -29,11 +58,11 @@ }, "nixCats_2": { "locked": { - "lastModified": 1756094250, - "narHash": "sha256-Bkghd2NdQqQfKwSftABMDjnfRMFTJXA/XvvfKVonV/Q=", + "lastModified": 1769085828, + "narHash": "sha256-TjhFIAtS628+/r3IuYWPcNa++mUMMDDG8PbSfFHXBiA=", "owner": "BirdeeHub", "repo": "nixCats-nvim", - "rev": "7496b06144a15e44301aacea31c3e1ccdb81546e", + "rev": "43fbf4d12b0a613f1a792503da4bb2bf270173c7", "type": "github" }, "original": { @@ -44,16 +73,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1756288264, - "narHash": "sha256-Om8adB1lfkU7D33VpR+/haZ2gI5r3Q+ZbIPzE5sYnwE=", + "lastModified": 1771043024, + "narHash": "sha256-O1XDr7EWbRp+kHrNNgLWgIrB0/US5wvw9K6RERWAj6I=", "owner": "nixos", "repo": "nixpkgs", - "rev": "ddd1826f294a0ee5fdc198ab72c8306a0ea73aa9", + "rev": "3aadb7ca9eac2891d52a9dec199d9580a6e2bf44", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixpkgs-unstable", + "ref": "nixos-25.11", "repo": "nixpkgs", "type": "github" } @@ -74,46 +103,14 @@ "type": "github" } }, - "plugins-cmp-pandoc-references_2": { - "flake": false, - "locked": { - "lastModified": 1743491695, - "narHash": "sha256-XsdneGNJzmRBggk8lz9JNDQYk7wbYfUAF2oZLXzFb9c=", - "owner": "jmbuhr", - "repo": "cmp-pandoc-references", - "rev": "130eae4f75029d6495808e0ea4b769fa1ce4c9ac", - "type": "github" - }, - "original": { - "owner": "jmbuhr", - "repo": "cmp-pandoc-references", - "type": "github" - } - }, "plugins-cmp-r": { "flake": false, "locked": { - "lastModified": 1746709841, - "narHash": "sha256-TwmLSILu1H3RyRivCQlbsgUN4dsEqO1E8Hx71N/lFws=", + "lastModified": 1764700377, + "narHash": "sha256-xb7VFWM/BKAkN7fg62y8n618t2qkQjdYbPwhBhLJwtk=", "owner": "R-nvim", "repo": "cmp-r", - "rev": "602f399c4efde01147cadfebcc884b7aa154f8b7", - "type": "github" - }, - "original": { - "owner": "R-nvim", - "repo": "cmp-r", - "type": "github" - } - }, - "plugins-cmp-r_2": { - "flake": false, - "locked": { - "lastModified": 1746709841, - "narHash": "sha256-TwmLSILu1H3RyRivCQlbsgUN4dsEqO1E8Hx71N/lFws=", - "owner": "R-nvim", - "repo": "cmp-r", - "rev": "602f399c4efde01147cadfebcc884b7aa154f8b7", + "rev": "70bfe8f4c062acc10266e24825439c009a0b1b89", "type": "github" }, "original": { @@ -125,54 +122,44 @@ "plugins-r": { "flake": false, "locked": { - "lastModified": 1755532199, - "narHash": "sha256-mb8HCaMasPUP9JZUkH1sPrtdbhM2HMUkJEKDsRt6wTs=", + "lastModified": 1763419698, + "narHash": "sha256-GUnRsFF6MlUcFfEsNmm0zXauG3+Pf22OOJNXszOvs+M=", "owner": "R-nvim", "repo": "R.nvim", - "rev": "fd992e1c13ad3df794a30af2e937b994bf7bf4db", - "type": "github" - }, - "original": { - "owner": "R-nvim", - "repo": "R.nvim", - "type": "github" - } - }, - "plugins-r_2": { - "flake": false, - "locked": { - "lastModified": 1755532199, - "narHash": "sha256-mb8HCaMasPUP9JZUkH1sPrtdbhM2HMUkJEKDsRt6wTs=", - "owner": "R-nvim", - "repo": "R.nvim", - "rev": "fd992e1c13ad3df794a30af2e937b994bf7bf4db", + "rev": "b40fca202f26be4c83875dae489c655f2b834df6", "type": "github" }, "original": { "owner": "R-nvim", + "ref": "v0.99.1", "repo": "R.nvim", "type": "github" } }, "rixpkgs": { "locked": { - "lastModified": 1754915244, - "narHash": "sha256-WtdFq/HbvAgq03AMhSAJW35go6R5bqhGNf2KBzXqH2U=", - "type": "tarball", - "url": "https://github.com/rstats-on-nix/nixpkgs/archive/2025-08-11.tar.gz" + "lastModified": 1770035835, + "narHash": "sha256-Pi8f3LPTFmG7SZeAJEAamOWly1SZhEofT92uBqS4atY=", + "owner": "rstats-on-nix", + "repo": "nixpkgs", + "rev": "0d3fe7afce51d2126fdccf0b717d8048b90e4781", + "type": "github" }, "original": { - "type": "tarball", - "url": "https://github.com/rstats-on-nix/nixpkgs/archive/2025-08-11.tar.gz" + "owner": "rstats-on-nix", + "ref": "2026-02-02", + "repo": "nixpkgs", + "type": "github" } }, "root": { "inputs": { + "fran": "fran", "nixCats": "nixCats", "nixpkgs": "nixpkgs", - "plugins-cmp-pandoc-references": "plugins-cmp-pandoc-references_2", - "plugins-cmp-r": "plugins-cmp-r_2", - "plugins-r": "plugins-r_2", + "plugins-cmp-pandoc-references": "plugins-cmp-pandoc-references", + "plugins-cmp-r": "plugins-cmp-r", + "plugins-r": "plugins-r", "rixpkgs": "rixpkgs" } } diff --git a/templates/rde/flake.nix b/templates/rde/flake.nix index 97f893e..a6aa9a3 100644 --- a/templates/rde/flake.nix +++ b/templates/rde/flake.nix @@ -1,27 +1,6 @@ { description = "New Project"; - inputs = { - rixpkgs.url = "https://github.com/rstats-on-nix/nixpkgs/archive/2025-08-11.tar.gz"; - nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; - nixCats = { - url = "github:dwinkler1/nixCatsConfig"; - inputs.nixpkgs.follows = "nixpkgs"; - inputs.rixpkgs.follows = "rixpkgs"; - }; - ## Git Plugins - "plugins-r" = { - url = "github:R-nvim/R.nvim"; - flake = false; - }; - "plugins-cmp-r" = { - url = "github:R-nvim/cmp-r"; - flake = false; - }; - "plugins-cmp-pandoc-references" = { - url = "github:jmbuhr/cmp-pandoc-references"; - flake = false; - }; - }; + outputs = { self, nixpkgs, @@ -39,7 +18,7 @@ enabledLanguages = { julia = false; python = false; - r = false; + r = true; }; ## Enable packages enabledPackages = { @@ -47,199 +26,52 @@ ### Always enable when R is enabled ### You can use your own R installation and just enable the plugin gitPlugins = enabledLanguages.r; + ## Create additional dev shells in the project + devenv = false; + }; + theme = rec { + ## set colortheme and background here + ### "cyberdream", "onedark", and "tokyonight" are pre-installed + colorscheme = "kanagawa"; + background = "dark"; + ## Add other colortheme packages and config here + ## The default is a best guess + extraColorschemePackage = rec { + name = colorscheme; + extraLua = '' + vim.notify("Loading ${colorscheme} with extra config...") + require('${name}').setup({ + commentStyle = {italic = false}, + keywordStyle = {italic = false}, + theme = 'dragon' + }) + ''; + plugin = name + "-nvim"; + }; }; - }; - # R packages - rixOverlay = final: prev: {rpkgs = inputs.rixpkgs.legacyPackages.${prev.system};}; - rOverlay = final: prev: let - reqPkgs = with final.rpkgs.rPackages; [ - broom - data_table - janitor - languageserver - reprex - styler - tidyverse - (buildRPackage { - name = "nvimcom"; - src = inputs.plugins-r; - sourceRoot = "source/nvimcom"; - buildInputs = with prev.rpkgs; [ - R - stdenv.cc.cc - gnumake - ]; - propagatedBuildInputs = []; - }) - ]; - in { - quarto = final.rpkgs.quarto.override {extraRPackages = reqPkgs;}; - rWrapper = final.rpkgs.rWrapper.override {packages = reqPkgs;}; - }; - - # Python packages - pythonOverlay = final: prev: { - python = prev.python3.withPackages (pyPackages: - with pyPackages; [ - requests - ]); }; ################################### ## âŦ†ī¸ BASIC CONFIG ABOVE HERE âŦ†ī¸ ## ################################### - projectScriptsOverlay = final: prev: let - initPython = '' - #!/usr/bin/env bash - set -euo pipefail - if [[ ! -f "pyproject.toml" ]]; then - echo "🐍 Initializing UV project..." - uv init - echo "đŸ“Ļ Adding IPython and Marimo..." - uv add ipython - uv add marimo - echo "--------------------------------------------------------------------------" - echo "✅ Python project initialized!" - echo "run 'uv add PACKAGE' to add more python packages." - echo "--------------------------------------------------------------------------" - else - echo "--------------------------------------------------------------------------" - echo "🔄 Existing Python project detected." - echo "đŸ“Ļ Ensuring IPython and Marimo are installed..." - uv add ipython - uv add marimo - echo "Run '${config.defaultPackageName}-updateDeps' to update dependencies." - echo "--------------------------------------------------------------------------" - fi - ''; + # Import overlays from separate files + # Each overlay adds specific packages or configurations + rOverlay = import ./overlays/r.nix; + pythonOverlay = import ./overlays/python.nix; + rixOverlay = import ./overlays/rix.nix inputs; + extraPkgOverlay = import ./overlays/theme.nix config; + projectScriptsOverlay = import ./overlays/project-scripts.nix config; - initProjectScript = '' - #!/usr/bin/env bash - set -euo pipefail + supportedSystems = [ + "x86_64-linux" + "aarch64-linux" + "aarch64-darwin" + ]; + forSystems = nixpkgs.lib.genAttrs supportedSystems; - PROJECT_NAME="''${1:-${config.defaultPackageName}}" - - echo "🚀 Setting up project: $PROJECT_NAME" - - # Create directory structure - directories=( - "data/raw" - "data/processed" - "data/interim" - "docs" - "figures" - "tables" - "src/analysis" - "src/data_prep" - "src/explore" - "src/utils" - ) - - for dir in "''${directories[@]}"; do - if [[ ! -d "$dir" ]]; then - mkdir -p "$dir" - echo "✓ Created $dir/" - fi - done - - # Create essential files - if [[ ! -f "README.md" ]]; then - cat > README.md << 'EOF' - # RDE - - ## Project Structure - - `data/`: Data files (gitignored) - - `docs/`: Documentation - - `figures/`: Output figures - - `tables/`: Output tables - - `src/`: Source code - - EOF - fi - - # Initialize git - if [[ ! -d ".git" ]]; then - git init - echo "✓ Initialized Git repository and added: flake.nix, flake.lock" - fi - # Check if files are already staged/tracked before adding - if ! git diff --cached --name-only | grep -q "flake.nix\|flake.lock" && - ! git ls-files --error-unmatch flake.nix flake.lock >/dev/null 2>&1; then - echo "✓ Adding flake.nix, flake.lock to Git repository" - git add flake.nix flake.lock - else - echo "✓ flake.nix, flake.lock already tracked/staged in Git" - fi - # Create .gitignore - if [[ ! -f ".gitignore" ]]; then - cat > .gitignore << 'EOF' - # Data files - data/ - *.csv - *.docx - *.xlsx - *.parquet - - # R specific - .Rproj.user/ - .Rhistory - .RData - .Ruserdata - *.Rproj - .Rlibs/ - - # Python specific - __pycache__/ - *.pyc - .pytest_cache/ - .venv/ - - # Jupyter - .ipynb_checkpoints/ - - # IDE - .vscode/ - .idea/ - - # OS - .DS_Store - Thumbs.db - EOF - fi - - echo "✅ Project setup completed successfully!" - ''; - - updateDepsScript = '' - #!/usr/bin/env bash - set -euo pipefail - - echo "🔄 Updating project dependencies..." - - if [[ -f "flake.lock" ]]; then - nix flake update - echo "✅ Flake inputs updated" - fi - - if [[ -f "pyproject.toml" ]]; then - uv sync --upgrade - echo "✅ Python dependencies updated" - fi - - if [[ -f "Project.toml" ]]; then - ${config.defaultPackageName}-jl -e "using Pkg; Pkg.update()" - echo "✅ Julia dependencies updated" - fi - - echo "🎉 All dependencies updated!" - ''; - in { - initPython = prev.writeShellScriptBin "initPython" initPython; - initProject = prev.writeShellScriptBin "initProject" initProjectScript; - updateDeps = prev.writeShellScriptBin "updateDeps" updateDepsScript; - }; - forSystems = nixpkgs.lib.genAttrs nixpkgs.lib.platforms.all; + # Main package configuration + # This configures the Neovim environment with language support projectConfig = forSystems ( system: let inherit (nixCats) utils; @@ -251,11 +83,14 @@ prev.dependencyOverlays ++ [ (utils.standardPluginOverlay inputs) + extraPkgOverlay rixOverlay + inputs.fran.overlays.default rOverlay pythonOverlay projectScriptsOverlay ]; + categoryDefinitions = utils.mergeCatDefs prev.categoryDefinitions ( { pkgs, @@ -266,28 +101,17 @@ mkPlugin, ... } @ packageDef: { + # Language servers and runtime dependencies lspsAndRuntimeDeps = { - project = with pkgs; [ - ]; - julia = with pkgs; [ - julia-bin - ]; - python = with pkgs; [ - python - nodejs - pyright - uv - ]; - r = with pkgs; [ - rWrapper - quarto - air-formatter - ]; + project = with pkgs; []; + julia = with pkgs; [julia-bin]; + python = with pkgs; [python nodejs basedpyright uv]; + r = with pkgs; [rWrapper quarto air-formatter]; }; + # Plugins that load automatically startupPlugins = { - project = with pkgs.vimPlugins; [ - ]; + project = with pkgs.vimPlugins; [pkgs.extraTheme]; gitPlugins = with pkgs.neovimPlugins; [ { plugin = r; @@ -296,63 +120,44 @@ ]; }; + # Plugins that load on-demand optionalPlugins = { - project = with pkgs.vimPlugins; [ - ]; + project = with pkgs.vimPlugins; []; gitPlugins = with pkgs.neovimPlugins; [ cmp-r cmp-pandoc-references ]; }; + + # Lua code to run before main config optionalLuaPreInit = { project = [ - '' - local predicate = function(notif) - if not (notif.data.source == "lsp_progress" and notif.data.client_name == "lua_ls") then - return true - end - -- Filter out some LSP progress notifications from 'lua_ls' - return notif.msg:find("Diagnosing") == nil and notif.msg:find("semantic tokens") == nil - end - local custom_sort = function(notif_arr) - return MiniNotify.default_sort(vim.tbl_filter(predicate, notif_arr)) - end - require("mini.notify").setup({ content = { sort = custom_sort } }) - vim.notify = MiniNotify.make_notify() - '' + (builtins.readFile ./lib/mini-notify-config.lua) ]; }; - optionalLuaAdditions = { - project = [ - "vim.notify('Project loaded: ${name}')" - ]; - }; - sharedLibraries = { - project = { - }; - }; + # Lua code to run after main config + optionalLuaAdditions = { + project = ["vim.notify('Project loaded: ${name}')"]; + }; + + sharedLibraries = { + project = {}; + }; + + # Environment variables for each language environmentVariables = { - project = { - }; - julia = { - JULIA_NUM_THREADS = "auto"; - }; + project = {}; + julia = {JULIA_NUM_THREADS = "auto";}; python = { - # Prevent uv from managing Python downloads UV_PYTHON_DOWNLOADS = "never"; - # Force uv to use nixpkgs Python interpreter UV_PYTHON = pkgs.python.interpreter; }; - r = { - R_LIBS_USER = "./.Rlibs"; - }; + r = {R_LIBS_USER = "./.Rlibs";}; }; extraWrapperArgs = { - python = [ - "--unset PYTHONPATH" - ]; + python = ["--unset PYTHONPATH"]; }; } ); @@ -360,6 +165,8 @@ packageDefinitions = prev.packageDefinitions // { + # Main package definition + # This creates the command with configured languages and tools "${config.defaultPackageName}" = utils.mergeCatDefs prev.packageDefinitions.n ( { pkgs, @@ -369,133 +176,19 @@ settings = { suffix-path = false; suffix-LD = false; - # your alias may not conflict with your other packages. aliases = ["pvim"]; - hosts = { - g = { - enable = true; - path = { - value = "${pkgs.neovide}/bin/neovide"; - args = [ - "--add-flags" - "--neovim-bin ${name}" - ]; - }; - }; - m = let - marimoInit = '' - set -euo pipefail - if [[ ! -f "pyproject.toml" ]]; then - echo "🐍 Initializing UV project..." - uv init - echo "đŸ“Ļ Adding Marimo..." - uv add marimo - echo "--------------------------------------------------------------------------" - echo "✅ Python project initialized!" - echo "run 'uv add PACKAGE' to add more python packages." - echo "--------------------------------------------------------------------------" - else - echo "--------------------------------------------------------------------------" - echo "🔄 Syncing existing project..." - uv sync - echo "🐍 Launching Marimo..." - echo "--------------------------------------------------------------------------" - fi - ''; - in { - enable = config.enabledLanguages.python; - path = { - value = "${pkgs.uv}/bin/uv"; - args = [ - "--run" - "${marimoInit}" - "--add-flags" - "run marimo edit \"$@\"" - ]; - }; - }; - py = let - ipythonInit = '' - set -euo pipefail - if [[ ! -f "pyproject.toml" ]]; then - echo "🐍 Initializing UV project..." - uv init - echo "đŸ“Ļ Adding IPython..." - uv add ipython - echo "--------------------------------------------------------------------------" - echo "✅ Python project initialized!" - echo "run 'uv add PACKAGE' to add more python packages." - echo "--------------------------------------------------------------------------" - else - echo "--------------------------------------------------------------------------" - echo "🔄 Syncing existing project..." - echo "đŸ“Ļ Ensuring IPython is installed..." - uv add ipython - uv sync - echo "🐍 Launching IPython..." - echo "--------------------------------------------------------------------------" - fi - ''; - in { - enable = config.enabledLanguages.python; - path = { - value = "${pkgs.uv}/bin/uv"; - args = [ - "--run" - "${ipythonInit}" - "--add-flags" - "run ipython \"$@\"" - ]; - }; - }; - jl = { - enable = config.enabledLanguages.julia; - path = { - value = "${pkgs.julia-bin}/bin/julia"; - args = ["--add-flags" "--project=."]; - }; - }; - initJl = { - enable = config.enabledLanguages.julia; - path = { - value = "${pkgs.julia-bin}/bin/julia"; - args = ["--add-flags" "--project=. -e 'using Pkg; Pkg.instantiate()'"]; - }; - }; - r = { - enable = config.enabledLanguages.r; - path = { - value = "${pkgs.rWrapper}/bin/R"; - args = ["--add-flags" "--no-save --no-restore"]; - }; - }; - initPython = { - enable = config.enabledLanguages.python; - path.value = "${pkgs.initPython}/bin/initPython"; - }; - initProject = { - enable = true; - path = { - value = "${pkgs.initProject}/bin/initProject"; - }; - }; - updateDeps = { - enable = true; - path = { - value = "${pkgs.updateDeps}/bin/updateDeps"; - }; - }; - node.enable = true; - perl.enable = true; - ruby.enable = true; - }; + # Import all host commands from hosts/ directory + hosts = import ./hosts config pkgs; }; + # Enable/disable features based on config categories = { julia = config.enabledLanguages.julia; python = config.enabledLanguages.python; r = config.enabledLanguages.r; project = true; gitPlugins = config.enabledPackages.gitPlugins; + background = config.theme.background; + colorscheme = config.theme.colorscheme; }; } ); @@ -506,41 +199,68 @@ ); in { packages = projectConfig; + # Development shell configuration devShells = forSystems (system: let pkgs = import nixpkgs {inherit system;}; + # Language-specific packages that should be available in shell + languagePackages = with pkgs; + [] + ++ (if config.enabledLanguages.r then [quarto] else []) + ++ (if config.enabledLanguages.python then [uv] else []) + ++ (if config.enabledLanguages.julia then [] else []); in { - default = let - shellCmds = pkgs.lib.concatLines (pkgs.lib.filter (cmd: cmd != "") [ - (pkgs.lib.optionalString config.enabledLanguages.r " - ${config.defaultPackageName}-r: Launch R console") - (pkgs.lib.optionalString config.enabledLanguages.julia " - ${config.defaultPackageName}-jl: Launch Julia REPL") - (pkgs.lib.optionalString config.enabledLanguages.julia " - ${config.defaultPackageName}-initJl: Init existing Julia project") - (pkgs.lib.optionalString config.enabledLanguages.python " - ${config.defaultPackageName}-m: Launch Marimo notebook") - (pkgs.lib.optionalString config.enabledLanguages.python " - ${config.defaultPackageName}-py: Launch IPython REPL") - (pkgs.lib.optionalString config.enabledLanguages.python " - ${config.defaultPackageName}-initPython: Init python project") - " " - "To adjust options run: ${config.defaultPackageName} flake.nix" - ]); - in - pkgs.mkShell { - name = config.defaultPackageName; - packages = [projectConfig.${system}.default]; - inputsFrom = []; - shellHook = '' - echo "" - echo "==========================================================================" - echo "đŸŽ¯ ${config.defaultPackageName} Development Environment" - echo "---" - echo "📝 Run '${config.defaultPackageName}-initProject' to set up project structure" - echo "🔄 Run '${config.defaultPackageName}-updateDeps' to update all dependencies" - echo "---" - echo "🚀 Available commands:" - echo " - ${config.defaultPackageName}: Launch Neovim" - echo " - ${config.defaultPackageName}-g: Launch Neovide" - echo "${shellCmds}" - echo "==========================================================================" - echo "" - ''; - }; + default = pkgs.mkShell { + name = config.defaultPackageName; + packages = [projectConfig.${system}.default] ++ languagePackages; + inputsFrom = []; + # Welcome message when entering the shell + shellHook = import ./lib/shell-hook.nix config pkgs; + }; }); }; + inputs = { + rixpkgs.url = "github:rstats-on-nix/nixpkgs/2026-02-02"; + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; + nixCats = { + url = "github:dwinkler1/nixCatsConfig"; + inputs = { + nixpkgs.follows = "nixpkgs"; + rixpkgs.follows = "rixpkgs"; + fran.follows = "fran"; + plugins-cmp-pandoc-references.follows = "plugins-cmp-pandoc-references"; + plugins-cmp-r.follows = "plugins-cmp-r"; + plugins-r.follows = "plugins-r"; + }; + }; + ## Extra R packages + fran = { + url = "github:dwinkler1/fran"; + inputs = { + nixpkgs.follows = "rixpkgs"; + }; + }; + ## Git Plugins + "plugins-r" = { + url = "github:R-nvim/R.nvim/v0.99.1"; + flake = false; + }; + "plugins-cmp-r" = { + url = "github:R-nvim/cmp-r"; + flake = false; + }; + "plugins-cmp-pandoc-references" = { + url = "github:jmbuhr/cmp-pandoc-references"; + flake = false; + }; + }; + nixConfig = { + extra-substituters = [ + "https://rstats-on-nix.cachix.org" + "https://rde.cachix.org" + ]; + extra-trusted-public-keys = [ + "rstats-on-nix.cachix.org-1:vdiiVgocg6WeJrODIqdprZRUrhi1JzhBnXv7aWI6+F0=" + "rde.cachix.org-1:yRxQYM+69N/dVER6HNWRjsjytZnJVXLS/+t/LI9d1D4=" + ]; + }; } diff --git a/templates/rde/hosts/default.nix b/templates/rde/hosts/default.nix new file mode 100644 index 0000000..9639c8b --- /dev/null +++ b/templates/rde/hosts/default.nix @@ -0,0 +1,27 @@ +# Merges all host configurations from separate modules +# +# This file combines host definitions from language-specific modules. +# It serves as the single entry point for all command definitions. +# +# Structure: +# - python.nix: Python commands (marimo, ipy, py, initPython) +# - julia.nix: Julia commands (jl, pluto, initJl) +# - r.nix: R commands (r console) +# - utils.nix: Utility commands (initProject, updateDeps, etc.) +# +# Usage: +# This file is imported in flake.nix: +# hosts = import ./hosts config pkgs; +# +# The merged result provides all commands in a single attribute set. +# Commands are enabled/disabled based on config.enabledLanguages settings. +config: pkgs: let + # Import individual host modules + pythonHosts = import ./python.nix config pkgs; + juliaHosts = import ./julia.nix config pkgs; + rHosts = import ./r.nix config pkgs; + utilsHosts = import ./utils.nix config pkgs; +in + # Merge all hosts into single attribute set + # Later definitions override earlier ones in case of conflicts + pythonHosts // juliaHosts // rHosts // utilsHosts diff --git a/templates/rde/hosts/julia.nix b/templates/rde/hosts/julia.nix new file mode 100644 index 0000000..1ac861e --- /dev/null +++ b/templates/rde/hosts/julia.nix @@ -0,0 +1,66 @@ +# Julia-related host configurations +# +# This module defines all Julia-related commands available in the dev shell. +# Julia is configured with project-local package management. +# +# Available commands (when Julia is enabled): +# - -jl: Launch Julia REPL with project environment +# - -pluto: Launch Pluto.jl notebook server +# - -initJl: Initialize Julia project and install Pluto +# +# How it works: +# - All commands use --project=. to activate local Project.toml +# - JULIA_NUM_THREADS=auto enables multi-threading +# - Packages are managed via Julia's built-in Pkg manager +# +# Project setup: +# 1. Run -initJl to create Project.toml +# 2. Add packages: julia --project=. -e 'using Pkg; Pkg.add("PackageName")' +# 3. Packages are stored in Project.toml and Manifest.toml +# +# Dependencies: julia-bin (configured in flake.nix) +config: pkgs: { + # jl: Julia REPL with project environment + # Activates local Project.toml for package management + # Use Pkg.add("PackageName") to install packages + jl = { + enable = config.enabledLanguages.julia; + path = { + value = "${pkgs.julia-bin}/bin/julia"; + args = ["--add-flags" "--project=."]; + }; + }; + + # initJl: Initialize Julia project + # Creates Project.toml and installs Pluto.jl notebook + # Run this once to set up Julia package management + initJl = { + enable = config.enabledLanguages.julia; + path = { + value = "${pkgs.julia-bin}/bin/julia"; + args = ["--add-flags" "--project=. -e 'using Pkg; Pkg.instantiate(); Pkg.add(\"Pluto\")'"]; + }; + }; + + # pluto: Launch Pluto.jl interactive notebook + # Auto-installs Pluto if not present in Project.toml + # Opens browser with notebook interface + # Notebooks are reactive - cells update automatically + pluto = let + runPluto = '' + import Pkg; import TOML; Pkg.instantiate(); + proj = isfile("Project.toml") ? TOML.parsefile(Base.active_project()) : Dict(); + deps = get(proj, "deps", Dict()); + if !haskey(deps, "Pluto") + Pkg.add("Pluto"); + end + import Pluto; Pluto.run(); + ''; + in { + enable = config.enabledLanguages.julia; + path = { + value = "${pkgs.julia-bin}/bin/julia"; + args = ["--add-flags" "--project=. -e '${runPluto}'"]; + }; + }; +} diff --git a/templates/rde/hosts/python.nix b/templates/rde/hosts/python.nix new file mode 100644 index 0000000..d1c7b16 --- /dev/null +++ b/templates/rde/hosts/python.nix @@ -0,0 +1,96 @@ +# Python-related host configurations +# +# This module defines all Python-related commands available in the dev shell. +# Each command is configured with enable conditions and execution paths. +# +# Available commands (when Python is enabled): +# - -marimo: Launch Marimo notebook (interactive Python notebooks) +# - -py: Run Python interpreter +# - -ipy: Launch IPython REPL (enhanced interactive shell) +# - -initPython: Initialize Python project with uv +# +# How it works: +# - Commands are enabled based on config.enabledLanguages.python +# - UV (Python package manager) handles project dependencies +# - Each command auto-initializes project if pyproject.toml doesn't exist +# +# Dependencies: uv, python, nodejs, basedpyright (configured in flake.nix) +config: pkgs: { + # Marimo: Interactive notebook environment for Python + # Auto-initializes UV project and installs marimo on first run + marimo = let + marimoInit = '' + set -euo pipefail + if [[ ! -f "pyproject.toml" ]]; then + echo "🐍 Initializing UV project..." + uv init + echo "đŸ“Ļ Adding Marimo..." + uv add marimo + echo "--------------------------------------------------------------------------" + echo "✅ Python project initialized!" + echo "run 'uv add PACKAGE' to add more python packages." + echo "--------------------------------------------------------------------------" + else + echo "--------------------------------------------------------------------------" + echo "🔄 Syncing existing project..." + uv sync + echo "🐍 Launching Marimo..." + echo "--------------------------------------------------------------------------" + fi + uv run marimo edit "$@" + ''; + in { + enable = config.enabledLanguages.python; + path = { + value = "${pkgs.writeShellScriptBin "marimo-wrapper" marimoInit}/bin/marimo-wrapper"; + }; + }; + + # py: Standard Python interpreter + # Direct access to Python REPL for quick experiments + py = { + enable = config.enabledLanguages.python; + path = { + value = "${pkgs.python.interpreter}"; + }; + }; + + # ipy: IPython - Enhanced interactive Python shell + # Features: syntax highlighting, tab completion, magic commands + # Auto-initializes UV project and installs IPython on first run + ipy = let + ipythonInit = '' + set -euo pipefail + if [[ ! -f "pyproject.toml" ]]; then + echo "🐍 Initializing UV project..." + uv init + echo "đŸ“Ļ Adding IPython..." + uv add ipython + echo "--------------------------------------------------------------------------" + echo "✅ Python project initialized!" + echo "run 'uv add PACKAGE' to add more python packages." + echo "--------------------------------------------------------------------------" + else + echo "--------------------------------------------------------------------------" + echo "🔄 Syncing existing project..." + uv sync + echo "🐍 Launching IPython..." + echo "--------------------------------------------------------------------------" + fi + uv run ipython "$@" + ''; + in { + enable = config.enabledLanguages.python; + path = { + value = "${pkgs.writeShellScriptBin "ipy-wrapper" ipythonInit}/bin/ipy-wrapper"; + }; + }; + + # initPython: Initialize Python project + # Creates pyproject.toml and adds IPython and Marimo + # Use this to set up Python tooling in an existing project + initPython = { + enable = config.enabledLanguages.python; + path.value = "${pkgs.initPython}/bin/initPython"; + }; +} diff --git a/templates/rde/hosts/r.nix b/templates/rde/hosts/r.nix new file mode 100644 index 0000000..21f2030 --- /dev/null +++ b/templates/rde/hosts/r.nix @@ -0,0 +1,31 @@ +# R-related host configurations +# +# This module defines R-related commands available in the dev shell. +# R is configured with project-local package library and Quarto support. +# +# Available commands (when R is enabled): +# - -r: Launch R console with packages +# +# How it works: +# - Uses rWrapper which includes all packages from overlays/r.nix +# - R_LIBS_USER=./.Rlibs enables project-local package installation +# - --no-save --no-restore ensures clean session startup +# +# Package management: +# - System packages: Edit overlays/r.nix +# - Project packages: Install with install.packages() in R +# - Custom packages: Create r-packages.nix in project root +# +# Dependencies: rWrapper, quarto, air-formatter (configured in flake.nix) +config: pkgs: { + # r: R console with pre-configured packages + # Includes tidyverse, data.table, and other common packages + # Session starts without saving/restoring workspace + r = { + enable = config.enabledLanguages.r; + path = { + value = "${pkgs.rWrapper}/bin/R"; + args = ["--add-flags" "--no-save --no-restore"]; + }; + }; +} diff --git a/templates/rde/hosts/utils.nix b/templates/rde/hosts/utils.nix new file mode 100644 index 0000000..2071ee0 --- /dev/null +++ b/templates/rde/hosts/utils.nix @@ -0,0 +1,90 @@ +# Utility and common host configurations +# +# This module defines general-purpose commands and utilities. +# These commands are available regardless of enabled languages. +# +# Available commands: +# - : Launch Neovim editor (default command) +# - -g: Launch Neovide (GUI for Neovim) +# - -initProject: Initialize project directory structure +# - -updateDeps: Update all dependencies (R, Python, Julia, flake) +# - -initDevenv: Initialize devenv project (if enabled) +# - -devenv: Run devenv commands (if enabled) +# - -activateDevenv: Activate devenv shell (if enabled) +# +# Note: node, perl, ruby are also available but have minimal configuration +# +# Dependencies: neovide, devenv (if enabled), project scripts +config: pkgs: { + # g: Neovide - GUI frontend for Neovim + # Provides smooth scrolling, animations, and GUI features + # Automatically connects to the configured Neovim instance + g = { + enable = true; + path = { + value = "${pkgs.neovide}/bin/neovide"; + args = [ + "--add-flags" + "--neovim-bin ${config.defaultPackageName}" + ]; + }; + }; + + # initProject: Initialize research project structure + # Creates standardized directory layout for data analysis + # Sets up: data/, docs/, figures/, tables/, src/ + # Also initializes git repository and .gitignore + initProject = { + enable = true; + path = { + value = "${pkgs.initProject}/bin/initProject"; + }; + }; + + # initDevenv: Initialize devenv project + # Devenv provides additional development environment features + # Only available if config.enabledPackages.devenv = true + initDevenv = { + enable = config.enabledPackages.devenv; + path = { + value = "${pkgs.devenv}/bin/devenv"; + args = ["--add-flags" "init"]; + }; + }; + + # activateDevenv: Activate devenv shell + # Automatically runs when entering dev shell if devenv.nix exists + # Only available if config.enabledPackages.devenv = true + activateDevenv = { + enable = config.enabledPackages.devenv; + path = { + value = "${pkgs.activateDevenv}/bin/activateDevenv"; + }; + }; + + # devenv: Run devenv commands + # Access to full devenv CLI for managing development environments + # Only available if config.enabledPackages.devenv = true + devenv = { + enable = config.enabledPackages.devenv; + path = { + value = "${pkgs.devenv}/bin/devenv"; + }; + }; + + # updateDeps: Update all project dependencies + # Updates: R packages (rixpkgs), Python (uv), Julia (Pkg), flake inputs + # Automatically detects which languages are in use + updateDeps = { + enable = true; + path = { + value = "${pkgs.updateDeps}/bin/updateDeps"; + }; + }; + + # Additional language runtimes with minimal configuration + # These are available but not heavily used by this template + node.enable = true; # Node.js runtime (used by some LSPs) + perl.enable = true; # Perl runtime + ruby.enable = true; # Ruby runtime +} diff --git a/templates/rde/lib/mini-notify-config.lua b/templates/rde/lib/mini-notify-config.lua new file mode 100644 index 0000000..fd258ff --- /dev/null +++ b/templates/rde/lib/mini-notify-config.lua @@ -0,0 +1,42 @@ +-- Neovim notification configuration using mini.notify +-- +-- This file configures the mini.notify plugin to filter out verbose LSP messages. +-- It reduces noise from the Lua language server during development. +-- +-- What it does: +-- - Sets up mini.notify as the notification handler +-- - Filters out "Diagnosing" and "semantic tokens" messages from lua_ls +-- - Keeps all other notifications visible +-- +-- Usage: +-- Loaded automatically via flake.nix: +-- optionalLuaPreInit.project = [(builtins.readFile ./lib/mini-notify-config.lua)] +-- +-- Customization: +-- - Add more patterns to filter in the predicate function +-- - Filter notifications from other LSP servers by client_name +-- - Adjust notification display settings in setup() call + +-- Predicate function to filter notifications +-- Returns true if notification should be shown, false to hide it +local predicate = function(notif) + -- Keep all non-LSP notifications + if not (notif.data.source == "lsp_progress" and notif.data.client_name == "lua_ls") then + return true + end + -- Filter out specific verbose LSP progress notifications from lua_ls + -- These messages are too frequent and not useful during development + return notif.msg:find("Diagnosing") == nil and notif.msg:find("semantic tokens") == nil +end + +-- Custom sort function that applies filtering +-- Filters notification array before sorting +local custom_sort = function(notif_arr) + return MiniNotify.default_sort(vim.tbl_filter(predicate, notif_arr)) +end + +-- Initialize mini.notify with custom configuration +require("mini.notify").setup({ content = { sort = custom_sort } }) + +-- Set mini.notify as the default notification handler +vim.notify = MiniNotify.make_notify() diff --git a/templates/rde/lib/shell-hook.nix b/templates/rde/lib/shell-hook.nix new file mode 100644 index 0000000..a4dcdfa --- /dev/null +++ b/templates/rde/lib/shell-hook.nix @@ -0,0 +1,56 @@ +# Shell hook configuration +# +# This module generates the welcome message displayed when entering the dev shell. +# It provides information about available commands and how to get started. +# +# The message includes: +# - Project name and welcome banner +# - Quick start instructions (initProject, updateDeps) +# - List of all available commands based on enabled languages +# - Instructions for editing configuration +# +# Commands are conditionally shown based on config.enabledLanguages settings. +# This ensures users only see commands relevant to their configuration. +# +# Usage: +# Imported in flake.nix as: +# shellHook = import ./lib/shell-hook.nix config pkgs; +# +# Generates the help message displayed when entering the dev shell +config: pkgs: let + inherit (config) defaultPackageName enabledLanguages enabledPackages; + + # Build dynamic list of available commands based on enabled languages + # Filters out empty strings for disabled languages + shellCmds = pkgs.lib.concatLines (pkgs.lib.filter (cmd: cmd != "") [ + (pkgs.lib.optionalString enabledLanguages.r " - ${defaultPackageName}-r: Launch R console") + (pkgs.lib.optionalString enabledLanguages.julia " - ${defaultPackageName}-jl: Launch Julia REPL") + (pkgs.lib.optionalString enabledLanguages.julia " - ${defaultPackageName}-pluto: Launch Pluto.jl notebook") + (pkgs.lib.optionalString enabledLanguages.julia " - ${defaultPackageName}-initJl: Init existing Julia project") + (pkgs.lib.optionalString enabledLanguages.python " - ${defaultPackageName}-marimo: Launch Marimo notebook") + (pkgs.lib.optionalString enabledLanguages.python " - ${defaultPackageName}-py: Run python") + (pkgs.lib.optionalString enabledLanguages.python " - ${defaultPackageName}-ipy: Launch IPython REPL") + (pkgs.lib.optionalString enabledLanguages.python " - ${defaultPackageName}-initPython: Init python project") + (pkgs.lib.optionalString enabledPackages.devenv " - ${defaultPackageName}-initDevenv: Init devenv project") + (pkgs.lib.optionalString enabledPackages.devenv " - ${defaultPackageName}-devenv: Run devenv") + " " + "To adjust options run: ${defaultPackageName} flake.nix" + ]); +in '' + echo "" + echo "==========================================================================" + echo "đŸŽ¯ ${defaultPackageName} Development Environment" + echo "---" + echo "📝 Run '${defaultPackageName}-initProject' to set up project structure" + echo "🔄 Run '${defaultPackageName}-updateDeps' to update all dependencies" + echo "---" + echo "🚀 Available commands:" + echo " - ${defaultPackageName}: Launch Neovim" + echo " - ${defaultPackageName}-g: Launch Neovide" + echo "${shellCmds}" + echo "==========================================================================" + echo "" + # Auto-activate devenv shell if devenv.nix exists (can be disabled in config) + ${pkgs.lib.optionalString enabledPackages.devenv "${defaultPackageName}-activateDevenv"} + echo "" +'' diff --git a/templates/rde/overlays/project-scripts.nix b/templates/rde/overlays/project-scripts.nix new file mode 100644 index 0000000..c13ca90 --- /dev/null +++ b/templates/rde/overlays/project-scripts.nix @@ -0,0 +1,39 @@ +# Project scripts overlay +# +# This overlay wraps shell scripts from the scripts/ directory as Nix packages. +# Scripts are made available as executable commands with the project name prefix. +# +# How it works: +# 1. Reads shell scripts from scripts/ directory +# 2. Substitutes @defaultPackageName@ with actual package name +# 3. Creates executable packages via writeShellScriptBin +# 4. Scripts become available as: - +# +# Available scripts: +# - initPython: Initialize Python project with uv +# - initProject: Set up project directory structure +# - updateDeps: Update all dependencies (R, Python, Julia, flake) +# - activateDevenv: Activate devenv shell if available +# +# Usage: Scripts are automatically available in the dev shell +config: final: prev: let + # Helper function to substitute config placeholders in scripts + # Replaces @defaultPackageName@ with the actual package name from config + substituteScript = scriptPath: + prev.lib.replaceStrings + ["@defaultPackageName@"] + [config.defaultPackageName] + (builtins.readFile scriptPath); +in { + # Python project initialization (creates pyproject.toml, adds packages) + initPython = prev.writeShellScriptBin "initPython" (substituteScript ../scripts/initPython.sh); + + # Project structure setup (creates directories, git repo, .gitignore) + initProject = prev.writeShellScriptBin "initProject" (substituteScript ../scripts/initProject.sh); + + # Update all dependencies (R packages, Python packages, flake inputs) + updateDeps = prev.writeShellScriptBin "updateDeps" (substituteScript ../scripts/updateDeps.sh); + + # Activate devenv environment if devenv.nix exists + activateDevenv = prev.writeShellScriptBin "activateDevenv" (substituteScript ../scripts/activateDevenv.sh); +} diff --git a/templates/rde/overlays/python.nix b/templates/rde/overlays/python.nix new file mode 100644 index 0000000..7f9c64e --- /dev/null +++ b/templates/rde/overlays/python.nix @@ -0,0 +1,22 @@ +# Python packages overlay +# +# This overlay configures the Python environment with essential packages. +# Note: Most Python packages should be managed via uv (pyproject.toml) +# This overlay is for packages needed at the system level. +# +# Usage: +# - Add system-level Python packages to the list below +# - For project-specific packages, use uv (e.g., 'uv add package-name') +# - The Python interpreter is available via pkgs.python +# +# Example additions: +# - numpy, pandas, scipy for scientific computing +# - pytest, black, mypy for development tools +final: prev: { + # Python 3 with system-level packages + python = prev.python3.withPackages (pyPackages: + with pyPackages; [ + requests # HTTP library for making API calls + # Add more system-level packages here + ]); +} diff --git a/templates/rde/overlays/r.nix b/templates/rde/overlays/r.nix new file mode 100644 index 0000000..255079b --- /dev/null +++ b/templates/rde/overlays/r.nix @@ -0,0 +1,39 @@ +# R packages overlay +# +# This overlay configures the R environment with essential packages for data analysis. +# It combines packages from rstats-on-nix (rpkgs) with custom packages. +# +# Usage: +# - Edit the package list below to add/remove R packages +# - Create r-packages.nix in your project root to add custom packages +# - Custom file format: rpkgs: with rpkgs.rPackages; [ package1 package2 ] +# +# The overlay exports: +# - quarto: Quarto with R packages +# - rWrapper: R executable with all packages available +final: prev: let + # Core R packages for data analysis and development + reqPkgs = with final.rpkgs.rPackages; + [ + broom # Tidy model outputs + data_table # Fast data manipulation + janitor # Data cleaning helpers + languageserver # LSP for IDE support + reprex # Reproducible examples + styler # Code formatting + tidyverse # Data science ecosystem + ] + # Additional packages from fran overlay + ++ (with final.extraRPackages; [ + httpgd # HTTP graphics device for interactive plots + ]) + # Import custom R packages from project root if file exists + # Users can create r-packages.nix in their project to add more packages + # Example r-packages.nix: rpkgs: with rpkgs.rPackages; [ ggplot2 dplyr ] + ++ (prev.lib.optional (builtins.pathExists ./r-packages.nix) (import ./r-packages.nix final.rpkgs)); +in { + # Quarto with R support and all required packages + quarto = final.rpkgs.quarto.override {extraRPackages = reqPkgs;}; + # R wrapper with all packages pre-loaded + rWrapper = final.rpkgs.rWrapper.override {packages = reqPkgs;}; +} diff --git a/templates/rde/overlays/rix.nix b/templates/rde/overlays/rix.nix new file mode 100644 index 0000000..068ec86 --- /dev/null +++ b/templates/rde/overlays/rix.nix @@ -0,0 +1,21 @@ +# Rix overlay for R packages from rstats-on-nix +# +# This overlay provides access to R packages from the rstats-on-nix project. +# rstats-on-nix maintains snapshots of CRAN packages built with Nix. +# +# Purpose: +# - Provides reproducible R package versions +# - Ensures binary cache availability for faster builds +# - Maintained by the rstats-on-nix community +# +# The rpkgs attribute gives access to: +# - rpkgs.rPackages: All CRAN packages +# - rpkgs.quarto: Quarto publishing system +# - rpkgs.rWrapper: R with package management +# +# Update the R snapshot date in flake.nix inputs section: +# rixpkgs.url = "github:rstats-on-nix/nixpkgs/YYYY-MM-DD" +inputs: final: prev: { + # R packages from rstats-on-nix for the current system + rpkgs = inputs.rixpkgs.legacyPackages.${prev.stdenv.hostPlatform.system}; +} diff --git a/templates/rde/overlays/theme.nix b/templates/rde/overlays/theme.nix new file mode 100644 index 0000000..7410bf0 --- /dev/null +++ b/templates/rde/overlays/theme.nix @@ -0,0 +1,30 @@ +# Extra theme packages overlay +# +# This overlay configures the Neovim color scheme based on user configuration. +# It transforms the theme config from flake.nix into a Neovim plugin structure. +# +# Usage: +# - Configure theme in flake.nix config.theme section +# - Specify colorscheme name, background (dark/light) +# - Add custom Lua configuration in extraColorschemePackage +# +# The overlay exports: +# - extraTheme: Plugin structure with theme configuration +# +# Built-in themes: cyberdream, onedark, tokyonight, kanagawa +config: final: prev: let + # Transform user theme config into Neovim plugin format + extraTheme = { + # Get the plugin package from nixpkgs + plugin = prev.vimPlugins."${config.theme.extraColorschemePackage.plugin}"; + # Theme name for identification + name = config.theme.extraColorschemePackage.name; + # Lua configuration to run when theme loads + config = { + lua = config.theme.extraColorschemePackage.extraLua; + }; + }; +in { + # Export theme for use in Neovim configuration + inherit extraTheme; +} diff --git a/templates/rde/scripts/activateDevenv.sh b/templates/rde/scripts/activateDevenv.sh new file mode 100644 index 0000000..8b8113a --- /dev/null +++ b/templates/rde/scripts/activateDevenv.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -euo pipefail +if [[ -f "devenv.nix" ]]; then + echo "🚀 Activating devenv environment..." + if ! command -v @defaultPackageName@-devenv &> /dev/null; then + echo "❌ Command '@defaultPackageName@-devenv' not found." + echo "Ensure devenv is properly configured in your environment." + exit 1 + fi + exec @defaultPackageName@-devenv shell +else + echo "❌ No devenv.nix file found in the current directory." + echo "To create one, run '@defaultPackageName@-initDevenv'" + exit 1 +fi diff --git a/templates/rde/scripts/initProject.sh b/templates/rde/scripts/initProject.sh new file mode 100644 index 0000000..ebaf8e8 --- /dev/null +++ b/templates/rde/scripts/initProject.sh @@ -0,0 +1,120 @@ +#!/usr/bin/env bash +set -euo pipefail + +PROJECT_NAME="${1:-@defaultPackageName@}" + +echo "🚀 Setting up project: $PROJECT_NAME" + +# Create directory structure +directories=( + "data/raw" + "data/processed" + "data/interim" + "docs" + "figures" + "tables" + "src/analysis" + "src/data_prep" + "src/explore" + "src/utils" +) + +for dir in "${directories[@]}"; do + if [[ ! -d "$dir" ]]; then + mkdir -p "$dir" + echo "✓ Created $dir/" + fi +done + +# Create essential files +if [[ ! -f "README.md" ]]; then + cat > README.md << 'EOF' +# RDE + +## Project Structure +- `data/`: Data files (gitignored) +- `docs/`: Documentation +- `figures/`: Output figures +- `tables/`: Output tables +- `src/`: Source code + +EOF +fi + +# Initialize git +if [[ ! -d ".git" ]]; then + if ! command -v git &> /dev/null; then + echo "âš ī¸ Warning: 'git' command not found. Skipping git initialization." + echo "Install git to enable version control." + else + git init + echo "✓ Initialized Git repository" + fi +fi + +# Check if files exist and are not already staged/tracked before adding +if command -v git &> /dev/null && [[ -d ".git" ]]; then + if [[ -f "flake.nix" ]] && ! git diff --cached --name-only 2>/dev/null | grep -q "flake.nix" && + ! git ls-files --error-unmatch flake.nix >/dev/null 2>&1; then + echo "✓ Adding flake.nix to Git repository" + git add flake.nix + elif [[ -f "flake.nix" ]]; then + echo "✓ flake.nix already tracked/staged in Git" + fi + + if [[ -f "flake.lock" ]] && ! git diff --cached --name-only 2>/dev/null | grep -q "flake.lock" && + ! git ls-files --error-unmatch flake.lock >/dev/null 2>&1; then + echo "✓ Adding flake.lock to Git repository" + git add flake.lock + elif [[ -f "flake.lock" ]]; then + echo "✓ flake.lock already tracked/staged in Git" + fi +fi +# Create .gitignore +if [[ ! -f ".gitignore" ]]; then + cat > .gitignore << 'EOF' +# Data files +data/ +*.csv +*.docx +*.xlsx +*.parquet + +# R specific +.Rproj.user/ +.Rhistory +.RData +.Ruserdata +*.Rproj +.Rlibs/ + +# Python specific +__pycache__/ +*.pyc +.pytest_cache/ +.venv/ + +# Jupyter +.ipynb_checkpoints/ + +# IDE +.vscode/ +.idea/ + +# OS +.DS_Store +Thumbs.db + +# Devenv +.devenv* +devenv.local.nix + +# direnv +.direnv + +# pre-commit +.pre-commit-config.yaml +EOF +fi + +echo "✅ Project setup completed successfully!" diff --git a/templates/rde/scripts/initPython.sh b/templates/rde/scripts/initPython.sh new file mode 100644 index 0000000..6c560a5 --- /dev/null +++ b/templates/rde/scripts/initPython.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Check if uv command is available +if ! command -v uv &> /dev/null; then + echo "❌ Command 'uv' not found." + echo "UV is required for Python project management." + echo "Ensure UV is properly installed in your environment." + exit 1 +fi + +if [[ ! -f "pyproject.toml" ]]; then + echo "🐍 Initializing UV project..." + uv init + echo "đŸ“Ļ Adding IPython and Marimo..." + uv add ipython + uv add marimo + echo "--------------------------------------------------------------------------" + echo "✅ Python project initialized!" + echo "run 'uv add PACKAGE' to add more python packages." + echo "--------------------------------------------------------------------------" +else + echo "--------------------------------------------------------------------------" + echo "🔄 Existing Python project detected." + echo "đŸ“Ļ Ensuring IPython and Marimo are installed..." + uv add ipython + uv add marimo + echo "Run '@defaultPackageName@-updateDeps' to update dependencies." + echo "--------------------------------------------------------------------------" +fi diff --git a/templates/rde/scripts/updateDeps.sh b/templates/rde/scripts/updateDeps.sh new file mode 100644 index 0000000..c4d6c60 --- /dev/null +++ b/templates/rde/scripts/updateDeps.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "🔄 Updating project dependencies..." + +# Check for required commands +if ! command -v wget &> /dev/null; then + echo "❌ Error: 'wget' command not found." + echo "Please install wget to fetch R version information." + exit 1 +fi + +if ! command -v sed &> /dev/null; then + echo "❌ Error: 'sed' command not found." + echo "Please install sed to update flake.nix." + exit 1 +fi + +if ! command -v nix &> /dev/null; then + echo "❌ Error: 'nix' command not found." + echo "Please install Nix to update flake inputs." + exit 1 +fi + +# Ensure we're in the repository root +if [[ ! -f "flake.nix" ]]; then + # Try to find git root + if command -v git &> /dev/null && git rev-parse --show-toplevel >/dev/null 2>&1; then + cd "$(git rev-parse --show-toplevel)" + if [[ ! -f "flake.nix" ]]; then + echo "❌ Error: flake.nix not found in repository root" + exit 1 + fi + else + echo "❌ Error: Not in a git repository and flake.nix not found" + exit 1 + fi +fi + +# Fetch latest R version from rstats-on-nix +# This command chain: downloads CSV, extracts last line, gets 4th field (date), removes quotes +echo "📡 Fetching latest R version from rstats-on-nix..." +RVER=$( wget -qO- 'https://raw.githubusercontent.com/ropensci/rix/refs/heads/main/inst/extdata/available_df.csv' | tail -n 1 | head -n 1 | cut -d',' -f4 | tr -d '"' ) + +# Validate RVER matches YYYY-MM-DD format +if [[ ! "$RVER" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then + echo "❌ Error: Failed to fetch valid R version date. Got: '$RVER'" + exit 1 +fi + +echo "✅ R date is $RVER" + +# Create backup of flake.nix before modifying +cp flake.nix flake.nix.backup + +# Update rixpkgs date in flake.nix +if sed -i "s|rixpkgs.url = \"github:rstats-on-nix/nixpkgs/[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\";|rixpkgs.url = \"github:rstats-on-nix/nixpkgs/$RVER\";|" flake.nix; then + echo "✅ Updated rixpkgs date in flake.nix" + rm flake.nix.backup +else + echo "âš ī¸ Warning: Failed to update flake.nix, restoring backup" + mv flake.nix.backup flake.nix +fi + +nix flake update +echo "✅ Flake inputs updated" + +# Update Python dependencies if pyproject.toml exists and uv is available +if [[ -f "pyproject.toml" ]]; then + if command -v uv >/dev/null 2>&1; then + uv sync --upgrade + echo "✅ Python dependencies updated" + else + echo "â„šī¸ pyproject.toml found but uv command not available, skipping Python update" + fi +fi + +# Update Julia dependencies if Project.toml exists and julia is available +if [[ -f "Project.toml" ]]; then + if command -v @defaultPackageName@-jl >/dev/null 2>&1; then + @defaultPackageName@-jl -e "using Pkg; Pkg.update()" + echo "✅ Julia dependencies updated" + else + echo "â„šī¸ Project.toml found but @defaultPackageName@-jl command not available, skipping Julia update" + fi +fi + +# Update devenv dependencies if devenv.nix exists and devenv is available +if [[ -f "devenv.nix" ]]; then + if command -v devenv >/dev/null 2>&1; then + devenv update + echo "✅ Devenv dependencies updated" + else + echo "â„šī¸ devenv.nix found but devenv command not available, skipping devenv update" + fi +fi + +echo "🎉 All dependencies updated!"